You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
web_Admision/temporal_revision_tecnica.md

29 KiB

ANÁLISIS TÉCNICO EXHAUSTIVO - PROYECTO DIRECCIÓN DE ADMISIÓN 2026

Generado: 2026-04-08


1. ESTRUCTURA GENERAL DEL PROYECTO

/direccion_de_admision_2026/
├── back/                          # Backend Laravel 12
│   ├── app/
│   │   ├── Models/               # 21 modelos Eloquent
│   │   ├── Http/Controllers/     # Controladores REST
│   │   └── Services/             # Lógica de negocio
│   ├── database/
│   │   ├── migrations/           # 9 migraciones
│   │   └── seeders/
│   ├── routes/api.php            # Definición de endpoints API
│   ├── config/                   # Configuraciones
│   ├── composer.json             # Dependencias PHP
│   └── Dockerfile               # Multi-stage build
├── front/                         # Frontend Vue 3 + Vite
│   ├── src/
│   │   ├── views/               # Vistas por rol (admin, postulante)
│   │   ├── components/          # Componentes reutilizables
│   │   ├── store/               # 14 stores Pinia
│   │   ├── router/index.js      # Rutas y guards
│   │   └── axios.js             # Instancia HTTP autenticada
│   ├── package.json
│   ├── vite.config.js
│   └── Dockerfile
├── nginx/                         # Configuración Nginx
├── mysql/                         # Configuración MySQL
├── docker-compose.prod.yml        # Orquestación producción
├── docker-compose.yml             # Desarrollo local
└── .github/workflows/            # CI/CD

2. STACK TÉCNICO - VERSIONES EXACTAS

Backend (Laravel 12)

Librería Versión Propósito
laravel/framework ^12.0 Framework principal
laravel/sanctum ^4.2 API tokens + autenticación
spatie/laravel-permission ^6.24 Gestión de roles/permisos
simplesoftwareio/simple-qrcode ^4.2 Generación códigos QR
laravel/tinker ^2.10.1 REPL para desarrollo

PHP: ^8.2 (Dockerfile especifica 8.4-fpm-alpine)
Base de datos: MySQL 8.0
ORM: Eloquent (nativo)
Autenticación: Laravel Sanctum (tokens bearer)

Frontend (Vue 3)

Librería Versión Propósito
vue ^3.5.24 Framework frontend
vue-router ^4.6.4 Enrutamiento SPA
pinia ^3.0.4 State management
ant-design-vue ^4.2.6 Componentes UI
axios ^1.13.3 Cliente HTTP
chart.js ^4.5.1 Gráficos
vue-chartjs ^5.3.3 Binding Vue para Chart.js
@vueup/vue-quill ^1.2.0 Editor WYSIWYG
marked ^17.0.1 Parser markdown
markdown-it ^14.1.0 Markdown avanzado
dayjs ^1.11.19 Utilidades fecha/hora
vue-qrcode ^2.2.2 Generador QR
katex ^0.16.28 Renderizado LaTeX

Build Tool: Vite 7.2.4
Package Manager: npm


3. BACKEND - ARQUITECTURA LARAVEL 12

3.1 Modelos y Relaciones (21 modelos)

Modelos Núcleo de Admisión

Modelo Tabla Relaciones Clave Propósito
ProcesoAdmision procesos_admision hasMany(ProcesoAdmisionDetalle), hasMany(ProcesoAdmisionResultadoArchivo), hasMany(ResultadoAdmision) Gestiona procesos de admisión (convocatorias)
ProcesoAdmisionDetalle proceso_admision_detalles belongsTo(ProcesoAdmision) Detalles informativos de procesos (requisitos, cronograma, etc.)
ProcesoAdmisionResultadoArchivo proceso_admision_resultado_archivos belongsTo(ProcesoAdmision) Archivos de resultados organizados por slot/sedes
ResultadoAdmision resultados_admision belongsTo(ProcesoAdmision), belongsTo(AreaAdmision) Resultados finales de admisión de postulantes
Proceso procesos belongsTo(Calificacion), belongsToMany(Area, via area_proceso) Procesos de examen asociados a áreas
Area areas belongsToMany(Curso), belongsToMany(Examen), belongsToMany(Proceso) Áreas temáticas (Biomedicas, Ingeniería)
Curso cursos belongsToMany(Area), hasMany(Pregunta) Cursos dentro de áreas (Matemática, Comunicación)
Pregunta preguntas belongsTo(Curso) Preguntas de examen con opciones múltiples
PreguntaAsignada preguntas_asignadas belongsTo(Examen) Asociación pregunta-examen

