Skip to main content

logistic-eu

1. Descripción general

Según el pom.xml, el proyecto se describe como "Orders send to logistic from Europa". Es un microservicio batch (runner) que recoge pedidos europeos (y de otros orígenes agrupados aquí, ver sección 13) pendientes de envío desde la base de datos de logística y los envía al almacén operado por Auro, generando factura en PDF cuando corresponde, gestionando cancelaciones, corrección de errores de teléfono y reintentos.

2. Información técnica

CampoValor
artifactIdlogistic-eu
groupIdcom.hawkersco
version1.0.25
Java25
Spring Boot4.0.6
Tipo de artefactojar (ejecutable, Spring Boot batch/CLI, spring.main.web-application-type=none)
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 7 CommandLineRunner, todos activos, ejecutados en el orden real verificado en el código (que difiere del descrito en CLAUDE.md, ver hallazgo en la sección 13):

Orden real (getOrder())RunnerPropósito
1LogisticEuEciRunnerPedidos de El Corte Inglés (id_source=62)
1LogisticEuMpRunnerPedidos de marketplace genérico (findByOrdersNoProcessedMpEu) — mismo valor de orden que LogisticEuEciRunner (ver hallazgo)
2LogisticEuRunnerPedidos directos EU principales (id_source en 8,17,36,58,61)
3LogisticEuPrivaliaRunnerPedidos de Privalia (id_source=44)
4LogisticEuErrorRunnerCorrige errores de teléfono tras 5 días; cancela pedidos atascados >15 días a las 11:00
5LogisticEuBrRunnerPedidos vía findByOrdersNoProcessedBrrunner completo no documentado en absoluto en CLAUDE.md (ver hallazgo)
6LogisticEuCancelledRunnerEnvía solicitudes de cancelación a Auro (id_source=45 + CANCELLED_BY_USER), y llama a System.exit() al terminar
  • .configLogisticEuConfiguration, LogisticEuOldConf, PDFThymeleafConfiguration (motor de plantillas para las facturas).
  • .modelsProductSend.
  • .utilsLogisticEuAuroUtils (construcción del JSON OrderAuroRequest, generación de factura PDF, comprobación de stock, embalaje, líneas creativas/personalización, envío a tienda), LogisticEuUtils (validación/corrección de código postal, generación de PDF, adjuntos de correo), LogisticEuConst (constantes centrales: listas de id_source, cadenas de estado, formato de zona horaria).
flowchart TD
A["1. Eci / Mp Runners"] -->|PENDING_SHIPMENT| B[(logistics · Order)]
C["2. LogisticEuRunner"] --> B
D["3. LogisticEuPrivaliaRunner"] --> B
E["4. LogisticEuErrorRunner"] -->|corrige teléfono / cancela >15 días| B
F["5. LogisticEuBrRunner"] --> B
A -->|postDocumentos| G[Auro Warehouse API]
C --> G
D --> G
F --> G
G -->|éxito| H[GCS response/ok/]
G -->|error| I[GCS response/ko/]
A -.->|factura PDF| J[SFTP /src/invoices/]
K["6. LogisticEuCancelledRunner"] -->|cancelaDoc, luego System.exit| G

4. Dependencias principales

DependenciaPropósito
spring-boot-starterNúcleo de Spring Boot (sin web, es un runner CLI)
spring-boot-starter-mailEnvío de correo (Andorra → equipo legal)
spring-boot-starter-thymeleafPlantillas HTML para generación de factura PDF
org.apache.poi:poi / poi-ooxmlManejo de hojas de cálculo (soporte auxiliar)
com.sun.xml.bind:jaxb-core / jaxb-implProcesamiento XML
commons-fileupload, commons-ioUtilidades de fichero
com.googlecode.json-simple:json-simpleParseo JSON auxiliar
com.hawkersco:auro-clientCliente @HttpExchange para la API del almacén Auro (postDocumentos, cancelaDoc, getFotoInventario)
com.hawkersco:logistics-commonsDAOs y servicios compartidos (Order, OrderLine, Country, OrderError, etc.)
com.hawkersco:pi-function-commonsDateUtils, StorageUtils, SftpUtils, SeveralUtils, DirectoryUtils
com.hawkersco:slack-clientNotificaciones de Slack

5. API / Endpoints

No aplica a este proyecto. Es un batch/runner sin capa REST.

6. Integraciones externas

