# client-auth-system 实施任务清单

## 1. 模型与迁移

- [x] 1.1 新增 `internal/model/personal_customer_openid.go`，定义 PersonalCustomerOpenID 模型与 TableName
- [x] 1.2 创建迁移文件，新增 `tb_personal_customer_openid` 表及 `UNIQUE(app_id, open_id) WHERE deleted_at IS NULL` 索引
- [x] 1.3 在 `internal/model/system.go` 注册新模型以纳入 AutoMigrate
- [x] 1.4 更新 `pkg/config/defaults/config.yaml`，新增 `client.require_phone_binding` 配置项

## 2. PersonalAuthMiddleware 增强

- [x] 2.1 在 `pkg/constants/redis.go` 新增 `RedisPersonalCustomerTokenKey(customerID)` 常量函数
- [x] 2.2 增强 `internal/middleware/personal_auth.go`，增加 JWT + Redis 双重校验
- [x] 2.3 完成 token 不在 Redis 时的拒绝逻辑与统一错误返回

## 3. 资产验证令牌(A1)

- [x] 3.1 新增认证 DTO（A1 请求/响应）并补齐 OpenAPI 标签
- [x] 3.2 新增 `internal/handler/app/client_auth.go` 的 `VerifyAsset` Handler
- [x] 3.3 新增 `internal/service/client_auth/service.go` 的资产解析与 `asset_token` 签发逻辑（5 分钟）
- [x] 3.4 实现 A1 IP 限流（30/min）与错误码映射

## 4. 微信 SDK 扩展（小程序 + 动态配置工厂）

- [x] 4.1 新增 `pkg/wechat/miniapp.go`：定义 `MiniAppService` 结构体 + `MiniAppServiceInterface` 接口 + `Code2Session(ctx, code)` 方法（直接 HTTP 调用微信 `jscode2session` 接口，不依赖 PowerWeChat SDK）
- [x] 4.2 在 `pkg/wechat/wechat.go` 中新增 `MiniAppServiceInterface` 接口定义和编译时类型检查 `var _ MiniAppServiceInterface = (*MiniAppService)(nil)`
- [x] 4.3 在 `pkg/wechat/config.go` 中新增 `NewOfficialAccountAppFromConfig(wechatConfig *model.WechatConfig, cache, logger)` 工厂函数——从 DB 记录的 `oa_app_id` + `oa_app_secret` 创建公众号实例（复用 PowerWeChat `officialAccount.NewOfficialAccount`）
- [x] 4.4 在 `pkg/wechat/config.go` 中新增 `NewMiniAppServiceFromConfig(wechatConfig *model.WechatConfig, logger)` 工厂函数——从 DB 记录的 `miniapp_app_id` + `miniapp_app_secret` 创建小程序服务
- [x] 4.5 在 `pkg/wechat/config.go` 中新增 `NewPaymentAppFromConfig(wechatConfig *model.WechatConfig, appID string, cache, logger)` 工厂函数——从 DB 记录创建支付实例，`appID` 参数决定关联应用（公众号/小程序）

## 5. 微信登录(A2+A3)

- [x] 5.1 新增 A2/A3 请求响应 DTO（公众号与小程序）
- [x] 5.2 在 `client_auth/service.go` 中实现动态读取 `tb_wechat_config WHERE is_active=true` 的配置加载逻辑（优先走 WechatConfigService 的 Redis 缓存）
- [x] 5.3 实现公众号登录(A2)：调用 `NewOfficialAccountAppFromConfig` → `NewOfficialAccountService` → `GetUserInfoDetailed(code)` 获取 openid+unionid+昵称+头像（复用现有 `official_account.go` 的方法，不重新实现）
- [x] 5.4 实现小程序登录(A3)：调用 `NewMiniAppServiceFromConfig` → `Code2Session(code)` 获取 openid+unionid+sessionKey；昵称/头像从请求体获取
- [x] 5.5 实现客户查找/创建/合并逻辑（openid 优先，unionid 回退）
- [x] 5.6 新增 `internal/store/postgres/personal_customer_openid_store.go` 与相关查询/写入方法
- [x] 5.7 实现每次登录创建 PersonalCustomerDevice 绑定记录（允许同资产多客户）；**首次绑定时**（该资产此前无任何 PersonalCustomerDevice 记录），将资产的 `asset_status` 从 1（在库）更新为 2（已销售），使用条件更新 `WHERE asset_status = 1` 确保幂等（已是 2 或其他状态则不变）
- [x] 5.8 实现登录 JWT 签发、Redis 存储与 `need_bind_phone` 计算

## 6. 验证码与手机号(A4+A5+A6)

- [x] 6.1 复用现有验证码服务（`internal/service/verification/service.go` 的 `SendCode`）实现 A4 发送验证码
- [x] 6.2 实现 A4 限流：手机号 60s、IP 20/hour、手机号 10/day
- [x] 6.3 实现 A5 首次绑定手机号逻辑（已绑定拒绝）
- [x] 6.4 实现 A6 双验证码换绑逻辑（旧手机号+新手机号）
- [x] 6.5 增补手机号绑定/换绑错误码与中文错误信息

## 7. 退出登录(A7)

- [x] 7.1 新增 A7 请求响应 DTO
- [x] 7.2 实现 `POST /api/c/v1/auth/logout` Handler 与 Service
- [x] 7.3 在 A7 中删除 `RedisPersonalCustomerTokenKey(customerID)` 完成服务端失效

## 8. 路由注册与文档

- [x] 8.1 在 `internal/bootstrap/types.go` 增加 ClientAuth Handler 字段
- [x] 8.2 在 `internal/bootstrap/handlers.go` 实例化 ClientAuth Handler
- [x] 8.3 在 `internal/routes/personal.go` 使用 `Register()` 注册 `/api/c/v1/auth/*` 七个端点
- [x] 8.4 在 `cmd/api/docs.go` 注册新 Handler 供文档生成器使用
- [x] 8.5 在 `cmd/gendocs/main.go` 注册新 Handler 供文档生成器使用
- [x] 8.6 执行 `go run cmd/gendocs/main.go` 并确认新接口出现在 OpenAPI 文档

## 9. 验证

- [x] 9.1 执行 `go build ./...`，确保构建通过
- [x] 9.2 运行 `lsp_diagnostics`，确保修改文件无错误
- [x] 9.3 按数据库验证规范检查新表与索引存在且结构正确
- [x] 9.4 在 `docs/client-auth-system/` 补充中文功能总结文档
