11import { RedisService } from '@liaoliaots/nestjs-redis' ;
22import { Injectable , OnApplicationBootstrap } from '@nestjs/common' ;
3- import { Cron , CronExpression } from '@nestjs/schedule' ;
3+ import { Cron , SchedulerRegistry } from '@nestjs/schedule' ;
4+ import { CronJob } from 'cron' ;
45import Redis from 'ioredis' ;
56
67import { Event } from '../../event/entity/event.entity' ;
78import { EventRepository } from '../../event/repository/event.reposiotry' ;
89import { SectionRepository } from '../../place/repository/section.repository' ;
10+ import { ONE_MINUTE_BEFORE_THE_HOUR } from '../const/cronExpressions.const' ;
911import { IN_BOOKING_DEFAULT_MAX_SIZE } from '../const/inBookingDefaultMaxSize.const' ;
1012
1113import { BookingSeatsService } from './booking-seats.service' ;
@@ -23,24 +25,47 @@ export class OpenBookingService implements OnApplicationBootstrap {
2325 private inBookingService : InBookingService ,
2426 private seatsUpdateService : BookingSeatsService ,
2527 private enterBookingService : EnterBookingService ,
28+ private schedulerRegistry : SchedulerRegistry ,
2629 ) {
2730 this . redis = this . redisService . getOrThrow ( ) ;
2831 }
2932
3033 async onApplicationBootstrap ( ) {
3134 await this . inBookingService . setInBookingSessionsDefaultMaxSize ( IN_BOOKING_DEFAULT_MAX_SIZE ) ;
32- await this . checkAndOpenReservations ( ) ;
35+ await this . scheduleUpcomingReservations ( ) ;
3336 }
3437
35- @Cron ( CronExpression . EVERY_HOUR )
36- async checkAndOpenReservations ( ) {
37- const events = await this . eventRepository . selectEvents ( ) ;
38+ @Cron ( ONE_MINUTE_BEFORE_THE_HOUR )
39+ async scheduleUpcomingReservations ( ) {
40+ const comingEvents = await this . eventRepository . selectUpcomingEvents ( ) ;
3841 const openedEventIds = new Set ( await this . getOpenedEventIds ( ) ) ;
39- const eventsToOpen = events . filter ( ( event ) => {
40- const now = new Date ( ) ;
41- return event . reservationOpenDate <= now && ! openedEventIds . has ( event . id ) ;
42+ const now = new Date ( ) ;
43+ const eventToOpen = comingEvents . filter ( ( event ) => event . reservationOpenDate <= now ) ;
44+ const eventsToSchedule = comingEvents . filter (
45+ ( event ) => ! openedEventIds . has ( event . id ) && event . reservationOpenDate > now ,
46+ ) ;
47+
48+ for ( const event of eventToOpen ) {
49+ await this . openReservation ( event ) ;
50+ }
51+ for ( const event of eventsToSchedule ) {
52+ this . scheduleReservationOpen ( event ) ;
53+ }
54+ }
55+
56+ private scheduleReservationOpen ( event : Event ) {
57+ const jobName = `reservation-open-${ event . id } ` ;
58+
59+ if ( this . schedulerRegistry . doesExist ( 'cron' , jobName ) ) {
60+ this . schedulerRegistry . deleteCronJob ( jobName ) ;
61+ }
62+
63+ const job = new CronJob ( event . reservationOpenDate , async ( ) => {
64+ await this . openReservationById ( event . id ) ;
4265 } ) ;
43- await Promise . all ( eventsToOpen . map ( ( event ) => this . openReservation ( event ) ) ) ;
66+
67+ this . schedulerRegistry . addCronJob ( jobName , job ) ;
68+ job . start ( ) ;
4469 }
4570
4671 async isEventOpened ( eventId : number ) {
@@ -53,7 +78,14 @@ export class OpenBookingService implements OnApplicationBootstrap {
5378 return eventIds ;
5479 }
5580
81+ private async openReservationById ( eventId : number ) {
82+ const event = await this . eventRepository . selectEvent ( eventId ) ;
83+ await this . openReservation ( event ) ;
84+ }
85+
5686 private async openReservation ( event : Event ) {
87+ this . validateOpeningEvent ( event ) ;
88+
5789 const eventId = event . id ;
5890 const place = await event . place ;
5991 const sections = await Promise . all (
@@ -71,6 +103,25 @@ export class OpenBookingService implements OnApplicationBootstrap {
71103 await this . registerOpenedEvent ( eventId ) ;
72104 }
73105
106+ private validateOpeningEvent ( event : Event ) {
107+ if ( ! event ) {
108+ return false ;
109+ }
110+
111+ const openTime = event . reservationOpenDate ;
112+ if ( openTime > new Date ( ) ) {
113+ const jobName = `reservation-open-${ event . id } ` ;
114+ const job = new CronJob ( openTime , async ( ) => {
115+ await this . openReservationById ( event . id ) ;
116+ } ) ;
117+ this . schedulerRegistry . addCronJob ( jobName , job ) ;
118+ job . start ( ) ;
119+ return false ;
120+ }
121+
122+ return true ;
123+ }
124+
74125 private async registerOpenedEvent ( eventId : number ) {
75126 await this . redis . set ( `open-booking:${ eventId } :opened` , 'true' ) ;
76127 }
0 commit comments