Skip to main content

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

CampoValor
artifactIdpickup-point-update-db
groupIdcom.hawkersco
version1.0.25
Java25 (maven.compiler.release=25)
Spring Boot4.0.6
Tipo de artefactojar (ejecutable, Spring Boot batch/CLI)
MódulosNo 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 beans PickupPointUpdateDbProcessor registrados, filtra los activos según la propiedad pickup.processors (lista separada por comas; vacía = todos), y los ejecuta en paralelo con un ExecutorService de tamaño igual al número de procesadores activos, esperando a que todos terminen antes de cerrar la JVM.
  • PickupPointUpdateDbRunner (procesador pickup-point-update) — geocodificación inversa.
  • PickupPointExclusionRulesDbRunner (procesador pickup-point-exclusion-rules) — aplicación de reglas de exclusión de visibilidad.
  • .configPickupPointUpdateDbConfig (beans PickupPointService, 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

DependenciaPropósito
spring-boot-starterNúcleo de Spring Boot (sin web, es un runner CLI)
com.hawkersco:logistics-commons:1.0.25-SNAPSHOTPickupPoint, 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

SistemaProtocoloDirecciónDetalle
Nominatim/OpenStreetMap (nominatim.openstreetmap.org)HTTP REST (JSON)EntranteGeocodificació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)EntranteGeocodificación inversa de respaldo, usada cuando Nominatim no aporta provincia o código ISO
PostgreSQL (logistics)JDBCEntrante/SalienteLectura/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).

ClaveDescripciónValor localValor producción
spring.datasource.url / .username / .passwordCredenciales de la BD logisticsvalores reales${dbLogisitcsUrl}, etc.
bigdatacloud.api-keyAPI key de BigDataCloud (geocodificación de respaldo)valor real${bigDataCloudApiKey}
pickup.processorsLista 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:

  1. Rotar la contraseña de BD y la API key de BigDataCloud expuestas.
  2. Sustituir los valores hardcodeados de application.properties por credenciales de un entorno de desarrollo aislado.
  3. 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 con isUpdated=false ordenados 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 marca isUpdated=true. Los puntos sin provincia/ISO resuelto (o cuando BigDataCloud devuelve cuota agotada/prohibido) se guardan con isUpdated=false para reintentarse en la siguiente ejecución. Los puntos sin coordenadas se marcan directamente como isUpdated=true sin 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 como isVisible=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 (base eclipse-temurin:25-jre, containerizingMode=packaged), publicada en europe-west3-docker.pkg.dev/pi-saldum/pi-repo/pickup-point-update-db:<tag>. El CLAUDE.md menciona eclipse-temurin:25-jdk-alpine como imagen base Docker, que no coincide con la configuración actual del pom.xml (usa jib-maven-plugin, no un Dockerfile propio).
  • Orquestación: Kubernetes CronJob (k8s/cronjob.yaml) en el clúster GKE pi-cluster-hw, namespace pi, concurrencyPolicy: Forbid, activeDeadlineSeconds: 3600, ejecutándose cada 30 minutos.
  • CI/CD (Jenkins): pipeline que sustituye application-pro.properties por application.properties antes de construir la imagen.
  • Las variables sensibles se inyectan en el pod mediante un Secret de 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.md describe una arquitectura completamente distinta a la actual: el documento afirma que toda la lógica vive en un único CommandLineRunner (PickupPointUpdateDbRunner) que filtra pedidos por un código de carrier GLS hardcodeado (457895). En el código real, PickupPointUpdateDbRunner ya no filtra por carrier (procesa todos los puntos con isUpdated=false sin distinción de transportista), y la aplicación se reestructuró alrededor de un patrón de procesadores (PickupPointUpdateDbProcessor) orquestados en paralelo por PickupPointUpdateDbOrchestrator. Además, existe una funcionalidad completa — las reglas de exclusión de visibilidad (PickupPointExclusionRulesDbRunner, entidad PickupPointExclusionRule) — que no se menciona en absoluto en CLAUDE.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 con ptCoordinates == null se omiten del procesamiento (ni se añaden a toUpdate ni a doNotUpdate), pero persistResults calcula los "omitidos" como la diferencia entre la lista completa y los otros dos conjuntos, y les asigna isUpdated=true igualmente. 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.persistenceManagedTypes escanea tanto com.hawkersco.logisticscommons.dao como com.hawkersco.glspickupclient.dao, pero gls-pickup-client no aparece entre las dependencias del pom.xml de este proyecto. Es probable que sea un resto de una versión anterior (quizá compartía código con pickup-point-create-db) sin limpiar tras la refactorización.
  • pickup.processors restringe el comportamiento en local pero no en producción: en application.properties (local) solo se ejecuta el procesador de reglas de exclusión; en application-pro.properties la 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.