mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 04:08:11 +00:00
Refactor business logic for train,schedule,coach,seat and search modules
This commit is contained in:
@@ -10,12 +10,12 @@ export class SeatsController {
|
||||
constructor(private service: SeatsService) {}
|
||||
|
||||
// ── Seat Map ──────────────────────────────────────────────────────────────
|
||||
@Get('seatmap/:tripId')
|
||||
@ApiOperation({ summary: 'Get seat map for a trip' })
|
||||
@ApiParam({ name: 'tripId', description: 'Trip UUID' })
|
||||
@Get('seatmap/:scheduleId')
|
||||
@ApiOperation({ summary: 'Get seat map for a schedule' })
|
||||
@ApiParam({ name: 'scheduleId', description: 'TrainSchedule UUID' })
|
||||
@ApiQuery({ name: 'coachId', required: false, description: 'Filter by coach UUID' })
|
||||
@ApiResponse({ status: 200, description: 'Returns coaches with seats and seat class info' })
|
||||
getSeatMap(@Param('tripId') tripId: string, @Query('coachId') coachId?: string) { return this.service.getSeatMap(tripId, coachId); }
|
||||
getSeatMap(@Param('scheduleId') scheduleId: string, @Query('coachId') coachId?: string) { return this.service.getSeatMap(scheduleId, coachId); }
|
||||
|
||||
// ── Hold / Release ────────────────────────────────────────────────────────
|
||||
@Post('hold')
|
||||
@@ -33,10 +33,10 @@ export class SeatsController {
|
||||
@ApiResponse({ status: 404, description: 'Hold not found' })
|
||||
releaseHold(@Param('holdId') holdId: string) { return this.service.releaseHold(holdId); }
|
||||
|
||||
@Get('export/csv/:tripId') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Export seats as CSV' })
|
||||
async exportCSV(@Param('tripId') tripId: string) {
|
||||
const csv = await this.service.exportSeatsCSV(tripId);
|
||||
return { csv, filename: `seats-${tripId}.csv` };
|
||||
@Get('export/csv/:scheduleId') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Export seats as CSV' })
|
||||
async exportCSV(@Param('scheduleId') scheduleId: string) {
|
||||
const csv = await this.service.exportSeatsCSV(scheduleId);
|
||||
return { csv, filename: `seats-${scheduleId}.csv` };
|
||||
}
|
||||
|
||||
@Post('import/preview') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Preview CSV import' })
|
||||
@@ -45,7 +45,7 @@ export class SeatsController {
|
||||
}
|
||||
|
||||
@Post('import/commit') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Commit CSV import' })
|
||||
importCSV(@Body() body: { tripId: string; csv: string; commit: boolean }) {
|
||||
return this.service.importSeatsCSV(body.tripId, body.csv, body.commit);
|
||||
importCSV(@Body() body: { scheduleId: string; csv: string; commit: boolean }) {
|
||||
return this.service.importSeatsCSV(body.scheduleId, body.csv, body.commit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { IsString, IsArray, IsOptional } from 'class-validator';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
export class HoldSeatsDto {
|
||||
@ApiProperty({ example: 'trip-uuid' }) @IsString() tripId: string;
|
||||
@ApiProperty({ example: 'schedule-uuid' }) @IsString() scheduleId: string;
|
||||
@ApiProperty({ example: 'passenger-uuid' }) @IsString() passengerId: string;
|
||||
@ApiProperty({ type: [String], example: ['seat-uuid-1', 'seat-uuid-2'] }) @IsArray() seatIds: string[];
|
||||
@ApiPropertyOptional({ example: 'fare-quote-uuid' }) @IsOptional() @IsString() fareQuoteId?: string;
|
||||
|
||||
@@ -8,14 +8,20 @@ export class SeatsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
// ── Seat Map ──────────────────────────────────────────────────────────────
|
||||
async getSeatMap(tripId: string, coachId?: string) {
|
||||
const coaches = await this.prisma.coach.findMany({ where: { tripId, ...(coachId ? { id: coachId } : {}) }, include: { seats: { orderBy: [{ row: 'asc' }, { col: 'asc' }] } } });
|
||||
async getSeatMap(scheduleId: string, coachId?: string) {
|
||||
const assignments = await this.prisma.coachAssignment.findMany({
|
||||
where: { scheduleId, ...(coachId ? { coachId } : {}) },
|
||||
include: { coach: { include: { seats: { orderBy: [{ row: 'asc' }, { col: 'asc' }] }, seatClass: true } } },
|
||||
orderBy: { positionNumber: 'asc' },
|
||||
});
|
||||
return {
|
||||
coaches: coaches.map((coach) => ({
|
||||
id: coach.id,
|
||||
name: `Coach ${coach.label}`,
|
||||
serviceClass: coach.serviceClass,
|
||||
seats: coach.seats.map((s) => ({ id: s.id, number: s.label, status: s.status, kind: s.kind })),
|
||||
coaches: assignments.map((a) => ({
|
||||
id: a.coach.id,
|
||||
assignmentId: a.id,
|
||||
name: `Coach ${a.coach.label}`,
|
||||
seatClass: a.coach.seatClass.name,
|
||||
positionNumber: a.positionNumber,
|
||||
seats: a.coach.seats.map((s) => ({ id: s.id, number: s.label, status: s.status, kind: s.kind })),
|
||||
})),
|
||||
};
|
||||
}
|
||||
@@ -28,9 +34,9 @@ export class SeatsService {
|
||||
const unavailable = seats.filter((s) => s.status === 'BOOKED' || s.status === 'BLOCKED' || (s.status === 'HELD' && s.heldUntil && s.heldUntil > new Date()));
|
||||
if (unavailable.length > 0) throw new ConflictException('One or more seats unavailable');
|
||||
await tx.seat.updateMany({ where: { id: { in: dto.seatIds } }, data: { status: 'HELD', heldUntil: expiresAt } });
|
||||
return tx.seatHold.create({ data: { tripId: dto.tripId, passengerId: dto.passengerId, seatIds: dto.seatIds, fareQuoteId: dto.fareQuoteId, expiresAt } });
|
||||
return tx.seatHold.create({ data: { scheduleId: dto.scheduleId, passengerId: dto.passengerId, seatIds: dto.seatIds, fareQuoteId: dto.fareQuoteId, expiresAt } });
|
||||
});
|
||||
return { id: hold.id, tripId: dto.tripId, seatIds: dto.seatIds, expiresAt };
|
||||
return { id: hold.id, scheduleId: dto.scheduleId, seatIds: dto.seatIds, expiresAt };
|
||||
}
|
||||
|
||||
async releaseHold(holdId: string) {
|
||||
@@ -44,10 +50,10 @@ export class SeatsService {
|
||||
async confirmSeats(seatIds: string[]) { await this.prisma.seat.updateMany({ where: { id: { in: seatIds } }, data: { status: 'BOOKED', heldUntil: null } }); }
|
||||
async releaseSeats(seatIds: string[]) { await this.prisma.seat.updateMany({ where: { id: { in: seatIds } }, data: { status: 'AVAILABLE', heldUntil: null } }); }
|
||||
|
||||
async autoAssignSeats(tripId: string, count: number, serviceClass: string, eligibility?: string): Promise<string[]> {
|
||||
async autoAssignSeats(scheduleId: string, count: number, seatClassName: string, eligibility?: string): Promise<string[]> {
|
||||
const seats = await this.prisma.seat.findMany({
|
||||
where: {
|
||||
coach: { tripId, serviceClass: serviceClass as any },
|
||||
coach: { seatClass: { name: seatClassName }, assignments: { some: { scheduleId } } },
|
||||
status: 'AVAILABLE',
|
||||
...(eligibility ? { eligibility } : {}),
|
||||
},
|
||||
@@ -81,18 +87,15 @@ export class SeatsService {
|
||||
return seats.slice(0, count);
|
||||
}
|
||||
|
||||
async exportSeatsCSV(tripId: string): Promise<string> {
|
||||
const coaches = await this.prisma.coach.findMany({
|
||||
where: { tripId },
|
||||
include: { seats: { orderBy: [{ row: 'asc' }, { col: 'asc' }] } },
|
||||
async exportSeatsCSV(scheduleId: string): Promise<string> {
|
||||
const assignments = await this.prisma.coachAssignment.findMany({
|
||||
where: { scheduleId },
|
||||
include: { coach: { include: { seats: { orderBy: [{ row: 'asc' }, { col: 'asc' }] } } } },
|
||||
});
|
||||
|
||||
const rows = ['coachId,coachLabel,row,col,label,kind,status,premiumFeeMinor,eligibility'];
|
||||
for (const coach of coaches) {
|
||||
for (const seat of coach.seats) {
|
||||
rows.push(
|
||||
`${coach.id},${coach.label},${seat.row},${seat.col},${seat.label},${seat.kind},${seat.status},${seat.premiumFeeMinor},${seat.eligibility || ''}`,
|
||||
);
|
||||
for (const a of assignments) {
|
||||
for (const seat of a.coach.seats) {
|
||||
rows.push(`${a.coach.id},${a.coach.label},${seat.row},${seat.col},${seat.label},${seat.kind},${seat.status},${seat.premiumFeeMinor},${seat.eligibility || ''}`);
|
||||
}
|
||||
}
|
||||
return rows.join('\n');
|
||||
@@ -123,7 +126,7 @@ export class SeatsService {
|
||||
return { valid, invalid, errors: errors.slice(0, 10) };
|
||||
}
|
||||
|
||||
async importSeatsCSV(tripId: string, csvContent: string, commit: boolean): Promise<{ imported: number; errors: string[] }> {
|
||||
async importSeatsCSV(scheduleId: string, csvContent: string, commit: boolean): Promise<{ imported: number; errors: string[] }> {
|
||||
const lines = csvContent.trim().split('\n').slice(1);
|
||||
const errors: string[] = [];
|
||||
let imported = 0;
|
||||
|
||||
Reference in New Issue
Block a user