pickup-point-update-db
1. Descripción general
El pom.xml no incluye un <description> explícito (el <name> tampoco está presente). A partir del código y del artifactId (pickup-point-update-db), el propósito real del proyecto es doble: (1) enriquecer los puntos de recogida pendientes de actualizar (isUpdated=false) con el nombre de provincia y el código ISO 3166-2, mediante geocodificación inversa (Nominatim, con fallback a BigDataCloud); y (2) aplicar reglas de exclusión de visibilidad (PickupPointExclusionRule) para marcar como no visibles los puntos que coincidan con dichas reglas.
El CLAUDE.md de este proyecto está gravemente desactualizado: describe una arquitectura de un único CommandLineRunner con toda la lógica y sin mencionar la reestructuración real a un patrón de "procesadores" orquestados en paralelo, ni la funcionalidad de reglas de exclusión (ver hallazgo detallado en la sección 13).
2. Información técnica
| Campo | Valor |
|---|---|
artifactId | pickup-point-update-db |
groupId | com.hawkersco |
version | 1.0.25 |
| Java | 25 (maven.compiler.release=25) |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | jar (ejecutable, Spring Boot batch/CLI) |
| Módulos | No aplica (proyecto de módulo único) |
3. Arquitectura y diseño
No es una API REST: es una aplicación Spring Boot CLI con un patrón de procesadores intercambiables ejecutados en paralelo por un único orquestador.
com.hawkersco.pickuppointupdatedb— clase principal (PickupPointUpdateDbApplication).PickupPointUpdateDbProcessor— interfaz común (process(),getName()) implementada por cada procesador.PickupPointUpdateDbOrchestrator(ApplicationRunner) — descubre todos los beansPickupPointUpdateDbProcessorregistrados, filtra los activos según la propiedadpickup.processors(lista separada por comas; vacía = todos), y los ejecuta en paralelo con unExecutorServicede tamaño igual al número de procesadores activos, esperando a que todos terminen antes de cerrar la JVM.PickupPointUpdateDbRunner(procesadorpickup-point-update) — geocodificación inversa.PickupPointExclusionRulesDbRunner(procesadorpickup-point-exclusion-rules) — aplicación de reglas de exclusión de visibilidad..config—PickupPointUpdateDbConfig(beansPickupPointService,PickupPointExclusionRuleService,PersistenceManagedTypes).
flowchart TD
A[PickupPointUpdateDbOrchestrator] -->|filtra por pickup.processors| B{Procesadores activos}
B -->|en paralelo| C["PickupPointUpdateDbRunner<br/>(pickup-point-update)"]
B -->|en paralelo| D["PickupPointExclusionRulesDbRunner<br/>(pickup-point-exclusion-rules)"]
C -->|findByIsUpdatedFalseOrderByDtSysCreatedAsc| E[(logistics · pickup_point)]
C -->|reverse geocoding| F[Nominatim OSM]
C -.->|fallback si falta dato| G[BigDataCloud]
C -->|guarda provincia + ISO, marca isUpdated| E
D -->|findAll reglas| H[(logistics · pickup_point_exclusion_rule)]
D -->|busca puntos que coinciden| E
D -->|marca isVisible=false| E
4. Dependencias principales
| Dependencia | Propósito |
|---|---|
spring-boot-starter | Núcleo de Spring Boot (sin web, es un runner CLI) |
com.hawkersco:logistics-commons:1.0.25-SNAPSHOT | PickupPoint, PickupPointExclusionRule (entidades), PickupPointService, PickupPointExclusionRuleService |
java.net.http.HttpClient (JDK estándar, sin dependencia externa) | Llamadas HTTP a Nominatim y BigDataCloud |
| SLF4J (transitivo de Spring Boot) | Logging |
spring-boot-starter-test (test) | JUnit 5 + Spring Test |
No hay dependencia de gls-pickup-client en el pom.xml (a diferencia de lo que sugeriría el escaneo de entidades com.hawkersco.glspickupclient.dao en PickupPointUpdateDbConfig, ver hallazgo en la sección 13).
5. API / Endpoints
No aplica a este proyecto. Es un batch/runner sin capa REST.
6. Integraciones externas
| Sistema | Protocolo | Dirección | Detalle |
|---|---|---|---|
Nominatim/OpenStreetMap (nominatim.openstreetmap.org) | HTTP REST (JSON) | Entrante | Geocodificación inversa primaria; respeta el límite de tasa con una espera de 1,5s tras cada llamada |
BigDataCloud (api.bigdatacloud.net) | HTTP REST (JSON) | Entrante | Geocodificación inversa de respaldo, usada cuando Nominatim no aporta provincia o código ISO |
PostgreSQL (logistics) | JDBC | Entrante/Saliente | Lectura/escritura de pickup_point y pickup_point_exclusion_rule |
7. Configuración
En producción (application-pro.properties) las credenciales llegan por variables de entorno inyectadas como Secret de Kubernetes; en local (application.properties) el repositorio contiene actualmente valores reales hardcodeados (ver alerta de seguridad).
| Clave | Descripción | Valor local | Valor producción |
|---|---|---|---|
spring.datasource.url / .username / .password | Credenciales de la BD logistics | valores reales | ${dbLogisitcsUrl}, etc. |
bigdatacloud.api-key | API key de BigDataCloud (geocodificación de respaldo) | valor real | ${bigDataCloudApiKey} |
pickup.processors | Lista de procesadores activos (vacío = todos) | pickup-point-exclusion-rules (solo las reglas de exclusión) | (vacío → se ejecutan ambos procesadores) |
⚠️ Alerta de seguridad
El fichero src/main/resources/application.properties (perfil local) contiene actualmente credenciales reales en texto plano: contraseña de la base de datos PostgreSQL y la API key de BigDataCloud. Ninguno de estos valores se ha reproducido en este documento. Se recomienda:
- Rotar la contraseña de BD y la API key de BigDataCloud expuestas.
- Sustituir los valores hardcodeados de
application.propertiespor credenciales de un entorno de desarrollo aislado. - Revisar el historial de control de versiones, ya que estas credenciales pueden seguir expuestas en commits anteriores.
8. Persistencia
Base de datos PostgreSQL (logistics), acceso vía JPA a través de la librería logistics-commons. spring.jpa.hibernate.ddl-auto=none. Entidades relevantes: PickupPoint (campos dsProvince, dsProvinceCode, isUpdated, isVisible) y PickupPointExclusionRule (reglas de exclusión por transportista/código postal/provincia/país). No hay Flyway/Liquibase en este repositorio.
9. Procesos programados y mensajería
No hay @Scheduled ni listeners de colas: la periodicidad la impone el CronJob de Kubernetes (k8s/cronjob.yaml), que ejecuta el contenedor cada 30 minutos (schedule: "*/30 * * * *"). El orquestador lanza en paralelo los procesadores habilitados:
pickup-point-update(PickupPointUpdateDbRunner): obtiene los puntos conisUpdated=falseordenados por fecha de creación. Para cada punto con coordenadas, geocodifica inversamente (Nominatim, con fallback a BigDataCloud); si el país es Portugal, normaliza el nombre de provincia contra una lista fija de 20 provincias/regiones portuguesas (con alias para "Bragança"→"Braganza"); guarda provincia + código ISO y marcaisUpdated=true. Los puntos sin provincia/ISO resuelto (o cuando BigDataCloud devuelve cuota agotada/prohibido) se guardan conisUpdated=falsepara reintentarse en la siguiente ejecución. Los puntos sin coordenadas se marcan directamente comoisUpdated=truesin haber sido enriquecidos (ver hallazgo en la sección 13).pickup-point-exclusion-rules(PickupPointExclusionRulesDbRunner): obtiene todas las reglas de exclusión (PickupPointExclusionRuleService.findAll()) y, para cada una, busca los puntos que coinciden (por transportista, código postal, código de provincia y país) y los marca comoisVisible=false.
10. Ejecución en local
Requisitos previos: JDK 25, Maven, acceso a la BD logistics y una API key de BigDataCloud.
# Compilar sin tests
./mvnw -B -DskipTests clean install
# Compilar y ejecutar el JAR directamente
./mvnw clean package -DskipTests
java -jar target/pickup-point-update-db-*.jar
# Ejecutar tests
./mvnw test
Por defecto en local (application.properties), solo se ejecuta el procesador de reglas de exclusión (pickup.processors=pickup-point-exclusion-rules); para probar la geocodificación en local hay que vaciar o ajustar esa propiedad. Al ser un runner sin servidor web, no expone Actuator/health: la verificación se hace revisando el log de consola o el estado de isUpdated/isVisible en la tabla pickup_point.
11. Despliegue
- Imagen: construida con
jib-maven-plugin(baseeclipse-temurin:25-jre,containerizingMode=packaged), publicada eneurope-west3-docker.pkg.dev/pi-saldum/pi-repo/pickup-point-update-db:<tag>. ElCLAUDE.mdmencionaeclipse-temurin:25-jdk-alpinecomo imagen base Docker, que no coincide con la configuración actual delpom.xml(usajib-maven-plugin, no unDockerfilepropio). - Orquestación: Kubernetes
CronJob(k8s/cronjob.yaml) en el clúster GKEpi-cluster-hw, namespacepi,concurrencyPolicy: Forbid,activeDeadlineSeconds: 3600, ejecutándose cada 30 minutos. - CI/CD (Jenkins): pipeline que sustituye
application-pro.propertiesporapplication.propertiesantes de construir la imagen. - Las variables sensibles se inyectan en el pod mediante un
Secretde Kubernetes llamado igual que la app (pickup-point-update-db).
Job de Jenkins: https://jenkins-pi.hawkersco.net/job/pickup-point-update-db/
12. Manejo de errores y logging
No hay una estrategia de excepciones centralizada (no hay @ControllerAdvice, es un runner). PickupPointUpdateDbOrchestrator captura cualquier excepción de cada procesador de forma independiente (CompletableFuture.runAsync con try/catch interno) y la registra con logger.error, sin que el fallo de un procesador afecte al otro. PickupPointUpdateDbRunner distingue explícitamente la respuesta de "cuota agotada/prohibido" de BigDataCloud (402/403) mediante una excepción interna (BigDataCloudQuotaException) para reintentar ese punto en la siguiente ejecución en lugar de descartarlo. Logging mediante SLF4J (consola).
13. Notas y consideraciones
CLAUDE.mddescribe una arquitectura completamente distinta a la actual: el documento afirma que toda la lógica vive en un únicoCommandLineRunner(PickupPointUpdateDbRunner) que filtra pedidos por un código de carrier GLS hardcodeado (457895). En el código real,PickupPointUpdateDbRunnerya no filtra por carrier (procesa todos los puntos conisUpdated=falsesin distinción de transportista), y la aplicación se reestructuró alrededor de un patrón de procesadores (PickupPointUpdateDbProcessor) orquestados en paralelo porPickupPointUpdateDbOrchestrator. Además, existe una funcionalidad completa — las reglas de exclusión de visibilidad (PickupPointExclusionRulesDbRunner, entidadPickupPointExclusionRule) — que no se menciona en absoluto enCLAUDE.md. Es la discrepancia más relevante encontrada en este proyecto; se recomienda regenerar el documento por completo.- Puntos sin coordenadas se marcan como "actualizados" sin haber sido enriquecidos: en
PickupPointUpdateDbRunner.classifyPickupPoints, los puntos conptCoordinates == nullse omiten del procesamiento (ni se añaden atoUpdateni adoNotUpdate), peropersistResultscalcula los "omitidos" como la diferencia entre la lista completa y los otros dos conjuntos, y les asignaisUpdated=trueigualmente. El resultado neto es que un punto sin coordenadas queda marcado como procesado para siempre, sin que nunca llegue a tener provincia/código ISO asignado ni se reintente en ejecuciones futuras. Si esto no es intencional, es un defecto que deja datos incompletos de forma permanente. - Escaneo de entidades de una librería no declarada como dependencia:
PickupPointUpdateDbConfig.persistenceManagedTypesescanea tantocom.hawkersco.logisticscommons.daocomocom.hawkersco.glspickupclient.dao, perogls-pickup-clientno aparece entre las dependencias delpom.xmlde este proyecto. Es probable que sea un resto de una versión anterior (quizá compartía código conpickup-point-create-db) sin limpiar tras la refactorización. pickup.processorsrestringe el comportamiento en local pero no en producción: enapplication.properties(local) solo se ejecuta el procesador de reglas de exclusión; enapplication-pro.propertiesla propiedad está vacía, por lo que en producción se ejecutan ambos procesadores. Es un patrón razonable para evitar disparar llamadas a Nominatim/BigDataCloud durante el desarrollo local, pero conviene documentarlo explícitamente para que no se interprete como que la geocodificación está deshabilitada por error.- Ver alerta de seguridad en la sección 7 sobre credenciales reales expuestas en
application.properties.