This commit is contained in:
2025-12-07 15:35:02 +03:30
parent 2ea65a947c
commit 041d28fe5d
2 changed files with 81 additions and 1 deletions
@@ -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);
}
}
}
+2 -1
View File
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
import { ReviewService } from './providers/review.service'; import { ReviewService } from './providers/review.service';
import { ReviewController } from './controllers/review.controller'; import { ReviewController } from './controllers/review.controller';
import { ReviewRepository } from './repositories/review.repository'; import { ReviewRepository } from './repositories/review.repository';
import { FoodRatingCronService } from './providers/food-rating-cron.service';
import { MikroOrmModule } from '@mikro-orm/nestjs'; import { MikroOrmModule } from '@mikro-orm/nestjs';
import { Review } from './entities/review.entity'; import { Review } from './entities/review.entity';
import { FoodModule } from '../foods/food.module'; import { FoodModule } from '../foods/food.module';
@@ -18,7 +19,7 @@ import { JwtModule } from '@nestjs/jwt';
JwtModule, JwtModule,
], ],
controllers: [ReviewController], controllers: [ReviewController],
providers: [ReviewService, ReviewRepository], providers: [ReviewService, ReviewRepository, FoodRatingCronService],
exports: [ReviewRepository], exports: [ReviewRepository],
}) })
export class ReviewModule {} export class ReviewModule {}