Verbesserung der Dokumentation und implementierung von automation funktion in pck_auto_import

This commit is contained in:
2026-04-21 14:51:50 +02:00
parent b2233e7da0
commit a5e2fc6720
7 changed files with 166 additions and 48 deletions

View File

@@ -62,6 +62,8 @@ Alle zur Laufzeit via `pck_system.f_get_par_wert_by_programmid`:
| `NETSTORE_BA_PREFIX` | Pfad in Object Storage, wo BA-Daten liegen. Muss mit einem `/` enden, z.B. `BA/Eingang/` | | `NETSTORE_BA_PREFIX` | Pfad in Object Storage, wo BA-Daten liegen. Muss mit einem `/` enden, z.B. `BA/Eingang/` |
| `NETSTORE_BA_IMPORT` | Name des Unterordners von NETSTORE_BA_PREFIX im Object Storage, wo entpackte Dateien, die noch importiert werden müssen, zwischengespeichert werden. | | `NETSTORE_BA_IMPORT` | Name des Unterordners von NETSTORE_BA_PREFIX im Object Storage, wo entpackte Dateien, die noch importiert werden müssen, zwischengespeichert werden. |
| `BA_IMPORT_SB_MIT_ID` | Mitarbeiter-ID für Import von BA Daten (z.B. Korrespondenzen). Diese Mitarbeiter-ID bekommt eine Wiedervorlage, für jede Datei, die nicht automatisch importiert werden konnte. | | `BA_IMPORT_SB_MIT_ID` | Mitarbeiter-ID für Import von BA Daten (z.B. Korrespondenzen). Diese Mitarbeiter-ID bekommt eine Wiedervorlage, für jede Datei, die nicht automatisch importiert werden konnte. |
| `AUTOMATON_BASE_URL` | Base-URL des Quarkus Dateieingang Service, z.B. `http://dateieingang:8080` |
| `AUTOMATON_API_KEY` | API-Key für den Quarkus Dateieingang Service (Header `X-Api-Key`) |
--- ---
@@ -156,7 +158,7 @@ Bucket nicht erreichbar → -20003 via `f_make_request`
## Sicherheit / Tenant-Isolation ## Sicherheit / Tenant-Isolation
`p_assert_allowed` wird am Anfang jeder öffentlichen Funktion/Prozedur aufgerufen: `p_assert_allowed` wird am Anfang jeder öffentlichen Funktion/Prozedur aufgerufen:
1. Tenant-Prefix laden via `f_get_cfg('NET_STORAGE_TENANT_ID')` 1. Tenant-Prefix laden via `f_get_cfg('NETSTORE_TENANT_ID')`
2. `..`-Sequenzen im übergebenen Key prüfen → -20004 2. `..`-Sequenzen im übergebenen Key prüfen → -20004
3. Prüfen ob Key mit Tenant-Prefix beginnt → -20005 3. Prüfen ob Key mit Tenant-Prefix beginnt → -20005
@@ -184,7 +186,22 @@ Die DB verarbeitet einen Unterordner **ausschließlich wenn der Marker vorhanden
Der Marker wird erst gelöscht wenn **alle** Dateien des Unterordners erfolgreich Der Marker wird erst gelöscht wenn **alle** Dateien des Unterordners erfolgreich
verarbeitet wurden — so werden fehlgeschlagene Dateien beim nächsten Lauf erneut versucht. verarbeitet wurden — so werden fehlgeschlagene Dateien beim nächsten Lauf erneut versucht.
### Prozedur `p_process_incoming_ba_data` (in `pck_auto_import`, nicht in pck_net_storage) ### Prozedur `p_run_ba_korrespondenz_dateieingang_automation` (in `pck_auto_import`) — APEX Automation Einstiegspunkt
Von der APEX Automation (stündlich) aufgerufen. Orchestriert den Gesamtablauf:
```
1. p_process_incoming_ba_data aufrufen
→ verarbeitet Batches die bereits in OCI liegen (Fallback, falls ORDS-Aufruf letzten Lauf fehlschlug)
→ Fehler eskalieren (APEX Automation markiert Lauf als fehlerhaft)
2. HTTP POST an AUTOMATON_BASE_URL/api/process-incoming (Header: X-Api-Key: AUTOMATON_API_KEY)
→ 202 Accepted: Pipeline gestartet
→ 409 Conflict: Service läuft bereits — kein zweiter Lauf, kein Fehler
→ sonstige Fehler: loggen, nicht eskalieren (nächster Lauf führt Schritt 1 wieder aus)
```
### Prozedur `p_process_incoming_ba_data` (in `pck_auto_import`) — ORDS-Einstiegspunkt
``` ```
1. Unterordner von eingang/ auflisten (f_list_objects mit delimiter='/') 1. Unterordner von eingang/ auflisten (f_list_objects mit delimiter='/')

