workspace/projects/gamilit/orchestration/directivas/DIRECTIVA-DISENO-BASE-DATOS.md
rckrdmrd ea1879f4ad feat: Initial workspace structure with multi-level Git configuration
- Configure workspace Git repository with comprehensive .gitignore
- Add Odoo as submodule for ERP reference code
- Include documentation: SETUP.md, GIT-STRUCTURE.md
- Add gitignore templates for projects (backend, frontend, database)
- Structure supports independent repos per project/subproject level

Workspace includes:
- core/ - Reusable patterns, modules, orchestration system
- projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.)
- knowledge-base/ - Reference code and patterns (includes Odoo submodule)
- devtools/ - Development tools and templates
- customers/ - Client implementations template

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 10:44:23 -06:00

19 KiB

DIRECTIVA: DISENO DE BASE DE DATOS - GAMILIT

Proyecto: GAMILIT - Sistema de Gamificacion Educativa Extiende: core/orchestration/directivas/DIRECTIVA-DISENO-BASE-DATOS.md Version: 2.0.0 Fecha: 2025-12-05 Ambito: Database-Agent y subagentes del proyecto GAMILIT Tipo: Directiva Especifica de Proyecto Stack: PostgreSQL 15+ con PostGIS


RELACION CON DIRECTIVA GLOBAL

Esta directiva EXTIENDE la directiva global de diseno de base de datos ubicada en:

core/orchestration/directivas/DIRECTIVA-DISENO-BASE-DATOS.md

Principios heredados de la directiva global:

  • Normalizacion minimo 3NF
  • UUIDs como primary keys
  • Nomenclatura de constraints (fk_, uq_, chk_)
  • Indexacion estrategica
  • Columnas de auditoria obligatorias

Esta directiva especifica:

  • Schemas del proyecto GAMILIT
  • Contextos de negocio especificos
  • Ejemplos concretos del dominio de gamificacion educativa

PATHS ESPECIFICOS DE GAMILIT

Variables resueltas para GAMILIT:
  PROJECT:           gamilit
  PROJECT_ROOT:      projects/gamilit
  APPS_ROOT:         projects/gamilit/apps
  DOCS_ROOT:         projects/gamilit/docs
  ORCHESTRATION:     projects/gamilit/orchestration

Base de Datos:
  DB_NAME:           gamilit_platform
  DB_DDL_PATH:       projects/gamilit/apps/database/ddl
  DB_SCRIPTS_PATH:   projects/gamilit/apps/database
  DB_SEEDS_PATH:     projects/gamilit/apps/database/seeds
  RECREATE_CMD:      drop-and-recreate-database.sh

SCHEMAS DE GAMILIT

Organizacion por Bounded Context

Schemas del proyecto GAMILIT:

auth_management:
  Descripcion: Autenticacion, autorizacion y gestion de tenants
  Tablas principales:
    - users (usuarios del sistema)
    - roles (roles disponibles)
    - permissions (permisos granulares)
    - user_roles (asignacion de roles)
    - tenants (multi-tenancy)
    - sessions (sesiones activas)

academic_management:
  Descripcion: Gestion academica (instituciones, cursos, estudiantes)
  Tablas principales:
    - institutions (instituciones educativas)
    - courses (cursos/materias)
    - course_sections (secciones de cursos)
    - students (estudiantes)
    - teachers (profesores)
    - enrollments (inscripciones)

gamification_system:
  Descripcion: Nucleo de gamificacion (puntos, niveles, badges, challenges)
  Tablas principales:
    - user_points (puntos acumulados)
    - point_transactions (historial de puntos)
    - levels (niveles del sistema)
    - user_levels (nivel actual por usuario)
    - badges (insignias disponibles)
    - user_badges (badges ganados)
    - challenges (desafios)
    - user_challenges (progreso en desafios)
    - rewards (recompensas canjeables)
    - reward_redemptions (canjes realizados)

exercise_management:
  Descripcion: Gestion de ejercicios y evaluaciones
  Tablas principales:
    - exercise_types (tipos de ejercicios)
    - exercises (ejercicios)
    - exercise_variants (variantes por dificultad)
    - exercise_solutions (soluciones correctas)
    - exercise_submissions (entregas de estudiantes)
    - exercise_evaluations (evaluaciones automaticas/manuales)

