mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
378 lines
14 KiB
TypeScript
378 lines
14 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
Delete,
|
|
Get,
|
|
Param,
|
|
ParseUUIDPipe,
|
|
Patch,
|
|
Post,
|
|
Query,
|
|
} from '@nestjs/common';
|
|
import { CurrentUser } from '@edr/api-common';
|
|
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
|
import type { AuthUserPayload } from '../../common/resolve-auth-user-id';
|
|
import { resolveAuthUserId } from '../../common/resolve-auth-user-id';
|
|
|
|
import { TrainSchedulingManage, TrainSchedulingView } from '../../common/booking-guards';
|
|
import { AssignBookingsDto } from './dto/assign-bookings.dto';
|
|
import { AssignUnassignedBookingDto } from './dto/assign-unassigned-booking.dto';
|
|
import { CreateContainerTrainScheduleDto } from './dto/create-container-train-schedule.dto';
|
|
import { GetEligibleBookingsDto } from './dto/get-eligible-bookings.dto';
|
|
import { GetEligibleBulkBookingsDto } from './dto/get-eligible-bulk-bookings.dto';
|
|
import { GetEligibleContainerBookingsDto } from './dto/get-eligible-container-bookings.dto';
|
|
import { PinWagonsDto } from './dto/pin-wagons.dto';
|
|
import { UpdateContainerItemDto } from './dto/update-container-item.dto';
|
|
import { PreviewBulkTrainScheduleDto } from './dto/preview-bulk-train-schedule.dto';
|
|
import { PreviewContainerTrainScheduleDto } from './dto/preview-container-train-schedule.dto';
|
|
import { PreviewTrainScheduleDto } from './dto/preview-train-schedule.dto';
|
|
import { RecordCheckpointDto } from './dto/record-checkpoint.dto';
|
|
import { AvailableLocomotivesQueryDto } from './dto/available-locomotives-query.dto';
|
|
import { BookableSchedulesQueryDto } from './dto/bookable-schedules-query.dto';
|
|
import { UpdateTrainSchedulingGlobalRulesDto } from './dto/update-train-scheduling-global-rules.dto';
|
|
import { TrainSchedulingService } from './train-scheduling.service';
|
|
import { BookingBatchService } from './booking-batch.service';
|
|
|
|
@ApiTags('train-scheduling')
|
|
@ApiBearerAuth()
|
|
@Controller('train-scheduling')
|
|
export class TrainSchedulingController {
|
|
constructor(
|
|
private readonly trainSchedulingService: TrainSchedulingService,
|
|
private readonly bookingBatchService: BookingBatchService,
|
|
) {}
|
|
|
|
@Get('global-rules')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'Get global train scheduling rules (singleton)' })
|
|
getGlobalRules() {
|
|
return this.trainSchedulingService.getTrainSchedulingGlobalRules();
|
|
}
|
|
|
|
@Patch('global-rules')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Update global train scheduling rules (singleton)' })
|
|
updateGlobalRules(@Body() dto: UpdateTrainSchedulingGlobalRulesDto) {
|
|
return this.trainSchedulingService.updateTrainSchedulingGlobalRules(dto);
|
|
}
|
|
|
|
@Get('eligible-bookings')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'List eligible bookings (container and/or bulk)' })
|
|
getEligibleBookings(@Query() query: GetEligibleBookingsDto) {
|
|
return this.trainSchedulingService.getEligibleBookings(query);
|
|
}
|
|
|
|
@Get('batch-board')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'Batch monitoring board: schedules with bookings grouped by state' })
|
|
getBatchBoard() {
|
|
return this.bookingBatchService.getBatchBoard();
|
|
}
|
|
|
|
@Get('batch-board/:scheduleId')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'Batch board detail for one schedule with EAT 3h windows' })
|
|
getBatchBoardDetail(@Param('scheduleId', ParseUUIDPipe) scheduleId: string) {
|
|
return this.bookingBatchService.getBatchBoardDetail(scheduleId);
|
|
}
|
|
|
|
@Get('available-locomotives')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({
|
|
summary: 'List AVAILABLE locomotives at the route origin yard',
|
|
})
|
|
getAvailableLocomotives(@Query() query: AvailableLocomotivesQueryDto) {
|
|
return this.trainSchedulingService.getAvailableLocomotivesForRoute(query.routeId);
|
|
}
|
|
|
|
@Get('bookable-schedules')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'OPEN same-route schedules a new booking can target' })
|
|
getBookableSchedules(@Query() query: BookableSchedulesQueryDto) {
|
|
return this.trainSchedulingService.getBookableSchedules(
|
|
query.originYardId,
|
|
query.destinationYardId,
|
|
);
|
|
}
|
|
|
|
@Get('container/eligible-bookings')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'List eligible container bookings' })
|
|
getEligibleContainerBookings(@Query() query: GetEligibleContainerBookingsDto) {
|
|
return this.trainSchedulingService.getEligibleContainerBookings(query);
|
|
}
|
|
|
|
@Get('bulk/eligible-bookings')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'List eligible bulk bookings' })
|
|
getEligibleBulkBookings(@Query() query: GetEligibleBulkBookingsDto) {
|
|
return this.trainSchedulingService.getEligibleBulkBookings(query);
|
|
}
|
|
|
|
@Post('preview')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'Preview a mixed-capable train schedule' })
|
|
previewTrainSchedule(@Body() dto: PreviewTrainScheduleDto) {
|
|
return this.trainSchedulingService.previewTrainSchedule(dto);
|
|
}
|
|
|
|
@Post('container/preview')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'Preview a container train schedule' })
|
|
previewContainerTrainSchedule(@Body() dto: PreviewContainerTrainScheduleDto) {
|
|
return this.trainSchedulingService.previewContainerTrainSchedule(dto);
|
|
}
|
|
|
|
@Post('bulk/preview')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'Preview a bulk train schedule' })
|
|
previewBulkTrainSchedule(@Body() dto: PreviewBulkTrainScheduleDto) {
|
|
return this.trainSchedulingService.previewBulkTrainSchedule(dto);
|
|
}
|
|
|
|
@Post('container/schedules')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Create a container train schedule' })
|
|
createContainerTrainSchedule(@Body() dto: CreateContainerTrainScheduleDto) {
|
|
return this.trainSchedulingService.createContainerTrainSchedule(dto);
|
|
}
|
|
|
|
@Post('bulk/schedules')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Create a bulk train schedule' })
|
|
createBulkTrainSchedule(@Body() dto: CreateContainerTrainScheduleDto) {
|
|
return this.trainSchedulingService.createContainerTrainSchedule(dto);
|
|
}
|
|
|
|
@Post('schedules/:id/assign-bookings')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Assign bookings to a train schedule (mixed-capable)' })
|
|
assignBookings(
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Body() dto: AssignBookingsDto,
|
|
) {
|
|
return this.trainSchedulingService.assignBookingsToSchedule(id, dto);
|
|
}
|
|
|
|
@Post('container/schedules/:id/assign-bookings')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Assign container bookings to a train schedule' })
|
|
assignContainerBookings(
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Body() dto: AssignBookingsDto,
|
|
) {
|
|
return this.trainSchedulingService.assignBookingsToSchedule(id, dto, 'CONTAINER');
|
|
}
|
|
|
|
@Post('bulk/schedules/:id/assign-bookings')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Assign bulk bookings to a train schedule' })
|
|
assignBulkBookings(
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Body() dto: AssignBookingsDto,
|
|
) {
|
|
return this.trainSchedulingService.assignBookingsToSchedule(id, dto, 'BULK');
|
|
}
|
|
|
|
@Delete('schedules/:id/bookings/:bookingId')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Unassign a booking from a train schedule' })
|
|
unassignBooking(
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Param('bookingId', ParseUUIDPipe) bookingId: string,
|
|
@CurrentUser() user: AuthUserPayload,
|
|
) {
|
|
return this.trainSchedulingService.unassignBooking(id, bookingId, resolveAuthUserId(user));
|
|
}
|
|
|
|
@Delete('schedules/:id/wagons/:trainSetWagonId')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Remove an empty wagon slot from a train' })
|
|
removeWagonSlot(
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Param('trainSetWagonId', ParseUUIDPipe) trainSetWagonId: string,
|
|
) {
|
|
return this.trainSchedulingService.removeTrainSetWagonSlot(id, trainSetWagonId);
|
|
}
|
|
|
|
@Patch('schedules/:id/container-items/:itemId')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Update a container number on a wagon slot' })
|
|
updateContainerItem(
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Param('itemId', ParseUUIDPipe) itemId: string,
|
|
@Body() dto: UpdateContainerItemDto,
|
|
) {
|
|
return this.trainSchedulingService.updateContainerItem(id, itemId, dto);
|
|
}
|
|
|
|
@Get('schedules/:id/unassigned-bookings')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'Get unassigned bookings for a schedule' })
|
|
getUnassignedBookings(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.trainSchedulingService.getUnassignedBookings(id);
|
|
}
|
|
|
|
@Post('schedules/:id/assign-unassigned-booking')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({
|
|
summary: 'Assign one linked unallocated booking to wagons (preserves existing assignments)',
|
|
})
|
|
assignUnassignedBooking(
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Body() dto: AssignUnassignedBookingDto,
|
|
) {
|
|
return this.trainSchedulingService.assignUnassignedBookingToWagons(id, dto.bookingId);
|
|
}
|
|
|
|
@Get('schedules/:id/composition-removals')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'Get removal log for a schedule' })
|
|
getCompositionRemovals(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.trainSchedulingService.getCompositionRemovals(id);
|
|
}
|
|
|
|
@Post('schedules/:id/pin-wagons')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Pin physical wagons to train set slots' })
|
|
pinWagons(@Param('id', ParseUUIDPipe) id: string, @Body() dto: PinWagonsDto) {
|
|
return this.trainSchedulingService.pinWagons(id, dto);
|
|
}
|
|
|
|
@Post('schedules/:id/finalize')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Finalize a draft train schedule' })
|
|
finalizeSchedule(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.trainSchedulingService.finalizeSchedule(id);
|
|
}
|
|
|
|
@Post('schedules/:id/dispatch')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Dispatch a scheduled train' })
|
|
dispatchSchedule(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.trainSchedulingService.dispatchSchedule(id);
|
|
}
|
|
|
|
// ---- batch / booking-window staff actions ----
|
|
|
|
@Post('schedules/:id/run-batch')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Manually run the batch fill for a schedule' })
|
|
async runBatch(@Param('id', ParseUUIDPipe) id: string) {
|
|
await this.bookingBatchService.fillSchedule(id);
|
|
return this.bookingBatchService.getBatchBoardDetail(id);
|
|
}
|
|
|
|
@Post('schedules/:id/run-allocation')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Run wagon-level allocation for all eligible linked bookings' })
|
|
async runAllocation(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.bookingBatchService.runWagonAllocation(id);
|
|
}
|
|
|
|
@Patch('schedules/:id/booking-window')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Open or close a schedule booking window' })
|
|
async setBookingWindow(
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Body('status') status: 'OPEN' | 'CLOSED',
|
|
) {
|
|
await this.trainSchedulingService.setBookingWindow(id, status === 'CLOSED' ? 'CLOSED' : 'OPEN');
|
|
return this.trainSchedulingService.getContainerTrainScheduleById(id);
|
|
}
|
|
|
|
@Post('bookings/:bookingId/mark-paid')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Staff: mark a reserved booking paid and allocate it now' })
|
|
async markBookingPaid(@Param('bookingId', ParseUUIDPipe) bookingId: string) {
|
|
await this.bookingBatchService.markPaid(bookingId);
|
|
return { ok: true };
|
|
}
|
|
|
|
@Post('bookings/:bookingId/expire')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Staff: expire a reservation and free its capacity' })
|
|
async expireBooking(@Param('bookingId', ParseUUIDPipe) bookingId: string) {
|
|
await this.bookingBatchService.expireReservation(bookingId);
|
|
return { ok: true };
|
|
}
|
|
|
|
@Post('bookings/:bookingId/move-schedule')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Re-point a booking to another OPEN same-route schedule' })
|
|
async moveBookingSchedule(
|
|
@Param('bookingId', ParseUUIDPipe) bookingId: string,
|
|
@Body('trainScheduleId', ParseUUIDPipe) trainScheduleId: string,
|
|
) {
|
|
await this.bookingBatchService.moveToSchedule(bookingId, trainScheduleId);
|
|
return { ok: true };
|
|
}
|
|
|
|
@Get('schedules/:id/checkpoints')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'Get the tracking corridor + logged checkpoints for a train' })
|
|
getScheduleCheckpoints(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.trainSchedulingService.getScheduleCheckpoints(id);
|
|
}
|
|
|
|
@Post('schedules/:id/checkpoints')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Log the train passing a station (final station triggers arrival)' })
|
|
recordCheckpoint(
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Body() dto: RecordCheckpointDto,
|
|
) {
|
|
return this.trainSchedulingService.recordCheckpoint(id, dto);
|
|
}
|
|
|
|
@Post('schedules/:id/arrive')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Mark a dispatched train arrived (move assets to destination yard, free assets)' })
|
|
arriveSchedule(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.trainSchedulingService.arriveSchedule(id);
|
|
}
|
|
|
|
@Get('container/schedules')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'List container train schedules' })
|
|
getContainerTrainSchedules() {
|
|
return this.trainSchedulingService.getContainerTrainSchedules();
|
|
}
|
|
|
|
@Get('bulk/schedules')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'List bulk train schedules' })
|
|
getBulkTrainSchedules() {
|
|
return this.trainSchedulingService.getContainerTrainSchedules();
|
|
}
|
|
|
|
@Get('container/schedules/:id')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'Get container train schedule detail' })
|
|
getContainerTrainScheduleById(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.trainSchedulingService.getContainerTrainScheduleById(id);
|
|
}
|
|
|
|
@Get('bulk/schedules/:id')
|
|
@TrainSchedulingView()
|
|
@ApiOperation({ summary: 'Get bulk train schedule detail' })
|
|
getBulkTrainScheduleById(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.trainSchedulingService.getContainerTrainScheduleById(id);
|
|
}
|
|
|
|
@Post('container/schedules/:id/cancel')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Cancel container train schedule' })
|
|
cancelTrainSchedule(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.trainSchedulingService.cancelTrainSchedule(id);
|
|
}
|
|
|
|
@Post('bulk/schedules/:id/cancel')
|
|
@TrainSchedulingManage()
|
|
@ApiOperation({ summary: 'Cancel bulk train schedule' })
|
|
cancelBulkTrainSchedule(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.trainSchedulingService.cancelTrainSchedule(id);
|
|
}
|
|
}
|