Coppel Create DB
1. Descripción general
coppel-create-db es un microservicio batch (según el pom.xml, "Get orders by coppel") que ingiere en la base de datos logística de Hawkers los pedidos del marketplace mexicano Coppel. No expone ninguna API HTTP: es una aplicación Spring Boot basada en varios CommandLineRunner ordenados que se ejecutan hasta completar su trabajo y terminan, desplegada como un CronJob de Kubernetes cada 15 minutos.
El flujo cubre tres responsabilidades: (1) descargar pedidos nuevos en estado SHIPPING y persistirlos en base de datos, (2) descargar en lote las etiquetas de envío PDF de los pedidos pendientes de etiqueta y promoverlos a "pendiente de envío", y (3) detectar pedidos cancelados en Coppel y reflejar la cancelación en la base de datos (esta última fase está desactivada actualmente). Notifica incidencias operativas a Slack. Dentro del ecosistema Hawkers, cumple el mismo rol que bradery-create-db: puente entre un marketplace externo y el modelo de datos interno de pedidos.
2. Información técnica
| Propiedad | Valor |
|---|---|
artifactId | coppel-create-db |
groupId | com.hawkersco |
version | 1.0.25 |
| Java | 25 |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | JAR ejecutable (imagen de contenedor vía Jib) |
| Módulos | Proyecto único (no multi-módulo) |
3. Arquitectura y diseño
Estructura del proyecto:
com.hawkersco.coppelcreatedb
├── CoppelCreateDbApplication.java # @SpringBootApplication + @EnableJpaRepositories
├── CoppelCreateDbRunner.java # CommandLineRunner, orden 1: ingiere pedidos nuevos en estado SHIPPING
├── CoppelProcessLabelRunner.java # CommandLineRunner, orden 2: descarga y asocia etiquetas de envío
├── CoppelCreateDbCheckRunner.java # CommandLineRunner, orden 3: detecta cancelaciones — DESACTIVADO (@Component comentado)
├── config/
│ └── CoppelCreateDbConfig.java # Declara manualmente los @Bean de servicios de logistics-commons
└── util/
└── CoppelCreateDbUtil.java # Transformación de pedido Coppel → entidades logistics-commons, notificaciones Slack
Flujo principal (runners ordenados)
sequenceDiagram
participant CronJob as CronJob K8s (cada 15 min)
participant R1 as CoppelCreateDbRunner (orden 1)
participant Coppel as API Coppel (Mirakl)
participant DB as PostgreSQL (logistics)
participant R2 as CoppelProcessLabelRunner (orden 2)
participant Slack as Slack
CronJob->>R1: run()
loop paginado (100/página, 10s entre páginas)
R1->>Coppel: getOrderListByStateCode(SHIPPING, últimos 5 días)
Coppel-->>R1: pedidos
R1->>R1: omite WAITING_DEBIT
R1->>DB: si no existe, crea OrderMarketplace, Customer, Order, OrderLine, Shipment, OrderShipmentStatus (PENDING_LABEL)
end
CronJob->>R2: run()
R2->>DB: findOrderByListSourceAndListCdProcessingStatusLimit(Coppel, PENDING_LABEL, máx 50)
R2->>Coppel: downloadDocumentsByOrderList(orderIds) → ZIP
R2->>R2: descomprime, busca PDF por tracking o alternativo
alt etiqueta encontrada
R2->>DB: guarda etiqueta en base64, estado → PENDING_SHIPMENT
else sin etiqueta tras 48h
R2->>DB: estado → ERROR_LABEL
R2->>Slack: alerta al canal ATC-MX
end
R2->>R2: System.exit(SpringApplication.exit(context))
CoppelCreateDbCheckRunner (orden 3, detección de cancelaciones) tiene su anotación @Component comentada en el código — está completamente desactivado y no se ejecuta en ningún despliegue actual, pese a estar documentado como parte del flujo en CLAUDE.md y en el propio Javadoc de la clase ("Currently disabled — restore @Component to activate").
CoppelCreateDbConfig declara manualmente cada servicio de logistics-commons como @Bean (mismo patrón que bradery-create-db y chatbot, ya que estos servicios no se auto-detectan por @ComponentScan al vivir en una librería externa).
4. Dependencias principales
| Dependencia | Versión | Propósito |
|---|---|---|
spring-boot-starter | (gestionada SB4) | Base de Spring Boot (contexto, CommandLineRunner) |
spring-web | (gestionada SB4) | Soporte HTTP para los clientes @HttpExchange inyectados |
com.hawkersco:coppel-client | 1.0.25-SNAPSHOT | Cliente @HttpExchange de la API Coppel/Mirakl (CoppelClient), modelo OrdersCoppelResponse |
com.hawkersco:logistics-commons | 1.0.25-SNAPSHOT | Entidades JPA (Order, OrderLine, OrderMarketplace, Customer, Shipment, etc.) y servicios |
com.hawkersco:pi-function-commons | 1.0.25-SNAPSHOT | Utilidades DateUtils, ZipUtils, DirectoryUtils |
com.hawkersco:slack-client | 1.0.25-SNAPSHOT | Cliente @HttpExchange de Slack, usado activamente para alertas operativas |
spring-boot-starter-test | (gestionada SB4) | Testing (scope test) |
A diferencia de bradery-create-db y chatbot, aquí slack-client sí está correctamente declarado en el pom.xml y usado en el código (CoppelProcessLabelRunner, CoppelCreateDbUtil, CoppelCreateDbCheckRunner).
5. API / Endpoints
No aplica a este proyecto. coppel-create-db es un batch CommandLineRunner sin servidor HTTP ni controladores REST.
6. Integraciones externas
| Sistema | Protocolo | Dirección | Descripción |
|---|---|---|---|
API Coppel/Mirakl (vía coppel-client) | HTTP REST | Saliente | Listado de pedidos por estado (SHIPPING, CANCELED) y descarga de documentos ZIP con etiquetas |
Slack (vía slack-client) | HTTP REST | Saliente | Alertas de pedidos sin etiqueta tras 48h, pedidos con Source ID no registrado, errores al guardar Shipment, y (en el runner desactivado) notificación de cancelaciones |
PostgreSQL (logistics, vía logistics-commons) | JDBC | Saliente | Persistencia de pedidos, clientes, líneas, envíos y estado de etiqueta |
Convenciones de negocio relevantes:
- Todos los pedidos de Coppel se asocian al Source ID 55 (
SOURCE_ID_COPPEL, constante duplicada en los tres runners y enCoppelCreateDbUtil). - El nombre interno del pedido sigue el formato
MKCOPPEL+ sufijo numérico secuencial de 7 cifras con ceros a la izquierda, derivado demax(idOrderMarketplace) + 1. - Flujo de estados del pedido:
PENDING_LABEL(al crearse) →PENDING_SHIPMENT(cuando se encuentra la etiqueta) →ERROR_LABEL(si no aparece etiqueta tras 48 horas) /CANCELLED(si Coppel cancela el pedido, vía el runner desactivado). CoppelCreateDbRunnerpausa 10 segundos entre páginas de la API Coppel y 30 segundos adicionales si una página falla conRestClientResponseException, presumiblemente para no saturar la API o respetar límites de tasa.
7. Configuración
El proyecto usa dos perfiles: application.properties (desarrollo, con valores reales) y application-pro.properties (producción, con placeholders ${VAR} inyectados por Kubernetes). El Jenkinsfile sustituye el primero por el segundo antes de empaquetar.
Grupos de propiedades
| Propiedad | Descripción | Valor en producción |
|---|---|---|
spring.datasource.url/username/password | Conexión PostgreSQL de logística | ${dbLogisitcsUrl} / ${dbLogisitcsUsername} / ${dbLogisitcsPassword} (sic, con el mismo typo "Logisitcs" observado en chatbot) |
coppel.credentials.url | URL base de la API Coppel | ${coppelCredUrl} |
coppel.credentials.key | Clave de autenticación de la API Coppel | ${coppelCredKey} |
slack.client.url / slack.auth.token | Configuración del cliente Slack | ${slackClientUrl} / ${slackAuthToken} |
slack.channel.id | Canal Slack para alertas generales (Source ID no registrado, error de Shipment) | ${slackChannelId} |
slack.channel-atc-mx.id | Canal Slack específico para alertas de etiquetas faltantes (equipo ATC-MX) | ${slackChannelAtcMxId} |
⚠️ Alerta de seguridad: El fichero
src/main/resources/application.properties(perfil de desarrollo, versionado en el repositorio) contiene credenciales reales en texto plano: contraseña de la base de datos PostgreSQL, clave de la API de Coppel y token de bot de Slack. Ninguno de estos valores se reproduce en este documento. Se recomienda rotarlos y migrar el perfil local al mismo patrón de variables de entorno que ya usaapplication-pro.properties. Mismo hallazgo que enbradery-create-dbychatbot, sugiriendo que es una práctica extendida en los runners*-create-db/batch de este ecosistema.
Variables de entorno (perfil de producción)
| Variable | Propiedad mapeada |
|---|---|
dbLogisitcsUrl / Username / Password | spring.datasource.* |
coppelCredUrl / coppelCredKey | coppel.credentials.url / .key |
slackClientUrl / slackAuthToken | slack.client.url / slack.auth.token |
slackChannelId | slack.channel.id |
slackChannelAtcMxId | slack.channel-atc-mx.id |
8. Persistencia
Base de datos: PostgreSQL (logistics), accedida vía JPA/Hibernate a través de las entidades de logistics-commons.
Entidades principales manipuladas por este runner:
| Entidad | Rol |
|---|---|
OrderMarketplace | Vínculo entre el pedido interno (MKCOPPEL...) y el pedido de Coppel; también almacena la etiqueta en base64 (updateLabel) |
Customer / CustomerSource | Cliente asociado al pedido, con origen registrado para Source ID 55 |
Order | Pedido logístico completo; rawData guarda el JSON crudo del pedido Coppel (con formato de fecha específico vía Gson) |
OrderLine | Líneas de producto, con resolución de SKU interno vía ProductMarketplaceService cuando existe mapeo |
Shipment / ShipmentLine | Envío generado a partir del pedido y sus líneas |
OrderShipmentStatus | Estado inicial de envío (PENDING_SHIPMENT) |
No se han encontrado migraciones Flyway/Liquibase en este proyecto (spring.jpa.hibernate.ddl-auto=none); el esquema se gestiona externamente, compartido con otros proyectos del ecosistema.
9. Procesos programados y mensajería
No hay @Scheduled en el código: la periodicidad se gestiona vía Kubernetes. k8s/cronjob.yaml define un CronJob con expresión 0/15 * * * * (cada 15 minutos) y concurrencyPolicy: Forbid (no permite solapar ejecuciones).
Dentro de cada ejecución, los CommandLineRunner activos (CoppelCreateDbRunner, orden 1; CoppelProcessLabelRunner, orden 2) se ejecutan secuencialmente al arrancar el contexto de Spring. CoppelCreateDbCheckRunner (orden 3) no se ejecuta al estar desactivado.
10. Ejecución en local
Requisitos previos
- Java 25
- Maven 3.x
- Acceso a la base de datos PostgreSQL de logística
- Credenciales válidas de la API de Coppel
- Configuración de Slack (URL, token, IDs de canal) si se quiere probar el envío de alertas
Comandos
# Build sin tests (como en CI)
./mvnw -B -DskipTests clean install
# Ejecutar tests
./mvnw test
# Ejecutar un test concreto
./mvnw test -Dtest=CoppelCreateDbApplicationTests
# Build de imagen Docker
docker build -t coppel-create-db .
Al no tener servidor HTTP, no hay endpoint de health; la verificación de correcta ejecución se hace revisando los logs (CoppelCreateDbRunner - END, CoppelProcessLabelRunner - END) o el estado del Job generado por el CronJob en Kubernetes.
Nota sobre el directorio zip/ en el repositorio local: el proyecto usa el directorio zip/ en el directorio de trabajo como área temporal para descargar y descomprimir el ZIP de etiquetas de Coppel (CoppelProcessLabelRunner.run() lo borra y recrea al inicio de cada ejecución). En el checkout local de este repositorio se han encontrado ficheros PDF y un labels.zip residuales dentro de zip/, aparentemente de ejecuciones previas — ver advertencia de privacidad en la sección 13.
11. Despliegue
Se despliega como imagen de contenedor en GKE, orquestada por un CronJob, con el mismo patrón que bradery-create-db y chatbot:
- Checkout — descarga el código.
- Build & Push — sustituye
application.propertiesporapplication-pro.properties, compila conmvn clean package jib:build -DskipTests -U -Dimage.tag=${BUILD_NUMBER}y publica eneurope-west3-docker.pkg.dev/pi-saldum/pi-repo/coppel-create-db:${BUILD_NUMBER}. - Deploy to GKE — obtiene credenciales del clúster
pi-cluster-hw(zonaeurope-west3-a), elimina elCronJobexistente y aplicak8s/cronjob.yamlen el namespacepi.
Imagen base de runtime (Jib): eclipse-temurin:25-jre.
Job de Jenkins:
https://jenkins-pi.hawkersco.net/job/coppel-create-db/
12. Manejo de errores y logging
Los tres runners usan java.util.logging.Logger.
CoppelCreateDbRunner: capturaRestClientResponseExceptionpor pedido individual dentro del bucle (no por página completa), registra el error y espera 30 segundos antes de continuar con el siguiente pedido — un fallo aislado al persistir un pedido no detiene el procesamiento de los demás.CoppelProcessLabelRunner: no envuelve su lógica principal en untry/catchgeneral (la firmarun()declarathrows Exception); un fallo no controlado propagaría la excepción y probablemente detendría el proceso antes de llegar alSystem.exit()final.CoppelCreateDbUtil.saveShipment: capturaParseExceptiony notifica a Slack en lugar de propagar, dejando el pedido persistido sinShipmentasociado.- Los pedidos sin etiqueta tras 48 horas se marcan
ERROR_LABELy generan una alerta Slack específica al canal ATC-MX — es el único de los tres runners*-create-dbdocumentados hasta ahora que integra notificaciones operativas activas.
No hay configuración de logback específica; los logs usan el formato por defecto de java.util.logging.
13. Notas y consideraciones
-
⚠️ Posibles etiquetas de envío reales en el repositorio local: El checkout local de este proyecto contiene, dentro del directorio
zip/, ficheros PDF de etiquetas de envío (p. ej.zip/309639623-B/TMKP2646569281724807.pdf) y unlabels.zip, con cambios de trabajo sin commitear detectados en el control de versiones (ficheros añadidos/modificados/eliminados). Estos ficheros pueden contener datos personales de clientes (nombre, dirección de envío) si corresponden a pedidos reales descargados por una ejecución previa deCoppelProcessLabelRunner. No se ha inspeccionado el contenido de estos ficheros al redactar este documento. Se recomienda: (1) confirmar sizip/está en.gitignore(actualmente no lo está) y añadirlo si contiene datos generados en tiempo de ejecución, y (2) verificar si estos ficheros concretos llegaron a incluirse en algún commit del historial, en cuyo caso deberían purgarse del historial de Git por motivos de protección de datos. -
CoppelCreateDbCheckRunnerdesactivado pero mantenido en el código: La detección de cancelaciones existe y está completamente implementada, pero su@Componentestá comentado. Cualquier pedido cancelado en Coppel actualmente no se refleja en el estado interno del pedido ni genera notificación — pendiente de verificar si esta funcionalidad se gestiona por otra vía (manual, otro proceso) o si simplemente está pendiente de reactivarse. -
Constante
SOURCE_ID_COPPELduplicada en cuatro clases: Igual que enbradery-create-db, el Source ID (55L) se declara como constante privada independiente enCoppelCreateDbRunner,CoppelProcessLabelRunner,CoppelCreateDbCheckRunneryCoppelCreateDbUtil, sin una única fuente de verdad compartida. -
Typo "Logisitcs" consistente con
chatbot: Las variables de entornodbLogisitcsUrl/Username/Passwordreproducen el mismo error ortográfico visto enchatbot, sugiriendo que ambos proyectos comparten una plantilla de configuración con el typo ya incorporado. -
System.exit()solo en el runner de etiquetas: Igual que enchatbot, solo el último runner en ejecutarse (CoppelProcessLabelRunner, orden 2, dado que el orden 3 está desactivado) fuerzaSystem.exit(SpringApplication.exit(context));CoppelCreateDbRunnerno lo hace. -
Resolución de SKU interno best-effort:
transformToOrderLinesLogisticusa el SKU de Coppel (offerSku) como valor por defecto y solo lo sustituye por el SKU interno si existe un mapeo enProductMarketplaceService— si no hay mapeo, la línea de pedido queda con el SKU externo de Coppel en el campocdSku, lo que podría causar inconsistencias si otros procesos esperan siempre un SKU interno en ese campo. -
Sin tests funcionales: Solo existe
CoppelCreateDbApplicationTests(test de contexto por defecto); no hay pruebas sobre la lógica de negocio (transformación de pedidos, resolución de etiquetas, cálculo de umbral de 48 horas).