import { Injectable } from '@nestjs/common'; import { ContributionAccountRepository } from '../../infrastructure/persistence/repositories/contribution-account.repository'; import { RedisService } from '../../infrastructure/redis/redis.service'; export interface ContributionRankingDto { rank: number; accountSequence: string; effectiveContribution: string; personalContribution: string; totalPending: string; totalUnlocked: string; } @Injectable() export class GetContributionRankingQuery { private readonly RANKING_CACHE_KEY = 'contribution:ranking'; private readonly CACHE_TTL = 300; // 5分钟缓存 constructor( private readonly accountRepository: ContributionAccountRepository, private readonly redis: RedisService, ) {} /** * 获取算力排行榜 */ async execute(limit: number = 100): Promise { // 尝试从缓存获取 const cached = await this.redis.getJson(`${this.RANKING_CACHE_KEY}:${limit}`); if (cached) { return cached; } // 从数据库获取 const topContributors = await this.accountRepository.findTopContributors(limit); const ranking: ContributionRankingDto[] = topContributors.map((account, index) => ({ rank: index + 1, accountSequence: account.accountSequence, effectiveContribution: account.effectiveContribution.value.toString(), personalContribution: account.personalContribution.value.toString(), totalPending: account.totalPending.value.toString(), totalUnlocked: account.totalUnlocked.value.toString(), })); // 缓存结果 await this.redis.setJson(`${this.RANKING_CACHE_KEY}:${limit}`, ranking, this.CACHE_TTL); return ranking; } /** * 获取指定用户的排名 */ async getUserRank(accountSequence: string): Promise<{ rank: number | null; effectiveContribution: string; percentile: number | null; } | null> { const account = await this.accountRepository.findByAccountSequence(accountSequence); if (!account) { return null; } // 使用 Redis 有序集合来快速获取排名 // 这需要在算力变化时同步更新 Redis const rank = await this.redis.zrevrank('contribution:leaderboard', accountSequence); const totalAccounts = await this.accountRepository.countAccountsWithContribution(); return { rank: rank !== null ? rank + 1 : null, effectiveContribution: account.effectiveContribution.value.toString(), percentile: rank !== null && totalAccounts > 0 ? ((totalAccounts - rank) / totalAccounts) * 100 : null, }; } /** * 刷新排行榜缓存 */ async refreshRankingCache(): Promise { // 清除旧缓存 await this.redis.del(`${this.RANKING_CACHE_KEY}:100`); await this.redis.del(`${this.RANKING_CACHE_KEY}:50`); await this.redis.del(`${this.RANKING_CACHE_KEY}:10`); // 重新生成缓存 await this.execute(100); } }