13 KiB
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: 轮询状态常量提取
发现和注意点
-
alert_service.go 的 "pending" 只有 1 处:
NotificationStatus: "pending"在triggerAlert()函数中,其他字符串如"sent"、"partial"、"failed"是通知发送状态(非轮询触发日志状态),不在替换范围内。 -
Store 层 SQL 字符串处理:
polling_manual_trigger_store.go中GetRunning函数原本使用 SQL 字面量status IN ('pending', 'processing'),改为 GORM 参数化查询status IN (?, ?)并传入常量,既消除硬编码又提升安全性。 -
Store 层需要新增 import:
polling_manual_trigger_store.go原本未导入constants包,添加时注意 Go import 分组规范(stdlib / 第三方 / 内部包)。 -
constants 包在 handler 文件已存在:
polling_manual_trigger.go已导入pkg/constants,无需额外修改 import。 -
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:645→TransactionTypeWithdrawal替换为AgentTransactionTypeWithdrawalrecharge/service.go:91,94,417,418→RechargeMinAmount/RechargeMaxAmount替换为AssetRechargeMinAmount/AssetRechargeMaxAmount(资产钱包充值场景,最小1元/最大100000元)
CheckAndStopCard 无法删除
stop_resume_service.go 中的 CheckAndStopCard 在接口 StopResumeServiceInterface(usage_service.go:20)中声明,并在 package_activation_handler.go(327、347行)和 usage_service.go(230、255行)中活跃调用。任务描述要求"确认无调用后再删除",此方法有调用,故跳过删除。
grep 验证注意事项
SyncHandler的 grep 命中了CommissionStatsSyncHandler(合法),不是废弃的 DataSync handler。CardWallet出现在data_scope.go:82的注释字符串里(非代码引用),不需要修改。
删除的代码统计
sync.go整个文件(166行)handler.go3行(syncHandler var + HandleFunc + logger.Info)constants.go1行(TaskTypeDataSync)wallet.go86行(149-234行废弃别名块)enterprise_card_authorization_dto.go22行(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 个 Handler:ClientAuth、AdminAuth、AssetLifecycle,它们在bootstrap/types.go中有定义,但从未加入文档生成器AdminAuth在docs.go和gendocs/main.go中完全未被设置(连手动覆写都没有),这意味着 Admin Auth 相关路由一直缺失于 OpenAPI 文档docs.go和gendocs/main.go的手动覆写列表(9 行handlers.Xxx = ...)中,大多数 handler 其实已经在BuildDocHandlers()中了,属于纯冗余
操作
pkg/openapi/handlers.go:按 types.go 字段顺序插入ClientAuth(PersonalCustomer 后)、AdminAuth(ShopRole 后)、AssetLifecycle(Asset 后)cmd/api/docs.go:删除所有手动覆写行(9 行)+ 移除admin和apphandlerimportcmd/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.goline 576-577:DeviceResponse构建中有引用(计划中未提及)internal/service/recharge/service.goline 31:ForceRechargeRequirementstruct 有FirstCommissionPaid字段internal/service/recharge/service.goline 458:result.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/下可以 importinternal/包(项目中已有先例:pkg/wechat/config.goimportinternal/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/ListShopResponses(3处) | 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 更新 |
关键发现
CommissionRecordResponse.statusdescription 原先错误(写的 "1:已入账" 但常量是 "1:已冻结"),已修正为正确值ClientRechargeInfo.Status使用的是rechargeStatusToClientStatus()转换后的值(0/1/2),与 RechargeStatus* 常量不同- 部分 DTO(MyPackage、CommissionRecord)未被 service 层引用,仅更新 DTO 定义
DeviceResponse和ImportTaskResponse已经有status_name和status_text字段,无需修改
验证结果
- ✅
go build ./cmd/api ./cmd/worker通过,无任何错误