feat: Externe Dokument-Referenzen erkennen, herunterladen & mitbewerten

Nach der Transkription eines Dokuments werden referenzierte externe
Dokumente (AGB, Verhaltenskodex) erkannt, sicher heruntergeladen und ihr
Text dem Hauptdokument fuer die "Summen"-Bewertung angehaengt (Option D).
Die Referenzen werden zusaetzlich als eigene, herunterladbare Zeilen
gespeichert.

- DB: dc_project_documents um parent_document_id, is_reference, source_url
  erweitert (DC_REFERENCES_ALTER.sql)
- ORDS: Dokumentenliste um die Felder erweitert; neue Endpunkte
  POST /documents/:id/references und DELETE /projects/:id/references;
  q-Quote-Parserfehler im ai-costs-Handler ('[]') behoben
- Backend: ReferenceDownloadService (SSRF-sicher via DNS-/Public-IP-
  Validierung, Redirect-Pruefung, Groessen-/Timeout-Limits, jsoup fuer HTML),
  EvaluationService.extractReferenceUrls (LLM-basierte URL-Erkennung),
  Orchestrierung (Discovery in Phase A, Referenz-Skip + combinedText in Phase B)
- Config: dc.references.* in application.properties

Verifiziert an Projekt 144: AGB-PDF/-HTML heruntergeladen, verknuepft,
"Summen"-Bewertung zitiert AGB-Klauseln aus dem Referenzdokument.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 12:45:55 +02:00
parent d00525c15f
commit c75f460ec5
91 changed files with 3949 additions and 255 deletions

View File

@@ -14,8 +14,13 @@ public class OrdsDocument {
private Long documentTypeId;
private Long catalogId;
private LocalDateTime uploadedAt;
private String status;
private Integer progress;
private Integer hasOriginalText; // 0 oder 1
private Integer hasTranslatedText; // 0 oder 1
private Long parentDocumentId; // gesetzt bei heruntergeladenen Referenzen
private Integer isReference; // 0 = Originaldokument, 1 = Referenz
private String sourceUrl; // Ursprungs-URL der Referenz
private Integer rowVersion;
private LocalDateTime created;
private LocalDateTime updated;
@@ -40,12 +45,30 @@ public class OrdsDocument {
public LocalDateTime getUploadedAt() { return uploadedAt; }
public void setUploadedAt(LocalDateTime v) { this.uploadedAt = v; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public Integer getProgress() { return progress; }
public void setProgress(Integer progress) { this.progress = progress; }
public Integer getHasOriginalText() { return hasOriginalText; }
public void setHasOriginalText(Integer v) { this.hasOriginalText = v; }
public Integer getHasTranslatedText() { return hasTranslatedText; }
public void setHasTranslatedText(Integer v) { this.hasTranslatedText = v; }
public Long getParentDocumentId() { return parentDocumentId; }
public void setParentDocumentId(Long v) { this.parentDocumentId = v; }
public Integer getIsReference() { return isReference; }
public void setIsReference(Integer v) { this.isReference = v; }
/** true, wenn dieses Dokument eine heruntergeladene Referenz ist. */
public boolean isReference() { return Integer.valueOf(1).equals(isReference); }
public String getSourceUrl() { return sourceUrl; }
public void setSourceUrl(String v) { this.sourceUrl = v; }
public Integer getRowVersion() { return rowVersion; }
public void setRowVersion(Integer v) { this.rowVersion = v; }