## New Tables Created (Sprint 1 - DDL Roadmap Q1-2026) ### users schema (4 tables): - profiles: Extended user profile information - user_settings: User preferences and configurations - kyc_verifications: KYC/AML verification records - risk_profiles: Trading risk assessment profiles ### admin schema (3 tables): - admin_roles: Platform administrative roles - platform_analytics: Aggregated platform metrics - api_keys: Programmatic API access keys ### notifications schema (1 table): - notifications: Multi-channel notification system ### market_data schema (4 tables): - tickers: Financial instruments catalog - ohlcv_5m: 5-minute OHLCV price data - technical_indicators: Pre-calculated TA indicators - ohlcv_5m_staging: Staging table for data ingestion ## Features: - Multi-tenancy with RLS policies - Comprehensive indexes for query optimization - Triggers for computed fields and timestamps - Helper functions for common operations - Views for dashboard and reporting - Full GRANTS configuration Roadmap: orchestration/planes/ROADMAP-IMPLEMENTACION-DDL-2026-Q1.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
277 lines
9.2 KiB
PL/PgSQL
277 lines
9.2 KiB
PL/PgSQL
-- ============================================================================
|
|
-- SCHEMA: admin
|
|
-- TABLE: platform_analytics
|
|
-- DESCRIPTION: Metricas agregadas de la plataforma
|
|
-- VERSION: 1.0.0
|
|
-- CREATED: 2026-01-16
|
|
-- SPRINT: Sprint 1 - DDL Implementation Roadmap Q1-2026
|
|
-- ============================================================================
|
|
|
|
-- Enum para tipo de metrica
|
|
DO $$ BEGIN
|
|
CREATE TYPE admin.metric_type AS ENUM (
|
|
'counter', -- Contador simple
|
|
'gauge', -- Valor actual
|
|
'histogram', -- Distribucion
|
|
'summary', -- Resumen estadistico
|
|
'rate' -- Tasa por periodo
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- Enum para granularidad
|
|
DO $$ BEGIN
|
|
CREATE TYPE admin.time_granularity AS ENUM (
|
|
'minute',
|
|
'hour',
|
|
'day',
|
|
'week',
|
|
'month',
|
|
'quarter',
|
|
'year'
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- Tabla de Metricas de Plataforma
|
|
CREATE TABLE IF NOT EXISTS admin.platform_analytics (
|
|
-- Identificadores
|
|
id BIGSERIAL PRIMARY KEY,
|
|
|
|
-- Scope
|
|
tenant_id UUID REFERENCES tenants.tenants(id), -- NULL = plataforma global
|
|
|
|
-- Metrica
|
|
metric_name VARCHAR(100) NOT NULL,
|
|
metric_type admin.metric_type NOT NULL DEFAULT 'gauge',
|
|
category VARCHAR(50) NOT NULL, -- 'users', 'trading', 'revenue', 'performance'
|
|
|
|
-- Periodo
|
|
period_start TIMESTAMPTZ NOT NULL,
|
|
period_end TIMESTAMPTZ NOT NULL,
|
|
granularity admin.time_granularity NOT NULL DEFAULT 'day',
|
|
|
|
-- Valores
|
|
value DECIMAL(20, 6) NOT NULL,
|
|
previous_value DECIMAL(20, 6), -- Valor periodo anterior (para calcular delta)
|
|
delta_value DECIMAL(20, 6), -- Cambio vs periodo anterior
|
|
delta_percent DECIMAL(10, 4), -- Cambio porcentual
|
|
|
|
-- Estadisticas (para histogramas/summaries)
|
|
min_value DECIMAL(20, 6),
|
|
max_value DECIMAL(20, 6),
|
|
avg_value DECIMAL(20, 6),
|
|
median_value DECIMAL(20, 6),
|
|
p95_value DECIMAL(20, 6),
|
|
p99_value DECIMAL(20, 6),
|
|
stddev_value DECIMAL(20, 6),
|
|
sample_count BIGINT,
|
|
|
|
-- Dimensiones adicionales
|
|
dimensions JSONB DEFAULT '{}'::JSONB, -- Dimensiones para desglose
|
|
|
|
-- Metadata
|
|
source VARCHAR(50) NOT NULL DEFAULT 'system', -- Origen del dato
|
|
is_estimated BOOLEAN NOT NULL DEFAULT FALSE, -- Dato estimado vs real
|
|
confidence_level DECIMAL(5, 4), -- Nivel de confianza (0-1)
|
|
|
|
-- Timestamps
|
|
collected_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
-- Constraints
|
|
CONSTRAINT analytics_period_check CHECK (period_end > period_start),
|
|
CONSTRAINT analytics_unique_metric UNIQUE (tenant_id, metric_name, granularity, period_start)
|
|
);
|
|
|
|
COMMENT ON TABLE admin.platform_analytics IS
|
|
'Metricas agregadas de la plataforma para dashboards y reportes';
|
|
|
|
COMMENT ON COLUMN admin.platform_analytics.tenant_id IS
|
|
'NULL para metricas globales de plataforma, UUID para metricas por tenant';
|
|
|
|
COMMENT ON COLUMN admin.platform_analytics.dimensions IS
|
|
'Dimensiones adicionales como {"country": "MX", "plan": "premium"}';
|
|
|
|
-- Particionamiento por fecha (recomendado para produccion)
|
|
-- CREATE TABLE admin.platform_analytics_partitioned (
|
|
-- LIKE admin.platform_analytics INCLUDING ALL
|
|
-- ) PARTITION BY RANGE (period_start);
|
|
|
|
-- Indices
|
|
CREATE INDEX IF NOT EXISTS idx_analytics_metric_period
|
|
ON admin.platform_analytics(metric_name, period_start DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_analytics_tenant_metric
|
|
ON admin.platform_analytics(tenant_id, metric_name, period_start DESC)
|
|
WHERE tenant_id IS NOT NULL;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_analytics_category
|
|
ON admin.platform_analytics(category, period_start DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_analytics_granularity
|
|
ON admin.platform_analytics(granularity, period_start DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_analytics_global
|
|
ON admin.platform_analytics(metric_name, granularity, period_start DESC)
|
|
WHERE tenant_id IS NULL;
|
|
|
|
-- GIN index para dimensiones
|
|
CREATE INDEX IF NOT EXISTS idx_analytics_dimensions_gin
|
|
ON admin.platform_analytics USING GIN (dimensions);
|
|
|
|
-- Trigger para calcular deltas automaticamente
|
|
CREATE OR REPLACE FUNCTION admin.calculate_analytics_delta()
|
|
RETURNS TRIGGER AS $$
|
|
DECLARE
|
|
v_prev RECORD;
|
|
v_period_interval INTERVAL;
|
|
BEGIN
|
|
-- Calcular intervalo basado en granularidad
|
|
CASE NEW.granularity
|
|
WHEN 'minute' THEN v_period_interval := INTERVAL '1 minute';
|
|
WHEN 'hour' THEN v_period_interval := INTERVAL '1 hour';
|
|
WHEN 'day' THEN v_period_interval := INTERVAL '1 day';
|
|
WHEN 'week' THEN v_period_interval := INTERVAL '1 week';
|
|
WHEN 'month' THEN v_period_interval := INTERVAL '1 month';
|
|
WHEN 'quarter' THEN v_period_interval := INTERVAL '3 months';
|
|
WHEN 'year' THEN v_period_interval := INTERVAL '1 year';
|
|
END CASE;
|
|
|
|
-- Buscar valor anterior
|
|
SELECT value INTO v_prev
|
|
FROM admin.platform_analytics
|
|
WHERE metric_name = NEW.metric_name
|
|
AND granularity = NEW.granularity
|
|
AND (tenant_id = NEW.tenant_id OR (tenant_id IS NULL AND NEW.tenant_id IS NULL))
|
|
AND period_start = NEW.period_start - v_period_interval
|
|
LIMIT 1;
|
|
|
|
IF v_prev IS NOT NULL THEN
|
|
NEW.previous_value := v_prev.value;
|
|
NEW.delta_value := NEW.value - v_prev.value;
|
|
IF v_prev.value != 0 THEN
|
|
NEW.delta_percent := ((NEW.value - v_prev.value) / v_prev.value) * 100;
|
|
END IF;
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS analytics_calc_delta ON admin.platform_analytics;
|
|
CREATE TRIGGER analytics_calc_delta
|
|
BEFORE INSERT ON admin.platform_analytics
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION admin.calculate_analytics_delta();
|
|
|
|
-- Funcion para insertar o actualizar metrica
|
|
CREATE OR REPLACE FUNCTION admin.upsert_metric(
|
|
p_metric_name VARCHAR(100),
|
|
p_category VARCHAR(50),
|
|
p_value DECIMAL(20, 6),
|
|
p_granularity admin.time_granularity DEFAULT 'day',
|
|
p_tenant_id UUID DEFAULT NULL,
|
|
p_metric_type admin.metric_type DEFAULT 'gauge',
|
|
p_dimensions JSONB DEFAULT '{}'::JSONB
|
|
)
|
|
RETURNS BIGINT AS $$
|
|
DECLARE
|
|
v_period_start TIMESTAMPTZ;
|
|
v_period_end TIMESTAMPTZ;
|
|
v_id BIGINT;
|
|
BEGIN
|
|
-- Calcular periodo basado en granularidad
|
|
CASE p_granularity
|
|
WHEN 'minute' THEN
|
|
v_period_start := DATE_TRUNC('minute', NOW());
|
|
v_period_end := v_period_start + INTERVAL '1 minute';
|
|
WHEN 'hour' THEN
|
|
v_period_start := DATE_TRUNC('hour', NOW());
|
|
v_period_end := v_period_start + INTERVAL '1 hour';
|
|
WHEN 'day' THEN
|
|
v_period_start := DATE_TRUNC('day', NOW());
|
|
v_period_end := v_period_start + INTERVAL '1 day';
|
|
WHEN 'week' THEN
|
|
v_period_start := DATE_TRUNC('week', NOW());
|
|
v_period_end := v_period_start + INTERVAL '1 week';
|
|
WHEN 'month' THEN
|
|
v_period_start := DATE_TRUNC('month', NOW());
|
|
v_period_end := v_period_start + INTERVAL '1 month';
|
|
WHEN 'quarter' THEN
|
|
v_period_start := DATE_TRUNC('quarter', NOW());
|
|
v_period_end := v_period_start + INTERVAL '3 months';
|
|
WHEN 'year' THEN
|
|
v_period_start := DATE_TRUNC('year', NOW());
|
|
v_period_end := v_period_start + INTERVAL '1 year';
|
|
END CASE;
|
|
|
|
INSERT INTO admin.platform_analytics (
|
|
tenant_id, metric_name, metric_type, category,
|
|
period_start, period_end, granularity,
|
|
value, dimensions
|
|
) VALUES (
|
|
p_tenant_id, p_metric_name, p_metric_type, p_category,
|
|
v_period_start, v_period_end, p_granularity,
|
|
p_value, p_dimensions
|
|
)
|
|
ON CONFLICT (tenant_id, metric_name, granularity, period_start)
|
|
DO UPDATE SET
|
|
value = EXCLUDED.value,
|
|
dimensions = EXCLUDED.dimensions,
|
|
collected_at = NOW()
|
|
RETURNING id INTO v_id;
|
|
|
|
RETURN v_id;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Vista de metricas recientes
|
|
CREATE OR REPLACE VIEW admin.v_recent_metrics AS
|
|
SELECT
|
|
metric_name,
|
|
category,
|
|
granularity,
|
|
tenant_id,
|
|
value,
|
|
delta_percent,
|
|
period_start,
|
|
collected_at
|
|
FROM admin.platform_analytics
|
|
WHERE collected_at > NOW() - INTERVAL '24 hours'
|
|
ORDER BY metric_name, period_start DESC;
|
|
|
|
-- Vista de KPIs principales
|
|
CREATE OR REPLACE VIEW admin.v_platform_kpis AS
|
|
SELECT
|
|
metric_name,
|
|
value AS current_value,
|
|
previous_value,
|
|
delta_percent AS change_percent,
|
|
period_start,
|
|
granularity
|
|
FROM admin.platform_analytics
|
|
WHERE tenant_id IS NULL -- Solo metricas globales
|
|
AND granularity = 'day'
|
|
AND metric_name IN (
|
|
'total_users',
|
|
'active_users_daily',
|
|
'total_trades',
|
|
'total_volume_usd',
|
|
'revenue_usd',
|
|
'new_signups',
|
|
'churn_rate'
|
|
)
|
|
AND period_start = DATE_TRUNC('day', NOW())
|
|
ORDER BY metric_name;
|
|
|
|
-- Grants
|
|
GRANT SELECT, INSERT, UPDATE ON admin.platform_analytics TO trading_app;
|
|
GRANT SELECT ON admin.platform_analytics TO trading_readonly;
|
|
GRANT USAGE, SELECT ON SEQUENCE admin.platform_analytics_id_seq TO trading_app;
|
|
GRANT SELECT ON admin.v_recent_metrics TO trading_app;
|
|
GRANT SELECT ON admin.v_platform_kpis TO trading_app;
|
|
GRANT EXECUTE ON FUNCTION admin.upsert_metric TO trading_app;
|