Introduce tournament structure management for Entries Service:

- Add multi-layered entity support for `Turniere`, `Bewerbe`, and `Abteilungen` with tenant isolation.
- Implement Flyway schema migrations with constraints, indices, and default values for `Turniere`.
- Add Kotlin repositories and services for CRUD operations and validation across entities.
- Ensure tenant-safe transactions and implement new exception handling for `LockedException` and `ValidationException`.
- Provide REST APIs with controllers for managing lifecycle, hierarchy, and relationships between entities (`Turniere`, `Bewerbe`, and `Abteilungen`).
- Update Spring configuration with dependency wiring for new services and repositories.
This commit is contained in:
2026-04-03 00:06:16 +02:00
parent 85282ea7b4
commit c483f4925d
22 changed files with 908 additions and 10 deletions
+68 -10
View File
@@ -1,6 +1,6 @@
# 👷 [Backend Developer] — Schritt-für-Schritt Roadmap
> **Stand:** 2. April 2026
> **Stand:** 3. April 2026
> **Rolle:** Spring Boot / Ktor, Kotlin, SQL, API-Design, Datenbankschema, Services
---
@@ -42,6 +42,12 @@
- [x] Tabelle `teilnehmer_konten` anlegen (FK → `veranstaltung_id`, aggregiert Salden über Turniere)
- [x] Tabelle `turnier_kassa` anlegen (FK → `turnier_id`, separate Kassa pro Turnier)
- [x] Migrations-Skript schreiben und testen (`db/tenant/V2__domain_hierarchy.sql`, Test: `DomainHierarchyMigrationTest`)
- [x] Ergänzung V3 (Turnier-Status): Migration `db/tenant/V3__turniere_status.sql`
- [x] `ALTER TABLE turniere ADD COLUMN status VARCHAR(16) NOT NULL DEFAULT 'DRAFT'` + CHECK(`status` IN ('DRAFT','PUBLISHED'))
- [x] `ALTER TABLE turniere ADD COLUMN published_at TIMESTAMP WITH TIME ZONE NULL`
- [x] Backfill: Alle bestehenden Zeilen auf `DRAFT` setzen; Default danach wieder entfernen oder beibehalten (Entscheidung: beibehalten für Insert-Sicherheit)
- [x] Indexe: `CREATE INDEX IF NOT EXISTS idx_turniere_status ON turniere(status)`
- [x] Folgetasks: Domänenservice-Validierung für Statuswechsel (siehe B-1 Turniere/PATCH)
- [ ] **A-3** | Validierungs-Grundlage: Turnierkategorie-Limits
- [x] Grundlage implementiert: Entkoppelte Policy-Schnittstelle + Bewerb-Descriptor
@@ -57,15 +63,67 @@
## 🟠 Sprint B — Kurzfristig (nächste Woche)
- [ ] **B-1** | CRUD-Endpunkte für alle Stammdaten-Entitäten
- [ ] `POST/GET/PUT/DELETE /veranstaltungen`
- [ ] `POST/GET/PUT/DELETE /turniere` (inkl. Status-Feld: `DRAFT | PUBLISHED`)
- [ ] `POST/GET/PUT/DELETE /bewerbe`
- [ ] `POST/GET/PUT/DELETE /abteilungen`
- [ ] `POST/GET/PUT/DELETE /reiter`
- [ ] `POST/GET/PUT/DELETE /pferde`
- [ ] `POST/GET/PUT/DELETE /vereine`
- [ ] `POST/GET/PUT/DELETE /funktionaere`
- [ ] **B-1** | CRUD-Endpunkte für alle Stammdaten-Entitäten (überarbeitet)
- Multitenancy: Alle Endpunkte laufen im Tenant-Schema (Erkennung via `X-Event-Id` oder Subdomain; siehe A-1). IDs sind UUIDs. Fehlercodes: 400 (Bad Request), 404 (Not Found), 409 (Conflict), 422 (Validation), 423 (Locked Tenant/Status).
- Konventionen:
- POST → 201 + `Location`-Header; GET (Liste) ist paginiert (`page`, `size`) + einfache Filter (`q`, spezifische Felder).
- PUT = Voll-Update; PATCH = Teil-Update für Status/kleine Änderungen, wo sinnvoll.
- Lösch-Strategie: Hard-Delete nur für Stammdaten ohne Referenzen; sonst 409 bei FK-Verletzung.
- Standard-HTTP-Codes: `GET` 200, `POST` 201, `PUT` 200, `PATCH` 200, `DELETE` 204; Fehler gemäß obiger Liste.
- Veranstaltung (Singleton pro Tenant)
- [x] `GET /veranstaltung` — aktuelle Veranstaltung lesen
- [x] `PUT /veranstaltung` — Veranstaltung aktualisieren
- Hinweis: Erstellen/Löschen einer Veranstaltung erfolgt im Control-Plane (außerhalb des Tenant-Services); daher kein `POST/DELETE` hier.
- Turniere
- [x] `POST /turniere` — Turnier anlegen (Felder: `veranstaltungId` implizit aus Tenant, `oepsTurniernummer`, optional `bezeichnung`, `datumVon/Bis`, optional `status`—Default `DRAFT`)
- [x] `GET /turniere` — Liste (Filter: `oepsTurniernummer`, Zeitraum, `status`; Paging)
- [x] `GET /turniere/{id}` — Detail
- [x] `PUT /turniere/{id}` — Voll-Update (ohne Status-Übergang)
- Regeln: Bei `PUBLISHED` nur Metadaten änderbar, keine strukturellen Felder (z. B. `oepsTurniernummer`) → sonst `423 Locked`.
- [x] `DELETE /turniere/{id}` — löschen (409, falls abhängige Bewerbe existieren; bei `PUBLISHED` grundsätzlich gesperrt → `423 Locked`)
- Status-Management (neues Feld, Migration `V3__turniere_status.sql`): `DRAFT | PUBLISHED`
- [x] `PATCH /turniere/{id}/status` — Statuswechsel mit Validierung
- Erlaubt: `DRAFT → PUBLISHED` (setzt `publishedAt`-Timestamp serverseitig)
- `PUBLISHED → DRAFT` nur erlaubt, wenn keine Nennungen/Zahlungen verbucht sind (sonst `409 Conflict`)
- Unerlaubte Übergänge → `422 Validation` (inkl. Begründung im `problem+json`-Body)
- Bewerbe (FK → Turnier)
- [x] `POST /turniere/{turnierId}/bewerbe` — anlegen
- [x] `GET /turniere/{turnierId}/bewerbe` — Liste im Turnier
- [x] `GET /bewerbe/{id}` — Detail
- [x] `PUT /bewerbe/{id}` — aktualisieren
- [x] `DELETE /bewerbe/{id}` — löschen (409 bei existierenden Abteilungen/Nennungen; gesperrt falls zu `PUBLISHED` Turnier → `423 Locked`)
- Abteilungen (FK → Bewerb)
- [x] `POST /bewerbe/{bewerbId}/abteilungen` — anlegen (Felder: `nr`, `bezeichnung`, `typ: SEPARATE_SIEGEREHRUNG | ORGANISATORISCH`)
- [x] `GET /bewerbe/{bewerbId}/abteilungen` — Liste
- [x] `GET /abteilungen/{id}` — Detail
- [x] `PUT /abteilungen/{id}` — aktualisieren
- [x] `DELETE /abteilungen/{id}` — löschen (gesperrt falls zu `PUBLISHED` Turnier → `423 Locked`)
- Hinweis: Filter `q` (LIKE/ILIKE) bei Bewerbe-Liste ist vorerst ausgelassen und kann nachgezogen werden.
- Reiter (Athleten-Stammdaten)
- [ ] `POST/GET/GET{id}/PUT/DELETE /reiter` — Suche über `q` (Name, Lizenznr.), Filter: `lizenzKlasse`, `vereinId`
- Pferde (Pferde-Stammdaten)
- [ ] `POST/GET/GET{id}/PUT/DELETE /pferde` — Suche `q` (Name, Lebensnr.), Filter: `jahrgang`, `besitzerId`
- Vereine
- [ ] `POST/GET/GET{id}/PUT/DELETE /vereine` — Suche `q` (Name, Kürzel), Filter: `verband`
- Funktionäre
- [ ] `POST/GET/GET{id}/PUT/DELETE /funktionaere` — Suche `q` (Name, Lizenznr.), Filter: `rolle`
- Technische Notizen
- [ ] API-Doku per OpenAPI (Springdoc) veröffentlichen; Beispiel-Payloads für POST/PUT/PATCH (Statuswechsel)
- [x] Konsistentes Error-Format (`problem+json`)
- [ ] E2E-Tests: CRUD-Flows für Turnier → Bewerb → Abteilung inkl. FK-Constraints
- [x] Migration `V3__turniere_status.sql` in Flyway integrieren und gegen H2/Postgres testen (Back/Forward kompatibel)
- [x] Guardrails: Service-Ebene erzwingt Locks für `PUBLISHED` (PUT/DELETE) und valide Status-Transitions (PATCH)
- [x] Problem+JSON-Details: `type`, `title`, `status`, `detail`, `instance` befüllen; bei `422` Begründung/Violations je Feld mitschicken.
- [ ] **B-2** | Kassa-Service implementieren
- [ ] `TeilnehmerKonto`-Service: Saldo aus mehreren Turnieren aggregieren