39 KiB
📐 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:
- ✅ FASE B validada: Los 15 temas críticos confirmados
- ✅ Separación de capas: Operativa, Formativa, Referencia (NUNCA mezclar)
- ✅ Versionado completo: Todo contenido es versionable y reversible
- ✅ Trazabilidad total: Auditoría completa de todas las acciones
- ✅ Validación clínica: Workflow de validación con roles TES/Medico/Formador
- ✅ Offline-first: Todo debe funcionar sin conexión
- ✅ 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'):
{
"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'):
{
"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'):
{
"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'):
{
"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'):
{
"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_guideyguide_to_protocolson siempre bidireccionales - Las relaciones
protocol_to_manualyguide_to_manualpueden 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 = trueporcontent_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 = truey 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:
{
"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
ContentRelationpermite relaciones bidireccionales- Campo
is_bidirectionalindica 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
-
Separación de Capas:
- Un ContentItem SOLO puede tener
functional_layerde UNA capa - El
contentJSON debe ser compatible con la capa
- Un ContentItem SOLO puede tener
-
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)
-
Versionado:
versiondebe seguir formato semántico:^\d+\.\d+\.\d+$- Solo una versión puede tener
is_active=trueporcontent_id
-
SCORM:
- Solo
type='guide'yfunctional_layer='formativa'pueden tener SCORM - Si
scorm_enabled=true, debe existirSCORMPackageasociado
- Solo
Validaciones de Relaciones
-
ContentRelation:
source_content_id != target_content_id(no auto-relaciones)- Si
is_bidirectional=true, debe existir relación inversa
-
ContentResourceRelation:
content_idyresource_iddeben existircontextdebe ser válido para el tipo de contenido
Validaciones de Workflow
- ValidationWorkflow:
current_statusdebe seguir el workflow definido- No se puede pasar de
publishedadraftdirectamente rejectedrequiererejection_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_atyupdated_aten 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