import { Injectable, Logger } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; import { EntityManager } from '@mikro-orm/postgresql'; import { FoodRepository } from '../../foods/repositories/food.repository'; import { ReviewRepository } from '../repositories/review.repository'; import { ReviewStatus } from '../enums/review-status.enum'; @Injectable() export class FoodRatingCronService { private readonly logger = new Logger(FoodRatingCronService.name); constructor( private readonly em: EntityManager, private readonly foodRepository: FoodRepository, private readonly reviewRepository: ReviewRepository, ) { } /** * Cron job that runs at midnight every day (00:00:00 UTC) * Calculates and updates the average rating for each restaurant food */ @Cron('0 0 * * *', { name: 'calculateFoodRatings', timeZone: 'UTC', }) async handleCron() { this.logger.log('Starting daily food rating calculation cron job...'); try { // Get all foods from all restaurants const foods = await this.foodRepository.find({}); this.logger.log(`Found ${foods.length} foods to process`); let updatedCount = 0; let errorCount = 0; // Process each food item for (const food of foods) { try { // Get all approved reviews for this food that are not deleted const reviews = await this.reviewRepository.find( { food: { id: food.id }, status: ReviewStatus.APPROVED, deletedAt: null, }, { fields: ['rating'] }, ); // Calculate average rating let averageRating = 0; if (reviews.length > 0) { const sum = reviews.reduce((acc, review) => acc + review.rating, 0); averageRating = sum / reviews.length; } // Update food score food.score = parseFloat(averageRating.toFixed(2)); this.em.persist(food); updatedCount++; } catch (error) { this.logger.error(`Error processing food ${food.id}: ${error.message}`, error.stack); errorCount++; } } // Flush all updates at once for better performance await this.em.flush(); this.logger.log(`Food rating calculation completed. Updated: ${updatedCount}, Errors: ${errorCount}`); } catch (error) { this.logger.error(`Error in food rating cron job: ${error.message}`, error.stack); } } }