Build issue resolution and production checklist updates

This commit is contained in:
Stephanos A
2026-07-05 15:11:54 +03:00
parent b6dbfe43e3
commit 9e77ca7865
13 changed files with 20 additions and 578 deletions

View File

@@ -1,238 +0,0 @@
/**
* SEGMENT-BASED SEAT RESERVATION EXAMPLE
*
* Demonstrates the complete flow for booking Addis Ababa → Dire Dawa
* on the Addis Ababa → Djibouti route with segment-based seat management.
*
* Route: Addis Ababa (seq:1) → Adama (seq:2) → Awash (seq:3) → Dire Dawa (seq:4) → Aysha (seq:5) → Djibouti (seq:6)
* Booking: Addis Ababa → Dire Dawa (segments: 1→2, 2→3, 3→4)
*/
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function exampleBookingFlow() {
console.log('=== SEGMENT-BASED BOOKING FLOW ===\n');
const scheduleId = 'schedule_add_dji_001';
const passengerId = 'passenger_kelemu';
const seatIds = ['seat_coach_a_1a', 'seat_coach_a_1b'];
const originStationId = 'st_ADD';
const destinationStationId = 'st_DRE';
try {
console.log('1. Checking seat availability...');
const segments = await getJourneySegments(scheduleId, originStationId, destinationStationId);
console.log('Journey segments:', segments.map(s => `${s.fromName}${s.toName}`));
console.log('\n2. Holding seats...');
const holdResult = await holdSeatsTransaction(scheduleId, seatIds, passengerId, originStationId, destinationStationId);
console.log('Hold created:', holdResult);
console.log('\n3. Processing payment...');
await new Promise(resolve => setTimeout(resolve, 5000));
console.log('\n4. Confirming booking...');
const bookingId = 'booking_' + Date.now();
const confirmResult = await confirmBookingTransaction(holdResult.holdId, bookingId, segments);
console.log('Booking confirmed:', confirmResult);
console.log('\n5. Simulating trip progress...');
await simulateTripProgress(scheduleId, segments);
} catch (error) {
console.error('Booking flow error:', error);
}
}
async function getJourneySegments(scheduleId: string, originStationId: string, destinationStationId: string) {
const stopTimes = await prisma.tripStopTime.findMany({
where: { scheduleId },
include: { station: true },
orderBy: { sequence: 'asc' },
});
const originStop = stopTimes.find(st => st.stationId === originStationId);
const destinationStop = stopTimes.find(st => st.stationId === destinationStationId);
if (!originStop || !destinationStop || originStop.sequence >= destinationStop.sequence) {
throw new Error('Invalid origin/destination');
}
const segments = [];
for (let i = originStop.sequence; i < destinationStop.sequence; i++) {
const fromStop = stopTimes.find(st => st.sequence === i);
const toStop = stopTimes.find(st => st.sequence === i + 1);
if (fromStop && toStop) {
segments.push({
fromStationId: fromStop.stationId,
toStationId: toStop.stationId,
fromSequence: fromStop.sequence,
toSequence: toStop.sequence,
fromName: fromStop.station.name,
toName: toStop.station.name,
});
}
}
return segments;
}
async function holdSeatsTransaction(scheduleId: string, seatIds: string[], passengerId: string, originStationId: string, destinationStationId: string) {
return prisma.$transaction(async (tx) => {
console.log(' → Starting seat hold transaction...');
const seats = await tx.seat.findMany({ where: { id: { in: seatIds } }, include: { coach: true } });
if (seats.length !== seatIds.length) throw new Error('Some seats not found');
for (const seat of seats) {
if (seat.status !== 'AVAILABLE') {
throw new Error(`Seat ${seat.seatNumber} is not available (status: ${seat.status})`);
}
}
const expiresAt = new Date(Date.now() + 10 * 60 * 1000);
const seatHold = await tx.seatHold.create({
data: { scheduleId, seatIds, passengerId, expiresAt },
});
await tx.seat.updateMany({ where: { id: { in: seatIds } }, data: { status: 'HELD', heldUntil: expiresAt } });
console.log(' → Seats held successfully');
return { holdId: seatHold.id, expiresAt, seats: seatIds.length };
});
}
async function confirmBookingTransaction(holdId: string, bookingId: string, segments: any[]) {
return prisma.$transaction(async (tx) => {
console.log(' → Starting booking confirmation transaction...');
const hold = await tx.seatHold.findUnique({ where: { id: holdId } });
if (!hold || hold.expiresAt < new Date()) throw new Error('Hold expired or not found');
const booking = await tx.booking.create({
data: {
id: bookingId,
bookingRef: 'BK' + Date.now().toString().slice(-6),
passengerId: hold.passengerId,
scheduleId: hold.scheduleId,
status: 'CONFIRMED',
totalMinor: 45000,
currency: 'ETB',
},
});
const journey = await tx.journey.create({
data: { passengerId: hold.passengerId, status: 'CONFIRMED', totalMinor: 45000, currency: 'ETB' },
});
for (const seatId of hold.seatIds) {
for (let i = 0; i < segments.length; i++) {
await tx.journeySegment.create({
data: {
journeyId: journey.id,
scheduleId: hold.scheduleId,
segmentOrder: i + 1,
seatId,
departureStationId: segments[i].fromStationId,
arrivalStationId: segments[i].toStationId,
},
});
}
}
for (const seatId of hold.seatIds) {
await tx.bookingSeat.create({ data: { bookingId, seatId, passengerName: 'Kelemu Ketsela' } });
}
await tx.seat.updateMany({ where: { id: { in: hold.seatIds } }, data: { status: 'BOOKED', heldUntil: null } });
await tx.seatHold.delete({ where: { id: holdId } });
console.log(' → Booking confirmed successfully');
return { bookingId, bookingRef: booking.bookingRef, confirmedSeats: hold.seatIds.length, segments: segments.length };
});
}
async function simulateTripProgress(scheduleId: string, bookedSegments: any[]) {
console.log(' → Simulating trip progress...');
for (const segment of bookedSegments) {
console.log(` → Train approaching ${segment.toName}...`);
await prisma.tripLiveStatus.upsert({
where: { scheduleId },
update: { currentLocationLabel: segment.toName, progressPercent: Math.round((segment.toSequence / 4) * 100) },
create: {
scheduleId,
state: 'EN_ROUTE',
currentLocationLabel: segment.toName,
progressPercent: Math.round((segment.toSequence / 4) * 100),
delayMinutes: 0,
},
});
if (segment.toName === 'Dire Dawa') {
console.log(' → Passengers reached destination, releasing seats...');
await releaseSeatsAtStation(scheduleId, segment.toStationId);
}
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
async function releaseSeatsAtStation(scheduleId: string, stationId: string) {
return prisma.$transaction(async (tx) => {
const completedSegments = await tx.journeySegment.findMany({
where: { scheduleId, arrivalStationId: stationId },
include: { journey: { include: { journeySegments: { where: { scheduleId } } } } },
});
const seatsToRelease: string[] = [];
for (const segment of completedSegments) {
const passengerSegments = segment.journey.journeySegments.filter((js: any) => js.seatId === segment.seatId);
const maxOrder = Math.max(...passengerSegments.map((js: any) => js.segmentOrder));
if (segment.segmentOrder === maxOrder) seatsToRelease.push(segment.seatId!);
}
if (seatsToRelease.length > 0) {
await tx.seat.updateMany({ where: { id: { in: seatsToRelease } }, data: { status: 'AVAILABLE' } });
console.log(` → Released ${seatsToRelease.length} seats at station`);
}
return seatsToRelease;
});
}
async function checkOverlappingReservations(tx: any, scheduleId: string, seatId: string, segments: any[]) {
const activeHolds = await tx.seatHold.findMany({
where: { scheduleId, seatIds: { has: seatId }, expiresAt: { gt: new Date() } },
});
const activeBookings = await tx.journeySegment.findMany({
where: {
scheduleId,
seatId,
journey: { status: { in: ['PENDING_PAYMENT', 'CONFIRMED'] } },
},
});
return [...activeHolds, ...activeBookings];
}
if (require.main === module) {
exampleBookingFlow()
.then(() => console.log('\n=== EXAMPLES COMPLETED ==='))
.catch(console.error)
.finally(() => prisma.$disconnect());
}
export {
exampleBookingFlow,
getJourneySegments,
holdSeatsTransaction,
confirmBookingTransaction,
simulateTripProgress,
releaseSeatsAtStation,
checkOverlappingReservations,
};