Files
junhong_cmp_fiber/.sisyphus/notepads/tech-debt-cleanup/learnings.md

13 KiB
Raw Blame History

tech-debt-cleanup 学习记录

[2026-04-14] 初始化

项目规范关键点

  • 所有注释必须使用中文
  • Redis Key 必须通过函数生成(格式:Redis{Module}{Purpose}Key(params...)
  • 错误码必须定义在 pkg/errors/codes.go
  • 常量定义在 pkg/constants/
  • 架构Handler → Service → Store → Model
  • 禁止使用外键约束
  • 禁止 Go 测试文件(*_test.go

关键文件路径

  • 支付服务: internal/service/order/service.go, internal/service/recharge/service.go
  • 轮询服务: internal/service/polling/manual_trigger_service.go, alert_service.go
  • 轮询 Handler: internal/handler/admin/polling_manual_trigger.go
  • 轮询 Store: internal/store/postgres/polling_manual_trigger_store.go
  • 文档生成: cmd/api/docs.go, cmd/gendocs/main.go
  • Redis Key: pkg/constants/redis.go
  • 支付包: pkg/payment/
  • 废弃 DTO: internal/model/dto/enterprise_card_authorization_dto.go
  • 废弃任务: internal/task/sync.go
  • 空文件: internal/routes/recharge.go
  • Model: internal/model/iot_card.go, internal/model/device.go

[2026-04-14] Task 3: 轮询状态常量提取

发现和注意点

  1. alert_service.go 的 "pending" 只有 1 处NotificationStatus: "pending"triggerAlert() 函数中,其他字符串如 "sent""partial""failed" 是通知发送状态(非轮询触发日志状态),不在替换范围内。

  2. Store 层 SQL 字符串处理polling_manual_trigger_store.goGetRunning 函数原本使用 SQL 字面量 status IN ('pending', 'processing'),改为 GORM 参数化查询 status IN (?, ?) 并传入常量,既消除硬编码又提升安全性。

  3. Store 层需要新增 importpolling_manual_trigger_store.go 原本未导入 constants 包,添加时注意 Go import 分组规范stdlib / 第三方 / 内部包)。

  4. constants 包在 handler 文件已存在polling_manual_trigger.go 已导入 pkg/constants,无需额外修改 import。

  5. manual_trigger_service.go 共 6 处替换3 处 "processing"triggerLog 创建、2 处 "completed"UpdateStatus 调用、1 处同时含 "pending" + "processing" + "cancelled" 的 CancelTrigger 状态检查。

[2026-04-14] Task 4: 废弃代码清理

废弃别名实际引用情况

扫描 internal/pkg/ 后,真正使用废弃别名的只有 3 处(其他文件早已用新常量):

  • shop_commission/service.go:645TransactionTypeWithdrawal 替换为 AgentTransactionTypeWithdrawal
  • recharge/service.go:91,94,417,418RechargeMinAmount/RechargeMaxAmount 替换为 AssetRechargeMinAmount/AssetRechargeMaxAmount资产钱包充值场景最小1元/最大100000元

CheckAndStopCard 无法删除

stop_resume_service.go 中的 CheckAndStopCard 在接口 StopResumeServiceInterfaceusage_service.go:20)中声明,并在 package_activation_handler.go327、347行usage_service.go230、255行中活跃调用。任务描述要求"确认无调用后再删除",此方法有调用,故跳过删除。

grep 验证注意事项

  • SyncHandler 的 grep 命中了 CommissionStatsSyncHandler(合法),不是废弃的 DataSync handler。
  • CardWallet 出现在 data_scope.go:82 的注释字符串里(非代码引用),不需要修改。

删除的代码统计

  • sync.go 整个文件166行
  • handler.go 3行syncHandler var + HandleFunc + logger.Info
  • constants.go 1行TaskTypeDataSync
  • wallet.go 86行149-234行废弃别名块
  • enterprise_card_authorization_dto.go 22行DeviceBundle、DeviceBundleCard、AllocatedDevice
  • order/service.go 约243行CreateLegacy 方法)

[2026-04-14] Task 7+8: 常量清理和空文件删除

执行内容

Task 7.1:删除 CodeExceedLimit 错误码

  • 删除常量定义:CodeExceedLimit = 1055 (line 68)
  • 删除 allErrorCodes 列表条目 (line 227)
  • 删除 errorMessages 映射条目 (line 359)
  • 验证grep 确认代码库中无任何引用