Modelos de Usuarios

Modelo Tabla Relaciones Propósito
User users belongsToMany(Academia), hasMany(IntentoExamen) Administradores/Staff (usa Spatie roles)
Postulante postulantes hasMany(Examen) Postulantes que rinden exámenes
AreaAdmision areas_admision hasMany(ResultadoAdmision) Áreas en contexto de admisión (diferente a Area de exámenes)

Modelos de Evaluación

Modelo Tabla Relaciones Propósito
Examen examenes belongsTo(Postulante), belongsTo(AreaProceso), belongsTo(Pago), hasMany(PreguntaAsignada) Examen renderizado por postulante
Calificacion calificaciones hasMany(Proceso) Esquemas de puntuación (puntos por correcta, incorrecta, nula)
ResultadoExamen resultados_examenes Resultados parciales de exámenes
Pago pagos belongsTo(Postulante) Pagos de procesos que requieren arancel

Modelos de Contenido

Modelo Tabla Relaciones Propósito
Noticia noticias Standalone (SoftDeletes) Noticias publicables en web pública
Comunicado comunicados hasMany(ComunicadoImagen) Comunicados activos con imágenes
ComunicadoImagen comunicado_imagenes belongsTo(Comunicado) Imágenes asociadas a comunicados

Modelos Intermedios y Apoyo

Modelo Tabla Propósito
ReglaAreaProceso reglas_area_proceso Define reglas de asignación de preguntas por área-proceso
ResultadoAdmisionCarga resultados_admision_carga Almacena detalles de cursos en resultados de admisión

3.2 Esquema de Base de Datos (30+ tablas)

areas                              -- Catálogo de áreas (Biomedicas, Ingenierías)
cursos                             -- Catálogo de cursos (Matemática, Comunicación)
area_curso                         -- M2M: área-curso
procesos                           -- Procesos de examen
area_proceso                       -- M2M: área-proceso (pivot con ID)
preguntas                          -- Preguntas de examen
preguntas_asignadas               -- Preguntas asignadas a examen del postulante
examenes                           -- Exámenes rendidos
postulantes                        -- Usuarios que rinden exámenes
pagos                              -- Control de pagos
resultados_examenes               -- Resultados de exámenes

procesos_admision                 -- Procesos de admisión (convocatorias)
proceso_admision_detalles         -- Detalles de procesos (requisitos, cronograma)
proceso_admision_resultado_archivos -- Archivos de resultados por sede/slot
resultados_admision               -- Resultados de admisión
areas_admision                    -- Áreas de admisión

noticias                          -- Artículos publicables
comunicados                       -- Comunicados activos
comunicado_imagenes               -- Imágenes en comunicados

users                             -- Administradores
roles, permissions                -- Spatie permission tables
model_has_roles, model_has_permissions
personal_access_tokens            -- Tokens Sanctum

Restricciones de Integridad:

  • ON DELETE CASCADE en relaciones examenes → postulantes, preguntas_asignadas → examenes
  • UNIQUE CONSTRAINTS en area_proceso(area_id, proceso_id) y area_curso
  • UNIQUE KEY en proceso_admision_resultado_archivos(proceso_admision_id, orden)

4. API REST - ENDPOINTS COMPLETOS (73 rutas)

Autenticación Admin

POST   /api/register
POST   /api/login
POST   /api/logout                         [auth:sanctum]
GET    /api/me                             [auth:sanctum]
POST   /api/refresh-token                  [auth:sanctum]
GET    /api/user                           [auth:sanctum]

Autenticación Postulante

POST   /api/postulante/register
POST   /api/postulante/login
POST   /api/postulante/logout              [auth:sanctum]
GET    /api/postulante/me                  [auth:sanctum]
GET    /api/postulante/pagos               [auth:sanctum]
GET    /api/postulante/mis-procesos        [auth:sanctum]
GET    /api/postulante/observacion         [auth:sanctum]
GET    /api/mis-procesos/{id}/avance       [auth:sanctum]

Procesos de Examen

