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

154 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 实现提供契约。