# 技术债务清理实现任务清单

## 0. 数据库迁移文件合并为生产基线（优先执行）

- [ ] 0.1 确认当前数据库 schema 完整可用
  - 在本地执行 `go run cmd/api/main.go`，确认所有接口正常启动
  - 验证：服务启动无错误

- [ ] 0.2 使用 `pg_dump` 生成当前完整 schema（仅 DDL，不含数据）
  - 命令：`pg_dump --schema-only --no-owner --no-acl -d junhong_cmp $DB_DSN > migrations/000114_squash_baseline.up.sql`
  - 检查生成的 SQL：确认包含所有表（tb_account、tb_iot_card、tb_device、tb_order 等）、所有索引
  - 确认 SQL 中无外键约束（项目禁止）、无 GORM 关联标签
  - 为每张表添加 `CREATE TABLE IF NOT EXISTS` 保护（替换 `CREATE TABLE`）
  - 验证：SQL 文件语法正确，能在全新数据库上执行成功

- [ ] 0.3 编写 `migrations/000114_squash_baseline.down.sql`（DROP 所有表）
  - 按依赖顺序（子表先删、主表后删）逐一 DROP TABLE
  - 使用 `DROP TABLE IF EXISTS` 保护
  - 验证：`.down.sql` 在已建库的数据库上能完整回滚（所有表被删除）

- [ ] 0.4 创建 `migrations/000115_init_data.up.sql`（初始化数据）
  - 合并以下内容：
    1. **轮询配置初始数据**（原 `000104_polling_config_data.up.sql` 内容）
    2. **历史订单 purchase_role 回填**（原 `backfill_order_purchase_role.sql` 内容）
  - 确认所有 INSERT 使用 `ON CONFLICT DO NOTHING` 保护（幂等性）
  - 确认 UPDATE 语句带 `WHERE xxx IS NULL` 条件（幂等性）
  - 验证：在空数据库（已跑完 000114）上执行 000115 无报错，数据正确

- [ ] 0.5 创建 `migrations/000115_init_data.down.sql`（回滚初始数据）
  - DELETE 轮询配置中由 000115 插入的行（按 `config_name` 或 `description` 标识）
  - purchase_role 回填不需要 rollback（DOWN 时留空注释说明原因即可）
  - 验证：执行后轮询初始配置被清除

- [ ] 0.6 归档旧迁移文件
  - 创建 `migrations/archive/` 目录
  - 将 `000000` ~ `000113` 全部移入 `migrations/archive/`（包含 `.up.sql` 和 `.down.sql`）
  - 将游离文件 `backfill_order_purchase_role.sql` 也移入 `migrations/archive/`
  - 验证：`migrations/` 目录下只剩 `000114`、`000115`（以及后续 `000116` 废弃字段迁移）

- [ ] 0.7 编写测试环境重置脚本 `scripts/reset_db.sh`
  ```bash
  #!/bin/bash
  # 重置数据库到干净状态（仅用于开发/测试环境）
  DB_NAME="${JUNHONG_DATABASE_DBNAME:-junhong_cmp}"
  DB_DSN="postgres://..."
  
  echo "警告：即将清空数据库 $DB_NAME，5 秒后继续..."
  sleep 5
  
  psql -c "DROP DATABASE IF EXISTS $DB_NAME;"
  psql -c "CREATE DATABASE $DB_NAME;"
  migrate -path migrations -database "$DB_DSN" up
  echo "数据库重置完成"
  ```
  - 验证：脚本可执行，在测试环境运行后数据库从 0 恢复到完整状态

- [ ] 0.8 在全新数据库上验证 migrate up 完整链路
  - 创建临时空数据库
  - 执行 `migrate -path migrations -database "$DB_DSN" up`
  - 确认顺序执行 000114 → 000115（→ 000116 在任务 5 完成后）全部成功
  - 检查表结构与旧测试环境一致（关键表：tb_order、tb_iot_card、tb_device、tb_agent_wallet 等）
  - 验证：migrate up 无报错，表结构正确

- [ ] 0.9 重置测试环境数据库，切换到新基线
  - 与团队确认测试环境可以重置
  - 执行 `./scripts/reset_db.sh`
  - 确认服务重新启动后所有接口正常
  - 验证：测试环境运行正常，migrate status 显示 `000115` 为最新已应用迁移

