Introduction of segment-based seat reservation

This commit is contained in:
Stephanos A
2026-05-17 11:11:39 +03:00
parent 39ba561d8f
commit 071a57a668
14 changed files with 2042 additions and 8 deletions

View File

@@ -0,0 +1,136 @@
import { Controller, Post, Get, Body, Query, Param } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
import { EnhancedSeatsService } from './enhanced-seats.service';
import { HoldSeatsDto, ConfirmBookingDto, SeatAvailabilityDto, ReleaseSeatsDto } from './segments.dto';
@ApiTags('Segment-based Seats')
@Controller('segments/seats')
export class SegmentSeatsController {
constructor(private enhancedSeatsService: EnhancedSeatsService) {}
@Post('hold')
@ApiOperation({
summary: 'Hold seats for specific journey segments',
description: 'Reserve seats for a partial journey (e.g., Addis Ababa → Dire Dawa) with 10-minute expiry'
})
@ApiResponse({
status: 201,
description: 'Seats held successfully',
schema: {
example: {
holdId: 'hold_123',
expiresAt: '2024-01-15T10:10:00Z',
segments: [
{ fromName: 'Addis Ababa', toName: 'Adama', fromSequence: 0, toSequence: 1 },
{ fromName: 'Adama', toName: 'Awash', fromSequence: 1, toSequence: 2 },
{ fromName: 'Awash', toName: 'Dire Dawa', fromSequence: 2, toSequence: 3 }
],
seats: ['seat_1', 'seat_2']
}
}
})
@ApiResponse({ status: 409, description: 'Seats not available for requested segments' })
async holdSeats(@Body() dto: HoldSeatsDto) {
return this.enhancedSeatsService.holdSeats({
tripId: dto.tripId,
seatIds: dto.seatIds,
passengerId: dto.passengerId,
originStationId: dto.originStationId,
destinationStationId: dto.destinationStationId,
fareQuoteId: dto.fareQuoteId
});
}
@Post('confirm')
@ApiOperation({
summary: 'Confirm booking and convert hold to reservation',
description: 'Convert seat hold to confirmed booking after payment success'
})
@ApiResponse({
status: 200,
description: 'Booking confirmed successfully',
schema: {
example: {
bookingId: 'booking_123',
confirmedSeats: ['seat_1', 'seat_2'],
segments: [
{ fromName: 'Addis Ababa', toName: 'Adama' },
{ fromName: 'Adama', toName: 'Awash' },
{ fromName: 'Awash', toName: 'Dire Dawa' }
]
}
}
})
@ApiResponse({ status: 400, description: 'Hold expired or not found' })
async confirmBooking(@Body() dto: ConfirmBookingDto) {
return this.enhancedSeatsService.confirmBooking(dto);
}
@Post('release')
@ApiOperation({
summary: 'Release seats when train reaches station',
description: 'Automatically release seats for passengers who have reached their destination'
})
@ApiResponse({
status: 200,
description: 'Seats released successfully',
schema: {
example: {
releasedSeats: ['seat_1', 'seat_2'],
stationId: 'st_DRE'
}
}
})
async releaseSeats(@Body() dto: ReleaseSeatsDto) {
return this.enhancedSeatsService.releaseSeats(dto.tripId, dto.currentStationId);
}
@Get('availability')
@ApiOperation({
summary: 'Check seat availability for journey segments',
description: 'Get available seats for a specific origin-destination pair'
})
@ApiResponse({
status: 200,
description: 'Seat availability retrieved',
schema: {
example: {
segments: [
{ fromName: 'Addis Ababa', toName: 'Adama', fromSequence: 0, toSequence: 1 },
{ fromName: 'Adama', toName: 'Awash', fromSequence: 1, toSequence: 2 }
],
availableSeats: [
{ id: 'seat_1', label: '1A', coach: 'A', serviceClass: 'ECONOMY', row: 1, col: 'A' },
{ id: 'seat_2', label: '1B', coach: 'A', serviceClass: 'ECONOMY', row: 1, col: 'B' }
],
totalAvailable: 2
}
}
})
async getSeatAvailability(@Query() dto: SeatAvailabilityDto) {
return this.enhancedSeatsService.getSeatAvailability(
dto.tripId,
dto.originStationId,
dto.destinationStationId
);
}
@Post('expire-holds')
@ApiOperation({
summary: 'Expire old seat holds (background job)',
description: 'Release seats from expired holds and make them available'
})
@ApiResponse({
status: 200,
description: 'Expired holds processed',
schema: {
example: {
expiredHolds: 5,
releasedSeats: ['seat_1', 'seat_2', 'seat_3']
}
}
})
async expireHolds() {
return this.enhancedSeatsService.expireHolds();
}
}