From c74e49e11ba266abb8b944f46948c23bf2a99f0d Mon Sep 17 00:00:00 2001 From: "Simon C. Kessler" Date: Wed, 13 May 2026 16:17:03 +0200 Subject: [PATCH] =?UTF-8?q?Upload=20der=20BA=20ZIP=20Dateien=20hinzugef?= =?UTF-8?q?=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 4 +- .../dateieingang/config/OciConfig.java | 42 +++++++++++++++++-- .../dateieingang/oci/OciUploadService.java | 37 ++++++++++++---- .../pipeline/FileProcessingPipeline.java | 5 +++ .../src/main/resources/application.properties | 12 ++++-- 5 files changed, 83 insertions(+), 17 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 84e2f73..e8d5c38 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -15,7 +15,9 @@ "WebFetch(domain:walidhajeri.hashnode.dev)", "Bash(find \"C:\\\\\\\\src\\\\\\\\Galabau\\\\\\\\glb-spielwiese\\\\\\\\automaton\" -name \"FileProcessingPipeline.java\")", "PowerShell(Get-ChildItem -Path \"C:\\\\src\\\\Galabau\\\\glb-spielwiese\\\\quarkus-automaton\\\\src\\\\main\\\\java\" -Recurse | Where-Object { !$_.PSIsContainer } | Select-Object FullName)", - "PowerShell(cmd /c \"dir /s /b C:\\\\src\\\\Galabau\\\\glb-spielwiese\\\\quarkus-automaton\\\\src\\\\main\\\\java\")" + "PowerShell(cmd /c \"dir /s /b C:\\\\src\\\\Galabau\\\\glb-spielwiese\\\\quarkus-automaton\\\\src\\\\main\\\\java\")", + "Bash(Get-ChildItem -Path \"C:\\\\src\\\\Galabau\\\\glb-spielwiese\" -Directory)", + "Bash(Select-Object Name)" ] } } diff --git a/quarkus-automaton/src/main/java/de/galabau/dateieingang/config/OciConfig.java b/quarkus-automaton/src/main/java/de/galabau/dateieingang/config/OciConfig.java index f6cefd3..a682637 100644 --- a/quarkus-automaton/src/main/java/de/galabau/dateieingang/config/OciConfig.java +++ b/quarkus-automaton/src/main/java/de/galabau/dateieingang/config/OciConfig.java @@ -16,16 +16,22 @@ public interface OciConfig { String bucket(); /** - * Root-Prefix für alle Objekte im Bucket, z.B. {@code mandant_42/}. + * Root-Prefix für alle Objekte im Bucket, z.B. {@code testmandant-42/}. * Muss mit {@code /} enden. */ String tenantPrefix(); /** - * Prefix für eingehende Dateien unterhalb von {@code tenantPrefix}, - * z.B. {@code eingang/}. Muss mit {@code /} enden. + * Gemeinsamer Basis-Prefix für alle BA-Eingangs-Pfade unterhalb von {@code tenantPrefix}, + * z.B. {@code BA/Eingang/}. Muss mit {@code /} enden. */ - String incomingKorrespondenzenPrefix(); + String baBasePrefix(); + + /** Konfiguration für die BA-Korrespondenzen-Pipeline. */ + Korrespondenzen korrespondenzen(); + + /** Konfiguration für die BA-Aufrechnungen-Pipeline. */ + Aufrechnungen aufrechnungen(); /** OCI Tenancy OCID. Aus Env-Var {@code OCI_TENANCY_ID}. */ String tenancyId(); @@ -49,4 +55,32 @@ public interface OciConfig { * Muss mit der APEX Automation und dem ORDS-Package abgestimmt sein. */ String markerFilenameDbProcessing(); + + interface Korrespondenzen { + + /** + * Prefix für eingehende Korrespondenz-Dateien relativ zu {@code baBasePrefix}, + * z.B. {@code Import/BA-Korrespondenzen/}. Muss mit {@code /} enden. + * Vollständiger Pfad: {@code tenantPrefix + baBasePrefix + incomingPrefix}. + */ + String incomingPrefix(); + + /** + * Prefix für archivierte ZIP-Originaldateien relativ zu {@code baBasePrefix}, + * z.B. {@code BA-Korrespondenzen ZIP-Dateien}. Kein abschließendes {@code /} — + * das aktuelle Jahr wird zur Laufzeit angehängt: {@code /}. + * Vollständiger Pfad: {@code tenantPrefix + baBasePrefix + archivePrefix + " 2026/"}. + */ + String archivePrefix(); + } + + interface Aufrechnungen { + + /** + * Prefix für eingehende Aufrechnungs-Dateien relativ zu {@code baBasePrefix}, + * z.B. {@code Import/Aufrechnungen/}. Muss mit {@code /} enden. + * Vollständiger Pfad: {@code tenantPrefix + baBasePrefix + incomingPrefix}. + */ + String incomingPrefix(); + } } diff --git a/quarkus-automaton/src/main/java/de/galabau/dateieingang/oci/OciUploadService.java b/quarkus-automaton/src/main/java/de/galabau/dateieingang/oci/OciUploadService.java index 2569479..3de1680 100644 --- a/quarkus-automaton/src/main/java/de/galabau/dateieingang/oci/OciUploadService.java +++ b/quarkus-automaton/src/main/java/de/galabau/dateieingang/oci/OciUploadService.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Year; import java.util.List; /** @@ -79,38 +80,56 @@ public class OciUploadService { Log.infof("OCI-Upload: %d Datei(en) für '%s'", files.size(), context.zipNameWithoutExt); for (FileEntry entry : files) { - String key = buildKey(context.zipNameWithoutExt, entry.relativePath); + String key = buildKorrespondenzenKey(context.zipNameWithoutExt, entry.relativePath); entry.ociKey = key; putFile(key, context.localExtractDir.resolve(entry.relativePath), entry.fileSize); Log.infof("Datei hochgeladen: %s (%d Bytes)", key, entry.fileSize); } Log.infof("OCI-Upload Dateien abgeschlossen: %d Datei(en) in '%s'", - files.size(), buildPrefix(context.zipNameWithoutExt)); + files.size(), buildKorrespondenzenPrefix(context.zipNameWithoutExt)); } /** * Setzt den Marker in OCI — signalisiert der DB-Verarbeitung, dass der Batch vollständig ist. - * Wird erst nach dem SFTP-Rename zu {@code .processed} aufgerufen, damit Marker und - * SFTP-Zustand immer konsistent sind: Marker vorhanden ↔ ZIP bereits als verarbeitet markiert. + * Wird erst nach dem SFTP-Delete aufgerufen, damit Marker und + * SFTP-Zustand immer konsistent sind: Marker vorhanden ↔ ZIP bereits vom SFTP gelöscht. * * @param context enthält den Ziel-Prefix für den Marker-Key * @throws OciException bei Verbindungs- oder Upload-Fehlern */ public void uploadMarker(ProcessingContext context) throws OciException { - String markerKey = buildKey(context.zipNameWithoutExt, config.markerFilenameDbProcessing()); + String markerKey = buildKorrespondenzenKey(context.zipNameWithoutExt, config.markerFilenameDbProcessing()); Log.infof("Lade Marker hoch: '%s'", markerKey); putMarker(markerKey); context.markerUploaded = true; Log.infof("Marker hochgeladen: '%s'", markerKey); } - private String buildPrefix(String zipNameWithoutExt) { - return config.tenantPrefix() + config.incomingKorrespondenzenPrefix() + zipNameWithoutExt + "/"; + /** + * Lädt die Original-ZIP-Datei in den Archivordner in OCI hoch. + * Ziel-Key: {@code tenantPrefix + baBasePrefix + archivePrefix + " /" + zipFilename} + * + * @param context enthält den lokalen ZIP-Pfad und den Dateinamen + * @throws OciException bei Verbindungs- oder Upload-Fehlern + * @throws IOException bei Problemen beim Lesen der lokalen ZIP-Datei + */ + public void uploadZipFile(ProcessingContext context) throws OciException, IOException { + String yearFolder = config.korrespondenzen().archivePrefix() + " " + Year.now().getValue() + "/"; + String key = config.tenantPrefix() + config.baBasePrefix() + yearFolder + context.zipFilename; + long fileSize = Files.size(context.localZipPath); + Log.infof("Lade ZIP-Archiv hoch: '%s' (%d Bytes)", key, fileSize); + putFile(key, context.localZipPath, fileSize); + Log.infof("ZIP-Archiv hochgeladen: '%s'", key); } - private String buildKey(String zipNameWithoutExt, String relativePath) { - return buildPrefix(zipNameWithoutExt) + relativePath; + private String buildKorrespondenzenPrefix(String zipNameWithoutExt) { + return config.tenantPrefix() + config.baBasePrefix() + + config.korrespondenzen().incomingPrefix() + zipNameWithoutExt + "/"; + } + + private String buildKorrespondenzenKey(String zipNameWithoutExt, String relativePath) { + return buildKorrespondenzenPrefix(zipNameWithoutExt) + relativePath; } private void putFile(String key, Path localFile, long fileSize) throws OciException { diff --git a/quarkus-automaton/src/main/java/de/galabau/dateieingang/pipeline/FileProcessingPipeline.java b/quarkus-automaton/src/main/java/de/galabau/dateieingang/pipeline/FileProcessingPipeline.java index 8a63180..174c680 100644 --- a/quarkus-automaton/src/main/java/de/galabau/dateieingang/pipeline/FileProcessingPipeline.java +++ b/quarkus-automaton/src/main/java/de/galabau/dateieingang/pipeline/FileProcessingPipeline.java @@ -130,6 +130,11 @@ public class FileProcessingPipeline { Log.infof("ZIP '%s' heruntergeladen (%d Bytes)", zipFilename, Files.size(context.localZipPath)); + // --- OCI ZIP-Archiv --- + MDC.put("step", "oci-zip-archive"); + Log.info("Starte ZIP-Upload in OCI"); + ociUploadService.uploadZipFile(context); + // --- Entpacken --- MDC.put("step", "zip-extract"); zipExtractionService.extract(context); diff --git a/quarkus-automaton/src/main/resources/application.properties b/quarkus-automaton/src/main/resources/application.properties index 10b5658..a202046 100644 --- a/quarkus-automaton/src/main/resources/application.properties +++ b/quarkus-automaton/src/main/resources/application.properties @@ -12,7 +12,7 @@ galabau.sftp.password=${GALABAU_SFTP_PASSWORD:} # Fingerprint auf host: ssh-keyscan | ssh-keygen -lf - galabau.sftp.host-key-fingerprint=${GALABAU_SFTP_HOST_KEY_FINGERPRINT:SHA256:xyz} # Verzeichnis auf dem SFTP-Server, in dem der Lieferant ZIP-Dateien ablegt -galabau.sftp.remote-path=${GALABAU_SFTP_REMOTE_PATH:/bundesagenturfuerarbeit/austausch/dev/galaeingang} +galabau.sftp.remote-path=${GALABAU_SFTP_REMOTE_PATH:/bundesagenturfuerarbeit/austausch/sck-dev/galaeingang} # Temporäres lokales Verzeichnis für Download + Entpacken — wird nach jeder ZIP bereinigt galabau.sftp.local-work-dir=/tmp/sftp-work # galabau.sftp.private-key-path=/etc/secrets/sftp-key @@ -27,8 +27,14 @@ galabau.oci.region=${OCI_REGION} galabau.oci.bucket=${OCI_BUCKET} # Root-Prefix im Bucket, muss mit / enden galabau.oci.tenant-prefix=${OCI_TENANT_PREFIX:testmandant-42/} -# Eingangs-Prefix unterhalb von tenant-prefix, muss mit / enden -galabau.oci.incoming-korrespondenzen-prefix=${OCI_INCOMING_FILES_PATH:BA/Eingang/Import/BA-Korrespondenzen/} +# Gemeinsamer Basis-Prefix für alle BA-Eingangs-Pfade, muss mit / enden +galabau.oci.ba-base-prefix=${OCI_BA_BASE_PREFIX:BA/Eingang/} +# BA-Korrespondenzen: Eingangs-Prefix relativ zu ba-base-prefix, muss mit / enden +galabau.oci.korrespondenzen.incoming-prefix=${OCI_KORRESPONDENZEN_INCOMING_PREFIX:Import/BA-Korrespondenzen/} +# BA-Korrespondenzen: Archiv-Prefix relativ zu ba-base-prefix — Jahr wird zur Laufzeit angehängt +galabau.oci.korrespondenzen.archive-prefix=${OCI_KORRESPONDENZEN_ARCHIVE_PREFIX:BA-Korrespondenzen ZIP-Dateien} +# BA-Aufrechnungen: Eingangs-Prefix relativ zu ba-base-prefix, muss mit / enden +galabau.oci.aufrechnungen.incoming-prefix=${OCI_AUFRECHNUNGEN_INCOMING_PREFIX:Import/Aufrechnungen/} galabau.oci.tenancy-id=${OCI_TENANCY_ID} galabau.oci.user-id=${OCI_USER_ID} galabau.oci.fingerprint=${OCI_FINGERPRINT}