Files
zt-closet-backend/.codebuddy/plans/dao-tracing-refactor_d4fb1e5c.md
T
2026-09-11 01:41:52 +08:00

133 lines
7.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: dao-tracing-refactor
overview: 按 UserCreate 的链路追踪模式,为 mysqldao(13 文件) 与 redisdao(10 文件) 中所有 DAO 方法/函数统一补全 tracing span(Start/defer End/RecordError),并保持增删改查list 顺序。
todos:
- id: mysqldao-batch-a
content: 按样板改造 mysqldao 的 user.go 剩余方法、auditlog.go、closet.go、clothes.go、collect.go、goods.go,补充 Span 追踪与错误记录
status: completed
- id: mysqldao-batch-b
content: 改造 mysqldao 的 horoscope.go、like.go、notification.go、order.go、outfit.go、quota.go、vipflowlog.go,处理 RowsAffected 分支并加追踪
status: in_progress
dependencies:
- mysqldao-batch-a
- id: redisdao-batch-a
content: 改造 redisdao 的 auth.go、closet.go、collect.go、horoscope.go、like.go,处理管道 Exec 与 redis.Nil 豁免并加追踪
status: pending
- id: redisdao-batch-b
content: 改造 redisdao 的 lock.go、notification.go、outfit.go、quota.go、user.go,处理 Lua 脚本与 redis.Nil 豁免(user.go 补充 go-redis 导入)
status: pending
dependencies:
- redisdao-batch-a
- id: verify-build
content: 执行 go build ./... 与 go vet ./... 验证编译通过、无新增告警且语义不变
status: pending
dependencies:
- mysqldao-batch-b
- redisdao-batch-b
---
## 产品概述
为 `zt-closet-backend` 的数据访问层(DAO)统一接入 OpenTelemetry 链路追踪能力,使 MySQL 与 Redis 的每个数据操作方法都具备可观测的 Span 追踪和错误记录,便于分布式追踪与线上问题定位。
## 核心功能
- 以 `UserCreate` 方法为改造样板,为 `internal/mysqldao` 下全部 DAO 方法(13 个文件)补充统一的 Span 追踪与错误记录逻辑。
- 为 `internal/redisdao` 下全部包级函数/方法(10 个文件)补充相同风格的 Span 追踪与错误记录逻辑。
- 统一改造模式:方法开头启动 Span(命名 `dao.<方法名>`)→ `defer span.End()` → 执行原有 DB/缓存调用 → 出错时先 `span.RecordError(err)` 再返回。
- 保持各方法原有业务逻辑、入参与返回值语义完全不变,仅新增追踪与错误记录。
- 对 Redis 查询"未命中"(`redis.Nil`)等属于正常业务分支的场景不记录为错误,避免污染追踪链路。
- 维持既有 "增-删-改-查-list" 的方法排列顺序。
## 技术栈
- 语言:Go
- ORM:GORM(`gorm.io/gorm`),已通过 `d.db.WithContext(ctx)` 传递上下文
- 缓存:go-redis v9(`github.com/redis/go-redis/v9`)
- 追踪:OpenTelemetry,全局追踪器 `global.Tracer`(定义于 `global/otel.go` 的 `var Tracer trace.Tracer`)
## 实现方式
基于用户提供的 `UserCreate` 样板,对每个 DAO 方法套用统一的追踪装饰模式。这是纯增量式改造,不改变任何业务语义与函数签名,风险可控、易于批量执行。
### 改造模板(mysqldao,结构体方法)
```
func (d *XxxDao) XxxCreate(ctx context.Context, ...) (...) {
ctx, span := global.Tracer.Start(ctx, "dao.XxxCreate")
defer span.End()
if err := d.db.WithContext(ctx).Create(x).Error; err != nil {
span.RecordError(err)
return err
}
return nil
}
```
- 单行 `return ...Error` 需改写为 `if err := ...; err != nil { span.RecordError(err); return err }` 形式。
- 带 `RowsAffected` 返回值的方法(如 `QuotaIncreaseUsed`、`OrderUpdateStatusByOrderNo`):先判断 `res.Error`,记录后再返回,保留 `RowsAffected > 0` 判断逻辑。
- 查询类方法(`First`/`Find`)出错统一 `span.RecordError(err)` 后返回。
- `ctx` 必须重新赋值(`ctx, span := ...`),确保新 Span 的上下文继续向下游 `WithContext(ctx)` 传递。
### 改造模板(redisdao,多为包级函数)
```
func XxxSet(ctx context.Context, ...) error {
ctx, span := global.Tracer.Start(ctx, "dao.XxxSet")
defer span.End()
if err := global.RedisEngine.Set(ctx, key, val, ttl).Err(); err != nil {
span.RecordError(err)
return err
}
return nil
}
```
- redisdao 方法原多为 `return xxx.Err()` / `return xxx.Result()`,需先捕获 error/result 再记录并返回。
- 事务管道(`TxPipeline` + `Exec`,见 `auth.go`):对 `pipe.Exec(ctx)` 返回的 err 做记录后返回。
- Lua 脚本调用(`lock.go`、`quota.go`):对 `script.Run(...)` 返回结果的 error 做记录后返回。
- **`redis.Nil` 处理策略**:对 Get 类"未命中属正常"的方法(如 `UserInfoGet`、`AuthUserIDByAccess`、`QuotaRemainGet`),仅当 `err != nil && err != redis.Nil` 时才调用 `span.RecordError(err)`,其余错误正常记录;`redis.Nil` 仍原样返回给调用方,语义不变。`quotaDailyValue` 已内部吞掉 `redis.Nil`,无需特殊处理。
### 实现要点
- **保持顺序**:延续既有 "增删改查list" 排列,不调整方法位置。
- **import**:mysqldao 各文件已含 `global`;redisdao 各文件已含 `global`,对需判断 `redis.Nil` 的文件确认已导入 `github.com/redis/go-redis/v9`(`user.go` 当前未导入,需新增)。
- **零行为变更**:不改动 SQL/Redis 命令、条件、返回值与错误传播路径,仅叠加追踪。
- **性能**:Span 创建为轻量操作,DAO 均为单次 I/O,无额外热点开销;不引入 N+1 或重复遍历。
- **安全**:仅记录 error 对象,不写入用户敏感数据(避免 PII 进入追踪属性)。
## 目录结构
```
zt-closet-backend/
├── internal/
│ ├── mysqldao/
│ │ ├── user.go # [MODIFY] UserCreate 已完成;改造 UserUpdateByID/UserGetByID/UserGetByUUID/UserGetByOpenID
│ │ ├── auditlog.go # [MODIFY] ClosetAuditLog* 与 OutfitAuditLog* 全部方法加追踪
│ │ ├── closet.go # [MODIFY] ClosetShare* 与 ClosetShareItem* 全部方法加追踪
│ │ ├── clothes.go # [MODIFY] Clothes* 全部方法加追踪
│ │ ├── collect.go # [MODIFY] 收藏相关 DAO 方法加追踪
│ │ ├── goods.go # [MODIFY] 商品相关 DAO 方法加追踪
│ │ ├── horoscope.go # [MODIFY] 星座相关 DAO 方法加追踪
│ │ ├── like.go # [MODIFY] 点赞相关 DAO 方法加追踪
│ │ ├── notification.go # [MODIFY] 通知相关 DAO 方法加追踪
│ │ ├── order.go # [MODIFY] Order* 与 RefundFlowLog* 方法加追踪(含 RowsAffected 分支)
│ │ ├── outfit.go # [MODIFY] 穿搭相关 DAO 方法加追踪
│ │ ├── quota.go # [MODIFY] Quota*/UserDailyUsage*/QuotaFlowLog* 方法加追踪(含 RowsAffected 分支)
│ │ └── vipflowlog.go # [MODIFY] VIP 流水相关 DAO 方法加追踪
│ └── redisdao/
│ ├── auth.go # [MODIFY] AuthTokenSet/Del(管道 Exec)、AuthUserIDBy*(redis.Nil 豁免)
│ ├── closet.go # [MODIFY] 衣柜缓存相关函数加追踪
│ ├── collect.go # [MODIFY] 收藏缓存相关函数加追踪
│ ├── horoscope.go # [MODIFY] 星座缓存相关函数加追踪
│ ├── like.go # [MODIFY] LikeAdd/Remove/IsMember/Count 加追踪
│ ├── lock.go # [MODIFY] TryLock/Unlock(Lua 脚本)加追踪
│ ├── notification.go # [MODIFY] 通知缓存相关函数加追踪
│ ├── outfit.go # [MODIFY] 穿搭缓存相关函数加追踪
│ ├── quota.go # [MODIFY] Quota 缓存/Lua 脚本相关函数加追踪
│ └── user.go # [MODIFY] UserInfoSet/Del/Get(Get 需 redis.Nil 豁免,新增 go-redis 导入)
├── global/
│ └── otel.go # [无需修改] 提供 global.Tracer
```