Files
junhong_cmp_fiber/.planning/phases/04-refund-pay-ops/04-CONTEXT.md
huang e81d3a58ad
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m6s
docs(04): capture phase context
2026-03-28 14:24:25 +08:00

165 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 4: 退款 + 支付 + 运营修复 - Context
**Gathered:** 2026-03-28
**Status:** Ready for planning
<domain>
## Phase Boundary
实现完整退款流程REFUND-01~03、补全富友支付 JSAPI/小程序接口PAY-01~04、修复运营逻辑断点OPS-01~02。共 9 个需求:
- **REFUND**:新功能模块(退款申请→审批→佣金回扣)
- **PAY**:留桩替换(富友支付 JSAPI/小程序,平台代购场景修复)
- **OPS**:业务逻辑修复(卡/设备 status 3/4 触发、payment_status 枚举统一)
</domain>
<decisions>
## Implementation Decisions
### Plan 分组策略
- **D-01:** 拆分为 **2 个 Plan**
- **Plan 1REFUND**REFUND-01 先行DB 迁移 + Model + Store然后串行完成 REFUND-027 个接口)+ REFUND-03佣金扣减逻辑嵌入 Approve
- **Plan 2PAY + OPS 合并)**PAY-01~04 + OPS-01~02 共 6 个需求,两者互相独立可并行编排
- **D-02:** REFUND-01 必须先于 REFUND-02/03 执行DB 迁移和模型是所有上层代码的基础)
- **D-03:** 每个子任务独立一个 commit延续 Phase 1/2/3 粒度规范)
### 退款状态机REFUND
- **D-04:** 退款单号生成规则:`{order_no}-RF{seq}`(同一订单第 1 次退款为 RF1第 2 次为 RF2以此类推。Create 时查询该订单现有退款数量 +1 作为序号
- **D-05:** 同一订单同时只允许一个"待处理"状态的退款申请status=pending/returned防止重复退款
- **D-06:** 退款状态流转:
- `pending``approved`(审批通过,触发佣金扣减 + 更新订单 payment_status=4
- `pending``rejected`(审批拒绝,必填拒绝原因)
- `pending``returned`(审批人退回,申请人可重提)
- `returned``pending`(申请人 Resubmit 重提)
- **D-07:** **Return退回由审批人操作**,退回后状态变为 `returned`,申请人可调用 Resubmit 重新提交变回 `pending`
- **D-08:** **审批人可修改退款金额**approved_refund_amount 可与 requested_refund_amount 不同)。佣金扣减以 approved_refund_amount 为准
- **D-09:** 审批通过后**同步更新订单 payment_status = 4**(已退款),与 OPS-02 的 payment_status 枚举统一对齐
### 退款佣金扣减REFUND-03
- **D-10:** 佣金扣减**内嵌在 Approve() 事务内同步执行**(不走 Asynq 异步任务)。原子性:退款审批通过、佣金扣减、订单状态更新在同一事务中完成,失败可回滚
- **D-11:** 佣金扣减算法(**禁用 float64使用整数算术**
```go
deductAmount := commission.Amount * refund.ApprovedRefundAmount / refund.ActualReceivedAmount
```
先乘后除,全程 int64防止整数除法精度丢失。对该订单所有佣金记录逐一计算扣减额写负向交易流水
- **D-12:** 佣金扣减允许余额变为负数(规格书明确:允许为负数)
### 富友支付PAY
- **D-13:** `FuiouPayJSAPI` 和 `FuiouPayMiniApp` 函数签名需从 `error` 改为 `(*dto.FuiouPayJSAPIResponse, error)`,参考 `WechatPayJSAPI` 的返回结构
- **D-14:** 新建 `FuiouPayJSAPIResponse` DTO字段从 `pkg/fuiou/types.go` 的 `WxPreCreateResponse` SDK 字段映射SdkAppid, SdkTimestamp, SdkNoncestr, SdkPrepayid, SdkPackage, SdkSigntype, SdkPaysign与微信官方 `wx.requestPayment()` 参数对齐
- **D-15:** JSAPI公众号tradeType = `JSAPI`;小程序 tradeType = `LETPAY`(两者逻辑相同,仅此字段不同)
- **D-16:** 已有富友 SDK 实现(`pkg/fuiou/client.go` + `wxprecreate.go`),直接调用 `client.WxPreCreate()` 方法
### 平台代理钱包代购PAY-04
- **D-17:** 修改 **`CreateAdminOrder`****不是 CreateLegacy**CreateLegacy 已废弃Handler 实际调用的是 CreateAdminOrder
- **D-18:** 触发条件:`buyerType == ""` && `resourceShopID != nil`(平台用户在 Handler 层传入 buyerType="" buyerID=0
- **D-19:** 钱包和成本价均来自**资产所属代理(*resourceShopID**的钱包和成本价(等同于代理自购场景)
- **D-20:** 订单标记:`purchaseRole = purchased_by_platform``operatorType = "platform"`
- **D-21:** `sellerCostPrice = resourceShop 代理的成本价`(与代理自购相同,上下级佣金链照常计算)
### 卡/设备 status 3/4 触发OPS-01
- **D-22:** **status=3已激活**:在 `activation_service.go` 套餐激活事务内同步更新卡/设备表的 status=3不走 goroutine保证原子性
- **D-23:** **status=4已停用**:在 `activation_service.go` 的套餐到期检测逻辑中,调用 `activateNextMainPackage` 后,若无法找到下一个待激活套餐(即无剩余 active/pending 套餐),则同步更新卡/设备 status=4
- **D-24:** 卡和设备**分开更新两张表**tb_iot_card 和 tb_device根据 carrierType 分支处理,无需统一封装
- **D-25:** status 更新在已有套餐激活/到期事务内完成,失败可回滚
### payment_status 枚举统一OPS-02
- **D-26:** **删除所有 C 端映射函数**
- `internal/service/client_order/service.go` 中的 `orderStatusToClientStatus()`
- `internal/handler/app/client_order.go` 中的 `orderStatusToClientPaymentStatus()` 和 `clientPaymentStatusToOrderStatus()`
- **D-27:** C 端直接输出 DB 原始 `payment_status` 字段1=待支付, 2=已支付, 3=已取消, 4=已退款),与管理端完全一致
### the Agent's Discretion
- 富友支付 `WxPreCreate` 调用时的 `goodsDesc` 字段内容(可从 order.OrderNo 或套餐名称组合)
- 退款 Store 层具体查询方法(参考现有 commission_withdrawal_request_store.go 风格)
- status=3/4 更新时的日志内容(按项目中文日志规范)
- `getCostPrice` 函数参数(已有现成封装,直接复用)
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### 修复规格(核心参考)
- `.sisyphus/plans/修正业务-完整方案.md` — 完整修复规格书:
- 方案 IREFUND-01~03第 1716 行起退款表结构、7 个接口清单、佣金扣减逻辑)
- 方案 JPAY + OPS第 1862 行起J-1/J-2/J-3/J-4 各子方案代码片段、人工验收清单)
- J-1富友支付 JSAPI/小程序PAY-01/02/03
- J-2平台钱包代购PAY-04—— **注意:规格书错误引用了 CreateLegacy已废弃实际修改目标是 CreateAdminOrder**
- J-3status 3/4 触发逻辑OPS-01
- J-4payment_status 枚举统一OPS-02
### 核心修改文件
- `internal/service/order/service.go` — FuiouPayJSAPI/FuiouPayMiniApp 留桩替换CreateAdminOrder 钱包支付 platform 分支PAY
- `pkg/fuiou/client.go` + `pkg/fuiou/wxprecreate.go` — 富友 SDK直接调用 WxPreCreate
- `pkg/fuiou/types.go` — WxPreCreateResponse 字段FuiouPayJSAPIResponse DTO 来源)
- `internal/service/package/activation_service.go` — 套餐激活/到期逻辑OPS-01 插入点)
- `internal/service/client_order/service.go` — orderStatusToClientStatus() 删除OPS-02
- `internal/handler/app/client_order.go` — orderStatusToClientPaymentStatus() / clientPaymentStatusToOrderStatus() 删除OPS-02
### 参考结构REFUND 设计参照)
- `internal/service/commission_withdrawal/service.go` — Approve/Reject 风格参考REFUND-02 接口设计)
- `internal/model/dto/commission_withdrawal_dto.go` — DTO 字段风格参考REFUND DTO 设计)
- `internal/store/postgres/commission_withdrawal_request_store.go` — Store 层风格参考REFUND Store
### 依赖项与约束
- `.planning/REQUIREMENTS.md` — REFUND-01~03, PAY-01~04, OPS-01~02 完整需求定义
- `internal/handler/admin/order.go` — PAY-04Handler 层传入 buyerType="" buyerID=0 的逻辑(了解上下游)
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- `pkg/fuiou/client.go:WxPreCreate()` — 富友预下单接口PAY-01/02 直接调用,无需重写
- `internal/service/order/service.go:WechatPayJSAPI()` — 参考模板:验证 wechatPayment != nil → 查订单 → 调用 SDK → 返回 DTO
- `internal/service/commission_withdrawal/service.go:Approve()` — REFUND Approve 逻辑的最佳参考
- `internal/service/package/activation_service.go` — status=3/4 的插入位置activateMainPackage激活+ 到期检测循环(到期)
- `internal/model/order.go:PurchaseRolePurchasedByPlatform` — PAY-04 购买角色常量已存在
### Established Patterns
- 佣金扣减:参考 `internal/service/commission_withdrawal/service.go` 的钱包扣款方式(使用 version 乐观锁)
- DB 迁移:双文件形式(`.up.sql` / `.down.sql`),编号接续现有最大值(当前 Phase 3 使用了 000090/91
- 退款单号参考提现单号的生成逻辑WD+时间戳+随机数 → 改为 {order_no}-RF{seq}
- 路由注册:所有新接口通过 `internal/routes/registry.go` 的 `Register()` 注册,并同步更新 `cmd/api/docs.go` 和 `cmd/gendocs/main.go`
### Integration Points
- REFUND Handler 注册到 `/api/admin/refunds` 路由组(参考 commission_withdrawal 的路由注册方式)
- PAY-04 修改 `CreateAdminOrder` 的 wallet 分支Handler 层无需改动
- OPS-01/02 在 activation_service.go 和 client_order 两个文件内修改,无新增路由
- 富友支付 JSAPI/小程序的 Handler 已存在留桩(`internal/handler/app/`),替换 Service 层即可
</code_context>
<specifics>
## Specific Ideas
- **退款佣金允许负数**:规格书明确允许,代理佣金钱包余额可降至负值(为了确保退款公平性)
- **CreateLegacy 废弃问题**:规格书 J-2 错误引用了已废弃的 CreateLegacy实际修改目标是 CreateAdminOrder这一决策在讨论中由用户确认
- **富友 SDK 已完整**`pkg/fuiou/` 包含完整 client + types + notify + wxprecreatePAY 工作量主要是替换留桩和编写 DTO
</specifics>
<deferred>
## Deferred Ideas
None — discussion stayed within phase scope.
</deferred>
---
*Phase: 04-refund-pay-ops*
*Context gathered: 2026-03-28*