codigo0/docs/MODELO_DATOS_CANONICO_DEFINITIVO.md

1061 lines
39 KiB
Markdown
Raw Normal View History

2026-01-19 08:10:16 +00:00
# 📐 MODELO DE DATOS CANÓNICO DEFINITIVO
**Proyecto:** Guía TES - Sistema de Gestión de Contenido
**Fuente de Verdad:** FASE B - Matriz Maestra de Contenidos (15 temas críticos)
**Fecha:** 2025-01-06
**Arquitecto:** Sistema de Contenido
**Estado:** ✅ DEFINITIVO
---
## 🎯 PRINCIPIOS FUNDAMENTALES
Este modelo es la **ÚNICA FUENTE DE VERDAD** para el sistema de contenidos. Se basa en:
1.**FASE B validada:** Los 15 temas críticos confirmados
2.**Separación de capas:** Operativa, Formativa, Referencia (NUNCA mezclar)
3.**Versionado completo:** Todo contenido es versionable y reversible
4.**Trazabilidad total:** Auditoría completa de todas las acciones
5.**Validación clínica:** Workflow de validación con roles TES/Medico/Formador
6.**Offline-first:** Todo debe funcionar sin conexión
7.**SCORM-ready:** Guías formativas exportables a SCORM 1.2
---
## 📊 ENTIDADES PRINCIPALES
### 1. CONTENIDO (ContentItem)
**Definición:** Cualquier pieza de contenido del sistema: protocolos, guías, manual, fármacos, checklists.
**Regla de Oro:** Cada ContentItem pertenece a UNA SOLA capa funcional (operativa, formativa, referencia). NUNCA mezclar.
#### 1.1 Identificación y Clasificación
```
ContentItem {
// ============================================
// IDENTIFICACIÓN ÚNICA
// ============================================
id: UUID // ID único e inmutable (generado por BD)
slug: String // Identificador legible único (ej: "control-hemorragias-externas")
version: String // Versión semántica actual (ej: "1.2.3")
latest_version: String // Última versión disponible (ej: "1.2.3")
is_latest: Boolean // ¿Es la última versión? (derivado)
// ============================================
// CLASIFICACIÓN POR TIPO
// ============================================
type: Enum // 'protocol' | 'guide' | 'manual' | 'drug' | 'checklist'
// ============================================
// CLASIFICACIÓN CLÍNICA (FASE B)
// ============================================
// Basado en los 15 temas críticos de FASE B
clinical_topic: Enum // 'hemorragias_externas' | 'torniquetes' | 'convulsiones' |
// 'aspiracion_secreciones' | 'ventilacion_ambu' |
// 'reevaluacion_abcde' | 'valoracion_secundaria_sample' |
// 'shock_hipovolemico' | 'shock_septico' | 'ictus_completo' |
// 'trauma_politrauma' | 'tce' | 'alteracion_conciencia' |
// 'vademecum_tes' | 'preparacion_intubacion'
category: String // Categoría general (ej: "soporte_vital", "trauma", "farmacologia")
subcategory: String? // Subcategoría específica (ej: "hemorragia", "shock", "rcp")
// ============================================
// CAPA FUNCIONAL (CRÍTICO: NO MEZCLAR)
// ============================================
functional_layer: Enum // 'operativa' | 'formativa' | 'referencia'
// Regla: Un ContentItem SOLO puede tener contenido de UNA capa
// Ejemplo: Un protocolo es SIEMPRE operativa, una guía es SIEMPRE formativa
// ============================================
// METADATOS BÁSICOS
// ============================================
title: String // Título completo (ej: "Control de Hemorragias Externas")
short_title: String? // Título corto para UI (ej: "Hemorragias")
description: String // Descripción breve (1-2 líneas)
keywords: String[] // Palabras clave para búsqueda
// ============================================
// CONTENIDO ESPECÍFICO (JSON flexible)
// ============================================
content: JSON // Estructura específica por tipo y capa (ver secciones siguientes)
// ============================================
// PRIORIDAD Y CLASIFICACIÓN CLÍNICA
// ============================================
priority: Enum // 'critical' | 'high' | 'medium' | 'low'
clinical_priority: Enum? // Prioridad clínica específica (si aplica)
age_group: Enum? // 'adulto' | 'pediatrico' | 'neonatal' | 'todos'
// ============================================
// ESTADO Y VALIDACIÓN
// ============================================
status: Enum // 'draft' | 'in_review' | 'approved' | 'published' | 'archived'
// Workflow de validación (ver entidad ValidationWorkflow)
validation_status: Enum? // 'pending' | 'validated' | 'rejected'
validated_by: UUID? // Usuario que validó (TES, Médico, Formador)
validated_at: Timestamp? // Fecha de validación
validation_expires_at: Timestamp? // Fecha de expiración de validación
// ============================================
// RELACIONES CON OTRO CONTENIDO
// ============================================
// Relaciones bidireccionales (ver entidad ContentRelation)
related_content_ids: UUID[] // IDs de contenido relacionado (derivado de ContentRelation)
// Relaciones específicas por tipo (para facilitar queries)
related_protocol_ids: UUID[] // Si es guía, protocolos relacionados
related_guide_ids: UUID[] // Si es protocolo, guías relacionadas
related_manual_ids: UUID[] // Si es protocolo/guía, manual relacionado
// ============================================
// RECURSOS MULTIMEDIA ASOCIADOS
// ============================================
// Relaciones con recursos (ver entidad ContentResourceRelation)
media_resource_ids: UUID[] // IDs de recursos asociados (derivado)
// ============================================
// SCORM (Solo para guías formativas)
// ============================================
scorm_enabled: Boolean // ¿Tiene versión SCORM disponible?
scorm_package_id: UUID? // ID del paquete SCORM generado (ver entidad SCORMPackage)
scorm_version: String? // Versión SCORM (ej: "1.2")
// ============================================
// FUENTE Y TRAZABILIDAD
// ============================================
source_guideline: Enum // 'ERC' | 'SEMES' | 'AHA' | 'INTERNO' | 'MANUAL_TES_DIGITAL'
source_reference: String? // Referencia específica (ej: "BLOQUE_03_6_CONTROL_HEMORRAGIAS.md")
source_year: Integer? // Año de la fuente
source_url: String? // URL de la fuente (si aplica)
// ============================================
// METADATOS DE GESTIÓN
// ============================================
created_by: UUID // Usuario que creó
created_at: Timestamp // Fecha de creación
updated_by: UUID // Usuario que actualizó
updated_at: Timestamp // Fecha de última actualización
published_by: UUID? // Usuario que publicó
published_at: Timestamp? // Fecha de publicación (si está published)
// ============================================
// METADATOS ADICIONALES (JSON flexible)
// ============================================
metadata: JSON // {
// "tiempo_lectura": 15, // minutos
// "dificultad": "intermedio", // basico | intermedio | avanzado
// "checklist_mode": true, // Si tiene modo checklist
// "secciones_count": 8, // Para guías
// "steps_count": 10, // Para protocolos
// ...
// }
}
```
#### 1.2 Estructura de `content` por Tipo y Capa
**PROTOCOLO OPERATIVO (type='protocol', functional_layer='operativa'):**
```json
{
"steps": [
{
"order": 1,
"text": "Texto del paso",
"critical": false, // ¿Es paso crítico?
"verification": "¿Verificación opcional?",
"warnings": ["Advertencia 1"],
"media_resource_ids": ["uuid-imagen-paso"]
}
],
"warnings": ["Advertencia global 1"],
"key_points": ["Punto clave 1"],
"equipment": [
{
"name": "Equipo 1",
"required": true,
"media_resource_id": "uuid-imagen-equipo"
}
],
"drugs": ["Fármaco 1", "Fármaco 2"],
"checklist_mode": true, // ¿Tiene modo checklist?
"checklist_steps": [
{
"order": 1,
"text": "Paso checklist",
"critical": true
}
],
"category": "soporte_vital",
"subcategory": "rcp",
"age_group": "adulto"
}
```
**GUÍA FORMATIVA (type='guide', functional_layer='formativa'):**
```json
{
"secciones": [
{
"numero": 1,
"titulo": "Introducción y Contexto",
"archivo": "SECCION_01_NOMBRE.md",
"ruta": "/docs/consolidado/SECCION_01_NOMBRE.md",
"contenido_markdown": "...", // Contenido Markdown completo
"tiempo_estimado": 5, // minutos
"media_resource_ids": ["uuid-imagen-1", "uuid-video-1"]
}
],
"protocolo_operativo": {
"id": "uuid-protocolo",
"slug": "control-hemorragias",
"titulo": "Control de Hemorragias Externas",
"ruta": "/protocolos/control-hemorragias"
},
"scorm_available": true,
"tiempo_total_estimado": 40,
"objetivos_aprendizaje": [
"Objetivo 1",
"Objetivo 2"
]
}
```
**MANUAL REFERENCIA (type='manual', functional_layer='referencia'):**
```json
{
"parte": 1,
"parte_nombre": "Fundamentos y Evaluación Inicial",
"bloque": 0,
"bloque_nombre": "Fundamentos de Emergencias Prehospitalarias",
"capitulo": "1.1.1",
"ruta_archivo": "/manual/BLOQUE_XX/ARCHIVO.md",
"ruta_url": "/manual/parte-i/bloque-0/1.1.1",
"contenido_markdown": "...",
"nivel_dificultad": "basico", // basico | intermedio | avanzado
"importancia": "alta", // alta | media | baja
"tiempo_lectura": 15, // minutos
"navegacion": {
"anterior": "1.0.1",
"siguiente": "1.1.2",
"relacionados": ["1.1.2", "1.1.3"]
}
}
```
**FÁRMACO REFERENCIA (type='drug', functional_layer='referencia'):**
```json
{
"generic_name": "Nombre genérico",
"trade_name": "Nombre comercial",
"category": "cardiovascular",
"presentation": "Presentación",
"adult_dose": "Dosis adulto",
"pediatric_dose": "Dosis pediátrica",
"routes": ["IV", "IM"],
"dilution": "Dilución",
"indications": ["Indicación 1"],
"contraindications": ["Contraindicación 1"],
"side_effects": ["Efecto adverso 1"],
"antidote": "Antídoto",
"notes": ["Nota 1"],
"critical_points": ["Punto crítico TES 1"],
"source": "Fuente"
}
```
**CHECKLIST OPERATIVA (type='checklist', functional_layer='operativa'):**
```json
{
"phase": "pre_escena", // 'inicio_turno' | 'pre_escena' | 'post_servicio'
"sections": [
{
"id": "seccion-1",
"title": "Título sección",
"category": "oxigeno",
"items": [
{
"id": "item-1",
"text": "Texto del item",
"critical": false
}
],
"notes": "Notas opcionales"
}
]
}
```
---
### 2. RECURSOS MULTIMEDIA (MediaResource)
**Definición:** Imágenes, vídeos y otros recursos visuales asociados al contenido.
```
MediaResource {
// ============================================
// IDENTIFICACIÓN
// ============================================
id: UUID // ID único
filename: String // Nombre del archivo (ej: "hemorragia_presion_directa.jpg")
path: String // Ruta relativa en servidor (ej: "/storage/media/images/hemorragia/...")
url: String? // URL pública completa (si está en CDN)
thumbnail_url: String? // URL del thumbnail (para vídeos)
// ============================================
// TIPO Y FORMATO
// ============================================
type: Enum // 'image' | 'video' | 'audio' | 'document'
format: String // 'jpg' | 'png' | 'svg' | 'webp' | 'mp4' | 'webm' | 'pdf'
mime_type: String // 'image/jpeg', 'video/mp4', etc.
// ============================================
// METADATOS VISUALES
// ============================================
alt: String // Texto alternativo (accesibilidad - OBLIGATORIO)
caption: String? // Caption opcional
title: String? // Título del recurso
// ============================================
// CLASIFICACIÓN
// ============================================
functional_layer: Enum[] // ['operativa'] | ['formativa'] | ['referencia'] |
// ['operativa', 'formativa'] (puede usarse en múltiples capas)
clinical_topic: Enum? // Mismo enum que ContentItem (si aplica a tema específico)
category: String? // Categoría (ej: "hemorragia", "rcp")
tags: String[] // Tags para búsqueda
// ============================================
// DIMENSIONES Y TAMAÑO
// ============================================
width: Integer? // Ancho (para imágenes/vídeos)
height: Integer? // Alto (para imágenes/vídeos)
file_size: Integer // Tamaño en bytes
duration: Integer? // Duración en segundos (para vídeos/audio)
// ============================================
// PRIORIDAD Y USO
// ============================================
priority: Enum // 'critical' | 'high' | 'medium' | 'low'
usage_context: Enum[] // ['operativo'] | ['formativo'] | ['referencia'] |
// ['operativo', 'formativo'] (dónde se usa)
// ============================================
// FUENTE Y ATRIBUCIÓN
// ============================================
source: String? // Fuente del recurso
attribution: String? // Atribución si es necesario
license: String? // Licencia (ej: "CC BY 4.0", "Propietario")
// ============================================
// ESTADO
// ============================================
status: Enum // 'draft' | 'approved' | 'published' | 'archived'
// ============================================
// METADATOS DE GESTIÓN
// ============================================
uploaded_by: UUID // Usuario que subió
uploaded_at: Timestamp // Fecha de subida
updated_at: Timestamp // Fecha de última actualización
// ============================================
// METADATOS ADICIONALES
// ============================================
metadata: JSON // {
// "original_filename": "...",
// "compression": "lossy",
// "color_space": "RGB",
// ...
// }
}
```
---
### 3. RELACIONES CONTENIDO ⇄ RECURSOS (ContentResourceRelation)
**Definición:** Asociación entre contenido y recursos multimedia con contexto.
```
ContentResourceRelation {
id: UUID
content_id: UUID // FK a ContentItem
resource_id: UUID // FK a MediaResource
// ============================================
// CONTEXTO DE LA RELACIÓN
// ============================================
context: String? // Dónde se usa:
// - Para protocolos: "step-3", "checklist-item-5", "header"
// - Para guías: "seccion-2", "introduccion", "caso-clinico-1"
// - Para manual: "capitulo-1.1.1", "figura-3"
placement: Enum // 'inline' | 'before' | 'after' | 'modal' | 'sidebar'
order: Integer? // Orden de aparición (si hay múltiples en mismo contexto)
// ============================================
// PRIORIDAD Y CRITICIDAD
// ============================================
is_primary: Boolean // ¿Es recurso principal? (para destacar)
is_critical: Boolean // ¿Es crítico para comprensión?
priority: Enum // 'critical' | 'high' | 'medium' | 'low'
// ============================================
// METADATOS
// ============================================
caption_override: String? // Caption específico para este contexto (sobrescribe el del recurso)
created_at: Timestamp
created_by: UUID
}
```
---
### 4. RELACIONES CONTENIDO ⇄ CONTENIDO (ContentRelation)
**Definición:** Relaciones bidireccionales entre contenidos (Protocolo ↔ Guía ↔ Manual).
```
ContentRelation {
id: UUID
source_content_id: UUID // FK a ContentItem (origen)
target_content_id: UUID // FK a ContentItem (destino)
// ============================================
// TIPO DE RELACIÓN
// ============================================
relation_type: Enum // 'protocol_to_guide' | 'guide_to_protocol' |
// 'protocol_to_manual' | 'guide_to_manual' |
// 'manual_to_protocol' | 'manual_to_guide' |
// 'related' (contenido relacionado genérico)
// ============================================
// BIDIRECCIONALIDAD
// ============================================
is_bidirectional: Boolean // ¿Es bidireccional?
// Si true, se crea automáticamente la relación inversa
// ============================================
// METADATOS
// ============================================
order: Integer? // Orden de aparición (si hay múltiples relaciones)
label: String? // Etiqueta opcional (ej: "Guía formativa relacionada")
metadata: JSON? // {
// "description": "Relación entre protocolo y guía",
// "auto_generated": true,
// ...
// }
created_at: Timestamp
created_by: UUID
}
```
**Reglas:**
- Si `is_bidirectional = true`, el sistema crea automáticamente la relación inversa
- Las relaciones `protocol_to_guide` y `guide_to_protocol` son siempre bidireccionales
- Las relaciones `protocol_to_manual` y `guide_to_manual` pueden ser unidireccionales
---
### 5. VERSIONADO (ContentVersion)
**Definición:** Historial de versiones de contenido para versionado semántico y rollback.
```
ContentVersion {
id: UUID
content_id: UUID // FK a ContentItem (versión actual)
version: String // Versión semántica (ej: "1.2.3")
previous_version_id: UUID? // FK a ContentVersion (versión anterior)
// ============================================
// CONTENIDO DE LA VERSIÓN
// ============================================
content: JSON // Snapshot completo del contenido en esta versión
content_hash: String // Hash SHA-256 del contenido (para detectar cambios)
// ============================================
// CAMBIOS
// ============================================
change_summary: String // Resumen de cambios (ej: "Añadido paso de torniquete")
change_details: JSON // Detalles de cambios:
// {
// "added": ["step-5"],
// "modified": ["step-3"],
// "deleted": [],
// "fields_changed": ["title", "content.steps"]
// }
change_type: Enum // 'major' | 'minor' | 'patch'
// - major: Cambios incompatibles (ej: estructura diferente)
// - minor: Nuevas funcionalidades compatibles
// - patch: Correcciones compatibles
is_breaking: Boolean // ¿Es cambio incompatible? (derivado de change_type)
// ============================================
// METADATOS
// ============================================
created_at: Timestamp
created_by: UUID
published_at: Timestamp? // Fecha de publicación de esta versión
published_by: UUID? // Usuario que publicó esta versión
// ============================================
// ESTADO
// ============================================
is_active: Boolean // ¿Es la versión activa? (solo una por content_id)
}
```
**Reglas de Versionado:**
- Versionado semántico: `MAJOR.MINOR.PATCH`
- Cada cambio crea una nueva versión
- Solo una versión puede estar `is_active = true` por `content_id`
- Las versiones anteriores se mantienen para rollback
---
### 6. VALIDACIÓN Y WORKFLOW (ValidationWorkflow)
**Definición:** Workflow completo de validación clínica con historial.
```
ValidationWorkflow {
id: UUID
content_id: UUID // FK a ContentItem
// ============================================
// ESTADO ACTUAL
// ============================================
current_status: Enum // 'draft' | 'submitted' | 'in_review' |
// 'approved' | 'rejected' | 'published'
// ============================================
// HISTORIAL COMPLETO
// ============================================
history: JSON[] // Array de eventos ordenados:
// [
// {
// "status": "draft",
// "timestamp": "2025-01-06T10:00:00Z",
// "user_id": "uuid",
// "user_role": "editor_clinico",
// "notes": "Creación inicial"
// },
// {
// "status": "submitted",
// "timestamp": "2025-01-06T11:00:00Z",
// "user_id": "uuid",
// "user_role": "editor_clinico",
// "notes": "Enviado para revisión"
// },
// {
// "status": "in_review",
// "timestamp": "2025-01-06T12:00:00Z",
// "user_id": "uuid",
// "user_role": "tes_validador",
// "reviewer_id": "uuid",
// "notes": "En revisión por TES"
// },
// {
// "status": "approved",
// "timestamp": "2025-01-06T14:00:00Z",
// "user_id": "uuid",
// "user_role": "tes_validador",
// "reviewer_id": "uuid",
// "notes": "Aprobado para publicación"
// }
// ]
// ============================================
// EVENTOS ESPECÍFICOS (para queries rápidas)
// ============================================
submitted_at: Timestamp?
submitted_by: UUID?
reviewed_at: Timestamp?
reviewed_by: UUID?
reviewer_role: Enum? // 'tes_validador' | 'medico' | 'formador'
approved_at: Timestamp?
approved_by: UUID?
rejected_at: Timestamp?
rejected_by: UUID?
rejection_reason: String?
// ============================================
// VALIDACIÓN CLÍNICA
// ============================================
clinical_validation: JSON? // {
// "validated_by_tes": true,
// "validated_by_medico": false,
// "validated_by_formador": true,
// "validation_notes": "...",
// "compliance_checklist": {
// "protocolo_correcto": true,
// "recursos_asociados": true,
// "relaciones_correctas": true
// }
// }
// ============================================
// METADATOS
// ============================================
updated_at: Timestamp
}
```
**Workflow de Estados:**
```
draft → submitted → in_review → approved → published
↓ ↓ ↓
rejected rejected rejected
```
---
### 7. SCORM (SCORMPackage)
**Definición:** Paquetes SCORM generados desde guías formativas.
```
SCORMPackage {
id: UUID
content_id: UUID // FK a ContentItem (type='guide', functional_layer='formativa')
// ============================================
// IDENTIFICACIÓN SCORM
// ============================================
scorm_version: String // '1.2' (versión SCORM soportada)
package_id: String // ID único del paquete SCORM
title: String // Título del paquete
// ============================================
// ARCHIVO
// ============================================
package_path: String // Ruta al archivo ZIP del paquete
package_url: String? // URL pública del paquete
package_size: Integer // Tamaño en bytes
package_hash: String // Hash SHA-256 del paquete
// ============================================
// METADATOS SCORM
// ============================================
metadata: JSON // {
// "organization": "TES Digital",
// "identifier": "uuid",
// "title": "Guía de Refuerzo: RCP Adulto SVB",
// "description": "...",
// "mastery_score": 80,
// "time_limit_action": "exit,message",
// "launch_data": "...",
// "max_time_allowed": "PT40M",
// "prerequisites": "..."
// }
// ============================================
// ESTADO
// ============================================
status: Enum // 'generating' | 'ready' | 'error' | 'archived'
generation_error: String? // Mensaje de error si falló la generación
// ============================================
// METADATOS DE GESTIÓN
// ============================================
generated_at: Timestamp
generated_by: UUID
updated_at: Timestamp
}
```
**Reglas:**
- Solo guías formativas pueden tener paquetes SCORM
- Un ContentItem puede tener múltiples versiones SCORM (una por versión del contenido)
- El paquete se genera automáticamente cuando `scorm_enabled = true` y el contenido está `published`
---
### 8. USUARIOS Y ROLES (User)
**Definición:** Usuarios del panel de administración con roles y permisos granulares.
```
User {
id: UUID
email: String // Email único
username: String // Nombre de usuario
password_hash: String // Hash de contraseña (bcrypt)
// ============================================
// ROLES
// ============================================
role: Enum // 'super_admin' | 'admin' |
// 'editor_clinico' | 'editor_formativo' |
// 'revisor' | 'viewer' |
// 'tes_validador' | 'formador' | 'medico'
// ============================================
// PERMISOS GRANULARES (JSON)
// ============================================
permissions: JSON // {
// "content": {
// "create": true,
// "edit": true,
// "delete": false,
// "publish": false,
// "validate": true
// },
// "media": {
// "upload": true,
// "delete": false
// },
// "scorm": {
// "generate": true,
// "export": true
// },
// "validation": {
// "approve": true,
// "reject": true
// },
// "admin": {
// "manage_users": false,
// "manage_roles": false
// }
// }
// ============================================
// ESTADO
// ============================================
is_active: Boolean
last_login: Timestamp?
// ============================================
// METADATOS
// ============================================
created_at: Timestamp
updated_at: Timestamp
}
```
**Roles y Permisos por Defecto:**
| Rol | Descripción | Permisos Clave |
|-----|-------------|----------------|
| `super_admin` | Administrador total | Todos los permisos |
| `admin` | Administrador | Gestión de contenido y usuarios |
| `editor_clinico` | Editor contenido clínico | Crear/editar protocolos, fármacos |
| `editor_formativo` | Editor contenido formativo | Crear/editar guías formativas |
| `revisor` | Revisor general | Revisar y aprobar contenido |
| `tes_validador` | Validador TES | Validar contenido operativo |
| `medico` | Médico validador | Validar contenido clínico |
| `formador` | Formador | Validar contenido formativo |
| `viewer` | Solo lectura | Ver contenido |
---
### 9. AUDITORÍA (AuditLog)
**Definición:** Registro completo de todas las acciones del sistema.
```
AuditLog {
id: UUID
entity_type: String // 'content_item' | 'media_resource' |
// 'content_relation' | 'content_version' |
// 'scorm_package' | 'user' | 'validation_workflow'
entity_id: UUID // ID de la entidad afectada
// ============================================
// ACCIÓN
// ============================================
action: Enum // 'create' | 'update' | 'delete' |
// 'publish' | 'unpublish' | 'archive' |
// 'approve' | 'reject' | 'submit' |
// 'validate' | 'generate_scorm' |
// 'upload_media' | 'delete_media'
action_details: JSON? // Detalles específicos de la acción:
// {
// "fields_changed": ["title", "content"],
// "old_value": {...},
// "new_value": {...},
// "reason": "..."
// }
// ============================================
// USUARIO Y CONTEXTO
// ============================================
user_id: UUID // FK a User
user_role: String // Rol del usuario (snapshot al momento de la acción)
ip_address: String? // IP desde la que se realizó la acción
user_agent: String? // User agent del navegador
// ============================================
// METADATOS
// ============================================
timestamp: Timestamp // Fecha y hora exacta
metadata: JSON? // Metadatos adicionales:
// {
// "session_id": "...",
// "request_id": "...",
// "duration_ms": 150,
// ...
// }
}
```
**Reglas:**
- Todas las acciones importantes se registran
- Los logs nunca se eliminan (solo se archivan)
- Los logs son inmutables (no se pueden modificar)
---
## 🔗 RELACIONES ENTRE ENTIDADES
```
ContentItem (1) ──< (N) ContentResourceRelation (N) >── (1) MediaResource
ContentItem (1) ──< (N) ContentRelation (N) >── (1) ContentItem
ContentItem (1) ──< (N) ContentVersion
ContentItem (1) ──< (1) ValidationWorkflow
ContentItem (1) ──< (N) SCORMPackage
User (1) ──< (N) ContentItem (created_by, updated_by, published_by)
User (1) ──< (N) MediaResource (uploaded_by)
User (1) ──< (N) AuditLog
```
---
## 📦 EXPORTACIÓN A CONTENT PACK JSON
El Content Pack se genera desde estas entidades siguiendo esta estructura:
```json
{
"metadata": {
"version": "1.0.0",
"generated_at": "2025-01-06T12:00:00Z",
"hash": "sha256:...",
"total_items": 150,
"total_resources": 200,
"source": "server"
},
"content": {
"protocols": [
// ContentItem donde:
// type='protocol' AND functional_layer='operativa' AND status='published'
// Incluye: id, slug, title, short_title, description, content, priority, etc.
],
"guides": [
// ContentItem donde:
// type='guide' AND functional_layer='formativa' AND status='published'
],
"drugs": [
// ContentItem donde:
// type='drug' AND functional_layer='referencia' AND status='published'
],
"checklists": [
// ContentItem donde:
// type='checklist' AND functional_layer='operativa' AND status='published'
],
"manuals": [
// ContentItem donde:
// type='manual' AND functional_layer='referencia' AND status='published'
]
},
"media": {
"images": [
// MediaResource donde type='image' AND status='published'
// Incluye: id, filename, path, url, alt, caption, etc.
],
"videos": [
// MediaResource donde type='video' AND status='published'
]
},
"relations": {
"content_to_content": [
// ContentRelation donde ambos contenidos están published
// Incluye: source_content_id, target_content_id, relation_type, is_bidirectional
],
"content_to_media": [
// ContentResourceRelation donde ambos están published
// Incluye: content_id, resource_id, context, order, is_primary
]
}
}
```
**Reglas del Content Pack:**
- Solo incluye contenido con `status='published'`
- Solo incluye la última versión (`is_latest=true`)
- Las relaciones están incluidas explícitamente
- Los recursos multimedia tienen URLs relativas o absolutas
- El hash se calcula sobre `content` + `media` + `relations`
---
## 🎯 PRINCIPIOS DE DISEÑO
### 1. Separación de Capas Funcionales
**CRÍTICO:** El campo `functional_layer` garantiza que:
- ✅ Contenido operativo NO se mezcle con formativo
- ✅ Contenido formativo NO se mezcle con referencia
- ✅ Cada capa tiene su propia estructura de `content`
- ✅ Las relaciones entre capas están explícitas
### 2. Versionado Semántico
- **Major (X.0.0):** Cambios incompatibles (estructura diferente)
- **Minor (0.X.0):** Nuevas funcionalidades compatibles
- **Patch (0.0.X):** Correcciones compatibles
### 3. Estados y Workflow
```
draft → submitted → in_review → approved → published
↓ ↓ ↓
rejected rejected rejected
```
### 4. Trazabilidad Completa
- Todo cambio queda registrado en `AuditLog`
- Historial de versiones en `ContentVersion`
- Workflow de validación en `ValidationWorkflow`
### 5. Relaciones Bidireccionales
- `ContentRelation` permite relaciones bidireccionales
- Campo `is_bidirectional` indica si la relación es simétrica
- El Content Pack incluye todas las relaciones
### 6. Offline-First
- El Content Pack es autocontenido (incluye todo lo necesario)
- Las relaciones están incluidas en el pack
- Los recursos multimedia tienen URLs relativas o absolutas
- El sistema funciona sin conexión usando cache
### 7. SCORM-Ready
- Solo guías formativas pueden tener SCORM
- El paquete SCORM se genera automáticamente
- Metadatos SCORM completos en `SCORMPackage`
---
## ✅ VALIDACIONES Y CONSTRAINTS
### Validaciones de Contenido
1. **Separación de Capas:**
- Un ContentItem SOLO puede tener `functional_layer` de UNA capa
- El `content` JSON debe ser compatible con la capa
2. **Tipo y Capa:**
- `type='protocol'``functional_layer='operativa'` (SIEMPRE)
- `type='guide'``functional_layer='formativa'` (SIEMPRE)
- `type='drug'``functional_layer='referencia'` (SIEMPRE)
- `type='manual'``functional_layer='referencia'` (SIEMPRE)
- `type='checklist'``functional_layer='operativa'` (SIEMPRE)
3. **Versionado:**
- `version` debe seguir formato semántico: `^\d+\.\d+\.\d+$`
- Solo una versión puede tener `is_active=true` por `content_id`
4. **SCORM:**
- Solo `type='guide'` y `functional_layer='formativa'` pueden tener SCORM
- Si `scorm_enabled=true`, debe existir `SCORMPackage` asociado
### Validaciones de Relaciones
1. **ContentRelation:**
- `source_content_id != target_content_id` (no auto-relaciones)
- Si `is_bidirectional=true`, debe existir relación inversa
2. **ContentResourceRelation:**
- `content_id` y `resource_id` deben existir
- `context` debe ser válido para el tipo de contenido
### Validaciones de Workflow
1. **ValidationWorkflow:**
- `current_status` debe seguir el workflow definido
- No se puede pasar de `published` a `draft` directamente
- `rejected` requiere `rejection_reason`
---
## 📝 NOTAS TÉCNICAS
### Campos JSON Flexibles
Los campos `content` y `metadata` son JSON para permitir:
- ✅ Estructuras específicas por tipo de contenido
- ✅ Extensibilidad sin cambios de esquema
- ✅ Metadatos adicionales sin migraciones
### UUIDs vs IDs Numéricos
- **UUIDs** para IDs principales (mejor para distribución, sin colisiones)
- **Strings** para slugs (legibles, SEO-friendly)
### Timestamps
- Todos los timestamps en formato ISO 8601
- Timezone: UTC
- Incluir `created_at` y `updated_at` en todas las entidades principales
### Soft Deletes
- No eliminar físicamente contenido publicado
- Usar campo `status='archived'` para "eliminación"
- Mantener historial completo
---
## 🚀 COMPATIBILIDAD
### Panel de Administración
- ✅ CRUD completo para todas las entidades
- ✅ Editor visual para cada tipo de contenido
- ✅ Gestor de recursos multimedia
- ✅ Sistema de validación con workflow
- ✅ Generación de SCORM
- ✅ Exportación de Content Pack
### Content Pack JSON
- ✅ Estructura autocontenida
- ✅ Incluye relaciones explícitas
- ✅ URLs relativas/absolutas para recursos
- ✅ Hash para verificación de integridad
### Uso Offline
- ✅ Content Pack descargable y cacheable
- ✅ Funciona sin conexión
- ✅ Fallback a datos locales garantizado
### SCORM
- ✅ Generación automática desde guías formativas
- ✅ Metadatos SCORM 1.2 completos
- ✅ Exportación como ZIP
---
**Última actualización:** 2025-01-06
**Estado:** ✅ MODELO CANÓNICO DEFINITIVO - FUENTE DE VERDAD