同步相关内容
This commit is contained in:
@@ -414,120 +414,33 @@ GET /api/admin/bulk-purchases/{task_id}/items?status=4&page=1&page_size=50
|
||||
|
||||
## 需求21:充值审核流程
|
||||
|
||||
### 充值单现有状态
|
||||
> 本节已按 UR#34 最终评审结论重写。完整可执行契约见 [UR#34 PRD](../../../../.scratch/ur34-agent-recharge/PRD.md),如有细节差异以该 PRD 和标准评审稿为准。
|
||||
|
||||
```
|
||||
tb_agent_recharge_record:1=待支付 2=已支付 3=已完成 4=已关闭 5=已退款
|
||||
```
|
||||
### 业务分流
|
||||
|
||||
代码中已经存在 `6=已驳回`,不能改写其含义。员工线下充值走审批流时,在现有状态基础上追加 `7=已退回`:
|
||||
- 代理只能为当前登录店铺主钱包创建 `wechat` 或 `alipay` 在线充值,请求不接受 `shop_id`;平台、超级管理员和企业账号不能替代理创建在线支付。
|
||||
- 平台和超级管理员只能为指定 `shop_id` 创建 `offline` 线下代充值,必须携带固定金额、1~5 个结构化付款凭证和可选备注,并进入真实企业微信审批;代理和企业账号不能创建线下代充值。
|
||||
- 在线充值金额为 `10000~100000000` 分,线下代充值为 `1~100000000` 分。
|
||||
- 平台和超级管理员的线下提交人使用本人绑定的企微账号;审批人只能同意或拒绝,不能修改金额。拒绝后原充值单以 `6=已驳回`终结,若要修正资料必须新建充值单和企微审批;不新增 `7=已退回`,也不提供原单重提接口。
|
||||
|
||||
```sql
|
||||
-- 现有:1=待支付 2=已支付 3=已完成 4=已关闭 5=已退款 6=已驳回
|
||||
-- 新增:7=已退回
|
||||
### 在线支付与入账
|
||||
|
||||
ALTER TABLE tb_agent_recharge_record
|
||||
ADD COLUMN approval_instance_id BIGINT,
|
||||
ADD COLUMN processing_status INT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN processing_error TEXT NOT NULL DEFAULT '',
|
||||
ADD COLUMN processing_started_at TIMESTAMPTZ,
|
||||
ADD COLUMN processing_completed_at TIMESTAMPTZ,
|
||||
ADD COLUMN return_reason VARCHAR(500) NOT NULL DEFAULT '';
|
||||
- 继续使用现有支付配置,只按 `wechat` 或 `alipay` 选择当前可用配置,不建设多通道自动路由、优先级或故障转移。
|
||||
- 微信使用 Native,支付宝使用 `alipay.trade.precreate`。后端把第三方返回的字符串或 HTTPS URL 原样映射为 `qr_content`,前端渲染二维码;后端不生成二维码图片或新增二维码生成接口。
|
||||
- 创建接口不返回 `expires_at`,前端不展示本地推算的精确倒计时。本地时间不能判定第三方支付单是否失效,支付成功或关闭以回调和后端受控查单为准。
|
||||
- `request_id` 只防止同一次提交重试。代理每次主动创建或再次拉起支付都使用新 `request_id` 并产生新的充值单和支付单,旧单等待第三方自然收敛,不复用、不主动取消。
|
||||
- 支付回调或查单先在事务中固化真实收款事实:支付单已支付、充值单 `2=已支付`、`processing_status=1`并可靠写入钱包入账 Outbox;随后 Worker 在独立事务中更新钱包和版本、创建唯一流水、将充值单改为 `3=已完成`和 `processing_status=2`并写资金审计。入账失败使用 `processing_status=3`可靠重试,不回滚支付事实。
|
||||
- 支付状态继续使用 `0=待支付, 1=已支付, 2=已失败, 3=已退款`,不新增“已关闭”;第三方明确关闭或失效时支付记为已失败、充值记为 `4=已关闭`。有效的迟到成功回调仍必须恢复支付事实并幂等入账。
|
||||
|
||||
CREATE INDEX idx_agent_recharge_approval_instance
|
||||
ON tb_agent_recharge_record(approval_instance_id)
|
||||
WHERE approval_instance_id IS NOT NULL;
|
||||
```
|
||||
### 线下审批终态
|
||||
|
||||
充值业务的状态语义:
|
||||
- `1=待支付`:创建未支付(线下充值等待审批时也停在这里,由 `approval_instance_id` 查询审批状态)
|
||||
- `2=已支付`:在线支付已确认,或线下充值审批通过后正在执行钱包入账
|
||||
- `3=已完成`:充值到账
|
||||
- `4=已关闭`:取消/超时
|
||||
- `5=已退款`:退款
|
||||
- `6=已驳回`:审批流程拒绝
|
||||
- `7=已退回`:审批人退回给提交人修改
|
||||
- 企微同意后按与在线充值相同的钱包入账 Worker 幂等入账;企微拒绝时终结为已驳回,不触发钱包。
|
||||
- 企微在通过前撤销或删除时关闭充值单;通过后、入账前撤销时终止入账并关闭;已经到账后再撤销时不得自动扣款,保持已完成并写严重 Audit Event、通知财务人工处理。
|
||||
- 停机发布时,下线旧 `offline-pay` 和本地 `reject` 路由。历史终态只读保留;历史待处理线下单只有在真实提交人已绑定企微且申请资料完整时才迁移为真实企微审批,其余进入异常清单,禁止猜测入账。
|
||||
|
||||
`rejection_reason` 只保存驳回原因,新增 `return_reason` 保存退回修改原因,禁止复用一个字段导致前端无法区分终止和可重提。
|
||||
### 查询、通知和测试门禁
|
||||
|
||||
充值与退款处理状态统一为:`0=未触发, 1=处理中, 2=处理成功, 3=处理失败`,共用同一组中文状态语义,业务页面再结合审批和支付方式解释当前动作。
|
||||
|
||||
### 流程
|
||||
|
||||
**代理自行充值(不走审批)**:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[代理提交充值申请] --> B[系统生成收款码]
|
||||
B --> C[代理扫码支付]
|
||||
C --> D[支付回调幂等入账]
|
||||
```
|
||||
|
||||
**员工线下代充值(走审批)**:
|
||||
|
||||
现有 `offline-pay` 的全局操作密码校验必须保留。通用审批详情在最后一个审批节点返回 `operation_password` 动作字段;审批动作适配器调用现有 `OperationPasswordService` 校验通过后才允许流程完成。密码只在内存中参与本次校验,不落库、不写审批日志、不进入 Outbox。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor Staff as 平台员工
|
||||
actor Approver as 审批人
|
||||
participant Recharge as Recharge Application
|
||||
participant Approval as Approval Application
|
||||
participant DB as PostgreSQL
|
||||
participant Worker as RechargeApprovalHandler
|
||||
|
||||
Staff->>Recharge: POST /api/admin/agent-recharges(payment_method=offline)
|
||||
Recharge->>DB: 同事务创建充值单(status=1)
|
||||
Recharge->>Approval: StartProcess(recharge, recharge_id)
|
||||
Approval->>DB: 创建实例、首任务、审批人、Outbox
|
||||
Recharge->>DB: 回写 approval_instance_id
|
||||
Approver->>Approval: 按 task_id 审批
|
||||
DB-->>Worker: 投递流程结果事件
|
||||
alt 审批通过
|
||||
Worker->>DB: status 从 1 更新为 2,processing_status=1
|
||||
Worker->>DB: 幂等增加钱包余额并写流水
|
||||
Worker->>DB: status 从 2 更新为 3,processing_status=2
|
||||
else 审批拒绝
|
||||
Worker->>DB: status 从 1 更新为 6,写 rejection_reason
|
||||
else 退回修改
|
||||
Worker->>DB: status 从 1 更新为 7,写 return_reason
|
||||
end
|
||||
```
|
||||
|
||||
充值接口独立返回审批状态和业务处理状态。`RechargeApprovalHandler` 使用 `recharge:{recharge_no}` 作为幂等键;钱包余额、版本、充值单和交易流水必须在同一事务更新。
|
||||
|
||||
充值处理同样使用 `processing_started_at` 作为可恢复租约。重复事件不能再次增加余额;若钱包流水已经存在而充值单状态未完成,重试只补齐充值单状态。
|
||||
|
||||
### 退回后重新提交
|
||||
|
||||
```
|
||||
POST /api/admin/agent-recharges/{id}/resubmit
|
||||
→ 校验 status=7
|
||||
→ 请求体可修改 amount、payment_voucher_key、remark
|
||||
→ 在同一事务新建 ProcessInstance
|
||||
→ 更新 approval_instance_id,status 回到 1(待支付/待审批)
|
||||
→ processing_status 重置为 0,清空处理错误和 return_reason
|
||||
→ 旧审批实例保留为历史记录
|
||||
```
|
||||
|
||||
新增 `resubmit` 路由时沿用现有 `/api/admin/agent-recharges` 资源名,不另建 `/agent-recharge-records` 路径。店铺、支付方式和提交人不可修改;编辑与新流程创建必须同事务完成。
|
||||
|
||||
### 停机切换
|
||||
|
||||
现有 `POST /api/admin/agent-recharges/{id}/offline-pay` 和 `POST /api/admin/agent-recharges/{id}/reject` 都会绕过通用审批任务,本次不保留兼容窗口:
|
||||
|
||||
1. 发布前进入维护模式,停止创建和处理线下充值。
|
||||
2. 执行审批关联字段迁移,同时发布新 API、Worker 和前端。
|
||||
3. 初始化并启用 `recharge → recharge_approval` 绑定。
|
||||
4. 为存量“平台员工创建 + 线下支付 + 尚未入账”的充值记录幂等创建流程实例,代理在线充值不回填审批。
|
||||
5. 新前端创建线下充值后直接进入审批详情,不再展示“确认线下充值”按钮。
|
||||
6. 新版本不注册 `offline-pay` 和业务单级 `reject` 路由;线下充值只能由 `ProcessApproved` 事件触发幂等入账,驳回统一由任务级审批接口产生 `ProcessRejected`。
|
||||
7. 验证审批通过、驳回、退回、处理失败重试和钱包流水后再解除维护模式。
|
||||
|
||||
### 前端技术方案
|
||||
|
||||
- 代理自行充值保留现有收款码和支付状态页面,不显示审批信息。
|
||||
- 平台员工选择 `offline` 时,提交成功进入充值详情并展示审批时间线。
|
||||
- `status=6` 展示“已驳回”,`status=7` 展示“已退回”;两者按钮不同,只有已退回可编辑和重新提交。
|
||||
- 审批通过但 `processing_status=1` 时显示“充值处理中”;状态为 3 且处理成功后才显示最新钱包余额。
|
||||
- `processing_status=3` 时不允许前端再次点击入账,只展示系统重试状态和管理员排查入口。
|
||||
- `payment_status` 与 `processing_status` 分离;处理状态统一为 `0=未触发, 1=处理中, 2=处理成功, 3=处理失败`,不提供同义字段 `wallet_posting_status`。
|
||||
- 代理按既有充值业务权限和店铺层级范围查看本店及有权管理的下级店铺记录,不按创建账号隔离;平台和超级管理员也受业务权限与数据范围约束,企业账号不可访问。
|
||||
- 主钱包实际入账后,必须向目标代理主账号发送“充值到账”站内通知;在线实际提交账号不同于主账号时也接收一条。通知按充值单与接收人防重,失败不回滚资金。
|
||||
- 本地自动化使用真实 PostgreSQL、Redis DB 7、真实 S3 和可编程支付 Adapter;测试环境 Redis 保持 DB 6,微信/支付宝真实扫码由人工验证。真实企微审批链路是实现完成门禁,回调可经用户中转应用原样转发到本地。
|
||||
|
||||
Reference in New Issue
Block a user