# 微服务架构设计文档 ## 一、架构设计原则 ### 1.1 拆分原则 1. **领域驱动设计(DDD)**:按业务领域拆分,而非技术层次 2. **高内聚低耦合**:同一领域的功能放在同一服务 3. **独立部署**:服务可独立扩展和部署 4. **数据隔离**:每个服务拥有独立数据库(或逻辑隔离) 5. **渐进式拆分**:初期可适当合并,后续按需拆分 ### 1.2 粉丝身份隔离方案 **实现方式**:账号ID + 明星ID(star_id)作为联合主键/隔离键 **JWT Token设计**: - JWT Claims:`{"user_id": 10000001, "star_id": 123, "updated_at": 1704067200000, "iat": 1704067200000, "exp": 1704672000000}` - Token中包含所有必要信息(user_id、star_id等),前端可直接解析JWT获取 - 后端验证Token时,从Claims中提取user_id和star_id **数据库设计**: - 用户表:`users` (user_id, mobile, password_hash, ...) - 粉丝档案表:`fan_profiles` (user_id, star_id, nickname, level, ...) - 联合主键 - 所有业务表都包含 `star_id` 作为隔离键 --- ## 二、微服务拆分方案 ### 方案A:核心服务拆分(推荐初期方案) #### 2.1 服务列表 ``` 1. User Service(用户与认证服务) 2. Asset Service(资产服务) 3. Social Service(社交服务) 4. Gallery Service(展馆服务) 5. Task Service(任务服务) ``` #### 2.2 服务职责划分 ##### 2.2.1 User Service(用户与认证服务) **职责**: - 用户注册、登录、登出 - JWT Token 生成、刷新、验证 - 用户基本信息管理 - 粉丝档案(FanProfile)管理 - 粉丝身份列表管理与切换 - 个人信息页数据 - 密码管理 **核心功能(认证相关)**: - `POST /api/auth/register` - 注册(同时创建第一个粉丝身份) - `POST /api/auth/login` - 登录(返回JWT Token,Token中已包含star_id) - `POST /api/auth/logout` - 登出 - `POST /api/auth/refresh` - 刷新Token - `GET /api/auth/me` - 获取当前登录用户信息 **核心功能(用户信息相关)**: - `GET /api/users/{user_id}` - 获取用户信息 - `GET /api/users/{user_id}/fan-profiles/{star_id}` - 获取粉丝档案 - `GET /api/fan-identities` - 获取可选粉丝身份列表 - `POST /api/my/fan-identities` - 新增粉丝身份(最多2个) - `POST /api/my/fan-identities/switch` - 切换粉丝身份(返回新token) - `GET /api/me/profile` - 获取个人信息页 - `POST /api/me/profile` - 修改昵称 - `POST /api/account/password` - 修改密码 **数据库**: - `users` - 用户账号表(user_id, mobile, password_hash, global_wallet_address, ...) - `fan_profiles` - 粉丝档案表(user_id, star_id, nickname, level, crystal_balance, ...) - `stars` - 明星信息表(star_id, name, pic_url, ...) - `user_sessions` - 用户会话表(Token黑名单) **数据隔离**:所有查询都基于 `user_id + star_id` 联合查询 **优势**: - 认证和用户管理逻辑紧密相关,合并后减少服务间调用 - 注册、登录、身份切换等操作可以在同一服务内完成,保证事务一致性 - 简化架构,降低维护成本 --- ##### 2.2.2 Asset Service(资产服务) **职责**: - 数字藏品的铸造(链下记录 + 链上确权) - 资产查询与列表 - 资产详情 - 素材管理(平台素材) - 图片上传与审核 **核心功能**: - `POST /api/uploadPic` - 上传藏品封面图片 - `DELETE /api/cancelMint` - 取消铸造 - `POST /api/mints` - 创建铸造订单(异步) - `GET /api/starbooks/me/items` - 获取我的藏品列表 - `GET /api/assets/{asset_id}` - 获取藏品详情 - `GET /api/materials/platform` - 获取平台素材列表 - `GET /api/assets/{asset_id}/status` - 查询上链状态 **数据库**: - `assets` - 资产表(asset_id, owner_uid, star_id, name, cover_url, status, tx_hash, token_id, ...) - `mint_orders` - 铸造订单表(order_id, asset_id, status, created_at, ...) - `materials` - 素材表(material_id, star_id, pic_url, ...) - `upload_tasks` - 图片上传任务表(task_id, pic_url, status, ...) **外部依赖**: - 区块链服务(上链确权,异步) - AI服务(生成封面,可选) - 存储服务(MinIO/S3,图片存储) **触发事件**: - 铸造完成 → 发布事件 → Task Service(更新任务进度) --- ##### 2.2.3 Social Service(社交服务) **职责**: - 好友关系管理(申请、同意、拒绝、删除) - 好友列表查询 - 用户搜索 - 系统推荐好友 - 点赞功能 **核心功能**: - `GET /api/users/search` - 搜索用户(按UID) - `GET /api/friends/recommendations` - 系统推荐好友 - `POST /api/friends/requests` - 发起好友申请 - `POST /api/friends/requests/respond` - 处理好友申请(action: true=同意) - `GET /api/friend-list` - 好友列表 - `DELETE /api/friends/{friend_uid}` - 删除好友 - `POST /api/assets/likes` - 点赞藏品 - `DELETE /api/assets/{asset_id}/likes` - 取消点赞 **数据库**: - `friend_relations` - 好友关系表(id, user_a, user_b, star_id, status, created_at) - `friend_requests` - 好友申请表(request_id, from_uid, to_uid, star_id, status, created_at) - `asset_likes` - 点赞表(id, asset_id, user_id, star_id, created_at) **数据隔离**:所有操作都基于 `star_id` 隔离 --- ##### 2.2.4 Gallery Service(展馆服务) **职责**: - 展馆管理(我的展馆、他人展馆) - 展位管理(槽位列表、解锁/购买展位) - 展品展示(放置、下架、踢走占位) - 展位规则管理(从PostgreSQL加载) **核心功能**: - `GET /api/mygalleries` - 获取我的展馆 - `GET /api/galleries/{target_uid}` - 获取他人展馆 - `POST /api/galleries/place` - 在展位展示藏品 - `POST /api/galleries/remove` - 下架展位藏品 - `POST /api/galleries/me/slots/{slot_id}/kick` - 踢走占位 - `POST /api/galleries/slots_unlock` - 解锁/购买新展位 **数据库**: - `booth_slots` - 展位表(slot_id, host_profile_id, star_id, slot_index, visibility, is_enabled, ...) - `exhibitions` - 展品展示表(id, asset_id, slot_id, host_profile_id, occupier_uid, start_time, expire_at, ...) **规则表(PostgreSQL)**: - `gallery_rules` - 展位规则表(rule_id, star_id, rule_type, rule_value, ...) - 初始展位数:3 - 抢展位时长:4小时 - 解锁条件:等级/水晶购买价格 **启动时加载规则**: ```go // 服务启动时从PostgreSQL加载规则到内存 type GalleryRules struct { InitialSlotCount int // 初始展位数 GrabSlotDuration int64 // 抢展位时长(秒) UnlockCostByLevel map[int]int // 按等级解锁成本 } ``` **依赖**: - Asset Service(验证资产是否存在) - User Service(验证用户权限和粉丝档案) **触发事件**: - 展示完成 → 发布事件 → Task Service(更新任务进度) --- ##### 2.2.5 Task Service(任务服务) **职责**: - 任务定义管理(从PostgreSQL加载) - 用户任务进度管理 - 任务奖励发放 - 任务进度更新(接收事件) - 新手引导状态管理 **核心功能**: - `GET /api/tasks` - 获取任务列表 - `GET /api/tasks/{task_id}` - 获取任务详情 - `POST /api/tasks/claim` - 领取任务奖励 - `POST /api/tasks/progress` - 更新任务进度(内部调用/事件触发) - `GET /api/onboarding/status` - 获取新手引导状态 - `POST /api/onboarding/status` - 更新新手引导状态 **数据库**: - `user_tasks` - 用户任务进度表(id, user_id, star_id, task_id, status, progress, target, ...) **规则表(PostgreSQL)**: - `task_definitions` - 任务定义表(task_id, star_id, task_type, title, condition, reward_type, reward_amount, ...) - `task_conditions` - 任务条件表(condition_id, action, count, ...) **启动时加载规则**: ```go // 服务启动时从PostgreSQL加载任务定义到内存 type TaskDefinitions struct { Tasks map[int64]*TaskDefinition // task_id -> TaskDefinition Rules map[string]*TaskCondition // action -> TaskCondition } ``` **事件监听**: - 监听来自其他服务的事件: - `user.login` → 更新登录任务进度 - `asset.mint` → 更新铸造任务进度 - `gallery.exhibit` → 更新展示任务进度 - `friend.add` → 更新社交任务进度 **依赖**: - User Service(发放奖励时更新粉丝档案的余额、等级) --- ##### 2.2.6 Recommendation Service(推荐服务)- 可选 **职责**: - 广场推荐小屋列表 - TOP 藏品展板(基于点赞数) - 推荐算法(未来扩展) **核心功能**: - `GET /api/square/huts` - 广场推荐小屋列表 - `GET /api/square/top-assets` - TOP藏品展板 **数据库**: - `recommendation_cache` - 推荐缓存表(id, type, data, updated_at) **依赖**: - User Service(获取用户信息和粉丝档案) - Asset Service(获取资产信息) - Social Service(获取点赞数据) **注意**:初期可以放在 Social Service 或 Gallery Service 中,后续按需拆分 --- ### 方案B:进一步简化拆分(可选,适合更小团队) 如果团队规模较小或初期快速迭代,可以考虑进一步合并: ``` 1. User Service(用户、认证、社交) 2. Asset Service(资产服务) 3. Gallery Service(展馆服务 + 任务服务) ``` **合并理由**: - User + Social:社交功能与用户紧密相关,数据查询频繁,且需要粉丝身份隔离 - Gallery + Task:任务主要是展馆相关行为触发,初期合并减少服务间调用 **注意**:当前推荐的方案A已经将认证和用户服务合并,足够简化。方案B仅在团队非常小(2-3人)时考虑。 **拆分时机**: - 当单个服务负载过高时 - 当功能迭代冲突频繁时 - 当需要独立扩展时 --- ## 三、服务间通信 ### 3.1 通信方式 **同步调用**:gRPC(内部服务间) **异步调用**:消息队列(RocketMQ/Kafka/RabbitMQ) **对外API**:REST API(通过Gateway/BFF层) ### 3.2 事件驱动架构 **事件类型**: ``` user.login // 用户登录 user.identity_switch // 切换身份 asset.mint // 资产铸造 asset.mint_complete // 铸造完成 gallery.exhibit // 展品上架 gallery.remove // 展品下架 friend.add // 添加好友 friend.delete // 删除好友 asset.like // 点赞 task.complete // 任务完成 ``` **事件发布者与订阅者**: ``` User Service → user.login → Task Service User Service → user.identity_switch → Task Service Asset Service → asset.mint → Task Service Gallery Service → gallery.exhibit → Task Service Social Service → friend.add → Task Service ``` --- ## 四、数据存储方案 ### 4.1 数据库选择 **PostgreSQL**(主数据库): - 用户数据 - 粉丝档案 - 资产数据 - 社交关系 - 展馆展位 - 任务进度 - **规则表**(任务规则、展位规则) **Redis**(缓存): - JWT Token 黑名单 - 用户会话缓存 - 热门数据缓存(TOP藏品、推荐列表) - 分布式锁 **MinIO/S3**(对象存储): - 图片资源(藏品封面、用户头像、素材) **区块链**(链上存储): - 资产确权(Token ID、交易哈希) ### 4.2 规则存储与加载 **设计思路**: - 规则存储在PostgreSQL的配置表中 - 服务启动时一次性加载到内存 - 支持热更新(通过配置中心或管理后台触发) **规则表设计**: ```sql -- 任务规则表 CREATE TABLE task_definitions ( task_id BIGSERIAL PRIMARY KEY, star_id BIGINT NOT NULL DEFAULT 0, -- 0=全局任务 task_type INT NOT NULL, -- 1=日常, 2=周常, 3=成就, 4=活动 title VARCHAR(255) NOT NULL, description TEXT, trigger_type INT NOT NULL, -- 1=计数, 2=条件, 3=一次性 condition JSONB, -- 完成条件 {"action":"login","count":3} reward_type INT NOT NULL, -- 1=经验, 2=水晶, 3=游戏币 reward_amount BIGINT NOT NULL, is_active BOOLEAN DEFAULT true, start_time BIGINT, -- Unix时间戳毫秒 end_time BIGINT, created_at BIGINT NOT NULL ); -- 展位规则表 CREATE TABLE gallery_rules ( rule_id BIGSERIAL PRIMARY KEY, star_id BIGINT NOT NULL DEFAULT 0, -- 0=全局规则 rule_type VARCHAR(50) NOT NULL, -- 'initial_slot_count', 'grab_duration', 'unlock_cost' rule_value JSONB NOT NULL, -- 规则值 updated_at BIGINT NOT NULL ); ``` **服务加载示例**: ```go // Task Service 启动时 func (s *TaskService) LoadTaskDefinitions() error { tasks, err := s.repo.LoadAllActiveTasks() if err != nil { return err } s.taskCache = make(map[int64]*TaskDefinition) for _, task := range tasks { s.taskCache[task.TaskID] = task } return nil } // Gallery Service 启动时 func (s *GalleryService) LoadGalleryRules() error { rules, err := s.repo.LoadAllRules() if err != nil { return err } s.rules = &GalleryRules{ InitialSlotCount: rules.GetInt("initial_slot_count", 3), GrabSlotDuration: rules.GetInt64("grab_duration", 4*3600), // ... } return nil } ``` --- ## 五、JWT Token 设计(账号+明星ID) ### 5.1 Token 结构 **JWT Claims**: ```json { "user_id": 10000001, "star_id": 123, "iat": 1704067200, "exp": 1704070800 } ``` **前端使用**: - 直接使用JWT Token,无需拼接格式 - 前端可以直接解析JWT的payload部分(Base64解码)获取star_id等信息 - 请求时在Header中携带:`Authorization: Bearer {jwt_token}` **后端验证**: ```go func ValidateToken(tokenString string) (*Claims, error) { // 解析和验证JWT Token claims, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { return jwtSecret, nil }) if err != nil { return nil, err } // Token中已包含user_id和star_id // claims.UserID // claims.StarID // claims.UpdatedAt return claims, nil } ``` ### 5.2 登录响应 ```json { "code": 200, "message": "ok", "data": { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expires_in": 604800, "user": { "uid": 10000001, "current_star_id": 123, "nickname": "爱战战", "chain_address": "0xabc...", "starbook_limit": 3, "assets_num": 2 } } } ``` **说明**: - `access_token`:JWT Token,包含user_id、star_id、updated_at等信息 - 前端可直接解析Token获取star_id,无需额外字段 --- ## 六、API Gateway / BFF 层 ### 6.1 职责 - 统一入口:所有外部请求通过Gateway - 协议转换:REST API → gRPC(内部服务) - 认证授权:JWT验证(Token中已包含user_id和star_id) - 请求路由:根据路径路由到对应微服务 - 响应聚合:合并多个服务的数据 - 限流熔断:保护后端服务 ### 6.2 路由规则 ``` /api/auth/* → User Service /api/users/* → User Service /api/fan-identities → User Service /api/me/* → User Service /api/account/* → User Service /api/assets/* → Asset Service /api/friends/* → Social Service /api/galleries/* → Gallery Service /api/tasks/* → Task Service /api/square/* → Recommendation Service (或 Social Service) ``` --- ## 七、服务依赖关系图 ``` ┌─────────────┐ │ Client │ │ (Mobile) │ └──────┬──────┘ │ │ REST API ▼ ┌─────────────┐ │ Gateway │ │ (BFF) │ └──────┬──────┘ │ ┌──────────────────┼──────────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ User Service │ │Asset Service │ │Social Service│ │ (Auth+User) │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ └─────────────────┼──────────────────┘ │ │ ┌────────────────┼────────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │Gallery Service│ │Task Service │ │Recommendation│ └──────┬───────┘ └──────┬───────┘ │ Service │ │ │ └──────────────┘ └────────────────┘ │ │ └────────┬────────┘ │ │ Events (MQ) │ ┌────────┴────────┐ │ PostgreSQL │ │ (Rules DB) │ └────────────────┘ ``` --- ## 八、部署建议 ### 8.1 初期部署(方案A) - **User Service**: 2-3实例(认证和用户管理,高频访问) - **Asset Service**: 2-3实例(异步任务较多) - **Gallery Service**: 2-3实例 - **Social Service**: 2-3实例 - **Task Service**: 2实例 ### 8.2 扩展建议 - **水平扩展**:根据负载增加实例数 - **数据库读写分离**:主从复制,读操作走从库 - **缓存层**:Redis缓存热点数据 - **CDN**:静态资源(图片)使用CDN加速 --- ## 九、总结 ### 9.1 推荐方案 **初期(MVP)**:使用**方案A(核心服务拆分)**,共5个核心微服务 - User Service(用户与认证服务):合并认证和用户管理,减少服务间调用,保证事务一致性 - Asset Service(资产服务) - Social Service(社交服务) - Gallery Service(展馆服务) - Task Service(任务服务) - **可选**:Recommendation Service(推荐服务)- 初期可放在Social Service中 **优势**: - 职责清晰,易于维护 - 可独立扩展 - 服务间耦合度适中 - 认证和用户管理合并后,注册、登录、身份切换等操作在同一服务内完成,保证数据一致性 **未来扩展**: - 如果推荐算法复杂 → 拆分 Recommendation Service - 如果任务系统复杂 → 可保持独立或合并到其他服务 - 如果需要搜索功能 → 新增 Search Service - 如果认证服务负载过高 → 可考虑将认证独立拆分(但当前阶段不建议) ### 9.2 关键设计点 1. **粉丝身份隔离**:使用 `user_id + star_id` 作为联合主键 2. **JWT Token**:Token中包含user_id和star_id,前端直接使用Token 3. **规则管理**:PostgreSQL存储,启动时加载到内存,支持热更新 4. **异步处理**:铸造、任务进度等通过消息队列解耦 5. **数据一致性**:关键操作使用分布式事务或最终一致性 ### 9.3 下一步行动 1. 细化每个服务的接口定义(更新proto文件) 2. 设计数据库表结构(包括规则表) 3. 实现JWT Token生成和验证逻辑(Token中包含user_id和star_id) 4. 实现规则加载机制 5. 搭建消息队列基础设施