# Payment Merchant Pool Management Specification ## ADDED Requirements ### Requirement: 平台专属的商户池管理入口 系统 SHALL 仅向超级管理员和平台用户开放支付商户、商户池及微信授权配置的管理入口和操作。 #### Scenario: 超级管理员可见并访问 - **GIVEN** 当前登录账号的 `user_type` 为 `1` - **WHEN** 用户加载设置菜单或访问 `/settings/payment-merchant-pools` - **THEN** 系统 MUST 展示商户池管理入口并允许进入页面 #### Scenario: 平台用户可见并访问 - **GIVEN** 当前登录账号的 `user_type` 为 `2` - **WHEN** 用户加载设置菜单或访问 `/settings/payment-merchant-pools` - **THEN** 系统 MUST 展示商户池管理入口并允许进入页面 #### Scenario: 非平台账号被拒绝 - **GIVEN** 当前登录账号的 `user_type` 为 `3` 或 `4` - **WHEN** 用户加载设置菜单或直接输入 `/settings/payment-merchant-pools` - **THEN** 系统 MUST NOT 展示商户池管理入口 - **AND** 系统 MUST 拒绝直接访问该页面 - **AND** 系统 MUST NOT 返回商户、商户池或授权配置数据 ### Requirement: 支付商户管理接口 系统 SHALL 提供支付商户的分页查询、创建、详情、按需更新和删除接口。 #### Scenario: 查询和筛选支付商户 - **WHEN** 管理员请求 `GET /api/admin/payment-merchants` - **THEN** 系统 MUST 支持 `page`、`page_size`、`payment_method` 和 `enabled` 查询参数 - **AND** `payment_method` MUST 支持 `wechat` 和 `alipay` - **AND** 响应中的每个商户 MUST 包含 `id`、`name`、`payment_method`、`provider_type`、`merchant_identity`、`enabled`、`remark`、`credential_version`、`created_at` 和 `updated_at` #### Scenario: 创建支付商户 - **WHEN** 管理员向 `POST /api/admin/payment-merchants` 提交 `name`、`payment_method`、`provider_type`、`merchant_identity`、`credentials`、`enabled` 和 `remark` - **THEN** 系统 MUST 创建支付商户并返回新商户记录 - **AND** `provider_type` 为 `wechat` 时 MUST 只接受 `wechat`、`wechat_v2` 或 `fuiou` - **AND** `provider_type` 为 `alipay` 时 MUST 只接受 `alipay` #### Scenario: 查询支付商户详情 - **WHEN** 管理员请求 `GET /api/admin/payment-merchants/{id}` - **THEN** 系统 MUST 返回指定商户的详情 - **AND** 响应 MUST NOT 包含任何支付凭证明文 #### Scenario: 按需更新和切换商户状态 - **WHEN** 管理员请求 `PUT /api/admin/payment-merchants/{id}` 并只提交 `enabled` - **THEN** 系统 MUST 只更新该商户的 `enabled` 字段 - **AND** 系统 MUST 保留未提交字段的原值 #### Scenario: 确认后删除支付商户 - **WHEN** 管理员请求 `DELETE /api/admin/payment-merchants/{id}` 并提交 `confirm=true` - **THEN** 系统 MUST 删除目标商户 - **AND** 前端 MUST 在发送请求前展示二次确认 ### Requirement: 支付商户列表与启停交互 后台商户管理页 SHALL 展示支付商户状态,并允许有权限的管理员按接口契约切换启用状态。 #### Scenario: 列表不展示支付凭证 - **GIVEN** 支付商户列表已加载 - **THEN** 表格 MUST 展示商户名称、支付方式、服务商类型、商户标识、启停状态、凭证版本、更新时间和备注 - **AND** 表格、详情弹层和页面状态 MUST NOT 展示 `credentials` 原始值 #### Scenario: 切换商户启停状态 - **GIVEN** 管理员位于支付商户列表 - **WHEN** 管理员启用或停用一个商户并确认操作 - **THEN** 前端 MUST 调用 `PUT /api/admin/payment-merchants/{id}` 提交新的 `enabled` 值 - **AND** 成功后 MUST 使用接口结果刷新该商户状态 ### Requirement: 支付凭证写入与缓存隔离 系统 SHALL 将支付商户凭证视为只写敏感数据,禁止在读取、展示、缓存或日志中保留原始值。 #### Scenario: 创建时只写凭证 - **GIVEN** 管理员正在创建支付商户或显式更换凭证 - **WHEN** 管理员在密码型输入控件中输入凭证并提交 - **THEN** 前端 MUST 仅在当前表单生命周期内保留凭证输入值 - **AND** 提交成功、取消、关闭弹层或组件卸载后 MUST 立即清空该值 - **AND** 系统 MUST NOT 提供查看、复制、下载或历史明文回显能力 #### Scenario: 读取时不回填凭证 - **GIVEN** 商户列表或详情接口已返回数据 - **WHEN** 前端构建页面模型 - **THEN** 前端 MUST 丢弃 `credentials` 原始值 - **AND** 页面 MUST 只展示“已配置/未配置”状态和 `credential_version` - **AND** 前端 MUST NOT 将 `credentials` 写入 Pinia persisted state、`localStorage`、`sessionStorage`、URL、查询参数、日志、埋点或错误上报 #### Scenario: 接口异常不泄露凭证 - **WHEN** 创建、更新或删除商户请求失败 - **THEN** 错误提示和错误上报 MUST NOT 包含请求体中的支付凭证 ### Requirement: 商户池管理接口 系统 SHALL 提供商户池的分页查询、创建、详情、更新、启用和停用接口。 #### Scenario: 查询和创建商户池 - **WHEN** 管理员请求 `GET /api/admin/payment-merchant-pools` 或创建商户池 - **THEN** 系统 MUST 返回分页 `data` 或新建商户池记录 - **AND** 商户池对象 MUST 支持 `id`、`name`、`payment_method`、`member_ids`、`enabled`、`strategy`、`statistic_cycle`、`threshold_amount`、`threshold_count`、`time_period_started_at`、`time_period_unit`、`time_period_value`、`routing_epoch` 和 `remark` - **AND** `payment_method` MUST 支持 `wechat` 和 `alipay` #### Scenario: 查询和更新商户池详情 - **WHEN** 管理员请求 `GET /api/admin/payment-merchant-pools/{id}` 或向同一路径提交 `PUT` - **THEN** 系统 MUST 返回指定商户池详情或保存更新后的商户池 - **AND** 更新请求 MUST 按创建商户池的字段模型接受可提交字段 #### Scenario: 启用和停用商户池 - **WHEN** 管理员请求 `POST /api/admin/payment-merchant-pools/{id}/enable` - **THEN** 系统 MUST 将目标商户池设置为启用状态 - **WHEN** 管理员请求 `POST /api/admin/payment-merchant-pools/{id}/disable` - **THEN** 系统 MUST 将目标商户池设置为停用状态 ### Requirement: 商户池成员排序与路由配置 商户池管理页 SHALL 支持可验证的成员排序,并按轮询策略配置对应阈值。 #### Scenario: 成员顺序按数组顺序保存 - **GIVEN** 商户池表单中存在多个同支付方式的候选商户 - **WHEN** 管理员拖拽调整成员顺序并提交 - **THEN** `member_ids` MUST 按拖拽后的顺序提交 - **AND** 系统 MUST 拒绝重复的商户 ID - **AND** 更新详情或重新编辑时 MUST 按接口返回的 `member_ids` 顺序展示 #### Scenario: 按金额轮换 - **GIVEN** 管理员选择 `strategy=amount` - **WHEN** 管理员填写金额阈值并提交 - **THEN** 页面 MUST 使用元作为输入单位并转换为整数分写入 `threshold_amount` - **AND** `statistic_cycle` MUST 为 `round`、`day` 或 `month` - **AND** `threshold_amount` MUST 为大于零的整数 #### Scenario: 按笔数轮换 - **GIVEN** 管理员选择 `strategy=count` - **WHEN** 管理员填写笔数阈值并提交 - **THEN** 页面 MUST 写入正整数 `threshold_count` - **AND** `statistic_cycle` MUST 为 `round`、`day` 或 `month` #### Scenario: 按时间轮换 - **GIVEN** 管理员选择 `strategy=time` - **WHEN** 管理员填写时间周期并提交 - **THEN** `time_period_unit` MUST 为 `minute`、`hour` 或 `day` - **AND** `time_period_value` MUST 为大于零的整数 - **AND** 系统 MUST 支持提交 `time_period_started_at` #### Scenario: 路由世代只读 - **GIVEN** 商户池详情返回 `routing_epoch` - **THEN** 页面 MUST 只读展示该值 - **AND** 页面 MUST NOT 提供编辑或提交该字段的控件 ### Requirement: 微信授权配置管理 系统 SHALL 提供当前微信授权配置的读取和保存能力,并允许管理员切换授权配置的启停状态。 #### Scenario: 读取当前微信授权配置状态 - **WHEN** 管理员请求 `GET /api/admin/wechat-authorizations` - **THEN** 页面 MUST 展示 `enabled`、`miniapp_app_id`、`oa_app_id` 和 `oa_oauth_redirect_url` 等非敏感字段 - **AND** 页面 MUST NOT 回填或展示 `miniapp_app_secret`、`oa_app_secret`、`oa_token` 或 `oa_aes_key` 的原始值 #### Scenario: 保存或切换微信授权启停状态 - **WHEN** 管理员向 `PUT /api/admin/wechat-authorizations/current` 保存配置 - **THEN** 请求 MUST 支持 `enabled`、`miniapp_app_id`、`miniapp_app_secret`、`oa_app_id`、`oa_app_secret`、`oa_token`、`oa_aes_key` 和 `oa_oauth_redirect_url` - **AND** 保存成功后页面 MUST 使用接口结果刷新启停状态 - **AND** 敏感字段 MUST 只在当前编辑会话内存在,并在保存、取消、关闭或卸载后清空 #### Scenario: 未更换敏感字段时保留后端原值 - **GIVEN** 页面只修改 `enabled` 或其他非敏感字段 - **WHEN** 管理员提交微信授权配置 - **THEN** 请求 MUST NOT 使用脱敏占位值覆盖后端已有敏感字段 - **AND** 页面 MUST NOT 在提交后缓存敏感字段值