P1: drivers + trips(★state machine) + matching(Redis GEO) + tariff engine + maps + realtime + seed

- drivers: apply/approve/status/location (+Redis GEO presence)
- trips: request→accept→status transitions + trip_events audit + offers via socket
- matching: Redis GEO nearby drivers
- tariff: JSON-rule engine (time_or_distance/window/rounding) + quote endpoint
- maps: straight-line routing seam (انطلق later)
- realtime: Socket.IO gateway (JWT auth, tenant rooms)
- seed: demo tenant siro + default tariff on boot
- migration InitP1 (drivers/tariffs/trips/trip_events)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 16:41:58 +03:00
co-authored by Claude Opus 4.8
parent 4397b07d2f
commit 00870a8cc3
26 changed files with 1358 additions and 1 deletions
@@ -0,0 +1,87 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
/**
* جداول P1: drivers, tariffs, trips, trip_events (كلها ببادئة tripz_).
*/
export class InitP11721300000000 implements MigrationInterface {
public async up(q: QueryRunner): Promise<void> {
// drivers
await q.query(`
CREATE TABLE IF NOT EXISTS tripz_drivers (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
tenant_id uuid NOT NULL,
user_id uuid NOT NULL,
service_class varchar NOT NULL DEFAULT 'economy',
vehicle_make varchar, vehicle_model varchar,
vehicle_plate varchar, vehicle_color varchar,
docs jsonb NOT NULL DEFAULT '{}',
verification_status varchar NOT NULL DEFAULT 'pending',
is_online boolean NOT NULL DEFAULT false,
rating numeric(3,2) NOT NULL DEFAULT 5,
last_lat double precision, last_lng double precision,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
)`);
await q.query(`CREATE UNIQUE INDEX IF NOT EXISTS "UQ_tripz_drivers_tenant_user" ON tripz_drivers (tenant_id, user_id)`);
// tariffs
await q.query(`
CREATE TABLE IF NOT EXISTS tripz_tariffs (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
tenant_id uuid NOT NULL,
city varchar NOT NULL,
service_class varchar NOT NULL,
definition jsonb NOT NULL,
version int NOT NULL DEFAULT 1,
active boolean NOT NULL DEFAULT true,
created_at timestamptz NOT NULL DEFAULT now()
)`);
await q.query(`CREATE INDEX IF NOT EXISTS "IDX_tripz_tariffs_lookup" ON tripz_tariffs (tenant_id, city, service_class, active)`);
// trips
await q.query(`
CREATE TABLE IF NOT EXISTS tripz_trips (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
tenant_id uuid NOT NULL,
rider_id uuid NOT NULL,
driver_id uuid,
service_class varchar NOT NULL DEFAULT 'economy',
city varchar,
origin_lat double precision NOT NULL, origin_lng double precision NOT NULL,
dest_lat double precision NOT NULL, dest_lng double precision NOT NULL,
status varchar NOT NULL DEFAULT 'searching',
distance_km numeric(8,3), duration_min numeric(8,1),
tariff_id uuid, tariff_version int,
quoted_fare numeric(12,3), final_fare numeric(12,3),
currency varchar,
payment_method varchar NOT NULL DEFAULT 'cash',
requested_at timestamptz NOT NULL DEFAULT now(),
assigned_at timestamptz, completed_at timestamptz,
updated_at timestamptz NOT NULL DEFAULT now()
)`);
await q.query(`CREATE INDEX IF NOT EXISTS "IDX_tripz_trips_status" ON tripz_trips (tenant_id, status)`);
await q.query(`CREATE INDEX IF NOT EXISTS "IDX_tripz_trips_rider" ON tripz_trips (tenant_id, rider_id)`);
await q.query(`CREATE INDEX IF NOT EXISTS "IDX_tripz_trips_driver" ON tripz_trips (tenant_id, driver_id)`);
// trip_events
await q.query(`
CREATE TABLE IF NOT EXISTS tripz_trip_events (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
tenant_id uuid NOT NULL,
trip_id uuid NOT NULL,
from_status varchar,
to_status varchar NOT NULL,
source varchar NOT NULL DEFAULT 'system',
payload jsonb NOT NULL DEFAULT '{}',
created_at timestamptz NOT NULL DEFAULT now()
)`);
await q.query(`CREATE INDEX IF NOT EXISTS "IDX_tripz_trip_events_trip" ON tripz_trip_events (tenant_id, trip_id)`);
}
public async down(q: QueryRunner): Promise<void> {
await q.query(`DROP TABLE IF EXISTS tripz_trip_events`);
await q.query(`DROP TABLE IF EXISTS tripz_trips`);
await q.query(`DROP TABLE IF EXISTS tripz_tariffs`);
await q.query(`DROP TABLE IF EXISTS tripz_drivers`);
}
}