All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m26s
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
58 lines
2.8 KiB
Markdown
58 lines
2.8 KiB
Markdown
## Context
|
||
|
||
`POST /api/admin/agent-recharges` 是平台管理员为代理商创建线下充值订单的接口。当 `payment_method=offline` 时,代表平台已收到代理商的线下转账,此时应同步录入支付凭证(如银行转账截图的对象存储 key),以便后续对账审计。
|
||
|
||
充值流程分两个阶段:
|
||
1. **创建阶段**(`POST /api/admin/agent-recharges`):平台录入充值单 + 上传凭证
|
||
2. **确认阶段**(`POST /api/admin/agent-recharges/:id/offline-pay`):财务二次确认,凭证已在创建时录入,无需重复上传
|
||
|
||
凭证文件本身通过已有的 `/storage/upload-url` 预签名 URL 上传至对象存储(OSS),后端仅存储 `file_key`。
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
- `Create` 接口在 `payment_method=offline` 时强制要求传入 `payment_voucher_key`
|
||
- 凭证 key 随充值记录一起写入数据库
|
||
- 响应 DTO 返回凭证 key 供前端展示
|
||
- `OfflinePay` 接口不做任何凭证相关改动
|
||
|
||
**Non-Goals:**
|
||
- 不涉及文件上传本身(OSS 预签名链路已有)
|
||
- 不修改 `OfflinePay` 接口(财务确认流程独立于凭证管理)
|
||
- 不做凭证有效性验证(文件是否真实存在于 OSS)
|
||
- `payment_method=wechat` 时 `payment_voucher_key` 无需传,应忽略
|
||
|
||
## Decisions
|
||
|
||
### 决策1:凭证在创建时收集,而非确认时
|
||
|
||
**选择**:在 `Create` 时收集凭证,因为平台管理员在录入线下充值单时就已持有转账凭证。
|
||
|
||
**理由**:线下充值的业务含义是"平台已收到转账,现在录入系统"。凭证是这次录入的必要信息。`OfflinePay` 是财务的二次确认操作,凭证应已在录入时保存,财务只负责审核确认。
|
||
|
||
### 决策2:字段命名与 orders 保持一致
|
||
|
||
**选择**:使用 `payment_voucher_key`,与 `order.payment_voucher_key` 完全一致。
|
||
|
||
**理由**:统一命名降低认知负担,前端可复用同一组件。
|
||
|
||
### 决策3:`wechat` 支付方式时字段可选
|
||
|
||
**选择**:`payment_voucher_key` 使用 `validate:"omitempty,max=500"`,仅在 Service 层对 `offline` 方式做非空校验。
|
||
|
||
**理由**:微信在线支付无需凭证,不应因为加了字段就破坏微信支付的调用契约。
|
||
|
||
### 决策4:凭证随 Create 直接写入
|
||
|
||
**选择**:在 `Create` 方法构建 `AgentRechargeRecord` 时直接赋值 `PaymentVoucherKey`,与记录创建同一个 `agentRechargeStore.Create()` 调用写入。
|
||
|
||
**理由**:凭证是充值记录的组成部分,不需要单独事务,最简路径。
|
||
|
||
## Risks / Trade-offs
|
||
|
||
**[风险] 历史记录无凭证**
|
||
→ 历史已创建的线下充值记录 `payment_voucher_key` 为空,属正常情况,不补录。
|
||
|
||
**[风险] 数据库迁移**
|
||
→ 新列 `VARCHAR(500)` 允许 NULL(兼容历史数据),新接口 Service 层对 `offline` 方式强制非空。迁移简单,无需锁表。
|