# 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 垂直扩展 - 异步任务队列 - 数据库分片(未来) - 冷热数据分离