progress_tracking:
  Descripcion: Seguimiento de progreso estudiantil
  Tablas principales:
    - student_progress (progreso general)
    - topic_mastery (dominio por tema)
    - skill_assessments (evaluacion de habilidades)
    - learning_paths (rutas de aprendizaje)
    - progress_milestones (hitos de progreso)

guild_management:
  Descripcion: Sistema de guildas y competencias grupales
  Tablas principales:
    - guilds (guildas/equipos)
    - guild_members (miembros de guildas)
    - guild_challenges (desafios de guilda)
    - guild_rankings (rankings por guilda)
    - guild_competitions (competencias entre guildas)

notification_management:
  Descripcion: Sistema de notificaciones y mensajeria
  Tablas principales:
    - notification_templates (plantillas)
    - notifications (notificaciones enviadas)
    - notification_preferences (preferencias por usuario)
    - in_app_messages (mensajes internos)

EJEMPLOS ESPECIFICOS DE GAMILIT

Tabla de Puntos (gamification_system.user_points)

-- File: apps/database/ddl/schemas/gamification_system/tables/01-user-points.sql

DROP TABLE IF EXISTS gamification_system.user_points CASCADE;

CREATE TABLE gamification_system.user_points (
    -- Primary Key
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Relaciones
    user_id UUID NOT NULL,
    tenant_id UUID NOT NULL,

    -- Datos de puntos
    total_points INTEGER NOT NULL DEFAULT 0,
    available_points INTEGER NOT NULL DEFAULT 0,  -- Puntos para canjear
    lifetime_points INTEGER NOT NULL DEFAULT 0,   -- Puntos totales historicos

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),

    -- Constraints
    CONSTRAINT fk_user_points_to_users
        FOREIGN KEY (user_id)
        REFERENCES auth_management.users(id)
        ON DELETE CASCADE,

    CONSTRAINT fk_user_points_to_tenants
        FOREIGN KEY (tenant_id)
        REFERENCES auth_management.tenants(id)
        ON DELETE CASCADE,

    CONSTRAINT uq_user_points_user_tenant
        UNIQUE (user_id, tenant_id),

    CONSTRAINT chk_user_points_total_positive
        CHECK (total_points >= 0),

    CONSTRAINT chk_user_points_available_positive
        CHECK (available_points >= 0),

    CONSTRAINT chk_user_points_available_lte_total
        CHECK (available_points <= total_points)
);

-- Indices
CREATE INDEX idx_user_points_user_id ON gamification_system.user_points(user_id);
CREATE INDEX idx_user_points_tenant_id ON gamification_system.user_points(tenant_id);
CREATE INDEX idx_user_points_total_points ON gamification_system.user_points(total_points DESC);

-- Trigger para updated_at
CREATE TRIGGER trg_user_points_updated_at
BEFORE UPDATE ON gamification_system.user_points
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();

-- Comentarios
COMMENT ON TABLE gamification_system.user_points IS
'Puntos acumulados por usuario. Soporta multi-tenancy para diferentes instituciones.';

COMMENT ON COLUMN gamification_system.user_points.available_points IS
'Puntos disponibles para canjear por recompensas. Puede ser menor que total_points si ya se canjearon algunos.';

Tabla de Badges (gamification_system.badges)

-- File: apps/database/ddl/schemas/gamification_system/tables/03-badges.sql

DROP TABLE IF EXISTS gamification_system.badges CASCADE;

CREATE TABLE gamification_system.badges (
    -- Primary Key
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Identificacion
    code VARCHAR(50) NOT NULL UNIQUE,
    name VARCHAR(200) NOT NULL,
    description TEXT,

    -- Configuracion
    category VARCHAR(50) NOT NULL,
    rarity VARCHAR(20) NOT NULL DEFAULT 'common',
    points_value INTEGER NOT NULL DEFAULT 0,
    icon_url VARCHAR(500),

    -- Requisitos para obtener el badge
    requirement_type VARCHAR(50) NOT NULL,  -- 'points', 'exercises', 'streak', 'challenge'
    requirement_value INTEGER NOT NULL,
    requirement_config JSONB,  -- Configuracion adicional flexible

    -- Estado
    is_active BOOLEAN NOT NULL DEFAULT true,
    is_secret BOOLEAN NOT NULL DEFAULT false,  -- Badges ocultos hasta obtenidos

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    created_by_id UUID,
    updated_by_id UUID,

    -- Constraints
    CONSTRAINT chk_badges_rarity_valid
        CHECK (rarity IN ('common', 'uncommon', 'rare', 'epic', 'legendary')),

    CONSTRAINT chk_badges_category_valid
        CHECK (category IN ('achievement', 'participation', 'mastery', 'social', 'special')),

    CONSTRAINT chk_badges_requirement_type_valid
        CHECK (requirement_type IN ('points', 'exercises', 'streak', 'challenge', 'level', 'time', 'custom')),

    CONSTRAINT chk_badges_points_value_positive
        CHECK (points_value >= 0),

    CONSTRAINT fk_badges_created_by
        FOREIGN KEY (created_by_id) REFERENCES auth_management.users(id)
        ON DELETE SET NULL,

    CONSTRAINT fk_badges_updated_by
        FOREIGN KEY (updated_by_id) REFERENCES auth_management.users(id)
        ON DELETE SET NULL
);

