更新一下
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 需求15/16/18/19/20/21 技术方案
|
||||
|
||||
> 状态:原需求来源稿;其中本地审批流、审批页面和操作密码方案已废弃,最终以企微审批方案和标准评审稿为准。
|
||||
> 状态:原需求来源稿;其中本地审批流、审批页面、操作密码以及基于原退款单 `resubmit` 的方案均已废弃,最终以企微审批方案和标准评审稿为准。
|
||||
> 评审建议:需求 15/16、需求 18/20/21、需求 19 分三组评审,不在一次会议中混合确认。
|
||||
|
||||
---
|
||||
@@ -166,15 +166,15 @@ sequenceDiagram
|
||||
|
||||
评审结论:支付方式按**整批统一**设计:
|
||||
|
||||
- 页面选择 `offline` 或 `agent_wallet`,Excel 不再重复填写支付方式。
|
||||
- 页面选择 `offline` 或 `agent_wallet`,CSV 不再重复填写支付方式。
|
||||
- 混合支付拆成两个批次,避免一份凭证对应多种支付语义。
|
||||
- Excel 不包含支付方式列,后端拒绝同一批次混合支付。
|
||||
- CSV 不包含支付方式列,后端拒绝同一批次混合支付。
|
||||
|
||||
### 业务流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[员工选择代理和支付方式] --> Upload[上传 Excel]
|
||||
Start[员工选择整批支付方式] --> Upload[上传可含不同代理资产的 CSV]
|
||||
Upload --> Parse[解析并持久化逐行明细]
|
||||
Parse --> Validate[校验资产、套餐、归属和重复行]
|
||||
Validate --> Item{处理下一条有效明细}
|
||||
@@ -193,6 +193,10 @@ flowchart TD
|
||||
|
||||
任务允许部分成功。每一行是独立、可重试、可审计的业务单元,不能只保存一段失败 JSON。
|
||||
|
||||
同一 CSV 内按“资产类型 + 标准化资产标识 + 套餐编码”判断重复:首次出现的行正常处理,后续重复行失败并记录首次出现的行号;同一资产订购不同套餐不算重复。本期不把重复行解释为购买多份,未来如有多份订购需求,应增加明确数量字段。
|
||||
|
||||
本页面和全部批量订购 API 只允许超级管理员,或具备独立“批量订购套餐”权限的平台账号访问;代理和企业账号一律禁止。平台权限不能替代逐行的资产归属、套餐授权、成本价和钱包业务校验。
|
||||
|
||||
### 数据库变更
|
||||
|
||||
```sql
|
||||
@@ -205,7 +209,6 @@ CREATE TABLE tb_bulk_purchase_task (
|
||||
updater BIGINT NOT NULL DEFAULT 0,
|
||||
task_no VARCHAR(30) NOT NULL,
|
||||
source_file_key VARCHAR(500) NOT NULL,
|
||||
shop_id BIGINT NOT NULL,
|
||||
operator_id BIGINT NOT NULL,
|
||||
payment_method VARCHAR(20) NOT NULL,
|
||||
voucher_keys JSONB NOT NULL DEFAULT '[]',
|
||||
@@ -232,6 +235,8 @@ CREATE TABLE tb_bulk_purchase_item (
|
||||
asset_type VARCHAR(20) NOT NULL,
|
||||
asset_identifier VARCHAR(100) NOT NULL,
|
||||
package_code VARCHAR(50) NOT NULL,
|
||||
resolved_shop_id BIGINT,
|
||||
resolved_shop_name VARCHAR(200) NOT NULL DEFAULT '',
|
||||
package_name_snapshot VARCHAR(200) NOT NULL DEFAULT '',
|
||||
package_id BIGINT,
|
||||
amount BIGINT NOT NULL DEFAULT 0,
|
||||
@@ -260,10 +265,10 @@ CREATE INDEX idx_bulk_purchase_item_task_status
|
||||
|
||||
### 处理与幂等
|
||||
|
||||
1. API 校验文件、代理、支付方式和凭证,将 Excel 保存到对象存储并创建任务,返回 `task_id`。
|
||||
2. Worker 根据 `source_file_key` 下载并解析 Excel,将每一行先写入明细表,再开始业务处理。
|
||||
3. 同一任务按行顺序处理,避免对同一代理钱包制造不必要的乐观锁冲突。
|
||||
4. 每行使用独立事务。代理钱包支付时锁定钱包记录,校验 `balance - frozen_balance + credit_limit` 后,在同一事务扣款、创建订单、资金流水并更新明细。
|
||||
1. 前端通过对象存储预签名地址直传 CSV 和线下凭证,再由 API 校验 `file_key`、支付方式和 `voucher_keys` 并创建任务,返回 `task_id`;业务接口不接收 `shop_id` 或文件字节。
|
||||
2. Worker 根据 `source_file_key` 下载并解析 UTF-8 CSV(允许 BOM),将每一行先写入明细表,再开始业务处理;不接受 Excel。
|
||||
3. 同一任务按行顺序处理;每行按资产当前归属解析结算代理,同一任务可以依次处理不同代理的钱包。
|
||||
4. 每行使用独立事务。代理钱包支付时锁定该行结算代理的主钱包,按有效信用额度校验总可用金额后,在同一事务扣款、创建订单、资金流水并更新明细;无归属、归属异常、无主钱包或余额不足只失败该行。
|
||||
5. `idempotency_key` 使用 `bulk_purchase:{task_id}:{row_no}`。Worker 重试时,已存在成功订单的明细直接跳过。
|
||||
6. 单行失败不回滚已成功行;失败原因写结构化错误码和用户可见中文原因。
|
||||
7. 任务汇总从明细表计算,不信任内存计数。
|
||||
@@ -274,7 +279,7 @@ Asynq 载荷只传任务 ID,不传文件字节或临时路径。源文件保
|
||||
|
||||
### API 设计
|
||||
|
||||
**前端静态 Excel 模板**:
|
||||
**前端静态 CSV 模板**:
|
||||
|
||||
模板由前端项目随版本发布,后端不提供下载接口。按已确认的“整批统一支付方式”,模板字段为:
|
||||
|
||||
@@ -288,15 +293,17 @@ Asynq 载荷只传任务 ID,不传文件字节或临时路径。源文件保
|
||||
|
||||
```text
|
||||
POST /api/admin/bulk-purchases
|
||||
Content-Type: multipart/form-data
|
||||
Content-Type: application/json
|
||||
|
||||
shop_id: 123
|
||||
payment_method: offline | agent_wallet
|
||||
voucher_keys: ["key1","key2"]
|
||||
file: <Excel文件>
|
||||
{
|
||||
"request_id": "01J...",
|
||||
"payment_method": "offline",
|
||||
"file_key": "bulk-purchase/2026/07/xxx.csv",
|
||||
"voucher_keys": ["attachment/2026/07/key1"]
|
||||
}
|
||||
```
|
||||
|
||||
`offline` 时 `voucher_keys` 必填,`agent_wallet` 时忽略该字段。
|
||||
CSV 先调用 `POST /api/admin/storage/upload-url` 并使用独立 `purpose=bulk_purchase` 获取预签名地址后直传;线下凭证使用附件用途直传。`offline` 时 `voucher_keys` 必填,`agent_wallet` 时必须为空。创建任务前校验对象存在、归属当前上传主体、CSV 类型和 10MB 大小限制。
|
||||
|
||||
**查询任务状态**:
|
||||
|
||||
@@ -313,7 +320,7 @@ GET /api/admin/bulk-purchases/{task_id}/items?status=4&page=1&page_size=50
|
||||
### 前端技术方案
|
||||
|
||||
- 页面分为“参数确认 → 文件上传 → 处理中 → 结果”四个稳定步骤,刷新页面后可根据任务 ID 恢复进度。
|
||||
- 提交前展示代理、支付方式、凭证数量和文件名的二次确认;钱包支付额外展示当前可用余额,但最终以 Worker 扣款时校验为准。
|
||||
- 提交前展示支付方式、凭证数量和文件名的二次确认;不展示整批目标代理或单一钱包余额。任务结果按行展示后端解析的结算代理及金额。
|
||||
- 任务处理中展示总数、已处理数、成功数和失败数,轮询规则复用统一异步任务方案。
|
||||
- 结果页默认显示失败明细,可切换全部/成功/失败,并可按资产标识搜索。
|
||||
- 部分成功使用明确状态,不弹“全部成功”提示;再次上传失败行会创建新任务,不修改旧任务历史。
|
||||
@@ -413,7 +420,9 @@ POST /api/admin/refunds/{id}/manual-complete
|
||||
|
||||
请求包含 `request_id`、可选 `remark` 和最多 5 个完成凭证。后端仅允许具备财务确认权限的账号对 `status=2 AND processing_status IN (0,3)` 的非代理钱包退款操作;实际金额沿用审批金额,不允许在确认时再次改价。确认记录操作人、时间、备注和凭证后将处理状态置为已完成。
|
||||
|
||||
### 退回后重新提交
|
||||
### 退回后重新提交(已废弃,禁止实施)
|
||||
|
||||
> 本节是旧本地审批方案的历史记录。七月最终方案下线该接口;企微拒绝会终结原退款单,后续仍需退款时重新创建新退款单。
|
||||
|
||||
```
|
||||
POST /api/admin/refunds/{id}/resubmit
|
||||
|
||||
Reference in New Issue
Block a user