feat(server): add booking, payment, and scheduler modules
Booking: reservation with atomic transactions, cancellation with refund logic based on cancelHoursLimit (23 tests) Payment: WeChat Pay integration (mock), order lifecycle, membership creation on payment callback (13 tests) Scheduler: cron tasks for slot generation, cleanup, membership expiry (8 tests) 109 total tests passing across 9 test suites
This commit is contained in:
54
packages/server/src/scheduler/scheduler.service.ts
Normal file
54
packages/server/src/scheduler/scheduler.service.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Injectable, Logger } from '@nestjs/common'
|
||||
import { Cron } from '@nestjs/schedule'
|
||||
import { SlotGeneratorService } from '../time-slot/slot-generator.service'
|
||||
|
||||
@Injectable()
|
||||
export class SchedulerService {
|
||||
private readonly logger = new Logger(SchedulerService.name)
|
||||
|
||||
constructor(private readonly slotGenerator: SlotGeneratorService) {}
|
||||
|
||||
/** 02:00 daily — generate slots 14 days ahead from week templates */
|
||||
@Cron('0 2 * * *')
|
||||
async handleSlotGeneration(): Promise<void> {
|
||||
try {
|
||||
const count = await this.slotGenerator.generateSlots(14)
|
||||
this.logger.log(`[handleSlotGeneration] Created ${count} new time slots`)
|
||||
} catch (err) {
|
||||
this.logger.error('[handleSlotGeneration] Failed to generate slots', err)
|
||||
}
|
||||
}
|
||||
|
||||
/** 02:30 daily — close past OPEN slots */
|
||||
@Cron('30 2 * * *')
|
||||
async handleCleanupSlots(): Promise<void> {
|
||||
try {
|
||||
const count = await this.slotGenerator.cleanupExpiredSlots()
|
||||
this.logger.log(`[handleCleanupSlots] Closed ${count} expired slots`)
|
||||
} catch (err) {
|
||||
this.logger.error('[handleCleanupSlots] Failed to clean up slots', err)
|
||||
}
|
||||
}
|
||||
|
||||
/** 03:00 daily — expire memberships past their end date or with 0 sessions */
|
||||
@Cron('0 3 * * *')
|
||||
async handleCheckMemberships(): Promise<void> {
|
||||
try {
|
||||
const count = await this.slotGenerator.checkExpiredMemberships()
|
||||
this.logger.log(`[handleCheckMemberships] Updated ${count} memberships`)
|
||||
} catch (err) {
|
||||
this.logger.error('[handleCheckMemberships] Failed to check memberships', err)
|
||||
}
|
||||
}
|
||||
|
||||
/** 22:00 daily — mark past CONFIRMED bookings as COMPLETED */
|
||||
@Cron('0 22 * * *')
|
||||
async handleCompleteBookings(): Promise<void> {
|
||||
try {
|
||||
const count = await this.slotGenerator.completeBookings()
|
||||
this.logger.log(`[handleCompleteBookings] Completed ${count} bookings`)
|
||||
} catch (err) {
|
||||
this.logger.error('[handleCompleteBookings] Failed to complete bookings', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user