-- Indices
CREATE INDEX idx_badges_category ON gamification_system.badges(category);
CREATE INDEX idx_badges_rarity ON gamification_system.badges(rarity);
CREATE INDEX idx_badges_is_active ON gamification_system.badges(is_active) WHERE is_active = true;
CREATE INDEX idx_badges_requirement_config_gin ON gamification_system.badges USING GIN(requirement_config);

-- Trigger para updated_at
CREATE TRIGGER trg_badges_updated_at
BEFORE UPDATE ON gamification_system.badges
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();

-- Comentarios
COMMENT ON TABLE gamification_system.badges IS
'Badges/insignias que los estudiantes pueden ganar. Incluye sistema de rareza y requisitos configurables.';

COMMENT ON COLUMN gamification_system.badges.requirement_config IS
'Configuracion adicional en JSON para requisitos complejos. Ej: {"exercise_type": "math", "min_score": 90}';

Tabla de Desafios (gamification_system.challenges)

-- File: apps/database/ddl/schemas/gamification_system/tables/05-challenges.sql

DROP TABLE IF EXISTS gamification_system.challenges CASCADE;

CREATE TABLE gamification_system.challenges (
    -- Primary Key
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Identificacion
    code VARCHAR(50) NOT NULL UNIQUE,
    name VARCHAR(200) NOT NULL,
    description TEXT,

    -- Configuracion
    challenge_type VARCHAR(50) NOT NULL,  -- 'daily', 'weekly', 'special', 'guild'
    difficulty VARCHAR(20) NOT NULL DEFAULT 'medium',
    points_reward INTEGER NOT NULL DEFAULT 0,
    experience_reward INTEGER NOT NULL DEFAULT 0,

    -- Requisitos
    required_level_id UUID,
    requirements JSONB NOT NULL DEFAULT '{}',

    -- Duracion
    start_date TIMESTAMPTZ,
    end_date TIMESTAMPTZ,
    duration_hours INTEGER,  -- Para challenges con tiempo limite

    -- Estado
    is_active BOOLEAN NOT NULL DEFAULT true,
    is_repeatable BOOLEAN NOT NULL DEFAULT false,
    max_completions INTEGER,  -- null = sin limite

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    created_by_id UUID,

    -- Constraints
    CONSTRAINT fk_challenges_to_levels
        FOREIGN KEY (required_level_id)
        REFERENCES gamification_system.levels(id)
        ON DELETE SET NULL,

    CONSTRAINT fk_challenges_created_by
        FOREIGN KEY (created_by_id) REFERENCES auth_management.users(id)
        ON DELETE SET NULL,

    CONSTRAINT chk_challenges_type_valid
        CHECK (challenge_type IN ('daily', 'weekly', 'monthly', 'special', 'guild', 'event')),

    CONSTRAINT chk_challenges_difficulty_valid
        CHECK (difficulty IN ('easy', 'medium', 'hard', 'expert')),

    CONSTRAINT chk_challenges_points_positive
        CHECK (points_reward >= 0),

    CONSTRAINT chk_challenges_dates_valid
        CHECK (end_date IS NULL OR start_date IS NULL OR end_date > start_date)
);

-- Indices
CREATE INDEX idx_challenges_type ON gamification_system.challenges(challenge_type);
CREATE INDEX idx_challenges_difficulty ON gamification_system.challenges(difficulty);
CREATE INDEX idx_challenges_required_level_id ON gamification_system.challenges(required_level_id);
CREATE INDEX idx_challenges_active ON gamification_system.challenges(is_active) WHERE is_active = true;
CREATE INDEX idx_challenges_dates ON gamification_system.challenges(start_date, end_date);
CREATE INDEX idx_challenges_requirements_gin ON gamification_system.challenges USING GIN(requirements);

