topfans/backend/docs/微服务架构设计.md
2026-04-07 22:29:48 +08:00

614 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 微服务架构设计文档
## 一、架构设计原则
### 1.1 拆分原则
1. **领域驱动设计DDD**:按业务领域拆分,而非技术层次
2. **高内聚低耦合**:同一领域的功能放在同一服务
3. **独立部署**:服务可独立扩展和部署
4. **数据隔离**:每个服务拥有独立数据库(或逻辑隔离)
5. **渐进式拆分**:初期可适当合并,后续按需拆分
### 1.2 粉丝身份隔离方案
**实现方式**账号ID + 明星IDstar_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 TokenToken中已包含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. 搭建消息队列基础设施