---

## 1. 支付配置动态加载（三个 TODO 点）

- [ ] 1.1 在 `pkg/constants/redis.go` 中新增支付配置 Redis Key 生成函数
  - 新增函数：`RedisPaymentConfigKey(configID uint) string`，返回格式 `payment:config:{configID}`
  - 遵循项目规范：所有 Redis Key 必须通过函数生成，禁止硬编码字符串
  - 验证：编译通过，函数签名符合项目现有 `Redis*Key` 命名规范

- [ ] 1.2 确认现有 `pkg/payment/` 包中的 `Payment` 接口/类型定义
  - 查阅 `pkg/payment/` 目录，确认是否已存在支付接口（如 `WechatPayment` 等）
  - 若已存在统一接口，`LoadConfig` 返回该接口类型；若不存在，在此步骤新建 `Payment` 接口
  - 验证：`LoadConfig` 的返回类型有明确的定义来源，无模糊引用

- [ ] 1.3 创建 `pkg/payment/loader.go` 文件，定义 `PaymentConfigLoader` 接口和实现
  - 接口包含 `LoadConfig(ctx context.Context, configID uint) (Payment, error)` 方法
  - 实现中使用 `constants.RedisPaymentConfigKey(configID)` 作为缓存 key（禁止硬编码）
  - 实现包含 Redis 缓存逻辑（TTL 1h）
  - **权限检查说明**：仅在用户主动操作（创建充值单等）时做权限校验；支付回调场景（`/api/callback/wechat`）无登录态，校验逻辑为"回调商户号与配置商户号一致"，不做用户身份鉴权
  - 验证：编译通过，无语法错误

- [ ] 1.4 修改 `internal/service/order/service.go`，在两处 TODO（行 2254、2310）实现动态加载
  - 注入 `paymentLoader` 依赖
  - 替换 `s.wechatPayment` 为 `s.paymentLoader.LoadConfig(ctx, order.PaymentConfigID)`
  - 添加错误处理：若配置不存在返回 `errors.New(errors.CodePaymentConfigNotFound)`
  - 验证：编译通过，`go vet` 无报告

- [ ] 1.5 修改 `internal/service/recharge/service.go`，在 TODO（行 271）实现动态加载
  - 注入 `paymentLoader` 依赖
  - 替换硬编码为 `s.paymentLoader.LoadConfig(ctx, rechargeOrder.PaymentConfigID)`
  - 验证：编译通过，`go vet` 无报告

- [ ] 1.7 运行 `go build ./cmd/api ./cmd/worker` 确认编译通过，并通过 PostgreSQL MCP 手动验证支付逻辑
  - 执行 `go vet ./internal/service/order/ ./internal/service/recharge/` 静态分析
  - 使用 curl 或 Postman 手动触发支付回调，确认验签逻辑正常
  - 验证：编译通过，vet 无报告，手动验签流程无报错

---

## 2. API 文档生成器补全（39 个缺失 Handler）

- [ ] 2.1 修改 `cmd/api/docs.go`，在 `bootstrap.Handlers` 结构体中注册所有 48 个 Handler
  - 按模块分组注册（admin、agent、h5 等）
  - 每个 Handler 字段添加注释说明对应的路由前缀
  - 完整列表：Account、AdminOrder、AgentRecharge、Asset、AssetAllocationRecord、AssetWallet、Auth、Authorization、Carrier、CommissionWithdrawal、CommissionWithdrawalSetting、Device、DeviceImport、Enterprise、EnterpriseCard、EnterpriseDevice、IotCard、IotCardImport、Package、PackageSeries、PackageUsage、PaymentCallback、Permission、PersonalCustomer、PollingAlert、PollingCleanup、PollingConcurrency、PollingConfig、PollingManualTrigger、PollingMonitoring、Refund、Role、Shop、ShopCommission、ShopPackageBatchAllocation、ShopPackageBatchPricing、ShopRole、ShopSeriesGrant、Storage、WechatConfig
  - 验证：代码可编译

- [ ] 2.2 修改 `cmd/gendocs/main.go`，在 `bootstrap.Handlers` 结构体中同样注册所有 48 个 Handler
  - 保持与 docs.go 中的结构一致
  - 验证：代码可编译

