All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 46s
998 lines
29 KiB
Markdown
998 lines
29 KiB
Markdown
## 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 | 停用后的配置状态 |
|