rwadurian/backend/services/contribution-service/src/application/queries/get-contribution-ranking.qu...

90 lines
2.9 KiB
TypeScript

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<ContributionRankingDto[]> {
// 尝试从缓存获取
const cached = await this.redis.getJson<ContributionRankingDto[]>(`${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<void> {
// 清除旧缓存
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);
}
}