- [ ] 2.3 运行 `go run cmd/gendocs/main.go` 生成 OpenAPI 文档，验证包含所有 48 个接口路由
  - 检查生成的文档中是否有所有接口的 path、method、parameters、responses
  - 验证：无"缺失 Handler"的警告

- [ ] 2.4 人工核查生成的 OpenAPI 文档中接口数量与路由注册数量一致
  - 统计 `internal/router/` 下实际注册的路由数，与文档中的 path 条目数对比
  - 验证：文档中接口数量 ≥ 48，无缺失路由

---

## 3. 轮询状态常量提取（10+ 处硬编码）

- [ ] 3.1 在 `pkg/constants/polling.go` 中新增轮询手动触发日志状态常量
  ```go
  const (
      PollingManualTriggerStatusPending    = "pending"
      PollingManualTriggerStatusProcessing = "processing"
      PollingManualTriggerStatusCompleted  = "completed"
      PollingManualTriggerStatusCancelled  = "cancelled"
  )
  ```
  - 验证：编译通过

- [ ] 3.2 修改 `internal/handler/admin/polling_manual_trigger.go`，将硬编码状态字符串替换为常量
  - 搜索并替换所有 `"pending"`、`"completed"`、`"cancelled"` 为对应常量
  - 验证：编译通过，`go vet ./internal/handler/admin/...` 无报告

- [ ] 3.3 修改 `internal/service/polling/manual_trigger_service.go`，替换硬编码状态字符串
  - 替换所有 `"pending"`、`"processing"`、`"completed"`、`"cancelled"` 为常量
  - 验证：编译通过，`go vet ./internal/service/polling/...` 无报告

- [ ] 3.4 修改 `internal/store/postgres/polling_manual_trigger_store.go`，替换硬编码字符串
  - 替换状态比较和更新操作中的硬编码
  - 验证：编译通过

- [ ] 3.5 修改 `internal/service/polling/alert_service.go`，替换 `NotificationStatus` 中的硬编码
  - 将 `"pending"` 替换为常量
  - 验证：编译通过，`go vet ./internal/service/polling/...` 无报告，通过 PostgreSQL MCP 查询告警日志确认状态值正确

- [ ] 3.6 运行 `grep -r '"pending"\|"completed"\|"cancelled"' internal/` 确认轮询相关文件中无剩余硬编码
  - 验证：无轮询相关的硬编码字符串

---

## 4. 废弃代码清理（15 个别名 + 3 个 DTO + 2 个方法）

- [ ] 4.1 删除 `internal/task/sync.go` 整个文件
  - 在 `pkg/queue/handler.go` 中删除 `syncHandler` 的注册代码（行 61-67）
  - 在 `pkg/constants/constants.go` 中删除 `TaskTypeDataSync` 常量定义
  - 验证：`grep -r "TaskTypeDataSync\|SyncHandler\|HandleDataSync" internal/ pkg/` 无结果

- [ ] 4.2 全局搜索 `pkg/constants/wallet.go` 中的 15 个废弃别名，确认引用情况
  ```
  WalletTypeMain, WalletTypeCommission, 
  WalletResourceType*, 
  WalletStatus*, 
  TransactionType*, 
  RechargeOrderPrefix, RechargeMinAmount, RechargeMaxAmount, 
  Card* 前缀别名（9 个）
  ```
  - 运行 `grep -r "WalletTypeMain\|WalletTypeCommission\|WalletResourceType" internal/ pkg/` 统计引用次数
  - 验证：若有引用则记录文件和行号，后续逐个替换

- [ ] 4.3 对于每个有引用的废弃别名，替换为新常量
  - 使用 IDE 的重构工具或 `sed` 批量替换
  - 示例：`WalletTypeMain` → `AgentWalletTypeMain`
  - 验证：编译通过，无引用残留

- [ ] 4.4 删除 `pkg/constants/wallet.go` 中所有 Deprecated 别名定义（共 15 个）
  - 删除注释块和别名定义
  - 验证：编译通过，运行 `grep -c "Deprecated" pkg/constants/wallet.go` 返回 0

