Compare commits

..

3 Commits

7 changed files with 203 additions and 30 deletions

View File

@@ -24,10 +24,33 @@ create or replace package body pck_auto_import as
l_log_action constant varchar2(512 char) := 'BA_KORRESPONDENZEN_DATEIEINGANG_AUTOMATION'; l_log_action constant varchar2(512 char) := 'BA_KORRESPONDENZEN_DATEIEINGANG_AUTOMATION';
l_automaton_endpoint constant varchar2(256 char) := 'api/process-incoming-ba-korrespondenz'; l_automaton_endpoint constant varchar2(256 char) := 'api/process-incoming-ba-korrespondenz';
begin begin
-- Schritt 1: Offene Batches in OCI verarbeiten pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Automation gestartet'
);
-- Offene Batches in OCI verarbeiten
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Verarbeitung offener OCI-Batches gestartet'
);
p_process_incoming_ba_data; p_process_incoming_ba_data;
-- Schritt 2: Quarkus anstoßen — Fehler werden geloggt, nicht eskaliert pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Verarbeitung offener OCI-Batches abgeschlossen'
);
-- Quarkus anstoßen — Fehler werden geloggt, nicht eskaliert
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Quarkus Dateieingang Service wird angestoßen'
);
begin begin
l_service_url := pck_system.f_get_par_wert_by_programmid('AUTOMATON_BASE_URL') || l_automaton_endpoint; l_service_url := pck_system.f_get_par_wert_by_programmid('AUTOMATON_BASE_URL') || l_automaton_endpoint;
l_api_key := pck_system.f_get_par_wert_by_programmid('AUTOMATON_API_KEY'); l_api_key := pck_system.f_get_par_wert_by_programmid('AUTOMATON_API_KEY');
@@ -48,7 +71,7 @@ create or replace package body pck_auto_import as
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 => 'Dateieingang Service angestoßen (202 Accepted)' ,i_message => 'BA Dateieingang Service angestoßen (202 Accepted)'
); );
when 409 when 409
then then
@@ -56,24 +79,25 @@ create or replace package body pck_auto_import as
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 => 'Dateieingang Service läuft bereits (409 Conflict) — kein neuer Lauf gestartet' ,i_message => 'BA Dateieingang Service läuft bereits (409 Conflict) — kein neuer Lauf gestartet'
); );
else else
pck_log.p_warn( pck_log.p_error(
i_module => c_log_module i_module => c_log_module
,i_action => l_log_action ,i_action => l_log_action
,i_message => 'Dateieingang Service: unerwarteter HTTP-Status ' || l_http_status ,i_message => 'Fehler in BA Dateieingang Service: unerwarteter HTTP-Status ' || l_http_status
,i_detail => l_response
); );
end case; end case;
exception exception
when others when others
then then
-- Quarkus-Aufruf fehlgeschlagen: loggen, nicht eskalieren. -- Quarkus-Aufruf fehlgeschlagen: loggen, nicht eskalieren.
-- Nächster Stundenlauf führt Schritt 1 erneut aus. -- Nächster Stundenlauf führt BA-Import-Schritt erneut aus.
pck_log.p_error( pck_log.p_error(
i_module => c_log_module i_module => c_log_module
,i_action => l_log_action ,i_action => l_log_action
,i_message => 'Aufruf des Dateieingang Service fehlgeschlagen: ' || sqlerrm ,i_message => 'Aufruf des BA Dateieingang Service fehlgeschlagen: ' || sqlerrm
,i_detail => to_clob(dbms_utility.format_error_backtrace) ,i_detail => to_clob(dbms_utility.format_error_backtrace)
); );
end; end;
@@ -111,6 +135,13 @@ create or replace package body pck_auto_import as
l_filename := substr(i_object_key, instr(i_object_key, '/', -1) + 1); l_filename := substr(i_object_key, instr(i_object_key, '/', -1) + 1);
l_file_size := dbms_lob.getlength(i_content); l_file_size := dbms_lob.getlength(i_content);
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Import gestartet: "' || l_filename || '" (' || l_file_size || ' Bytes)'
,i_object_ref => i_object_key
);
l_return := inkasso.pck_import.f_import_ba_dokument( l_return := inkasso.pck_import.f_import_ba_dokument(
i_datei => i_content i_datei => i_content
,i_dateiname => l_filename ,i_dateiname => l_filename
@@ -133,9 +164,23 @@ create or replace package body pck_auto_import as
,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')
); );
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Wiedervorlage angelegt.'
,i_object_ref => i_object_key
);
raise_application_error(-20000, 'Import fehlgeschlagen: "' || l_filename || '" (Rückgabe: ' || l_return || ')'); raise_application_error(-20000, 'Import fehlgeschlagen: "' || l_filename || '" (Rückgabe: ' || l_return || ')');
end if; end if;
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Datei "' || l_filename || '" erfolgreich importiert.'
,i_object_ref => i_object_key
);
-- Datei in Verarbeitet-Ordner verschieben -- Datei in Verarbeitet-Ordner verschieben
pck_net_storage.p_move_object( pck_net_storage.p_move_object(
i_object_key => i_object_key i_object_key => i_object_key
@@ -179,9 +224,15 @@ create or replace package body pck_auto_import as
l_has_remaining_files boolean; l_has_remaining_files boolean;
l_log_action varchar2(512 char) := 'IMPORT_BA_DATA'; l_log_action varchar2(512 char) := 'IMPORT_BA_DATA';
begin begin
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Verarbeitung von eingehen BA Korrespondenzen gestartet'
);
-- Zielordner Name zusammenstellen -- Zielordner Name zusammenstellen
l_target_prefix := pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_PREFIX') || pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_ARCHIV') || ' ' || to_char(sysdate, 'YYYY') || '/'; l_target_prefix := pck_system.f_get_par_wert_by_programmid('NETSTORE_TENANT_ID') || pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_PREFIX') || pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_ARCHIV') || ' ' || 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_TENANT_ID') || 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) -- 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(
@@ -197,10 +248,24 @@ create or replace package body pck_auto_import as
continue; continue;
end if; end if;
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Datei-Ordner gefunden'
,i_object_ref => rec_folder.object_key
);
-- 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_key || 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');
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Marker für Ordner wird geprüft. Marker: ' || l_db_processing_marker_key || ' in Ordner: ' || rec_folder.object_key
,i_object_ref => rec_folder.object_key
);
-- Marker prüfen: -20001 = nicht vorhanden → Upload noch nicht abgeschlossen -- Marker prüfen: -20001 = nicht vorhanden → Upload noch nicht abgeschlossen
begin begin
l_meta := pck_net_storage.f_get_object_metadata(l_db_processing_marker_key); l_meta := pck_net_storage.f_get_object_metadata(l_db_processing_marker_key);
@@ -209,6 +274,12 @@ create or replace package body pck_auto_import as
then then
if sqlcode = -20001 if sqlcode = -20001
then then
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Kein DB-Verarbeitungsmarker vorhanden — Batch wird übersprungen (Upload noch nicht abgeschlossen)'
,i_object_ref => rec_folder.object_key
);
continue; continue;
end if; end if;
raise; raise;
@@ -222,6 +293,13 @@ create or replace package body pck_auto_import as
); );
l_target_folder := l_target_prefix || l_zip_name || '/'; l_target_folder := l_target_prefix || l_zip_name || '/';
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Batch-Verarbeitung gestartet — Zielordner: "' || l_target_folder || '"'
,i_object_ref => rec_folder.object_key
);
-- Alle Dateien im Unterordner auflisten (inkl. Unterordner = 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_parent_folder => rec_folder.object_key i_parent_folder => rec_folder.object_key
@@ -237,6 +315,13 @@ create or replace package body pck_auto_import as
end if; end if;
begin begin
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'BA-Datei wird verarbeitet'
,i_object_ref => rec_file.object_key
);
-- 1. Dateiinhalt laden -- 1. Dateiinhalt laden
l_file_content := pck_net_storage.f_download_object(rec_file.object_key); l_file_content := pck_net_storage.f_download_object(rec_file.object_key);
@@ -264,6 +349,13 @@ create or replace package body pck_auto_import as
-- DB-Marker immer entfernen — verhindert erneute Verarbeitung beim nächsten Lauf -- DB-Marker immer entfernen — verhindert erneute Verarbeitung beim nächsten Lauf
pck_net_storage.p_delete_object(l_db_processing_marker_key); pck_net_storage.p_delete_object(l_db_processing_marker_key);
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'DB-Verarbeitungsmarker gelöscht'
,i_object_ref => l_db_processing_marker_key
);
-- 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_parent_folder => rec_folder.object_key i_parent_folder => rec_folder.object_key
@@ -283,6 +375,13 @@ create or replace package body pck_auto_import as
if l_has_remaining_files if l_has_remaining_files
then then
pck_log.p_warn(
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 wird hochgeladen...'
,i_object_ref => rec_folder.object_key
);
-- Sachbearbeiter (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_key || 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(
@@ -290,12 +389,7 @@ create or replace package body pck_auto_import as
,i_content => empty_blob() ,i_content => empty_blob()
,i_content_type => 'application/octet-stream' ,i_content_type => 'application/octet-stream'
); );
pck_log.p_warn(
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_key
);
else else
pck_log.p_info( pck_log.p_info(
i_module => c_log_module i_module => c_log_module

View File

@@ -69,6 +69,7 @@ create or replace package body pck_net_storage as
------------------------------------------------------------------------------------------------Kopf*/ ------------------------------------------------------------------------------------------------Kopf*/
is is
l_tenant_prefix varchar2(256); l_tenant_prefix varchar2(256);
l_log_action varchar2(256) := 'ASSERT_ALLOWED';
begin begin
if i_object_key is null if i_object_key is null
then then
@@ -82,6 +83,15 @@ create or replace package body pck_net_storage as
l_tenant_prefix := pck_system.f_get_par_wert_by_programmid('NETSTORE_TENANT_ID'); l_tenant_prefix := pck_system.f_get_par_wert_by_programmid('NETSTORE_TENANT_ID');
/*
-- Log for debugging Prefix check
pck_log.p_info(
i_module => c_log_module
,i_action => l_log_action
,i_message => 'Checking Prefix: Netstore tenant-Prefix: ' || l_tenant_prefix || '; Accessed Prefix: ' || substr(i_object_key, 1, length(l_tenant_prefix)) || '; Accessed Object: ' || i_object_key
);
*/
if l_tenant_prefix is not null and length(l_tenant_prefix) > 0 if l_tenant_prefix is not null and length(l_tenant_prefix) > 0
then then
if substr(i_object_key, 1, length(l_tenant_prefix)) != l_tenant_prefix if substr(i_object_key, 1, length(l_tenant_prefix)) != l_tenant_prefix
@@ -121,6 +131,8 @@ create or replace package body pck_net_storage as
l_header_index number := 1; l_header_index number := 1;
l_content_length number; l_content_length number;
begin begin
-- headers zurücksetzen - nur zur Sicherheit, damit keine alten Header übertragen werden.
apex_web_service.g_request_headers.delete;
if i_content_type is not null if i_content_type is not null
then then
@@ -149,10 +161,13 @@ create or replace package body pck_net_storage as
l_header_index := l_header_index + 1; l_header_index := l_header_index + 1;
*/ */
-- nur für BLOB (z.B. leerer Ordner) Content-Length setzen -- nur für leere BLOBs (z.B. leerer Ordner) Content-Length setzen
if i_body_blob is not null -- bei nicht leeren blob setzt apex_web_service.make_rest_request den content-length header automatisch, doppeltes setzen führt aber zu einem HTTP-400 API Fehler
-- bei leeren blobs (empty_blob()) wird er aber nicht automatisch gesetzt, daher müssen wir ihn manuell setzen
if i_body_blob is not null
and dbms_lob.getlength(i_body_blob) = 0
then then
l_content_length := coalesce(dbms_lob.getlength(i_body_blob), 0); l_content_length := 0; -- coalesce(dbms_lob.getlength(i_body_blob), 0);
apex_web_service.g_request_headers(l_header_index).name := 'Content-Length'; apex_web_service.g_request_headers(l_header_index).name := 'Content-Length';
apex_web_service.g_request_headers(l_header_index).value := l_content_length; apex_web_service.g_request_headers(l_header_index).value := l_content_length;
l_header_index := l_header_index + 1; l_header_index := l_header_index + 1;
@@ -162,8 +177,8 @@ create or replace package body pck_net_storage as
if i_body_clob is not null if i_body_clob is not null
then then
apex_debug.info('Clob Request Body used:'); --apex_debug.info('Clob Request Body used:');
apex_debug.info(i_body_clob); --apex_debug.info(i_body_clob);
l_response := apex_web_service.make_rest_request( l_response := apex_web_service.make_rest_request(
p_url => i_url p_url => i_url
,p_http_method => i_method ,p_http_method => i_method
@@ -172,7 +187,7 @@ create or replace package body pck_net_storage as
,p_wallet_path => pck_system.f_get_par_wert_by_programmid('NETSTORE_WALLET_PATH') ,p_wallet_path => pck_system.f_get_par_wert_by_programmid('NETSTORE_WALLET_PATH')
); );
else else
apex_debug.info('BLOB Request Body used! Length: ' || dbms_lob.getlength(i_body_blob)); --apex_debug.info('BLOB Request Body used! Length: ' || dbms_lob.getlength(i_body_blob));
l_response := apex_web_service.make_rest_request( l_response := apex_web_service.make_rest_request(
p_url => i_url p_url => i_url
,p_http_method => i_method ,p_http_method => i_method
@@ -535,13 +550,12 @@ create or replace package body pck_net_storage as
raise_application_error(-20012, 'Object Key darf nicht mit / enden — zum Anlegen von Ordnern p_create_folder verwenden'); raise_application_error(-20012, 'Object Key darf nicht mit / enden — zum Anlegen von Ordnern p_create_folder verwenden');
end if; end if;
-- TEST l_response := f_make_request(
--l_response := f_make_request( i_method => 'PUT'
-- i_method => 'PUT' ,i_url => f_build_url(i_object_key)
-- ,i_url => f_build_url(i_object_key) ,i_body_blob => i_content
-- ,i_body_blob => i_content ,i_content_type => i_content_type
-- ,i_content_type => i_content_type );
--);
l_obj_path := f_split_object_key(i_object_key); l_obj_path := f_split_object_key(i_object_key);
pck_log.p_info( pck_log.p_info(

View File

@@ -1,3 +1,4 @@
{ {
"java.configuration.updateBuildConfiguration": "disabled" "java.configuration.updateBuildConfiguration": "disabled",
"java.compile.nullAnalysis.mode": "disabled"
} }

View File

@@ -0,0 +1,17 @@
# ---------------------------------------------------------------------------
# Build-Stage: Maven-Build
# ---------------------------------------------------------------------------
FROM eclipse-temurin:25-jdk AS builder
WORKDIR /build
COPY . .
RUN ./mvnw package -DskipTests --no-transfer-progress
# ---------------------------------------------------------------------------
# Runtime-Stage: Minimales JRE-Image
# ---------------------------------------------------------------------------
FROM eclipse-temurin:25-jre
WORKDIR /app
COPY --from=builder /build/target/quarkus-app/ ./
EXPOSE 8080
USER 1000
ENTRYPOINT ["java", "-jar", "quarkus-run.jar"]

View File

@@ -0,0 +1,10 @@
# ---------------------------------------------------------------------------
# Build- und Deploy-Konfiguration (keine Secrets — kann committet werden)
# ---------------------------------------------------------------------------
REGISTRY=ocir.eu-frankfurt-1.oci.oraclecloud.com/frhqaxi5sgcg
IMAGE_NAME=container/automaton
IMAGE_TAG=1.0.0
REGISTRY_USER=frhqaxi5sgcg/<your username> # frhqaxi5sgcg is the tenancy ID
REGISTRY_PW=<your users auth token>

View File

@@ -0,0 +1,17 @@
# Docker build (for arm nodes using qemu)
- Install docker CLI https://daniel.es/blog/how-to-install-docker-in-wsl-without-docker-desktop/
- For reference: Guide for setting up multiarch build support: https://docs.docker.com/build/building/multi-platform/
- Configure qemu emulation for arm on x86_64:
- docker buildx create --name multi-arch-builder --use
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- docker buildx inspect --bootstrap
- Before building and pushing: Copy the .env-example file to .env and enter auth info inside it
- run ./build.sh (executes docker buildx build command to build an image on a x86 host that can run on an arm node, e.g.:
docker buildx build --platform linux/arm64 --load -t $IMAGE . --build-arg http_proxy=$http_proxy --build-arg https_proxy=$https_proxy --build-arg no_proxy=$no_proxy)
Multiarch images cannot be loaded into the deamon using --load but need to be pushed into a registry directly using --push or be exported to a file
# Docker push image to OCI
- run .

View File

@@ -0,0 +1,20 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "${SCRIPT_DIR}/.env"
IMAGE="${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
echo "=== Image bauen & pushen: ${IMAGE} ==="
docker login -u $REGISTRY_USER -p $REGISTRY_PW https://$REGISTRY
docker buildx build \
--platform linux/arm64,linux/amd64 \
--push \
-t $IMAGE \
"${SCRIPT_DIR}/.."
# in case you use a proxy:
#--build-arg http_proxy=$http_proxy \
#--build-arg https_proxy=$https_proxy \
#--build-arg no_proxy=$no_proxy \
echo "Fertig: ${IMAGE}"