Files
junhong_cmp_fiber/openspec/changes/tech-debt-cleanup/tasks.md
huang 42c5ec912f
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m13s
feat: 技术债务清理(支付配置动态化、API文档补全、轮询常量提取、废弃代码清理)
2026-04-14 11:11:15 +08:00

21 KiB
Raw Blame History

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

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.sqlDROP 所有表)

    • 按依赖顺序(子表先删、主表后删)逐一 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_namedescription 标识)
    • purchase_role 回填不需要 rollbackDOWN 时留空注释说明原因即可)
    • 验证:执行后轮询初始配置被清除
  • 0.6 归档旧迁移文件

    • 创建 migrations/archive/ 目录
    • 000000 ~ 000113 全部移入 migrations/archive/(包含 .up.sql.down.sql
    • 将游离文件 backfill_order_purchase_role.sql 也移入 migrations/archive/
    • 验证:migrations/ 目录下只剩 000114000115(以及后续 000116 废弃字段迁移)
  • 0.7 编写测试环境重置脚本 scripts/reset_db.sh

    #!/bin/bash
    # 重置数据库到干净状态(仅用于开发/测试环境)
    DB_NAME="${JUNHONG_DATABASE_DBNAME:-junhong_cmp}"
    DB_DSN="postgres://..."
    
    echo "警告:即将清空数据库 $DB_NAME5 秒后继续..."
    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.wechatPayments.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 中新增轮询手动触发日志状态常量

    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 批量替换
    • 示例:WalletTypeMainAgentWalletTypeMain
    • 验证:编译通过,无引用残留
  • 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 类型

    • DeviceBundleDeviceBundleCardAllocatedDevice
    • 验证:全局搜索确认无引用,编译通过
  • 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 中删除 FirstCommissionPaidAccumulatedRecharge 字段定义

    • 验证:编译通过
  • 5.2 从 internal/model/device.go 中删除 FirstCommissionPaidAccumulatedRecharge 字段定义

    • 验证:编译通过
  • 5.3 创建数据库迁移文件 migrations/000116_remove_legacy_commission_fields.up.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 确认字段已不存在:
      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_cardtb_device 表结构确认字段已删除

    • 执行 go build ./cmd/api ./cmd/worker 确认编译通过
    • 验证:information_schema.columns 查询结果中不含 first_commission_paidaccumulated_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.gointernal/service/asset/service.go
    • statusnetwork_statusactivation_statusonline_status 等多状态字段各自补全
    • 验证:编译通过;手动调用资产详情接口确认返回 *_name 字段
  • 6.4 Order 模块:补全 DTO _name 字段 + 常量映射函数 + Service 层赋值

    • 涉及文件:internal/model/dto/order_dto.gointernal/service/order/service.go
    • 覆盖 payment_statuscommission_statusorder_status 等字段
    • 验证:编译通过;手动调用订单详情接口确认返回 *_name 字段
  • 6.5 Commission 模块:补全 DTO _name 字段 + 常量映射函数 + Service 层赋值

    • 涉及文件commission 相关 DTO 和 Service
    • 验证:编译通过,手动验证分佣列表接口响应
  • 6.6 IotCard / Device 模块:补全 DTO _name 字段 + 常量映射函数 + Service 层赋值

    • 涉及文件:internal/model/dto/iot_card_dto.gointernal/model/dto/device_dto.go
    • 验证:编译通过,手动验证相关接口响应
  • 6.7 剩余模块Recharge、Wallet、Package、Shop 等):按同样模式批量补全

    • 以 6.1 生成的清单为依据,逐模块完成,每模块补完即编译验证一次
    • 验证:所有模块编译通过
  • 6.8 全量编译与静态分析

    • 运行 go build ./cmd/api ./cmd/workergo vet ./internal/service/ ./internal/handler/
    • 验证编译通过vet 无报告
  • 6.9 使用 curl/Postman 抽查各模块代表性接口,确认 _name 字段有值且内容正确

    • 示例:GET /api/admin/accounts/1 返回 status_nameGET /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/workergo 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/apigo 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. 测试全 PASSgo test -v ./... 所有测试通过
  3. 文档完整OpenAPI 文档覆盖 100%48/48 Handler所有接口可查阅
  4. 代码规范:无 vet 错误,代码格式统一
  5. 功能正常:支付、轮询、账号管理等核心模块功能无破坏
  6. 数据一致:数据库迁移成功,废弃字段已删除
  7. 生产稳定:灰度部署完成,无生产告警或错误