rwadurian/backend/services/mining-service/src/application/schedulers/mining.scheduler.ts

114 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<void> {
try {
await this.distributionService.executeMinuteDistribution();
} catch (error) {
this.logger.error('Failed to execute minute distribution', error);
}
}
/**
* 每天凌晨0点生成每日统计
*/
@Cron('0 0 * * *')
async generateDailyStats(): Promise<void> {
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<void> {
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);
}
}
}