From 041d28fe5d933bd31f0adc872c238c8625620ced Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sun, 7 Dec 2025 15:35:02 +0330 Subject: [PATCH] crone --- .../providers/food-rating-cron.service.ts | 79 +++++++++++++++++++ src/modules/review/review.module.ts | 3 +- 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/modules/review/providers/food-rating-cron.service.ts diff --git a/src/modules/review/providers/food-rating-cron.service.ts b/src/modules/review/providers/food-rating-cron.service.ts new file mode 100644 index 0000000..27a9515 --- /dev/null +++ b/src/modules/review/providers/food-rating-cron.service.ts @@ -0,0 +1,79 @@ +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'; + +@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 }, + isApproved: true, + 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 rate + food.rate = 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); + } + } +} + diff --git a/src/modules/review/review.module.ts b/src/modules/review/review.module.ts index de16608..ec6d3b8 100644 --- a/src/modules/review/review.module.ts +++ b/src/modules/review/review.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { ReviewService } from './providers/review.service'; import { ReviewController } from './controllers/review.controller'; import { ReviewRepository } from './repositories/review.repository'; +import { FoodRatingCronService } from './providers/food-rating-cron.service'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { Review } from './entities/review.entity'; import { FoodModule } from '../foods/food.module'; @@ -18,7 +19,7 @@ import { JwtModule } from '@nestjs/jwt'; JwtModule, ], controllers: [ReviewController], - providers: [ReviewService, ReviewRepository], + providers: [ReviewService, ReviewRepository, FoodRatingCronService], exports: [ReviewRepository], }) export class ReviewModule {}