GET    /api/procesos                       [auth:sanctum]
POST   /api/procesos                       [auth:sanctum]
GET    /api/procesos/{id}                  [auth:sanctum]
PUT    /api/procesos/{id}                  [auth:sanctum]
PATCH  /api/procesos/{id}/toggle-activo    [auth:sanctum]
DELETE /api/procesos/{id}                  [auth:sanctum]

Admin - Áreas

GET    /api/admin/areas                              [auth:sanctum]
POST   /api/admin/areas                              [auth:sanctum]
GET    /api/admin/areas/{id}                         [auth:sanctum]
PUT    /api/admin/areas/{id}                         [auth:sanctum]
DELETE /api/admin/areas/{id}                         [auth:sanctum]
PATCH  /api/admin/areas/{id}/toggle                  [auth:sanctum]
POST   /api/admin/areas/{area}/vincular-cursos       [auth:sanctum]
POST   /api/admin/areas/{area}/desvincular-curso     [auth:sanctum]
GET    /api/admin/areas/{area}/cursos-disponibles    [auth:sanctum]
POST   /api/admin/areas/{area}/vincular-procesos     [auth:sanctum]
GET    /api/admin/areas/{area}/procesos-disponibles  [auth:sanctum]
POST   /api/admin/areas/{area}/desvincular-procesos  [auth:sanctum]

Admin - Cursos

GET    /api/admin/cursos                   [auth:sanctum]
POST   /api/admin/cursos                   [auth:sanctum]
GET    /api/admin/cursos/{id}              [auth:sanctum]
PUT    /api/admin/cursos/{id}              [auth:sanctum]
DELETE /api/admin/cursos/{id}              [auth:sanctum]
PATCH  /api/admin/cursos/{id}/toggle       [auth:sanctum]

Admin - Preguntas

GET    /api/admin/cursos/{id}/preguntas    [auth:sanctum]
POST   /api/admin/preguntas               [auth:sanctum]
GET    /api/admin/preguntas/{id}          [auth:sanctum]
PUT    /api/admin/preguntas/{id}          [auth:sanctum]
DELETE /api/admin/preguntas/{id}          [auth:sanctum]

Admin - Noticias

GET    /api/admin/noticias               [auth:sanctum]
GET    /api/admin/noticias/{id}          [auth:sanctum]
POST   /api/admin/noticias               [auth:sanctum]
PUT    /api/admin/noticias/{id}          [auth:sanctum]
DELETE /api/admin/noticias/{id}          [auth:sanctum]

Admin - Procesos de Admisión

GET       /api/admin/procesos-admision              [auth:sanctum]
POST      /api/admin/procesos-admision              [auth:sanctum]
GET       /api/admin/procesos-admision/{id}         [auth:sanctum]
PUT/PATCH /api/admin/procesos-admision/{id}         [auth:sanctum]
DELETE    /api/admin/procesos-admision/{id}         [auth:sanctum]

GET       /api/admin/procesos-admision/{id}/detalles  [auth:sanctum]
POST      /api/admin/procesos-admision/{id}/detalles  [auth:sanctum]
GET       /api/admin/detalles-admision/{id}           [auth:sanctum]
PUT/PATCH /api/admin/detalles-admision/{id}           [auth:sanctum]
DELETE    /api/admin/detalles-admision/{id}           [auth:sanctum]

Admin - Resultados (Archivos)

GET    /api/admin/proceso-resultado/{id}/archivos   [auth:sanctum]
POST   /api/admin/proceso-resultado/{id}/archivos   [auth:sanctum]
DELETE /api/admin/proceso-resultado/archivos/{id}   [auth:sanctum]
GET    /api/proceso-resultado/{id}/archivos         [público]

Admin - Comunicados

GET       /api/admin/comunicados                     [auth:sanctum]
POST      /api/admin/comunicados                     [auth:sanctum]
PUT/PATCH /api/admin/comunicados/{id}               [auth:sanctum]
DELETE    /api/admin/comunicados/{id}               [auth:sanctum]
PATCH     /api/admin/comunicados/{id}/toggle-activo [auth:sanctum]
DELETE    /api/admin/comunicados/imagenes/{id}      [auth:sanctum]

Examen Online