Task 7.2:为预留常量添加 [预留] 注释

  • LadderType 系列(激活量、提货量、充值量):添加 [预留] 用于分佣阶梯功能,待产品规划
  • ApprovalType/ApprovalStatus 系列(审批类型/状态):添加 [预留] 用于审批流程功能,待产品规划
  • MerchantType 系列(支付宝、微信、银行卡):添加 [预留] 用于未来商户管理功能,待产品规划
  • ReplacementStatus 系列(换卡申请状态):添加 [预留] 用于换卡申请功能,待产品规划
  • ReplacementReason 系列(换货原因):添加 [预留] 用于换卡原因管理功能,待产品规划

Task 8.1:删除空文件

  • 删除 internal/routes/recharge.go(仅包含 package routes

Task 8.2:删除 postgres.go 的 AutoMigrate 注释块

  • 删除 lines 90-95 的注释掉的 AutoMigrate 代码块

Task 8.3:更新 client_order/service.go 注释

  • 在 line 140 的实名认证检查注释前添加:// [待确认] 业务是否需要实名认证检查?参见 tech-debt-cleanup 提案

验证结果

  • go build ./cmd/api ./cmd/worker 通过
  • go vet ./pkg/errors/ ./pkg/constants/ ./pkg/database/ 无报告
  • grep -r "CodeExceedLimit" 无结果(完全删除)
  • 所有预留常量已添加 [预留] 标记和产品规划说明

[2026-04-14] Task 2: API 文档生成器补全

发现

  • BuildDocHandlers() 缺少 3 个 HandlerClientAuthAdminAuthAssetLifecycle,它们在 bootstrap/types.go 中有定义,但从未加入文档生成器
  • AdminAuthdocs.gogendocs/main.go完全未被设置(连手动覆写都没有),这意味着 Admin Auth 相关路由一直缺失于 OpenAPI 文档
  • docs.gogendocs/main.go 的手动覆写列表9 行 handlers.Xxx = ...)中,大多数 handler 其实已经在 BuildDocHandlers() 中了,属于纯冗余

操作

  1. pkg/openapi/handlers.go:按 types.go 字段顺序插入 ClientAuthPersonalCustomer 后)、AdminAuthShopRole 后)、AssetLifecycleAsset 后)
  2. cmd/api/docs.go删除所有手动覆写行9 行)+ 移除 adminapphandler import
  3. cmd/gendocs/main.go:同上

注意点

  • admin.NewAuthHandler 需要 2 个 nil 参数authService + validator
  • app.NewClientAuthHandler 需要 2 个 nil 参数service + logger
  • admin.NewAssetLifecycleHandler 只需 1 个 nil 参数service 接口)
  • 清理完 import 后 go build + go vet 均通过,无报错

[2026-04-14] Task 5: Model 废弃字段清理

引用情况(超出预期的额外文件)

计划中列出的文件之外,还发现了额外引用:

  • internal/service/device/service.go line 576-577DeviceResponse 构建中有引用(计划中未提及)
  • internal/service/recharge/service.go line 31ForceRechargeRequirement struct 有 FirstCommissionPaid 字段
  • internal/service/recharge/service.go line 458result.FirstCommissionPaid = firstCommissionPaid 赋值

关键判断recharge/service.go 的 firstCommissionPaid 变量

