# Phase 4: 退款 + 支付 + 运营修复 - Discussion Log

> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.

**Date:** 2026-03-28
**Phase:** 04-退款-支付-运营修复
**Areas discussed:** Plan 分组策略, 退款状态机细节, OPS-01 卡/设备 status=4 触发路径, PAY-04 平台代购的具体场景

---

## Plan 分组策略

| Option | Description | Selected |
|--------|-------------|----------|
| REFUND 一个 Plan | DB迁移+Model+Store+Service+Handler+路由全部一次完成 | ✓ |
| REFUND 拆两个 Plan | Plan A: DB迁移+基础层；Plan B: 功能层 | |

**User's choice:** 一个 Plan（推荐）

---

| Option | Description | Selected |
|--------|-------------|----------|
| PAY + OPS 合并一个 Plan | PAY 和 OPS 彼此独立，合并一个 Plan 减少上下文切换 | ✓ |
| PAY 和 OPS 各自独立 | 两个 Plan，粒度更细 | |

**User's choice:** PAY + OPS 合并一个 Plan（推荐）

---

| Option | Description | Selected |
|--------|-------------|----------|
| 内嵌同步（Approve 事务内） | 退款审批和佣金扣减原子，失败可回滚 | ✓ |
| 异步 Asynq 任务 | 入队 TaskTypeRefundCommissionDeduct，审批响应快 | |

**User's choice:** 内嵌同步（推荐）

---

| Option | Description | Selected |
|--------|-------------|----------|
| REFUND-01 先行，再做 REFUND-02+03 | 与 Phase 3 DEVICE 基础层先行规范一致 | ✓ |
| 一次性全部完成 | DB迁移先执行，所有上层代码同时完成 | |

**User's choice:** REFUND-01 先行，再做 REFUND-02+03（推荐）

---

| Option | Description | Selected |
|--------|-------------|----------|
| 不需要，OPS-01 独立实现 | OPS-01 是套餐激活/到期直接更新 status，不经过 StopResumeCallback | ✓ |
| 需要，提前将 CLEAN-06 并入本阶段 | 一并修复 bootstrap 注入 | |

**Notes:** 用户对 CLEAN-06 和 OPS-01 的关系有疑问，确认两者独立后选择不提前。CLEAN-06 保留在 Phase 5。

---

## 退款状态机细节

| Option | Description | Selected |
|--------|-------------|----------|
| 审批人退回 | 审批人退回申请人（信息不全/待补充等），返回 returned 状态，申请人可重提 | ✓ |
| 申请人自撤回 | 申请人主动撤销，运营后可重提 | |

**User's choice:** 审批人退回（推荐）

---

| Option | Description | Selected |
|--------|-------------|----------|
| RF + 时间戳 + 随机数 | 独立生成，不关联订单号 | |
| 基于订单号迭代 | {order_no}-RF{seq}，关联清晰，seq 解决冲突 | ✓ |

**User's choice:** 基于订单号迭代，冲突解法用 {order_no}-RF{seq}

---

| Option | Description | Selected |
|--------|-------------|----------|
| 允许多次退款 | 同一订单可有多条退款记录，审批退款总额不超过实收金额 | |
| 同时只有一个待处理申请 | 上一次退款完成前不允许新申请 | ✓ |

**User's choice:** 同一订单同时只能有一个待处理的退款申请（推荐）

---

| Option | Description | Selected |
|--------|-------------|----------|
| 可修改金额 | 审批人可调整 approved_refund_amount，以此为佣金扣减基准 | ✓ |
| 只能审批全额 | 简化 Approve 逻辑 | |

**User's choice:** 可修改金额（推荐）

---

| Option | Description | Selected |
|--------|-------------|----------|
| amount * approvedRefund / actualReceived（int64） | 先乘后除，禁用 float64 | ✓ |
| 先确认字段含义再决定 | — | |

