diff --git a/src/modules/users/crone/user-restuarant.crone.ts b/src/modules/users/crone/user-restuarant.crone.ts new file mode 100644 index 0000000..d7a2639 --- /dev/null +++ b/src/modules/users/crone/user-restuarant.crone.ts @@ -0,0 +1,70 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { Cron } from '@nestjs/schedule'; +import { CreateRequestContext } from '@mikro-orm/core'; +import { EntityManager } from '@mikro-orm/postgresql'; +import { OrderStatus } from '../../orders/interface/order.interface'; + +const COUNTED_ORDER_STATUSES = [ + OrderStatus.PAID, + OrderStatus.PREPARING, + OrderStatus.DELIVERED_TO_WAITER, + OrderStatus.DELIVERED_TO_RECEPTIONIST, + OrderStatus.SHIPPED, + OrderStatus.COMPLETED, +]; + +@Injectable() +export class UserRestaurantCrone { + private readonly logger = new Logger(UserRestaurantCrone.name); + + constructor(private readonly em: EntityManager) {} + + // run every day at 03:00 (3:00 AM) + @Cron('0 3 * * *', { + name: 'syncUserRestaurantOrderStats', + timeZone: 'UTC', + }) + @CreateRequestContext() + async handleCron() { + try { + this.logger.debug('Starting nightly user-restaurant order stats sync'); + + const statusPlaceholders = COUNTED_ORDER_STATUSES.map(() => '?').join(', '); + + await this.em.execute( + `UPDATE user_restuarant + SET order_count = 0, total_order_amount = 0, updated_at = now() + WHERE deleted_at IS NULL`, + ); + + await this.em.execute( + ` + UPDATE user_restuarant ur + SET + order_count = stats.order_count, + total_order_amount = stats.total_order_amount, + updated_at = now() + FROM ( + SELECT + o.user_id, + o.restaurant_id, + COUNT(*)::int AS order_count, + COALESCE(SUM(o.total), 0)::int AS total_order_amount + FROM orders o + WHERE o.status IN (${statusPlaceholders}) + AND o.deleted_at IS NULL + GROUP BY o.user_id, o.restaurant_id + ) stats + WHERE ur.user_id = stats.user_id + AND ur.restaurant_id = stats.restaurant_id + AND ur.deleted_at IS NULL + `, + COUNTED_ORDER_STATUSES, + ); + + this.logger.log('Successfully synced user-restaurant order stats'); + } catch (err) { + this.logger.error(`UserRestaurantCrone failed: ${err?.message}`, err); + } + } +} diff --git a/src/modules/users/user.module.ts b/src/modules/users/user.module.ts index 0ddb3b0..d002b18 100644 --- a/src/modules/users/user.module.ts +++ b/src/modules/users/user.module.ts @@ -14,11 +14,12 @@ import { PointTransaction } from './entities/point-transaction.entity'; import { PointTransactionRepository } from './repositories/point-transaction.repository'; import { UserRestaurant } from './entities/user-restuarant.entity'; import { UserRestaurantRepository } from './repositories/user-restuarant.repository'; +import { UserRestaurantCrone } from './crone/user-restuarant.crone'; @Module({ providers: [UserService, WalletService, UserRepository, WalletTransactionRepository, - PointTransactionRepository, UserRestaurantRepository], + PointTransactionRepository, UserRestaurantRepository, UserRestaurantCrone], controllers: [UsersController], imports: [ MikroOrmModule.forFeature([User, UserAddress, WalletTransaction, PointTransaction, UserRestaurant]),