GET    /api/examen/procesos                        [auth:sanctum]
GET    /api/examen/areas                           [auth:sanctum]
GET    /api/examen/actual                          [auth:sanctum]
POST   /api/examen/crear                           [auth:sanctum]
POST   /api/examen/{id}/generar-preguntas          [auth:sanctum]
GET    /api/examen/{id}/preguntas                  [auth:sanctum]
POST   /api/examen/iniciar                         [auth:sanctum]
POST   /api/examen/pregunta/{id}/responder         [auth:sanctum]
POST   /api/examen/{id}/finalizar                  [auth:sanctum]
POST   /api/examen/{id}/calificar                  [auth:sanctum]

Reglas de Examen

GET    /api/area-proceso/areasprocesos             [auth:sanctum]
GET    /api/area-proceso/{id}/reglas               [auth:sanctum]
POST   /api/area-proceso/{id}/reglas               [auth:sanctum]
POST   /api/area-proceso/{id}/reglas/multiple      [auth:sanctum]
PUT    /api/reglas/{id}                            [auth:sanctum]
DELETE /api/reglas/{id}                            [auth:sanctum]

Calificaciones

GET    /api/calificaciones                         [auth:sanctum]
POST   /api/calificaciones                         [auth:sanctum]
GET    /api/calificaciones/{id}                    [auth:sanctum]
PUT    /api/calificaciones/{id}                    [auth:sanctum]
DELETE /api/calificaciones/{id}                    [auth:sanctum]

Postulantes (Admin)

GET    /api/admin/postulantes                      [auth:sanctum]
PUT    /api/admin/postulantes/{id}                 [auth:sanctum]

Web Pública

GET    /api/procesos-admision                                     [público]
GET    /api/noticias                                              [público]
GET    /api/noticias/{slug}                                       [público]
GET    /api/comunicados/activo                                    [público]
GET    /api/procesos-disponibles-preinscripcion                   [auth:sanctum]

5. AUTENTICACIÓN Y AUTORIZACIÓN

Flujo Admin

  1. POST /api/login con email/password
  2. AuthController verifica credenciales, revoca tokens previos
  3. Genera token con createToken('api_token', ['*'], now()->addHours(12))
  4. Devuelve token + user + roles/permisos (Spatie)

Flujo Postulante

  1. POST /api/postulante/login
  2. Token con expiración 1 hora
  3. Device-Id único para detección multi-dispositivo
  4. Guard separado: postulante_token en localStorage

Roles (Spatie Permission)

  • administrador — acceso a panel admin
  • superadmin — acceso extendido
  • Permisos granulares via model_has_permissions

6. FRONTEND - ARQUITECTURA VUE 3

6.1 Rutas (23 rutas principales)

/ (público)                         → WebPage (SPA pública)
/login-postulante                   → LoginView
/proceso-resultado                  → ProcesoResultado
/modalidades/cepreuna               → Cepreuna
/modalidades/extraordinario         → Extraordinario
/modalidades/general                → General

/portal-postulante (requiresAuth)
  ├─ /                              → Dashboard postulante
  ├─ /test                          → Selector proceso/área
  ├─ /examen/:examenId              → PreguntasExamen (examen online)
  ├─ /resultados/:examenId          → Resultados (calificaciones)
  ├─ /mis-procesos                  → MisProcesos (admisión)
  └─ /mis-procesos-estado           → AvanceProceso

/admin/dashboard (requiresAuth + role:administrador)
  ├─ /                              → Dashboard admin
  ├─ /areas                         → AreasList (CRUD)
  ├─ /cursos                        → CursosList (CRUD)
  ├─ /cursos/:id/preguntas          → PreguntasCursoView
  ├─ /procesos                      → ProcesosList (CRUD)
  ├─ /reglas                        → ReglasList (CRUD)
  ├─ /procesos-admision             → ProcesosAdmisionList (CRUD)
  ├─ /procesos/:id/detalles         → ProcesoAdmisionDetalles
  ├─ /lista-calificacion            → CalificacionTest
  ├─ /lista-postulantes             → ListPostulantes
  ├─ /noticias                      → NoticiasAdmin (CRUD)
  └─ /comunicados                   → ComunicadosAdmin (CRUD)

/superadmin/dashboard               → Dashboard superadmin
/unauthorized                       → 403
/:pathMatch(.*)                     → 404

