feat: 实现 C 端完整认证系统(client-auth-system)
实现面向个人客户的 7 个认证接口(A1-A7),覆盖资产验证、 微信公众号/小程序登录、手机号绑定/换绑、退出登录完整流程。 主要变更: - 新增 PersonalCustomerOpenID 模型,支持多 AppID 多 OpenID 管理 - 实现有状态 JWT(JWT + Redis 双重校验),支持服务端主动失效 - 扩展微信 SDK:小程序 Code2Session + 3 个 DB 动态工厂函数 - 实现 A1 资产验证 IP 限流(30/min)和 A4 三层验证码限流 - 新增 7 个错误码(1180-1186)和 6 个 Redis Key 函数 - 注册 /api/c/v1/auth/* 下 7 个端点并更新 OpenAPI 文档 - 数据库迁移 000083:新建 tb_personal_customer_openid 表
This commit is contained in:
File diff suppressed because it is too large
Load Diff
141
docs/client-auth-system/功能总结.md
Normal file
141
docs/client-auth-system/功能总结.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# C 端认证系统功能总结
|
||||
|
||||
## 概述
|
||||
|
||||
本次实现了面向个人客户(C 端)的完整认证体系,替代旧 H5 登录接口。支持微信公众号和小程序两种登录方式,基于「资产标识符验证 → 微信授权 → 自动绑定资产 → 可选绑定手机号」的流程。
|
||||
|
||||
## 接口列表
|
||||
|
||||
| 接口 | 路径 | 认证 | 说明 |
|
||||
|------|------|------|------|
|
||||
| A1 | `POST /api/c/v1/auth/verify-asset` | 否 | 资产标识符验证,返回 asset_token |
|
||||
| A2 | `POST /api/c/v1/auth/wechat-login` | 否 | 微信公众号登录 |
|
||||
| A3 | `POST /api/c/v1/auth/miniapp-login` | 否 | 微信小程序登录 |
|
||||
| A4 | `POST /api/c/v1/auth/send-code` | 否 | 发送手机验证码 |
|
||||
| A5 | `POST /api/c/v1/auth/bind-phone` | 是 | 首次绑定手机号 |
|
||||
| A6 | `POST /api/c/v1/auth/change-phone` | 是 | 换绑手机号(双验证码) |
|
||||
| A7 | `POST /api/c/v1/auth/logout` | 是 | 退出登录 |
|
||||
|
||||
## 登录流程
|
||||
|
||||
```
|
||||
用户输入资产标识符(SN/IMEI/ICCID)
|
||||
│
|
||||
▼
|
||||
[A1] verify-asset → asset_token(5分钟有效)
|
||||
│
|
||||
▼
|
||||
微信授权(前端完成)
|
||||
│
|
||||
├── 公众号 → [A2] wechat-login (code + asset_token)
|
||||
└── 小程序 → [A3] miniapp-login (code + asset_token)
|
||||
│
|
||||
▼
|
||||
解析 asset_token → 获取微信 openid
|
||||
→ 查找/创建客户 → 绑定资产
|
||||
→ 签发 JWT + Redis 存储
|
||||
│
|
||||
▼
|
||||
返回 { token, need_bind_phone, is_new_user }
|
||||
│
|
||||
▼
|
||||
need_bind_phone == true?
|
||||
YES → [A4] 发送验证码 → [A5] 绑定手机号
|
||||
NO → 进入主页面
|
||||
```
|
||||
|
||||
## 核心设计
|
||||
|
||||
### 有状态 JWT(JWT + Redis)
|
||||
|
||||
- JWT payload 仅含 `customer_id` + `exp`
|
||||
- 登录时将 token 写入 Redis,TTL 与 JWT 一致
|
||||
- 每次请求在中间件同时校验 JWT 签名和 Redis 有效状态
|
||||
- 支持服务端主动失效(封禁、强制下线、退出登录)
|
||||
- 单点登录:新登录覆盖旧 token
|
||||
|
||||
### OpenID 多记录管理
|
||||
|
||||
- 新增 `tb_personal_customer_openid` 表
|
||||
- 同一客户可在多个 AppID(公众号/小程序)下拥有不同 OpenID
|
||||
- 唯一约束:`UNIQUE(app_id, open_id) WHERE deleted_at IS NULL`
|
||||
- 客户查找逻辑:openid 精确匹配 → unionid 回退合并 → 创建新客户
|
||||
|
||||
### 资产绑定
|
||||
|
||||
- 每次登录创建 `PersonalCustomerDevice` 绑定记录
|
||||
- 同一资产允许被多个客户绑定(支持转手场景)
|
||||
- 首次绑定时自动将资产状态从「在库(1)」更新为「已销售(2)」
|
||||
|
||||
### 微信配置动态加载
|
||||
|
||||
- 登录时从数据库 `tb_wechat_config` 动态读取激活配置
|
||||
- 优先走 WechatConfigService 的 Redis 缓存
|
||||
- 小程序登录直接 HTTP 调用微信 `jscode2session`(不依赖 PowerWeChat SDK)
|
||||
|
||||
## 限流策略
|
||||
|
||||
| 接口 | 维度 | 限制 |
|
||||
|------|------|------|
|
||||
| A1 | IP | 30 次/分钟 |
|
||||
| A4 | 手机号 | 60 秒冷却 |
|
||||
| A4 | IP | 20 次/小时 |
|
||||
| A4 | 手机号 | 10 次/天 |
|
||||
|
||||
## 新增/修改文件
|
||||
|
||||
### 新增文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `internal/model/personal_customer_openid.go` | OpenID 关联模型 |
|
||||
| `internal/model/dto/client_auth_dto.go` | A1-A7 请求/响应 DTO |
|
||||
| `internal/store/postgres/personal_customer_openid_store.go` | OpenID Store |
|
||||
| `internal/service/client_auth/service.go` | 认证 Service(核心业务逻辑) |
|
||||
| `internal/handler/app/client_auth.go` | 认证 Handler(7 个端点) |
|
||||
| `pkg/wechat/miniapp.go` | 小程序 SDK 封装 |
|
||||
| `migrations/000083_add_personal_customer_openid.up.sql` | 迁移文件 |
|
||||
| `migrations/000083_add_personal_customer_openid.down.sql` | 回滚文件 |
|
||||
|
||||
### 修改文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `internal/middleware/personal_auth.go` | 增加 Redis 双重校验 |
|
||||
| `pkg/constants/redis.go` | 新增 token 和限流 Redis Key |
|
||||
| `pkg/errors/codes.go` | 新增错误码 1180-1186 |
|
||||
| `pkg/config/defaults/config.yaml` | 新增 `client.require_phone_binding` |
|
||||
| `pkg/wechat/wechat.go` | 新增 MiniAppServiceInterface |
|
||||
| `pkg/wechat/config.go` | 新增 3 个 DB 动态工厂函数 |
|
||||
| `internal/bootstrap/types.go` | 新增 ClientAuth Handler 字段 |
|
||||
| `internal/bootstrap/handlers.go` | 实例化 ClientAuth Handler |
|
||||
| `internal/bootstrap/services.go` | 初始化 ClientAuth Service |
|
||||
| `internal/bootstrap/stores.go` | 初始化 OpenID Store |
|
||||
| `internal/routes/personal.go` | 注册 7 个认证端点 |
|
||||
| `cmd/api/docs.go` | 注册文档生成器 |
|
||||
| `cmd/gendocs/main.go` | 注册文档生成器 |
|
||||
|
||||
## 错误码
|
||||
|
||||
| 码值 | 常量名 | 说明 |
|
||||
|------|--------|------|
|
||||
| 1180 | CodeAssetNotFound | 资产不存在 |
|
||||
| 1181 | CodeWechatConfigUnavailable | 微信配置不可用 |
|
||||
| 1182 | CodeSmsSendFailed | 短信发送失败 |
|
||||
| 1183 | CodeVerificationCodeInvalid | 验证码错误或已过期 |
|
||||
| 1184 | CodePhoneAlreadyBound | 手机号已被其他客户绑定 |
|
||||
| 1185 | CodeAlreadyBoundPhone | 已绑定手机号不可重复绑定 |
|
||||
| 1186 | CodeOldPhoneMismatch | 旧手机号与当前绑定不匹配 |
|
||||
|
||||
## 数据库变更
|
||||
|
||||
- 新建表 `tb_personal_customer_openid`(迁移 000083)
|
||||
- 唯一索引:`idx_pco_app_id_open_id` (app_id, open_id) 软删除条件
|
||||
- 普通索引:`idx_pco_customer_id` (customer_id)
|
||||
- 条件索引:`idx_pco_union_id` (union_id) WHERE union_id != ''
|
||||
|
||||
## 配置项
|
||||
|
||||
| 配置路径 | 环境变量 | 默认值 | 说明 |
|
||||
|---------|---------|-------|------|
|
||||
| `client.require_phone_binding` | `JUNHONG_CLIENT_REQUIRE_PHONE_BINDING` | `true` | 是否要求绑定手机号 |
|
||||
Reference in New Issue
Block a user