78 lines
2.4 KiB
TypeScript
78 lines
2.4 KiB
TypeScript
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);
|
|
}
|
|
}
|
|
}
|