Guards:

  • requiresAuth — verifica token en localStorage, redirige a login
  • guest — evita acceso a login si ya autenticado
  • role — valida rol, redirige a /403 si no aplica

6.2 Stores Pinia (14 stores)

Store API Propósito principal
user.js Options Auth admin, roles, persistencia localStorage
postulanteStore.js Composition Auth postulante, Device-Id
examen.store.js Options Flujo completo de examen online
procesosAdmisionStore.js Options CRUD procesos admisión (admin)
area.store.js Options CRUD áreas
curso.store.js Options CRUD cursos
pregunta.store.js Options CRUD preguntas
proceso.store.js Options CRUD procesos examen
reglaAreaProceso.store.js Options Reglas de asignación de preguntas
noticiasStore.js Options CRUD noticias (admin)
noticiasPublicas.store.js Options Noticias para web pública
procesoAdmisionResultado.store.js Options Upload/fetch archivos de resultados
comunicadosStore.js Options CRUD comunicados
web.js Options Estado web pública (procesos, comunicado activo)

6.3 Instancias Axios

axios.js (Admin):

  • baseURL: VITE_API_URL
  • Request interceptor: inyecta Bearer ${localStorage.token}
  • Response interceptor: 401 → clearAuth + redirect login; 403 → redirect /unauthorized

axiosPostulante.js (Postulante):

  • Token en postulante_token
  • Header Device-Id opcional

6.4 Componentes Principales

Componente Líneas aprox. Función
ConvocatoriasSection.vue ~1,457 Tarjetas de procesos, modal detalles, archivos multi-sede
PreguntasExamen.vue ~598 Examen online con timer, 1 pregunta/vez, Markdown+LaTeX
ProcessSection.vue ~365 Timeline visual del cronograma
ComunicadoModal.vue ~207 Modal carrusel de imágenes, auto-cierre por fecha
HeroSection.vue Banner con card dinámica (v-if hayResultados)
ProcesoResultado.vue Sección pública por proceso con archivos v-for
ProcesosAdmisionList.vue Tabla admin con modales inline para CRUD + Resultados

7. FLUJOS FUNCIONALES

7.1 Gestión de Procesos de Admisión

Admin crea ProcesoAdmision
  ├─ título, descripción, slug único
  ├─ Fechas: pre-inscripción → inscripción → examen → resultados
  ├─ Imágenes: imagen (JPG/PNG), banner, brochure PDF
  ├─ Links externos: pre-inscripción, inscripción, resultados, reglamento
  ├─ Estados: nuevo → publicado → en_proceso → finalizado → cancelado
  └─ ProcesoAdmisionDetalle: requisitos, cronograma, etc.

Postulante visualiza en página pública
  └─ GET /api/procesos-admision (solo publicados, latest first)
      └─ ConvocatoriasSection: tarjetas + modal detalles

Admin gestiona resultados
  └─ ProcesoAdmisionResultadoArchivo por sede/slot
      ├─ Upload PDF por slot (1-6) por proceso
      └─ Descarga pública: GET /api/proceso-resultado/{id}/archivos

7.2 Examen Online (Flujo Completo)

1. Login postulante → Dashboard (procesos disponibles donde no ha rendido)
2. GET /api/examen/procesos → seleccionar proceso
3. GET /api/examen/areas?proceso_id={id} → seleccionar área
4. POST /api/examen/crear { area_proceso_id } → crea Examen (estado: pendiente)
5. POST /api/examen/{id}/generar-preguntas → selección aleatoria por ReglaAreaProceso
6. GET  /api/examen/{id}/preguntas → lista sin respuesta_correcta visible
7. POST /api/examen/iniciar → hora_inicio = NOW, estado = en_curso
8. POST /api/examen/pregunta/{id}/responder { respuesta } → valida + puntúa (Calificacion)
9. POST /api/examen/{id}/finalizar → estado = finalizado, suma puntaje
10. POST /api/examen/{id}/calificar → computa estadísticas:
    ├─ total_correctas / incorrectas / nulas
    ├─ total_puntos, porcentaje_correctas
    ├─ calificacion_sobre_20
    └─ correctas_por_curso (breakdown)
11. Resultados.vue: gráficos Chart.js por curso + ranking

Restricciones:

  • Máximo intentos_maximos (campo de Proceso)
  • Requiere Pago si proceso.requiere_pago = true
  • Timer bloquea respuestas al expirar duracion_minutos

