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

7.8 KiB
Raw Permalink Blame History

name, overview, todos
name overview todos
dao-tracing-refactor 按 UserCreate 的链路追踪模式,为 mysqldao(13 文件) 与 redisdao(10 文件) 中所有 DAO 方法/函数统一补全 tracing span(Start/defer End/RecordError),并保持增删改查list 顺序。
id content status
mysqldao-batch-a 按样板改造 mysqldao 的 user.go 剩余方法、auditlog.go、closet.go、clothes.go、collect.go、goods.go,补充 Span 追踪与错误记录 completed
id content status dependencies
mysqldao-batch-b 改造 mysqldao 的 horoscope.go、like.go、notification.go、order.go、outfit.go、quota.go、vipflowlog.go,处理 RowsAffected 分支并加追踪 in_progress
mysqldao-batch-a
id content status
redisdao-batch-a 改造 redisdao 的 auth.go、closet.go、collect.go、horoscope.go、like.go,处理管道 Exec 与 redis.Nil 豁免并加追踪 pending
id content status dependencies
redisdao-batch-b 改造 redisdao 的 lock.go、notification.go、outfit.go、quota.go、user.go,处理 Lua 脚本与 redis.Nil 豁免(user.go 补充 go-redis 导入) pending
redisdao-batch-a
id content status dependencies
verify-build 执行 go build ./... 与 go vet ./... 验证编译通过、无新增告警且语义不变 pending
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