---
name: 按层区分 DAO span 命名
overview: 将 mysqldao 的 span 名由 dao.X 改为 mysqldao.X、redisdao 的改为 redisdao.X,共 140 处,解决两层 span 名无法区分的问题。
todos:
- id: rename-mysqldao
content: 批量替换 internal/mysqldao 下 109 处 span 前缀为 mysqldao.
status: completed
- id: rename-redisdao
content: 批量替换 internal/redisdao 下 31 处 span 前缀为 redisdao.
status: completed
- id: verify-rename
content: 用 [subagent:code-explorer] 复核前缀计数与无残留无错改
status: completed
dependencies:
- rename-mysqldao
- rename-redisdao
- id: sync-readme
content: 在 README 目录说明中补充 span 命名约定
status: completed
dependencies:
- verify-rename
- id: build-verify
content: 执行 go build ./... 与 go vet ./... 验证
status: completed
dependencies:
- verify-rename
- sync-readme
---
## 产品概述
统一调整 OTel span 命名前缀:数据访问层的 span 名由通用的 `dao.XXX` 改为可区分来源的 `mysqldao.XXX` 与 `redisdao.XXX`,解决 MySQL 与 Redis 两层调用在链路追踪中无法区分的问题。
## 核心功能
- **MySQL 层改名**:`internal/mysqldao/` 下全部 109 处 span 名前缀 `dao.` 改为 `mysqldao.`
- **Redis 层改名**:`internal/redisdao/` 下全部 31 处 span 名前缀 `dao.` 改为 `redisdao.`
- **命名一致性保持**:仅改前缀,方法名(如 `UserInfoSet`)、业务逻辑、其他 span 属性均不变
- **不影响其他层**:`internal/service`(`service.`)、`pkg/upload`(`upload.`)、`pkg/tokenhub`(`scheduler.`)保持原样
- **可维护性**:README 目录说明补一句 span 命名约定,避免后续新增代码再用回 `dao.`
## 技术栈
- Go 1.27,模块 `git.lzcwy.cn/zhengchiliu/zt-closet-backend`
- OpenTelemetry Trace(`global.Tracer`,OTLP gRPC 上报)
- span 创建统一写法:`ctx, span := global.Tracer.Start(ctx, "<前缀>.<方法名>")`
## 实现方案
采用**目录限定的批量字符串替换**:把 `global.Tracer.Start(ctx, "dao.` 中的前缀分别替换为 `mysqldao.` / `redisdao.`,替换范围严格限定在 `internal/mysqldao/` 与 `internal/redisdao/` 两个目录。
关键决策与取舍:
1. **前缀命名用 `mysqldao.` / `redisdao.` 而非 `mysql.` / `redis.`**:与包名(`package mysqldao` / `package redisdao`)完全一致,Go 项目中 span 名即包名+方法名,排查时可从 span 名直接定位到文件与包,无需映射记忆。
2. **批量 sed 而非逐处手改**:140 处手工改必漏;用 `sed -i 's/Tracer\.Start(ctx, "dao\./Tracer.Start(ctx, "mysqldao./g'` 这类按目录执行的命令可保证一致性与完整性,且模式串锚定了 `Tracer.Start(ctx, "dao.` 全串,不会误伤字符串变量、注释或日志中的 `dao.`。
3. **只改前缀,不改方法名**:方法名与所在函数名一一对应,改名会造成与函数名不一致的二次困惑;同时避免破坏任何按名称检索的既有习惯。
4. **不引入常量**:项目其余前缀(`service.`、`upload.`、`scheduler.`)均为字面量,沿用字面量保持风格统一,不引入新的抽象层(YAGNI)。
5. **不改动 service / upload / tokenhub**:这些层前缀本身已能区分,不属于本次「分不清」的范围,缩小改动面。
性能与可靠性:
- span 名为启动期/调用期构造的字符串常量,改为更长前缀无运行时开销(无额外分配语义变化)。
- 风险点仅为「替换不完整」或「误伤其他目录」,通过目录限定 + 替换后计数校验(mysqldao 109 / redisdao 31 / 残留 `dao.` 为 0)来兜底。
## 实现要点(防回归)
- 替换模式必须锚定完整串 `global.Tracer.Start(ctx, "dao.`,避免误改 `mysqldao.`/`redisdao.` 之外的其它 `dao` 文本(如注释、错误信息)。
- 严格限定目录:`internal/mysqldao/*.go` 与 `internal/redisdao/*.go`,**不要**对仓库根或 `internal/` 整体执行替换(会误伤 `internal/service` 中可能存在的同名串)。
- 替换后必须核验:`internal/mysqldao` 与 `internal/redisdao` 中 `"dao.` 残留为 0;`"mysqldao.` 计数 109、`"redisdao.` 计数 31(与替换前一致)。
- 未发现任何测试或断言依赖 span 名字符串,改名不会破坏断言;仍以 `go build ./...` + `go vet ./...` 作为回归基线。
- 不改动 `configs/config.yaml`(第 2 行 `Domain: *` 为非法 YAML,属用户本地配置,本次不处理)。
## 架构设计
调用链与 span 命名分层(本次改动仅涉及最下两层的命名):
```mermaid
flowchart TD
A["routers/api/v1 handler
Gin 中间件 server span"] --> B["internal/service
span: service.Xxx"]
B --> C["internal/mysqldao
span: mysqldao.Xxx(本次由 dao. 改名)"]
B --> D["internal/redisdao
span: redisdao.Xxx(本次由 dao. 改名)"]
C --> E[("MySQL")]
D --> F[("Redis")]
```
## 目录结构
```
internal/mysqldao/
├── quota.go # [MODIFY] 18 处 span 前缀 dao. → mysqldao.
├── outfit.go # [MODIFY] 13 处
├── order.go # [MODIFY] 12 处
├── clothes.go # [MODIFY] 9 处
├── closet.go # [MODIFY] 8 处
├── user.go # [MODIFY] 7 处
├── horoscope.go # [MODIFY] 7 处
├── benefit_grant_task.go # [MODIFY] 7 处
├── collect.go # [MODIFY] 6 处
├── like.go # [MODIFY] 6 处
├── notification.go # [MODIFY] 6 处
├── auditlog.go # [MODIFY] 4 处
├── goods.go # [MODIFY] 3 处
└── vipflowlog.go # [MODIFY] 3 处
internal/redisdao/
├── quota.go # [MODIFY] 6 处 span 前缀 dao. → redisdao.
├── collect.go # [MODIFY] 4 处
├── like.go # [MODIFY] 4 处
├── auth.go # [MODIFY] 4 处
├── notification.go # [MODIFY] 4 处
├── user.go # [MODIFY] 3 处
├── horoscope.go # [MODIFY] 2 处
├── lock.go # [MODIFY] 2 处
├── outfit.go # [MODIFY] 1 处
└── closet.go # [MODIFY] 1 处
README.md # [MODIFY] 可选:目录树中 mysqldao/redisdao 说明补 span 命名约定(mysqldao.* / redisdao.*)
```
## Agent Extensions
### SubAgent
- **code-explorer**
- Purpose:替换完成后复核两个 DAO 目录的 span 命名:确认 `internal/mysqldao` 全部为 `mysqldao.` 前缀、`internal/redisdao` 全部为 `redisdao.` 前缀、无 `"dao.` 残留,且 `internal/service`、`pkg/upload`、`pkg/tokenhub` 的 span 名未被误改
- Expected outcome:输出各目录前缀计数与残留清单,确保 140 处全部正确改名、零误伤