SistemaProtocoloDirecciónDetalle
Auro Warehouse APIHTTP REST (AuroClient)SalienteEnvío (postDocumentos), cancelación (cancelaDoc) y consulta de foto de inventario
Google Cloud Storage (bucket pi-logistics-segment)API de GCSSalienteAlmacenamiento de respuestas de la API Auro en `logistic-eu/response/ok
Servidor SFTP (sftp.hawkersco.com)SFTPSalienteSubida de facturas PDF (/src/invoices/) y de pedidos a Auro (/src/orders/, credenciales de FTP independientes)
SlackHTTP (SlackClient)SalienteAlertas en 3 canales: general, ATC y almacén
Correo electrónico (SMTP)spring-boot-starter-mailSalienteNotificación al equipo legal para pedidos con destino Andorra
PostgreSQL (logistics)JDBCEntrante/SalienteLectura de pedidos pendientes y actualización de su estado

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ón
spring.datasource.*Credenciales de la BD logistics
gcs.bucket.nameBucket de GCS para respuestas de Auro
auro.auth.client.url / .app / .username / .passwordCredenciales de autenticación de la API Auro
logisticeu.ftp.*Credenciales SFTP para subida de facturas
logisticeu.auro.ftp.*Credenciales SFTP independientes para subida de pedidos a Auro
hawkers.order.invoice.countrycode.list / .provincecode.listPaíses/provincias que requieren factura
hawkers.orders.testMarcador de pedidos de prueba (lahermanadeaxel)
hw.regex.source / nw.regex.sourceExpresiones regulares para distinguir pedidos Hawkers/Northweek
creatives.skusSKUs considerados líneas creativas/personalización
slack.client.url / .auth.token / .channel.id / .channel-whs-not.id / .channel-atc.idConfiguración de Slack (3 canales)

⚠️ 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 logistics (la misma ya señalada como expuesta en múltiples proyectos de este ecosistema), la contraseña de autenticación de la API Auro, dos contraseñas SFTP independientes (una para subida de facturas, otra para subida de pedidos a Auro), y el token de bot de Slack. Ninguno de estos valores se ha reproducido en este documento. Se recomienda:

  1. Rotar la contraseña de BD, la contraseña de Auro, ambas contraseñas SFTP y el token de Slack.
  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 (spring.jpa.hibernate.ddl-auto=none). Entidades relevantes: Order, OrderLine, OrderError, Country. 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 5 minutos (schedule: "0/5 * * * *", concurrencyPolicy: Forbid) — el CLAUDE.md indica una periodicidad de 10 minutos, que no coincide con el manifiesto real. Se ejecutan en orden los 7 runners de la tabla de la sección 3.

10. Ejecución en local

Requisitos previos: JDK 25, Maven, acceso a la BD logistics, credenciales válidas de la API Auro y de ambos servidores SFTP.

# Compilar con tests
./mvnw clean install

# Compilar sin tests
./mvnw -DskipTests clean install

No existen tests en este repositorio (src/test/ está vacío). Al ser un CommandLineRunner, no expone Actuator/health: la verificación se hace revisando el log de consola o el estado de los pedidos en la BD logistics.

11. Despliegue

  • Imagen: construida con jib-maven-plugin (base eclipse-temurin:25-jre, containerizingMode=packaged, incluye directorios extra templates/ y plantilla/ para las facturas), publicada en europe-west3-docker.pkg.dev/pi-saldum/pi-repo/logistic-eu:<tag>.
  • Orquestación: Kubernetes CronJob en el clúster GKE pi-cluster-hw, namespace pi, con credenciales de cuenta de servicio de GCP montadas por volumen, ejecutándose cada 5 minutos.
  • CI/CD (Jenkins): pipeline real de 3 etapas — CheckoutBuild & PushDeploy to GKE.

Job de Jenkins: https://jenkins-pi.hawkersco.net/job/logistic-eu/

12. Manejo de errores y logging

No hay una estrategia de excepciones centralizada (no hay @ControllerAdvice, es un runner). Cada runner envuelve el procesamiento de cada pedido individual en su propio try/catch, notificando a Slack y continuando con el resto del lote. Tras 4 reintentos fallidos (nm_send_logistic >= MAX_RETRIES), el pedido se marca como error definitivo (OrderError persistido) y se notifica al canal de ATC con un enlace directo a infranete.hawkersco.net. Logging mediante java.util.logging.Logger estándar (consola).

13. Notas y consideraciones

  • LogisticEuBrRunner no está documentado en absoluto en CLAUDE.md: existe como séptimo runner activo (@Order(5)), con una lógica de construcción y envío del documento Auro casi idéntica a la de LogisticEuRunner/LogisticEuPrivaliaRunner (misma generación de factura PDF vía Thymeleaf, misma subida SFTP a /src/invoices/, mismo mecanismo de reintento y alerta a Slack tras 4 fallos), pero alimentado por una consulta distinta (findByOrdersNoProcessedBr). El CLAUDE.md no lo menciona en su tabla de arquitectura ni en ningún otro punto del documento — es la omisión más significativa encontrada en este proyecto.
  • Tabla de orden de ejecución de CLAUDE.md desactualizada: además de omitir LogisticEuBrRunner, asigna a LogisticEuEciRunner el orden 0 (el valor real en código es 1, empatado con LogisticEuMpRunner) y a LogisticEuCancelledRunner el orden 5 (el valor real es 6). El orden de ejecución real, verificado directamente en el código, es: Eci/Mp (ambos en 1) → Runner (2) → Privalia (3) → Error (4) → Br (5) → Cancelled (6).
  • LogisticEuEciRunner y LogisticEuMpRunner comparten el mismo valor de getOrder() (1): al ser beans de tipos distintos con el mismo valor de orden, Spring no garantiza un desempate estable entre ambos — el orden relativo de ejecución entre estos dos runners concretos depende de un criterio no especificado (típicamente el orden de descubrimiento del classpath). No representa un riesgo funcional grave porque ambos procesan orígenes de pedido disjuntos (ECI vs. marketplace genérico), pero es una inconsistencia de diseño que convendría corregir asignando valores de orden únicos.
  • Periodicidad del CronJob distinta de la documentada: ver hallazgo en la sección 9 (CLAUDE.md dice 10 minutos, el manifiesto real usa 5 minutos).
  • El resto de la arquitectura descrita en CLAUDE.md (ciclo de vida del pedido, detección de pedidos de prueba/fraudulentos, lentes de contacto, DOM/TOM de Francia, reglas de generación de factura) coincide con las utilidades compartidas (LogisticEuUtils, LogisticEuAuroUtils, LogisticEuConst) usadas de forma consistente por todos los runners.
  • Ver alerta de seguridad en la sección 7 sobre credenciales reales expuestas en application.properties.