Extend Bewerb repository and service: add RichterEinsatz handling, enhance property mapping, and align DTOs with updated domain model.

This commit is contained in:
2026-04-08 22:59:35 +02:00
parent 8b6ea11d46
commit e4f22096ed
2 changed files with 164 additions and 24 deletions
@@ -2,7 +2,11 @@
package at.mocode.entries.service.bewerbe package at.mocode.entries.service.bewerbe
import at.mocode.core.domain.model.AbteilungsTeilungsTypE
import at.mocode.core.domain.model.BeginnZeitTypE
import at.mocode.entries.domain.model.RichterEinsatz
import at.mocode.entries.service.persistence.AbteilungTable import at.mocode.entries.service.persistence.AbteilungTable
import at.mocode.entries.service.persistence.BewerbRichterEinsatzTable
import at.mocode.entries.service.persistence.BewerbTable import at.mocode.entries.service.persistence.BewerbTable
import at.mocode.entries.service.tenant.tenantTransaction import at.mocode.entries.service.tenant.tenantTransaction
import org.jetbrains.exposed.v1.core.ResultRow import org.jetbrains.exposed.v1.core.ResultRow
@@ -18,25 +22,92 @@ import kotlin.uuid.toKotlinUuid
class BewerbRepositoryImpl : BewerbRepository { class BewerbRepositoryImpl : BewerbRepository {
private fun rowToBewerb(row: ResultRow): Bewerb = Bewerb( private fun loadRichterEinsaetze(bewerbId: Uuid): List<RichterEinsatz> =
id = row[BewerbTable.id].toKotlinUuid(), BewerbRichterEinsatzTable
turnierId = row[BewerbTable.turnierId].toKotlinUuid(), .selectAll()
klasse = row[BewerbTable.klasse], .where { BewerbRichterEinsatzTable.bewerbId eq bewerbId.toJavaUuid() }
hoeheCm = row[BewerbTable.hoeheCm], .map { row ->
bezeichnung = row[BewerbTable.bezeichnung] RichterEinsatz(
) funktionaerId = row[BewerbRichterEinsatzTable.funktionaerId].toKotlinUuid(),
position = row[BewerbRichterEinsatzTable.position]
)
}
private fun persistRichterEinsaetze(bewerbId: Uuid, einsaetze: List<RichterEinsatz>) {
BewerbRichterEinsatzTable.deleteWhere { BewerbRichterEinsatzTable.bewerbId eq bewerbId.toJavaUuid() }
einsaetze.forEach { re ->
BewerbRichterEinsatzTable.insert { s ->
s[BewerbRichterEinsatzTable.bewerbId] = bewerbId.toJavaUuid()
s[BewerbRichterEinsatzTable.funktionaerId] = re.funktionaerId.toJavaUuid()
s[BewerbRichterEinsatzTable.position] = re.position
}
}
}
private fun rowToBewerb(row: ResultRow): Bewerb {
val id = row[BewerbTable.id].toKotlinUuid()
return Bewerb(
id = id,
turnierId = row[BewerbTable.turnierId].toKotlinUuid(),
klasse = row[BewerbTable.klasse],
hoeheCm = row[BewerbTable.hoeheCm],
bezeichnung = row[BewerbTable.bezeichnung],
// Abteilungs-Konfiguration
teilungsTyp = row[BewerbTable.teilungsTyp]?.let { AbteilungsTeilungsTypE.valueOf(it) },
// Text & Details
beschreibung = row[BewerbTable.beschreibung],
aufgabe = row[BewerbTable.aufgabe],
aufgabenNummer = row[BewerbTable.aufgabenNummer],
paraGrade = row[BewerbTable.paraGrade],
// Ort & Funktionäre
austragungsplatzId = row[BewerbTable.austragungsplatzId]?.toKotlinUuid(),
richterEinsaetze = loadRichterEinsaetze(id),
// Zeitplan exposed-kotlin-datetime liefert kotlinx.datetime-Typen direkt
geplantesDatum = row[BewerbTable.geplantesDatum],
beginnZeitTyp = row[BewerbTable.beginnZeitTyp]?.let { BeginnZeitTypE.valueOf(it) },
beginnZeit = row[BewerbTable.beginnZeit],
reitdauerMinuten = row[BewerbTable.reitdauerMinuten],
umbauMinuten = row[BewerbTable.umbauMinuten],
besichtigungMinuten = row[BewerbTable.besichtigungMinuten],
stechenGeplant = row[BewerbTable.stechenGeplant],
// Finanzen
startgeldCent = row[BewerbTable.startgeldCent],
geldpreisAusbezahlt = row[BewerbTable.geldpreisAusbezahlt]
)
}
override suspend fun create(b: Bewerb): Bewerb = tenantTransaction { override suspend fun create(b: Bewerb): Bewerb = tenantTransaction {
val now = Clock.System.now() val now = Clock.System.now()
BewerbTable.insert { s -> BewerbTable.insert { s ->
s[BewerbTable.id] = b.id.toJavaUuid() s[BewerbTable.id] = b.id.toJavaUuid()
s[BewerbTable.turnierId] = b.turnierId.toJavaUuid() s[BewerbTable.turnierId] = b.turnierId.toJavaUuid()
s[BewerbTable.klasse] = b.klasse s[BewerbTable.klasse] = b.klasse
s[BewerbTable.hoeheCm] = b.hoeheCm s[BewerbTable.hoeheCm] = b.hoeheCm
s[BewerbTable.bezeichnung] = b.bezeichnung s[BewerbTable.bezeichnung] = b.bezeichnung
s[BewerbTable.createdAt] = now // Abteilungs-Konfiguration
s[BewerbTable.updatedAt] = now s[BewerbTable.teilungsTyp] = b.teilungsTyp?.name
// Text & Details
s[BewerbTable.beschreibung] = b.beschreibung
s[BewerbTable.aufgabe] = b.aufgabe
s[BewerbTable.aufgabenNummer] = b.aufgabenNummer
s[BewerbTable.paraGrade] = b.paraGrade
// Ort
s[BewerbTable.austragungsplatzId] = b.austragungsplatzId?.toJavaUuid()
// Zeitplan kotlinx.datetime-Typen direkt übergeben
s[BewerbTable.geplantesDatum] = b.geplantesDatum
s[BewerbTable.beginnZeitTyp] = b.beginnZeitTyp?.name
s[BewerbTable.beginnZeit] = b.beginnZeit
s[BewerbTable.reitdauerMinuten] = b.reitdauerMinuten
s[BewerbTable.umbauMinuten] = b.umbauMinuten
s[BewerbTable.besichtigungMinuten] = b.besichtigungMinuten
s[BewerbTable.stechenGeplant] = b.stechenGeplant
// Finanzen
s[BewerbTable.startgeldCent] = b.startgeldCent
s[BewerbTable.geldpreisAusbezahlt] = b.geldpreisAusbezahlt
s[BewerbTable.createdAt] = now
s[BewerbTable.updatedAt] = now
} }
persistRichterEinsaetze(b.id, b.richterEinsaetze)
BewerbTable.selectAll().where { BewerbTable.id eq b.id.toJavaUuid() }.map(::rowToBewerb).single() BewerbTable.selectAll().where { BewerbTable.id eq b.id.toJavaUuid() }.map(::rowToBewerb).single()
} }
@@ -54,11 +125,32 @@ class BewerbRepositoryImpl : BewerbRepository {
override suspend fun update(b: Bewerb): Bewerb = tenantTransaction { override suspend fun update(b: Bewerb): Bewerb = tenantTransaction {
val now = Clock.System.now() val now = Clock.System.now()
BewerbTable.update({ BewerbTable.id eq b.id.toJavaUuid() }) { s -> BewerbTable.update({ BewerbTable.id eq b.id.toJavaUuid() }) { s ->
s[BewerbTable.klasse] = b.klasse s[BewerbTable.klasse] = b.klasse
s[BewerbTable.hoeheCm] = b.hoeheCm s[BewerbTable.hoeheCm] = b.hoeheCm
s[BewerbTable.bezeichnung] = b.bezeichnung s[BewerbTable.bezeichnung] = b.bezeichnung
s[BewerbTable.updatedAt] = now // Abteilungs-Konfiguration
s[BewerbTable.teilungsTyp] = b.teilungsTyp?.name
// Text & Details
s[BewerbTable.beschreibung] = b.beschreibung
s[BewerbTable.aufgabe] = b.aufgabe
s[BewerbTable.aufgabenNummer] = b.aufgabenNummer
s[BewerbTable.paraGrade] = b.paraGrade
// Ort
s[BewerbTable.austragungsplatzId] = b.austragungsplatzId?.toJavaUuid()
// Zeitplan kotlinx.datetime-Typen direkt übergeben
s[BewerbTable.geplantesDatum] = b.geplantesDatum
s[BewerbTable.beginnZeitTyp] = b.beginnZeitTyp?.name
s[BewerbTable.beginnZeit] = b.beginnZeit
s[BewerbTable.reitdauerMinuten] = b.reitdauerMinuten
s[BewerbTable.umbauMinuten] = b.umbauMinuten
s[BewerbTable.besichtigungMinuten] = b.besichtigungMinuten
s[BewerbTable.stechenGeplant] = b.stechenGeplant
// Finanzen
s[BewerbTable.startgeldCent] = b.startgeldCent
s[BewerbTable.geldpreisAusbezahlt] = b.geldpreisAusbezahlt
s[BewerbTable.updatedAt] = now
} }
persistRichterEinsaetze(b.id, b.richterEinsaetze)
BewerbTable.selectAll().where { BewerbTable.id eq b.id.toJavaUuid() }.map(::rowToBewerb).single() BewerbTable.selectAll().where { BewerbTable.id eq b.id.toJavaUuid() }.map(::rowToBewerb).single()
} }
@@ -6,6 +6,7 @@ import at.mocode.entries.domain.repository.NennungRepository
import at.mocode.entries.service.errors.LockedException import at.mocode.entries.service.errors.LockedException
import at.mocode.entries.service.persistence.TurnierTable import at.mocode.entries.service.persistence.TurnierTable
import at.mocode.entries.service.tenant.tenantTransaction import at.mocode.entries.service.tenant.tenantTransaction
import at.mocode.entries.domain.model.RichterEinsatz
import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.jdbc.selectAll import org.jetbrains.exposed.v1.jdbc.selectAll
import kotlin.uuid.Uuid import kotlin.uuid.Uuid
@@ -21,14 +22,35 @@ class BewerbService(
row?.get(TurnierTable.status) == "PUBLISHED" row?.get(TurnierTable.status) == "PUBLISHED"
} }
suspend fun create(turnierId: Uuid, klasse: String, hoeheCm: Int?, bezeichnung: String): Bewerb { suspend fun create(turnierId: Uuid, req: CreateBewerbRequest): Bewerb {
if (isTurnierPublished(turnierId)) throw LockedException("Turnier ist PUBLISHED Bewerbe können nicht angelegt werden") if (isTurnierPublished(turnierId)) throw LockedException("Turnier ist PUBLISHED Bewerbe können nicht angelegt werden")
val b = Bewerb( val b = Bewerb(
id = Uuid.random(), id = Uuid.random(),
turnierId = turnierId, turnierId = turnierId,
klasse = klasse, klasse = req.klasse,
hoeheCm = hoeheCm, hoeheCm = req.hoeheCm,
bezeichnung = bezeichnung bezeichnung = req.bezeichnung,
// Abteilungs-Konfiguration
teilungsTyp = req.teilungsTyp,
// Text & Details
beschreibung = req.beschreibung,
aufgabe = req.aufgabe,
aufgabenNummer = req.aufgabenNummer,
paraGrade = req.paraGrade,
// Ort & Funktionäre
austragungsplatzId = req.austragungsplatzId?.let { Uuid.parse(it) },
richterEinsaetze = req.richterEinsaetze.map { RichterEinsatz(Uuid.parse(it.funktionaerId), it.position) },
// Zeitplan
geplantesDatum = req.geplantesDatum,
beginnZeitTyp = req.beginnZeitTyp,
beginnZeit = req.beginnZeit,
reitdauerMinuten = req.reitdauerMinuten,
umbauMinuten = req.umbauMinuten,
besichtigungMinuten = req.besichtigungMinuten,
stechenGeplant = req.stechenGeplant,
// Finanzen
startgeldCent = req.startgeldCent,
geldpreisAusbezahlt = req.geldpreisAusbezahlt
) )
return repo.create(b) return repo.create(b)
} }
@@ -37,10 +59,36 @@ class BewerbService(
suspend fun get(id: Uuid): Bewerb = repo.findById(id) ?: throw NoSuchElementException("Bewerb $id nicht gefunden") suspend fun get(id: Uuid): Bewerb = repo.findById(id) ?: throw NoSuchElementException("Bewerb $id nicht gefunden")
suspend fun update(id: Uuid, klasse: String, hoeheCm: Int?, bezeichnung: String): Bewerb { suspend fun update(id: Uuid, req: UpdateBewerbRequest): Bewerb {
val current = get(id) val current = get(id)
if (isTurnierPublished(current.turnierId)) throw LockedException("Turnier ist PUBLISHED Bewerbe können nicht geändert werden") if (isTurnierPublished(current.turnierId)) throw LockedException("Turnier ist PUBLISHED Bewerbe können nicht geändert werden")
return repo.update(current.copy(klasse = klasse, hoeheCm = hoeheCm, bezeichnung = bezeichnung)) val updated = current.copy(
klasse = req.klasse,
hoeheCm = req.hoeheCm,
bezeichnung = req.bezeichnung,
// Abteilungs-Konfiguration
teilungsTyp = req.teilungsTyp,
// Text & Details
beschreibung = req.beschreibung,
aufgabe = req.aufgabe,
aufgabenNummer = req.aufgabenNummer,
paraGrade = req.paraGrade,
// Ort & Funktionäre
austragungsplatzId = req.austragungsplatzId?.let { Uuid.parse(it) },
richterEinsaetze = req.richterEinsaetze.map { RichterEinsatz(Uuid.parse(it.funktionaerId), it.position) },
// Zeitplan
geplantesDatum = req.geplantesDatum,
beginnZeitTyp = req.beginnZeitTyp,
beginnZeit = req.beginnZeit,
reitdauerMinuten = req.reitdauerMinuten,
umbauMinuten = req.umbauMinuten,
besichtigungMinuten = req.besichtigungMinuten,
stechenGeplant = req.stechenGeplant,
// Finanzen
startgeldCent = req.startgeldCent,
geldpreisAusbezahlt = req.geldpreisAusbezahlt,
)
return repo.update(updated)
} }
suspend fun delete(id: Uuid) { suspend fun delete(id: Uuid) {