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:
richarjiang
2026-04-02 12:33:50 +08:00
parent 593a6e5453
commit 994d1f75d5
15 changed files with 2183 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
import {
Body,
Controller,
Get,
Param,
Post,
Put,
Query,
UseGuards,
} from '@nestjs/common'
import { BookingStatus, UserRole } from '@mp-pilates/shared'
import { JwtAuthGuard } from '../auth/jwt-auth.guard'
import { RolesGuard } from '../auth/roles.guard'
import { Roles } from '../auth/roles.decorator'
import { CurrentUser } from '../common/decorators/current-user.decorator'
import { BookingService } from './booking.service'
import { CreateBookingDto } from './dto/create-booking.dto'
@Controller()
export class BookingController {
constructor(private readonly bookingService: BookingService) {}
// ─── User Endpoints ───────────────────────────────────────────────────────
@Post('booking')
@UseGuards(JwtAuthGuard)
async createBooking(
@CurrentUser('sub') userId: string,
@Body() dto: CreateBookingDto,
) {
return this.bookingService.createBooking(userId, dto)
}
@Put('booking/:id/cancel')
@UseGuards(JwtAuthGuard)
async cancelBooking(
@CurrentUser('sub') userId: string,
@Param('id') id: string,
) {
return this.bookingService.cancelBooking(userId, id)
}
@Get('booking/my/upcoming')
@UseGuards(JwtAuthGuard)
async getUpcomingBookings(@CurrentUser('sub') userId: string) {
return this.bookingService.getUpcomingBookings(userId)
}
@Get('booking/my')
@UseGuards(JwtAuthGuard)
async getMyBookings(
@CurrentUser('sub') userId: string,
@Query('status') status?: BookingStatus,
@Query('page') page?: string,
@Query('limit') limit?: string,
) {
return this.bookingService.getMyBookings(
userId,
status,
page ? Number(page) : 1,
limit ? Number(limit) : 10,
)
}
// ─── Admin Endpoints ──────────────────────────────────────────────────────
@Get('admin/bookings')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(UserRole.ADMIN)
async getAllBookings(
@Query('page') page?: string,
@Query('limit') limit?: string,
@Query('status') status?: BookingStatus,
) {
return this.bookingService.getAllBookings(
page ? Number(page) : 1,
limit ? Number(limit) : 10,
status,
)
}
}