# 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 存储到 Redis(Key: `powerwechat.access_token.{MD5(appid+secret)}`,TTL: 7200秒) - **THEN** 系统使用该 Token 完成 API 调用 #### Scenario: 从 Redis 缓存获取 Token - **WHEN** 系统调用微信 API,Redis 中存在有效的 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