-- Trigger para updated_at
CREATE TRIGGER trg_challenges_updated_at
BEFORE UPDATE ON gamification_system.challenges
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();

-- Comentarios
COMMENT ON TABLE gamification_system.challenges IS
'Desafios del sistema de gamificacion. Pueden ser diarios, semanales, especiales o de guilda.';

COMMENT ON COLUMN gamification_system.challenges.requirements IS
'Requisitos en JSON. Ej: {"complete_exercises": 5, "exercise_type": "math", "min_score": 80}';

VISTAS MATERIALIZADAS DE GAMILIT

Dashboard de Estudiantes

-- File: apps/database/ddl/schemas/gamification_system/views/dashboard-student-summary.sql

DROP MATERIALIZED VIEW IF EXISTS gamification_system.dashboard_student_summary;

CREATE MATERIALIZED VIEW gamification_system.dashboard_student_summary AS
SELECT
    s.id AS student_id,
    s.user_id,
    u.username,
    u.email,
    s.tenant_id,

    -- Puntos
    COALESCE(up.total_points, 0) AS total_points,
    COALESCE(up.available_points, 0) AS available_points,

    -- Nivel
    COALESCE(ul.level_number, 1) AS current_level,
    l.name AS level_name,

    -- Estadisticas de ejercicios
    COUNT(DISTINCT es.id) FILTER (WHERE es.status = 'completed') AS exercises_completed,
    COALESCE(AVG(es.score) FILTER (WHERE es.status = 'completed'), 0) AS average_score,

    -- Badges
    COUNT(DISTINCT ub.badge_id) AS total_badges,

    -- Challenges
    COUNT(DISTINCT uc.challenge_id) FILTER (WHERE uc.status = 'completed') AS challenges_completed,

    -- Actividad reciente
    MAX(es.submitted_at) AS last_exercise_date,
    MAX(uc.completed_at) AS last_challenge_date

FROM academic_management.students s
JOIN auth_management.users u ON u.id = s.user_id
LEFT JOIN gamification_system.user_points up ON up.user_id = s.user_id AND up.tenant_id = s.tenant_id
LEFT JOIN gamification_system.user_levels ul ON ul.user_id = s.user_id AND ul.tenant_id = s.tenant_id
LEFT JOIN gamification_system.levels l ON l.id = ul.level_id
LEFT JOIN exercise_management.exercise_submissions es ON es.student_id = s.id
LEFT JOIN gamification_system.user_badges ub ON ub.user_id = s.user_id
LEFT JOIN gamification_system.user_challenges uc ON uc.user_id = s.user_id

WHERE s.deleted_at IS NULL
  AND u.deleted_at IS NULL

GROUP BY
    s.id, s.user_id, u.username, u.email, s.tenant_id,
    up.total_points, up.available_points,
    ul.level_number, l.name;

-- Indices en vista materializada
CREATE INDEX idx_dashboard_student_summary_student_id
    ON gamification_system.dashboard_student_summary(student_id);
CREATE INDEX idx_dashboard_student_summary_tenant_id
    ON gamification_system.dashboard_student_summary(tenant_id);
CREATE INDEX idx_dashboard_student_summary_total_points
    ON gamification_system.dashboard_student_summary(total_points DESC);
CREATE INDEX idx_dashboard_student_summary_current_level
    ON gamification_system.dashboard_student_summary(current_level);

-- Refrescar con:
-- REFRESH MATERIALIZED VIEW CONCURRENTLY gamification_system.dashboard_student_summary;

Leaderboard Global

-- File: apps/database/ddl/schemas/gamification_system/views/leaderboard-global.sql

DROP MATERIALIZED VIEW IF EXISTS gamification_system.leaderboard_global;

CREATE MATERIALIZED VIEW gamification_system.leaderboard_global AS
SELECT
    ROW_NUMBER() OVER (PARTITION BY up.tenant_id ORDER BY up.total_points DESC) AS rank,
    up.user_id,
    u.username,
    up.tenant_id,
    t.name AS tenant_name,
    up.total_points,
    ul.level_number AS current_level,
    COUNT(DISTINCT ub.badge_id) AS badge_count,
    COUNT(DISTINCT uc.challenge_id) FILTER (WHERE uc.status = 'completed') AS challenges_completed

