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.
29 KiB
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 CASCADEen relaciones examenes → postulantes, preguntas_asignadas → examenesUNIQUE CONSTRAINTSenarea_proceso(area_id, proceso_id)yarea_cursoUNIQUE KEYenproceso_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
POST /api/logincon email/passwordAuthControllerverifica credenciales, revoca tokens previos- Genera token con
createToken('api_token', ['*'], now()->addHours(12)) - Devuelve token + user + roles/permisos (Spatie)
Flujo Postulante
POST /api/postulante/login- Token con expiración 1 hora
Device-Idúnico para detección multi-dispositivo- Guard separado:
postulante_tokenen localStorage
Roles (Spatie Permission)
administrador— acceso a panel adminsuperadmin— 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 loginguest— evita acceso a login si ya autenticadorole— valida rol, redirige a/403si 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-Idopcional
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
Pagosiproceso.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,contenidomarkdown - 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() $castsen modelos para tipos (boolean, datetime, integer, array)ON DELETE CASCADEen 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) vsaxiosPostulante(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=12para passwordsrevoke tokensal logout ($user->tokens()->delete())- Storage
publicdisk — sin acceso a archivos privados vía HTTP - CORS configurado via
config/cors.php
10. OBSERVACIONES: FORTALEZAS Y MEJORAS
Fortalezas
- Arquitectura modular — separación clara backend/frontend, cada uno con Dockerfile propio
- Auth robusta — Sanctum + Spatie, tokens con expiración, guards de router
- State management ordenado — 14 stores Pinia con responsabilidades claras
- Multi-sede — ProcesoAdmisionResultadoArchivo con orden y Promise.allSettled
- Docker optimizado — multi-stage builds, healthchecks, límites RAM
- Rich content — soporte Markdown + LaTeX en preguntas de examen
- Patrón de modales inline — consistente en todo el admin
- 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
- Procesos de Admisión multi-sede con archivos de resultados por slot
- Exámenes online con timer, generación dinámica, calificación automática
- Portal Admin con CRUD completo (áreas, cursos, preguntas, procesos, noticias)
- Portal Postulante (dashboard, examen, resultados Chart.js, seguimiento admisión)
- 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