# 任务清单：add-refund-system

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

## 任务组 1：I-1 数据模型

- [x] 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 等）
- [x] 1.2 新建对应 `.down.sql`：`DROP TABLE IF EXISTS tb_refund_request;`
- [x] 1.3 新建 `internal/model/refund.go`，定义 `RefundRequest` GORM Model（使用 `gorm.Model` + `BaseModel` 嵌入），实现 `TableName()` 返回 `tb_refund_request`，所有字段显式指定 `gorm:"column:xxx"` 标签，注释使用中文
- [x] 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` 的关联业务类型分组中新增）
- [x] 1.5 新建 `internal/model/dto/refund_dto.go`，定义请求/响应 DTO：`CreateRefundRequest`、`RejectRefundRequest`、`ResubmitRefundRequest`、`ApproveRefundRequest`、`ReturnRefundRequest`、`RefundListRequest`、`RefundResponse`、`RefundListResponse`。所有 DTO 字段需含 `description` 标签
- [x] 1.6 执行迁移，通过 DBHub 确认表已创建
- [x] 1.7 验证：`go build ./...` 编译通过

## 任务组 2：I-2 接口实现

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

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

- [x] 3.1 在 `internal/service/refund/service.go` 中新增私有方法 `deductAllCommission(ctx, refundID)`
- [x] 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`
- [x] 3.3 在 `Approve()` 方法中，事务提交成功后通过 Goroutine 调用 `deductAllCommission`
- [x] 3.4 回扣失败记录 Error 日志（含 refund_id 和 error），不影响审批结果
- [x] 3.5 验证：`go build ./...` 编译通过

## 任务组 4：I-4 资产处理

- [x] 4.1 在 `internal/service/package/activation_service.go` 中新增公开方法 `InvalidateAllPackagesByAsset(ctx, assetType string, assetID uint) error`：查询 `status IN (0,1,2)` 的套餐使用记录（按 `iot_card_id` 或 `device_id` 查），批量更新为 `status=4`（已失效）
- [x] 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+1`、`asset_status=1`、清零累计充值字段、清理个人客户绑定、删除旧资产钱包并创建新空钱包
  - e. 更新订单 `payment_status=4`（已退款）
  - f. 标记退款单 `asset_reset=true`
- [x] 4.3 在 `Approve()` 方法中，事务提交成功后通过 Goroutine 调用 `handleAssetReset`（与佣金回扣独立执行）
- [x] 4.4 资产处理失败记录 Error 日志（含 refund_id 和 error），`asset_reset` 保持 `false`，不影响审批结果
- [x] 4.5 验证：`go build ./...` 编译通过

## 收尾验证

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