# Proto文件设计说明 - 3.1 认证Service层 ## 一、设计依据 本proto设计基于以下技术文档: - **微服务架构设计文档** - 确定服务职责和API设计 - **数据库设计文档** - 确定数据结构和字段定义 - **开发功能顺序文档** - 确定Service层接口需求 - **JWT设计** - 确定Token相关字段和验证流程 ## 二、核心设计要点 ### 2.1 单Token方案 - ✅ `LoginResponse` 和 `RegisterResponse` 只返回 `access_token` - ✅ `RefreshTokenRequest` 使用 `access_token` 刷新(不是 `refresh_token`) - ✅ Token存储在 `users.access_token` 字段中 - ✅ 单设备登录,新登录会覆盖旧Token ### 2.2 Token中包含star_id - ✅ JWT Claims包含:`user_id`, `star_id`, `updated_at` - ✅ 前端可以直接从Token解析获取 `star_id` - ✅ 无需额外拼接或传递 `star_id` ### 2.3 粉丝身份隔离 - ✅ 所有业务操作都基于 `user_id + star_id` - ✅ `FanProfile` 使用联合唯一索引 `(user_id, star_id)` - ✅ 切换身份时生成新Token(包含新的 `star_id`) ### 2.4 数据库字段映射 #### User表字段映射 ```protobuf message User { int64 id = 1; // users.id string mobile = 2; // users.mobile string avatar_url = 3; // users.avatar_url string global_wallet_address = 4; // users.global_wallet_address bool is_active = 5; // users.is_active int64 created_at = 6; // users.created_at } ``` **注意**:不包含敏感字段(`password_hash`, `access_token`, `token_expires_at`) #### FanProfile表字段映射 ```protobuf message FanProfile { int64 id = 1; // fan_profiles.id int64 user_id = 2; // fan_profiles.user_id int64 star_id = 3; // fan_profiles.star_id(核心隔离键) string nickname = 4; // fan_profiles.nickname int32 level = 5; // fan_profiles.level int32 times = 6; // fan_profiles.times(剩余铸造次数) int32 social = 7; // fan_profiles.social(好友个数) int64 experience = 8; // fan_profiles.experience int64 coin_balance = 9; // fan_profiles.coin_balance int64 crystal_balance = 10; // fan_profiles.crystal_balance repeated string tags = 11; // fan_profiles.tags(JSONB数组) int64 created_at = 12; // fan_profiles.created_at } ``` ## 三、认证相关消息设计 ### 3.1 RegisterRequest / RegisterResponse **RegisterRequest**: - `mobile` - 手机号(必填) - `password` - 密码(必填) - `star_id` - 选择第一个粉丝身份的明星ID(必填) - `nickname` - 第一个粉丝身份的昵称(必填) **RegisterResponse**: - `base` - 基础响应(状态码、消息、时间戳) - `access_token` - JWT Token(已包含 `star_id`) - `expires_in` - Token过期时间(秒,7天=604800秒) - `user` - 创建的用户信息 - `fan_profile` - 创建的粉丝档案 **业务逻辑**: 1. 创建用户(`users`表) 2. 创建第一个粉丝档案(`fan_profiles`表,事务) 3. 生成JWT Token(包含 `user_id`, `star_id`, `updated_at`) 4. 更新用户Token(`users.access_token`, `users.token_expires_at`) ### 3.2 LoginRequest / LoginResponse **LoginRequest**: - `mobile` - 手机号(必填) - `password` - 密码(必填) **LoginResponse**: - `base` - 基础响应 - `access_token` - JWT Token(已包含 `star_id`) - `expires_in` - Token过期时间(秒) - `user` - 用户信息 - `fan_profile` - 当前粉丝档案(根据Token中的 `star_id`) - `fan_profiles` - 用户的所有粉丝身份列表 **业务逻辑**: 1. 根据手机号查询用户 2. 验证密码(bcrypt比对) 3. 验证用户是否激活 4. 获取用户的粉丝档案列表 5. 生成JWT Token(包含当前 `star_id`,从第一个粉丝档案获取) 6. 更新用户Token ### 3.3 RefreshTokenRequest / RefreshTokenResponse **RefreshTokenRequest**: - `access_token` - 旧的access_token(单token方案) **RefreshTokenResponse**: - `base` - 基础响应 - `access_token` - 新的Access Token - `expires_in` - Token过期时间(秒) **业务逻辑**: 1. 解析旧Token(即使过期也要解析) 2. 查询用户 3. 验证旧Token是否匹配数据库中的Token 4. 验证 `updated_at` 是否匹配(如果用户信息更新,Token失效) 5. 生成新Token 6. 更新数据库中的Token ### 3.4 ValidateTokenRequest / ValidateTokenResponse **ValidateTokenRequest**: - `access_token` - 要验证的Token **ValidateTokenResponse**: - `base` - 基础响应 - `user_id` - Token关联的用户ID - `star_id` - Token关联的明星ID - `is_valid` - Token是否有效 - `expires_at` - Token过期时间戳(毫秒) **业务逻辑**: 1. 解析Token 2. 验证签名和过期时间 3. 验证Token是否匹配数据库中的Token 4. 验证 `updated_at` 是否匹配 5. 返回验证结果 ### 3.5 LogoutRequest / LogoutResponse **LogoutRequest**: - `access_token` - 要失效的Token(可选) **LogoutResponse**: - `base` - 基础响应 **业务逻辑**: 1. 从Token中提取 `user_id` 2. 清除用户Token(`users.access_token = NULL`, `users.token_expires_at = NULL`) ## 四、消息设计原则 ### 4.1 字段编号规则 - 1-15:常用字段(占用1字节) - 16-2047:一般字段 - 不要重用已删除字段的编号 - 新增字段只能追加,不能删除 ### 4.2 数据类型选择 - **ID类型**:使用 `int64`(支持大整数) - **时间戳**:使用 `int64`(Unix时间戳,毫秒) - **布尔值**:使用 `bool` - **数组**:使用 `repeated` 关键字 - **可选字段**:proto3中所有字段默认都是可选的 ### 4.3 命名规范 - **消息类型**:PascalCase(如 `RegisterRequest`) - **字段名**:snake_case(如 `access_token`) - **RPC方法**:PascalCase(如 `Register`) ### 4.4 响应结构 - 所有Response的第一个字段必须是 `topfans.common.BaseResponse base = 1;` - `BaseResponse` 包含状态码、错误消息、时间戳 ## 五、REST API映射 使用 `google.api.annotations` 定义REST API路径: ```protobuf rpc Login(LoginRequest) returns (LoginResponse) { option (google.api.http) = { post: "/api/v1/auth/login" body: "*" }; } ``` 支持通过 grpc-gateway 自动生成REST API,无需手动实现HTTP路由。 ## 六、与Service层接口的对应关系 ### Service接口 → Proto消息 | Service方法 | Request | Response | |------------|---------|----------| | `Register(req)` | `RegisterRequest` | `RegisterResponse` | | `Login(req)` | `LoginRequest` | `LoginResponse` | | `Logout(req)` | `LogoutRequest` | `LogoutResponse` | | `RefreshToken(req)` | `RefreshTokenRequest` | `RefreshTokenResponse` | | `ValidateToken(req)` | `ValidateTokenRequest` | `ValidateTokenResponse` | **注意**:Service层接口应使用proto定义的类型,而不是原生类型。 ## 七、后续扩展 当前proto定义覆盖了3.1认证Service层的需求。后续可以添加: - 用户信息Service相关消息(已在proto中定义) - 粉丝身份Service相关消息(已在proto中定义) - 社交功能相关消息(后续添加) ## 八、验证检查清单 - [x] 所有必需的消息类型已定义 - [x] 字段类型与数据库设计一致 - [x] 单Token方案已正确实现 - [x] Token中包含 `star_id` 的设计已体现 - [x] 粉丝身份隔离设计已体现 - [x] REST API路径已定义 - [x] RPC服务定义完整 - [x] 字段编号无冲突 - [x] 命名规范一致 ## 九、下一步 1. 生成proto Go代码(运行 `make proto` 或手动执行protoc命令) 2. 验证生成的代码可以正常导入 3. 更新 `pkg/errors/errors.go` 使用proto类型 4. 开始开发Service层(使用proto类型)