-- Enable PostGIS extension CREATE EXTENSION IF NOT EXISTS postgis; -- API Keys table CREATE TABLE api_keys ( id SERIAL PRIMARY KEY, key VARCHAR(64) UNIQUE NOT NULL, app_name VARCHAR(100) NOT NULL, rate_limit INTEGER DEFAULT 1000, is_active BOOLEAN DEFAULT true, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_used_at TIMESTAMP ); -- Routes table (маршруты) CREATE TABLE routes ( id SERIAL PRIMARY KEY, route_number VARCHAR(10) NOT NULL, name VARCHAR(200) NOT NULL, type VARCHAR(20) NOT NULL CHECK (type IN ('bus', 'minibus', 'trolleybus', 'tram')), color VARCHAR(7) DEFAULT '#0066CC', is_active BOOLEAN DEFAULT true, description TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Stops table (остановки) with geospatial support CREATE TABLE stops ( id SERIAL PRIMARY KEY, name VARCHAR(200) NOT NULL, location GEOGRAPHY(Point, 4326) NOT NULL, address VARCHAR(300), description TEXT, is_active BOOLEAN DEFAULT true, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Create spatial index for stops CREATE INDEX idx_stops_location ON stops USING GIST(location); -- Route stops junction table (связь маршрутов и остановок) CREATE TABLE route_stops ( id SERIAL PRIMARY KEY, route_id INTEGER NOT NULL REFERENCES routes(id) ON DELETE CASCADE, stop_id INTEGER NOT NULL REFERENCES stops(id) ON DELETE CASCADE, sequence INTEGER NOT NULL, time_offset_minutes INTEGER NOT NULL DEFAULT 0, UNIQUE(route_id, stop_id, sequence) ); -- Schedules table (расписание) CREATE TABLE schedules ( id SERIAL PRIMARY KEY, route_id INTEGER NOT NULL REFERENCES routes(id) ON DELETE CASCADE, day_type VARCHAR(20) NOT NULL CHECK (day_type IN ('weekday', 'saturday', 'sunday', 'holiday')), departure_times TIME[] NOT NULL, valid_from DATE, valid_until DATE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Vehicles table (транспортные средства) CREATE TABLE vehicles ( id SERIAL PRIMARY KEY, vehicle_number VARCHAR(20) NOT NULL, registration VARCHAR(20), type VARCHAR(20) NOT NULL, capacity INTEGER, route_id INTEGER REFERENCES routes(id) ON DELETE SET NULL, is_active BOOLEAN DEFAULT true, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Historical delays table (статистика задержек) CREATE TABLE historical_delays ( id SERIAL PRIMARY KEY, route_id INTEGER NOT NULL REFERENCES routes(id) ON DELETE CASCADE, stop_id INTEGER NOT NULL REFERENCES stops(id) ON DELETE CASCADE, hour_of_day INTEGER NOT NULL CHECK (hour_of_day BETWEEN 0 AND 23), day_type VARCHAR(20) NOT NULL, avg_delay_minutes INTEGER NOT NULL, sample_count INTEGER DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(route_id, stop_id, hour_of_day, day_type) ); -- Alerts table (уведомления и оповещения) CREATE TABLE alerts ( id SERIAL PRIMARY KEY, route_id INTEGER REFERENCES routes(id) ON DELETE CASCADE, alert_type VARCHAR(20) NOT NULL CHECK (alert_type IN ('delay', 'cancellation', 'detour', 'info')), title VARCHAR(200) NOT NULL, message TEXT NOT NULL, start_time TIMESTAMP NOT NULL, end_time TIMESTAMP, is_active BOOLEAN DEFAULT true, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Sync log table (для offline синхронизации) CREATE TABLE sync_log ( id SERIAL PRIMARY KEY, table_name VARCHAR(50) NOT NULL, record_id INTEGER NOT NULL, action VARCHAR(20) NOT NULL CHECK (action IN ('insert', 'update', 'delete')), timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Create indexes CREATE INDEX idx_routes_number ON routes(route_number); CREATE INDEX idx_routes_active ON routes(is_active); CREATE INDEX idx_stops_name ON stops(name); CREATE INDEX idx_route_stops_route ON route_stops(route_id); CREATE INDEX idx_route_stops_stop ON route_stops(stop_id); CREATE INDEX idx_schedules_route ON schedules(route_id); CREATE INDEX idx_vehicles_route ON vehicles(route_id); CREATE INDEX idx_alerts_active ON alerts(is_active, start_time, end_time); CREATE INDEX idx_sync_log_timestamp ON sync_log(timestamp); -- Create function to update updated_at timestamp CREATE OR REPLACE FUNCTION update_updated_at_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = CURRENT_TIMESTAMP; RETURN NEW; END; $$ language 'plpgsql'; -- Create triggers for updated_at CREATE TRIGGER update_routes_updated_at BEFORE UPDATE ON routes FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); CREATE TRIGGER update_stops_updated_at BEFORE UPDATE ON stops FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); CREATE TRIGGER update_schedules_updated_at BEFORE UPDATE ON schedules FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); CREATE TRIGGER update_vehicles_updated_at BEFORE UPDATE ON vehicles FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- Create function to log changes for sync CREATE OR REPLACE FUNCTION log_sync_changes() RETURNS TRIGGER AS $$ BEGIN IF (TG_OP = 'DELETE') THEN INSERT INTO sync_log (table_name, record_id, action) VALUES (TG_TABLE_NAME, OLD.id, 'delete'); RETURN OLD; ELSIF (TG_OP = 'UPDATE') THEN INSERT INTO sync_log (table_name, record_id, action) VALUES (TG_TABLE_NAME, NEW.id, 'update'); RETURN NEW; ELSIF (TG_OP = 'INSERT') THEN INSERT INTO sync_log (table_name, record_id, action) VALUES (TG_TABLE_NAME, NEW.id, 'insert'); RETURN NEW; END IF; RETURN NULL; END; $$ LANGUAGE plpgsql; -- Create sync triggers for main tables CREATE TRIGGER sync_routes AFTER INSERT OR UPDATE OR DELETE ON routes FOR EACH ROW EXECUTE FUNCTION log_sync_changes(); CREATE TRIGGER sync_stops AFTER INSERT OR UPDATE OR DELETE ON stops FOR EACH ROW EXECUTE FUNCTION log_sync_changes(); CREATE TRIGGER sync_route_stops AFTER INSERT OR UPDATE OR DELETE ON route_stops FOR EACH ROW EXECUTE FUNCTION log_sync_changes(); CREATE TRIGGER sync_schedules AFTER INSERT OR UPDATE OR DELETE ON schedules FOR EACH ROW EXECUTE FUNCTION log_sync_changes(); -- Insert sample API key for development INSERT INTO api_keys (key, app_name, rate_limit) VALUES ('dev_key_12345678', 'Development App', 10000); -- Grant permissions GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO transit_admin; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO transit_admin;