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 { 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 { 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 { 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); } }