- [ ] 4.5 删除 `internal/model/dto/enterprise_card_authorization_dto.go` 中的 3 个废弃 DTO 类型
  - `DeviceBundle`、`DeviceBundleCard`、`AllocatedDevice`
  - 验证：全局搜索确认无引用，编译通过

- [ ] 4.6 删除 `internal/service/order/service.go` 中的 `CreateLegacy()` 方法（行 113 及方法体）
  - 全局搜索确认无调用点
  - 验证：编译通过

- [ ] 4.7 删除 `internal/service/iot_card/stop_resume_service.go` 中的 `CheckAndStopCard()` 方法（行 330）
  - 全局搜索确认无调用点
  - 验证：编译通过

- [ ] 4.8 运行 `go build ./cmd/api ./cmd/worker` 确认删除未引入编译错误
  - 执行 `go vet ./internal/model/dto/ ./internal/service/order/ ./internal/service/iot_card/` 静态分析
  - 验证：编译通过，vet 无报告

---

## 5. Model 废弃字段删除与数据库迁移

- [ ] 5.1 从 `internal/model/iot_card.go` 中删除 `FirstCommissionPaid` 和 `AccumulatedRecharge` 字段定义
  - 验证：编译通过

- [ ] 5.2 从 `internal/model/device.go` 中删除 `FirstCommissionPaid` 和 `AccumulatedRecharge` 字段定义
  - 验证：编译通过

- [ ] 5.3 创建数据库迁移文件 `migrations/000116_remove_legacy_commission_fields.up.sql`
  ```sql
  ALTER TABLE tb_iot_card DROP COLUMN IF EXISTS first_commission_paid;
  ALTER TABLE tb_iot_card DROP COLUMN IF EXISTS accumulated_recharge;
  ALTER TABLE tb_device DROP COLUMN IF EXISTS first_commission_paid;
  ALTER TABLE tb_device DROP COLUMN IF EXISTS accumulated_recharge;
  ```
  - 验证：语法正确，在测试数据库上运行成功

- [ ] 5.4 创建回滚迁移文件 `migrations/000116_remove_legacy_commission_fields.down.sql`
  - 添加这四个列回来，使用原始类型（确认类型后填入）
  - 验证：语法正确

- [ ] 5.5 在测试环境执行迁移，确认字段被删除，表结构正确
  - 运行 `migrate -path migrations -database "postgres://..." up`
  - 通过 PostgreSQL MCP 执行以下 SQL 确认字段已不存在：
    ```sql
    SELECT column_name FROM information_schema.columns
    WHERE table_name IN ('tb_iot_card', 'tb_device')
      AND column_name IN ('first_commission_paid', 'accumulated_recharge');
    -- 期望：0 行返回
    ```
  - 验证：迁移成功，查询结果为空

- [ ] 5.6 通过 PostgreSQL MCP 查询 `tb_iot_card` 和 `tb_device` 表结构确认字段已删除
  - 执行 `go build ./cmd/api ./cmd/worker` 确认编译通过
  - 验证：`information_schema.columns` 查询结果中不含 `first_commission_paid`、`accumulated_recharge`

---

## 6. DTO `_name` 字段补全（122 处）

> **分批说明**：122 处分布在多个模块，建议按模块分批提交，每批独立编译验证，避免大批量修改产生难以排查的冲突。推荐顺序：Account → Asset → Order → Commission → IotCard/Device → 其他模块。

- [ ] 6.1 遍历 `internal/model/dto/` 中的所有 DTO 文件，统计所有 int 类型状态字段
  - 使用 `grep -rn "int.*\`json:" internal/model/dto/` 列出候选字段
  - 过滤出缺少配套 `_name`/`_text` 字段的条目，生成模块维度的清单
  - 验证：清单按模块分组，总计不少于 122 条

- [ ] 6.2 **Account 模块**：补全 DTO `_name` 字段 + 常量映射函数 + Service 层赋值
  - 在 `internal/model/dto/account_dto.go` 中为每个 int 状态字段新增 `_name` 字符串字段
  - 在 `pkg/constants/account.go`（或对应文件）中新增 `GetAccountStatusName(status int) string`
  - 在 `internal/service/account/service.go` 中所有构建 Response DTO 的地方赋值 `_name` 字段
  - 验证：`go build ./cmd/api` 通过；`curl /api/admin/accounts/1` 响应包含 `status_name`