7.3 Gestión de Archivos de Resultados (Multi-sede)

Modelo: ProcesoAdmisionResultadoArchivo
Campos: proceso_admision_id, nombre, file_path, orden (UNIQUE por proceso)

Admin:
  ├─ Modal "Resultados" en ProcesosAdmisionList (6 slots pre-nombrados)
  ├─ Slots nombrados por: generarNombreSlot(orden, proceso) en store
  │    slot 1 → "Resultados Sábado {fecha_examen1}"
  │    slot 2 → "Resultados CONADIS"
  │    slots 3-6 → slugs personalizados
  └─ customRequest de Ant Design: requiere onSuccess()/onError()

Postulante:
  ├─ GET /api/proceso-resultado/{id}/archivos (público)
  ├─ archivosPorProceso en web.js store → mapa por procesoId
  ├─ fetchArchivosMultiples con Promise.allSettled (multi-sede)
  └─ HeroSection: card con v-if="hayResultados" (≥1 archivo)

7.4 Gestión de Contenido (Noticias + Comunicados)

Noticias:

  • CRUD admin con editor WYSIWYG (vue-quill)
  • Campo slug único, contenido markdown
  • Filtros: categoría, tag_color, destacado, publicado, orden
  • Web pública: GET /api/noticias, GET /api/noticias/{slug}

Comunicados:

  • Múltiples imágenes carrusel (ComunicadoImagen)
  • Solo 1 activo a la vez: GET /api/comunicados/activo
  • Fecha_inicio / fecha_fin de vigencia
  • ComunicadoModal.vue: auto-cierre, botón de acción

8. INFRAESTRUCTURA Y DESPLIEGUE

8.1 Docker Compose (Producción)

Servicio Imagen Puerto RAM
nginx nginx:alpine 127.0.0.1:8080 64MB
backend ghcr.io/…/backend:latest 9000 (interno) 1GB
frontend ghcr.io/…/frontend:latest interno 64MB
mysql mysql:8.0 127.0.0.1:3306 512MB

Volúmenes: mysql_data (BD), backend_storage (archivos subidos)
Red: Bridge admision_net
Health checks: PHP ping (backend), mysqladmin ping (mysql)

8.2 Dockerfile Backend (Multi-Stage)

# Stage 1: Composer (sin dev, optimizado)
FROM composer:2 AS vendor
RUN composer install --no-dev --optimize-autoloader --ignore-platform-reqs

# Stage 2: PHP 8.4-fpm-alpine
FROM php:8.4-fpm-alpine
# Extensiones: PDO MySQL, BCMath, MBString, GD, cURL, ZIP, XML, Intl
# OPcache: 128MB, 10k archivos, validate_timestamps=0
# Upload: 20MB archivos, 25MB POST
# PHP-FPM: 15 max_children, soporte 100+ concurrentes
# Entrypoint: config:cache + route:cache → php-fpm

8.3 Dockerfile Frontend

FROM node:20-alpine AS build
RUN npm ci && npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html

8.4 Nginx

# Frontend SPA
location / { try_files $uri $uri/ /index.html; }

