154 lines
8.8 KiB
Markdown
154 lines
8.8 KiB
Markdown
---
|
||
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 实现提供契约。 |