Files
junhong_cmp_fiber/openspec/changes/archive/2026-03-28-add-refund-system/tasks.md
huang f339fb1987
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m8s
fix: 资产钱包自动创建机制 — 修复C端购买时钱包不存在报错
- client_order: 新增 getOrCreateWallet 兜底,钱包不存在时自动创建
- device_import: 设备导入事务内同步创建设备钱包
- iot_card_import: IoT卡批量导入后批量创建卡钱包
- queue/handler: 传递 AssetWalletStore 给两个导入 handler
- migration 000098: 为存量IoT卡和设备补建资产钱包
2026-03-30 11:37:41 +08:00

6.1 KiB
Raw Blame History

任务清单add-refund-system

退款功能从零建设,严格串行:数据模型 → Store → Service → Handler → 路由注册 → 文档。 审批通过后需自动执行资产处理和佣金回扣,涉及跨模块依赖。

任务组 1I-1 数据模型

  • 1.1 新建迁移文件 migrations/000XXX_create_refund_request.up.sql(编号接续现有最大值),创建 tb_refund_request 表,包含所有字段(参考 design.md 表设计,含 shop_id、processor_id、processed_at、reject_reason、remark、asset_reset 等)
  • 1.2 新建对应 .down.sqlDROP TABLE IF EXISTS tb_refund_request;
  • 1.3 新建 internal/model/refund.go,定义 RefundRequest GORM Model使用 gorm.Model + BaseModel 嵌入),实现 TableName() 返回 tb_refund_request,所有字段显式指定 gorm:"column:xxx" 标签,注释使用中文
  • 1.4 在 pkg/constants/ 中新增以下常量:
    • 退款状态:RefundStatusPending=1, RefundStatusApproved=2, RefundStatusRejected=3, RefundStatusReturned=4
    • 交易类型:AgentTransactionTypeCommissionDeduct = "commission_deduct"(在 pkg/constants/wallet.go 的代理钱包交易类型分组中新增)
    • 关联业务类型:ReferenceTypeRefund = "refund"(在 pkg/constants/wallet.go 的关联业务类型分组中新增)
  • 1.5 新建 internal/model/dto/refund_dto.go,定义请求/响应 DTOCreateRefundRequestRejectRefundRequestResubmitRefundRequestApproveRefundRequestReturnRefundRequestRefundListRequestRefundResponseRefundListResponse。所有 DTO 字段需含 description 标签
  • 1.6 执行迁移,通过 DBHub 确认表已创建
  • 1.7 验证:go build ./... 编译通过

任务组 2I-2 接口实现

  • 2.1 新建 internal/store/postgres/refund_store.go,实现 CreateGetByIDList(分页+筛选,含 ApplyShopFilter 数据权限过滤)、Update 方法
  • 2.2 新建 internal/service/refund/service.go,实现 7 个业务方法:CreateListGetByIDApproveRejectReturnResubmit。每个状态变更方法 MUST 使用条件更新 WHERE status = expected 保证幂等。Create 方法 MUST 检查同一订单不能重复退款(已存在 status IN (1,2,4) 的退款记录时拒绝)
  • 2.3 退款单号生成:generateRefundNo() 格式 RF + 年月日时分秒 + 6位随机数参考 GenerateOrderNo 实现
  • 2.4 Create 方法中从关联订单读取 seller_shop_id(或根据 buyer_type/buyer_id 确定)填入退款单的 shop_id 字段
  • 2.5 新建 internal/handler/admin/refund.go,实现 7 个 Handler 方法,遵循 Handler 只做参数解析+响应格式化的原则
  • 2.6 新建 internal/routes/refund.go,注册 /api/admin/refunds 路由组(参考 routes/commission.go 的 Register 模式),路由中间件限制仅平台用户可访问
  • 2.7 在 internal/bootstrap/stores.goservices.gohandlers.go 中注册 refund 模块:
    • Store: RefundStore
    • Service 依赖注入: db, refundStore, orderStore, commissionRecordStore, agentWalletStore, agentWalletTransactionStore, stopResumeService, deviceService, packageActivationService, iotCardStore, deviceStore, assetWalletStore
    • Handler: RefundHandler
  • 2.8 在 internal/routes/routes.go 中调用 registerRefundRoutes
  • 2.9 更新 cmd/api/docs.gocmd/gendocs/main.go,注册退款 Handler
  • 2.10 验证:go build ./... 编译通过

任务组 3I-3 佣金全额回扣

  • 3.1 在 internal/service/refund/service.go 中新增私有方法 deductAllCommission(ctx, refundID)
  • 3.2 实现回扣逻辑:查退款单 → 查该订单所有已入账佣金记录(WHERE order_id=? AND status=1,使用 CommissionStatusReleased 常量)→ 对每条记录全额扣减对应代理的佣金钱包(wallet_type="commission",使用 GetCommissionWallet)→ 使用乐观锁更新余额(WHERE version=?)→ 创建交易流水(transaction_type="commission_deduct", reference_type="refund", reference_id=退款单ID)→ 全部完成后标记 commission_deducted=true
  • 3.3 在 Approve() 方法中,事务提交成功后通过 Goroutine 调用 deductAllCommission
  • 3.4 回扣失败记录 Error 日志(含 refund_id 和 error不影响审批结果
  • 3.5 验证:go build ./... 编译通过

任务组 4I-4 资产处理

  • 4.1 在 internal/service/package/activation_service.go 中新增公开方法 InvalidateAllPackagesByAsset(ctx, assetType string, assetID uint) error:查询 status IN (0,1,2) 的套餐使用记录(按 iot_card_iddevice_id 查),批量更新为 status=4(已失效)
  • 4.2 在 internal/service/refund/service.go 中新增私有方法 handleAssetReset(ctx, refundID),包含:
    • a. 查退款单和关联订单,确定资产类型(单卡/设备和资产ID
    • b. 调用 InvalidateAllPackagesByAsset 失效所有套餐
    • c. 停机:单卡调 stopResumeService.ManualStopCard(iccid) / 设备调 deviceService.StopDevice(deviceID)
    • d. 世代重置(参照 exchange/service.go 第 278-305 行):generation+1asset_status=1、清零累计充值字段、清理个人客户绑定、删除旧资产钱包并创建新空钱包
    • e. 更新订单 payment_status=4(已退款)
    • f. 标记退款单 asset_reset=true
  • 4.3 在 Approve() 方法中,事务提交成功后通过 Goroutine 调用 handleAssetReset(与佣金回扣独立执行)
  • 4.4 资产处理失败记录 Error 日志(含 refund_id 和 errorasset_reset 保持 false,不影响审批结果
  • 4.5 验证:go build ./... 编译通过

收尾验证

  • 5.1 执行 go build ./...,确认全量编译通过
  • 5.2 通过 DBHub 确认 tb_refund_request 表结构正确
  • 5.3 通过 rg "refund" internal/bootstrap/ 确认模块已注册
  • 5.4 通过 rg "commission_deduct" pkg/constants/ 确认新常量已添加
  • 5.5 通过 rg "InvalidateAllPackagesByAsset" internal/service/package/ 确认新方法已添加