feat(archive): RET-02 Aufbewahrungsfristen-Engine
internal/retentionengine: Frist je Aufbewahrungsklasse als natives Postgres-INTERVAL, Stichtagsberechnung an Postgres delegiert statt eigener Kalenderrechnung (Schaltjahr/Monatsende-Referenzwerte real verifiziert: 2024-02-29+1y=2025-02-28, 2026-01-31+1mo=2026-02-28). Periodischer Job (ListExpiringObjects) beschraenkt sich per DISTINCT ON auf die juengste Klassenzuordnung je Objekt - sonst wuerden Objekte mit mehrfach geaenderter Klasse (RET-01-Historisierung) doppelt auftauchen, real mit einem Zwei-Zuordnungen-Testobjekt bewiesen. Scope bewusst eng gehalten: keine RET-05-Anbindung, keine Vernichtungslogik - das ist Ticket-Scope, dependsOn ist nur RET-01.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
# RET-02 – Prüfprotokoll: Aufbewahrungsfristen-Engine
|
||||
|
||||
Voraussetzung RET-01 – erledigt, siehe eigenes Protokoll.
|
||||
|
||||
**Scope-Klarstellung:** Dieses Ticket ist die Fristen-BERECHNUNGS-Engine
|
||||
(Frist je Klasse, Stichtagsberechnung, periodischer Ablauf-Job) —
|
||||
`dependsOn: ["RET-01"]`, KEINE Abhängigkeit auf RET-05. Die DMS-/Mail-
|
||||
seitige Registrierung als RET-05-Adapter-Konsument sowie
|
||||
Vernichtungs-Job-Fehlerbehandlung (2xx/Requeue) sind NICHT Teil dieser
|
||||
Kachel — das ist ein späteres, eigenes Ticket (vermutlich im
|
||||
ARC-*/DOC-*-Umfeld). Bewusst nicht mitgebaut, um nicht über den
|
||||
Ticket-Umfang hinaus zu implementieren.
|
||||
|
||||
## Grundsatzentscheidung: Postgres-INTERVAL statt eigener Kalenderrechnung
|
||||
|
||||
`retention_class_rules.duration` ist ein natives Postgres-`INTERVAL`
|
||||
(z. B. `'10 years'`, `'6 months'`) — `ComputeDueDate` delegiert die
|
||||
gesamte Stichtagsberechnung an Postgres selbst (`start + duration`),
|
||||
statt eine eigene Schaltjahr-/Monatsende-Logik in Go nachzubauen, die
|
||||
von der WHERE-Klausel des periodischen Jobs (dieselbe Arithmetik)
|
||||
abweichen könnte. Referenzwerte für Akzeptanzkriterium 2 real gegen
|
||||
Postgres verifiziert, nicht angenommen:
|
||||
`2024-02-29 + 1 year = 2025-02-28`, `2026-01-31 + 1 month = 2026-02-28`.
|
||||
|
||||
## Umsetzung
|
||||
|
||||
- `migrations/0004_retention_class_rules.up.sql`/`.down.sql`.
|
||||
- `internal/retentionengine.ConfigureClassRule` — eine Regel je Klasse
|
||||
(`UPSERT`).
|
||||
- `internal/retentionengine.ComputeDueDate` — delegiert an Postgres.
|
||||
- `internal/retentionengine.ListExpiringObjects` — periodischer Job:
|
||||
`DISTINCT ON (retention_object_id)` auf die JÜNGSTE Klassenzuordnung
|
||||
beschränkt, sonst würde ein Objekt mit mehrfach geänderter Klasse
|
||||
(RET-01s Historisierung) mehrfach im Ergebnis auftauchen.
|
||||
|
||||
## Prüfungen
|
||||
|
||||
| # | Prüfung | Ergebnis |
|
||||
|---|---|---|
|
||||
| 1 | Fristberechnung an Referenzdaten mit bekannten Ablaufdaten geprüft | **bestanden** — `TestComputeDueDate_KnownReferenceDates`: Schaltjahr (29.02.2024 + 1 Jahr → 28.02.2025) und Monatsende (31.01.2026 + 1 Monat → 28.02.2026), beide Werte vorab real gegen Postgres verifiziert |
|
||||
| 2 | Job liefert bei leerem Bestand ein leeres, nicht fehlerhaftes Ergebnis | **bestanden** — `TestListExpiringObjects_EmptyBacklogReturnsEmptyNotError` |
|
||||
| 3 | Mehrfachausführung des Jobs erzeugt keine doppelten Einträge | **bestanden** — `TestListExpiringObjects_NoDuplicatesAcrossHistoricalClassChanges`: Objekt mit ZWEI historischen Klassenzuordnungen (beide abgelaufen), zwei Job-Läufe liefern je genau 1 Eintrag — ohne die `DISTINCT ON`-Einschränkung wäre es 2 gewesen |
|
||||
|
||||
## Echte Verdrahtung auf 192.168.1.131
|
||||
|
||||
- Migration real gegen `dms_tenant_test` angewendet — `retention_class_rules`
|
||||
bestätigt vorhanden neben `retention_objects`/`retention_class_assignments`
|
||||
- Kein systemd-Timer in diesem Ticket — "periodischer Job" ist die
|
||||
Bibliotheksfunktion `ListExpiringObjects`; ihr tatsächlicher
|
||||
Aufrufer/Zeitplan (systemd-Timer + Meldeweg für abgelaufene Objekte)
|
||||
ist Aufgabe eines Folgetickets, das auch die Vernichtungslogik selbst
|
||||
bringt (dieses Ticket berechnet nur, wer fällig ist — vernichtet
|
||||
nichts)
|
||||
|
||||
## Build/Test-Ergebnis (192.168.1.131, `make check`)
|
||||
|
||||
```
|
||||
go build ./... -> clean
|
||||
go vet ./... -> clean
|
||||
golangci-lint run ./... -> 0 issues
|
||||
go test ./... -p 1 -count=1 -> 10/10 Pakete mit Tests ok, 0 Fehlschläge
|
||||
```
|
||||
|
||||
## Gesamtergebnis
|
||||
|
||||
**Bestanden.** Alle drei Akzeptanzkriterien und alle drei
|
||||
Pflichtprüfungen real erfüllt — Stichtagsberechnung gegen real
|
||||
verifizierte Postgres-Referenzwerte, Job-Idempotenz gegen ein Objekt mit
|
||||
echter Mehrfach-Historie bewiesen (nicht nur behauptet).
|
||||
Reference in New Issue
Block a user