Formação — Schema (PR-1)
SSOT do modelo de dados da seção Formação após a re-arquitetura. ADRs relacionadas: ADR-003 (payload dual-format), ADR-018 (Least Privilege).
Tabelas
cursos (existente — estendida)
| Campo | Tipo | Notas |
|---|---|---|
id | uuid PK | |
titulo | text | |
descricao | text | |
categoria_visual | text | |
imagem_url | text (novo) | Referência cursos-imagens/<path> no bucket privado. |
video_intro_url | text (novo) | URL externa (YouTube/Vimeo). |
status | text (novo) | publicado | em_breve | rascunho — trigger fn_validar_status_curso. |
ativo | boolean | Soft delete legado. |
criado_por | uuid | FK usuarios.id (especialista). |
curso_modulos (novo — PR-1)
| Campo | Tipo | Notas |
|---|---|---|
id | uuid PK | |
curso_id | uuid FK | ON DELETE CASCADE. |
titulo | text | |
descricao | text | nullable |
ordem | int | UNIQUE(curso_id, ordem) — reorder em duas fases. |
criado_em / atualizado_em | timestamptz |
cursos_videos (existente — estendida)
| Campo | Tipo | Notas |
|---|---|---|
modulo_id | uuid FK | NOT NULL, ON DELETE RESTRICT. UNIQUE(modulo_id, ordem). |
Backfill PR-1: cada curso ganhou "Módulo 01" e todos os vídeos existentes foram realocados; 0 órfãos.
trilhas (novo — globais)
| Campo | Tipo | Notas |
|---|---|---|
id | uuid PK | |
titulo | text | |
descricao | text | nullable |
ordem | int | UNIQUE(ordem) |
ativo | boolean |
trilha_cursos (novo — junção)
| Campo | Tipo | Notas |
|---|---|---|
trilha_id | uuid FK | ON DELETE CASCADE |
curso_id | uuid FK | ON DELETE CASCADE |
ordem | int | UNIQUE(trilha_id, ordem), PRIMARY KEY(trilha_id, curso_id) |
RLS — resumo (ADR-018)
Todas as policies:
USING / WITH CHECKbaseiam-se emcurrent_role_is('especialista')(CUD) oucurrent_role_is_any('especialista','coordenador')(SELECT em publicados).- Administrador é explicitamente negado (sem policy permissiva).
Storage
- Bucket
cursos-imagens— privado. WRITE só especialista; READ via signed URL pela edge functioncursos-imagens-signed-url(especialista + coordenador).
Triggers
fn_validar_status_curso—BEFORE INSERT/UPDATE ON cursos. Lança22023sestatus NOT IN ('publicado','em_breve','rascunho').
Constraints & integridade (estado em produção)
Validado em PR-7 via pg_constraint (não alterado por PR-7 — apenas consolidado em doc):
| Tabela | Constraint | Tipo | Definição |
|---|---|---|---|
curso_modulos | curso_modulos_curso_id_fkey | FK | (curso_id) REFERENCES cursos(id) ON DELETE CASCADE |
curso_modulos | curso_modulos_curso_id_ordem_key | UNIQUE | (curso_id, ordem) — reorder two-phase |
cursos_videos | cursos_videos_modulo_id_fkey | FK | (modulo_id) REFERENCES curso_modulos(id) ON DELETE RESTRICT — apagar módulo com aula vinculada falha |
cursos_videos | cursos_videos_curso_id_fkey | FK | (curso_id) REFERENCES cursos(id) ON DELETE CASCADE |
cursos_videos | cursos_videos_modulo_ordem_unique | UNIQUE | (modulo_id, ordem) — reorder por módulo |
cursos_videos | cursos_videos_ordem_positive | CHECK | ordem >= 1 |
cursos_videos | cursos_videos_avaliacao_range | CHECK | avaliacao_media BETWEEN 0 AND 5 |
cursos_videos | cursos_videos_total_avaliacoes_nonneg | CHECK | total_avaliacoes >= 0 |
cursos_videos | cursos_videos_visualizacoes_nonneg | CHECK | visualizacoes >= 0 |
trilha_cursos | PK composta | PK | (trilha_id, curso_id) |
trilha_cursos | trilha_cursos_trilha_id_fkey | FK | (trilha_id) REFERENCES trilhas(id) ON DELETE CASCADE |
trilha_cursos | trilha_cursos_curso_id_fkey | FK | (curso_id) REFERENCES cursos(id) ON DELETE CASCADE |
trilha_cursos | trilha_cursos_trilha_id_ordem_key | UNIQUE | (trilha_id, ordem) |
Semântica das cascatas:
- Apagar curso → cascade em
curso_modulos,cursos_videos,trilha_cursos. - Apagar módulo → bloqueado se houver aula (
RESTRICT); UI deve mover/excluir aulas antes. - Apagar trilha → remove vínculos em
trilha_cursos(cursos permanecem).