Files
Dokumenten-Check/Scripts/DC_BACKEND_PKG.sql
Wolf G. Beckmann ac9ab7ab15 feat: Add catalog generation functionality and related models
- Implemented CatalogResource for handling catalog generation requests and status checks.
- Created CatalogGenerationService to manage the asynchronous generation process.
- Added models for catalog generation responses, requests, and structured catalog data.
- Introduced utility scripts for database interaction and Kubernetes pod retrieval.
- Enhanced error handling and logging throughout the catalog generation workflow.
2026-05-04 19:03:54 +02:00

684 lines
25 KiB
MySQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- =============================================================================
-- DC_BACKEND_PKG
-- PL/SQL-Schnittstelle zum Quarkus dc-backend (Kubernetes Service dc-backend:8090)
--
-- Voraussetzungen:
-- - APEX_WEB_SERVICE-Berechtigung für das Schema:
-- GRANT EXECUTE ON APEX_240100.APEX_WEB_SERVICE TO <schema>; -- APEX-Version anpassen
-- - ACL für ausgehende HTTP-Verbindung im selben Namespace:
-- DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
-- host => 'dc-backend', lower_port => 8090, upper_port => 8090,
-- ace => xs$ace_type(privilege_list => xs$name_list('connect','resolve'),
-- principal_name => '<schema>', principal_type => xs_acl.ptype_db));
--
-- Deployment:
-- sqlplus user/pass@db @"Scripts/DC_BACKEND_PKG.sql"
-- =============================================================================
-- =============================================================================
-- Package Spec
-- =============================================================================
CREATE OR REPLACE PACKAGE DC_BACKEND_PKG AS
-- Basis-URL des dc-backend Kubernetes Service
c_base_url CONSTANT VARCHAR2(200) := 'http://dc-backend:8090';
-- API-Key (X-API-KEY Header).
-- Leer = kein Auth (Dev-Modus).
-- Zur Laufzeit setzen: DC_BACKEND_PKG.g_api_key := 'mein-key';
g_api_key VARCHAR2(500) := '';
-- -------------------------------------------------------------------------
-- Startet die KI-gestützte Dokumentenprüfung für ein Projekt (asynchron).
-- Das Projekt muss im Status PENDING sein.
-- POST /check/{p_project_id} → 202 Accepted
--
-- p_project_id ID des dc_projects-Eintrags
-- p_status_code HTTP-Statuscode der Antwort (202 = OK, 409 = nicht PENDING)
-- p_response JSON-Antwort des Backend
-- -------------------------------------------------------------------------
PROCEDURE start_check(
p_project_id IN NUMBER,
p_status_code OUT NUMBER,
p_response OUT VARCHAR2
);
-- -------------------------------------------------------------------------
-- Health-Check: prüft ob der dc-backend Service erreichbar ist.
-- GET /check/health → 200 UP
--
-- Rückgabe: TRUE = Service läuft, FALSE = nicht erreichbar
-- -------------------------------------------------------------------------
FUNCTION health_check RETURN BOOLEAN;
-- -------------------------------------------------------------------------
-- Startet die Prüfung und wirft eine Exception wenn nicht 202 zurückkommt.
-- Convenience-Wrapper für direkte APEX-Button-Aufrufe.
-- -------------------------------------------------------------------------
PROCEDURE start_check_or_raise(
p_project_id IN NUMBER
);
-- -------------------------------------------------------------------------
-- Fortschritts-Record
-- -------------------------------------------------------------------------
TYPE t_project_status IS RECORD (
status dc_projects.status%TYPE,
processing_progress dc_projects.progress%TYPE,
is_completed BOOLEAN,
is_running BOOLEAN,
is_pending BOOLEAN
);
-- -------------------------------------------------------------------------
-- Liest Status und Fortschritt eines Projekts direkt aus der DB.
-- Kein REST-Aufruf sofort und transaktionssicher.
--
-- Beispiel:
-- l_s := DC_BACKEND_PKG.get_status(1);
-- DBMS_OUTPUT.PUT_LINE(l_s.status || ' ' || l_s.processing_progress || '%');
-- -------------------------------------------------------------------------
FUNCTION get_status(
p_project_id IN NUMBER
) RETURN t_project_status;
-- -------------------------------------------------------------------------
-- Wartet (polling) bis das Projekt COMPLETED oder ein Timeout erreicht ist.
-- Wirft eine Exception bei Timeout oder wenn das Projekt nicht gefunden wird.
--
-- p_timeout_sec Max. Wartezeit in Sekunden (Default: 1800 = 30 Min.)
-- p_interval_sec Polling-Intervall in Sekunden (Default: 5)
-- -------------------------------------------------------------------------
PROCEDURE wait_for_completion(
p_project_id IN NUMBER,
p_timeout_sec IN NUMBER DEFAULT 1800,
p_interval_sec IN NUMBER DEFAULT 5
);
-- -------------------------------------------------------------------------
-- Konvertiert einen Markdown-CLOB zu DOCX oder PDF.
-- POST /export/markdown → BLOB (application/pdf oder .docx)
--
-- p_markdown Markdown-Text (z.B. dc_projects.report_markdown)
-- p_format 'PDF' (Standard) oder 'DOCX'
-- Rückgabe BLOB mit dem fertigen Dokument
-- -------------------------------------------------------------------------
FUNCTION convert_markdown(
p_markdown IN CLOB,
p_format IN VARCHAR2 DEFAULT 'PDF'
) RETURN BLOB;
-- -------------------------------------------------------------------------
-- Convenience: liest report_markdown des Projekts und konvertiert zu PDF/DOCX.
--
-- Beispiel:
-- v_blob := DC_BACKEND_PKG.project_report(1, 'PDF');
-- -------------------------------------------------------------------------
FUNCTION project_report(
p_project_id IN NUMBER,
p_format IN VARCHAR2 DEFAULT 'PDF'
) RETURN BLOB;
-- -------------------------------------------------------------------------
-- Generiert den Markdown-Prüfbericht für ein Projekt neu und speichert ihn
-- in dc_projects.report_markdown. Ruft intern DC_UTILS_PKG.generate_report auf.
--
-- Beispiel:
-- DC_BACKEND_PKG.regenerate_report(1);
-- -------------------------------------------------------------------------
PROCEDURE regenerate_report(
p_project_id IN NUMBER
);
-- -------------------------------------------------------------------------
-- Startet die asynchrone Katalog-Generierung aus einem Regelwerk-Dokument.
-- Gibt sofort zurück (202 Accepted-Muster) die KI läuft im Hintergrund.
--
-- p_file BLOB des Regelwerks (PDF, DOCX, ODT, TXT)
-- p_mime_type MIME-Typ, z.B. 'application/pdf'
-- p_filename Dateiname (wird im multipart-Part übergeben)
-- p_catalog_name Name des anzulegenden Katalogs (z.B. 'ADSp 2017')
-- p_document_type_id ID des Dokumenttyps (dc_document_types.id)
-- p_description Optionale Beschreibung
-- Rückgabe ID des Katalog-Placeholders (Status: GENERATING)
--
-- Beispiel (mit Warten auf Abschluss):
-- DECLARE
-- l_blob BLOB;
-- l_id NUMBER;
-- BEGIN
-- SELECT file_blob INTO l_blob FROM dc_reference_documents WHERE id = 1;
-- l_id := DC_BACKEND_PKG.generate_catalog(
-- p_file => l_blob,
-- p_mime_type => 'application/pdf',
-- p_filename => 'ADSp-2017.pdf',
-- p_catalog_name => 'ADSp 2017',
-- p_document_type_id => 1,
-- p_description => 'Allgemeine Deutsche Spediteurbedingungen 2017');
-- DBMS_OUTPUT.PUT_LINE('Katalog ID: ' || l_id || ' (läuft im Hintergrund)');
-- DC_BACKEND_PKG.wait_for_catalog(l_id); -- optional, wartet bis READY
-- DBMS_OUTPUT.PUT_LINE('Katalog fertig!');
-- END;
-- -------------------------------------------------------------------------
FUNCTION generate_catalog(
p_file IN BLOB,
p_mime_type IN VARCHAR2 DEFAULT 'application/pdf',
p_filename IN VARCHAR2 DEFAULT 'dokument.pdf',
p_catalog_name IN VARCHAR2,
p_document_type_id IN NUMBER,
p_description IN VARCHAR2 DEFAULT NULL
) RETURN NUMBER;
-- -------------------------------------------------------------------------
-- Liest den Generierungsstatus direkt aus der DB (kein REST-Aufruf).
-- Rückgabe: 'GENERATING', 'READY' oder 'ERROR'
-- -------------------------------------------------------------------------
FUNCTION get_catalog_status(
p_catalog_id IN NUMBER
) RETURN VARCHAR2;
-- -------------------------------------------------------------------------
-- Wartet (polling) bis der Katalog den Status READY erreicht hat.
-- Wirft eine Exception bei Timeout oder wenn der Katalog ERROR meldet.
--
-- p_timeout_sec Max. Wartezeit in Sekunden (Default: 1800 = 30 Min.)
-- p_interval_sec Polling-Intervall in Sekunden (Default: 10)
-- -------------------------------------------------------------------------
PROCEDURE wait_for_catalog(
p_catalog_id IN NUMBER,
p_timeout_sec IN NUMBER DEFAULT 1800,
p_interval_sec IN NUMBER DEFAULT 10
);
END DC_BACKEND_PKG;
/
-- =============================================================================
-- Package Body
-- =============================================================================
CREATE OR REPLACE PACKAGE BODY DC_BACKEND_PKG AS
-- -------------------------------------------------------------------------
-- Setzt gemeinsame Request-Header (Content-Type + optional API-Key)
-- -------------------------------------------------------------------------
PROCEDURE set_headers IS
l_idx PLS_INTEGER := 1;
BEGIN
APEX_WEB_SERVICE.G_REQUEST_HEADERS.DELETE;
APEX_WEB_SERVICE.G_REQUEST_HEADERS(l_idx).NAME := 'Content-Type';
APEX_WEB_SERVICE.G_REQUEST_HEADERS(l_idx).VALUE := 'application/json';
l_idx := l_idx + 1;
IF g_api_key IS NOT NULL AND LENGTH(TRIM(g_api_key)) > 0 THEN
APEX_WEB_SERVICE.G_REQUEST_HEADERS(l_idx).NAME := 'X-API-KEY';
APEX_WEB_SERVICE.G_REQUEST_HEADERS(l_idx).VALUE := g_api_key;
END IF;
END set_headers;
-- -------------------------------------------------------------------------
PROCEDURE start_check(
p_project_id IN NUMBER,
p_status_code OUT NUMBER,
p_response OUT VARCHAR2
) IS
l_url VARCHAR2(500);
l_response CLOB;
BEGIN
l_url := c_base_url || '/check/' || TO_CHAR(p_project_id);
set_headers();
l_response := APEX_WEB_SERVICE.MAKE_REST_REQUEST(
p_url => l_url,
p_http_method => 'POST',
p_body => '{}' -- leerer Body, projectId ist im Pfad
);
p_status_code := APEX_WEB_SERVICE.G_STATUS_CODE;
p_response := SUBSTR(l_response, 1, 4000);
EXCEPTION
WHEN OTHERS THEN
p_status_code := -1;
p_response := 'Verbindungsfehler: ' || SQLERRM;
END start_check;
-- -------------------------------------------------------------------------
FUNCTION health_check RETURN BOOLEAN IS
l_url VARCHAR2(500);
l_response CLOB;
l_status NUMBER;
BEGIN
l_url := c_base_url || '/check/health';
set_headers();
l_response := APEX_WEB_SERVICE.MAKE_REST_REQUEST(
p_url => l_url,
p_http_method => 'GET'
);
l_status := APEX_WEB_SERVICE.G_STATUS_CODE;
RETURN l_status = 200;
EXCEPTION
WHEN OTHERS THEN
RETURN FALSE;
END health_check;
-- -------------------------------------------------------------------------
PROCEDURE start_check_or_raise(
p_project_id IN NUMBER
) IS
l_status NUMBER;
l_response VARCHAR2(4000);
BEGIN
start_check(
p_project_id => p_project_id,
p_status_code => l_status,
p_response => l_response
);
IF l_status = 202 THEN
-- OK: Verarbeitung wurde gestartet
NULL;
ELSIF l_status = 409 THEN
RAISE_APPLICATION_ERROR(-20100,
'Projekt ' || p_project_id || ' ist nicht im Status PENDING.');
ELSIF l_status = 401 THEN
RAISE_APPLICATION_ERROR(-20101,
'Ungültiger API-Key für dc-backend.');
ELSIF l_status = -1 THEN
RAISE_APPLICATION_ERROR(-20102,
'dc-backend nicht erreichbar: ' || l_response);
ELSE
RAISE_APPLICATION_ERROR(-20103,
'Unerwarteter HTTP-Status ' || l_status || ': ' || l_response);
END IF;
END start_check_or_raise;
-- -------------------------------------------------------------------------
FUNCTION get_status(
p_project_id IN NUMBER
) RETURN t_project_status IS
l_rec t_project_status;
BEGIN
SELECT status, progress
INTO l_rec.status, l_rec.processing_progress
FROM dc_projects
WHERE id = p_project_id;
l_rec.is_completed := l_rec.status = 'COMPLETED';
l_rec.is_running := l_rec.status = 'IN_PROGRESS';
l_rec.is_pending := l_rec.status = 'PENDING';
RETURN l_rec;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20110,
'Projekt ' || p_project_id || ' nicht gefunden.');
END get_status;
-- -------------------------------------------------------------------------
PROCEDURE wait_for_completion(
p_project_id IN NUMBER,
p_timeout_sec IN NUMBER DEFAULT 1800,
p_interval_sec IN NUMBER DEFAULT 5
) IS
l_start TIMESTAMP := SYSTIMESTAMP;
l_status t_project_status;
BEGIN
LOOP
l_status := get_status(p_project_id);
EXIT WHEN l_status.is_completed;
IF EXTRACT(SECOND FROM (SYSTIMESTAMP - l_start))
+ EXTRACT(MINUTE FROM (SYSTIMESTAMP - l_start)) * 60
+ EXTRACT(HOUR FROM (SYSTIMESTAMP - l_start)) * 3600
> p_timeout_sec
THEN
RAISE_APPLICATION_ERROR(-20111,
'Timeout nach ' || p_timeout_sec || 's Projekt ' ||
p_project_id || ' noch im Status ' || l_status.status ||
' (' || l_status.processing_progress || '%).');
END IF;
DBMS_SESSION.SLEEP(p_interval_sec);
END LOOP;
END wait_for_completion;
-- -------------------------------------------------------------------------
FUNCTION convert_markdown(
p_markdown IN CLOB,
p_format IN VARCHAR2 DEFAULT 'PDF'
) RETURN BLOB IS
l_url VARCHAR2(500);
l_body CLOB;
l_result BLOB;
l_status NUMBER;
BEGIN
l_url := c_base_url || '/export/markdown';
-- JSON-Body mit CLOB-Inhalt: APEX_JSON unterstützt CLOBs beliebiger Größe
APEX_JSON.INITIALIZE_CLOB_OUTPUT;
APEX_JSON.OPEN_OBJECT;
APEX_JSON.WRITE('content', p_markdown);
APEX_JSON.WRITE('format', UPPER(p_format));
APEX_JSON.CLOSE_OBJECT;
l_body := APEX_JSON.GET_CLOB_OUTPUT;
APEX_JSON.FREE_OUTPUT;
set_headers();
l_result := APEX_WEB_SERVICE.MAKE_REST_REQUEST_B(
p_url => l_url,
p_http_method => 'POST',
p_body => l_body
);
l_status := APEX_WEB_SERVICE.G_STATUS_CODE;
IF l_status NOT IN (200, 201) THEN
RAISE_APPLICATION_ERROR(-20200,
'Export-Service Fehler: HTTP ' || l_status ||
' (format=' || p_format || ')');
END IF;
RETURN l_result;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -20200 THEN RAISE; END IF;
RAISE_APPLICATION_ERROR(-20201,
'Verbindungsfehler beim Export: ' || SQLERRM);
END convert_markdown;
-- -------------------------------------------------------------------------
FUNCTION project_report(
p_project_id IN NUMBER,
p_format IN VARCHAR2 DEFAULT 'PDF'
) RETURN BLOB IS
l_markdown CLOB;
BEGIN
SELECT report_markdown
INTO l_markdown
FROM dc_projects
WHERE id = p_project_id;
IF l_markdown IS NULL OR DBMS_LOB.GETLENGTH(l_markdown) = 0 THEN
RAISE_APPLICATION_ERROR(-20202,
'Projekt ' || p_project_id || ' hat noch keinen Markdown-Bericht. '
|| 'Bitte zuerst POST /api/dc/projects/' || p_project_id || '/report aufrufen.');
END IF;
RETURN convert_markdown(l_markdown, p_format);
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20110,
'Projekt ' || p_project_id || ' nicht gefunden.');
END project_report;
-- -------------------------------------------------------------------------
PROCEDURE regenerate_report(
p_project_id IN NUMBER
) IS
BEGIN
DC_UTILS_PKG.generate_report(p_project_id => p_project_id);
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20110,
'Projekt ' || p_project_id || ' nicht gefunden.');
END regenerate_report;
-- -------------------------------------------------------------------------
-- Interne Hilfsfunktion: schreibt einen VARCHAR2-Text als UTF8-Bytes in ein BLOB.
-- -------------------------------------------------------------------------
PROCEDURE append_text_to_blob(p_dest IN OUT BLOB, p_text IN VARCHAR2) IS
l_raw RAW(32767);
BEGIN
l_raw := UTL_RAW.CAST_TO_RAW(p_text);
DBMS_LOB.WRITEAPPEND(p_dest, UTL_RAW.LENGTH(l_raw), l_raw);
END append_text_to_blob;
-- -------------------------------------------------------------------------
FUNCTION get_catalog_status(
p_catalog_id IN NUMBER
) RETURN VARCHAR2 IS
l_status VARCHAR2(20);
BEGIN
SELECT generation_status
INTO l_status
FROM dc_question_catalogs
WHERE id = p_catalog_id;
RETURN l_status;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20310,
'Katalog ' || p_catalog_id || ' nicht gefunden.');
END get_catalog_status;
-- -------------------------------------------------------------------------
PROCEDURE wait_for_catalog(
p_catalog_id IN NUMBER,
p_timeout_sec IN NUMBER DEFAULT 1800,
p_interval_sec IN NUMBER DEFAULT 10
) IS
l_start TIMESTAMP := SYSTIMESTAMP;
l_status VARCHAR2(20);
l_elapsed NUMBER;
BEGIN
LOOP
l_status := get_catalog_status(p_catalog_id);
IF l_status = 'READY' THEN
RETURN;
END IF;
IF l_status = 'ERROR' THEN
DECLARE
l_error VARCHAR2(4000);
BEGIN
SELECT generation_error
INTO l_error
FROM dc_question_catalogs
WHERE id = p_catalog_id;
RAISE_APPLICATION_ERROR(-20311,
'Katalog ' || p_catalog_id || ' Generierung fehlgeschlagen: '
|| NVL(l_error, 'Unbekannter Fehler'));
END;
END IF;
-- Timeout prüfen
l_elapsed :=
EXTRACT(SECOND FROM (SYSTIMESTAMP - l_start))
+ EXTRACT(MINUTE FROM (SYSTIMESTAMP - l_start)) * 60
+ EXTRACT(HOUR FROM (SYSTIMESTAMP - l_start)) * 3600;
IF l_elapsed > p_timeout_sec THEN
RAISE_APPLICATION_ERROR(-20312,
'Timeout nach ' || p_timeout_sec || 's Katalog ' ||
p_catalog_id || ' noch im Status ' || l_status || '.');
END IF;
DBMS_SESSION.SLEEP(p_interval_sec);
END LOOP;
END wait_for_catalog;
-- -------------------------------------------------------------------------
FUNCTION generate_catalog(
p_file IN BLOB,
p_mime_type IN VARCHAR2 DEFAULT 'application/pdf',
p_filename IN VARCHAR2 DEFAULT 'dokument.pdf',
p_catalog_name IN VARCHAR2,
p_document_type_id IN NUMBER,
p_description IN VARCHAR2 DEFAULT NULL
) RETURN NUMBER IS
c_crlf CONSTANT VARCHAR2(2) := CHR(13) || CHR(10);
l_boundary VARCHAR2(50) := 'dc-boundary-' || TO_CHAR(SYSDATE, 'YYYYMMDDHHMI')
|| DBMS_RANDOM.STRING('X', 8);
l_url VARCHAR2(500);
l_body BLOB;
l_response CLOB;
l_status NUMBER;
l_catalog_id NUMBER;
-- Hilfsfunktion: multipart-Textfeld anfügen
PROCEDURE add_field(p_name VARCHAR2, p_value VARCHAR2) IS
BEGIN
IF p_value IS NULL THEN RETURN; END IF;
append_text_to_blob(l_body,
'--' || l_boundary || c_crlf
|| 'Content-Disposition: form-data; name="' || p_name || '"' || c_crlf
|| c_crlf
|| p_value || c_crlf);
END add_field;
BEGIN
l_url := c_base_url || '/catalog/generate';
-- ---- multipart-Body aufbauen ----
DBMS_LOB.CREATETEMPORARY(l_body, TRUE);
-- Textfelder
add_field('catalog_name', p_catalog_name);
add_field('document_type_id', TO_CHAR(p_document_type_id));
add_field('description', p_description);
-- Datei-Part: Header
append_text_to_blob(l_body,
'--' || l_boundary || c_crlf
|| 'Content-Disposition: form-data; name="file"; filename="'
|| p_filename || '"' || c_crlf
|| 'Content-Type: ' || p_mime_type || c_crlf
|| c_crlf);
-- Datei-Part: BLOB-Inhalt anhängen
DBMS_LOB.APPEND(l_body, p_file);
-- Datei-Part: Abschluss-CRLF
append_text_to_blob(l_body, c_crlf);
-- Abschließende Boundary
append_text_to_blob(l_body, '--' || l_boundary || '--' || c_crlf);
-- ---- HTTP-Request senden ----
APEX_WEB_SERVICE.G_REQUEST_HEADERS.DELETE;
APEX_WEB_SERVICE.G_REQUEST_HEADERS(1).NAME := 'Content-Type';
APEX_WEB_SERVICE.G_REQUEST_HEADERS(1).VALUE :=
'multipart/form-data; boundary=' || l_boundary;
IF g_api_key IS NOT NULL AND LENGTH(TRIM(g_api_key)) > 0 THEN
APEX_WEB_SERVICE.G_REQUEST_HEADERS(2).NAME := 'X-API-KEY';
APEX_WEB_SERVICE.G_REQUEST_HEADERS(2).VALUE := g_api_key;
END IF;
l_response := APEX_WEB_SERVICE.MAKE_REST_REQUEST(
p_url => l_url,
p_http_method => 'POST',
p_body_blob => l_body
);
DBMS_LOB.FREETEMPORARY(l_body);
l_status := APEX_WEB_SERVICE.G_STATUS_CODE;
-- 202 Accepted = asynchrone Verarbeitung gestartet
IF l_status NOT IN (200, 201, 202) THEN
RAISE_APPLICATION_ERROR(-20300,
'Katalog-Generierung fehlgeschlagen: HTTP ' || l_status
|| ' ' || SUBSTR(l_response, 1, 500));
END IF;
-- catalog_id aus JSON-Antwort lesen:
-- {"catalog_id": 42, "catalog_name": "...", "generation_status": "GENERATING"}
l_catalog_id := TO_NUMBER(JSON_VALUE(l_response, '$.catalog_id'));
IF l_catalog_id IS NULL THEN
RAISE_APPLICATION_ERROR(-20301,
'Antwort enthält keine catalog_id: ' || SUBSTR(l_response, 1, 500));
END IF;
RETURN l_catalog_id;
EXCEPTION
WHEN OTHERS THEN
IF DBMS_LOB.ISTEMPORARY(l_body) = 1 THEN
DBMS_LOB.FREETEMPORARY(l_body);
END IF;
IF SQLCODE IN (-20300, -20301) THEN RAISE; END IF;
RAISE_APPLICATION_ERROR(-20302,
'Verbindungsfehler bei Katalog-Generierung: ' || SQLERRM);
END generate_catalog;
END DC_BACKEND_PKG;
/
-- =============================================================================
-- Schnelltest (optional, auskommentiert)
-- =============================================================================
/*
-- Health-Check
BEGIN
IF DC_BACKEND_PKG.health_check() THEN
DBMS_OUTPUT.PUT_LINE('dc-backend: UP');
ELSE
DBMS_OUTPUT.PUT_LINE('dc-backend: NICHT ERREICHBAR');
END IF;
END;
/
-- Prüfung starten (Projekt-ID anpassen)
DECLARE
l_status NUMBER;
l_response VARCHAR2(4000);
BEGIN
DC_BACKEND_PKG.start_check(
p_project_id => 1,
p_status_code => l_status,
p_response => l_response
);
DBMS_OUTPUT.PUT_LINE('Status: ' || l_status);
DBMS_OUTPUT.PUT_LINE('Response: ' || l_response);
END;
/
-- Bericht als PDF exportieren
DECLARE
l_blob BLOB;
BEGIN
DC_BACKEND_PKG.g_api_key := 'mein-api-key';
l_blob := DC_BACKEND_PKG.project_report(p_project_id => 1, p_format => 'PDF');
DBMS_OUTPUT.PUT_LINE('PDF-Größe: ' || DBMS_LOB.GETLENGTH(l_blob) || ' Bytes');
END;
/
-- Bericht als DOCX exportieren
DECLARE
l_blob BLOB;
BEGIN
DC_BACKEND_PKG.g_api_key := 'mein-api-key';
l_blob := DC_BACKEND_PKG.project_report(p_project_id => 1, p_format => 'DOCX');
DBMS_OUTPUT.PUT_LINE('DOCX-Größe: ' || DBMS_LOB.GETLENGTH(l_blob) || ' Bytes');
END;
/
*/