- [ ] 6.3 **Asset 模块**：补全 DTO `_name` 字段 + 常量映射函数 + Service 层赋值
  - 涉及文件：`internal/model/dto/asset_dto.go`、`internal/service/asset/service.go`
  - 为 `status`、`network_status`、`activation_status`、`online_status` 等多状态字段各自补全
  - 验证：编译通过；手动调用资产详情接口确认返回 `*_name` 字段

- [ ] 6.4 **Order 模块**：补全 DTO `_name` 字段 + 常量映射函数 + Service 层赋值
  - 涉及文件：`internal/model/dto/order_dto.go`、`internal/service/order/service.go`
  - 覆盖 `payment_status`、`commission_status`、`order_status` 等字段
  - 验证：编译通过；手动调用订单详情接口确认返回 `*_name` 字段

- [ ] 6.5 **Commission 模块**：补全 DTO `_name` 字段 + 常量映射函数 + Service 层赋值
  - 涉及文件：commission 相关 DTO 和 Service
  - 验证：编译通过，手动验证分佣列表接口响应

- [ ] 6.6 **IotCard / Device 模块**：补全 DTO `_name` 字段 + 常量映射函数 + Service 层赋值
  - 涉及文件：`internal/model/dto/iot_card_dto.go`、`internal/model/dto/device_dto.go` 等
  - 验证：编译通过，手动验证相关接口响应

- [ ] 6.7 **剩余模块**（Recharge、Wallet、Package、Shop 等）：按同样模式批量补全
  - 以 6.1 生成的清单为依据，逐模块完成，每模块补完即编译验证一次
  - 验证：所有模块编译通过

- [ ] 6.8 全量编译与静态分析
  - 运行 `go build ./cmd/api ./cmd/worker` 和 `go vet ./internal/service/ ./internal/handler/`
  - 验证：编译通过，vet 无报告

- [ ] 6.9 使用 curl/Postman 抽查各模块代表性接口，确认 `_name` 字段有值且内容正确
  - 示例：`GET /api/admin/accounts/1` 返回 `status_name`；`GET /api/admin/orders/1` 返回 `payment_status_name`
  - 验证：响应符合规范，`_name` 字段非空且为正确中文描述

---

## 7. 未使用常量和错误码清理

- [ ] 7.1 删除 `pkg/errors/codes.go` 中的 `CodeExceedLimit` 常量和对应的错误消息映射
  - 搜索确认 `CodeExceedLimit` 无任何引用
  - 验证：编译通过

- [ ] 7.2 为 `pkg/constants/iot.go` 中约 22 个未引用的预留常量添加注释
  - Replacement/Merchant/Approval/Ladder/CardType 系列
  - 在每个常量上方添加：`// [预留] 用于未来 XXX 功能，见需求文档 YYY`
  - 示例：`// [预留] 用于设备换卡功能，见产品规划 2025Q2`
  - 验证：注释清晰，预留用途明确

- [ ] 7.3 运行 `go build ./cmd/api ./cmd/worker` 和 `go vet ./pkg/errors/ ./pkg/constants/` 确认清理无破坏
  - 验证：编译通过，vet 无报告

---

## 8. 删除空文件和注释代码

- [ ] 8.1 删除 `internal/routes/recharge.go` 空文件
  - 验证：`ls -la internal/routes/recharge.go` 返回 File not found

- [ ] 8.2 删除 `pkg/database/postgres.go` 中的 AutoMigrate 注释块（行 90-95）
  - 验证：编译通过

- [ ] 8.3 保留 `internal/service/client_order/service.go` 中的实名认证检查注释（行 141-142）
  - 在注释上方添加：`// [待确认] 业务需要实名认证检查吗？见 ISSUE #XXX`
  - 验证：编译通过

---

## 9. 空文件清理和编译验证

- [ ] 9.1 运行 `go mod tidy` 确认依赖无变化
  - 验证：`git diff go.mod go.sum` 无内容变化

- [ ] 9.2 运行 `go build ./cmd/api` 和 `go build ./cmd/worker` 确认编译通过
  - 验证：二进制文件生成成功

