feat: podpora simulace objednávek z Bolt Food ve vývoji
This commit is contained in:
13 files changed
+662
-4
No files matched your search
@@ -0,0 +1,134 @@
|
||||
import crypto from 'crypto';
|
||||
|
||||
/**
|
||||
* Vývojový simulátor sledování objednávek Bolt Food.
|
||||
*
|
||||
* Drží in-memory registr „simulovaných" objednávek klíčovaný tokenem. Funkce
|
||||
* pollBoltOrder v boltTracking.ts se na začátku podívá, zda je token simulovaný,
|
||||
* a pokud ano, vrátí vyfabrikovaný stav místo dotazu na reálné Bolt API.
|
||||
*
|
||||
* Registr se plní výhradně přes dev endpointy (gated requireDevMode), takže
|
||||
* v produkci zůstává prázdný a chování pollBoltOrder se nijak nemění.
|
||||
*
|
||||
* Postup stavů je řízen ručně (krokováním), bez časové osy — viz advance/setState.
|
||||
*/
|
||||
|
||||
/** Jeden krok simulace — odpovídá tomu, co vrací Bolt API a co čte BoltOrderProgress. */
|
||||
export interface SimStep {
|
||||
order_state: string;
|
||||
courier_state?: string;
|
||||
etaSeconds?: number;
|
||||
}
|
||||
|
||||
/** Tvar objednávky, který vrací pollBoltOrder (shodný s interface BoltOrder). */
|
||||
export interface SimulatedBoltOrder {
|
||||
order_id: number;
|
||||
order_state: string;
|
||||
expected_time_to_client_in_seconds?: number;
|
||||
courier?: { state?: string } | null;
|
||||
}
|
||||
|
||||
interface Simulation {
|
||||
groupId: string;
|
||||
token: string;
|
||||
orderId: number;
|
||||
steps: SimStep[];
|
||||
index: number;
|
||||
/** Jednorázové ruční přepsání aktuálního stavu (má přednost před steps[index]). */
|
||||
override?: SimStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Výchozí scénář „happy path". Stavy a stavy kurýra odpovídají mapování ve
|
||||
* client/src/components/BoltOrderProgress.tsx (Přijato → Příprava → Na cestě → Doručeno).
|
||||
*/
|
||||
export const DEFAULT_SCENARIO: SimStep[] = [
|
||||
{ order_state: 'accepted', etaSeconds: 1800 },
|
||||
{ order_state: 'preparing', etaSeconds: 1500 },
|
||||
{ order_state: 'waiting_delivery', courier_state: 'arrived_to_provider', etaSeconds: 900 },
|
||||
{ order_state: 'in_delivery', courier_state: 'heading_to_client', etaSeconds: 300 },
|
||||
{ order_state: 'delivered', courier_state: 'delivered', etaSeconds: 0 },
|
||||
];
|
||||
|
||||
const byToken = new Map<string, Simulation>();
|
||||
|
||||
function findByGroup(groupId: string): Simulation | undefined {
|
||||
for (const sim of byToken.values()) {
|
||||
if (sim.groupId === groupId) return sim;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Sestaví Bolt objednávku z aktuálního kroku simulace. */
|
||||
function buildOrder(sim: Simulation): SimulatedBoltOrder {
|
||||
const step = sim.override ?? sim.steps[sim.index];
|
||||
return {
|
||||
order_id: sim.orderId,
|
||||
order_state: step.order_state,
|
||||
expected_time_to_client_in_seconds: step.etaSeconds,
|
||||
courier: step.courier_state ? { state: step.courier_state } : null,
|
||||
};
|
||||
}
|
||||
|
||||
/** Je token registrovaný v simulátoru? */
|
||||
export function isBoltSimulated(token: string): boolean {
|
||||
return byToken.has(token);
|
||||
}
|
||||
|
||||
/** Vrátí simulovaný stav objednávky pro daný token (nebo null, pokud není simulovaný). */
|
||||
export function getSimulatedBoltOrder(token: string): SimulatedBoltOrder | null {
|
||||
const sim = byToken.get(token);
|
||||
return sim ? buildOrder(sim) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spustí novou simulaci pro skupinu. Pokud už pro skupinu simulace běží, nahradí ji.
|
||||
* Vrátí vygenerovaný 64-hex token (validní pro extractBoltToken).
|
||||
*/
|
||||
export function startBoltSimulation(groupId: string, steps: SimStep[] = DEFAULT_SCENARIO): string {
|
||||
stopBoltSimulationByGroup(groupId);
|
||||
const token = crypto.randomBytes(32).toString('hex');
|
||||
byToken.set(token, {
|
||||
groupId,
|
||||
token,
|
||||
orderId: crypto.randomInt(100000, 999999),
|
||||
steps: steps.length ? steps : DEFAULT_SCENARIO,
|
||||
index: 0,
|
||||
});
|
||||
return token;
|
||||
}
|
||||
|
||||
/** Posune simulaci skupiny na další krok (a zruší případný ruční override). Vrátí aktuální krok. */
|
||||
export function advanceBoltSimulation(groupId: string): SimStep {
|
||||
const sim = findByGroup(groupId);
|
||||
if (!sim) throw new Error('Pro skupinu neběží žádná simulace Bolt');
|
||||
sim.override = undefined;
|
||||
sim.index = Math.min(sim.index + 1, sim.steps.length - 1);
|
||||
return sim.steps[sim.index];
|
||||
}
|
||||
|
||||
/** Nastaví konkrétní stav simulace skupiny (ruční override — užitečné pro edge-case stavy). */
|
||||
export function setBoltSimulationStep(groupId: string, step: SimStep): SimStep {
|
||||
const sim = findByGroup(groupId);
|
||||
if (!sim) throw new Error('Pro skupinu neběží žádná simulace Bolt');
|
||||
sim.override = step;
|
||||
return step;
|
||||
}
|
||||
|
||||
/** Ukončí simulaci pro skupinu. */
|
||||
export function stopBoltSimulationByGroup(groupId: string): void {
|
||||
const sim = findByGroup(groupId);
|
||||
if (sim) byToken.delete(sim.token);
|
||||
}
|
||||
|
||||
/** Vrátí přehled simulace skupiny (pro stavové zobrazení v UI). */
|
||||
export function getBoltSimulation(groupId: string): { token: string; index: number; total: number; current: SimStep } | null {
|
||||
const sim = findByGroup(groupId);
|
||||
if (!sim) return null;
|
||||
return {
|
||||
token: sim.token,
|
||||
index: sim.index,
|
||||
total: sim.steps.length,
|
||||
current: sim.override ?? sim.steps[sim.index],
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user