import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { Cron, CronExpression } from '@nestjs/schedule'; import { MiningDistributionService } from '../services/mining-distribution.service'; import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service'; import { RedisService } from '../../infrastructure/redis/redis.service'; @Injectable() export class MiningScheduler implements OnModuleInit { private readonly logger = new Logger(MiningScheduler.name); constructor( private readonly distributionService: MiningDistributionService, private readonly prisma: PrismaService, private readonly redis: RedisService, ) {} onModuleInit() { this.logger.log('Mining scheduler initialized'); } /** * 每分钟执行挖矿分配 */ @Cron(CronExpression.EVERY_MINUTE) async executeMinuteDistribution(): Promise { try { await this.distributionService.executeMinuteDistribution(); } catch (error) { this.logger.error('Failed to execute minute distribution', error); } } /** * 每天凌晨0点生成每日统计 */ @Cron('0 0 * * *') async generateDailyStats(): Promise { const lockValue = await this.redis.acquireLock('mining:daily-stats:lock', 300); if (!lockValue) { return; } try { const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); yesterday.setHours(0, 0, 0, 0); const endOfYesterday = new Date(yesterday); endOfYesterday.setHours(23, 59, 59, 999); // 聚合昨天的分钟统计 const minuteStats = await this.prisma.minuteMiningStat.aggregate({ where: { minute: { gte: yesterday, lte: endOfYesterday, }, }, _sum: { totalDistributed: true, burnAmount: true, }, _avg: { totalContribution: true, }, _max: { participantCount: true, }, }); // 创建每日统计 await this.prisma.dailyMiningStat.create({ data: { date: yesterday, totalContribution: minuteStats._avg.totalContribution || 0, totalDistributed: minuteStats._sum.totalDistributed || 0, totalBurned: minuteStats._sum.burnAmount || 0, participantCount: minuteStats._max.participantCount || 0, avgContributionRate: 0, // TODO: 计算 }, }); this.logger.log(`Daily stats generated for ${yesterday.toISOString().split('T')[0]}`); } catch (error) { this.logger.error('Failed to generate daily stats', error); } finally { await this.redis.releaseLock('mining:daily-stats:lock', lockValue); } } /** * 每小时清理旧的分钟统计(保留7天) */ @Cron('0 * * * *') async cleanupOldMinuteStats(): Promise { try { const sevenDaysAgo = new Date(); sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); const result = await this.prisma.minuteMiningStat.deleteMany({ where: { minute: { lt: sevenDaysAgo }, }, }); if (result.count > 0) { this.logger.log(`Cleaned up ${result.count} old minute stats`); } } catch (error) { this.logger.error('Failed to cleanup old minute stats', error); } } }