Solución completa y profesional desarrollada para la prueba técnica integral fullstack, estructurada rigurosamente en sus tres fases correspondientes:
database/- Evaluación PL/SQL Fullstack (Oracle 11gR2 / 23ai Free)backend/- Evaluación Spring Boot Fullstack (Java 21, Arquitectura Limpia, SOLID, JWT, OpenPDF, Docker)frontend/- Evaluación React Fullstack (React 19, TypeScript, Vite, OWASP, Estado Global)
| Reto / Componente | Descripción | Estado |
|---|---|---|
| Reglas Generales | Oracle 11gR2+, Java 21, React 19, Clean Architecture, OWASP TOP 10 | ✅ Cumplido |
| Reto 01 | Modelo de Datos relacional, normalización, auditoría y MER | ✅ Cumplido |
| Reto 02 | Secuencias de BD, triggers BEFORE INSERT y triggers de auditoría |
✅ Cumplido |
| Reto 03 | Datos semilla aleatorios (10 comerciantes y 30 establecimientos) | ✅ Cumplido |
| Reto 04 | Paquete PL/SQL PKG_COMERCIANTES (CRUD, cursores explícitos, validaciones) |
✅ Cumplido |
| Reto 05 | Función PL/SQL de Reporte con SYS_REFCURSOR y totales calculados |
✅ Cumplido |
| Reto 06 | Web API: Seguridad con JWT (1 hora), RBAC y política CORS | ✅ Cumplido |
| Reto 07 | Web API: Listas de valores con caché en memoria (@Cacheable) |
✅ Cumplido |
| Reto 08 | Web API: CRUD Comerciante (paginación, filtros, PATCH estado, PL/SQL) | ✅ Cumplido |
| Reto 09 | Web API: CRUD Establecimiento con validaciones y ORM JPA | ✅ Cumplido |
| Reto 10 | Web API: Descarga de Reporte CSV delimitado por pipe (|) (Admin) |
✅ Cumplido |
| Reto 11 | Web API: Ficha de Comerciante en PDF con OpenPDF (Admin) | ✅ Cumplido |
| Reto 12 | React: Página de Login (OWASP, validación términos, JWT ofuscado) | ✅ Cumplido |
| Reto 13 | React: Página Home (tabla, paginado, filtros, acciones por rol, CSV/PDF) | ✅ Cumplido |
| Reto 14 | React: Formulario dinámico con establecimientos y footer en tiempo real | ✅ Cumplido |
| Pruebas Unitarias | Mínimo 3 requeridas: 10 en Backend (JUnit 5) + 6 en Frontend (Vitest) | ✅ Cumplido |
| Compilado Distribución | Archivo comprimido frontend-dist.zip para producción |
✅ Cumplido |
| Entrega Final | Envío de ruta de repositorio público al correo de la prueba | ⏳ Pendiente |
- Utilizar Oracle 11gR2 o superior (compatible con Oracle 23ai Free en contenedor).
- Definir los índices que considere necesarios (índices funcionales
UPPER, índices en filtrosFECHA_REGISTRO,ESTADO). - Cero cursores implícitos: todos los cursores implementados son explícitos y parametrizados.
- Todos los procedimientos, funciones, records y tipos PL/SQL contenidos dentro de un paquete de base de datos (
PKG_COMERCIANTES). - Sentencias DML y DDL consignadas en archivos
.sqlorganizados y numerados secuencialmente. - Script maestro
00_run_all.sqlpara replicación desatendida en ambientes de calificación.
- Diseñar modelo de datos para la entidad
COMERCIANTE: Nombre/Razón Social, Departamento, Municipio, Teléfono, Correo Electrónico, Fecha de Registro y Estado (Registrado,Activo,Inactivo,Suspendido,Cancelado). - Diseñar modelo de datos para la entidad
ESTABLECIMIENTO: Nombre del Establecimiento, Ingresos (NUMBER(14,2)con 2 decimales) y Número de Empleados (NUMBER(6)). - Normalización del modelo: entidades maestras
DEPARTAMENTOyMUNICIPIOcon integridad referencial (ON DELETE CASCADEen establecimientos). - Campos de auditoría incluidos en las entidades:
FECHA_ACTUALIZACION,USUARIO_ACTUALIZACION(y fechas de registro). - Modelo Entidad-Relación (MER): Documentado en
database/06_mer_diagram.mdcon diagrama Mermaid, diccionario de datos y justificación 1FN, 2FN y 3FN.
- Identificador único generado por secuencias de base de datos (
SEQ_COMERCIANTE,SEQ_ESTABLECIMIENTO,SEQ_DEPARTAMENTO,SEQ_MUNICIPIO). - Cada secuencia gestionada por triggers
BEFORE INSERT(TRG_COMERCIANTE_BI,TRG_ESTABLECIMIENTO_BI). - Campos de auditoría actualizados automáticamente mediante triggers en inserción y actualización (
TRG_COMERCIANTE_BU,TRG_ESTABLECIMIENTO_BU).
- Generar datos semilla para
COMERCIANTE: exactamente 10 registros aleatorios y con datos no repetitivos. - Generar datos semilla para
ESTABLECIMIENTO: exactamente 30 registros aleatorios distribuidos entre los comerciantes. - Cantidad de establecimientos por comerciante asignada de forma aleatoria.
- Inclusión de departamentos y municipios de Colombia como catálogo precargado (
database/03_seed_data.sql).
- Función
FN_CONSULTAR_POR_ID: Retorna cursor/registro con Nombre/Razón Social, Departamento, Municipio, Teléfono, Correo, Fecha de Registro, Estado, y los campos calculados Total Activos (sumatoria de Ingresos) y Cantidad de Empleados (sumatoria de empleados de establecimientos asociados). - Función
FN_CONSULTAR_COMERCIANTES: Consulta con paginación (offset / limit) y filtros parametrizados por:- Nombre o Razón Social
- Municipio
- Fecha de Registro
- Estado
- Procedimientos DML:
PR_CREAR_COMERCIANTEPR_ACTUALIZAR_COMERCIANTEPR_CAMBIAR_ESTADO_COMERCIANTEPR_ELIMINAR_COMERCIANTE
- Campos de entrada solicitados en creación/actualización: Nombre/Razón Social, Departamento, Municipio, Teléfono (Opcional), Correo (Opcional), Fecha de Registro y Estado.
- Validaciones de entrada: tipos de datos, obligatoriedad, formato de correo con expresión regular RFC.
- Parámetros de salida obligatorios:
P_CODIGO_ERROR(0 en éxito, no cero en error) yP_MENSAJE_ERROR(nulo en éxito, texto capturado en bloque de excepciones). - Funciones auxiliares (
FN_VALIDAR_EMAIL, etc.).
- Función
FN_REPORTE_COMERCIANTES_ACTIVOSque retorna cursor referenciado (SYS_REFCURSOR). - Filtro estricto por comerciantes registrados y activos (
ESTADO IN ('Registrado', 'Activo')). - Estructura requerida: Nombre, Departamento, Municipio, Teléfono, Correo, Fecha Registro, Estado, Cantidad de Establecimientos, Total Activos y Cantidad de Empleados.
- Campos calculados en tiempo real sumando los valores de los establecimientos asociados.
- Ordenamiento descendente por la cantidad de establecimientos asociados (
ORDER BY CANTIDAD_ESTABLECIMIENTOS DESC).
- Utilizar Java 11 o superior (implementado con Java 21 LTS).
- Framework Spring Boot (versión 3.3.5).
- Mínimo 3 pruebas unitarias con JUnit (implementadas 10 pruebas unitarias con JUnit 5 y Mockito).
- Documentación de APIs con Swagger UI (
/swagger-ui.html) y colección de Postman (backend/postman_collection.json). - Arquitectura limpia y principios SOLID (capas claras de Controller, Service, Repository, DTO, Mapper, Exception Handling).
- Construcción de
Dockerfilepara despliegue contenerizado independiente y con Docker Compose.
- Endpoint público de autenticación:
POST /api/v1/auth/login. - Recepción de credenciales en cuerpo:
correoElectronicoycontrasena. - Emisión de token JWT con tiempo de expiración exacto de 1 hora (3600 segundos).
- Autorización basada en roles (
ADMINISTRADORyAUXILIAR_REGISTRO) para control fino de acceso a cada endpoint. - Endpoint de login público sin requerir token JWT.
- Configuración de política CORS para permitir consumo seguro y controlado desde el cliente frontend.
- Endpoints para listas de valores:
GET /api/v1/catalogos/departamentos: Lista de departamentos.GET /api/v1/catalogos/departamentos/{id}/municipios: Municipios asociados al departamento.
- Caché en memoria implementado con Spring Cache (
@Cacheable("departamentos"),@Cacheable("municipios")) para evitar consultas redundantes a la base de datos. - Mapeo con ORM Spring Boot JPA / Hibernate.
- Endpoints privados protegidos con JWT.
- Estandarización de respuestas HTTP (
ApiResponse<T>).
- Endpoint de consulta paginada
GET /api/v1/comerciantescon valor por defecto de 5 registros por página. - Filtros reactivos por: Nombre o Razón Social, Municipio, Fecha de Registro y Estado.
- Endpoint de consulta por ID
GET /api/v1/comerciantes/{id}con campos calculados (Total Activos y Cantidad de Empleados). - Endpoint de creación
POST /api/v1/comerciantes. - Endpoint de actualización
PUT /api/v1/comerciantes/{id}. - Endpoint de eliminación
DELETE /api/v1/comerciantes/{id}(restringido a Administrador). - Consumo / compatibilidad con los procedimientos y funciones del Reto 04.
- Endpoint para modificar estado (Activar / Inactivar) utilizando el método HTTP PATCH:
PATCH /api/v1/comerciantes/{id}/estado. - Validaciones de entrada (Jakarta Validation:
@NotBlank,@Email, obligatoriedad). - Endpoints privados con JWT y respuestas estandarizadas.
- Endpoint de consulta por comerciante:
GET /api/v1/establecimientos/comerciante/{comercianteId}. - Endpoint de creación:
POST /api/v1/establecimientos. - Endpoint de actualización:
PUT /api/v1/establecimientos/{id}. - Endpoint de eliminación:
DELETE /api/v1/establecimientos/{id}. - Campos solicitados: Nombre del Establecimiento, Ingresos (con decimales) y Número de Empleados.
- Mapeo con ORM Spring Boot JPA / Hibernate y validaciones de datos.
- Endpoints privados con JWT y respuestas estandarizadas.
- Endpoint para generar archivo plano:
GET /api/v1/reportes/comerciantes/csv. - Filtro exclusivo para comerciantes registrados y activos.
- Estructura requerida: Nombre, Departamento, Municipio, Teléfono, Correo, Fecha Registro, Estado, Cantidad Establecimientos, Total Activos y Cantidad Empleados.
- Separador de columnas utilizando el carácter pipe (
|). - Consumo / compatibilidad con la función del Reto 05.
- Acceso privado con JWT restringido exclusivamente al rol Administrador (
@PreAuthorize("hasRole('ADMINISTRADOR')")). - Headers HTTP correctos para descarga de archivo adjunto (
Content-Disposition: attachment; filename=...).
- Endpoint para generar archivo PDF:
GET /api/v1/reportes/comerciantes/{id}/pdf. - Parámetro de entrada: identificador del comerciante.
- Información detallada del comerciante con sumatoria agregada de establecimientos, ingresos y empleados.
- Mapeo con Spring Boot JPA y generación estilizada con OpenPDF.
- Acceso privado con JWT restringido exclusivamente al rol Administrador.
- Headers HTTP correctos para visualización / descarga del documento PDF.
- Utilizar React 18 o superior (implementado en React 19 con TypeScript y Vite).
- Sistema de gestión para el estado global (
AuthContextpara sesión/roles,ToastContextpara notificaciones en pantalla). - Mínimo 3 pruebas unitarias (implementadas 6 pruebas unitarias con Vitest y Testing Library).
- Recomendaciones OWASP TOP 10:
- Almacenamiento seguro de tokens y credenciales en
sessionStorage(sin exposición enlocalStorageowindow). - Ofuscación de credenciales antes del envío en red para prevenir capturas en debug de navegadores.
- Sanitización y prevención contra ataques XSS y CSRF.
- Rutas privadas protegidas con guardias (
ProtectedRoute.tsx).
- Almacenamiento seguro de tokens y credenciales en
- Compilado para producción comprimido entregado en el repositorio:
frontend/frontend-dist.zip.
- Formulario con campos obligatorios: Correo Electrónico (Usuario) y Contraseña.
- Checkbox obligatorio: "Acepto términos y condiciones", requerido para habilitar el botón "Iniciar sesión".
- Proceso de autenticación por JWT sin exponer datos en texto plano desde el debug de red.
- Consumo del endpoint público de autenticación del Reto 06.
- Diseño fiel al prototipo suministrado (con accesos directos para probar credenciales de Administrador y Auxiliar).
- Encabezado superior con visualización de: Nombre del Usuario y Rol (
AdministradoroAuxiliar de Registro). - Tabla de comerciantes con todas las columnas requeridas:
- Nombre o Razón Social
- Teléfono
- Correo Electrónico
- Fecha Registro
- No. Establecimientos
- Estado
- Acciones
- Paginador funcional con selector de cantidad de ítems por página (5, 10, 20).
- Botones en columna de acciones:
- Editar: Redirige al formulario de edición con los datos precargados (Reto 14).
- Activar / Inactivar: Consume el endpoint PATCH del Reto 08 para cambiar el estado.
- Eliminar: Visible y ejecutable únicamente para usuarios con rol Administrador.
- Descargar PDF: Visible y ejecutable para rol Administrador, consume el endpoint del Reto 11.
- Botón "Descargar Reporte en CSV": Consume el endpoint del Reto 10 para descargar el archivo delimitado por pipe (
|). - Botón magenta "Crear Formulario Nuevo": Redirige a la pantalla de creación del Reto 14.
- Opción para cerrar sesión (
Cerrar Sesión). - Diseño fiel al prototipo de la prueba.
- Datos Generales con los campos requeridos:
- Nombre o Razón Social (Obligatorio)
- Departamento (Obligatorio, selector alimentado por la API de catálogos)
- Municipio o Ciudad (Obligatorio, selector filtrado dinámicamente según el departamento elegido)
- Teléfono
- Correo Electrónico
- Fecha de Registro (Obligatorio, selector de fecha)
- Checkbox "¿Posee establecimientos?"
- Sección condicional "Gestionar Establecimiento":
- Se despliega dinámicamente solo si el checkbox "¿Posee establecimientos?" está marcado.
- Permite crear, listar, editar y eliminar múltiples establecimientos en tarjetas.
- Modal interactivo para creación y edición de establecimiento.
- Campos obligatorios de establecimiento: Nombre del Establecimiento, Ingresos y Número de Empleados.
- Footer flotante fijo en pantalla:
- Caja independiente con Total Ingresos Formulario (sumatoria formateada en moneda colombiana
COP). - Caja independiente con Cantidad de empleados (sumatoria en tiempo real).
- Botón de envío "Enviar Formulario".
- Caja independiente con Total Ingresos Formulario (sumatoria formateada en moneda colombiana
- Consumo de los endpoints del Reto 08 (Comerciante) y Reto 09 (Establecimientos).
- Validaciones de formato, estructura y obligatoriedad con feedback interactivo mediante Toasts y mensajes inline.
- Respeto riguroso de ambos prototipos (Checkbox inactivo y Checkbox activo).
- Repositorio de código fuente completo estructurado en
database/,backend/yfrontend/. - Scripts SQL ordenados y numerados con script maestro
00_run_all.sql. - Documentación de endpoints en OpenAPI/Swagger y archivo
backend/postman_collection.json. - Paquete compilado de producción comprimido:
frontend/frontend-dist.zip. - Orquestación contenerizada con
docker-compose.ymlpara los 3 servicios integrados. - Enviar la ruta del repositorio público (GitHub / GitLab / Bitbucket) al correo electrónico de la convocatoria. (Paso final del postulante)
- Adjuntar el compilado
frontend-dist.zipal correo en caso de requerirse por el equipo evaluador. (Paso final del postulante)
El proyecto cuenta con orquestación completa para levantar los 3 servicios integrados con un solo comando:
docker compose up --build- Frontend Web:
http://localhost:5173ohttp://localhost:3000 - Backend API & Swagger UI:
http://localhost:8080/swagger-ui.html - OpenAPI Spec:
http://localhost:8080/v3/api-docs - Oracle Database 23ai Free (Motor Relacional):
- Host / Servidor:
localhost(o127.0.0.1) - Puerto:
1521 - Nombre de Base de Datos / Servicio (PDB):
FREEPDB1(SID:FREE) - Usuario de Aplicación:
COMERCIANTE_APP - Contraseña:
Comerciante123* - Usuario Administrador (SYS / SYSTEM):
SYSTEM/OraclePassword123* - URL de Conexión JDBC:
jdbc:oracle:thin:@localhost:1521/FREEPDB1
- Host / Servidor:
| Rol | Correo Electrónico | Contraseña | Permisos |
|---|---|---|---|
| Administrador | admin@olsoftware.com |
Admin123* |
Acceso Total: CRUD, Eliminar, Reporte CSV pipe, Ficha PDF |
| Auxiliar de Registro | auxiliar@olsoftware.com |
Auxiliar123* |
Consulta, Creación, Edición y Activación/Inactivación |
El modelo de datos relacional fue diseñado siguiendo las mejores prácticas de modelado en Oracle Database, garantizando alta cohesión, normalización, integridad referencial y soporte para auditoría transversal. El esquema se encuentra estructurado en tres dominios fundamentales:
COMERCIANTE: Representa a la persona natural o jurídica inscrita ante la agremiación.- Identificador (
ID_COMERCIANTE- PK): Clave primaria numérica autogenerada mediante la secuenciaSEQ_COMERCIANTEy asignada por el triggerBEFORE INSERT(TRG_COMERCIANTE_BI). - Datos del Negocio: Razón social (
NOMBRE_RAZON_SOCIAL), ubicación geográfica (DEPARTAMENTO,MUNICIPIO), canales de contacto (TELEFONO,CORREO_ELECTRONICO) y fecha de formalización mercantil (FECHA_REGISTRO). - Regla de Dominio: Restricción
CHECK (ESTADO IN ('Registrado', 'Activo', 'Inactivo', 'Suspendido', 'Cancelado'))que asegura la validez del ciclo de vida del comerciante. - Auditoría Transversal: Atributos
FECHA_ACTUALIZACION(defaultSYSDATE) yUSUARIO_ACTUALIZACION(defaultUSER), administrados automáticamente por triggers ante inserciones o actualizaciones.
- Identificador (
ESTABLECIMIENTO: Sucursales, sedes o locales comerciales vinculados a un comerciante.- Identificador (
ID_ESTABLECIMIENTO- PK): Clave primaria autogenerada por secuencia y trigger. - Relación Foránea (
ID_COMERCIANTE- FK): Vinculación obligatoria conCOMERCIANTEconfigurada con eliminación en cascada (ON DELETE CASCADE), garantizando que al eliminar un comerciante no queden establecimientos huérfanos. - Métricas Financieras y Operativas:
INGRESOScon precisión monetaria (NUMBER(14,2)con 2 decimales para soportar cifras financieras exactas) yNUMERO_EMPLEADOS(NUMBER(6)).
- Identificador (
DEPARTAMENTO: Maestro de departamentos de Colombia con su código oficial DANE (CODIGO_DANEcon restricciónUNIQUE) y nombre descriptivo.MUNICIPIO: Ciudades y municipios colombianos vinculados a su respectivo departamento mediante la llave foráneaID_DEPARTAMENTO(relación 1:N).- Beneficio de Diseño: Permite el consumo eficiente de catálogos en cascada optimizados con caché en memoria (
@Cacheable) en el backend (Reto 07) y selectores dinámicos en el frontend (Reto 14).
- Beneficio de Diseño: Permite el consumo eficiente de catálogos en cascada optimizados con caché en memoria (
USUARIOS: Entidad dedicada a la autenticación, emisión de tokens JWT y control de acceso basado en roles (RBAC) para los operadores del sistema.- Atributos:
CORREO_ELECTRONICO(identificador único de acceso),CONTRASENA(almacenada de forma segura con hash BCrypt),ROL(ADMINISTRADORoAUXILIAR_REGISTRO),ACTIVO(estado de habilitación del usuario) yFECHA_CREACION.
- Atributos:
- Relación
COMERCIANTE (1) <─── (0..N) ESTABLECIMIENTO: Un comerciante puede poseer desde 0 hasta múltiples establecimientos comerciales. - Relación
DEPARTAMENTO (1) <─── (1..N) MUNICIPIO: Cada municipio pertenece a un único departamento. - 1FN, 2FN y 3FN Estricta: Todos los atributos son atómicos y dependientes exclusivamente de su llave primaria. En cumplimiento estricto con la 3ª Forma Normal, los campos de resumen
Total ActivosyCantidad de Empleadosno se almacenan como columnas desnormalizadas enCOMERCIANTE, sino que son calculados dinámicamente en tiempo real mediante funciones de agregación (SUM,COUNT) en el paquete PL/SQLPKG_COMERCIANTESy en las consultas del backend.
| Parámetro | Valor de Conexión | Descripción |
|---|---|---|
| Motor de BD | Oracle Database 23ai / 21c / 11gR2+ | Contenedor oficial gvenzl/oracle-free |
| Host / Hostname | localhost (o 127.0.0.1) |
Dirección del servidor local |
| Puerto | 1521 |
Puerto estándar de Oracle Listener |
| Nombre de BD / Service Name | FREEPDB1 |
Pluggable Database (PDB) del proyecto |
| SID (Alternativo) | FREE |
Identificador del contenedor |
| Usuario de Aplicación | COMERCIANTE_APP |
Propietario del esquema, tablas y paquete PL/SQL |
| Contraseña de Aplicación | Comerciante123* |
Clave de acceso del esquema de aplicación |
| Usuario DBA (Opcional) | SYSTEM / OraclePassword123* |
Para administración general o privilegios SYSDBA |
| String JDBC | jdbc:oracle:thin:@localhost:1521/FREEPDB1 |
Cadena utilizada por Spring Boot y DBeaver |
- Tipo de Conexión: Oracle
- Tipo de Conexión / Host Type:
Service Name(Nombre de Servicio) - Host:
localhost - Port:
1521 - Database / Service Name:
FREEPDB1 - Username:
COMERCIANTE_APP - Password:
Comerciante123*
Tip
Si utiliza Oracle SQL Developer, seleccione en Connection Type la opción TNS o Basic, elija el radio button Service Name e ingrese FREEPDB1.
Para conectarse directamente a la base de datos por línea de comandos utilizando el cliente sqlplus incluido en el contenedor Docker:
# Conexión directa con el usuario de la aplicación
docker exec -it comerciantes-database sqlplus COMERCIANTE_APP/Comerciante123*@//localhost:1521/FREEPDB1
# Conexión con privilegios de administrador (SYSDBA) si se requiere
docker exec -it comerciantes-database sqlplus sys/OraclePassword123*@//localhost:1521/FREEPDB1 as sysdbaLos scripts DDL, DML y el paquete PL/SQL se ejecutan automáticamente al levantar Docker Compose. No obstante, si se desea recrear o ejecutar manualmente en Oracle SQL*Plus o SQL Developer:
@00_run_all.sqlcd backend
# Ejecutar las 10 pruebas unitarias con JUnit 5 y Mockito
.tools\apache-maven-3.9.9\bin\mvn.cmd clean test
# Iniciar la aplicación Spring Boot
.tools\apache-maven-3.9.9\bin\mvn.cmd spring-boot:runcd frontend
# Ejecutar las 6 pruebas unitarias con Vitest
yarn test
# Iniciar servidor de desarrollo Vite
yarn devAcceder en el navegador a: http://localhost:5173

