Files
hackathon-biocode-17bf223ba…/init/01_schema.sql

193 lines
7.9 KiB
PL/PgSQL

-- ================================================================
-- SCHEMA - App Recolección Inteligente · Hackathon Celaya 2026
-- ================================================================
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- ----------------------------------------------------------------
-- 1. USERS
-- ----------------------------------------------------------------
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE,
phone VARCHAR(20) UNIQUE,
password_hash VARCHAR(255) NOT NULL,
fcm_token VARCHAR(500),
rol VARCHAR(20) NOT NULL DEFAULT 'ciudadano',
activo BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_contacto CHECK (email IS NOT NULL OR phone IS NOT NULL)
);
-- ----------------------------------------------------------------
-- 2. RUTAS
-- ----------------------------------------------------------------
CREATE TABLE rutas (
id SERIAL PRIMARY KEY,
route_id VARCHAR(20) NOT NULL UNIQUE,
nombre VARCHAR(255) NOT NULL,
truck_id SMALLINT NOT NULL,
hora_inicio TIME NOT NULL,
hora_fin_ref TIME NOT NULL,
activa BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ----------------------------------------------------------------
-- 3. RUTA_POSICIONES
-- ----------------------------------------------------------------
CREATE TABLE ruta_posiciones (
id BIGSERIAL PRIMARY KEY,
ruta_id INT NOT NULL REFERENCES rutas(id) ON DELETE CASCADE,
position_id SMALLINT NOT NULL,
lat DECIMAL(9,6) NOT NULL,
lng DECIMAL(9,6) NOT NULL,
speed_kmh SMALLINT NOT NULL DEFAULT 0,
ts_referencia TIMESTAMPTZ NOT NULL,
offset_seg INT NOT NULL DEFAULT 0,
punto GEOGRAPHY(POINT, 4326) NOT NULL,
CONSTRAINT uq_ruta_posicion UNIQUE (ruta_id, position_id)
);
CREATE INDEX idx_ruta_pos_ruta ON ruta_posiciones (ruta_id, position_id);
CREATE INDEX idx_ruta_pos_punto ON ruta_posiciones USING GIST (punto);
-- ----------------------------------------------------------------
-- 4. COLONIAS
-- ----------------------------------------------------------------
CREATE TABLE colonias (
id SERIAL PRIMARY KEY,
nombre VARCHAR(255) NOT NULL UNIQUE,
ruta_id INT NOT NULL REFERENCES rutas(id),
horario_turno VARCHAR(20) NOT NULL,
hora_inicio_est TIME NOT NULL,
hora_fin_est TIME NOT NULL,
poligono GEOGRAPHY(POLYGON, 4326),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_colonias_ruta ON colonias (ruta_id);
CREATE INDEX idx_colonias_pol ON colonias USING GIST (poligono) WHERE poligono IS NOT NULL;
-- ----------------------------------------------------------------
-- 5. DOMICILIOS
-- ----------------------------------------------------------------
CREATE TABLE domicilios (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
alias VARCHAR(50) NOT NULL,
direccion_texto TEXT NOT NULL,
lat DECIMAL(9,6) NOT NULL,
lng DECIMAL(9,6) NOT NULL,
coordenada GEOGRAPHY(POINT, 4326) NOT NULL,
colonia_id INT REFERENCES colonias(id) ON DELETE SET NULL,
validado BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_domicilio_alias UNIQUE (user_id, alias)
);
CREATE INDEX idx_domicilios_user ON domicilios (user_id);
CREATE INDEX idx_domicilios_col ON domicilios (colonia_id);
CREATE INDEX idx_domicilios_coord ON domicilios USING GIST (coordenada);
-- ----------------------------------------------------------------
-- 6. ESTADO_RUTA (actualizado por el simulador/cron)
-- ----------------------------------------------------------------
CREATE TABLE estado_ruta (
ruta_id INT PRIMARY KEY REFERENCES rutas(id) ON DELETE CASCADE,
estado VARCHAR(20) NOT NULL DEFAULT 'INACTIVA',
position_id_actual SMALLINT NOT NULL DEFAULT 1,
lat_actual DECIMAL(9,6),
lng_actual DECIMAL(9,6),
hora_real_inicio TIMESTAMPTZ,
hora_estim_fin TIMESTAMPTZ,
minutos_retraso SMALLINT NOT NULL DEFAULT 0,
motivo_retraso TEXT,
actualizado_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ----------------------------------------------------------------
-- 7. NOTIFICACION_TEMPLATES
-- ----------------------------------------------------------------
CREATE TABLE notificacion_templates (
id SERIAL PRIMARY KEY,
trigger_event VARCHAR(30) NOT NULL UNIQUE,
position_id_trigger SMALLINT NOT NULL,
titulo VARCHAR(150) NOT NULL,
cuerpo TEXT NOT NULL,
activo BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ----------------------------------------------------------------
-- 8. NOTIFICACIONES
-- ----------------------------------------------------------------
CREATE TABLE notificaciones (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
domicilio_id UUID REFERENCES domicilios(id) ON DELETE SET NULL,
template_id INT REFERENCES notificacion_templates(id),
ruta_id INT REFERENCES rutas(id),
titulo VARCHAR(150) NOT NULL,
mensaje TEXT NOT NULL,
enviada_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
leida BOOLEAN NOT NULL DEFAULT FALSE,
leida_at TIMESTAMPTZ
);
CREATE INDEX idx_notif_user ON notificaciones (user_id, enviada_at DESC);
CREATE INDEX idx_notif_no_leida ON notificaciones (user_id) WHERE leida = FALSE;
-- ----------------------------------------------------------------
-- 9. REPORTES
-- ----------------------------------------------------------------
CREATE TABLE reportes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
domicilio_id UUID REFERENCES domicilios(id) ON DELETE SET NULL,
ruta_id INT REFERENCES rutas(id),
tipo VARCHAR(40) NOT NULL,
descripcion TEXT,
calificacion SMALLINT CHECK (calificacion BETWEEN 1 AND 5),
estado VARCHAR(20) NOT NULL DEFAULT 'abierto',
creado_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_reportes_user ON reportes (user_id);
CREATE INDEX idx_reportes_ruta ON reportes (ruta_id, creado_at);
-- ----------------------------------------------------------------
-- 10. GUÍA DE SEPARACIÓN
-- ----------------------------------------------------------------
CREATE TABLE residuos_categorias (
id SERIAL PRIMARY KEY,
nombre VARCHAR(50) NOT NULL,
descripcion TEXT,
color_hex CHAR(7),
icono VARCHAR(100)
);
CREATE TABLE residuos_ejemplos (
id SERIAL PRIMARY KEY,
categoria_id INT NOT NULL REFERENCES residuos_categorias(id) ON DELETE CASCADE,
nombre VARCHAR(255) NOT NULL,
descripcion TEXT
);
-- ----------------------------------------------------------------
-- TRIGGERS
-- ----------------------------------------------------------------
CREATE OR REPLACE FUNCTION fn_set_updated_at()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$;
CREATE TRIGGER trg_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION fn_set_updated_at();