fix: 代理
This commit is contained in:
88
openspec/changes/add-employee-collection/design.md
Normal file
88
openspec/changes/add-employee-collection/design.md
Normal file
@@ -0,0 +1,88 @@
|
||||
## Context
|
||||
|
||||
员工代收款是 8 月迭代新增的财务能力,普通员工与超级管理员共用同一套接口,靠登录态区分数据范围。后台管理端需要新增三类页面,并复用已有的表格、搜索、详情与上传组件。`docs/admin-openapi.yaml` 未随仓库提供,接口字段以后端契约(需求文档 + 创建订单接口 OpenAPI 片段)为准,类型集中在一个文件便于联调收敛。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals**
|
||||
|
||||
- 在财务管理下提供收款方式、员工代收款账单、核销申请三类页面。
|
||||
- 复用 `ArtTableFullScreen`、`ArtSearchBar`、`ArtTableHeader`、`ArtTable`、`DetailPage`、`VoucherUpload`、`PaymentVoucherDialog`、`useCheckedColumns` 等既有组件与约定。
|
||||
- 复用既有企微审批场景配置能力,仅新增业务类型。
|
||||
|
||||
**Non-Goals**
|
||||
|
||||
- 不实现后端接口、数据库、Worker、企微回调。
|
||||
- 不实现 H5/C 端页面与支付流程。
|
||||
- 不新增导出任务场景(需求文档未要求)。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 接口契约
|
||||
|
||||
| 能力 | 关键字段 |
|
||||
|---|---|
|
||||
| 统一响应 | `{ code, data, msg, timestamp }` |
|
||||
| 列表分页 | 账单列表返回 `{ items, total, page, size }`,取数处对 `items` / `list` / `records` 做兼容 |
|
||||
| 收款方式 | `{ id, code, name, sort, enabled, remark, created_at, updated_at }`,列表接口返回 `{ items, page, size, total }`,支持 `page` / `page_size` / `enabled` / `keyword` 筛选 |
|
||||
| 账单列表项 | `{ id, source_type, source_type_name, source_no, debtor_snapshot, customer_snapshot, receivable_amount, received_amount, reserved_amount, remaining_amount, status, status_name, approval_pending, closed_reason, created_at, updated_at }` |
|
||||
| 账单详情 | `data` 为 `{ bill, refunds, allocations, applications }`;`refunds` 为退款冲销(`refund_id`、`source_order_id`、`refund_amount`、`reduced_amount`、`bill_receivable_amount`、`outcome_name`),`allocations` 为账单侧分摊(含 `application_id` / `application_status_name` / `attempt_id`),`applications` 内嵌该申请的 `attempts` |
|
||||
| 账单统计 | `{ receivable_total, received_total, unsettled_total, pending_bill_count }` |
|
||||
| 账单筛选 | `page`、`page_size`、`source_type`、`source_no`、`status`、`debtor_account_id`、`customer_id`、`created_from`(`YYYY-MM-DD`)、`created_to`(`YYYY-MM-DD`) |
|
||||
| 核销申请请求体 | `{ payment_method_id, paid_amount, paid_at, payer_name, external_transaction_no, payment_voucher_keys, remark, allocations: [{ bill_id, amount }], acting_reason }` |
|
||||
| 核销申请列表项 | `{ id, applicant_account_id, acting_operator_id, payment_method_id, payment_method_name, paid_amount, payer_name, external_transaction_no, status, status_name, terminal_reason, decided_at, created_at, updated_at }` |
|
||||
| 核销申请详情 | `data` 为 `{ application, allocations, attempts }`,`attempts` 保存每次提交的完整材料快照,重新提交不清空历史 |
|
||||
|
||||
账单状态为数字枚举 `0` 待核销 / `1` 部分核销 / `2` 已核销 / `3` 已关闭;申请状态为数字枚举 `0` 审批中 / `1` 已通过 / `2` 已驳回 / `3` 已撤销或已关闭;两者展示均优先使用后端 `status_name`。
|
||||
|
||||
### 页面与路由组织
|
||||
|
||||
在 `/finance` 下新增:
|
||||
|
||||
| 路由 | 页面 | 说明 |
|
||||
|---|---|---|
|
||||
| `/finance/employee-collection/bills` | 员工代收款账单 | 统计 + 列表 |
|
||||
| `/finance/employee-collection/bills/detail/:id` | 账单详情 | 隐藏菜单 |
|
||||
| `/finance/employee-collection/applications` | 核销申请 | 列表 + 创建 |
|
||||
| `/finance/employee-collection/applications/detail/:id` | 核销申请详情 | 隐藏菜单 |
|
||||
| `/finance/employee-collection/payment-methods` | 收款方式管理 | 仅超管 |
|
||||
|
||||
账单与申请拆分为独立菜单,符合项目「列表页 + 详情页」的既有组织方式,避免单页堆叠过多交互。账单列表的「店铺」筛选用远程搜索复用 `ShopService.getShops`,与退款列表一致。
|
||||
|
||||
账单详情与核销申请详情保持只读:页面只在顶部保留「返回」导航,创建核销申请、关闭账单、修改并重新提交等操作入口统一放在列表页的操作列,详情页不出现业务操作按钮。
|
||||
|
||||
### 权限编码
|
||||
|
||||
新增 `src/config/constants/augustIteration.ts`,沿用 `模块:动作` 风格,例如 `employee_collection:bill_close`、`employee_collection:application_create`。页面级 `permissions` 用于菜单可见性,按钮级编码用于 `hasAuth()` / `v-permission`。
|
||||
|
||||
### 金额、时间与附件
|
||||
|
||||
- 金额统一以「分」传输,展示时通过 `fenToYuan` / `formatCurrency` 转换,与退款、代理充值保持一致。
|
||||
- 所有时间字段统一通过 `formatDateTime` 格式化为 `YYYY-MM-DD HH:mm:ss`,不在模板中直接输出后端原始时间字符串。
|
||||
- 附件仅返回对象 Key(`payment_voucher_keys`),展示复用 `PaymentVoucherDialog`,由预签名下载接口换取访问地址。
|
||||
|
||||
### 核销申请分摊
|
||||
|
||||
创建申请时按账单逐条录入核销金额,并填写付款事实(付款金额、付款方名称、付款时间、外部交易流水号),前端校验:
|
||||
|
||||
- 至少选择 1 张账单,最多 N 张;
|
||||
- 单张核销金额不得大于账单未核销金额,且大于 0;
|
||||
- 付款金额(`paid_amount`,分)不得小于各账单分摊之和(允许存在差额);
|
||||
- 超管代办时 `acting_reason` 必填;
|
||||
- 付款凭证至少 1 个 Key。
|
||||
|
||||
提交成功后自动发起企微审批;仅已驳回申请可再次进入弹窗修改并重新提交,重新提交生成新的审批实例,历史审批记录只读展示。未配置企微审批场景时后端返回 503,前端展示「企微审批场景未配置,请联系管理员」并保留已填内容。
|
||||
|
||||
### 企微审批场景
|
||||
|
||||
`WecomBusinessType` 增加 `employee_collection_approval`,企微审批场景页面下拉新增「员工代收款审批」。模板控件同步、字段查询、字段映射保存全部复用既有 `WecomService`。
|
||||
|
||||
### 订单付款凭证规则
|
||||
|
||||
线下订单创建时,满足「平台账号(`user_type` 为 1 或 2)操作 + 非赠送套餐 + 实际收款金额大于 0」条件的订单会生成员工代收款账单,此时 `payment_voucher_key` 非必填,字段结构不变,付款凭证改在核销申请中提交。前端以当前登录账号类型、所选套餐是否赠送、套餐有效价格(`effective_retail_price` / `suggested_retail_price` / `retail_price`)判断是否展示提示并放宽必填;赠送套餐或其他非平台账号的线下订单仍需上传凭证。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **接口字段以契约文档为准**:`docs/admin-openapi.yaml` 未入库,字段来自需求文档与创建订单接口片段;类型集中在一个文件,便于联调时收敛修改。
|
||||
- **员工/超管同接口**:前端不做数据范围过滤,仅做展示与操作可见性控制,数据隔离以后端为准。
|
||||
- **关闭账单与审批中申请**:前端依据 `approval_pending` 与状态字段禁用关闭按钮,最终一致性以后端校验为准。
|
||||
25
openspec/changes/add-employee-collection/proposal.md
Normal file
25
openspec/changes/add-employee-collection/proposal.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Change: 员工代收款功能前端对接
|
||||
|
||||
## Why
|
||||
|
||||
8 月产品迭代新增「员工代收款」能力:员工线下代收款项后,通过创建核销申请、经企业微信审批完成账单核销;超级管理员可维护收款方式、查看全部账单并关闭账单。后端接口已按 `docs/产品迭代8月份/员工代收款功能简介.md` 落地,后台管理端目前缺少收款方式管理、账单列表与核销申请页面,普通员工与超级管理员的分类视图、以及线下订单付款凭证规则尚未接入。
|
||||
|
||||
本次已按后端实际契约对齐字段:列表响应统一使用 `items` / `total` / `page` / `size`;账单状态(`0` 待核销 / `1` 部分核销 / `2` 已核销 / `3` 已关闭)与申请状态(`0` 审批中 / `1` 已通过 / `2` 已驳回 / `3` 已撤销或已关闭)为数字枚举;收款方式列表返回 `{ items, page, size, total }` 并支持 `enabled` / `keyword` 筛选;核销申请请求体使用 `paid_amount`、`paid_at`、`payer_name`、`external_transaction_no`、`payment_voucher_keys` 与 `allocations`。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 新增 `employee-collection` 能力的前端类型与服务封装:收款方式 4 个接口、员工代收款账单 4 个接口、核销申请 4 个接口。
|
||||
- 新增「收款方式管理」页面(仅超级管理员):列表 + 新增/编辑弹窗 + 删除;已被核销申请引用的方式不可删除、不可修改 `code`(未被引用时可改),可停用。
|
||||
- 新增「员工代收款账单」页面:应收/已核销/未核销/待处理账单统计 + 列表 + 详情;普通员工仅见本人账单,超级管理员可见全部;存在审批中申请时账单不可关闭,关闭必须填写原因。
|
||||
- 新增「核销申请」页面:列表 + 详情(含分摊账单、付款凭证、付款信息与全部审批尝试记录)+ 创建/重新提交弹窗;一笔线下收款可核销 1~N 张账单,填写付款金额、付款方名称、付款时间、外部交易流水号、选择收款方式并上传付款凭证;提交后自动发起企微审批;仅已驳回申请可修改并重新提交,重新提交生成新的审批实例且历史记录不被覆盖。
|
||||
- 扩展企业微信审批场景:新增 `employee_collection_approval` 业务类型,复用既有企微应用列表、模板控件同步与业务字段查询接口。
|
||||
- 调整订单创建:由平台账号操作、实际收款金额大于 0 且非赠送的线下订单会生成员工代收款账单,该场景 `payment_voucher_key` 改为非必填,付款凭证改在核销申请中提交,字段结构保持不变。
|
||||
- 附件接口仅返回对象 Key,统一通过系统既有预签名下载接口展示。
|
||||
- **不实现后端接口、数据库、Worker、企微回调**;**不实现 H5/C 端页面**。
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs: `employee-collection`
|
||||
- Affected code: `src/types/api/employeeCollection.ts`、`src/api/modules/employeeCollection.ts`、`src/views/finance/employee-collection/*`、`src/router/routes/asyncRoutes.ts`、`src/router/routesAlias.ts`、`src/config/constants/augustIteration.ts`、`src/types/api/wecom.ts`、`src/views/settings/wecom/scenes/index.vue`、`src/views/order-management/order-list/index.vue`、`src/locales/langs/{zh,en}.json`
|
||||
- Dependencies: `docs/产品迭代8月份/员工代收款功能简介.md`
|
||||
- Contract note: `docs/admin-openapi.yaml` 未随仓库提供,字段以需求文档描述的后端契约与创建订单接口的 OpenAPI 片段为准;类型集中在单一文件,联调时便于收敛。
|
||||
@@ -0,0 +1,155 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 收款方式管理
|
||||
超级管理员 MUST 能够维护线下收款方式,包括名称、编码、排序、状态与备注;普通员工只能看到启用的收款方式。收款方式列表接口 MUST 返回 `{ items, page, size, total }`,并 MUST 支持 `enabled` 与 `keyword` 筛选。已被核销申请引用的收款方式 MUST NOT 被删除;引用后修改稳定编码 MUST 被后端拒绝,前端 MUST 展示错误提示。
|
||||
|
||||
#### Scenario: 超级管理员新增收款方式
|
||||
- **GIVEN** 超级管理员已登录并拥有收款方式新增权限
|
||||
- **WHEN** 其填写名称、唯一编码、排序、状态与备注并提交
|
||||
- **THEN** 系统 MUST 调用新增接口并在成功后刷新列表
|
||||
- **AND** 新增成功后 MUST 清空并关闭弹窗
|
||||
|
||||
#### Scenario: 删除被引用的收款方式
|
||||
- **GIVEN** 某收款方式已被业务引用
|
||||
- **WHEN** 超级管理员尝试删除该方式
|
||||
- **THEN** 前端 MUST 阻止删除或展示后端返回的业务错误
|
||||
- **AND** MUST 提示改为停用
|
||||
|
||||
#### Scenario: 修改收款方式编码
|
||||
- **GIVEN** 超级管理员打开编辑弹窗
|
||||
- **WHEN** 其修改稳定编码并提交
|
||||
- **THEN** 前端 MUST 提交最新编码
|
||||
- **AND** 若该方式已被核销申请引用,后端拒绝时前端 MUST 展示错误提示
|
||||
|
||||
### Requirement: 员工代收款账单统计
|
||||
账单页面 MUST 展示应收金额、已核销金额、未核销金额与待处理账单数量四项统计,数据 MUST 来自 `GET /api/admin/employee-collection-bills/statistics`,字段为 `receivable_total`、`received_total`、`unsettled_total`、`pending_bill_count`;前端 MUST NOT 通过遍历当前分页数据自行计算。
|
||||
|
||||
#### Scenario: 加载账单统计
|
||||
- **GIVEN** 用户进入员工代收款账单页面
|
||||
- **WHEN** 页面初始化或筛选条件变化
|
||||
- **THEN** 前端 MUST 调用账单统计接口
|
||||
- **AND** MUST 将后端返回的「分」按元格式化后展示应收、已核销与未核销金额
|
||||
|
||||
### Requirement: 员工代收款账单列表与详情
|
||||
账单列表响应 MUST 为 `{ items, total, page, size }`,前端 MUST 兼容 `items` / `list` / `records` 等列表字段。列表 MUST 支持按来源(`source_type`)、来源单号(`source_no`)、账单状态、客户/店铺(`customer_id`)与创建时间(`created_from` / `created_to`,`YYYY-MM-DD`)筛选,并展示账单编号、来源、关联单号、负责员工、客户/店铺、应收金额、已核销金额、未核销金额与状态。账单状态 MUST 为数字枚举:`0` 待核销、`1` 部分核销、`2` 已核销、`3` 已关闭,展示 MUST 优先使用后端 `status_name`。账单详情响应 MUST 为 `{ bill, refunds, allocations, applications }`:`refunds` MUST 展示退款金额、冲减应收、冲销前应收与处理结果,`applications` MUST 可展开查看该申请的审批尝试记录。
|
||||
|
||||
#### Scenario: 普通员工查看账单
|
||||
- **GIVEN** 普通员工已登录
|
||||
- **WHEN** 其打开账单列表
|
||||
- **THEN** 列表 MUST 只展示后端返回的本人账单数据
|
||||
- **AND** MUST NOT 展示仅超管可见的操作入口
|
||||
|
||||
#### Scenario: 打开账单详情
|
||||
- **GIVEN** 用户拥有账单详情权限
|
||||
- **WHEN** 其点击账单号或详情操作
|
||||
- **THEN** 前端 MUST 跳转账单详情页并加载对应账单(详情数据取自 `data.bill`)
|
||||
- **AND** 详情 MUST 展示退款冲销、核销分摊与关联核销申请
|
||||
|
||||
### Requirement: 关闭账单
|
||||
超级管理员 MUST 能够关闭账单,且关闭原因必填(最多 500 字符)。仅待核销或部分核销账单可关闭;存在审批中的核销申请(`approval_pending` 为真)时,账单 MUST NOT 被关闭。
|
||||
|
||||
#### Scenario: 存在审批中申请时关闭账单
|
||||
- **GIVEN** 账单存在审批中的核销申请
|
||||
- **WHEN** 用户查看该账单操作
|
||||
- **THEN** 关闭入口 MUST 被禁用或不可见
|
||||
- **AND** MUST 展示不可关闭的原因提示
|
||||
|
||||
#### Scenario: 关闭原因必填
|
||||
- **GIVEN** 账单可关闭
|
||||
- **WHEN** 超级管理员打开关闭弹窗并留空原因提交
|
||||
- **THEN** 前端 MUST 阻止提交并提示填写原因
|
||||
|
||||
### Requirement: 创建核销申请
|
||||
员工 MUST 能够使用一笔线下收款核销 1~N 张账单。创建申请 MUST 提交收款方式 `payment_method_id`、付款金额 `paid_amount`(分,大于 0)、付款方名称 `payer_name`、付款时间 `paid_at`(带时区 RFC3339)、外部交易流水号 `external_transaction_no`、付款凭证 `payment_voucher_keys`(1~5 个对象 Key)与账单分摊 `allocations`。超级管理员代办时 MUST 填写 `acting_reason`,本人办理 MUST NOT 提交该字段。提交成功后系统 MUST 自动发起企业微信审批。
|
||||
|
||||
#### Scenario: 一笔收款核销多张账单
|
||||
- **GIVEN** 用户选择了多张可核销账单
|
||||
- **WHEN** 其录入各账单核销金额、选择收款方式并填写付款事实后提交
|
||||
- **THEN** 前端 MUST 校验各账单核销金额大于 0 且不超过账单未核销余额
|
||||
- **AND** MUST 以 `allocations: [{ bill_id, amount }]` 提交账单分摊明细
|
||||
|
||||
#### Scenario: 付款金额小于核销合计
|
||||
- **GIVEN** 用户已录入各账单核销金额
|
||||
- **WHEN** 其填写的付款金额小于核销合计即提交
|
||||
- **THEN** 前端 MUST 阻止提交并提示付款金额不能小于核销合计
|
||||
|
||||
#### Scenario: 缺少付款凭证
|
||||
- **GIVEN** 用户已选择账单与收款方式
|
||||
- **WHEN** 其未上传任何付款凭证即提交
|
||||
- **THEN** 前端 MUST 阻止提交并提示上传付款凭证
|
||||
|
||||
#### Scenario: 缺少付款事实
|
||||
- **GIVEN** 用户已选择账单与收款方式
|
||||
- **WHEN** 其未填写付款方名称、付款时间或外部交易流水号即提交
|
||||
- **THEN** 前端 MUST 阻止提交并提示补齐必填项
|
||||
|
||||
#### Scenario: 超管代办未填写原因
|
||||
- **GIVEN** 超级管理员以代办身份创建申请
|
||||
- **WHEN** 其未填写 `acting_reason` 即提交
|
||||
- **THEN** 前端 MUST 阻止提交并提示填写代办原因
|
||||
|
||||
#### Scenario: 企微审批场景未配置
|
||||
- **GIVEN** 企业微信审批场景尚未配置
|
||||
- **WHEN** 用户提交核销申请
|
||||
- **THEN** 前端 MUST 展示后端返回的 503 提示「企微审批场景未配置,请联系管理员」
|
||||
- **AND** MUST 保留用户已填写的内容且不产生申请数据
|
||||
|
||||
### Requirement: 核销申请列表与详情
|
||||
核销申请列表响应 MUST 为 `{ items, page, size, total }`,MUST 支持按状态、收款方式与创建时间筛选;列表项 MUST 包含 `payment_method_name`、`paid_amount`、`status`、`status_name` 与 `created_at`,状态 MUST 优先展示后端 `status_name`。申请详情响应 MUST 为 `{ application, allocations, attempts }`;`attempts` MUST 按提交顺序展示全部审批尝试记录,包含付款金额、付款方、流水号、付款凭证与审批意见。
|
||||
|
||||
#### Scenario: 查看审批历史
|
||||
- **GIVEN** 申请存在多次审批尝试记录
|
||||
- **WHEN** 用户打开申请详情
|
||||
- **THEN** 详情 MUST 按提交顺序展示每次尝试的提交材料与审批状态
|
||||
- **AND** 历史材料 MUST NOT 因重新提交而被覆盖
|
||||
|
||||
### Requirement: 核销申请重新提交
|
||||
仅已驳回(`status` 为 `2`)的申请 MUST 允许修改并重新提交;重新提交 MUST 生成新的企业微信审批实例,且历史审批记录 MUST NOT 被覆盖。重新提交入口 MUST 位于核销申请列表的操作列,详情页 MUST 只读且 MUST NOT 展示业务操作按钮(仅保留返回导航)。
|
||||
|
||||
#### Scenario: 重新提交被驳回申请
|
||||
- **GIVEN** 申请状态为已驳回
|
||||
- **WHEN** 用户在核销申请列表点击「修改并重新提交」并修改账单分摊、收款方式、付款事实或付款凭证后提交
|
||||
- **THEN** 前端 MUST 调用修改接口重新提交
|
||||
- **AND** 成功后 MUST 刷新详情并展示新的审批实例状态
|
||||
|
||||
#### Scenario: 非驳回申请不可修改
|
||||
- **GIVEN** 申请处于审批中或已通过
|
||||
- **WHEN** 用户查看核销申请列表
|
||||
- **THEN** 修改并重新提交入口 MUST 不可见或不可用
|
||||
|
||||
#### Scenario: 详情页只读
|
||||
- **GIVEN** 用户打开账单详情或核销申请详情
|
||||
- **WHEN** 页面渲染完成
|
||||
- **THEN** 页面 MUST NOT 展示创建核销申请、关闭账单或修改并重新提交等业务操作按钮
|
||||
- **AND** MUST 只保留返回导航
|
||||
|
||||
### Requirement: 企业微信审批场景配置
|
||||
企业微信审批场景 MUST 支持 `employee_collection_approval` 业务类型,复用既有的应用列表、模板控件同步、业务字段查询与字段映射保存接口。
|
||||
|
||||
#### Scenario: 配置员工代收款审批场景
|
||||
- **GIVEN** 超级管理员打开企微审批场景页面
|
||||
- **WHEN** 其选择业务类型「员工代收款审批」
|
||||
- **THEN** 前端 MUST 使用 `employee_collection_approval` 调用模板同步、字段查询与保存接口
|
||||
|
||||
### Requirement: 附件预签名展示
|
||||
附件接口 MUST 只返回对象存储 Key(`payment_voucher_keys`);前端 MUST 通过系统既有的预签名下载接口获取实际访问地址后再展示。
|
||||
|
||||
#### Scenario: 查看付款凭证
|
||||
- **GIVEN** 申请包含付款凭证 Key
|
||||
- **WHEN** 用户点击查看付款凭证
|
||||
- **THEN** 前端 MUST 先批量换取预签名地址
|
||||
- **AND** 图片 MUST 支持预览,非图片 MUST 支持查看或下载
|
||||
|
||||
### Requirement: 订单付款凭证规则调整
|
||||
由平台账号(`user_type` 为 1 或 2)操作、实际收款金额大于 0 且非赠送的线下订单会生成员工代收款账单;该场景 `payment_voucher_key` MUST 变为非必填,付款凭证改在核销申请中提交,订单字段结构 MUST 保持不变。其余线下订单 MUST 继续要求付款凭证。
|
||||
|
||||
#### Scenario: 线下订单生成代收款账单
|
||||
- **GIVEN** 当前登录账号为平台账号,所选套餐非赠送且实际收款金额大于 0,支付方式为线下支付
|
||||
- **WHEN** 用户创建该订单
|
||||
- **THEN** 前端 MUST 不再强制要求上传付款凭证
|
||||
- **AND** MUST 提示付款凭证将在核销申请中提交
|
||||
|
||||
#### Scenario: 赠送套餐或非平台账号的线下订单
|
||||
- **GIVEN** 所选套餐为赠送套餐,或当前账号非平台账号,或实际收款金额为 0
|
||||
- **WHEN** 用户以线下支付方式创建订单
|
||||
- **THEN** 前端 MUST 继续要求上传付款凭证
|
||||
48
openspec/changes/add-employee-collection/tasks.md
Normal file
48
openspec/changes/add-employee-collection/tasks.md
Normal file
@@ -0,0 +1,48 @@
|
||||
## 1. Contract and API Types
|
||||
|
||||
- [x] 1.1 新增 `src/types/api/employeeCollection.ts`:收款方式、账单、核销申请、统计、查询参数与请求/响应类型;列表统一使用 `items` / `total` / `page` / `size`。
|
||||
- [x] 1.2 定义账单状态(`0` 待核销 / `1` 部分核销 / `2` 已核销 / `3` 已关闭)与申请状态(`0` 审批中 / `1` 已通过 / `2` 已驳回 / `3` 已撤销或已关闭)数字枚举,并保留后端 `*_name` 展示字段。
|
||||
- [x] 1.3 金额字段以「分」传输;附件字段使用 `payment_voucher_keys` 对象存储 Key 数组,展示复用预签名下载接口。
|
||||
- [x] 1.4 新增 `src/api/modules/employeeCollection.ts` 并在 `src/api/modules/index.ts`、`src/types/api/index.ts` 导出。
|
||||
- [x] 1.5 按后端实际契约收敛字段:账单详情为 `{ bill, refunds, allocations, applications }`,核销申请详情为 `{ application, allocations, attempts }`,付款金额/付款方/付款时间/外部交易流水号以 `paid_amount` / `payer_name` / `paid_at` / `external_transaction_no` 提交。
|
||||
|
||||
## 2. Permissions, Routes, and Menu
|
||||
|
||||
- [x] 2.1 新增 `src/config/constants/augustIteration.ts`,集中声明页面与按钮权限编码。
|
||||
- [x] 2.2 在 `src/router/routesAlias.ts` 与 `src/router/routes/asyncRoutes.ts` 的财务管理下新增账单、核销申请、收款方式路由。
|
||||
- [x] 2.3 在 `src/locales/langs/zh.json`、`en.json` 的 `menus.financialManagement` 下补充菜单文案。
|
||||
|
||||
## 3. Payment Methods Page
|
||||
|
||||
- [x] 3.1 实现收款方式列表(名称、编码、排序、状态、备注、创建时间);接口返回 `{ items, page, size, total }`,复用 `ArtTableFullScreen` / `ArtSearchBar` / `ArtTableHeader` / `ArtTable`。
|
||||
- [x] 3.2 实现新增/编辑弹窗;编辑时允许提交 `code`,被核销申请引用时由后端拒绝并提示。
|
||||
- [x] 3.3 实现删除操作,被引用的方式给出提示并引导停用。
|
||||
|
||||
## 4. Bills Pages
|
||||
|
||||
- [x] 4.1 实现账单统计卡片(应收、已核销、未核销、待处理账单数量),数据来自 `GET /api/admin/employee-collection-bills/statistics`。
|
||||
- [x] 4.2 实现账单列表与筛选(来源、来源单号、状态、客户/店铺、创建时间范围),展示账单号、来源、关联单号、负责员工、客户/店铺、应收/已核销/未核销金额与状态。
|
||||
- [x] 4.3 实现账单详情,展示账单信息、退款冲销、核销分摊与关联核销申请(关联申请可展开查看审批尝试记录)。
|
||||
- [x] 4.4 实现超管关闭账单弹窗,关闭原因必填;存在审批中申请时禁用关闭并给出说明。
|
||||
- [x] 4.5 列表「创建核销申请」入口按权限与账单状态控制可用性。
|
||||
|
||||
## 5. Applications Pages
|
||||
|
||||
- [x] 5.1 实现核销申请列表与筛选(状态、收款方式、创建时间),展示申请编号、收款方式、付款金额、状态与提交时间。
|
||||
- [x] 5.2 实现创建/重新提交弹窗:选择 1~N 张可核销账单、按账单录入分摊金额、填写付款金额/付款方名称/付款时间/外部交易流水号、选择收款方式并上传付款凭证。
|
||||
- [x] 5.3 超管代办时必须填写 `acting_reason`,否则禁止提交。
|
||||
- [x] 5.4 实现申请详情,展示申请信息、分摊账单、付款凭证与全部审批尝试记录;历史记录只读,不被重新提交覆盖。
|
||||
- [x] 5.5 仅已驳回申请展示「修改并重新提交」,提交成功后生成新的审批实例并刷新详情。
|
||||
- [x] 5.6 统一处理 503「企微审批场景未配置」等业务错误,保留用户已填内容。
|
||||
|
||||
## 6. WeCom Scene and Order Rule
|
||||
|
||||
- [x] 6.1 扩展 `WecomBusinessType` 增加 `employee_collection_approval`,并在企微审批场景页面新增可选业务类型。
|
||||
- [x] 6.2 场景保存复用既有 `inspectTemplate`、`getBusinessFields`、`saveScene` 接口,无需新增企微接口。
|
||||
- [x] 6.3 调整线下订单创建:平台账号操作、非赠送且实际收款金额大于 0 时 `payment_voucher_key` 非必填,字段结构不变。
|
||||
|
||||
## 7. Verification
|
||||
|
||||
- [x] 7.1 运行 `npm run build`(含 `vue-tsc --noEmit`)与 `npm run check:encoding`,确保类型与编码通过。
|
||||
- [ ] 7.2 校验普通员工与超管的菜单、列与操作可见性差异。
|
||||
- [ ] 7.3 校验金额分/元转换、附件预签名展示与 503 错误兜底。
|
||||
@@ -0,0 +1,92 @@
|
||||
## Context
|
||||
|
||||
AUG26-017 给代理预存款带来两类变化:在线自充的收款方式从「由支付配置自动决定」改为「超管配置允许范围」;线下预存款审批从「金额 + 支付凭证」补齐为「收款方式 + 交易流水号 + 其他凭证」。后端接口边界已经确认:
|
||||
|
||||
| 用途 | 接口 |
|
||||
|---|---|
|
||||
| 在线可用方式 | `GET /api/admin/agent-self-recharge-payment-methods`,代理/平台可用,超管 403;旧接口 `GET /api/admin/agent-recharges/payment-methods` 保留但前端不再调用 |
|
||||
| 允许范围读取 | `GET /api/admin/system-configs`(`module`、`page`、`page_size`,返回 `list` / `page` / `page_size` / `total`) |
|
||||
| 允许范围写入 | `PUT /api/admin/system-configs/{key}`,请求体 `{ key, value }`,`value` 为字符串化配置值 |
|
||||
| 交易流水号识别 | `POST /api/admin/agent-recharges/payment-voucher-ocr`,请求 `{ payment_voucher_key }`,响应 `{ external_transaction_no }` |
|
||||
| 线下创建 | `POST /api/admin/agent-recharges`,`payment_method=offline` 时新增 `offline_payment_method_id`、`external_transaction_no`、`other_voucher_key` |
|
||||
| 列表与详情 | `GET /api/admin/agent-recharges`、`GET /api/admin/agent-recharges/{id}` 新增 5 个响应字段 |
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals**
|
||||
|
||||
- 在线充值只展示后端返回的实际可用方式,并优雅处理空列表。
|
||||
- 超管可在既有系统配置页面维护允许范围,无需新增接口或页面。
|
||||
- 线下预存款申请可提交收款方式、交易流水号与其他凭证,并支持 OCR 预填流水号。
|
||||
- 列表与详情正确展示两类交易号与收款方式快照。
|
||||
|
||||
**Non-Goals**
|
||||
|
||||
- 不新增「代理自充设置」独立页面与专用配置接口。
|
||||
- 不实现允许范围的审计查询接口(后端记录操作者、前后值与时间,前端不查询)。
|
||||
- 不实现后端接口、商户池交集计算、企微审批回调。
|
||||
- 不实现 H5/C 端在线充值,不为企业账号做分支。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 允许范围沿用受控系统配置
|
||||
|
||||
允许范围配置项的读取与写入统一走系统配置:读取 `GET /api/admin/system-configs`,写入 `PUT /api/admin/system-configs/{key}`。系统配置页已是元数据驱动的通用渲染器(`value_type` 决定控件、`enum_values` 决定枚举选项、`readonly` 与 `sensitive` 决定保护策略),因此后端的允许范围配置项注册后即可在页面中展示与编辑。
|
||||
|
||||
前端 MUST NOT 硬编码该配置项的 `config_key`,也 MUST NOT 新增专用设置写接口。该配置项归属 `c2b.payment` 模块,属于现有 `SystemConfigModule` 取值,无需扩展模块枚举。
|
||||
|
||||
允许范围的枚举取值为 `wechat_only`(仅微信支付)、`alipay_only`(仅支付宝支付)、`both`(同时支持微信与支付宝);系统配置页展示与选择时使用中文标签,未命中映射时回退展示原值。
|
||||
|
||||
超管入口使用一个「代理自充设置」菜单项,跳转到系统配置页面并带 `module=c2b.payment` 过滤条件;系统配置页面除了既有的 `config_key` query,还需支持从 `route.query.module` 初始化模块筛选,保证跳转后列表已按模块收敛。
|
||||
|
||||
### 在线可用方式
|
||||
|
||||
| 项 | 取值 |
|
||||
|---|---|
|
||||
| 接口 | `GET /api/admin/agent-self-recharge-payment-methods` |
|
||||
| 响应 | `{ methods: (wechat \| alipay)[], min_amount, max_amount }` |
|
||||
| 空列表 | 只提示「当前暂无可用的在线支付方式」,禁用提交,不解释被限制还是无可用商户 |
|
||||
| 超管 | 该接口对超管返回 403,前端在超管视角不请求它,仅通过系统配置查看允许范围 |
|
||||
| 存量单 | 配置变更不影响已创建的待支付单,前端不在配置变更后刷新待支付单 |
|
||||
|
||||
金额上下限优先使用接口返回的 `min_amount` / `max_amount`(单位分),接口未返回时回退既有常量。
|
||||
|
||||
### 线下预存款创建字段
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|---|---|---|
|
||||
| `offline_payment_method_id` | 是 | 取自 `GET /api/admin/employee-collection-payment-methods` 的启用项(`enabled=true`,`page_size=100`) |
|
||||
| `external_transaction_no` | 是 | 交易流水号,OCR 预填后人工确认,可编辑;前端不做重复校验 |
|
||||
| `other_voucher_key` | 否 | 其他凭证对象键数组,最多 5 个 |
|
||||
| `payment_voucher_key` | 是 | 支付凭证,至少 1 个,与其他凭证分开提交 |
|
||||
|
||||
历史线下单的收款方式只用 `offline_payment_method_code` / `offline_payment_method_name` 快照展示,不用 `offline_payment_method_id` 反查字典当前值。
|
||||
|
||||
列表筛选保持后端已有参数集合(`page`、`page_size`、`shop_id`、`status`、`recharge_source`、`start_date`、`end_date`),不新增交易流水号筛选,交易流水号只做展示。
|
||||
|
||||
### OCR 预填交互
|
||||
|
||||
- 入口:线下代充弹窗支付凭证区的「识别凭证」按钮,取已上传的第一个 `payment_voucher_key`;未上传凭证时禁用。
|
||||
- 请求:`POST /api/admin/agent-recharges/payment-voucher-ocr`;通过 `BaseService.post` 的第三个参数传 `{ timeout: 30000 }`,请求期间按钮与交易流水号字段展示 loading 并防重复点击。
|
||||
- 成功:把 `external_transaction_no` 写入交易流水号输入框,字段保持可编辑并提示对照凭证核对。
|
||||
- 失败(凭证不是图片、对象不存在、识别服务异常):只提示,不清空已填内容、不阻断手工填写与提交。
|
||||
- 只预填交易流水号,金额、付款人、付款时间、备注一律不预填。
|
||||
|
||||
### 凭证上传类型
|
||||
|
||||
取上传地址时必须显式声明 `content_type` 为 `image/jpeg`,否则 OCR 会以「不是图片」直接拒绝。`VoucherUpload` 新增可选 `contentType` prop,代理充值的支付凭证与其他凭证固定传 `image/jpeg`,并把它透传给 `StorageService.getUploadUrl` 与 `StorageService.uploadFile`。
|
||||
|
||||
### 交易流水号展示
|
||||
|
||||
`payment_transaction_id` 是在线渠道返回的权威交易号,只有在线单有值;`external_transaction_no` 是线下人工申报的交易流水号。两者独立展示、互不覆盖:在线单只展示前者,线下单只展示后者。
|
||||
|
||||
### 详情页保持只读
|
||||
|
||||
代理充值详情页只做信息展示,顶部仅保留返回导航;本次新增的交易流水号、收款方式与其他凭证都只读呈现,不引入任何业务操作按钮。创建、确认线下充值、驳回等操作入口仍留在列表页操作列。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **配置 Key 由后端注册决定**:前端不硬编码,配置项按 `c2b.payment` 模块渲染;若后端最终调整模块归属,只需同步调整菜单跳转的 `module` 参数。
|
||||
- **OCR 端到端约 15 至 16 秒**:必须配置不低于 30 秒的超时并展示 loading,否则用户容易重复点击。
|
||||
- **强制声明 `image/jpeg`**:按需求文档要求统一声明为 `image/jpeg`,上传非 JPEG 图片时以声明类型为准。
|
||||
- **收款方式字典项被引用后会冻结**:展示历史单依赖快照字段,避免字典改名或停用导致历史数据展示漂移。
|
||||
@@ -0,0 +1,36 @@
|
||||
# Change: 代理自充收款方式配置与线下预存款审批字段
|
||||
|
||||
## Why
|
||||
|
||||
8 月迭代 AUG26-017(对应 PRD-008-021 与 PRD-008-013)包含两项要求:代理在线自充的收款方式由超级管理员维护「允许范围」(仅微信 / 仅支付宝 / 同时支持),代理实际可用方式取允许范围与当前可用商户方式的交集,交集为空时不允许创建在线充值单;线下预存款审批需要补齐「收款方式」「交易流水号」「其他凭证」,其中交易流水号支持从付款凭证 OCR 预填。
|
||||
|
||||
后台管理端现状与该要求有差距:在线充值直接调用旧接口 `GET /api/admin/agent-recharges/payment-methods`,线下代充表单只有店铺、支付凭证与运营备注,充值列表与详情也没有交易流水号、线下收款方式快照与其他凭证字段。
|
||||
|
||||
本次按后端实际 OpenAPI 契约对齐:可用方式改读 `GET /api/admin/agent-self-recharge-payment-methods`;允许范围的读取与写入沿用受控系统配置 `GET /api/admin/system-configs` 与 `PUT /api/admin/system-configs/{key}`;交易流水号识别使用 `POST /api/admin/agent-recharges/payment-voucher-ocr`。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 在线充值可用支付方式改由 `GET /api/admin/agent-self-recharge-payment-methods` 提供,读取 `methods`、`min_amount`、`max_amount`;旧接口 `GET /api/admin/agent-recharges/payment-methods` 保留但前端不再调用。
|
||||
- `methods` 为空时只提示「当前暂无可用的在线支付方式」并禁用提交,不解释是被允许范围限制还是无可用商户。
|
||||
- 代理自充允许范围不新增专用接口与专用页面:配置项归属 `c2b.payment` 模块,超管沿用「系统配置」页面,通过 `GET /api/admin/system-configs` 读取、`PUT /api/admin/system-configs/{key}` 提交字符串化 `value`;前端不硬编码配置 Key,允许范围对代理与平台账号不可见。
|
||||
- 新增「代理自充设置」菜单(仅超级管理员),跳转到系统配置页面并带 `module=c2b.payment` 过滤条件;系统配置页面支持从路由 query 初始化模块筛选。
|
||||
- 线下预存款创建弹窗新增:收款方式(数据源 `GET /api/admin/employee-collection-payment-methods` 的启用项,提交 `offline_payment_method_id`)、交易流水号(必填、可编辑)、其他凭证(可选,最多 5 个 `other_voucher_key`);支付凭证 `payment_voucher_key` 仍必填且至少 1 个,与其他凭证分开提交。
|
||||
- 新增付款凭证 OCR 预填:`POST /api/admin/agent-recharges/payment-voucher-ocr` 请求 `{ payment_voucher_key }`、响应只有 `{ external_transaction_no }`;结果只作预填且始终可编辑,识别失败不阻断人工填写与提交,请求超时不低于 30 秒并展示 loading。
|
||||
- 充值列表与详情展示新增字段:`external_transaction_no`、`offline_payment_method_id`、`offline_payment_method_code`、`offline_payment_method_name`、`other_voucher_key`;线下单收款方式使用编码/名称快照展示,不用 `id` 反查字典当前值。
|
||||
- 代理充值列表不新增交易流水号筛选:`GET /api/admin/agent-recharges` 未提供该查询参数,交易流水号仅做展示。
|
||||
- 付款凭证与其他凭证获取上传地址时显式声明 `content_type` 为 `image/jpeg`。
|
||||
- 交易流水号 `external_transaction_no` 与在线渠道 `payment_transaction_id` 独立展示、互不覆盖;前端不做交易流水号重复校验。
|
||||
- 不实现后端接口、商户池逻辑、企微审批回调与 H5/C 端页面。
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs: `agent-recharge`、`system-config-management`
|
||||
- Affected code:
|
||||
- `src/types/api/agentRecharge.ts`、`src/api/modules/agentRecharge.ts`、`src/types/api/index.ts`
|
||||
- `src/views/finance/agent-recharge/index.vue`、`src/views/finance/agent-recharge/detail.vue`
|
||||
- `src/views/settings/system-configs/index.vue`、`src/types/api/systemConfig.ts`
|
||||
- `src/router/routesAlias.ts`、`src/router/routes/asyncRoutes.ts`
|
||||
- `src/components/business/VoucherUpload.vue`
|
||||
- `src/locales/langs/{zh,en}.json`
|
||||
- Dependencies: `docs/产品迭代8月份/代理.md`
|
||||
- Out of scope: 代理自充设置独立页面、允许范围审计查询接口、企业账号分支、H5/C 端在线充值。
|
||||
@@ -0,0 +1,132 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 代理在线自充可用收款方式
|
||||
|
||||
前端 SHALL 通过 `GET /api/admin/agent-self-recharge-payment-methods` 获取代理在线自充的可用支付方式与金额范围,并在该接口返回空 `methods` 时禁止创建在线充值单。
|
||||
|
||||
#### Scenario: 读取可用支付方式
|
||||
|
||||
- **WHEN** 平台账号或代理账号进入代理充值页面并打开在线充值弹窗
|
||||
- **THEN** 前端 MUST 调用 `GET /api/admin/agent-self-recharge-payment-methods`
|
||||
- **AND** 前端 MUST 使用响应 `methods` 渲染可选的微信与支付宝方式
|
||||
- **AND** 前端 MUST 使用响应 `min_amount` 与 `max_amount` 作为金额上下限
|
||||
- **AND** 前端 MUST NOT 再调用 `GET /api/admin/agent-recharges/payment-methods`
|
||||
|
||||
#### Scenario: 可用方式为空
|
||||
|
||||
- **GIVEN** `GET /api/admin/agent-self-recharge-payment-methods` 返回空 `methods`
|
||||
- **WHEN** 用户打开在线充值弹窗
|
||||
- **THEN** 前端 MUST 提示「当前暂无可用的在线支付方式」
|
||||
- **AND** 前端 MUST 禁用在线充值提交
|
||||
- **AND** 前端 MUST NOT 解释为空的原因是被允许范围限制还是无可用商户
|
||||
- **AND** 前端 MUST NOT 因空列表报错或阻断页面其余功能
|
||||
|
||||
#### Scenario: 超级管理员不通过该接口读取允许范围
|
||||
|
||||
- **WHEN** 当前登录账号为超级管理员
|
||||
- **THEN** 前端 MUST NOT 为读取允许范围调用 `GET /api/admin/agent-self-recharge-payment-methods`
|
||||
- **AND** 前端 MUST 统一处理该接口对超级管理员返回的 403
|
||||
|
||||
#### Scenario: 允许范围变更不影响存量待支付单
|
||||
|
||||
- **WHEN** 允许范围配置发生变更
|
||||
- **THEN** 前端 MUST NOT 刷新或改写已创建待支付充值单的支付方式与商户信息
|
||||
|
||||
### Requirement: 线下预存款申请字段
|
||||
|
||||
线下预存款创建请求 SHALL 在 `payment_method` 为 `offline` 时提交 `offline_payment_method_id`、`external_transaction_no` 与可选的 `other_voucher_key`,并与既有的 `payment_voucher_key` 分开提交。
|
||||
|
||||
#### Scenario: 提交线下预存款申请
|
||||
|
||||
- **GIVEN** 用户以平台账号或超级管理员身份打开线下代充弹窗
|
||||
- **WHEN** 用户提交申请
|
||||
- **THEN** 请求体 MUST 包含 `amount`、`payment_method` 为 `offline` 与 `shop_id`
|
||||
- **AND** MUST 包含必填 `offline_payment_method_id`,取值来自 `GET /api/admin/employee-collection-payment-methods` 的启用项
|
||||
- **AND** MUST 包含必填 `external_transaction_no`
|
||||
- **AND** MUST 包含至少 1 个 `payment_voucher_key`
|
||||
- **AND** MAY 包含最多 5 个 `other_voucher_key`
|
||||
|
||||
#### Scenario: 支付凭证与其他凭证分开提交
|
||||
|
||||
- **WHEN** 用户上传支付凭证与其他凭证
|
||||
- **THEN** 支付凭证 MUST 通过 `payment_voucher_key` 提交
|
||||
- **AND** 其他凭证 MUST 通过 `other_voucher_key` 提交
|
||||
- **AND** 前端 MUST NOT 将两类凭证合并到同一字段
|
||||
|
||||
#### Scenario: 交易流水号与在线交易号互不覆盖
|
||||
|
||||
- **WHEN** 前端提交或展示线下预存款申请
|
||||
- **THEN** `external_transaction_no` MUST 只作为线下人工申报的交易流水号
|
||||
- **AND** 前端 MUST NOT 用 `payment_transaction_id` 覆盖或替代 `external_transaction_no`
|
||||
- **AND** 前端 MUST NOT 对 `external_transaction_no` 做重复性校验
|
||||
|
||||
### Requirement: 付款凭证交易流水号识别预填
|
||||
|
||||
前端 SHALL 通过 `POST /api/admin/agent-recharges/payment-voucher-ocr` 以已上传的付款凭证对象键换取交易流水号预填值,识别失败 MUST NOT 阻断人工填写与提交。
|
||||
|
||||
#### Scenario: 识别成功预填交易流水号
|
||||
|
||||
- **GIVEN** 线下代充弹窗已上传至少 1 个付款凭证
|
||||
- **WHEN** 用户点击识别凭证
|
||||
- **THEN** 前端 MUST 调用 `POST /api/admin/agent-recharges/payment-voucher-ocr`,请求体为 `{ payment_voucher_key }`
|
||||
- **AND** 前端 MUST 将响应 `external_transaction_no` 写入交易流水号输入框
|
||||
- **AND** 交易流水号输入框 MUST 保持可编辑并提示用户对照凭证核对
|
||||
|
||||
#### Scenario: 识别失败不阻断提交
|
||||
|
||||
- **GIVEN** OCR 返回失败,包括凭证不是图片、对象不存在或识别服务异常
|
||||
- **WHEN** 用户点击识别凭证
|
||||
- **THEN** 前端 MUST 展示失败提示
|
||||
- **AND** 前端 MUST NOT 阻断用户手动填写交易流水号并提交
|
||||
- **AND** 前端 MUST NOT 清空用户已填写内容
|
||||
|
||||
#### Scenario: 识别请求展示加载态并使用足够超时
|
||||
|
||||
- **WHEN** 前端发起 OCR 请求
|
||||
- **THEN** 前端 MUST 在请求期间展示 loading 并防止重复提交
|
||||
- **AND** 请求超时 MUST NOT 低于 30 秒
|
||||
|
||||
#### Scenario: 只预填交易流水号
|
||||
|
||||
- **WHEN** OCR 识别成功
|
||||
- **THEN** 前端 MUST 只预填 `external_transaction_no`
|
||||
- **AND** 前端 MUST NOT 预填金额、付款人、付款时间或备注
|
||||
|
||||
### Requirement: 代理充值交易流水号与收款方式展示
|
||||
|
||||
代理充值列表与详情 SHALL 展示线下预存款的 `external_transaction_no` 与收款方式快照,在线充值只展示 `payment_transaction_id`。
|
||||
|
||||
#### Scenario: 列表展示线下补充字段
|
||||
|
||||
- **WHEN** 列表返回 `payment_method` 为 `offline` 的充值单
|
||||
- **THEN** 列表 MUST 展示 `external_transaction_no`
|
||||
- **AND** 列表 MUST 使用 `offline_payment_method_name` 展示收款方式
|
||||
|
||||
#### Scenario: 详情展示收款方式快照
|
||||
|
||||
- **WHEN** 详情返回历史线下充值单
|
||||
- **THEN** 前端 MUST 使用 `offline_payment_method_code` 与 `offline_payment_method_name` 展示收款方式
|
||||
- **AND** 前端 MUST NOT 用 `offline_payment_method_id` 反查字典当前值
|
||||
- **AND** 前端 MUST 展示 `other_voucher_key` 对应的其他凭证
|
||||
|
||||
#### Scenario: 两类交易号独立展示
|
||||
|
||||
- **WHEN** 详情展示在线充值单
|
||||
- **THEN** 前端 MUST 只展示 `payment_transaction_id`
|
||||
- **AND** 前端 MUST NOT 把 `external_transaction_no` 当作在线渠道交易号展示
|
||||
|
||||
#### Scenario: 列表不新增交易流水号筛选
|
||||
|
||||
- **WHEN** 前端实现代理充值列表筛选
|
||||
- **THEN** 前端 MUST 只使用 `page`、`page_size`、`shop_id`、`status`、`recharge_source`、`start_date` 与 `end_date`
|
||||
- **AND** 前端 MUST NOT 新增交易流水号筛选条件
|
||||
|
||||
### Requirement: 付款凭证上传类型声明
|
||||
|
||||
代理充值付款凭证与其他凭证在获取上传地址时 SHALL 显式声明 `content_type` 为 `image/jpeg`。
|
||||
|
||||
#### Scenario: 上传地址声明图片类型
|
||||
|
||||
- **WHEN** 前端为代理充值线下凭证请求上传地址
|
||||
- **THEN** 请求 MUST 携带 `content_type` 为 `image/jpeg`
|
||||
- **AND** 上传请求 MUST 使用相同的内容类型
|
||||
@@ -0,0 +1,42 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 代理自充允许范围配置项
|
||||
|
||||
超级管理员维护的代理自充允许范围 SHALL 作为受控系统配置项,通过既有系统配置页面读取与更新,前端 MUST NOT 新增专用接口或专用页面。
|
||||
|
||||
#### Scenario: 通过系统配置读取允许范围
|
||||
|
||||
- **WHEN** 超级管理员进入系统配置页面
|
||||
- **THEN** 前端 MUST 调用 `GET /api/admin/system-configs` 获取配置列表
|
||||
- **AND** 后端注册的代理自充允许范围配置项 MUST 依据返回元数据渲染
|
||||
- **AND** 前端 MUST NOT 为读取允许范围调用专用接口
|
||||
|
||||
#### Scenario: 以枚举控件编辑允许范围
|
||||
|
||||
- **GIVEN** 允许范围配置项返回非空 `enum_values`
|
||||
- **WHEN** 超级管理员编辑该项
|
||||
- **THEN** 页面 MUST 使用枚举选择控件渲染允许范围
|
||||
- **AND** 页面 MUST 以字符串化 `value` 调用 `PUT /api/admin/system-configs/{key}`
|
||||
- **AND** 页面 MUST 在提交前校验取值属于 `enum_values`
|
||||
- **AND** 允许范围枚举值 MUST 以中文标签展示,包括仅微信支付、仅支付宝支付与同时支持微信与支付宝
|
||||
|
||||
#### Scenario: 不硬编码配置 Key
|
||||
|
||||
- **WHEN** 前端实现允许范围配置项的展示与编辑
|
||||
- **THEN** 前端 MUST NOT 硬编码该配置项的 `config_key`
|
||||
- **AND** 前端 MUST 依据 `config_key`、`module`、`value_type`、`control` 与 `enum_values` 等元数据驱动渲染
|
||||
- **AND** 该配置项归属 `c2b.payment` 模块,前端 MUST NOT 扩展模块枚举取值
|
||||
|
||||
#### Scenario: 代理自充设置菜单带入模块筛选
|
||||
|
||||
- **GIVEN** 当前登录账号为超级管理员
|
||||
- **WHEN** 用户点击「代理自充设置」菜单
|
||||
- **THEN** 前端 MUST 跳转到系统配置页面并携带 `module=c2b.payment`
|
||||
- **AND** 系统配置页面 MUST 使用该 query 初始化模块筛选并据此查询配置列表
|
||||
- **AND** 非超级管理员 MUST NOT 看到该菜单
|
||||
|
||||
#### Scenario: 允许范围对代理与平台不可见
|
||||
|
||||
- **WHEN** 当前登录账号不是超级管理员
|
||||
- **THEN** 前端 MUST NOT 向代理与平台账号展示允许范围配置的入口或当前值
|
||||
- **AND** 代理与平台账号 MUST 只能通过 `GET /api/admin/agent-self-recharge-payment-methods` 获取实际可用方式
|
||||
@@ -0,0 +1,55 @@
|
||||
## 1. Contract and API Types
|
||||
|
||||
- [x] 1.1 `src/types/api/agentRecharge.ts`:`AgentRecharge` 新增 `external_transaction_no`、`offline_payment_method_id`、`offline_payment_method_code`、`offline_payment_method_name`、`other_voucher_key`(`string[] | null`),保持 `payment_transaction_id` 与 `payment_voucher_key` 语义不变。
|
||||
- [x] 1.2 扩展 `CreateAgentRechargeOfflineRequest`:新增必填 `offline_payment_method_id`、必填 `external_transaction_no`、可选 `other_voucher_key`(最多 5 个)。
|
||||
- [x] 1.3 新增 `AgentRechargePaymentVoucherOcrRequest { payment_voucher_key }` 与 `AgentRechargePaymentVoucherOcrResponse { external_transaction_no }`。
|
||||
- [x] 1.4 在 `src/types/api/index.ts` 导出新增类型。
|
||||
|
||||
## 2. Agent Recharge Service
|
||||
|
||||
- [x] 2.1 `src/api/modules/agentRecharge.ts` 新增 `getSelfRechargePaymentMethods()`,请求 `GET /api/admin/agent-self-recharge-payment-methods`。
|
||||
- [x] 2.2 新增 `recognizePaymentVoucher(data)`,请求 `POST /api/admin/agent-recharges/payment-voucher-ocr`,超时不低于 30000 毫秒。
|
||||
- [x] 2.3 保留旧 `getPaymentMethods()` 方法,但页面不再调用。
|
||||
|
||||
## 3. Online Recharge Available Methods
|
||||
|
||||
- [x] 3.1 在线充值弹窗改用 `getSelfRechargePaymentMethods()` 读取 `methods` / `min_amount` / `max_amount`,并把 `wechat`、`alipay` 映射为微信、支付宝选项。
|
||||
- [x] 3.2 `methods` 为空时提示「当前暂无可用的在线支付方式」并禁用提交,不解释原因。
|
||||
- [x] 3.3 超管视角不请求该接口(403 兜底);允许范围变更后不刷新已创建的待支付单。
|
||||
|
||||
## 4. Super Admin Allow-Range Config
|
||||
|
||||
- [x] 4.1 允许范围沿用系统配置页通用渲染:`GET /api/admin/system-configs` 读取、`PUT /api/admin/system-configs/{key}` 提交字符串化 `value`;不新增专用接口与专用页面。
|
||||
- [x] 4.2 `wechat_only` / `alipay_only` / `both` 在系统配置页展示与选择时显示中文标签,未命中映射时回退原值。
|
||||
- [x] 4.3 新增「代理自充设置」菜单(仅超级管理员),跳转到系统配置页面并带 `module=c2b.payment`;系统配置页支持从 `route.query.module` 初始化模块筛选。
|
||||
- [x] 4.4 在 `zh.json`、`en.json` 的 `menus.settings` 下补充 `agentSelfRecharge` 中英文文案,并清理系统配置页预存的空样式块。
|
||||
|
||||
## 5. Offline Pre-deposit Create Dialog
|
||||
|
||||
- [x] 5.1 新增「收款方式」下拉,数据来自 `GET /api/admin/employee-collection-payment-methods`(`enabled=true`、`page_size=100`),必填,提交 `offline_payment_method_id`。
|
||||
- [x] 5.2 新增「交易流水号」输入(必填、可编辑),并提示对照凭证核对。
|
||||
- [x] 5.3 新增「其他凭证」上传(可选,最多 5 个),提交 `other_voucher_key`;支付凭证仍必填且至少 1 个,两者分开提交。
|
||||
- [x] 5.4 `VoucherUpload` 新增可选 `contentType` prop 并透传给 `getUploadUrl` 与 `uploadFile`;代理充值线下凭证固定传 `image/jpeg`。
|
||||
- [x] 5.5 线下提交体按契约组装:`{ amount, payment_method: offline, shop_id, offline_payment_method_id, external_transaction_no, payment_voucher_key, other_voucher_key, remark }`,未填写的可选字段不提交。
|
||||
|
||||
## 6. Voucher OCR Prefill
|
||||
|
||||
- [x] 6.1 支付凭证区新增「识别凭证」按钮,取第一个已上传凭证 Key;未上传凭证时禁用。
|
||||
- [x] 6.2 识别期间展示 loading(按钮与交易流水号字段)并防重复点击,超时不低于 30 秒。
|
||||
- [x] 6.3 识别成功写入 `external_transaction_no`,字段保持可编辑;失败只提示,不清空已填内容、不阻断提交。
|
||||
- [x] 6.4 不预填金额、付款人、付款时间与备注。
|
||||
|
||||
## 7. List and Detail Display
|
||||
|
||||
- [x] 7.1 列表新增线下交易流水号与线下收款方式(`offline_payment_method_name` 快照)展示,仅线下单展示。
|
||||
- [x] 7.2 详情新增交易流水号、收款方式(`offline_payment_method_code` 与 `offline_payment_method_name` 快照)与其他凭证预览;在线单只展示 `payment_transaction_id`。
|
||||
- [x] 7.3 不使用 `offline_payment_method_id` 反查字典当前值,也不做交易流水号重复校验。
|
||||
- [x] 7.4 列表不新增交易流水号筛选,保持后端已有查询参数集合不变。
|
||||
|
||||
## 8. Verification
|
||||
|
||||
- [ ] 8.1 校验 `methods` 为空、超管调用新接口 403、允许范围变更不影响存量待支付单三种情况。
|
||||
- [ ] 8.2 校验 OCR 成功、失败、超时三条路径均不阻断人工填写与提交。
|
||||
- [ ] 8.3 校验线下单与在线单的两类交易号、收款方式快照与其他凭证展示正确。
|
||||
- [x] 8.4 运行 `npm run build`(含 `vue-tsc --noEmit`)与 `npm run check:encoding`。
|
||||
- [x] 8.5 运行 `openspec validate update-agent-self-recharge-and-offline-approval --strict`。
|
||||
@@ -0,0 +1,54 @@
|
||||
# 商户池详情与商户凭证契约设计
|
||||
|
||||
## Context
|
||||
|
||||
支付商户与商户池管理页已经上线,运营在使用时遇到三个问题:商户池详情入口和支付商户不一致且信息不完整、新增商户池直接报错、商户凭证缺少字段枚举与类型校验。本次优化只新增详情页、收敛成员顺序数据源,并在既有凭证写入表单上叠加契约校验,不改变接口路径与请求结构。
|
||||
|
||||
## Goals
|
||||
|
||||
- 让商户池详情的入口和展示完整度与支付商户详情对齐。
|
||||
- 让新增/编辑商户池恢复可用,并且拖拽排序结果仍按顺序提交。
|
||||
- 让前端提交的商户凭证满足后端的字段枚举、必填键、值类型与商户标识约束。
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- 不改变“更换凭证”表单的交互方式与凭证值不回显约束。
|
||||
- 不新增后端接口。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 商户池详情复用支付商户详情的入口模式
|
||||
|
||||
支付商户详情已采用“列表点击名称 → 独立详情页”的模式,因此商户池改为同样方式:列表名称列渲染为可点击文本,点击后跳转 `/settings/payment-merchant-pools/pool-detail/:id`,并移除行操作里的“详情”抽屉。
|
||||
|
||||
详情页复用 `src/components/common/DetailPage.vue`,按“基本信息”“轮询配置”“运行状态”三组展示字段。创建时间、更新时间只在接口返回时渲染,避免出现空白字段行。
|
||||
|
||||
替代方案是保留抽屉并补齐字段;但两个详情入口不一致会让运营难以形成稳定预期,因此不采用。
|
||||
|
||||
### 成员商户名称由前端解析
|
||||
|
||||
详情接口只返回 `member_ids`。详情页在加载详情后按该商户池的 `payment_method` 拉取支付商户列表,把成员 ID 映射为商户名称展示,同时保留成员数量。解析不到名称时展示“未知商户”,不展示 `member_ids` 原始 ID,避免把内部标识暴露给运营。
|
||||
|
||||
### 成员顺序使用单一数据源
|
||||
|
||||
原实现同时监听 `form.member_ids` 和 `orderedMemberIds`,并在两个回调中互相赋新数组,形成“赋值 → 触发 → 再赋值”的无限循环,从而在新增商户池时触发 `Maximum recursive updates exceeded`。
|
||||
|
||||
修复方式是把成员顺序收敛为单一数据源:`orderedMemberIds` 改为基于 `form.member_ids` 的 `computed`(get 返回成员数组,set 写回成员数组)。`VueDraggable` 通过 `v-model` 触发 setter 写回 `form.member_ids`,不再存在互相触发的 watch。
|
||||
|
||||
### 凭证字段枚举固化在类型模块并在提交前校验
|
||||
|
||||
凭证必填键由后端契约按 `payment_method` 与 `provider_type` 组合给出,前端与该契约保持一致,因此把枚举定义在 `src/types/api/paymentMerchantPools.ts`:`PAYMENT_CREDENTIAL_FIELD_SPECS` 描述每个 `provider_type` 的必填键与可选键,`PAYMENT_CREDENTIAL_BOOLEAN_KEYS`、`PAYMENT_CREDENTIAL_INTEGER_KEYS` 描述 `ali_production`、`ali_pay_expire_minutes` 的值类型,`PAYMENT_MERCHANT_IDENTITY_KEYS` 描述商户标识必须一致的凭证字段。枚举之外的字段名一律拦截,避免提交必然被后端拒绝的请求。
|
||||
|
||||
`buildPaymentCredentials` 负责提交前校验并转换:拒绝不属于当前服务商的字段名、补齐必填键检查、把布尔字段与整数字段从输入框字符串转换为布尔值/数字、校验 `merchant_identity` 与对应凭证字段一致。表单继续使用“字段名 + 字段值”的通用编辑方式,只在分隔线下方展示当前服务商的必填/可选字段提示。
|
||||
|
||||
替代方案是把凭证表单改成按服务商渲染固定中文标签字段;该方案会改变既有交互与凭证只写约定,本次不采用。
|
||||
|
||||
### 凭证仅平台账号可读写
|
||||
|
||||
凭证读取与写入入口复用既有平台账号限制:`/settings/payment-merchant-pools` 及其详情路由都带 `allowedUserTypes: [1, 2]`,页面内 `canManage` 再按 `isPlatformAccount` 过滤操作。凭证值只在当前编辑会话内存中存在,提交、取消或关闭后清空,不进入 Pinia 持久化、浏览器存储、日志或错误上报。
|
||||
|
||||
## Risks and Trade-offs
|
||||
|
||||
- 详情页与支付商户详情一样受 `allowedUserTypes: [1, 2]` 限制,代理与企业账号无法访问。
|
||||
- 商户池成员名称依赖支付商户列表接口,成员数量超过单页上限时可能解析不到名称,此时展示“未知商户”而不是原始 ID。
|
||||
- 凭证字段枚举与后端校验规则必须保持一致;后端新增字段时前端需要同步更新枚举,否则提交会被前端拦截。
|
||||
@@ -0,0 +1,48 @@
|
||||
# Change: 商户池详情与商户凭证契约
|
||||
|
||||
## Why
|
||||
|
||||
`docs/产品迭代8月份/支付商户优化.md` 记录了支付商户与商户池管理页的三个问题,加上运营补充的凭证契约与权限要求,本次处理三件事:
|
||||
|
||||
- 商户池详情与支付商户详情的入口不一致:支付商户是点击名称进入独立详情页,商户池却是行操作里的详情抽屉,且详情没有展示接口已返回的全部字段(例如 `time_period_started_at`),成员只给出数量。
|
||||
- 点击“新增商户池”时页面抛出 `Maximum recursive updates exceeded in component <PaymentMerchantPoolManagement>`,新增和编辑商户池流程完全不可用。
|
||||
- 商户凭证的字段契约此前只存在于接口文档:必填键由 `payment_method` 与 `provider_type` 组合决定,前端提交前没有字段枚举与类型校验,容易出现漏填必填键、`ali_production` / `ali_pay_expire_minutes` 以字符串提交、`merchant_identity` 与凭证字段不一致等会被后端拒绝的请求。
|
||||
|
||||
“更换凭证”表单的交互(手工逐行填写字段名与字段值、值不回显)按反馈保持原有行为,本次只在其上叠加字段契约校验。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 新增商户池详情页,并改为点击列表中商户池名称进入,与支付商户详情保持一致;移除列表行操作中的“详情”入口。
|
||||
- 商户池详情页展示详情接口返回的业务字段:商户池名称、支付方式、启停状态、成员数量、成员商户、轮询策略、统计周期、金额/笔数/时间阈值、时间起点、路由世代、备注,以及接口返回时的创建时间和更新时间。
|
||||
- 成员商户按支付商户名称展示,不展示 `member_ids` 原始 ID;名称无法解析时展示“未知商户”占位文案。
|
||||
- 合并商户池表单中 `form.member_ids` 与 `orderedMemberIds` 的双向 `watch` 为单一数据源,消除递归更新错误,同时保持拖拽排序结果按顺序提交。
|
||||
- 将商户凭证字段枚举(各 `payment_method` + `provider_type` 组合的必填键与可选键)固化到前端类型模块,并在提交前校验字段枚举、必填键、值类型(布尔/整数/字符串)与 `merchant_identity` 一致性。
|
||||
- 凭证写入表单展示当前服务商组合的必填/可选字段提示,降低漏填必填键的概率。
|
||||
- 明确凭证访问控制:仅超级管理员(`user_type=1`)与平台用户(`user_type=2`)可读写商户凭证,凭证内容不进入日志、审计与支付快照。
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs: `payment-merchant-pool-management`
|
||||
- Affected code:
|
||||
- `src/views/settings/payment-merchant-pools/pool-detail.vue`(新增)
|
||||
- `src/views/settings/payment-merchant-pools/components/PoolManagement.vue`
|
||||
- `src/views/settings/payment-merchant-pools/components/MerchantManagement.vue`
|
||||
- `src/types/api/paymentMerchantPools.ts`
|
||||
- `src/router/routes/asyncRoutes.ts`
|
||||
- `src/router/routesAlias.ts`
|
||||
- `src/locales/langs/zh.json`、`src/locales/langs/en.json`
|
||||
- Dependencies:
|
||||
- 后端 `GET /api/admin/payment-merchant-pools/{id}` 返回 `member_ids`、`routing_epoch`、`time_period_started_at` 等字段。
|
||||
- 后端按 `payment_method` 与 `provider_type` 组合校验凭证必填键与值类型。
|
||||
- 成员商户名称由前端使用同 `payment_method` 的支付商户列表解析,不要求后端在商户池详情返回名称。
|
||||
- Compatibility:
|
||||
- 不改变“更换凭证”表单的交互方式与凭证值不回显约束。
|
||||
- 不影响支付商户列表与支付商户详情页的信息结构。
|
||||
- 不改变商户池创建、更新、启用、停用的请求结构。
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- 不按服务商渲染带中文标签的固定凭证表单,仍保留“字段名 + 字段值”的通用编辑方式。
|
||||
- 不新增商户池详情、凭证校验之外的后端接口。
|
||||
- 不在商户池详情页展示 `id`、`member_ids` 等内部标识。
|
||||
- 不改动商户池轮询策略、成员排序规则和阈值换算规则。
|
||||
@@ -0,0 +1,155 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 商户池详情入口
|
||||
|
||||
系统 SHALL 与支付商户保持一致,以点击列表中商户池名称的方式进入独立详情页,并且不再在行操作中提供“详情”入口。
|
||||
|
||||
#### Scenario: 点击商户池名称进入详情页
|
||||
|
||||
- **GIVEN** 管理员位于支付商户与商户池管理页的“商户池”页签
|
||||
- **WHEN** 管理员点击列表中某个商户池名称
|
||||
- **THEN** 系统 MUST 跳转到该商户池的详情页
|
||||
- **AND** 详情页 MUST 通过 `GET /api/admin/payment-merchant-pools/{id}` 加载数据
|
||||
- **AND** 列表行操作 MUST NOT 再提供“详情”操作
|
||||
|
||||
#### Scenario: 商户池名称以可点击样式展示
|
||||
|
||||
- **GIVEN** 商户池列表加载成功
|
||||
- **WHEN** 页面渲染商户池名称列
|
||||
- **THEN** 商户池名称 MUST 以可点击样式展示
|
||||
- **AND** 点击名称 MUST NOT 触发表格行选中或其他行操作
|
||||
|
||||
#### Scenario: 非平台账号不能查看商户池详情
|
||||
|
||||
- **GIVEN** 当前登录账号的 `user_type` 不是 `1` 或 `2`
|
||||
- **WHEN** 用户点击商户池名称或直接访问商户池详情页地址
|
||||
- **THEN** 系统 MUST NOT 展示商户池详情数据
|
||||
- **AND** 路由 MUST 按平台账号限制拦截该访问
|
||||
|
||||
### Requirement: 商户池详情信息完整
|
||||
|
||||
系统 SHALL 在商户池详情页展示详情接口返回的全部业务字段,并 SHALL 以支付商户名称展示成员商户,不得展示 `id` 或 `member_ids` 原始标识。
|
||||
|
||||
#### Scenario: 展示轮询策略与阈值字段
|
||||
|
||||
- **GIVEN** 管理员进入某个商户池详情页
|
||||
- **WHEN** 详情数据加载成功
|
||||
- **THEN** 详情 MUST 展示商户池名称、支付方式、启停状态、轮询策略、路由世代和备注
|
||||
- **AND** `strategy` 为 `amount` 或 `count` 时 MUST 展示统计周期和对应阈值
|
||||
- **AND** `strategy` 为 `time` 时 MUST 展示时间单位、时间长度和时间起点
|
||||
|
||||
#### Scenario: 成员商户按名称展示
|
||||
|
||||
- **GIVEN** 商户池详情返回 `member_ids`
|
||||
- **WHEN** 页面展示成员信息
|
||||
- **THEN** 页面 MUST 展示成员商户名称和成员数量
|
||||
- **AND** 页面 MUST NOT 展示 `member_ids` 原始 ID
|
||||
- **AND** 成员名称无法解析时 MUST 展示“未知商户”而不是 ID
|
||||
|
||||
#### Scenario: 未返回的字段不渲染
|
||||
|
||||
- **GIVEN** 商户池详情接口未返回创建时间或更新时间
|
||||
- **WHEN** 页面展示商户池详情
|
||||
- **THEN** 页面 MUST NOT 渲染对应字段的空白行
|
||||
|
||||
### Requirement: 商户池成员顺序单一数据源
|
||||
|
||||
系统 SHALL 使用单一数据源维护商户池成员顺序,保证新增、编辑和拖拽排序成员时不会出现递归更新错误,并且提交顺序与页面展示顺序一致。
|
||||
|
||||
#### Scenario: 新增商户池不再递归更新
|
||||
|
||||
- **WHEN** 管理员点击“新增商户池”
|
||||
- **THEN** 系统 MUST NOT 抛出 `Maximum recursive updates exceeded`
|
||||
- **AND** 表单 MUST 正常打开并允许选择成员
|
||||
|
||||
#### Scenario: 拖拽后按展示顺序提交
|
||||
|
||||
- **GIVEN** 商户池表单中存在多个成员商户
|
||||
- **WHEN** 管理员拖拽调整成员顺序并提交
|
||||
- **THEN** `member_ids` MUST 按拖拽后的顺序提交
|
||||
- **AND** 再次编辑该商户池时 MUST 按提交顺序展示成员
|
||||
|
||||
### Requirement: 商户凭证字段枚举契约
|
||||
|
||||
系统 SHALL 按 `payment_method` 与 `provider_type` 组合枚举商户凭证的必填键与可选键,并在提交前校验字段枚举、必填键、值类型与商户标识一致性。
|
||||
|
||||
字段枚举以后端契约为准(键名与渠道配置字段一致),前端必须与后端保持一致,后端新增或调整字段时需同步更新前端枚举。`credentials` MUST 为扁平 JSON 对象,必填键为:
|
||||
|
||||
- `payment_method=wechat`、`provider_type=wechat`:`wx_mch_id`、`wx_api_v3_key`、`wx_cert_content`、`wx_key_content`、`wx_serial_no`、`wx_notify_url`
|
||||
- `payment_method=wechat`、`provider_type=wechat_v2`:`wx_mch_id`、`wx_api_v2_key`、`wx_notify_url`
|
||||
- `payment_method=wechat`、`provider_type=fuiou`:`fy_mchnt_cd`、`fy_ins_cd`、`fy_term_id`、`fy_private_key`、`fy_public_key`、`fy_api_url`、`fy_notify_url`
|
||||
- `payment_method=alipay`、`provider_type=alipay`:`ali_app_id`、`ali_private_key`、`ali_public_key`、`ali_notify_url`、`ali_return_url`
|
||||
|
||||
可选键为:`wechat` 可附 `wx_api_v2_key`;`alipay` 可附 `ali_production`(布尔,是否生产环境)与 `ali_pay_expire_minutes`(整数,支付过期分钟数)。除 `ali_production` 与 `ali_pay_expire_minutes` 外,凭证值 MUST 为字符串。`merchant_identity` MUST 分别等于 `wx_mch_id`、`fy_mchnt_cd` 或 `ali_app_id`。
|
||||
|
||||
#### Scenario: 提交微信直连凭证
|
||||
|
||||
- **GIVEN** 管理员提交 `payment_method=wechat`、`provider_type=wechat` 的商户凭证
|
||||
- **THEN** 请求 MUST 包含 `wx_mch_id`、`wx_api_v3_key`、`wx_cert_content`、`wx_key_content`、`wx_serial_no`、`wx_notify_url`
|
||||
- **AND** 请求 MAY 附带 `wx_api_v2_key`
|
||||
- **AND** `merchant_identity` MUST 等于 `wx_mch_id`
|
||||
|
||||
#### Scenario: 提交微信直连 V2 凭证
|
||||
|
||||
- **GIVEN** 管理员提交 `payment_method=wechat`、`provider_type=wechat_v2` 的商户凭证
|
||||
- **THEN** 请求 MUST 包含 `wx_mch_id`、`wx_api_v2_key`、`wx_notify_url`
|
||||
- **AND** `merchant_identity` MUST 等于 `wx_mch_id`
|
||||
|
||||
#### Scenario: 提交富友凭证
|
||||
|
||||
- **GIVEN** 管理员提交 `payment_method=wechat`、`provider_type=fuiou` 的商户凭证
|
||||
- **THEN** 请求 MUST 包含 `fy_mchnt_cd`、`fy_ins_cd`、`fy_term_id`、`fy_private_key`、`fy_public_key`、`fy_api_url`、`fy_notify_url`
|
||||
- **AND** `merchant_identity` MUST 等于 `fy_mchnt_cd`
|
||||
|
||||
#### Scenario: 提交支付宝凭证
|
||||
|
||||
- **GIVEN** 管理员提交 `payment_method=alipay`、`provider_type=alipay` 的商户凭证
|
||||
- **THEN** 请求 MUST 包含 `ali_app_id`、`ali_private_key`、`ali_public_key`、`ali_notify_url`、`ali_return_url`
|
||||
- **AND** 请求 MAY 附带 `ali_production` 与 `ali_pay_expire_minutes`
|
||||
- **AND** `merchant_identity` MUST 等于 `ali_app_id`
|
||||
|
||||
#### Scenario: 缺少必填字段时拒绝提交
|
||||
|
||||
- **WHEN** 提交的凭证缺少当前服务商组合的必填键
|
||||
- **THEN** 前端 MUST 阻止提交并提示缺失的凭证字段名
|
||||
|
||||
#### Scenario: 拒绝不属于当前服务商的凭证字段
|
||||
|
||||
- **WHEN** 提交的凭证包含必填键与可选键之外的字段名
|
||||
- **THEN** 前端 MUST 阻止提交并提示该字段不属于当前服务商支持的字段
|
||||
|
||||
#### Scenario: 校验凭证值类型
|
||||
|
||||
- **GIVEN** 凭证包含 `ali_production` 或 `ali_pay_expire_minutes`
|
||||
- **WHEN** 管理员提交凭证
|
||||
- **THEN** `ali_production` MUST 以布尔值提交
|
||||
- **AND** `ali_pay_expire_minutes` MUST 以正整数提交
|
||||
- **AND** 其余凭证字段 MUST 以字符串提交
|
||||
|
||||
#### Scenario: 校验商户标识一致性
|
||||
|
||||
- **WHEN** `merchant_identity` 与 `wx_mch_id`、`fy_mchnt_cd` 或 `ali_app_id` 的值不一致
|
||||
- **THEN** 前端 MUST 阻止提交并提示商户标识必须与对应凭证字段一致
|
||||
|
||||
### Requirement: 商户凭证访问控制
|
||||
|
||||
系统 SHALL 仅允许超级管理员(`user_type=1`)与平台用户(`user_type=2`)读取和写入商户凭证,且凭证内容 MUST NOT 进入日志、审计记录或支付快照。
|
||||
|
||||
#### Scenario: 平台账号可读写凭证
|
||||
|
||||
- **GIVEN** 当前登录账号的 `user_type` 为 `1` 或 `2`
|
||||
- **WHEN** 账号打开支付商户管理页、支付商户详情页或提交商户凭证
|
||||
- **THEN** 系统 MUST 允许读取凭证配置状态与写入凭证
|
||||
|
||||
#### Scenario: 非平台账号不可读写凭证
|
||||
|
||||
- **GIVEN** 当前登录账号的 `user_type` 为 `3` 或 `4`
|
||||
- **WHEN** 账号访问支付商户管理页或支付商户详情页
|
||||
- **THEN** 系统 MUST 拒绝访问并 MUST NOT 返回凭证字段或凭证状态
|
||||
- **AND** 页面 MUST NOT 渲染凭证写入入口
|
||||
|
||||
#### Scenario: 凭证不写入日志与快照
|
||||
|
||||
- **WHEN** 创建、更新或删除支付商户成功或失败
|
||||
- **THEN** 凭证内容 MUST NOT 出现在浏览器存储、页面日志、错误上报或支付快照中
|
||||
- **AND** 页面 MUST 只展示“已配置/未配置”状态与 `credential_version`
|
||||
@@ -0,0 +1,33 @@
|
||||
# Implementation Tasks
|
||||
|
||||
## 1. 商户池详情入口
|
||||
|
||||
- [x] 1.1 新增商户池详情页 `pool-detail.vue`,复用 `DetailPage` 展示基本信息、轮询配置和运行状态。
|
||||
- [x] 1.2 新增 `/settings/payment-merchant-pools/pool-detail/:id` 路由与路由别名,并按平台账号(`allowedUserTypes: [1, 2]`)限制访问。
|
||||
- [x] 1.3 商户池列表名称列改为可点击文本,点击跳转对应详情页;移除行操作中的“详情”入口。
|
||||
- [x] 1.4 补充中英文路由标题文案 `menus.settings.detailsOfPaymentMerchantPool`。
|
||||
|
||||
## 2. 商户池详情内容
|
||||
|
||||
- [x] 2.1 详情页展示详情接口返回的全部业务字段:名称、支付方式、启停状态、成员数量、成员商户、轮询策略、统计周期、金额/笔数/时间阈值、时间起点、路由世代和备注。
|
||||
- [x] 2.2 成员按支付商户名称展示,不展示 `member_ids` 原始 ID,无法解析时展示“未知商户”。
|
||||
- [x] 2.3 创建时间、更新时间等字段仅在接口返回时渲染,避免空白字段行。
|
||||
|
||||
## 3. 商户池表单稳定性
|
||||
|
||||
- [x] 3.1 将 `orderedMemberIds` 改为基于 `form.member_ids` 的单一数据源,移除互相赋值的双向 watch。
|
||||
- [x] 3.2 验证新增、编辑、切换支付方式、添加成员、移除成员和拖拽排序后提交的成员顺序正确。
|
||||
- 新增/编辑/切换支付方式直接重置 `form.member_ids`;添加与移除成员在 `form.member_ids` 上增删;拖拽经 `VueDraggable` 的 `v-model` 写回同一数组,`buildPayload` 按数组顺序提交。
|
||||
|
||||
## 4. 商户凭证字段枚举与校验
|
||||
|
||||
- [x] 4.1 在 `src/types/api/paymentMerchantPools.ts` 固化凭证字段枚举:`PAYMENT_CREDENTIAL_FIELD_SPECS`(各 `provider_type` 的必填键与可选键)、`PAYMENT_CREDENTIAL_BOOLEAN_KEYS`、`PAYMENT_CREDENTIAL_INTEGER_KEYS`、`PAYMENT_MERCHANT_IDENTITY_KEYS`。
|
||||
- [x] 4.2 实现 `buildPaymentCredentials`:校验字段枚举与必填键,把 `ali_production` 转为布尔值、`ali_pay_expire_minutes` 转为正整数,其余字段保持字符串,并校验 `merchant_identity` 与对应凭证字段一致。
|
||||
- [x] 4.3 支付商户凭证写入表单接入该校验,并在“写入支付凭证”分隔线下方展示当前服务商组合的必填/可选字段提示。
|
||||
- [x] 4.4 凭证读写仍仅限超级管理员(`user_type=1`)与平台用户(`user_type=2`):路由 `allowedUserTypes` 与页面 `canManage` 双层限制,凭证值不进入持久化、日志与错误上报。
|
||||
|
||||
## 5. 验证
|
||||
|
||||
- [x] 5.1 运行 `eslint`、`vue-tsc --noEmit` 与 `vite build`,确认改动通过类型检查和构建。
|
||||
- `eslint`(改动文件)、`vue-tsc --noEmit` 均通过;`vite build --mode development` 成功产出 `MerchantManagement`、`PoolManagement` 与 `pool-detail` chunk。
|
||||
- [x] 5.2 执行 `openspec validate update-payment-merchant-pool-optimization --strict`。
|
||||
Reference in New Issue
Block a user