--- name: redismodel 从0全量设计 overview: 以 internal/mysqlmodel 全部实体语义为基准,从 0 重新设计 internal/redismodel 的 Redis key 常量层,覆盖认证、用户、点赞、收藏、配额、锁、星座、通知、分享等全部业务,文件组织与术语对齐 mysqlmodel。 todos: - id: create-keys-basics content: 新建 keys.go 定义 RedisPrefix,删除旧 org.go status: completed - id: rewrite-auth-user content: 重写 auth.go(token 复数 Set 语义)并新建 user.go 定义 KeyUserInfo status: completed dependencies: - create-keys-basics - id: rewrite-quota-like-collect content: 重写 quota.go(术语对齐与总配额参数化)并新建 like.go、collect.go status: completed dependencies: - create-keys-basics - id: create-cache-count-keys content: 新建 closet.go、outfit.go、horoscope.go、notification.go 的缓存与计数 key status: completed dependencies: - create-keys-basics - id: rewrite-lock-keys content: 重写 lock.go 补齐全量锁与幂等 key 及参数注释 status: completed dependencies: - create-keys-basics - id: build-lint-verify content: 运行 go build 与 read_lints 校验 internal/redismodel 包 status: completed dependencies: - rewrite-auth-user - rewrite-quota-like-collect - create-cache-count-keys - rewrite-lock-keys --- ## 产品概述 以 `internal/mysqlmodel` 的实体与字段术语为唯一语义基准,从 0 重新设计 `internal/redismodel` 的 Redis key 常量层。本次只交付 key 常量定义,不写任何 `redisdao` 操作函数(`redisdao` 已清空,由用户后续补充),也不改动 `middleware`。 ## 核心功能 - 全局命名空间前缀 `RedisPrefix = "ztcloset:"` 独立成文件,不再挂靠在 auth.go - 认证与会话:access/refresh token 映射,以及 `user_id -> token 集合` 的 Set 反向索引(支持多端登录) - 用户缓存:`user_id -> 用户鉴权核心信息`(对齐 `User.Status` 语义) - 点赞/收藏:按 `type:target_id` 分桶的 Set key,成员为 user_id - 配额:每日计数 key(对齐 `UserDailyUsage` 的 AI/社区分享/衣柜分享三字段)与总配额 key(对齐 `Quota.QuotaType` 四类枚举) - 缓存与计数:星座运势缓存、通知未读数、衣柜分享访问计数、公开穿搭浏览计数 - 分布式锁与幂等:下单防重、支付回调幂等、退款回调幂等、AI 推荐防并发、AI 星座防并发、衣柜/穿搭审核防重 - 文件组织对齐 mysqlmodel 的模块划分(like/collect 分开,删除旧的 org.go) ## 技术栈 - 语言:Go - 改动范围:仅 `internal/redismodel` 下的 key 常量定义 - Redis 客户端(已存在,不修改):`global.RedisEngine`(`github.com/redis/go-redis/v9`) - 语义基准:`internal/mysqlmodel` 全部实体 ## 实现方案 ### 总体策略 推翻旧的 4 个文件(`auth.go`、`quota.go`、`org.go`、`lock.go`),按 mysqlmodel 的实体划分重新组织为 11 个文件。每个文件只包含对应业务模块的 key 常量,常量名与 key 字符串均对齐 mysqlmodel 的字段/枚举术语。所有 key 统一携带 `ztcloset:` 前缀。 ### 关键设计决策 1. **命名空间收敛**:`RedisPrefix` 抽到独立 `keys.go`,作为全包共享前缀,消除跨文件隐式依赖。 2. **文件对齐 mysqlmodel**:删除语义不符的 `org.go`,点赞、收藏分别用 `like.go`、`collect.go`;新增 `user.go`、`closet.go`、`outfit.go`、`horoscope.go`、`notification.go` 对应各实体。 3. **术语统一**:`community → outfit_share`(社区分享/搭配分享,对齐 `UserDailyUsage.OutfitShareCount`)、`wardrobe_share → closet_share`(对齐 `CLosetShareCount`)、`upload → cos`(对齐 `Quota.QuotaType=1=cos`)。 4. **token 一对多**:`KeyAuthUserToken` 改为复数 `KeyAuthUserTokens`,key 用 `auth:user:tokens:%d` 并明确为 Set 语义,解决单值无法表达多端登录的问题。 5. **配额双维度**:每日计数按 `UserDailyUsage` 的三个具名字段拆分;总配额用 `quota:total:%d:%d`(`user_id:quota_type`)参数化覆盖 `Quota.QuotaType` 全部 4 类,与数据库表结构(user_id + quota_type 唯一)完全对齐。 6. **锁与幂等维度**:下单用 order_id、支付回调用 order_no、退款回调用 refund_no、AI 推荐/星座用 user_id 维度防并发、审核用 target_id 防重,均对齐 mysqlmodel 对应字段。 7. **纯流水不设计**:`QuotaFlowLog`、`VIPFlowLog` 为纯流水日志;`Goods`、`Clothes`、`ClosetShareItem`、`OutfitItem` 为低频/关系数据,均无需 Redis key,避免过度设计。 ## 目录结构 ``` internal/redismodel/ ├── keys.go # [NEW] 全局 RedisPrefix = "ztcloset:" ├── auth.go # [MODIFY] 移除 RedisPrefix;KeyAuthUserToken 改为复数 Set 语义 ├── user.go # [NEW] KeyUserInfo 用户鉴权缓存 key ├── like.go # [NEW] KeyLikeSet(由旧 org.go 拆分) ├── collect.go # [NEW] KeyCollectSet(由旧 org.go 拆分) ├── quota.go # [MODIFY] 每日计数对齐 UserDailyUsage;总配额参数化对齐 QuotaType ├── closet.go # [NEW] KeyClosetShareView 衣柜分享访问计数 ├── outfit.go # [NEW] KeyOutfitView 公开穿搭浏览计数 ├── horoscope.go # [NEW] KeyHoroscope 星座运势缓存 ├── notification.go # [NEW] KeyNotificationUnread 通知未读数 ├── lock.go # [MODIFY] 补齐全量锁/幂等 key 与参数注释 └── org.go # [DELETE] 内容已拆分至 like.go / collect.go ``` ## 关键代码结构 修复后的 key 常量全集(均为常量定义,无函数): ``` // keys.go const RedisPrefix = "ztcloset:" // auth.go const ( KeyAuthAccess = RedisPrefix + "auth:access:%s" // access_token -> user_id(String) KeyAuthRefresh = RedisPrefix + "auth:refresh:%s" // refresh_token -> user_id(String) KeyAuthUserTokens = RedisPrefix + "auth:user:tokens:%d" // user_id -> token 集合(Set,支持多端登录) ) // user.go const KeyUserInfo = RedisPrefix + "user:info:%d" // user_id -> 用户鉴权核心信息(String/JSON,含 Status) // like.go const KeyLikeSet = RedisPrefix + "like:set:%d:%d" // like_type:target_id,成员为 user_id(Set) // collect.go const KeyCollectSet = RedisPrefix + "collect:set:%d:%d" // collect_type:target_id,成员为 user_id(Set) // quota.go const ( KeyQuotaDailyAI = RedisPrefix + "quota:daily:ai:%d:%s" // user_id:date,当日AI推荐次数 KeyQuotaDailyOutfitShare = RedisPrefix + "quota:daily:outfit_share:%d:%s" // user_id:date,当日社区分享次数(搭配分享) KeyQuotaDailyClosetShare = RedisPrefix + "quota:daily:closet_share:%d:%s" // user_id:date,当日衣柜分享次数 KeyQuotaTotal = RedisPrefix + "quota:total:%d:%d" // user_id:quota_type,总配额(1=cos 2=AI 3=社区分享 4=衣柜分享) ) // closet.go const KeyClosetShareView = RedisPrefix + "closet:share:view:%s" // share_uuid,衣柜分享访问计数(INCR) // outfit.go const KeyOutfitView = RedisPrefix + "outfit:view:%d" // outfit_id,公开穿搭浏览计数(INCR) // horoscope.go const KeyHoroscope = RedisPrefix + "horoscope:%s:%d" // zodiac:stat_date(YYYYMMDD),星座运势缓存(String/JSON) // notification.go const KeyNotificationUnread = RedisPrefix + "notification:unread:%d" // user_id,未读消息数(INCR/DECR) // lock.go const ( KeyLockOrderCreate = RedisPrefix + "lock:order:create:%d" // order_id,下单防重 KeyLockPayCallback = RedisPrefix + "lock:pay:callback:%s" // order_no,支付回调幂等 KeyLockRefundCallback = RedisPrefix + "lock:refund:callback:%s" // refund_no,退款回调幂等 KeyLockAIRecommend = RedisPrefix + "lock:ai:recommend:%d" // user_id,AI推荐防并发 KeyLockAIHoroscope = RedisPrefix + "lock:ai:horoscope:%d:%s" // user_id:zodiac,AI星座防并发 KeyLockClosetAudit = RedisPrefix + "lock:audit:closet:%d" // target_id,衣柜分享审核防重 KeyLockOutfitAudit = RedisPrefix + "lock:audit:outfit:%d" // target_id,穿搭审核防重 ) ``` ## 实现注意事项 - 校验仅针对 `redismodel` 包:`go build ./internal/redismodel/` 与 `read_lints`(路径限定在 `internal/redismodel`)。不要运行 `go build ./...`,因为 `redisdao` 已清空而 `middleware/lzcwyjwt.go` 仍引用 `redisdao.UserInfoGet`,全量编译会失败(该问题由用户后续补 redisdao 解决,不在本次范围)。 - key 字符串变更属于破坏性变更,依赖可清库/未上线前提(用户已确认)。 - 所有注释需标明占位符含义与数据结构类型(String/Set/INCR/JSON),为后续 redisdao 实现提供契约。