- [ ] 9.3 运行 `go vet ./...` 进行静态分析
  - 验证：无 vet 错误

- [ ] 9.4 运行 `gofmt -l ./internal ./pkg` 检查代码格式
  - 若有格式问题，运行 `gofmt -w ./internal ./pkg` 修复
  - 验证：无格式问题

---

## 10. 全面测试与集成验证

- [ ] 10.1 运行 `go build ./...` 确认全量编译通过，运行 `go vet ./...` 全量静态分析
  - 验证：编译无错误，vet 无报告

- [ ] 10.2 在测试环境启动 API 服务，运行完整的业务流程测试
  - 测试支付流程（订单支付 → 回调 → 验签）
  - 测试轮询系统（手动触发 → 日志记录 → 状态更新）
  - 测试各模块的 API 响应中是否包含 `_name` 字段
  - 验证：所有流程正常，无错误

- [ ] 10.3 运行 `go vet ./internal/... ./pkg/...` 确认无静态分析问题
  - 验证：vet 无报告，输出为空

- [ ] 10.4 验证数据库迁移（如使用 golang-migrate）
  - 在测试环境运行迁移：`migrate -path migrations -database "..." up`
  - 验证所有迁移成功执行，表结构正确
  - 验证：迁移完成，无错误

- [ ] 10.5 检查 Redis 缓存功能（支付配置加载）
  - 启动 Redis，验证支付配置加载时是否正确使用缓存
  - 修改支付配置后验证缓存清除和重新加载
  - 验证：缓存功能正常

- [ ] 10.6 生成最终的 OpenAPI 文档并审查
  - 运行 `go run cmd/gendocs/main.go`
  - 检查生成的文档是否包含全部 48 个接口
  - 验证：文档完整无误

---

## 11. 代码审查与合并前检查

- [ ] 11.1 检查 commit 历史，确保每个 commit 的 message 清晰且符合规范
  - commit message 格式：`[模块] 功能描述` 或 `fix: 修复内容`
  - 验证：commit 历史清晰

- [ ] 11.2 运行 `git diff main...HEAD` 审查所有代码变更
  - 确认无意外的格式改动或无关的修改
  - 验证：diff 清晰，仅包含计划内的改动

- [ ] 11.3 向团队成员申请代码审查，处理审查意见
  - 验证：至少一人 approve

- [ ] 11.4 确认所有 CI/CD 检查通过（若项目使用）
  - 验证：绿色勾号

---

## 12. 部署与监控

- [ ] 12.1 在预发环境部署最新的代码和数据库迁移
  - 验证：部署成功，服务正常启动

- [ ] 12.2 在预发环境执行烟测用例，验证关键功能
  - 支付流程、轮询系统、账号管理
  - 验证：功能正常

- [ ] 12.3 灰度发布到生产环境（10% → 50% → 100%）
  - 逐步增加流量比例，监控错误率和性能指标
  - 验证：无异常告警

- [ ] 12.4 监控生产环境，查看日志确认无错误
  - 特别关注支付验签相关的日志
  - 验证：`grep -i "error\|failed" logs/app.log` 无相关错误

---

## 13. 最终验收与文档更新

- [ ] 13.1 生成变更总结文档，列出所有删除、添加、修改的代码、文件、字段
  - 验证：文档完整准确

- [ ] 13.2 更新项目 README 或 CHANGELOG，标注此次技术债务清理的内容
  - 验证：文档同步

- [ ] 13.3 向团队分享清理成果和改进点
  - 验证：信息传达完整

---

## 完成标准

**所有上述任务完成后，技术债务清理工作才算完成。具体标准**：

1. ✅ **编译通过**：`go build ./cmd/api ./cmd/worker` 无错误
2. ✅ **测试全 PASS**：`go test -v ./...` 所有测试通过
3. ✅ **文档完整**：OpenAPI 文档覆盖 100%（48/48 Handler），所有接口可查阅
4. ✅ **代码规范**：无 vet 错误，代码格式统一
5. ✅ **功能正常**：支付、轮询、账号管理等核心模块功能无破坏
6. ✅ **数据一致**：数据库迁移成功，废弃字段已删除
7. ✅ **生产稳定**：灰度部署完成，无生产告警或错误
