feat: модуль GPS-мониторинга (карта Яндекс.Карт, Traccar, геозоны, маршруты, группы)
This commit is contained in:
106
migrations/0069_gps_tracking.sql
Normal file
106
migrations/0069_gps_tracking.sql
Normal file
@@ -0,0 +1,106 @@
|
||||
-- GPS-мониторинг: объекты (транспорт/люди), треки, геозоны, события, группы, подписки.
|
||||
-- Интеграция с Traccar (forward позиций на /api/gps/ingest) и приём геолокации из PWA.
|
||||
|
||||
-- Объекты мониторинга (машины, люди с трекерами)
|
||||
CREATE TABLE IF NOT EXISTS gps_assets (
|
||||
id SERIAL PRIMARY KEY,
|
||||
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL DEFAULT 'vehicle',
|
||||
user_id INTEGER REFERENCES users(id),
|
||||
external_id TEXT,
|
||||
color TEXT DEFAULT '#3b82f6',
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
is_online BOOLEAN DEFAULT FALSE,
|
||||
last_lat DOUBLE PRECISION,
|
||||
last_lng DOUBLE PRECISION,
|
||||
last_speed DOUBLE PRECISION,
|
||||
last_course DOUBLE PRECISION,
|
||||
last_seen_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS gps_assets_org_idx ON gps_assets(organization_id);
|
||||
-- IMEI/идентификатор трекера уникален в рамках организации (только для заполненных)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS gps_assets_org_external_idx
|
||||
ON gps_assets(organization_id, external_id) WHERE external_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS gps_assets_user_idx ON gps_assets(user_id);
|
||||
|
||||
-- История позиций (треки)
|
||||
CREATE TABLE IF NOT EXISTS gps_positions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
asset_id INTEGER NOT NULL REFERENCES gps_assets(id) ON DELETE CASCADE,
|
||||
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
lat DOUBLE PRECISION NOT NULL,
|
||||
lng DOUBLE PRECISION NOT NULL,
|
||||
speed DOUBLE PRECISION,
|
||||
course DOUBLE PRECISION,
|
||||
accuracy DOUBLE PRECISION,
|
||||
recorded_at TIMESTAMP NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS gps_positions_asset_recorded_idx ON gps_positions(asset_id, recorded_at);
|
||||
CREATE INDEX IF NOT EXISTS gps_positions_org_recorded_idx ON gps_positions(organization_id, recorded_at);
|
||||
|
||||
-- Геозоны (полигоны)
|
||||
CREATE TABLE IF NOT EXISTS gps_geozones (
|
||||
id SERIAL PRIMARY KEY,
|
||||
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
polygon JSONB NOT NULL,
|
||||
color TEXT DEFAULT '#f59e0b',
|
||||
notify_enter BOOLEAN DEFAULT TRUE,
|
||||
notify_exit BOOLEAN DEFAULT TRUE,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS gps_geozones_org_idx ON gps_geozones(organization_id);
|
||||
|
||||
-- События входа/выхода из геозон
|
||||
CREATE TABLE IF NOT EXISTS gps_geozone_events (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
geozone_id INTEGER NOT NULL REFERENCES gps_geozones(id) ON DELETE CASCADE,
|
||||
asset_id INTEGER NOT NULL REFERENCES gps_assets(id) ON DELETE CASCADE,
|
||||
event TEXT NOT NULL,
|
||||
lat DOUBLE PRECISION,
|
||||
lng DOUBLE PRECISION,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS gps_geozone_events_asset_idx ON gps_geozone_events(asset_id, created_at);
|
||||
CREATE INDEX IF NOT EXISTS gps_geozone_events_org_idx ON gps_geozone_events(organization_id, created_at);
|
||||
|
||||
-- Текущее состояние «объект внутри геозоны» (для детекта входа/выхода)
|
||||
CREATE TABLE IF NOT EXISTS gps_geozone_states (
|
||||
asset_id INTEGER NOT NULL REFERENCES gps_assets(id) ON DELETE CASCADE,
|
||||
geozone_id INTEGER NOT NULL REFERENCES gps_geozones(id) ON DELETE CASCADE,
|
||||
inside BOOLEAN NOT NULL,
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
PRIMARY KEY (asset_id, geozone_id)
|
||||
);
|
||||
|
||||
-- Группы объектов (для фильтров на карте)
|
||||
CREATE TABLE IF NOT EXISTS gps_groups (
|
||||
id SERIAL PRIMARY KEY,
|
||||
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS gps_groups_org_idx ON gps_groups(organization_id);
|
||||
|
||||
-- Связь групп и объектов (многие-ко-многим)
|
||||
CREATE TABLE IF NOT EXISTS gps_group_assets (
|
||||
group_id INTEGER NOT NULL REFERENCES gps_groups(id) ON DELETE CASCADE,
|
||||
asset_id INTEGER NOT NULL REFERENCES gps_assets(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (group_id, asset_id)
|
||||
);
|
||||
|
||||
-- Подписчики на события объекта (уведомления о входе/выходе/офлайне)
|
||||
CREATE TABLE IF NOT EXISTS gps_asset_subscribers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
asset_id INTEGER NOT NULL REFERENCES gps_assets(id) ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
on_enter BOOLEAN DEFAULT TRUE,
|
||||
on_exit BOOLEAN DEFAULT TRUE,
|
||||
on_offline BOOLEAN DEFAULT FALSE,
|
||||
UNIQUE (asset_id, user_id)
|
||||
);
|
||||
Reference in New Issue
Block a user