feat(backend): tracking simulator, RBAC status, feedback & addresses
- Add automatic route simulator (30s tick) that advances trucks and dispatches notifications without needing client-driven pull - Add GET /api/tracking/status protected by JWT for tunnel-view (each user only sees their own route + own inbox) - Add POST /api/tracking/reset-demo to wipe in-memory state without restarting the server (useful for repeated demos) - Add feedback module (POST /api/feedback, GET /api/feedback/me) with 4 feedback types and optional rating - Add addresses module: GET /colonias, GET/PUT /me with colonia validation against the catalog (rejects unknown colonias) - Add in-memory repos for route-state and notification inbox - Auto-register new users in the service mock with default route on register/login so they receive notifications immediately
This commit is contained in:
72
backend/src/data/simulation/route-simulator.ts
Normal file
72
backend/src/data/simulation/route-simulator.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* route-simulator.ts
|
||||
* Simulador automático: cada N segundos avanza un positionId en cada ruta
|
||||
* y dispara el ProcessGpsUpdateUseCase. Reemplaza al "pull to refresh" del
|
||||
* frontend para que el flujo sea más realista en la demo.
|
||||
*
|
||||
* Solo simula RUTA-01 por defecto para enfocar la demo, pero se puede
|
||||
* activar para todas las rutas si se desea.
|
||||
*/
|
||||
|
||||
import { routes } from "../mocks/routes.mock.js";
|
||||
import type { ProcessGpsUpdateUseCase } from "../../domain/use-cases/notifications/process-gps-update.use-case.js";
|
||||
|
||||
const TICK_INTERVAL_MS = 30_000; // 30 segundos
|
||||
const SIMULATED_ROUTE_IDS = ["RUTA-01"];
|
||||
|
||||
export class RouteSimulator {
|
||||
private timer: NodeJS.Timeout | null = null;
|
||||
private positionIndexByRoute = new Map<string, number>();
|
||||
|
||||
constructor(private readonly processGpsUpdate: ProcessGpsUpdateUseCase) {}
|
||||
|
||||
start() {
|
||||
if (this.timer) return;
|
||||
|
||||
// Inicializa cada ruta en positionId=1 inmediatamente
|
||||
void this.tick();
|
||||
|
||||
this.timer = setInterval(() => {
|
||||
void this.tick();
|
||||
}, TICK_INTERVAL_MS);
|
||||
|
||||
console.log(`RouteSimulator started — tick every ${TICK_INTERVAL_MS / 1000}s`);
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async tick() {
|
||||
for (const routeId of SIMULATED_ROUTE_IDS) {
|
||||
const route = routes.find((r) => r.routeId === routeId);
|
||||
if (!route) continue;
|
||||
|
||||
const index = this.positionIndexByRoute.get(routeId) ?? 0;
|
||||
const position = route.positions[index];
|
||||
if (!position) continue;
|
||||
|
||||
try {
|
||||
await this.processGpsUpdate.execute({
|
||||
truckId: String(route.truckId),
|
||||
routeId: route.routeId,
|
||||
lat: position.lat,
|
||||
lng: position.lng,
|
||||
speed: position.speed,
|
||||
status: route.status,
|
||||
positionId: position.positionId,
|
||||
timestamp: position.timestamp,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`Simulator tick failed for ${routeId}:`, err);
|
||||
}
|
||||
|
||||
// Avanza al siguiente; vuelve a empezar al terminar el array
|
||||
const nextIndex = (index + 1) % route.positions.length;
|
||||
this.positionIndexByRoute.set(routeId, nextIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user