# API (proxy a PHP-FPM backend)
location /api { proxy_pass http://backend:9000; }

# Storage público
location /storage { alias /var/www/html/storage/app/public; }

9. PATRONES DE DESARROLLO

9.1 Convenciones Backend

  • Validación en controladores (Validator::make), no Form Requests
  • Respuestas JSON estándar: { success, data/message, errors }
  • HTTP status codes semánticos (200, 201, 400, 401, 403, 422, 500)
  • Accessors para URLs de storage: getImagenUrlAttribute()
  • $casts en modelos para tipos (boolean, datetime, integer, array)
  • ON DELETE CASCADE en FK críticas
  • ->latest() para orden cronológico inverso

9.2 Convenciones Frontend

  • Componentes: PascalCase | Métodos/props: camelCase
  • Stores Pinia Options API (mayoría) — patrón: fetch → state → computed
  • Modales admin: inline en el mismo archivo .vue (patrón establecido)
  • axios (admin) vs axiosPostulante (público/postulante) — instancias separadas
  • Páginas públicas completas en WebPageSections/navbarcontent/
  • Datos sensibles de auth solo en localStorage (no Pinia, no sessionStorage)

9.3 Patrones de Componentes

// Patrón típico: Store + Component
const store = useProcesoAdmisionStore()
onMounted(() => store.fetchProcesos({ page: 1 }))

// Patrón: Tabla paginada (AntD)
<a-table :dataSource="store.procesos" :pagination="pagination" :loading="store.loading" />

// Patrón: Modal inline (no rutas separadas)
<a-modal v-model:open="showModal" @ok="handleSubmit" />

9.4 Seguridad

  • Bearer tokens con expiración (12h admin, 1h postulante)
  • Validación siempre en servidor (no confiar en cliente)
  • BCRYPT_ROUNDS=12 para passwords
  • revoke tokens al logout ($user->tokens()->delete())
  • Storage public disk — sin acceso a archivos privados vía HTTP
  • CORS configurado via config/cors.php

10. OBSERVACIONES: FORTALEZAS Y MEJORAS

Fortalezas

  1. Arquitectura modular — separación clara backend/frontend, cada uno con Dockerfile propio
  2. Auth robusta — Sanctum + Spatie, tokens con expiración, guards de router
  3. State management ordenado — 14 stores Pinia con responsabilidades claras
  4. Multi-sede — ProcesoAdmisionResultadoArchivo con orden y Promise.allSettled
  5. Docker optimizado — multi-stage builds, healthchecks, límites RAM
  6. Rich content — soporte Markdown + LaTeX en preguntas de examen
  7. Patrón de modales inline — consistente en todo el admin
  8. Encoding correcto — fix UTF-8 para tildes en archivos Windows-1252

Mejoras Sugeridas

Backend

Área Problema Solución recomendada
Form Requests Validación no reutilizable Crear clases FormRequest
Rate Limiting Sin throttle ThrottleRequests middleware
Tests Sin cobertura visible PHPUnit para flujos críticos (examen, auth)
N+1 Queries Riesgo en relaciones Usar with() en índices con relaciones
Caché database driver (lento) Redis para sesiones y caché
API Docs Sin OpenAPI scribe-php o l5-swagger
Servicios Lógica en controllers PagoService, ExamenService

Frontend

Área Problema Solución recomendada
TypeScript Sin tipos Migración gradual a TS
Tests Sin cobertura Vitest + Vue Test Utils
Composables Lógica en componentes grandes Extraer composables reutilizables
Lazy loading Sin code splitting visible Dynamic imports en rutas
Accesibilidad No auditado WCAG 2.1 audit
i18n Solo español vue-i18n si se requiere multiidioma

Infraestructura

Área Problema Solución recomendada
Backup Sin automatización Cron + mysqldump → S3/volumen
Logs Solo internos Centralizar (ELK/CloudWatch)
SSL No visible Let's Encrypt + Certbot
Secrets .env en repo Secret manager o .env.local ignorado
Monitoreo Sin métricas Prometheus + Grafana

11. RESUMEN EJECUTIVO

Stack Definitivo

  • Backend: Laravel 12 + Sanctum + Spatie + MySQL 8.0
  • Frontend: Vue 3.5 + Vite 7 + Pinia + Ant Design Vue 4
  • DevOps: Docker Compose, Nginx, GitHub Actions
  • Base de datos: 30+ tablas con integridad referencial

Funcionalidades Implementadas

  1. Procesos de Admisión multi-sede con archivos de resultados por slot
  2. Exámenes online con timer, generación dinámica, calificación automática
  3. Portal Admin con CRUD completo (áreas, cursos, preguntas, procesos, noticias)
  4. Portal Postulante (dashboard, examen, resultados Chart.js, seguimiento admisión)
  5. Página Web Pública (convocatorias, noticias, comunicados, descarga resultados)

Métricas

  • 73 endpoints API REST
  • 21 modelos Eloquent
  • 14 stores Pinia
  • 23 rutas Vue Router
  • 30+ tablas en BD
  • Soporte 100+ usuarios concurrentes (PHP-FPM config)

Estado del Proyecto

  • Etapa: MVP con iteraciones activas (commits recientes)
  • Calidad: Código limpio y consistente, sin tests automatizados
  • Seguridad: Auth/Authz implementada, validación servidor
  • Escalabilidad: Containerizado, DB con FK integridad, caché configurable