View File

@@ -2,6 +2,82 @@ create or replace package body pck_auto_import as
c_log_module constant lg_app_log.log_module%type := 'AUTOMATISCHER_BA_IMPORT'; c_log_module constant lg_app_log.log_module%type := 'AUTOMATISCHER_BA_IMPORT';
procedure p_run_ba_korrespondenz_dateieingang_automation
/*Kopf------------------------------------------------------------------------------------------------
-- Beschreibung: Einstiegspunkt für die APEX Automation (stündlich).
-- Schritt 1: p_process_incoming_ba_data aufrufen — verarbeitet Batches, die bereits
-- in OCI liegen (Fallback für den Fall, dass der ORDS-Aufruf im letzten
-- Quarkus-Lauf fehlgeschlagen ist).
-- Schritt 2: Quarkus Dateieingang Service via HTTP POST anstoßen (fire & forget).
-- Schlägt Schritt 2 fehl, läuft Schritt 1 beim nächsten Stundenlauf erneut.
------------------------------------------------------------------------------------------------------
-- Parameter: —
------------------------------------------------------------------------------------------------------
-- MA Datum Änderung
-- SCK 2026-04-21 Prozedur erstellt
------------------------------------------------------------------------------------------------Kopf*/
is
l_service_url varchar2(4000 char);
l_api_key varchar2(500 char);
l_response clob;
l_http_status number;
l_log_action constant varchar2(512 char) := 'DATEIEINGANG_AUTOMATION';
begin
-- Schritt 1: Offene Batches in OCI verarbeiten
p_process_incoming_ba_data;
-- Schritt 2: Quarkus anstoßen — Fehler werden geloggt, nicht eskaliert
begin
l_service_url := pck_system.f_get_par_wert_by_programmid('AUTOMATON_BASE_URL') || '/api/process-incoming';
l_api_key := pck_system.f_get_par_wert_by_programmid('AUTOMATON_API_KEY');
apex_web_service.g_request_headers.delete;
apex_web_service.g_request_headers(1).name := 'X-Api-Key';
apex_web_service.g_request_headers(1).value := l_api_key;
l_response := apex_web_service.make_rest_request(
p_url => l_service_url
,p_http_method => 'POST'
);
l_http_status := apex_web_service.g_status_code;
case l_http_status
when 202
then
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Dateieingang Service angestoßen (202 Accepted)'
);
when 409
then
-- Service läuft bereits — kein Fehler, kein zweiter Lauf nötig
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Dateieingang Service läuft bereits (409 Conflict) — kein neuer Lauf gestartet'
);
else
pck_log.p_warn(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Dateieingang Service: unerwarteter HTTP-Status ' || l_http_status
);
end case;
exception
when others
then
-- Quarkus-Aufruf fehlgeschlagen: loggen, nicht eskalieren.
-- Nächster Stundenlauf führt Schritt 1 erneut aus.
pck_log.p_error(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Aufruf des Dateieingang Service fehlgeschlagen: ' || sqlerrm
,i_detail => to_clob(dbms_utility.format_error_backtrace)
);
end;
end p_run_ba_korrespondenz_dateieingang_automation;
procedure p_import_ba_korrespondenz ( procedure p_import_ba_korrespondenz (
i_object_key in varchar2 i_object_key in varchar2
,i_content in blob ,i_content in blob
@@ -52,7 +128,7 @@ create or replace package body pck_auto_import as
-- Wiedervorlage für Sachbearbeiter erstellen -- Wiedervorlage für Sachbearbeiter erstellen
pck_wiedervorlage.p_wiedervorlage_anlegen (i_swv_stp_id_art => pck_stammdaten.f_get_stp_id_by_programmid('WV_IMPORT_DATEV') -- TODO: neue WV Art? z.B. WV_IMPORT_BA_DATEN pck_wiedervorlage.p_wiedervorlage_anlegen (i_swv_stp_id_art => pck_stammdaten.f_get_stp_id_by_programmid('WV_IMPORT_DATEV') -- TODO: neue WV Art? z.B. WV_IMPORT_BA_DATEN
,i_swv_wdatum => sysdate --TODO: welches Datum? sysdate? ,i_swv_wdatum => sysdate --TODO: welches Datum? sysdate?
,i_swv_bemerkung => 'Bitte manuell Prüfen: Die Datei "' || l_filename || '" konnte nicht automatisch importiert werden (Siehe "' || i_object_key || '").' ,i_swv_bemerkung => 'Bitte manuell Prüfen: Die BA-Datei "' || l_filename || '" konnte nicht automatisch importiert werden (Siehe "' || i_object_key || '").'
,i_swv_mit_id_wsachbearbeiter => pck_system.f_get_par_wert_by_programmid('BA_IMPORT_SB_MIT_ID') ,i_swv_mit_id_wsachbearbeiter => pck_system.f_get_par_wert_by_programmid('BA_IMPORT_SB_MIT_ID')
); );
@@ -75,12 +151,12 @@ create or replace package body pck_auto_import as
procedure p_process_incoming_ba_data procedure p_process_incoming_ba_data
/*Kopf------------------------------------------------------------------------------------------------ /*Kopf------------------------------------------------------------------------------------------------
-- Beschreibung: Verarbeitet alle fertigen Eingangs-Batches aus dem OCI Eingangsordner. -- Beschreibung: Verarbeitet alle fertigen Eingangs-Batches aus dem OCI Eingangsordner (Netzlaufwerk).
-- Wird von ORDS-Endpunkt und APEX Automation aufgerufen. -- Wird von ORDS-Endpunkt (von Quarkus Automaton) und aus Apex Automation aufgerufen.
-- Pro Datei: Import + Move → Commit; bei Exception: Rollback, Datei bleibt liegen, -- Pro Datei: Import + Move → Commit; bei Exception: Rollback, Datei bleibt liegen,
-- nächste Datei wird trotzdem verarbeitet. -- nächste Datei wird trotzdem verarbeitet.
-- Nach dem Datei-Loop: DB-Marker immer löschen (verhindert erneuten Durchlauf). -- Nach dem Datei-Loop: DB-Marker immer löschen (verhindert erneuten Durchlauf).
-- Wenn danach noch Dateien im Ordner liegen: SB-Marker anlegen damit Sachbearbeiter -- Wenn danach noch Dateien im Ordner liegen: Sachbearbeiter(SB)-Marker anlegen damit Sachbearbeiter
-- die übriggebliebenen Dateien manuell prüfen können. -- die übriggebliebenen Dateien manuell prüfen können.
------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------
-- MA Datum Änderung -- MA Datum Änderung
@@ -106,13 +182,13 @@ create or replace package body pck_auto_import as
l_target_prefix := pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_PREFIX') || 'Verarbeitet ' || to_char(sysdate, 'YYYY') || '/'; l_target_prefix := pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_PREFIX') || 'Verarbeitet ' || to_char(sysdate, 'YYYY') || '/';
l_eingang_prefix := pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_PREFIX') || pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_IMPORT'); l_eingang_prefix := pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_PREFIX') || pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_IMPORT');
-- Unterordner in eingangs-ordner auflisten (es gibt einen Ordner für jeden entpackte ZIP-Datei) (Delimiter '/' liefert nur direkte Kinder) -- Unterordner in eingangs-ordner auflisten (es gibt einen Ordner für jeden entpackte ZIP-Datei)
l_folders := pck_net_storage.f_list_objects( l_folders := pck_net_storage.f_list_objects(
i_prefix => l_eingang_prefix i_parent_folder => l_eingang_prefix
,i_delimiter => '/' ,i_include_subfolders => 'N'
); );
for rec_folder in (select object_name, is_folder from table(l_folders)) for rec_folder in (select object_key, is_folder from table(l_folders))
loop loop
-- Nur Unterordner verarbeiten -- Nur Unterordner verarbeiten
if rec_folder.is_folder != 'Y' if rec_folder.is_folder != 'Y'
@@ -122,7 +198,7 @@ create or replace package body pck_auto_import as
-- Der Marker ist eine Datei mit speziellem Namen, welche vom quarkus automaton in einen entpackten zip-ordner gelegt wird um zu signalisieren, dass alle Dateien des ZIPs erfolgreich in den ordner gelegt wurden. -- Der Marker ist eine Datei mit speziellem Namen, welche vom quarkus automaton in einen entpackten zip-ordner gelegt wird um zu signalisieren, dass alle Dateien des ZIPs erfolgreich in den ordner gelegt wurden.
-- Das verhindert die verarbeitung von unvollständig entpackten zips -- Das verhindert die verarbeitung von unvollständig entpackten zips
l_db_processing_marker_key := rec_folder.object_name || pck_system.f_get_par_wert_by_programmid('NETSTORE_MARKER_DB'); l_db_processing_marker_key := rec_folder.object_key || pck_system.f_get_par_wert_by_programmid('NETSTORE_MARKER_DB');
-- Marker prüfen: -20001 = nicht vorhanden → Upload noch nicht abgeschlossen -- Marker prüfen: -20001 = nicht vorhanden → Upload noch nicht abgeschlossen
begin begin
@@ -139,32 +215,32 @@ create or replace package body pck_auto_import as
-- Zip-Namen aus Ordnerpfad ableiten: eingang/<zip-name>/ → <zip-name> -- Zip-Namen aus Ordnerpfad ableiten: eingang/<zip-name>/ → <zip-name>
l_zip_name := substr( l_zip_name := substr(
rec_folder.object_name rec_folder.object_key
,length(l_eingang_prefix) + 1 ,length(l_eingang_prefix) + 1
,length(rec_folder.object_name) - length(l_eingang_prefix) - 1 ,length(rec_folder.object_key) - length(l_eingang_prefix) - 1
); );
l_target_folder := l_target_prefix || l_zip_name || '/'; l_target_folder := l_target_prefix || l_zip_name || '/';
-- Alle Dateien im Unterordner auflisten (kein Delimiter = flach, alle Tiefen) -- Alle Dateien im Unterordner auflisten (inkl. Unterordner = alle Tiefen)
l_files := pck_net_storage.f_list_objects( l_files := pck_net_storage.f_list_objects(
i_prefix => rec_folder.object_name i_parent_folder => rec_folder.object_key
,i_delimiter => '' ,i_include_subfolders => 'Y'
); );
for rec_file in (select object_name, is_folder from table(l_files)) for rec_file in (select object_key, is_folder from table(l_files))
loop loop
-- Marker und Pseudo-Ordner überspringen -- Marker und Pseudo-Ordner überspringen
if rec_file.object_name = l_db_processing_marker_key or rec_file.is_folder = 'Y' if rec_file.object_key = l_db_processing_marker_key or rec_file.is_folder = 'Y'
then then
continue; continue;
end if; end if;
begin begin
-- 1. Dateiinhalt laden -- 1. Dateiinhalt laden
l_file_content := pck_net_storage.f_download_object(rec_file.object_name); l_file_content := pck_net_storage.f_download_object(rec_file.object_key);
-- 2. Fachliche Verarbeitung + Move bei Erfolg (innerhalb p_import_ba_korrespondenz) -- 2. Fachliche Verarbeitung + Move bei Erfolg (innerhalb p_import_ba_korrespondenz)
p_import_ba_korrespondenz(rec_file.object_name, l_file_content, l_target_folder); p_import_ba_korrespondenz(rec_file.object_key, l_file_content, l_target_folder);
-- Commit pro Datei: OCI-Move ist nicht transaktional, daher DB-Änderungen sofort sichern -- Commit pro Datei: OCI-Move ist nicht transaktional, daher DB-Änderungen sofort sichern
-- sonst würde ein Fehler bei einer späteren Datei den DB-Import bereits verschobener Dateien zurückrollen -- sonst würde ein Fehler bei einer späteren Datei den DB-Import bereits verschobener Dateien zurückrollen
@@ -179,7 +255,7 @@ create or replace package body pck_auto_import as
,i_action => 'IMPORT_FILE' ,i_action => 'IMPORT_FILE'
,i_message => 'Fehler bei Dateiverarbeitung: ' || sqlerrm ,i_message => 'Fehler bei Dateiverarbeitung: ' || sqlerrm
,i_detail => to_clob(dbms_utility.format_error_backtrace) ,i_detail => to_clob(dbms_utility.format_error_backtrace)
,i_object_ref => rec_file.object_name ,i_object_ref => rec_file.object_key
); );
end; end;
end loop; end loop;
@@ -189,13 +265,13 @@ create or replace package body pck_auto_import as
-- Prüfen ob noch Dateien im Unterordner liegen (nicht erfolgreich importierte Dateien) -- Prüfen ob noch Dateien im Unterordner liegen (nicht erfolgreich importierte Dateien)
l_files := pck_net_storage.f_list_objects( l_files := pck_net_storage.f_list_objects(
i_prefix => rec_folder.object_name i_parent_folder => rec_folder.object_key
,i_delimiter => '' ,i_include_subfolders => 'Y'
); );
l_has_remaining_files := false; l_has_remaining_files := false;
for rec_remaining in (select object_name, is_folder from table(l_files)) for rec_remaining in (select object_key, is_folder from table(l_files))
loop loop
if rec_remaining.is_folder != 'Y' if rec_remaining.is_folder != 'Y'
then then
@@ -206,8 +282,8 @@ create or replace package body pck_auto_import as
if l_has_remaining_files if l_has_remaining_files
then then
-- SB-Marker anlegen: signalisiert Sachbearbeitern, dass Dateien manuell geprüft werden müssen -- Sachbearbeiter (SB)-Marker anlegen: signalisiert Sachbearbeitern, dass Dateien manuell geprüft werden müssen
l_sb_marker_key := rec_folder.object_name || pck_system.f_get_par_wert_by_programmid('NETSTORE_MARKER_SB'); l_sb_marker_key := rec_folder.object_key || pck_system.f_get_par_wert_by_programmid('NETSTORE_MARKER_SB');
pck_net_storage.p_upload_object( pck_net_storage.p_upload_object(
i_object_key => l_sb_marker_key i_object_key => l_sb_marker_key
,i_content => empty_blob() ,i_content => empty_blob()
@@ -217,14 +293,14 @@ create or replace package body pck_auto_import as
i_module => c_log_module i_module => c_log_module
,i_action => l_log_action ,i_action => l_log_action
,i_message => 'Batch mit Fehlern abgeschlossen — mind. eine Datei konnte nicht importiert werden, SB-Marker gesetzt' ,i_message => 'Batch mit Fehlern abgeschlossen — mind. eine Datei konnte nicht importiert werden, SB-Marker gesetzt'
,i_object_ref => rec_folder.object_name ,i_object_ref => rec_folder.object_key
); );
else else
pck_log.p_info( pck_log.p_info(
i_module => c_log_module i_module => c_log_module
,i_action => l_log_action ,i_action => l_log_action
,i_message => 'Batch abgeschlossen, alle Dateien erfolgreich importiert' ,i_message => 'Batch abgeschlossen, alle Dateien erfolgreich importiert'
,i_object_ref => rec_folder.object_name ,i_object_ref => rec_folder.object_key
); );
end if; end if;
end loop; end loop;

View File

@@ -1,5 +1,9 @@
create or replace package pck_auto_import as create or replace package pck_auto_import as
-- Von APEX Automation aufgerufen: verarbeitet offene OCI-Batches, stößt dann Quarkus an
procedure p_run_ba_korrespondenz_dateieingang_automation;
-- Von ORDS-Endpunkt aufgerufen (net_storage/process_incoming_ba_data): importiert Dateien aus OCI
procedure p_process_incoming_ba_data; procedure p_process_incoming_ba_data;
end pck_auto_import; end pck_auto_import;

View File

@@ -4,7 +4,7 @@
Der Service empfängt einen HTTP-Trigger von der APEX Automation, verbindet sich mit einem Der Service empfängt einen HTTP-Trigger von der APEX Automation, verbindet sich mit einem
SFTP-Server, lädt neue ZIP-Dateien herunter, entpackt sie und lädt den Inhalt in OCI Object SFTP-Server, lädt neue ZIP-Dateien herunter, entpackt sie und lädt den Inhalt in OCI Object
Storage hoch. Anschließend wird der ORDS-Endpunkt `pck_auto_import.p_process_incoming_files` Storage hoch. Anschließend wird der ORDS-Endpunkt `pck_auto_import.p_process_incoming_ba_data`
aufgerufen, der die DB-Verarbeitung anstößt. Das Scheduling bleibt bei APEX — der Service aufgerufen, der die DB-Verarbeitung anstößt. Das Scheduling bleibt bei APEX — der Service
selbst hat keinen eigenen Scheduler. selbst hat keinen eigenen Scheduler.
@@ -34,7 +34,7 @@ FileProcessingPipeline [ManagedExecutor — Hintergrund-Thread]
│ └─ .processed (Erfolg) oder .error (Fehler) │ └─ .processed (Erfolg) oder .error (Fehler)
├─→ OrdsNotificationService.notify() [MicroProfile REST Client] ├─→ OrdsNotificationService.notify() [MicroProfile REST Client]
│ └─ POST pck_auto_import.p_process_incoming_files │ └─ POST pck_auto_import.p_process_incoming_ba_data
└─→ Cleanup: lokale Dateien löschen [immer, im finally] └─→ Cleanup: lokale Dateien löschen [immer, im finally]
@@ -105,7 +105,7 @@ galabau.oci.private-key-path=${OCI_PRIVATE_KEY_PATH}
# ORDS # ORDS
galabau.ords.base-url=http://ords:8080 galabau.ords.base-url=http://ords:8080
galabau.ords.process-incoming-path=/ords/.../auto_import/process_incoming galabau.ords.process-incoming-path=/ords/.../net_storage/process_incoming_ba_data
galabau.ords.api-key=${GALABAU_ORDS_API_KEY} galabau.ords.api-key=${GALABAU_ORDS_API_KEY}
quarkus.rest-client.ords-client.url=${galabau.ords.base-url} quarkus.rest-client.ords-client.url=${galabau.ords.base-url}

View File

@@ -49,7 +49,7 @@ FileProcessingPipeline [Hintergrund-Thread — für jede neue ZIP]
## Projektstruktur (Maven) ## Projektstruktur (Maven)
``` ```
backend-n8n-replacement/ quarkus-automaton/
├── pom.xml (Java 25, Quarkus 3.x LTS) ├── pom.xml (Java 25, Quarkus 3.x LTS)
├── src/main/java/de/galabau/ ├── src/main/java/de/galabau/
@@ -150,7 +150,7 @@ galabau.oci.private-key-path=${OCI_PRIVATE_KEY_PATH} # z.B. /etc/oci/private-k
# ORDS # ORDS
galabau.ords.base-url=http://ords:8080 galabau.ords.base-url=http://ords:8080
galabau.ords.process-incoming-path=/ords/.../net_storage/process_incoming galabau.ords.process-incoming-path=/ords/.../net_storage/process_incoming_ba_data
galabau.ords.api-key=${GALABAU_ORDS_API_KEY} galabau.ords.api-key=${GALABAU_ORDS_API_KEY}
# Fehlerbehandlung # Fehlerbehandlung
@@ -160,7 +160,7 @@ galabau.retry.backoff-ms=1000
**Credential-Strategie:** **Credential-Strategie:**
- Alle Passwörter und API-Keys über `${ENV_VAR_NAME}` — Quarkus liest Umgebungsvariablen nativ - Alle Passwörter und API-Keys über `${ENV_VAR_NAME}` — Quarkus liest Umgebungsvariablen nativ
- OCI-Auth via **Instance Principal** — kein Key-Management, keine Rotation (empfohlen auf OKE) - OCI-Auth via **SimpleAuthenticationDetailsProvider** — Credentials aus Env-Vars, Private Key als gemountetes Kubernetes Secret (PEM-Datei); entspricht dem was `apex_web_service` mit OCI Web Credential intern tut (OCI HTTP Signature V1)
- Deployment: Kubernetes Secrets → Environment Injection für Passwörter/API-Keys - Deployment: Kubernetes Secrets → Environment Injection für Passwörter/API-Keys
--- ---
@@ -249,7 +249,7 @@ n8n fire-and-forget-Verhalten.
| SFTP-Verbindung fehlgeschlagen | transient | nein | Nächster APEX-Lauf (1h) versucht es | | SFTP-Verbindung fehlgeschlagen | transient | nein | Nächster APEX-Lauf (1h) versucht es |
| ZIP beschädigt | persistent | nein | ZIP auf SFTP umbenennen zu `.error`, Log | | ZIP beschädigt | persistent | nein | ZIP auf SFTP umbenennen zu `.error`, Log |
| OCI-Verbindung fehlgeschlagen (z.B. 503) | transient | ja (exponential backoff) | @Retry | | OCI-Verbindung fehlgeschlagen (z.B. 503) | transient | ja (exponential backoff) | @Retry |
| OCI-Upload einer Datei schlägt fehl | persistent | nein | Bereits hochgeladene löschen, ZIP belassen, Log | | OCI-Upload einer Datei schlägt fehl | persistent | nein | SFTP-Rename zu `.error`, Log — bereits hochgeladene OCI-Dateien bleiben (idempotent) |
| ORDS-Aufruf schlägt fehl | transient | ja (2-3x) | Marker liegt vor → APEX Automation schlägt beim nächsten Lauf ein | | ORDS-Aufruf schlägt fehl | transient | ja (2-3x) | Marker liegt vor → APEX Automation schlägt beim nächsten Lauf ein |
| Allgemein technischer Fehler | fallabhängig | siehe SmallRye Fault Tolerance | Exception-Log | | Allgemein technischer Fehler | fallabhängig | siehe SmallRye Fault Tolerance | Exception-Log |
@@ -300,7 +300,7 @@ public class FileEntry {
## SFTP-Verarbeitung mit SSHJ ## SFTP-Verarbeitung mit SSHJ
Siehe `plan_camel_integration.md` für Details zur SSHJ-Integration (Host-Key-Verification, Siehe `docs/SFTP-Integration.md` für Details zur SSHJ-Integration (Host-Key-Verification,
Credentials, Fehlerbehandlung). Credentials, Fehlerbehandlung).
### Verarbeitungslogik ### Verarbeitungslogik
@@ -580,7 +580,7 @@ MDC nach jeder ZIP mit `MDC.clear()` leeren — damit kein Kontext in die nächs
- [x] Concurrency Guard: **AtomicBoolean** (kein Doppellauf bei schnellen APEX-Retries) - [x] Concurrency Guard: **AtomicBoolean** (kein Doppellauf bei schnellen APEX-Retries)
- [x] SFTP-Lösung: **SSHJ** (`com.hierynomus:sshj`) — leichtgewichtiger SFTP-Client - [x] SFTP-Lösung: **SSHJ** (`com.hierynomus:sshj`) — leichtgewichtiger SFTP-Client
- [x] SFTP Host-Key: **Fingerprint-basiert** via `galabau.sftp.host-key-fingerprint` in Config - [x] SFTP Host-Key: **Fingerprint-basiert** via `galabau.sftp.host-key-fingerprint` in Config
- [x] OCI Auth: **SimpleAuthenticationDetailsProvider** (Credentials via Env-Vars + gemountetes Kubernetes Secret für Private Key) - [x] OCI Auth: **SimpleAuthenticationDetailsProvider** (Credentials via Env-Vars + gemountetes Kubernetes Secret für Private Key — entspricht `apex_web_service` mit OCI Credential)
- [x] OCI SDK: **OCI Java SDK** (HTTP Signature V1 automatisch) - [x] OCI SDK: **OCI Java SDK** (HTTP Signature V1 automatisch)
- [x] ZIP: **Apache Commons Compress** - [x] ZIP: **Apache Commons Compress**
- [x] REST Clients: **MicroProfile REST Client** (configKey-basiert) - [x] REST Clients: **MicroProfile REST Client** (configKey-basiert)

View File

@@ -288,4 +288,4 @@ galabau.sftp.password=${GALABAU_SFTP_PASSWORD}
galabau.api.key=${GALABAU_API_KEY} galabau.api.key=${GALABAU_API_KEY}
``` ```
Siehe `plan_n8n_replacement_quarkus.md` für vollständige Konfigurationsübersicht. Siehe `docs/Plan.md` für vollständige Konfigurationsübersicht.

View File

@@ -13,7 +13,7 @@
| **OCI Object Storage** | Zwischenspeicher — Eingangsordner, Zielordner nach Verarbeitung | | **OCI Object Storage** | Zwischenspeicher — Eingangsordner, Zielordner nach Verarbeitung |
| **Oracle DB / APEX** | Verarbeitung — liest Dateien aus OCI, importiert Daten | | **Oracle DB / APEX** | Verarbeitung — liest Dateien aus OCI, importiert Daten |
Details zum Dateieingang Service: `agent-backend/docs/Architecture.md` Details zum Dateieingang Service: `quarkus-automaton/docs/Architecture.md`
Details zur DB-Verarbeitung: `database/docs/plan_pck_net_storage.md` Details zur DB-Verarbeitung: `database/docs/plan_pck_net_storage.md`
--- ---
@@ -23,9 +23,11 @@ Details zur DB-Verarbeitung: `database/docs/plan_pck_net_storage.md`
``` ```
┌─────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────┐
│ APEX Automation (stündlich) │ │ APEX Automation (stündlich) │
│ → pck_auto_import.p_run_ba_korrespondenz_dateieingang_automation │
│ │ │ │
│ 1. Unterordner in eingang/ mit Marker vorhanden? │ 1. p_process_incoming_ba_data aufrufen
│ → ja: Dateien verarbeiten (gleiche Logik wie Schritt 4-6) │ → OCI-Batches mit Marker verarbeiten (Fallback: ORDS-Aufruf
│ im letzten Quarkus-Lauf fehlgeschlagen) │
│ │ │ │
│ 2. Dateieingang Service aufrufen (fire & forget) │ │ 2. Dateieingang Service aufrufen (fire & forget) │
│ HTTP POST /api/process-incoming (Header: X-Api-Key) │ │ HTTP POST /api/process-incoming (Header: X-Api-Key) │
@@ -44,13 +46,13 @@ Details zur DB-Verarbeitung: `database/docs/plan_pck_net_storage.md`
│ hochladen │ │ hochladen │
│ 3e. ZIP auf SFTP umbenennen (.processed oder .error) │ │ 3e. ZIP auf SFTP umbenennen (.processed oder .error) │
│ → erst NACH erfolgreichem Marker-Upload │ │ → erst NACH erfolgreichem Marker-Upload │
│ 3f. ORDS-Endpunkt aufrufen (pck_auto_import.p_process_incoming)│ │ 3f. ORDS-Endpunkt aufrufen (pck_auto_import.p_process_incoming_ba_data)│
│ 3g. Lokale Arbeitsdateien löschen │ │ 3g. Lokale Arbeitsdateien löschen │
└────────────────────────────┬────────────────────────────────────┘ └────────────────────────────┬────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────┐
│ Oracle DB (via ORDS-Endpunkt oder APEX Automation) │ │ Oracle DB (via ORDS-Endpunkt) │
│ │ │ │
│ 4. Unterordner in eingang/ auflisten │ │ 4. Unterordner in eingang/ auflisten │
│ 5. Für jeden Unterordner mit Marker: │ │ 5. Für jeden Unterordner mit Marker: │
@@ -85,7 +87,9 @@ bucket/
``` ```
Der Marker bleibt solange erhalten bis **alle** Dateien des Unterordners Der Marker bleibt solange erhalten bis **alle** Dateien des Unterordners
erfolgreich verarbeitet wurden. Fehlgeschlagene Dateien werden beim nächsten Lauf erneut versucht. verarbeitet wurden. Fehlgeschlagene Dateien bleiben im Ordner.
Wenn ein Import-Lauf alle Dateien im Ordner einmal versucht hat zu importieren, und min. eine Datei übrig geblieben ist, für die der automatische Import also nicht funktioniert hat, dann wird für jede dieser Dateien eine Wiedervorlage für die Sachbearbeiter erstellt und eine Marker Datei für die Sachbearbeiter wird im Ordner abgelegt.
Daran können die Sachbearbeiter erkennen, dass der Ordner nicht mehr automatisch importiert wird, sondern sie manuell tätig werden müssen.
--- ---
@@ -104,13 +108,30 @@ erfolgreich verarbeitet wurden. Fehlgeschlagene Dateien werden beim nächsten La
**DB: Verarbeitung einer einzelnen Datei schlägt fehl** **DB: Verarbeitung einer einzelnen Datei schlägt fehl**
- Rollback — Datei bleibt in `eingang/<zip-name>/` - Rollback — Datei bleibt in `eingang/<zip-name>/`
- ERROR in `lg_app_log` mit `log_object_ref = eingang/<zip-name>/datei.csv` - ERROR in `lg_app_log` mit `log_object_ref = eingang/<zip-name>/datei.csv`
- Marker bleibt erhalten → nächste Dateien im Batch werden weiterverarbeitet - Nächste Dateien im Batch werden weiterverarbeitet
- Beim nächsten Lauf wird die fehlgeschlagene Datei erneut versucht
**DB: Batch-Abschluss (nach dem Datei-Loop)**
- DB-Marker (`_READY_FOR_DB_PROCESSING_`) wird **immer gelöscht** — kein automatischer Retry
- Liegen noch Dateien im Unterordner: SB-Marker (`_BITTE_PRÜFEN_`) wird angelegt → Sachbearbeiter müssen manuell eingreifen
- Alle Dateien erfolgreich: INFO in `lg_app_log`, Unterordner ist leer
**DB: p_move_object schlägt nach erfolgreichem Import fehl** **DB: p_move_object schlägt nach erfolgreichem Import fehl**
- Rollback des Imports → sauberer Ausgangszustand - Rollback des Imports → sauberer Ausgangszustand
- Datei bleibt in `eingang/<zip-name>/`, Marker bleibt erhalten - Datei bleibt in `eingang/<zip-name>/`
- Nächster Lauf versucht es erneut - DB-Marker wird trotzdem am Ende des Loops gelöscht; falls noch Dateien übrig → SB-Marker
---
## Warum ruft die APEX Automation p_process_incoming_ba_data auf, obwohl Quarkus das auch tut?
`p_process_incoming_ba_data` wird in jedem Stundenlauf **zweimal** aufgerufen:
1. Direkt durch `p_run_ba_korrespondenz_dateieingang_automation` (Schritt 1)
2. Indirekt — von Quarkus via ORDS, nachdem der Upload abgeschlossen ist (Schritt 3f)
Der direkte Aufruf in Schritt 1 ist ein **Fallback**: Wenn der ORDS-Aufruf in einem vorherigen Quarkus-Lauf fehlgeschlagen ist, liegt der Marker bereits in OCI, aber `p_process_incoming_ba_data` wurde nie aufgerufen. Ohne Schritt 1 würde dieser Batch erst beim nächsten Quarkus-Lauf verarbeitet — und nur dann, wenn Quarkus beim nächsten Mal auch wieder erfolgreich bis zum ORDS-Aufruf kommt. Mit Schritt 1 ist die DB-Verarbeitung unabhängig davon, ob Quarkus den ORDS-Aufruf erfolgreich abgeschlossen hat.
Das heißt: Quarkus ist der **schnelle Pfad** (Upload + sofortiger DB-Trigger), die APEX Automation ist die **Absicherung** (findet Marker, die noch nicht verarbeitet wurden).
--- ---