## ADDED Requirements ### Requirement: 微信参数配置 CRUD 管理 系统 SHALL 支持平台用户对微信支付参数配置的完整生命周期管理,包括创建、列表查询、详情查询、更新、删除。每个配置包含完整的支付身份信息(渠道凭证 + 公众号 OAuth 信息 + 小程序 OAuth 信息)。 --- #### Scenario: 创建微信直连支付配置 **WHEN** 平台用户调用 `POST /api/admin/wechat-configs`,`provider_type` 为 `wechat`,提供名称、公众号信息、小程序信息、商户号、API V3 密钥、证书内容(Base64)、私钥内容(Base64)、证书序列号、回调地址 **THEN** 系统创建配置记录,`is_active` 默认为 `false`,返回完整配置信息(敏感字段脱敏) ##### 请求 ``` POST /api/admin/wechat-configs Authorization: Bearer {token} Content-Type: application/json ``` ```json { "name": "微信直连主配置", "description": "生产环境微信直连支付配置", "provider_type": "wechat", "oa_app_id": "wx1234567890abcdef", "oa_app_secret": "abcdef1234567890abcdef1234567890", "oa_token": "mytoken123", "oa_aes_key": "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG", "oa_oauth_redirect_url": "https://example.com/oauth/callback", "miniapp_app_id": "wx9876543210fedcba", "miniapp_app_secret": "fedcba0987654321fedcba0987654321", "wx_mch_id": "1234567890", "wx_api_v3_key": "your32charv3keyhere1234567890abc", "wx_api_v2_key": "your32charv2keyhere1234567890abc", "wx_cert_content": "BASE64_ENCODED_CERT_CONTENT_HERE", "wx_key_content": "BASE64_ENCODED_KEY_CONTENT_HERE", "wx_serial_no": "ABCDEF1234567890ABCDEF1234567890ABCDEF12", "wx_notify_url": "https://example.com/api/payment/wechat/notify" } ``` ##### 请求字段说明 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | name | string | 是 | 配置名称,最长 100 字符 | | description | string | 否 | 配置描述,最长 500 字符 | | provider_type | string | 是 | 渠道类型,枚举值:`wechat`(微信直连)、`fuiou`(富友) | | oa_app_id | string | 否 | 公众号 AppID | | oa_app_secret | string | 否 | 公众号 AppSecret | | oa_token | string | 否 | 公众号消息校验 Token | | oa_aes_key | string | 否 | 公众号消息加解密 Key | | oa_oauth_redirect_url | string | 否 | 公众号 OAuth 回调地址 | | miniapp_app_id | string | 否 | 小程序 AppID | | miniapp_app_secret | string | 否 | 小程序 AppSecret | | wx_mch_id | string | provider_type=wechat 时必填 | 微信商户号 | | wx_api_v3_key | string | provider_type=wechat 时必填 | API V3 密钥(32字符) | | wx_api_v2_key | string | 否 | API V2 密钥(32字符) | | wx_cert_content | string | provider_type=wechat 时必填 | 商户证书内容(Base64 编码) | | wx_key_content | string | provider_type=wechat 时必填 | 商户私钥内容(Base64 编码) | | wx_serial_no | string | provider_type=wechat 时必填 | 商户证书序列号 | | wx_notify_url | string | provider_type=wechat 时必填 | 微信支付回调地址 | ##### 成功响应 ```json { "code": 0, "data": { "id": 1, "name": "微信直连主配置", "description": "生产环境微信直连支付配置", "provider_type": "wechat", "is_active": false, "oa_app_id": "wx1234567890abcdef", "oa_app_secret": "abcd***7890", "oa_token": "myto***n123", "oa_aes_key": "[已配置]", "oa_oauth_redirect_url": "https://example.com/oauth/callback", "miniapp_app_id": "wx9876543210fedcba", "miniapp_app_secret": "fedc***4321", "wx_mch_id": "1234567890", "wx_api_v3_key": "your***0abc", "wx_api_v2_key": "your***0abc", "wx_cert_content": "[已配置]", "wx_key_content": "[已配置]", "wx_serial_no": "ABCD***EF12", "wx_notify_url": "https://example.com/api/payment/wechat/notify", "fy_ins_cd": "", "fy_mchnt_cd": "", "fy_term_id": "", "fy_private_key": "[未配置]", "fy_public_key": "[未配置]", "fy_api_url": "", "fy_notify_url": "", "created_at": "2026-03-16T10:00:00+08:00", "updated_at": "2026-03-16T10:00:00+08:00" }, "msg": "success", "timestamp": "2026-03-16T10:00:00+08:00" } ``` ##### 敏感字段脱敏规则 | 字段 | 脱敏规则 | 示例原值 | 脱敏后 | |------|---------|---------|--------| | oa_app_secret | 前4位 + `***` + 后4位 | `abcdef1234567890abcdef1234567890` | `abcd***7890` | | oa_token | 前4位 + `***` + 后4位 | `mytoken123` | `myto***n123` | | oa_aes_key | `[已配置]` / `[未配置]` | 任意值 | `[已配置]` | | miniapp_app_secret | 前4位 + `***` + 后4位 | `fedcba0987654321fedcba0987654321` | `fedc***4321` | | wx_api_v3_key | 前4位 + `***` + 后4位 | `your32charv3keyhere1234567890abc` | `your***0abc` | | wx_api_v2_key | 前4位 + `***` + 后4位 | `your32charv2keyhere1234567890abc` | `your***0abc` | | wx_cert_content | `[已配置]` / `[未配置]` | Base64 内容 | `[已配置]` | | wx_key_content | `[已配置]` / `[未配置]` | Base64 内容 | `[已配置]` | | wx_serial_no | 前4位 + `***` + 后4位 | `ABCDEF1234567890ABCDEF1234567890ABCDEF12` | `ABCD***EF12` | | fy_private_key | `[已配置]` / `[未配置]` | Base64 内容 | `[已配置]` | | fy_public_key | `[已配置]` / `[未配置]` | Base64 内容 | `[已配置]` | --- #### Scenario: 创建富友支付配置 **WHEN** 平台用户调用 `POST /api/admin/wechat-configs`,`provider_type` 为 `fuiou`,提供名称、公众号信息、小程序信息、机构号、商户号、终端号、商户私钥(Base64)、富友公钥(Base64)、API 地址、回调地址 **THEN** 系统创建配置记录,`is_active` 默认为 `false` ##### 请求 ``` POST /api/admin/wechat-configs Authorization: Bearer {token} Content-Type: application/json ``` ```json { "name": "富友支付配置", "description": "富友聚合支付渠道配置", "provider_type": "fuiou", "oa_app_id": "wx1234567890abcdef", "oa_app_secret": "abcdef1234567890abcdef1234567890", "oa_oauth_redirect_url": "https://example.com/oauth/callback", "miniapp_app_id": "wx9876543210fedcba", "miniapp_app_secret": "fedcba0987654321fedcba0987654321", "fy_ins_cd": "0000100", "fy_mchnt_cd": "0000100002000001", "fy_term_id": "00000001", "fy_private_key": "BASE64_ENCODED_MERCHANT_PRIVATE_KEY", "fy_public_key": "BASE64_ENCODED_FUIOU_PUBLIC_KEY", "fy_api_url": "https://spay.fuiou.com", "fy_notify_url": "https://example.com/api/payment/fuiou/notify" } ``` ##### 请求字段说明 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | name | string | 是 | 配置名称,最长 100 字符 | | description | string | 否 | 配置描述,最长 500 字符 | | provider_type | string | 是 | 渠道类型,此处为 `fuiou` | | oa_app_id | string | 否 | 公众号 AppID | | oa_app_secret | string | 否 | 公众号 AppSecret | | oa_token | string | 否 | 公众号消息校验 Token | | oa_aes_key | string | 否 | 公众号消息加解密 Key | | oa_oauth_redirect_url | string | 否 | 公众号 OAuth 回调地址 | | miniapp_app_id | string | 否 | 小程序 AppID | | miniapp_app_secret | string | 否 | 小程序 AppSecret | | fy_ins_cd | string | provider_type=fuiou 时必填 | 富友机构号 | | fy_mchnt_cd | string | provider_type=fuiou 时必填 | 富友商户号 | | fy_term_id | string | provider_type=fuiou 时必填 | 富友终端号 | | fy_private_key | string | provider_type=fuiou 时必填 | 商户私钥(Base64 编码) | | fy_public_key | string | provider_type=fuiou 时必填 | 富友公钥(Base64 编码) | | fy_api_url | string | provider_type=fuiou 时必填 | 富友 API 地址 | | fy_notify_url | string | provider_type=fuiou 时必填 | 富友支付回调地址 | ##### 成功响应 ```json { "code": 0, "data": { "id": 2, "name": "富友支付配置", "description": "富友聚合支付渠道配置", "provider_type": "fuiou", "is_active": false, "oa_app_id": "wx1234567890abcdef", "oa_app_secret": "abcd***7890", "oa_token": "", "oa_aes_key": "[未配置]", "oa_oauth_redirect_url": "https://example.com/oauth/callback", "miniapp_app_id": "wx9876543210fedcba", "miniapp_app_secret": "fedc***4321", "wx_mch_id": "", "wx_api_v3_key": "", "wx_api_v2_key": "", "wx_cert_content": "[未配置]", "wx_key_content": "[未配置]", "wx_serial_no": "", "wx_notify_url": "", "fy_ins_cd": "0000100", "fy_mchnt_cd": "0000100002000001", "fy_term_id": "00000001", "fy_private_key": "[已配置]", "fy_public_key": "[已配置]", "fy_api_url": "https://spay.fuiou.com", "fy_notify_url": "https://example.com/api/payment/fuiou/notify", "created_at": "2026-03-16T10:00:00+08:00", "updated_at": "2026-03-16T10:00:00+08:00" }, "msg": "success", "timestamp": "2026-03-16T10:00:00+08:00" } ``` --- #### Scenario: 创建配置参数校验失败 **WHEN** 平台用户创建配置时缺少必填字段(如 `provider_type` 为 `wechat` 但未提供 `wx_mch_id`) **THEN** 系统返回错误码 `1001`,拒绝创建 ##### 请求示例(缺少 wx_mch_id) ``` POST /api/admin/wechat-configs Authorization: Bearer {token} Content-Type: application/json ``` ```json { "name": "微信直连配置", "provider_type": "wechat", "wx_api_v3_key": "your32charv3keyhere1234567890abc" } ``` ##### 错误响应 ```json { "code": 1001, "data": null, "msg": "参数错误", "timestamp": "2026-03-16T10:00:00+08:00" } ``` --- #### Scenario: 查询配置列表 **WHEN** 平台用户调用 `GET /api/admin/wechat-configs`,支持按 `provider_type` 和 `is_active` 筛选,支持分页 **THEN** 系统返回配置列表,敏感字段脱敏 ##### 请求 ``` GET /api/admin/wechat-configs?provider_type=wechat&is_active=false&page=1&page_size=20 Authorization: Bearer {token} ``` ##### 查询参数说明 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | provider_type | string | 否 | 按渠道类型筛选,枚举值:`wechat`、`fuiou` | | is_active | boolean | 否 | 按激活状态筛选,`true` 或 `false` | | page | integer | 否 | 页码,默认 1 | | page_size | integer | 否 | 每页条数,默认 20,最大 100 | ##### 成功响应 ```json { "code": 0, "data": { "list": [ { "id": 1, "name": "微信直连主配置", "description": "生产环境微信直连支付配置", "provider_type": "wechat", "is_active": false, "oa_app_id": "wx1234567890abcdef", "oa_app_secret": "abcd***7890", "oa_token": "myto***n123", "oa_aes_key": "[已配置]", "oa_oauth_redirect_url": "https://example.com/oauth/callback", "miniapp_app_id": "wx9876543210fedcba", "miniapp_app_secret": "fedc***4321", "wx_mch_id": "1234567890", "wx_api_v3_key": "your***0abc", "wx_api_v2_key": "your***0abc", "wx_cert_content": "[已配置]", "wx_key_content": "[已配置]", "wx_serial_no": "ABCD***EF12", "wx_notify_url": "https://example.com/api/payment/wechat/notify", "fy_ins_cd": "", "fy_mchnt_cd": "", "fy_term_id": "", "fy_private_key": "[未配置]", "fy_public_key": "[未配置]", "fy_api_url": "", "fy_notify_url": "", "created_at": "2026-03-16T10:00:00+08:00", "updated_at": "2026-03-16T10:00:00+08:00" } ], "total": 1, "page": 1, "page_size": 20 }, "msg": "success", "timestamp": "2026-03-16T10:00:00+08:00" } ``` --- #### Scenario: 查询配置详情 **WHEN** 平台用户调用 `GET /api/admin/wechat-configs/:id` **THEN** 系统返回配置详情,敏感字段脱敏 ##### 请求 ``` GET /api/admin/wechat-configs/1 Authorization: Bearer {token} ``` ##### 成功响应 ```json { "code": 0, "data": { "id": 1, "name": "微信直连主配置", "description": "生产环境微信直连支付配置", "provider_type": "wechat", "is_active": false, "oa_app_id": "wx1234567890abcdef", "oa_app_secret": "abcd***7890", "oa_token": "myto***n123", "oa_aes_key": "[已配置]", "oa_oauth_redirect_url": "https://example.com/oauth/callback", "miniapp_app_id": "wx9876543210fedcba", "miniapp_app_secret": "fedc***4321", "wx_mch_id": "1234567890", "wx_api_v3_key": "your***0abc", "wx_api_v2_key": "your***0abc", "wx_cert_content": "[已配置]", "wx_key_content": "[已配置]", "wx_serial_no": "ABCD***EF12", "wx_notify_url": "https://example.com/api/payment/wechat/notify", "fy_ins_cd": "", "fy_mchnt_cd": "", "fy_term_id": "", "fy_private_key": "[未配置]", "fy_public_key": "[未配置]", "fy_api_url": "", "fy_notify_url": "", "created_at": "2026-03-16T10:00:00+08:00", "updated_at": "2026-03-16T10:00:00+08:00" }, "msg": "success", "timestamp": "2026-03-16T10:00:00+08:00" } ``` ##### 配置不存在时的错误响应 ```json { "code": 1170, "data": null, "msg": "微信支付配置不存在", "timestamp": "2026-03-16T10:00:00+08:00" } ``` --- #### Scenario: 更新配置(非敏感字段) **WHEN** 平台用户调用 `PUT /api/admin/wechat-configs/:id`,仅更新名称、描述、回调地址等非敏感字段 **THEN** 系统更新对应字段,敏感字段保持不变 ##### 请求 ``` PUT /api/admin/wechat-configs/1 Authorization: Bearer {token} Content-Type: application/json ``` ```json { "name": "微信直连主配置(已更新)", "description": "更新后的描述", "wx_notify_url": "https://new.example.com/api/payment/wechat/notify" } ``` ##### 请求字段说明 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | name | string | 否 | 配置名称 | | description | string | 否 | 配置描述 | | oa_app_id | string | 否 | 公众号 AppID | | oa_app_secret | string | 否 | 公众号 AppSecret;空字符串或不传 = 保留原值;传新值 = 替换 | | oa_token | string | 否 | 公众号消息校验 Token;空字符串或不传 = 保留原值;传新值 = 替换 | | oa_aes_key | string | 否 | 公众号消息加解密 Key;空字符串或不传 = 保留原值;传新值 = 替换 | | oa_oauth_redirect_url | string | 否 | 公众号 OAuth 回调地址 | | miniapp_app_id | string | 否 | 小程序 AppID | | miniapp_app_secret | string | 否 | 小程序 AppSecret;空字符串或不传 = 保留原值;传新值 = 替换 | | wx_mch_id | string | 否 | 微信商户号 | | wx_api_v3_key | string | 否 | API V3 密钥;空字符串或不传 = 保留原值;传新值 = 替换 | | wx_api_v2_key | string | 否 | API V2 密钥;空字符串或不传 = 保留原值;传新值 = 替换 | | wx_cert_content | string | 否 | 商户证书内容(Base64);空字符串或不传 = 保留原值;传新值 = 替换 | | wx_key_content | string | 否 | 商户私钥内容(Base64);空字符串或不传 = 保留原值;传新值 = 替换 | | wx_serial_no | string | 否 | 商户证书序列号;空字符串或不传 = 保留原值;传新值 = 替换 | | wx_notify_url | string | 否 | 微信支付回调地址 | | fy_ins_cd | string | 否 | 富友机构号 | | fy_mchnt_cd | string | 否 | 富友商户号 | | fy_term_id | string | 否 | 富友终端号 | | fy_private_key | string | 否 | 商户私钥(Base64);空字符串或不传 = 保留原值;传新值 = 替换 | | fy_public_key | string | 否 | 富友公钥(Base64);空字符串或不传 = 保留原值;传新值 = 替换 | | fy_api_url | string | 否 | 富友 API 地址 | | fy_notify_url | string | 否 | 富友支付回调地址 | ##### 成功响应 ```json { "code": 0, "data": { "id": 1, "name": "微信直连主配置(已更新)", "description": "更新后的描述", "provider_type": "wechat", "is_active": false, "oa_app_id": "wx1234567890abcdef", "oa_app_secret": "abcd***7890", "oa_token": "myto***n123", "oa_aes_key": "[已配置]", "oa_oauth_redirect_url": "https://example.com/oauth/callback", "miniapp_app_id": "wx9876543210fedcba", "miniapp_app_secret": "fedc***4321", "wx_mch_id": "1234567890", "wx_api_v3_key": "your***0abc", "wx_api_v2_key": "your***0abc", "wx_cert_content": "[已配置]", "wx_key_content": "[已配置]", "wx_serial_no": "ABCD***EF12", "wx_notify_url": "https://new.example.com/api/payment/wechat/notify", "fy_ins_cd": "", "fy_mchnt_cd": "", "fy_term_id": "", "fy_private_key": "[未配置]", "fy_public_key": "[未配置]", "fy_api_url": "", "fy_notify_url": "", "created_at": "2026-03-16T10:00:00+08:00", "updated_at": "2026-03-16T10:30:00+08:00" }, "msg": "success", "timestamp": "2026-03-16T10:30:00+08:00" } ``` --- #### Scenario: 更新当前生效配置时清除 Redis 缓存 **WHEN** 平台用户更新的配置 `is_active=true`(当前生效配置) **THEN** 系统更新字段后,主动清除 Redis 缓存 `wechat:config:active`,使变更即时生效 **THEN** 响应格式与普通更新相同 --- #### Scenario: 更新配置时替换敏感字段 **WHEN** 平台用户更新配置时,对脱敏字段传入新的明文值(非空字符串、非脱敏格式) **THEN** 系统将该字段替换为新值 ##### 请求示例(替换 API V3 密钥) ```json { "wx_api_v3_key": "newkey32charsnewkey32charsnewkey" } ``` **THEN** 系统将 `wx_api_v3_key` 更新为新值,响应中该字段显示脱敏后的新值 `newk***ekey` --- #### Scenario: 删除未激活的配置 **WHEN** 平台用户调用 `DELETE /api/admin/wechat-configs/:id`,且该配置 `is_active=false` **THEN** 系统软删除该配置记录,返回成功 ##### 请求 ``` DELETE /api/admin/wechat-configs/1 Authorization: Bearer {token} ``` ##### 成功响应 ```json { "code": 0, "data": null, "msg": "success", "timestamp": "2026-03-16T10:00:00+08:00" } ``` --- #### Scenario: 禁止删除激活中的配置 **WHEN** 平台用户尝试删除 `is_active=true` 的配置 **THEN** 系统返回错误码 `1171`,拒绝删除 ##### 错误响应 ```json { "code": 1171, "data": null, "msg": "不能删除当前生效的支付配置,请先停用", "timestamp": "2026-03-16T10:00:00+08:00" } ``` --- #### Scenario: 禁止删除有在途订单的配置 **WHEN** 平台用户尝试删除某配置,且存在 `payment_config_id` 指向该配置的待支付订单 **THEN** 系统返回错误码 `1172`,拒绝删除 ##### 错误响应 ```json { "code": 1172, "data": null, "msg": "该配置存在未完成的支付订单,暂时无法删除", "timestamp": "2026-03-16T10:00:00+08:00" } ``` --- ### Requirement: 全局唯一激活约束 系统 SHALL 保证任意时刻最多一个支付配置处于激活状态。激活新配置时 MUST 自动停用旧配置。 --- #### Scenario: 激活配置 **WHEN** 平台用户调用 `POST /api/admin/wechat-configs/:id/activate` **THEN** 系统在事务中执行:将所有 `is_active=true` 的配置设为 `false`,再将目标配置设为 `true` **THEN** 系统清除 Redis 缓存 `wechat:config:active` **THEN** 系统返回成功,新配置即时生效 ##### 请求 ``` POST /api/admin/wechat-configs/1/activate Authorization: Bearer {token} ``` ##### 成功响应 ```json { "code": 0, "data": { "id": 1, "name": "微信直连主配置", "description": "生产环境微信直连支付配置", "provider_type": "wechat", "is_active": true, "oa_app_id": "wx1234567890abcdef", "oa_app_secret": "abcd***7890", "oa_token": "myto***n123", "oa_aes_key": "[已配置]", "oa_oauth_redirect_url": "https://example.com/oauth/callback", "miniapp_app_id": "wx9876543210fedcba", "miniapp_app_secret": "fedc***4321", "wx_mch_id": "1234567890", "wx_api_v3_key": "your***0abc", "wx_api_v2_key": "your***0abc", "wx_cert_content": "[已配置]", "wx_key_content": "[已配置]", "wx_serial_no": "ABCD***EF12", "wx_notify_url": "https://example.com/api/payment/wechat/notify", "fy_ins_cd": "", "fy_mchnt_cd": "", "fy_term_id": "", "fy_private_key": "[未配置]", "fy_public_key": "[未配置]", "fy_api_url": "", "fy_notify_url": "", "created_at": "2026-03-16T10:00:00+08:00", "updated_at": "2026-03-16T10:05:00+08:00" }, "msg": "success", "timestamp": "2026-03-16T10:05:00+08:00" } ``` ##### 配置不存在时的错误响应 ```json { "code": 1170, "data": null, "msg": "微信支付配置不存在", "timestamp": "2026-03-16T10:05:00+08:00" } ``` --- #### Scenario: 停用配置 **WHEN** 平台用户调用 `POST /api/admin/wechat-configs/:id/deactivate` **THEN** 系统将该配置 `is_active` 设为 `false` **THEN** 系统清除 Redis 缓存 `wechat:config:active` **THEN** 此后创建订单时仅支持钱包支付或线下支付 ##### 请求 ``` POST /api/admin/wechat-configs/1/deactivate Authorization: Bearer {token} ``` ##### 成功响应 ```json { "code": 0, "data": { "id": 1, "name": "微信直连主配置", "description": "生产环境微信直连支付配置", "provider_type": "wechat", "is_active": false, "oa_app_id": "wx1234567890abcdef", "oa_app_secret": "abcd***7890", "oa_token": "myto***n123", "oa_aes_key": "[已配置]", "oa_oauth_redirect_url": "https://example.com/oauth/callback", "miniapp_app_id": "wx9876543210fedcba", "miniapp_app_secret": "fedc***4321", "wx_mch_id": "1234567890", "wx_api_v3_key": "your***0abc", "wx_api_v2_key": "your***0abc", "wx_cert_content": "[已配置]", "wx_key_content": "[已配置]", "wx_serial_no": "ABCD***EF12", "wx_notify_url": "https://example.com/api/payment/wechat/notify", "fy_ins_cd": "", "fy_mchnt_cd": "", "fy_term_id": "", "fy_private_key": "[未配置]", "fy_public_key": "[未配置]", "fy_api_url": "", "fy_notify_url": "", "created_at": "2026-03-16T10:00:00+08:00", "updated_at": "2026-03-16T10:10:00+08:00" }, "msg": "success", "timestamp": "2026-03-16T10:10:00+08:00" } ``` ##### 配置不存在时的错误响应 ```json { "code": 1170, "data": null, "msg": "微信支付配置不存在", "timestamp": "2026-03-16T10:10:00+08:00" } ``` --- #### Scenario: 查询当前生效配置 **WHEN** 平台用户调用 `GET /api/admin/wechat-configs/active` **THEN** 若有生效配置,返回该配置详情(脱敏) **THEN** 若无生效配置,`data` 返回 `null` ##### 请求 ``` GET /api/admin/wechat-configs/active Authorization: Bearer {token} ``` ##### 有生效配置时的成功响应 ```json { "code": 0, "data": { "id": 1, "name": "微信直连主配置", "description": "生产环境微信直连支付配置", "provider_type": "wechat", "is_active": true, "oa_app_id": "wx1234567890abcdef", "oa_app_secret": "abcd***7890", "oa_token": "myto***n123", "oa_aes_key": "[已配置]", "oa_oauth_redirect_url": "https://example.com/oauth/callback", "miniapp_app_id": "wx9876543210fedcba", "miniapp_app_secret": "fedc***4321", "wx_mch_id": "1234567890", "wx_api_v3_key": "your***0abc", "wx_api_v2_key": "your***0abc", "wx_cert_content": "[已配置]", "wx_key_content": "[已配置]", "wx_serial_no": "ABCD***EF12", "wx_notify_url": "https://example.com/api/payment/wechat/notify", "fy_ins_cd": "", "fy_mchnt_cd": "", "fy_term_id": "", "fy_private_key": "[未配置]", "fy_public_key": "[未配置]", "fy_api_url": "", "fy_notify_url": "", "created_at": "2026-03-16T10:00:00+08:00", "updated_at": "2026-03-16T10:05:00+08:00" }, "msg": "success", "timestamp": "2026-03-16T10:15:00+08:00" } ``` ##### 无生效配置时的响应 ```json { "code": 0, "data": null, "msg": "当前无生效的支付配置,仅支持钱包支付", "timestamp": "2026-03-16T10:15:00+08:00" } ``` --- #### Scenario: 配置切换不取消在途订单 **WHEN** 管理员激活新配置时,系统中存在使用旧配置创建的待支付订单 **THEN** 系统不取消这些订单 **THEN** 旧订单若支付成功,回调按订单关联的 `payment_config_id` 加载旧配置验签处理 **THEN** 旧订单若未支付,由现有 30 分钟超时机制自动取消 此场景无独立 API 接口,为激活接口的业务约束。 --- ### Requirement: 生效配置 Redis 缓存 系统 SHALL 将当前生效的支付配置缓存在 Redis 中,减少数据库查询。 --- #### Scenario: 缓存命中 **WHEN** 支付流程查询生效配置,Redis 缓存 `wechat:config:active` 存在 **THEN** 直接返回缓存数据,不查询数据库 此场景为内部实现约束,无独立 API 接口。 --- #### Scenario: 缓存未命中 **WHEN** 支付流程查询生效配置,Redis 缓存 `wechat:config:active` 不存在 **THEN** 查询数据库获取 `is_active=true` 的配置 **THEN** 将结果写入 Redis,TTL 为 5 分钟 **THEN** 返回配置数据 此场景为内部实现约束,无独立 API 接口。 --- #### Scenario: 无生效配置时缓存空标记 **WHEN** 数据库中无 `is_active=true` 的配置 **THEN** 在 Redis 写入空标记(值为 `"none"`),TTL 为 1 分钟 **THEN** 后续请求命中空标记后直接返回无配置,不穿透数据库 此场景为内部实现约束,无独立 API 接口。 --- #### Scenario: 配置变更主动清除缓存 **WHEN** 执行激活、停用、更新生效配置、删除配置操作 **THEN** 系统主动 DEL Redis 缓存 `wechat:config:active` 此场景为内部实现约束,体现在激活、停用、更新、删除接口的副作用中。 --- ### Requirement: 仅平台用户可操作 系统 SHALL 限制微信参数配置管理接口仅平台用户(`user_type=1` 超级管理员和 `user_type=2` 平台用户)可访问。路由层中间件统一拦截,无需在 Service 层重复校验。 --- #### Scenario: 平台用户访问成功 **WHEN** 超级管理员(`user_type=1`)或平台用户(`user_type=2`)请求微信参数配置管理接口 **THEN** 系统正常处理请求 --- #### Scenario: 非平台用户拒绝访问 **WHEN** 代理账号(`user_type=3`)或企业账号(`user_type=4`)请求微信参数配置管理接口 **THEN** 系统返回错误码 `1005`,消息"无权限访问支付配置管理功能" ##### 错误响应 ```json { "code": 1005, "data": null, "msg": "无权限访问支付配置管理功能", "timestamp": "2026-03-16T10:00:00+08:00" } ``` --- #### Scenario: 未登录用户拒绝访问 **WHEN** 请求未携带有效 Token 或 Token 已过期 **THEN** 系统返回 HTTP 401,错误码 `1002` ##### 错误响应 ```json { "code": 1002, "data": null, "msg": "无效或已过期的认证令牌", "timestamp": "2026-03-16T10:00:00+08:00" } ``` --- ### Requirement: 审计日志 系统 SHALL 对所有微信参数配置的写操作(创建、更新、删除、激活、停用)记录操作审计日志,便于追溯配置变更历史。 --- #### Scenario: 创建配置时记录审计日志 **WHEN** 平台用户成功创建微信参数配置 **THEN** 系统异步写入审计日志,记录以下信息: | 字段 | 值 | |------|-----| | operator_id | 当前操作人 ID | | operator_type | 操作人用户类型 | | operation_type | `create` | | operation_desc | `创建微信支付配置:{配置名称}` | | after_data | 新建配置的完整数据(敏感字段脱敏后存储) | | request_id | 当前请求 ID | | ip_address | 操作人 IP | | user_agent | 操作人 User-Agent | **THEN** 审计日志写入失败不影响业务操作,失败时记录 Error 日志 --- #### Scenario: 更新配置时记录审计日志 **WHEN** 平台用户成功更新微信参数配置 **THEN** 系统异步写入审计日志,记录以下信息: | 字段 | 值 | |------|-----| | operator_id | 当前操作人 ID | | operation_type | `update` | | operation_desc | `更新微信支付配置:{配置名称}` | | before_data | 更新前的配置数据(敏感字段脱敏后存储) | | after_data | 更新后的配置数据(敏感字段脱敏后存储) | --- #### Scenario: 删除配置时记录审计日志 **WHEN** 平台用户成功删除微信参数配置 **THEN** 系统异步写入审计日志,记录以下信息: | 字段 | 值 | |------|-----| | operator_id | 当前操作人 ID | | operation_type | `delete` | | operation_desc | `删除微信支付配置:{配置名称}` | | before_data | 删除前的配置数据(敏感字段脱敏后存储) | --- #### Scenario: 激活配置时记录审计日志 **WHEN** 平台用户成功激活微信参数配置 **THEN** 系统异步写入审计日志,记录以下信息: | 字段 | 值 | |------|-----| | operator_id | 当前操作人 ID | | operation_type | `activate` | | operation_desc | `激活微信支付配置:{配置名称},原生效配置:{旧配置名称或"无"}` | | before_data | 激活前的状态(旧生效配置 ID 和名称) | | after_data | 激活后的状态(新生效配置 ID 和名称) | --- #### Scenario: 停用配置时记录审计日志 **WHEN** 平台用户成功停用微信参数配置 **THEN** 系统异步写入审计日志,记录以下信息: | 字段 | 值 | |------|-----| | operator_id | 当前操作人 ID | | operation_type | `deactivate` | | operation_desc | `停用微信支付配置:{配置名称}` | | before_data | 停用前的配置状态 | | after_data | 停用后的配置状态 |