fix: 修正零售价架构错误 + 清理旧微信配置 + 归档提案 + 前端接口文档
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m12s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m12s
1. 修正 retail_price 架构:
- 删除 batch-pricing 接口的 pricing_target 字段和 retail_price 分支
(上级只能改下级成本价,不能改零售价)
- 新增 PATCH /api/admin/packages/:id/retail-price 接口
(代理自己改自己的零售价,校验 retail_price >= cost_price)
2. 清理旧微信 YAML 配置(已全部迁移到数据库 tb_wechat_config):
- 删除 config.yaml 中 wechat.official_account 配置节
- 删除 NewOfficialAccountApp() 旧工厂函数
- 清理 personal_customer service 中的死代码(旧登录/绑定微信方法)
- 清理 docker-compose.prod.yml 中旧微信环境变量和证书挂载注释
3. 归档四个已完成提案到 openspec/changes/archive/
4. 新增前端接口变更说明文档(docs/前端接口变更说明.md)
5. 修正归档提案和 specs 中关于 pricing_target 的错误描述
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# client-asset-token Specification
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: A1 资产标识符验证接口
|
||||
|
||||
系统 MUST 提供无认证资产验证接口 `POST /api/c/v1/auth/verify-asset`,用于将外部资产标识符兑换为短时效 `asset_token`。
|
||||
|
||||
- HTTP Method + Path: `POST /api/c/v1/auth/verify-asset`
|
||||
- 请求体字段:
|
||||
- `identifier` string,MUST,资产标识符(SN/IMEI/虚拟号/ICCID/MSISDN)
|
||||
- 响应体字段:
|
||||
- `asset_token` string,MUST,5 分钟有效
|
||||
- `expires_in` int,MUST,单位秒
|
||||
- 错误码:
|
||||
- `1006` 参数错误(标识符为空或格式非法)
|
||||
- `1404` 资产不存在
|
||||
- `1003` 请求过于频繁
|
||||
|
||||
#### Scenario: 资产验证成功并返回 asset_token
|
||||
- **WHEN** 客户端提交合法且存在的资产标识符
|
||||
- **THEN** 系统 SHALL 解析并定位资产
|
||||
- **THEN** 系统 SHALL 签发 5 分钟有效的 `asset_token`
|
||||
- **THEN** 系统 SHALL 返回 `{asset_token, expires_in}`
|
||||
|
||||
#### Scenario: 输入参数非法
|
||||
- **WHEN** 客户端提交空字符串或不支持格式的标识符
|
||||
- **THEN** 系统 MUST 返回参数错误码 `1006`
|
||||
|
||||
### Requirement: A1 输入校验与安全约束
|
||||
|
||||
系统 SHALL 对标识符进行白名单校验,并在 A1 响应中禁止暴露内部 `asset_id`。
|
||||
|
||||
- 输入校验规则:
|
||||
- MUST 去除前后空格并做长度限制
|
||||
- MUST 仅允许预定义字符集(数字、字母、必要分隔符)
|
||||
- MUST 拒绝 SQL 片段/控制字符
|
||||
- 输出安全规则:
|
||||
- MUST NOT 返回 `asset_id`
|
||||
- MUST NOT 返回内部表名/字段名
|
||||
|
||||
#### Scenario: 防止内部主键泄露
|
||||
- **WHEN** A1 接口返回成功响应
|
||||
- **THEN** 返回体 MUST 只包含 `asset_token` 与有效期信息
|
||||
- **THEN** 返回体 MUST NOT 包含 `asset_id`
|
||||
|
||||
### Requirement: A1 资产令牌签发规范
|
||||
|
||||
`asset_token` SHALL 使用独立签名密钥签发,且 payload 仅包含 `asset_type` 与 `asset_id`。
|
||||
|
||||
- JWT 约束:
|
||||
- `exp` = 当前时间 + 5 分钟
|
||||
- payload MUST 包含 `asset_type`、`asset_id`
|
||||
- payload MUST NOT 包含手机号、OpenID 等敏感信息
|
||||
|
||||
#### Scenario: token 结构与时效符合规范
|
||||
- **WHEN** 服务端签发 `asset_token`
|
||||
- **THEN** token MUST 使用资产令牌专用签名密钥
|
||||
- **THEN** token MUST 在 5 分钟后过期
|
||||
|
||||
### Requirement: A1 IP 级限频
|
||||
|
||||
系统 SHALL 对 A1 实施 IP 维度限频:`30 次/分钟`。
|
||||
|
||||
#### Scenario: 限频内请求通过
|
||||
- **WHEN** 同一 IP 在 1 分钟内请求次数不超过 30 次
|
||||
- **THEN** 系统 SHALL 正常处理请求
|
||||
|
||||
#### Scenario: 超过限频阈值
|
||||
- **WHEN** 同一 IP 在 1 分钟内请求次数超过 30 次
|
||||
- **THEN** 系统 MUST 返回错误码 `1003`
|
||||
@@ -0,0 +1,94 @@
|
||||
# client-phone-binding Specification
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: A4 发送验证码接口
|
||||
|
||||
系统 MUST 提供无认证验证码接口 `POST /api/c/v1/auth/send-code`,并复用现有验证码服务。
|
||||
|
||||
- HTTP Method + Path: `POST /api/c/v1/auth/send-code`
|
||||
- 请求体字段:
|
||||
- `phone` string,MUST,手机号
|
||||
- `scene` string,MUST,业务场景(`bind_phone` / `change_phone_old` / `change_phone_new`)
|
||||
- 响应体字段:
|
||||
- `cooldown_seconds` int,MUST,本次发送后的冷却秒数
|
||||
- 错误码:
|
||||
- `1006` 参数错误
|
||||
- `1003` 请求过于频繁(触发任一限流)
|
||||
- `1050` 短信发送失败
|
||||
|
||||
#### Scenario: 发送成功
|
||||
- **WHEN** 手机号格式合法且未触发限流
|
||||
- **THEN** 系统 SHALL 发送验证码并返回冷却时间
|
||||
|
||||
### Requirement: A4 限频规则
|
||||
|
||||
系统 SHALL 对 A4 实施三层限频:手机号 60 秒冷却、同 IP 每小时 20 次、同手机号每日 10 次。
|
||||
|
||||
#### Scenario: 60 秒内重复发送
|
||||
- **WHEN** 同一手机号在 60 秒冷却内再次请求
|
||||
- **THEN** 系统 MUST 返回 `1003`
|
||||
|
||||
#### Scenario: 同 IP 超过小时阈值
|
||||
- **WHEN** 同一 IP 在 1 小时内发送次数超过 20
|
||||
- **THEN** 系统 MUST 返回 `1003`
|
||||
|
||||
#### Scenario: 同手机号超过日阈值
|
||||
- **WHEN** 同一手机号在当日发送次数超过 10
|
||||
- **THEN** 系统 MUST 返回 `1003`
|
||||
|
||||
### Requirement: A5 首次绑定手机号接口
|
||||
|
||||
系统 MUST 提供需认证接口 `POST /api/c/v1/auth/bind-phone`,仅允许首次绑定。
|
||||
|
||||
- HTTP Method + Path: `POST /api/c/v1/auth/bind-phone`
|
||||
- 请求体字段:
|
||||
- `phone` string,MUST,新手机号
|
||||
- `code` string,MUST,验证码
|
||||
- 响应体字段:
|
||||
- `phone` string,MUST,已绑定手机号
|
||||
- `bound_at` string,MUST,绑定时间
|
||||
- 错误码:
|
||||
- `1001` 缺失认证令牌
|
||||
- `1002` 认证令牌无效
|
||||
- `1006` 参数错误
|
||||
- `1035` 验证码错误或过期
|
||||
- `1037` 手机号已被绑定
|
||||
- `1038` 已绑定手机号不可重复绑定
|
||||
|
||||
#### Scenario: 首次绑定成功
|
||||
- **WHEN** 客户已登录、验证码正确且手机号未被占用
|
||||
- **THEN** 系统 SHALL 完成手机号首次绑定并返回绑定信息
|
||||
|
||||
#### Scenario: 已绑定用户再次调用绑定
|
||||
- **WHEN** 当前客户已存在绑定手机号
|
||||
- **THEN** 系统 MUST 返回 `1038`
|
||||
|
||||
### Requirement: A6 换绑手机号接口
|
||||
|
||||
系统 MUST 提供需认证接口 `POST /api/c/v1/auth/change-phone`,并执行旧手机号与新手机号双验证码校验。
|
||||
|
||||
- HTTP Method + Path: `POST /api/c/v1/auth/change-phone`
|
||||
- 请求体字段:
|
||||
- `old_phone` string,MUST,旧手机号
|
||||
- `old_code` string,MUST,旧手机号验证码
|
||||
- `new_phone` string,MUST,新手机号
|
||||
- `new_code` string,MUST,新手机号验证码
|
||||
- 响应体字段:
|
||||
- `phone` string,MUST,换绑后的手机号
|
||||
- `changed_at` string,MUST,换绑时间
|
||||
- 错误码:
|
||||
- `1001` 缺失认证令牌
|
||||
- `1002` 认证令牌无效
|
||||
- `1006` 参数错误
|
||||
- `1035` 验证码错误或过期
|
||||
- `1037` 新手机号已被绑定
|
||||
- `1039` 旧手机号不匹配
|
||||
|
||||
#### Scenario: 换绑成功
|
||||
- **WHEN** 登录客户提交正确旧/新验证码且新手机号未占用
|
||||
- **THEN** 系统 SHALL 更新绑定手机号为新手机号
|
||||
|
||||
#### Scenario: 旧手机号校验失败
|
||||
- **WHEN** `old_phone` 与当前客户绑定手机号不一致或 `old_code` 错误
|
||||
- **THEN** 系统 MUST 拒绝换绑并返回对应错误码
|
||||
@@ -0,0 +1,57 @@
|
||||
# client-token-management Specification
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 登录 JWT 签发与 Redis 状态存储
|
||||
|
||||
系统 MUST 在 A2/A3 登录成功后签发个人客户 JWT,并将 token 状态写入 Redis。
|
||||
|
||||
- JWT payload 字段:
|
||||
- `customer_id` uint,MUST
|
||||
- `exp` int64,MUST
|
||||
- Redis Key:`RedisPersonalCustomerTokenKey(customerID)`
|
||||
- Redis Value:当前有效 token(或 token 集合,取决于实现)
|
||||
- TTL:MUST 与 JWT 过期时间一致
|
||||
|
||||
#### Scenario: 登录成功写入 Redis
|
||||
- **WHEN** 客户完成微信登录
|
||||
- **THEN** 系统 SHALL 签发 JWT
|
||||
- **THEN** 系统 SHALL 将 token 写入 Redis 并设置 TTL
|
||||
|
||||
### Requirement: PersonalAuthMiddleware 双重校验
|
||||
|
||||
系统 SHALL 在个人客户认证中间件执行双重校验:JWT 解析校验 + Redis 状态校验。
|
||||
|
||||
#### Scenario: JWT 与 Redis 均有效
|
||||
- **WHEN** 请求携带有效 JWT 且 Redis 中存在有效状态
|
||||
- **THEN** 中间件 SHALL 放行并写入 `customer_id` 到上下文
|
||||
|
||||
#### Scenario: JWT 有效但 Redis 不存在
|
||||
- **WHEN** JWT 仍在有效期但 Redis 中不存在该客户 token 状态
|
||||
- **THEN** 中间件 MUST 返回未认证错误 `1002`
|
||||
|
||||
### Requirement: A7 退出登录接口
|
||||
|
||||
系统 MUST 提供需认证接口 `POST /api/c/v1/auth/logout`,用于删除 Redis token 状态。
|
||||
|
||||
- HTTP Method + Path: `POST /api/c/v1/auth/logout`
|
||||
- 请求体字段:无
|
||||
- 响应体字段:
|
||||
- `success` bool,MUST
|
||||
- 错误码:
|
||||
- `1001` 缺失认证令牌
|
||||
- `1002` 认证令牌无效
|
||||
|
||||
#### Scenario: 退出登录成功
|
||||
- **WHEN** 登录客户调用 A7
|
||||
- **THEN** 系统 SHALL 删除 `RedisPersonalCustomerTokenKey(customerID)`
|
||||
- **THEN** 系统 SHALL 返回成功
|
||||
|
||||
### Requirement: 服务端主动失效能力
|
||||
|
||||
系统 MUST 支持服务端主动使 token 失效(如封禁/强制下线),且无需等待 JWT 自然过期。
|
||||
|
||||
#### Scenario: 服务端主动踢出
|
||||
- **WHEN** 管理动作触发客户强制下线
|
||||
- **THEN** 系统 SHALL 删除对应 Redis token 状态
|
||||
- **THEN** 该客户后续请求 MUST 被中间件拒绝
|
||||
@@ -0,0 +1,105 @@
|
||||
# client-wechat-login Specification
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: A2 微信公众号登录接口
|
||||
|
||||
系统 MUST 提供 `POST /api/c/v1/auth/wechat-login`,使用公众号 OAuth code + `asset_token` 完成登录。
|
||||
|
||||
- HTTP Method + Path: `POST /api/c/v1/auth/wechat-login`
|
||||
- 请求体字段:
|
||||
- `code` string,MUST,微信 OAuth 授权码
|
||||
- `asset_token` string,MUST,A1 返回的资产令牌
|
||||
- 响应体字段:
|
||||
- `token` string,MUST,登录 JWT
|
||||
- `need_bind_phone` bool,MUST,是否需要绑定手机号
|
||||
- `is_new_user` bool,MUST,是否新创建用户
|
||||
- 错误码:
|
||||
- `1002` token 无效或过期(asset_token/JWT)
|
||||
- `1040` 微信授权失败
|
||||
- `1006` 参数错误
|
||||
|
||||
#### Scenario: 公众号登录成功
|
||||
- **WHEN** 客户端提交有效 `code` 与有效 `asset_token`
|
||||
- **THEN** 系统 SHALL 调用公众号 OAuth 获取 `openid` 与可选 `unionid`
|
||||
- **THEN** 系统 SHALL 执行客户查找/创建/合并逻辑
|
||||
- **THEN** 系统 SHALL 绑定资产并签发登录 token
|
||||
|
||||
### Requirement: A3 微信小程序登录接口
|
||||
|
||||
系统 MUST 提供 `POST /api/c/v1/auth/miniapp-login`,使用小程序 `jscode2session` + `asset_token` 完成登录。
|
||||
|
||||
- HTTP Method + Path: `POST /api/c/v1/auth/miniapp-login`
|
||||
- 请求体字段:
|
||||
- `code` string,MUST,小程序登录凭证
|
||||
- `asset_token` string,MUST,A1 返回的资产令牌
|
||||
- 响应体字段:
|
||||
- `token` string,MUST,登录 JWT
|
||||
- `need_bind_phone` bool,MUST
|
||||
- `is_new_user` bool,MUST
|
||||
- 错误码:
|
||||
- `1002` token 无效或过期
|
||||
- `1040` 微信授权失败
|
||||
- `1006` 参数错误
|
||||
|
||||
#### Scenario: 小程序登录成功
|
||||
- **WHEN** 客户端提交有效小程序 `code` 与有效 `asset_token`
|
||||
- **THEN** 系统 SHALL 调用 `jscode2session` 获取 `openid` 与可选 `unionid`
|
||||
- **THEN** 系统 SHALL 执行与 A2 一致的客户查找/创建/合并、资产绑定与签发逻辑
|
||||
|
||||
### Requirement: asset_token 校验与资产解析
|
||||
|
||||
系统 SHALL 在 A2/A3 登录前强制校验 `asset_token`,并解析出 `asset_type` + `asset_id`。
|
||||
|
||||
#### Scenario: asset_token 无效
|
||||
- **WHEN** `asset_token` 签名不合法或已过期
|
||||
- **THEN** 系统 MUST 拒绝登录并返回 `1002`
|
||||
|
||||
#### Scenario: asset_token 有效
|
||||
- **WHEN** `asset_token` 可被成功解析
|
||||
- **THEN** 系统 SHALL 使用解析出的资产信息继续登录流程
|
||||
|
||||
### Requirement: 客户查找/创建/合并逻辑
|
||||
|
||||
系统 MUST 按以下顺序处理客户归属:
|
||||
|
||||
1. 先查 `PersonalCustomerOpenID`:`(app_id, open_id)`;
|
||||
2. 未命中且存在 `unionid` 时按 `unionid` 回查并复用客户;
|
||||
3. 仍未命中时创建新 `PersonalCustomer` 与 OpenID 记录。
|
||||
|
||||
#### Scenario: openid 命中既有客户
|
||||
- **WHEN** `(app_id, open_id)` 已存在
|
||||
- **THEN** 系统 SHALL 直接复用对应 `customer_id`
|
||||
|
||||
#### Scenario: openid 未命中但 unionid 命中
|
||||
- **WHEN** `(app_id, open_id)` 不存在且 `unionid` 命中历史记录
|
||||
- **THEN** 系统 SHALL 复用已存在客户
|
||||
- **THEN** 系统 SHALL 新增当前 `app_id + open_id` 记录
|
||||
|
||||
#### Scenario: openid/unionid 均未命中
|
||||
- **WHEN** 无任何匹配记录
|
||||
- **THEN** 系统 SHALL 创建新客户并写入 OpenID 记录
|
||||
|
||||
### Requirement: 登录后资产绑定
|
||||
|
||||
系统 SHALL 在 A2/A3 每次登录时创建一条 `PersonalCustomerDevice` 绑定记录,且 MUST 允许同一资产被多个客户绑定。
|
||||
|
||||
#### Scenario: 已有绑定时再次登录
|
||||
- **WHEN** 同一客户再次登录同一资产
|
||||
- **THEN** 系统 SHALL 记录本次登录绑定关系(按实现可去重或追加历史)
|
||||
|
||||
#### Scenario: 不同客户绑定同一资产
|
||||
- **WHEN** 资产已被其他客户绑定
|
||||
- **THEN** 系统 MUST 允许新增绑定,不得覆盖已有客户绑定关系
|
||||
|
||||
### Requirement: 登录响应与手机号绑定开关
|
||||
|
||||
系统 MUST 在登录响应中返回 `need_bind_phone`,该值由 `client.require_phone_binding` 与客户手机号绑定状态共同决定。
|
||||
|
||||
#### Scenario: 要求手机号绑定且未绑定
|
||||
- **WHEN** 配置 `client.require_phone_binding=true` 且客户未绑定手机号
|
||||
- **THEN** 登录响应 MUST 返回 `need_bind_phone=true`
|
||||
|
||||
#### Scenario: 已绑定手机号或配置关闭
|
||||
- **WHEN** 客户已绑定手机号或 `client.require_phone_binding=false`
|
||||
- **THEN** 登录响应 MUST 返回 `need_bind_phone=false`
|
||||
@@ -0,0 +1,37 @@
|
||||
# personal-customer-openid Specification
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: PersonalCustomerOpenID 模型定义
|
||||
|
||||
系统 MUST 新增 `PersonalCustomerOpenID` 模型与数据表 `tb_personal_customer_openid`,用于保存客户在不同 AppID 下的 OpenID 记录。
|
||||
|
||||
- 关键字段:
|
||||
- `id` uint,主键
|
||||
- `customer_id` uint,MUST,关联个人客户 ID
|
||||
- `app_id` string,MUST,微信应用标识
|
||||
- `open_id` string,MUST,当前应用下 OpenID
|
||||
- `union_id` string,可选,开放平台统一标识
|
||||
- `created_at`/`updated_at`/`deleted_at`
|
||||
- 索引约束:
|
||||
- MUST 存在唯一索引 `UNIQUE(app_id, open_id)`(软删条件下唯一)
|
||||
|
||||
#### Scenario: 新增 OpenID 记录成功
|
||||
- **WHEN** 登录流程创建新 OpenID 关系
|
||||
- **THEN** 系统 SHALL 插入一条包含 `customer_id/app_id/open_id` 的记录
|
||||
|
||||
#### Scenario: 重复 app_id + open_id 被拒绝
|
||||
- **WHEN** 试图插入已存在的 `(app_id, open_id)` 组合
|
||||
- **THEN** 系统 MUST 触发唯一约束并拒绝写入
|
||||
|
||||
### Requirement: 与 PersonalCustomer 的关系约束
|
||||
|
||||
系统 SHALL 通过 `customer_id` 与 `PersonalCustomer` 建立逻辑关联(不使用数据库外键约束)。
|
||||
|
||||
#### Scenario: 根据 customer_id 查询 OpenID 列表
|
||||
- **WHEN** 业务根据 `customer_id` 查询 OpenID
|
||||
- **THEN** 系统 SHALL 返回该客户在多 AppID 下的全部有效记录
|
||||
|
||||
#### Scenario: 软删除客户后的记录处理
|
||||
- **WHEN** 客户逻辑删除或状态失效
|
||||
- **THEN** 系统 MUST 支持按业务策略同步停用或软删除 OpenID 记录
|
||||
@@ -0,0 +1,52 @@
|
||||
# personal-customer Specification
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 个人客户登录主流程改为微信授权
|
||||
|
||||
系统 SHALL 将个人客户登录主流程从“手机号 + 验证码登录”调整为“资产验证 + 微信授权登录”。
|
||||
|
||||
- 新登录入口:
|
||||
- `POST /api/c/v1/auth/verify-asset`(A1,无认证)
|
||||
- `POST /api/c/v1/auth/wechat-login`(A2,无认证)
|
||||
- `POST /api/c/v1/auth/miniapp-login`(A3,无认证)
|
||||
- 请求与响应要点:
|
||||
- A2/A3 请求体 MUST 包含 `code` 与 `asset_token`
|
||||
- A2/A3 响应体 MUST 包含 `token`、`need_bind_phone`、`is_new_user`
|
||||
- 错误码:
|
||||
- `1006` 参数错误
|
||||
- `1002` token 无效或过期
|
||||
- `1040` 微信授权失败
|
||||
|
||||
#### Scenario: 通过微信授权完成登录
|
||||
- **WHEN** 用户先完成 A1,再提交 A2 或 A3
|
||||
- **THEN** 系统 SHALL 完成客户识别/创建、资产绑定并返回登录 token
|
||||
|
||||
#### Scenario: 不再支持旧手机号直登入口
|
||||
- **WHEN** 客户端调用旧手机号登录路径(如 `/api/c/v1/login`)
|
||||
- **THEN** 系统 MUST 按新路由规范拒绝或迁移提示,不再作为主登录路径
|
||||
|
||||
### Requirement: 手机号从“登录凭据”调整为“登录后补充资料”
|
||||
|
||||
系统 MUST 将手机号能力调整为登录后绑定/换绑,而非登录入口。
|
||||
|
||||
- 相关接口:
|
||||
- `POST /api/c/v1/auth/send-code`(A4,无认证)
|
||||
- `POST /api/c/v1/auth/bind-phone`(A5,需认证)
|
||||
- `POST /api/c/v1/auth/change-phone`(A6,需认证)
|
||||
- 响应字段:
|
||||
- A5/A6 MUST 返回绑定后的 `phone`
|
||||
|
||||
#### Scenario: 首次登录后要求绑定手机号
|
||||
- **WHEN** `client.require_phone_binding=true` 且用户未绑定手机号
|
||||
- **THEN** 登录响应 MUST 返回 `need_bind_phone=true`
|
||||
- **THEN** 用户通过 A4+A5 完成绑定后进入业务页面
|
||||
|
||||
### Requirement: 微信身份字段迁移到 OpenID 关联能力
|
||||
|
||||
系统 SHALL 保留 `PersonalCustomer.wx_open_id` 与 `wx_union_id` 字段的兼容性,但新登录链路 MUST 以 `PersonalCustomerOpenID` 为主。
|
||||
|
||||
#### Scenario: 读取用户微信身份
|
||||
- **WHEN** 登录流程需要按微信身份识别客户
|
||||
- **THEN** 系统 MUST 优先查询 `PersonalCustomerOpenID`
|
||||
- **THEN** 不再依赖 `PersonalCustomer` 单字段承载多 AppID 场景
|
||||
@@ -0,0 +1,47 @@
|
||||
# wechat-official-account Specification
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 微信配置源从 YAML 改为数据库动态读取
|
||||
|
||||
系统 MUST 将公众号/小程序授权配置源从 YAML 静态配置切换为数据库 `tb_wechat_config` 动态读取(`is_active=true`)。
|
||||
|
||||
- 配置读取规则:
|
||||
- 公众号登录(A2)使用 `app_id` + `app_secret`
|
||||
- 小程序登录(A3)使用 `miniapp_app_id` + `miniapp_app_secret`
|
||||
- 适配接口:
|
||||
- `POST /api/c/v1/auth/wechat-login`
|
||||
- `POST /api/c/v1/auth/miniapp-login`
|
||||
|
||||
#### Scenario: 公众号登录读取数据库配置
|
||||
- **WHEN** 调用 A2 执行 OAuth code 换取 OpenID
|
||||
- **THEN** 系统 SHALL 从 `tb_wechat_config` 读取当前激活公众号配置
|
||||
|
||||
#### Scenario: 小程序登录读取数据库配置
|
||||
- **WHEN** 调用 A3 执行 jscode2session
|
||||
- **THEN** 系统 SHALL 从 `tb_wechat_config` 读取当前激活小程序配置
|
||||
|
||||
### Requirement: 配置缺失或无激活记录时失败
|
||||
|
||||
系统 MUST 在缺少有效数据库配置时拒绝微信登录请求,并返回统一错误。
|
||||
|
||||
- 错误码:
|
||||
- `1041` 微信配置不可用
|
||||
- `1040` 微信授权失败(第三方调用失败)
|
||||
|
||||
#### Scenario: 无激活配置
|
||||
- **WHEN** `tb_wechat_config` 中不存在 `is_active=true` 记录
|
||||
- **THEN** 系统 MUST 返回 `1041`
|
||||
|
||||
#### Scenario: 配置存在但第三方调用失败
|
||||
- **WHEN** 已获取数据库配置但调用微信接口失败
|
||||
- **THEN** 系统 MUST 返回 `1040`
|
||||
|
||||
### Requirement: 旧 YAML 配置不再作为登录凭据来源
|
||||
|
||||
系统 SHALL 停止在登录链路中使用 `wechat.official_account.*` 静态配置作为 AppID/AppSecret 来源。
|
||||
|
||||
#### Scenario: 配置切换后行为一致
|
||||
- **WHEN** 运维在数据库中更新激活配置
|
||||
- **THEN** 后续登录请求 SHALL 使用新配置生效
|
||||
- **THEN** 无需重启服务加载 YAML
|
||||
Reference in New Issue
Block a user