Files
zt-closet-backend/.codebuddy/plans/redismodel_从0全量设计_31a88700.md
T
2026-09-11 01:41:52 +08:00

8.8 KiB
Raw Blame History

name, overview, todos
name overview todos
redismodel 从0全量设计 以 internal/mysqlmodel 全部实体语义为基准,从 0 重新设计 internal/redismodel 的 Redis key 常量层,覆盖认证、用户、点赞、收藏、配额、锁、星座、通知、分享等全部业务,文件组织与术语对齐 mysqlmodel。
id content status
create-keys-basics 新建 keys.go 定义 RedisPrefix,删除旧 org.go completed
id content status dependencies
rewrite-auth-user 重写 auth.go(token 复数 Set 语义)并新建 user.go 定义 KeyUserInfo completed
create-keys-basics
id content status dependencies
rewrite-quota-like-collect 重写 quota.go(术语对齐与总配额参数化)并新建 like.go、collect.go completed
create-keys-basics
id content status dependencies
create-cache-count-keys 新建 closet.go、outfit.go、horoscope.go、notification.go 的缓存与计数 key completed
create-keys-basics
id content status dependencies
rewrite-lock-keys 重写 lock.go 补齐全量锁与幂等 key 及参数注释 completed
create-keys-basics
id content status dependencies
build-lint-verify 运行 go build 与 read_lints 校验 internal/redismodel 包 completed
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 实现提供契约。