-- 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) );