checkForceRechargeRequirement 中的 firstCommissionPaid 变量不是废弃字段

  • 它读自 card.IsFirstRechargeTriggeredBySeries(*seriesID)device.IsFirstRechargeTriggeredBySeries(*seriesID)(均是新 BySeries 方法)
  • 该变量用于 if firstCommissionPaid { 的业务判断(已发放则跳过强充检查)
  • 正确做法:只删 ForceRechargeRequirement.FirstCommissionPaid 字段 + result.FirstCommissionPaid = firstCommissionPaid 赋值,保留变量声明和 if 判断

迁移文件

  • 新建 migrations/000116_remove_legacy_commission_fields.up.sql
  • 新建 migrations/000116_remove_legacy_commission_fields.down.sql

验证结果

  • go build ./cmd/api ./cmd/worker 通过,无任何错误

[2026-04-14] Task 1: 支付配置动态加载

实现架构决策

1. PaymentConfigLoader 位置

  • 新建 pkg/payment/loader.go(包名 payment
  • pkg/ 下可以 import internal/ 包(项目中已有先例:pkg/wechat/config.go import internal/model

2. 缓存策略

  • 不缓存 Payment 实例(含连接/状态,不可序列化)
  • 缓存 WechatConfig JSON 数据key: payment:config:{id}TTL: 1 小时)
  • 每次从缓存数据构建 Payment 实例(轻量操作,无 I/O

3. v2 适配器模式

  • PaymentV2Service 未实现完整 PaymentServiceInterface(缺少 H5/查单/关单/HandlePaymentNotify
  • 通过 v2PaymentAdapter 包装,补全缺失方法(返回"不支持"错误)
  • 不修改 payment_v2.go 原文件

4. appID 来源

  • NewPaymentAppFromConfig / NewPaymentV2ServiceFromConfig 均需要 appID 参数
  • 使用 wechatConfig.OaAppID(公众号 AppID

5. wechatCache 共享

  • loader 在构造时通过 wechat.NewRedisCache(rdb) 创建一次,所有 LoadConfig 调用复用
  • v3 Payment 实例构建需要此 cache用于 PowerWeChat SDK 内部 token 缓存)

6. worker_services.go 也需更新

  • internal/bootstrap/worker_services.go 里有另一处 orderSvc.New 调用(超时取消专用)
  • 需额外传 nil 占位

关键文件变更

文件 变更内容
pkg/constants/redis.go +RedisPaymentConfigKey
pkg/payment/loader.go 新建:接口 + 实现 + v2 适配器
internal/service/order/service.go +paymentLoader 字段/参数,替换 2 处 TODO
internal/service/recharge/service.go +paymentLoader 字段/参数,更新 1 处 TODO 注释
internal/bootstrap/services.go 创建 loader 实例,传入 Order/Recharge 构造函数
internal/bootstrap/worker_services.go orderSvc.New 补传 nil

验证结果

  • go build ./cmd/api ./cmd/worker 通过,无任何错误
  • go vet 无报告

[2026-04-14] Task 6: DTO _name 字段补全

概述

为所有 Response DTO 中的 int 类型状态字段补全对应 string 类型 _name 字段,并在 Service 层赋值。

常量映射函数(新增)

pkg/constants/constants.go

  • GetStatusName(status int) string — 通用 0=禁用/1=启用
  • GetShelfStatusName(status int) string — 1=上架/2=下架
  • GetExchangeStatusName(status int) string — 换货单状态

pkg/constants/iot.go

  • GetIotCardStatusName — 1=在库, 2=已分销, 3=已激活, 4=已停用
  • GetActivationStatusName — 0=未激活, 1=已激活
  • GetRealNameStatusName — 0=未实名, 1=已实名
  • GetNetworkStatusName — 0=停机, 1=开机
  • GetCommissionRecordStatusName — 1=已冻结/2=解冻中/3=已发放/4=已失效/99=待人工修正
  • GetOrderPaymentStatusName — 1=待支付/2=已支付/3=已取消/4=已退款
  • GetOrderCommissionStatusName — 1=待计算/2=已计算
  • GetRefundStatusName — 1=待审批/2=已通过/3=已拒绝/4=已退回

模块处理情况

模块 DTO 改动 Service 赋值位置 备注
Account AccountResponse + status_name toAccountResponse() constants 已导入
Shop ShopResponse + status_name Create/Update/ListShopResponses3处 constants 已导入
IotCard StandaloneIotCardResponse + 4个 _name 字段 toStandaloneResponse() status/activation/realname/network
Order OrderResponse + commission_status_name buildOrderResponse() payment_status 已有 _text 字段
Package PackageResponse + status_name/shelf_status_name toResponse() 末尾agent 覆盖后赋值) my_package.go 也同步更新(不涉及 service
Refund RefundResponse + status_name buildRefundResponse() constants 已导入
Device DeviceCardBindingResponse + status_name device/binding.go 新增 constants import
Asset AssetResolveResponse + status_name buildDeviceResolveResponse + buildCardResolveResponse constants 已导入
Commission CommissionRecordResponse + status_name 未找到 service 调用DTO 未被使用ShopCommissionRecordItem 已有 status_name 仅 DTO 更新;同时修正了 description 错误
Client Asset AssetInfoResponse + 4个 _name 字段 handler client_asset.go(直接赋值) constants 已导入
Client Order ClientOrderInfo/ListItem/DetailResponse + payment_status_name; ClientRechargeInfo + status_name client_order/service.go 增加 clientRechargeStatusName 辅助函数
MyPackage MyPackageResponse/DetailResponse + status_name/shelf_status_name; MySeriesAllocationResponse + status_name 未找到 service 调用,仅 DTO 更新

关键发现

  1. CommissionRecordResponse.status description 原先错误(写的 "1:已入账" 但常量是 "1:已冻结"),已修正为正确值
  2. ClientRechargeInfo.Status 使用的是 rechargeStatusToClientStatus() 转换后的值0/1/2与 RechargeStatus* 常量不同
  3. 部分 DTOMyPackage、CommissionRecord未被 service 层引用,仅更新 DTO 定义
  4. DeviceResponseImportTaskResponse 已经有 status_namestatus_text 字段,无需修改

验证结果

  • go build ./cmd/api ./cmd/worker 通过,无任何错误