FROM gamification_system.user_points up
JOIN auth_management.users u ON u.id = up.user_id
JOIN auth_management.tenants t ON t.id = up.tenant_id
LEFT JOIN gamification_system.user_levels ul ON ul.user_id = up.user_id AND ul.tenant_id = up.tenant_id
LEFT JOIN gamification_system.user_badges ub ON ub.user_id = up.user_id
LEFT JOIN gamification_system.user_challenges uc ON uc.user_id = up.user_id

WHERE u.deleted_at IS NULL
  AND up.total_points > 0

GROUP BY
    up.user_id, u.username, up.tenant_id, t.name,
    up.total_points, ul.level_number

ORDER BY up.tenant_id, up.total_points DESC;

-- Indices
CREATE INDEX idx_leaderboard_global_tenant_rank
    ON gamification_system.leaderboard_global(tenant_id, rank);
CREATE INDEX idx_leaderboard_global_user_id
    ON gamification_system.leaderboard_global(user_id);

FUNCIONES Y TRIGGERS ESPECIFICOS

Funcion para Otorgar Puntos

-- File: apps/database/ddl/schemas/gamification_system/functions/award-points.sql

CREATE OR REPLACE FUNCTION gamification_system.award_points(
    p_user_id UUID,
    p_tenant_id UUID,
    p_points INTEGER,
    p_reason VARCHAR(100),
    p_reference_type VARCHAR(50) DEFAULT NULL,
    p_reference_id UUID DEFAULT NULL
)
RETURNS UUID AS $$
DECLARE
    v_transaction_id UUID;
BEGIN
    -- Insertar transaccion de puntos
    INSERT INTO gamification_system.point_transactions (
        user_id, tenant_id, points, reason,
        reference_type, reference_id, transaction_type
    ) VALUES (
        p_user_id, p_tenant_id, p_points, p_reason,
        p_reference_type, p_reference_id, 'earn'
    )
    RETURNING id INTO v_transaction_id;

    -- Actualizar puntos del usuario (upsert)
    INSERT INTO gamification_system.user_points (user_id, tenant_id, total_points, available_points, lifetime_points)
    VALUES (p_user_id, p_tenant_id, p_points, p_points, p_points)
    ON CONFLICT (user_id, tenant_id) DO UPDATE SET
        total_points = gamification_system.user_points.total_points + p_points,
        available_points = gamification_system.user_points.available_points + p_points,
        lifetime_points = gamification_system.user_points.lifetime_points + p_points,
        updated_at = now();

    -- Verificar si hay nuevo nivel (trigger separado manejara esto)
    PERFORM gamification_system.check_level_up(p_user_id, p_tenant_id);

    -- Verificar badges por puntos
    PERFORM gamification_system.check_point_badges(p_user_id, p_tenant_id);

    RETURN v_transaction_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION gamification_system.award_points IS
'Otorga puntos a un usuario, registra la transaccion, y verifica level-ups y badges.';

REFERENCIA RAPIDA

Comandos de Recreacion

# Ubicacion
cd ~/workspace/projects/gamilit/apps/database

# Recrear base de datos completa
./drop-and-recreate-database.sh

# Solo crear (sin drop)
./create-database.sh

# Reset a estado inicial
./reset-database.sh

Estructura de DDL

projects/gamilit/apps/database/ddl/
├── 00-prerequisites.sql
└── schemas/
    ├── auth_management/
    │   ├── 00-schema.sql
    │   ├── tables/
    │   ├── functions/
    │   └── policies/
    ├── gamification_system/
    │   ├── 00-schema.sql
    │   ├── tables/
    │   ├── functions/
    │   ├── views/
    │   └── policies/
    ├── academic_management/
    ├── exercise_management/
    ├── progress_tracking/
    ├── guild_management/
    └── notification_management/

DOCUMENTOS RELACIONADOS

  • Global: core/orchestration/directivas/DIRECTIVA-DISENO-BASE-DATOS.md
  • Global: core/orchestration/directivas/DIRECTIVA-POLITICA-CARGA-LIMPIA.md
  • Proyecto: orchestration/directivas/DIRECTIVA-POLITICA-CARGA-LIMPIA.md
  • Proyecto: docs/database/ - Documentacion detallada de BD

Version: 2.0.0 Fecha: 2025-12-05 Tipo: Directiva Especifica de Proyecto (GAMILIT) Extiende: core/orchestration/directivas/DIRECTIVA-DISENO-BASE-DATOS.md