**Notes:** 用户初始想先确认字段含义。解释了 actual_received_amount（实收）/ approved_refund_amount（审批退款额）以及"先乘后除"的整数精度含义后确认使用 int64 算法。

---

| Option | Description | Selected |
|--------|-------------|----------|
| 是，更新 payment_status=4 | 审批通过同时更新订单状态 | ✓ |
| 不更新订单状态 | 退款表独立记录 | |

**User's choice:** 是，更新 payment_status=4

---

## OPS-01 卡/设备 status=4 触发路径

| Option | Description | Selected |
|--------|-------------|----------|
| 事务内同步更新 | 套餐激活和卡状态更新原子，失败可回滚 | ✓ |
| 异步 goroutine | 套餐激活后异步更新卡/设备状态 | |

**User's choice:** 事务内同步更新（推荐）

---

| Option | Description | Selected |
|--------|-------------|----------|
| activateNextMainPackage 后检查 active/pending count | 在现有到期处理循环内，激活下一个失败时检查剩余套餐数量 | ✓ |
| 独立轮询任务定期检查 | 新建 Asynq 定时任务扫描无 active 套餐的卡/设备 | |

**User's choice:** 调用 activateNextMainPackage 后检查无剩余 active/pending 套餐（推荐）

---

| Option | Description | Selected |
|--------|-------------|----------|
| 分开更新两张表 | 根据 carrierType 分支到 iot_card/device 各自的 Store | ✓ |
| 统一封装 UpdateCarrierStatus() | 新建封装方法处理两种类型 | |

**User's choice:** 分开更新两张表（推荐）

---

| Option | Description | Selected |
|--------|-------------|----------|
| 直接输出 DB 字段，删除所有映射函数 | DB 存的 payment_status 与管理端完全一致（1/2/3/4） | ✓ |
| 先确认 DB 存在的实际值 | 不确定 DB 值是否真的 1/2/3/4 | |

**User's choice:** 是，直接输出 DB 字段，删除所有映射函数

---

## PAY-04 平台代购的具体场景

| Option | Description | Selected |
|--------|-------------|----------|
| 仅 CreateLegacy | 规格书指定 | |
| 仅 CreateAdminOrder | Handler 实际调用的是 CreateAdminOrder，CreateLegacy 已废弃 | ✓ |

**Notes:** 用户指出 CreateLegacy 已废弃，讨论后确认修改目标为 CreateAdminOrder。规格书中的引用有误，这是本次讨论中最重要的一个勘误。

---

| Option | Description | Selected |
|--------|-------------|----------|
| 资产所属代理的钱包（*resourceShopID） | 平台帮代理买，用代理自己的钱包 | ✓ |
| 平台自己的测试钱包 | 平台先垫付 | |

**User's choice:** 资产所属代理的钱包（*resourceShopID）（推荐）

---

| Option | Description | Selected |
|--------|-------------|----------|
| purchaseRole=purchased_by_platform, operatorType=platform | 使用已有枚举 | ✓ |
| purchaseRole=self_purchase | 按代理自购处理 | |

**User's choice:** purchaseRole=purchased_by_platform, operatorType=platform（推荐）

---

| Option | Description | Selected |
|--------|-------------|----------|
| 与代理自购相同（sellerCostPrice=资产所属代理成本价） | 上下级佣金链照常计算 | ✓ |
| sellerCostPrice=0（不计佣金差） | 平台代购无差价 | |

**Notes:** 用户初始对 sellerCostPrice 含义不清楚，解释了佣金差价逻辑后确认：平台代购等同于代理自购，佣金链正常计算。

---

## the Agent's Discretion

- 富友支付 WxPreCreate 调用时的 goodsDesc 字段内容
- 退款 Store 层具体查询方法实现
- status=3/4 更新时的日志内容
- 富友 DTO 字段命名（参考 WechatPayJSAPIResponse 风格）

## Deferred Ideas

None — discussion stayed within phase scope.
