Volver a proyectosEn Producción

OposApp

Mi Trabajo de Fin de Grado (DAM): app móvil en Flutter que automatiza el seguimiento de convocatorias del BOPA y genera tests de práctica con IA local (Ollama), con privacidad total por diseño.

FlutterSpring BootIA Local (Ollama)PostgreSQLRGPD
OposApp

OposApp es mi Trabajo de Fin de Grado (Desarrollo de Aplicaciones Multiplataforma, curso 2025-2026): una aplicación full-stack que resuelve dos problemas reales del opositor medio en Asturias — los 15-30 minutos diarios perdidos buscando convocatorias a mano y los 50-150 €/mes que cuesta preparar los exámenes en academias tradicionales.

Qué hace

  • Seguimiento automático del BOPA — un workflow de n8n rastrea el boletín oficial cada día a las 07:00 y guarda las convocatorias nuevas en PostgreSQL, con filtros por categoría, organismo, fecha y estado, y búsqueda full-text tolerante a erratas.
  • Tests con IA local — un modelo en local (Ollama, familia Qwen: qwen2.5-coder en el plan gratuito y un modelo mayor qwen3 en premium) genera tests personalizados en ~11 s mediante un flujo asíncrono (el backend devuelve un solicitudId y la app hace polling), sin enviar ningún dato a servicios externos.
  • Dashboard de progreso — KPIs en tiempo real (tests, aciertos, racha), gráfico de evolución e historial completo.

Arquitectura y stack

La app Flutter (Android 14+, Material Design 3, provider, go_router, Dio + Retrofit) se comunica por REST/JWT con una API Spring Boot 3 · Java 21 en arquitectura por capas estricta. Los datos viven en PostgreSQL 15, con Spring Security (JWT HS512 + BCrypt coste 12), rate limiting con Bucket4j (30 req/min por IP) y documentación OpenAPI/Swagger.

Todo corre autohospedado en un NAS Synology detrás de Caddy (HTTPS automático), con n8n para el scraping y Ollama para la IA. Cero dependencias de nube de pago.

Un único APK para dos entornos: al arrancar, la app comprueba si el NAS es alcanzable en la red local; si lo es usa la IP directa (más rápido) y, si no, cae automáticamente al dominio HTTPS externo. El mismo binario funciona dentro y fuera de casa sin recompilar.

Funcionamiento clave

  • Modo offline con caché local Hive y detección de red (connectivity_plus): las últimas convocatorias siguen accesibles sin conexión.
  • Generación asíncrona de tests: el backend devuelve un solicitudId al instante y la app hace polling del estado; corrección inmediata con explicación por pregunta y animación de resultado.
  • Dashboard de progreso con gráficos fl_chart (evolución, aciertos, racha) e historial.
  • Panel de administración (rol ROLE_ADMIN): usuarios, estado de servicios (PostgreSQL, Ollama), registro de auditoría y métricas de scraping.

Privacidad por diseño (RGPD)

  • IA 100% local: sin servicios de terceros, sin salida de datos de la infraestructura propia.
  • Consentimiento opt-in explícito con registro de timestamp, borrado total de cuenta (Art. 17) y exportación del perfil en JSON (Art. 20).

Seguridad

  • JWT HS512 stateless (7 días), validado en cada petición.
  • BCrypt (coste 12) — contraseñas nunca en texto plano.
  • Bucket4j contra abuso, Bean Validation en todos los DTOs y JPA/Hibernate (imposible inyección SQL).
  • Panel de administración restringido a red local del NAS (LAN / WireGuard) + rol ROLE_ADMIN.

Rendimiento medido

  • Listado de 200 convocatorias: 187 ms (objetivo < 500 ms).
  • Generación de test con IA (10 preguntas): 11,3 s (objetivo < 15 s).
  • Login con verificación BCrypt: 312 ms.

Contexto académico

TFG del ciclo DAM (2025-2026). Tutores: Delio Tolivia Cadrecha (PIDAM) y Mario Álvarez Fernández (PDAW). Publicado como código abierto bajo licencia MIT.

OposApp is my final degree project (Multiplatform App Development, 2025-2026). It is a full-stack mobile app that solves two real problems for public-exam candidates in Asturias: the 15-30 minutes a day spent manually searching for job listings, and the 50-150 €/month cost of traditional exam-prep academies.

What it does

  • Automated BOPA tracking — an n8n workflow scrapes the official bulletin every day at 07:00 and stores new listings in PostgreSQL.
  • AI practice tests — a local model (Ollama, Qwen family: qwen2.5-coder on the free tier, a larger qwen3 model on premium) generates personalized tests in ~11 s, with no data ever leaving my own infrastructure.
  • Progress dashboard — real-time KPIs, evolution chart and full history.

Architecture & stack

Flutter (Android 14+, Material 3, provider, GoRouter, Dio + Retrofit) talking over REST/JWT to a Spring Boot 3 / Java 21 API, with PostgreSQL 15, Spring Security (JWT HS512 + BCrypt), Bucket4j rate limiting and OpenAPI docs. Everything runs self-hosted on a Synology NAS behind Caddy (automatic HTTPS), with n8n and Ollama for automation and AI.

One APK, two environments: on launch the app checks whether the NAS is reachable on the local network — if so it uses the direct IP, otherwise it falls back to the external HTTPS domain. The same binary works inside and outside the LAN with no rebuild.

Key features

  • Offline mode with local Hive cache and network detection.
  • Asynchronous test generation (immediate solicitudId + polling), instant grading with per-question explanations.
  • Progress dashboard with fl_chart graphs and full history.
  • Admin panel (ROLE_ADMIN): users, service health (PostgreSQL, Ollama), audit log and scraping metrics.

Privacy by design (GDPR)

  • 100% local AI — no third-party services, no data leaving the infrastructure.
  • Explicit opt-in consent with timestamp log; full account deletion (Art. 17) and JSON data export (Art. 20).

Measured performance

  • Listing 200 records: 187 ms (target < 500 ms).
  • AI test generation (10 questions): 11.3 s (target < 15 s).
  • Login with BCrypt verification: 312 ms.