107 lines
3.6 KiB
TypeScript
107 lines
3.6 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { MiningAccountRepository } from '../../infrastructure/persistence/repositories/mining-account.repository';
|
|
import { MiningConfigRepository } from '../../infrastructure/persistence/repositories/mining-config.repository';
|
|
import { PriceSnapshotRepository } from '../../infrastructure/persistence/repositories/price-snapshot.repository';
|
|
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
|
|
|
|
export interface MiningStatsDto {
|
|
// 配置信息
|
|
isActive: boolean;
|
|
currentEra: number;
|
|
eraStartDate: Date | null;
|
|
activatedAt: Date | null;
|
|
|
|
// 分配信息
|
|
totalShares: string;
|
|
distributionPool: string;
|
|
remainingDistribution: string;
|
|
secondDistribution: string;
|
|
|
|
// 参与信息
|
|
totalContribution: string;
|
|
participantCount: number;
|
|
totalMined: string;
|
|
|
|
// 价格信息
|
|
currentPrice: string;
|
|
priceChangePercent24h: number | null;
|
|
}
|
|
|
|
export interface MiningRankingDto {
|
|
rank: number;
|
|
accountSequence: string;
|
|
totalMined: string;
|
|
totalContribution: string;
|
|
}
|
|
|
|
@Injectable()
|
|
export class GetMiningStatsQuery {
|
|
constructor(
|
|
private readonly accountRepository: MiningAccountRepository,
|
|
private readonly configRepository: MiningConfigRepository,
|
|
private readonly priceRepository: PriceSnapshotRepository,
|
|
private readonly prisma: PrismaService,
|
|
) {}
|
|
|
|
async execute(): Promise<MiningStatsDto> {
|
|
const [config, latestPrice, price24hAgo, totalMined, participantCount, totalContribution] =
|
|
await Promise.all([
|
|
this.configRepository.getConfig(),
|
|
this.priceRepository.getLatestSnapshot(),
|
|
this.get24hAgoPrice(),
|
|
this.getTotalMined(),
|
|
this.accountRepository.countAccountsWithContribution(),
|
|
this.accountRepository.getTotalContribution(),
|
|
]);
|
|
|
|
let priceChangePercent24h: number | null = null;
|
|
if (latestPrice && price24hAgo) {
|
|
const currentPrice = latestPrice.price.value.toNumber();
|
|
const oldPrice = price24hAgo.price.value.toNumber();
|
|
if (oldPrice > 0) {
|
|
priceChangePercent24h = ((currentPrice - oldPrice) / oldPrice) * 100;
|
|
}
|
|
}
|
|
|
|
return {
|
|
isActive: config?.isActive || false,
|
|
currentEra: config?.currentEra || 1,
|
|
eraStartDate: config?.eraStartDate || null,
|
|
activatedAt: config?.activatedAt || null,
|
|
totalShares: config?.totalShares.toString() || '0',
|
|
distributionPool: config?.distributionPool.toString() || '0',
|
|
remainingDistribution: config?.remainingDistribution.toString() || '0',
|
|
secondDistribution: config?.secondDistribution.toString() || '0',
|
|
totalContribution: totalContribution.toString(),
|
|
participantCount,
|
|
totalMined: totalMined.toString(),
|
|
currentPrice: latestPrice?.price.toString() || '1',
|
|
priceChangePercent24h,
|
|
};
|
|
}
|
|
|
|
async getRanking(limit: number = 100): Promise<MiningRankingDto[]> {
|
|
const topMiners = await this.accountRepository.getTopMiners(limit);
|
|
|
|
return topMiners.map((account, index) => ({
|
|
rank: index + 1,
|
|
accountSequence: account.accountSequence,
|
|
totalMined: account.totalMined.toString(),
|
|
totalContribution: account.totalContribution.toString(),
|
|
}));
|
|
}
|
|
|
|
private async getTotalMined(): Promise<string> {
|
|
const result = await this.prisma.miningAccount.aggregate({
|
|
_sum: { totalMined: true },
|
|
});
|
|
return (result._sum.totalMined || 0).toString();
|
|
}
|
|
|
|
private async get24hAgoPrice() {
|
|
const time24hAgo = new Date();
|
|
time24hAgo.setHours(time24hAgo.getHours() - 24);
|
|
return this.priceRepository.getSnapshotAt(time24hAgo);
|
|
}
|
|
}
|