Verbesserung der Dokumentation und implementierung von automation funktion in pck_auto_import
This commit is contained in:
@@ -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_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. |
|
||||
| `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
|
||||
|
||||
`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
|
||||
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
|
||||
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='/')
|
||||
|
||||
@@ -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';
|
||||
|
||||
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 (
|
||||
i_object_key in varchar2
|
||||
,i_content in blob
|
||||
@@ -52,7 +128,7 @@ create or replace package body pck_auto_import as
|
||||
-- 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
|
||||
,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')
|
||||
);
|
||||
|
||||
@@ -75,12 +151,12 @@ create or replace package body pck_auto_import as
|
||||
|
||||
procedure p_process_incoming_ba_data
|
||||
/*Kopf------------------------------------------------------------------------------------------------
|
||||
-- Beschreibung: Verarbeitet alle fertigen Eingangs-Batches aus dem OCI Eingangsordner.
|
||||
-- Wird von ORDS-Endpunkt und APEX Automation aufgerufen.
|
||||
-- Beschreibung: Verarbeitet alle fertigen Eingangs-Batches aus dem OCI Eingangsordner (Netzlaufwerk).
|
||||
-- Wird von ORDS-Endpunkt (von Quarkus Automaton) und aus Apex Automation aufgerufen.
|
||||
-- Pro Datei: Import + Move → Commit; bei Exception: Rollback, Datei bleibt liegen,
|
||||
-- nächste Datei wird trotzdem verarbeitet.
|
||||
-- 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.
|
||||
------------------------------------------------------------------------------------------------------
|
||||
-- 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_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(
|
||||
i_prefix => l_eingang_prefix
|
||||
,i_delimiter => '/'
|
||||
i_parent_folder => l_eingang_prefix
|
||||
,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
|
||||
-- Nur Unterordner verarbeiten
|
||||
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.
|
||||
-- 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
|
||||
begin
|
||||
@@ -139,32 +215,32 @@ create or replace package body pck_auto_import as
|
||||
|
||||
-- Zip-Namen aus Ordnerpfad ableiten: eingang/<zip-name>/ → <zip-name>
|
||||
l_zip_name := substr(
|
||||
rec_folder.object_name
|
||||
rec_folder.object_key
|
||||
,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 || '/';
|
||||
|
||||
-- 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(
|
||||
i_prefix => rec_folder.object_name
|
||||
,i_delimiter => ''
|
||||
i_parent_folder => rec_folder.object_key
|
||||
,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
|
||||
-- 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
|
||||
continue;
|
||||
end if;
|
||||
|
||||
begin
|
||||
-- 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)
|
||||
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
|
||||
-- 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_message => 'Fehler bei Dateiverarbeitung: ' || sqlerrm
|
||||
,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 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)
|
||||
l_files := pck_net_storage.f_list_objects(
|
||||
i_prefix => rec_folder.object_name
|
||||
,i_delimiter => ''
|
||||
i_parent_folder => rec_folder.object_key
|
||||
,i_include_subfolders => 'Y'
|
||||
);
|
||||
|
||||
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
|
||||
if rec_remaining.is_folder != 'Y'
|
||||
then
|
||||
@@ -206,8 +282,8 @@ create or replace package body pck_auto_import as
|
||||
|
||||
if l_has_remaining_files
|
||||
then
|
||||
-- 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');
|
||||
-- Sachbearbeiter (SB)-Marker anlegen: signalisiert Sachbearbeitern, dass Dateien manuell geprüft werden müssen
|
||||
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(
|
||||
i_object_key => l_sb_marker_key
|
||||
,i_content => empty_blob()
|
||||
@@ -217,14 +293,14 @@ create or replace package body pck_auto_import as
|
||||
i_module => c_log_module
|
||||
,i_action => l_log_action
|
||||
,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
|
||||
pck_log.p_info(
|
||||
i_module => c_log_module
|
||||
,i_action => l_log_action
|
||||
,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 loop;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
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;
|
||||
|
||||
end pck_auto_import;
|
||||
|
||||
Reference in New Issue
Block a user