Files
junhong_cmp_fiber/openspec/changes/agent-recharges-payment-voucher/design.md
huang 6e15e1b853
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m26s
docs: 更新 OpenAPI 文档及提案任务清单,新增 payment_voucher_key 字段
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-17 18:29:42 +08:00

2.8 KiB
Raw Blame History

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=wechatpayment_voucher_key 无需传,应忽略

Decisions

决策1凭证在创建时收集而非确认时

选择:在 Create 时收集凭证,因为平台管理员在录入线下充值单时就已持有转账凭证。

理由:线下充值的业务含义是"平台已收到转账,现在录入系统"。凭证是这次录入的必要信息。OfflinePay 是财务的二次确认操作,凭证应已在录入时保存,财务只负责审核确认。

决策2字段命名与 orders 保持一致

选择:使用 payment_voucher_key,与 order.payment_voucher_key 完全一致。

理由:统一命名降低认知负担,前端可复用同一组件。

决策3wechat 支付方式时字段可选

选择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 方式强制非空。迁移简单,无需锁表。