rwadurian/backend/services/leaderboard-service/docs/ARCHITECTURE.md

486 lines
21 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.

# Leaderboard Service 架构设计文档
## 1. 概述
Leaderboard Service龙虎榜服务是一个基于 NestJS 框架的微服务,负责管理和展示用户的团队认种排名。服务采用 **领域驱动设计DDD** 结合 **六边形架构Hexagonal Architecture** 的设计模式。
### 1.1 核心功能
- **日榜/周榜/月榜管理**: 支持多种时间周期的排行榜
- **排名计算**: 基于团队认种数据计算龙虎榜分值
- **虚拟排名**: 支持系统虚拟账户占位显示
- **实时更新**: 定时刷新排名数据
- **缓存优化**: Redis 缓存热点数据
### 1.2 技术栈
| 组件 | 技术 | 版本 |
|------|------|------|
| 框架 | NestJS | 10.x |
| 语言 | TypeScript | 5.x |
| 数据库 | PostgreSQL | 15.x |
| ORM | Prisma | 5.x |
| 缓存 | Redis (ioredis) | 7.x |
| 消息队列 | Kafka (kafkajs) | 2.x |
| 认证 | JWT + Passport | - |
| API 文档 | Swagger | 7.x |
## 2. 架构设计
### 2.1 六边形架构(端口与适配器)
```
┌─────────────────────────────────────────┐
│ API Layer │
│ (Controllers, DTOs, Guards, Swagger) │
└─────────────────┬───────────────────────┘
┌─────────────────▼───────────────────────┐
│ Application Layer │
│ (Application Services, Schedulers) │
└─────────────────┬───────────────────────┘
┌─────────────────────────────┼─────────────────────────────┐
│ │ │
│ ┌────────────────▼────────────────┐ │
│ │ Domain Layer │ │
│ │ (Aggregates, Entities, VOs, │ │
│ │ Domain Services, Events) │ │
│ └────────────────┬────────────────┘ │
│ │ │
└─────────────────────────────┼─────────────────────────────┘
┌─────────────────▼───────────────────────┐
│ Infrastructure Layer │
│ (Repositories, External Services, │
│ Cache, Messaging, Database) │
└─────────────────────────────────────────┘
```
### 2.2 目录结构
```
src/
├── api/ # API 层(入站适配器)
│ ├── controllers/ # HTTP 控制器
│ │ ├── health.controller.ts
│ │ ├── leaderboard.controller.ts
│ │ ├── leaderboard-config.controller.ts
│ │ └── virtual-account.controller.ts
│ ├── dto/ # 数据传输对象
│ │ ├── leaderboard.dto.ts
│ │ ├── leaderboard-config.dto.ts
│ │ └── virtual-account.dto.ts
│ ├── guards/ # 认证守卫
│ │ ├── jwt-auth.guard.ts
│ │ └── admin.guard.ts
│ ├── decorators/ # 自定义装饰器
│ │ ├── public.decorator.ts
│ │ └── current-user.decorator.ts
│ └── strategies/ # Passport 策略
│ └── jwt.strategy.ts
├── application/ # 应用层
│ ├── services/ # 应用服务
│ │ └── leaderboard-application.service.ts
│ └── schedulers/ # 定时任务
│ └── leaderboard-refresh.scheduler.ts
├── domain/ # 领域层(核心业务逻辑)
│ ├── aggregates/ # 聚合根
│ │ ├── leaderboard-ranking/
│ │ │ └── leaderboard-ranking.aggregate.ts
│ │ └── leaderboard-config/
│ │ └── leaderboard-config.aggregate.ts
│ ├── entities/ # 实体
│ │ └── virtual-account.entity.ts
│ ├── value-objects/ # 值对象
│ │ ├── leaderboard-type.enum.ts
│ │ ├── leaderboard-period.vo.ts
│ │ ├── ranking-score.vo.ts
│ │ ├── rank-position.vo.ts
│ │ ├── user-snapshot.vo.ts
│ │ └── virtual-account-type.enum.ts
│ ├── events/ # 领域事件
│ │ ├── domain-event.base.ts
│ │ ├── leaderboard-refreshed.event.ts
│ │ ├── config-updated.event.ts
│ │ └── ranking-changed.event.ts
│ ├── repositories/ # 仓储接口(端口)
│ │ ├── leaderboard-ranking.repository.interface.ts
│ │ ├── leaderboard-config.repository.interface.ts
│ │ └── virtual-account.repository.interface.ts
│ └── services/ # 领域服务
│ ├── leaderboard-calculation.service.ts
│ ├── virtual-ranking-generator.service.ts
│ └── ranking-merger.service.ts
├── infrastructure/ # 基础设施层(出站适配器)
│ ├── database/ # 数据库
│ │ └── prisma.service.ts
│ ├── repositories/ # 仓储实现
│ │ ├── leaderboard-ranking.repository.impl.ts
│ │ ├── leaderboard-config.repository.impl.ts
│ │ └── virtual-account.repository.impl.ts
│ ├── cache/ # 缓存服务
│ │ ├── redis.service.ts
│ │ └── leaderboard-cache.service.ts
│ ├── messaging/ # 消息队列
│ │ ├── kafka.service.ts
│ │ ├── event-publisher.service.ts
│ │ └── event-consumer.service.ts
│ └── external/ # 外部服务客户端
│ ├── referral-service.client.ts
│ └── identity-service.client.ts
├── modules/ # NestJS 模块定义
│ ├── domain.module.ts
│ ├── infrastructure.module.ts
│ ├── application.module.ts
│ └── api.module.ts
├── app.module.ts # 应用根模块
└── main.ts # 应用入口
```
## 3. 领域模型设计
### 3.1 聚合根
#### LeaderboardRanking排名聚合
```typescript
class LeaderboardRanking {
// 标识
id: bigint;
// 榜单信息
leaderboardType: LeaderboardType; // DAILY | WEEKLY | MONTHLY
period: LeaderboardPeriod;
// 用户信息
userId: bigint;
isVirtual: boolean;
// 排名信息
rankPosition: RankPosition; // 实际排名
displayPosition: RankPosition; // 显示排名
previousRank: RankPosition | null;
// 分值信息
score: RankingScore;
// 用户快照
userSnapshot: UserSnapshot;
}
```
#### LeaderboardConfig配置聚合
```typescript
class LeaderboardConfig {
// 标识
id: bigint;
configKey: string;
// 榜单开关
dailyEnabled: boolean;
weeklyEnabled: boolean;
monthlyEnabled: boolean;
// 虚拟排名设置
virtualRankingEnabled: boolean;
virtualAccountCount: number;
// 显示设置
displayLimit: number;
refreshIntervalMinutes: number;
}
```
### 3.2 值对象
#### RankingScore排名分值
```typescript
// 龙虎榜分值计算公式:
// effectiveScore = totalTeamPlanting - maxDirectTeamPlanting
class RankingScore {
totalTeamPlanting: number; // 团队总认种
maxDirectTeamPlanting: number; // 最大单个直推团队认种
effectiveScore: number; // 有效分值(龙虎榜分值)
static calculate(total: number, maxDirect: number): RankingScore {
const effective = Math.max(0, total - maxDirect);
return new RankingScore(total, maxDirect, effective);
}
}
```
#### LeaderboardPeriod周期
```typescript
class LeaderboardPeriod {
key: string; // 2024-01-15 | 2024-W03 | 2024-01
startAt: Date;
endAt: Date;
static currentDaily(): LeaderboardPeriod;
static currentWeekly(): LeaderboardPeriod;
static currentMonthly(): LeaderboardPeriod;
}
```
### 3.3 领域事件
| 事件 | 触发时机 | 数据 |
|------|----------|------|
| LeaderboardRefreshedEvent | 榜单刷新完成 | type, period, rankings |
| ConfigUpdatedEvent | 配置变更 | configKey, changes |
| RankingChangedEvent | 用户排名变化 | userId, oldRank, newRank |
## 4. 数据模型
### 4.1 数据库表设计
```
┌─────────────────────────────────────────────────────────────────┐
│ leaderboard_rankings │
├─────────────────────────────────────────────────────────────────┤
│ ranking_id (PK) │ 排名ID │
│ leaderboard_type │ 榜单类型 (DAILY/WEEKLY/MONTHLY) │
│ period_key │ 周期标识 │
│ user_id │ 用户ID │
│ is_virtual │ 是否虚拟账户 │
│ rank_position │ 实际排名 │
│ display_position │ 显示排名 │
│ previous_rank │ 上次排名 │
│ total_team_planting │ 团队总认种 │
│ max_direct_team_planting│ 最大直推团队认种 │
│ effective_score │ 有效分值 │
│ user_snapshot │ 用户快照 (JSON) │
│ period_start_at │ 周期开始时间 │
│ period_end_at │ 周期结束时间 │
│ calculated_at │ 计算时间 │
│ created_at │ 创建时间 │
├─────────────────────────────────────────────────────────────────┤
│ UK: (leaderboard_type, period_key, user_id) │
│ IDX: (leaderboard_type, period_key, display_position) │
│ IDX: (leaderboard_type, period_key, effective_score DESC) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ leaderboard_configs │
├─────────────────────────────────────────────────────────────────┤
│ config_id (PK) │ 配置ID │
│ config_key (UK) │ 配置键 (GLOBAL) │
│ daily_enabled │ 日榜开关 │
│ weekly_enabled │ 周榜开关 │
│ monthly_enabled │ 月榜开关 │
│ virtual_ranking_enabled │ 虚拟排名开关 │
│ virtual_account_count │ 虚拟账户数量 │
│ display_limit │ 显示数量限制 │
│ refresh_interval_minutes│ 刷新间隔(分钟) │
│ created_at │ 创建时间 │
│ updated_at │ 更新时间 │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ virtual_accounts │
├─────────────────────────────────────────────────────────────────┤
│ virtual_account_id (PK) │ 虚拟账户ID │
│ account_type │ 账户类型 │
│ display_name │ 显示名称 │
│ avatar │ 头像URL │
│ province_code │ 省份代码 │
│ city_code │ 城市代码 │
│ min_score │ 最小分值 │
│ max_score │ 最大分值 │
│ current_score │ 当前分值 │
│ usdt_balance │ USDT余额 │
│ hashpower_balance │ 算力余额 │
│ is_active │ 是否激活 │
│ created_at │ 创建时间 │
│ updated_at │ 更新时间 │
└─────────────────────────────────────────────────────────────────┘
```
### 4.2 缓存设计
```
Redis Key 设计:
leaderboard:{type}:{period}:rankings # 排名列表 (ZSET)
leaderboard:{type}:{period}:user:{id} # 用户排名详情 (HASH)
leaderboard:config # 全局配置 (HASH)
leaderboard:virtual:accounts # 虚拟账户列表 (LIST)
TTL:
- 日榜: 10 分钟
- 周榜: 30 分钟
- 月榜: 1 小时
- 配置: 5 分钟
```
## 5. 核心业务流程
### 5.1 排名刷新流程
```
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
│ Scheduler │────▶│ Application │────▶│ ReferralService │
│ (Cron) │ │ Service │ │ (External) │
└─────────────┘ └──────┬───────┘ └────────┬────────┘
│ │
│ 获取团队数据 │
│◀──────────────────────┘
┌───────────────────────┐
│ LeaderboardCalculation│
│ Service │
│ - 计算有效分值 │
│ - 排序 │
│ - 生成排名 │
└───────────┬───────────┘
┌───────────────────────┐
│ VirtualRanking │
│ Generator │
│ - 生成虚拟排名 │
└───────────┬───────────┘
┌───────────────────────┐
│ RankingMerger │
│ - 合并真实/虚拟排名 │
│ - 调整显示位置 │
└───────────┬───────────┘
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Database │ │ Cache │ │ Kafka │
│ (Persist) │ │ (Update) │ │ (Publish) │
└───────────────┘ └───────────────┘ └───────────────┘
```
### 5.2 排名查询流程
```
┌─────────┐ ┌────────────┐ ┌─────────┐
│ Client │────▶│ Controller │────▶│ Cache │
└─────────┘ └─────┬──────┘ └────┬────┘
│ │
│ Cache Hit? │
│◀────────────────┘
┌───────┴───────┐
│ Yes No│
▼ ▼
┌──────────┐ ┌──────────┐
│ Return │ │ Database │
│ Cached │ │ Query │
└──────────┘ └────┬─────┘
┌──────────┐
│ Update │
│ Cache │
└────┬─────┘
┌──────────┐
│ Return │
└──────────┘
```
## 6. 安全设计
### 6.1 认证与授权
| 端点 | 认证要求 | 权限要求 |
|------|----------|----------|
| GET /leaderboard/* | 无 (公开) | - |
| GET /leaderboard/my-rank | JWT | 用户 |
| GET /leaderboard/config | JWT | 管理员 |
| POST /leaderboard/config/* | JWT | 管理员 |
| * /virtual-accounts/* | JWT | 管理员 |
### 6.2 数据安全
- 用户敏感信息脱敏
- BigInt ID 防止遍历
- 输入验证与清洗
- SQL 注入防护 (Prisma)
## 7. 性能优化
### 7.1 缓存策略
- **L1**: 应用内存缓存(热点数据)
- **L2**: Redis 分布式缓存
- **缓存预热**: 服务启动时加载
### 7.2 数据库优化
- 合理索引设计
- 分页查询
- 批量操作
- 读写分离(可选)
### 7.3 异步处理
- 排名计算异步执行
- 事件驱动更新
- 消息队列削峰
## 8. 可观测性
### 8.1 日志
```typescript
// 结构化日志
{
level: 'info',
timestamp: '2024-01-15T10:30:00Z',
service: 'leaderboard-service',
traceId: 'abc123',
message: 'Leaderboard refreshed',
context: {
type: 'DAILY',
period: '2024-01-15',
totalRankings: 100
}
}
```
### 8.2 健康检查
- `/health` - 服务存活检查
- `/health/ready` - 服务就绪检查(含依赖)
### 8.3 指标 (Metrics)
- 请求延迟
- 缓存命中率
- 排名计算耗时
- 数据库连接池状态
## 9. 扩展性考虑
### 9.1 水平扩展
- 无状态服务设计
- Redis 集群支持
- Kafka 分区消费
### 9.2 垂直扩展
- 异步任务队列
- 数据库分片(未来)
- 冷热数据分离