Files
junhong_cmp_fiber/openspec/specs/wechat-official-account/spec.md
huang b9733c4913
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m12s
fix: 修正零售价架构错误 + 清理旧微信配置 + 归档提案 + 前端接口文档
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 的错误描述
2026-03-19 17:39:43 +08:00

194 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# wechat-official-account Specification
## Purpose
微信公众号能力规范,定义微信 OAuth 2.0 授权登录、账号绑定、OpenID/UnionID 查询、Access Token 中控及配置管理。
## Requirements
### Requirement: 系统必须支持微信 OAuth 2.0 授权登录
系统 SHALL 实现微信公众号 OAuth 2.0 授权流程,允许个人客户通过微信授权获取用户身份信息。
#### Scenario: 用户首次通过微信授权码登录成功
- **WHEN** 用户在前端完成微信授权后端接收到有效的授权码code
- **THEN** 系统调用微信 API 获取用户 OpenID、UnionID 和基本信息(昵称、头像)
- **THEN** 系统在数据库中创建新的个人客户记录,保存微信 OpenID 和 UnionID
- **THEN** 系统生成 JWT Token 并返回给客户端
#### Scenario: 已存在的微信用户再次登录
- **WHEN** 用户通过微信授权码登录,且该 OpenID 已存在于数据库
- **THEN** 系统查询到现有客户记录
- **THEN** 系统更新客户的昵称和头像信息(保持最新)
- **THEN** 系统生成 JWT Token 并返回给客户端
#### Scenario: 微信授权码无效或过期
- **WHEN** 用户提交的授权码无效、过期或已被使用
- **THEN** 系统调用微信 API 失败
- **THEN** 系统返回错误码 1040微信 OAuth 授权失败)和中文错误消息"微信授权失败,请重试"
#### Scenario: 微信 API 服务不可用
- **WHEN** 调用微信 API 时发生网络超时或微信服务异常
- **THEN** 系统记录详细的错误日志(包含 Request ID
- **THEN** 系统返回错误码 1040微信 OAuth 授权失败)和用户友好的中文错误消息
### Requirement: 系统必须支持已有账号绑定微信
系统 SHALL 允许已注册的个人客户(通过手机号登录)绑定微信账号。
#### Scenario: 用户成功绑定微信账号
- **WHEN** 已登录用户提交有效的微信授权码,且该用户尚未绑定微信
- **THEN** 系统调用微信 API 获取 OpenID 和 UnionID
- **THEN** 系统验证该 OpenID 未被其他用户绑定
- **THEN** 系统更新该用户的 wx_open_id 和 wx_union_id 字段
- **THEN** 系统返回成功响应和更新后的用户信息
#### Scenario: 尝试绑定已被使用的微信账号
- **WHEN** 用户提交的微信授权码对应的 OpenID 已被其他用户绑定
- **THEN** 系统返回错误码 1036微信账号已被绑定和中文错误消息"该微信账号已绑定其他用户"
#### Scenario: 用户已绑定微信后再次绑定
- **WHEN** 已绑定微信的用户再次提交微信授权码
- **THEN** 系统更新用户的昵称和头像信息
- **THEN** 系统返回成功响应(允许更新信息,不报错)
### Requirement: 系统必须支持通过 OpenID/UnionID 查询用户
系统 MUST 提供通过微信 OpenID 或 UnionID 查询个人客户的能力。
#### Scenario: 通过 OpenID 查询到用户
- **WHEN** 调用 Store 层的 GetByWxOpenID 方法,传入有效的 OpenID
- **THEN** 系统返回对应的个人客户记录
#### Scenario: 通过 OpenID 查询不到用户
- **WHEN** 调用 Store 层的 GetByWxOpenID 方法,传入不存在的 OpenID
- **THEN** 系统返回 nil无错误表示用户不存在
#### Scenario: 通过 UnionID 查询到用户
- **WHEN** 调用 Store 层的 GetByWxUnionID 方法,传入有效的 UnionID
- **THEN** 系统返回对应的个人客户记录
### Requirement: 系统必须实现 Access Token 中控
系统 MUST 使用 Redis 缓存微信 Access Token支持多实例共享避免重复获取导致超出每日限额。
#### Scenario: 首次获取 Access Token
- **WHEN** 系统首次调用微信 API 需要 Access Token
- **THEN** 系统调用微信 API 获取 Access Token
- **THEN** 系统将 Token 存储到 RedisKey: `powerwechat.access_token.{MD5(appid+secret)}`TTL: 7200秒
- **THEN** 系统使用该 Token 完成 API 调用
#### Scenario: 从 Redis 缓存获取 Token
- **WHEN** 系统调用微信 APIRedis 中存在有效的 Access Token
- **THEN** 系统直接使用缓存的 Token不调用微信 API 获取新 Token
#### Scenario: Access Token 过期后自动刷新
- **WHEN** 系统使用缓存的 Token 调用微信 API 返回 Token 过期错误
- **THEN** 系统自动重新获取 Access Token
- **THEN** 系统更新 Redis 缓存
- **THEN** 系统重试原 API 调用
### Requirement: API 必须遵循统一响应格式
所有微信相关 API MUST 返回统一的 JSON 响应格式。
#### Scenario: 成功响应格式
- **WHEN** API 调用成功
- **THEN** 系统返回 HTTP 200 和以下 JSON 格式:
```json
{
"code": 0,
"message": "success",
"data": { /* 业务数据 */ },
"timestamp": 1706789012345
}
```
#### Scenario: 失败响应格式
- **WHEN** API 调用失败(参数错误、业务逻辑错误、微信 API 错误)
- **THEN** 系统返回对应的 HTTP 状态码400/401/500和以下 JSON 格式:
```json
{
"code": 1040,
"message": "微信授权失败,请重试",
"data": null,
"timestamp": 1706789012345
}
```
### Requirement: 系统必须记录完整的日志
所有微信 API 调用 MUST 记录完整的日志,便于排查问题。
#### Scenario: 记录微信 API 请求日志
- **WHEN** 系统调用微信 API
- **THEN** 系统记录 INFO 级别日志包含Request ID、API 端点、请求参数(脱敏)
#### Scenario: 记录微信 API 响应日志
- **WHEN** 系统收到微信 API 响应
- **THEN** 系统记录 INFO 级别日志包含Request ID、响应状态、响应时间、关键字段
#### Scenario: 记录微信 API 错误日志
- **WHEN** 微信 API 调用失败
- **THEN** 系统记录 ERROR 级别日志包含Request ID、错误码、错误消息、完整的错误详情
### Requirement: 系统必须支持配置管理
微信公众号相关配置 MUST 通过 Viper + 环境变量管理。
#### Scenario: 从环境变量读取配置
- **WHEN** 系统启动时
- **THEN** 系统从环境变量读取以下配置:
- `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_APP_ID`(公众号 AppID
- `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_APP_SECRET`(公众号 AppSecret
- `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_TOKEN`(回调 Token
- `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_AES_KEY`(回调加密密钥)
- `JUNHONG_WECHAT_OFFICIAL_ACCOUNT_OAUTH_REDIRECT_URL`OAuth 回调地址)
#### Scenario: 配置缺失时启动失败
- **WHEN** 必填配置项AppID、AppSecret缺失
- **THEN** 系统记录 FATAL 级别日志
- **THEN** 系统启动失败并退出
### 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