import { Injectable, Logger, Inject } from '@nestjs/common'; import { Cron, CronExpression } from '@nestjs/schedule'; import { MetricsService } from './metrics.service'; import { PresenceRedisRepository } from '../redis/presence-redis.repository'; import { DAILY_ACTIVE_STATS_REPOSITORY, IDailyActiveStatsRepository, } from '../../domain/repositories/daily-active-stats.repository.interface'; import { EVENT_LOG_REPOSITORY, IEventLogRepository, } from '../../domain/repositories/event-log.repository.interface'; import { format } from 'date-fns'; @Injectable() export class MetricsCollectorService { private readonly logger = new Logger(MetricsCollectorService.name); private readonly presenceWindowSeconds: number; constructor( private readonly metricsService: MetricsService, private readonly presenceRedisRepository: PresenceRedisRepository, @Inject(DAILY_ACTIVE_STATS_REPOSITORY) private readonly dauRepository: IDailyActiveStatsRepository, @Inject(EVENT_LOG_REPOSITORY) private readonly eventLogRepository: IEventLogRepository, ) { this.presenceWindowSeconds = parseInt( process.env.PRESENCE_WINDOW_SECONDS || '180', 10, ); } /** * 每 15 秒更新在线人数指标 */ @Cron('*/15 * * * * *') async collectOnlineUsersMetric(): Promise { try { const now = Math.floor(Date.now() / 1000); const threshold = now - this.presenceWindowSeconds; const count = await this.presenceRedisRepository.countOnlineUsers(threshold); this.metricsService.setOnlineUsers(count); this.logger.debug(`Online users metric updated: ${count}`); } catch (error) { this.logger.error('Failed to collect online users metric', error); } } /** * 每 5 分钟更新 DAU 指标 */ @Cron(CronExpression.EVERY_5_MINUTES) async collectDauMetric(): Promise { try { const today = new Date(); const dateStr = format(today, 'yyyy-MM-dd'); const stats = await this.dauRepository.findByDate(today); if (stats) { this.metricsService.setDau(dateStr, stats.dauCount); this.logger.debug(`DAU metric updated: ${dateStr} = ${stats.dauCount}`); } } catch (error) { this.logger.error('Failed to collect DAU metric', error); } } /** * 启动时立即收集一次 */ async onModuleInit(): Promise { await this.collectOnlineUsersMetric(); await this.collectDauMetric(); } }