Integrate qualification master data system (QualifikationMasterTable) for functionaries, refactor mapping logic in repositories, enhance database initialization for ZNS and Masterdata services, and add a seeder for ÖTO/FEI qualification data. Fix PSQLException during ZNS imports.

This commit is contained in:
2026-04-06 13:53:06 +02:00
parent 933ef9cd6c
commit c35869f8ee
7 changed files with 153 additions and 23 deletions
@@ -5,20 +5,17 @@ import at.mocode.core.domain.model.DatenQuelleE
import at.mocode.core.utils.database.DatabaseFactory
import at.mocode.masterdata.domain.model.Funktionaer
import at.mocode.masterdata.domain.repository.FunktionaerRepository
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.inList
import org.jetbrains.exposed.v1.jdbc.deleteWhere
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.selectAll
import org.jetbrains.exposed.v1.jdbc.update
import at.mocode.masterdata.infrastructure.persistence.funktionaer.*
import org.jetbrains.exposed.v1.core.*
import org.jetbrains.exposed.v1.jdbc.*
import org.slf4j.LoggerFactory
import kotlin.uuid.Uuid
/**
* Exposed-basierte Implementierung des Funktionaer-Repositorys.
*/
class FunktionaerExposedRepository : FunktionaerRepository {
private val log = LoggerFactory.getLogger(FunktionaerExposedRepository::class.java)
private fun rowToDomFunktionaer(row: ResultRow, qualifikationen: List<String> = emptyList()): Funktionaer {
return Funktionaer(
@@ -37,9 +34,9 @@ class FunktionaerExposedRepository : FunktionaerRepository {
}
override suspend fun findById(id: Uuid): Funktionaer? = DatabaseFactory.dbQuery {
val qualifikationen = FunktionaerQualifikationTable
val qualifikationen = (FunktionaerQualifikationTable innerJoin QualifikationMasterTable)
.selectAll().where { FunktionaerQualifikationTable.funktionaerId eq id }
.map { it[FunktionaerQualifikationTable.qualifikation] }
.map { it[QualifikationMasterTable.code] }
FunktionaerTable.selectAll().where { FunktionaerTable.id eq id }
.map { rowToDomFunktionaer(it, qualifikationen) }
@@ -51,9 +48,9 @@ class FunktionaerExposedRepository : FunktionaerRepository {
.where { (FunktionaerTable.satzId eq satzID) and (FunktionaerTable.satzNummer eq satzNummer) }
.singleOrNull() ?: return@dbQuery null
val qualifikationen = FunktionaerQualifikationTable
val qualifikationen = (FunktionaerQualifikationTable innerJoin QualifikationMasterTable)
.selectAll().where { FunktionaerQualifikationTable.funktionaerId eq row[FunktionaerTable.id] }
.map { it[FunktionaerQualifikationTable.qualifikation] }
.map { it[QualifikationMasterTable.code] }
rowToDomFunktionaer(row, qualifikationen)
}
@@ -64,9 +61,9 @@ class FunktionaerExposedRepository : FunktionaerRepository {
.toList()
val ids = funktionaere.map { it[FunktionaerTable.id] }
val qualisMap = FunktionaerQualifikationTable
val qualisMap = (FunktionaerQualifikationTable innerJoin QualifikationMasterTable)
.selectAll().where { FunktionaerQualifikationTable.funktionaerId inList ids }
.groupBy({ it[FunktionaerQualifikationTable.funktionaerId] }) { it[FunktionaerQualifikationTable.qualifikation] }
.groupBy({ it[FunktionaerQualifikationTable.funktionaerId] }) { it[QualifikationMasterTable.code] }
funktionaere.map { row ->
rowToDomFunktionaer(row, qualisMap[row[FunktionaerTable.id]] ?: emptyList())
@@ -101,12 +98,25 @@ class FunktionaerExposedRepository : FunktionaerRepository {
}
}
// Qualifikationen synchronisieren
// Qualifikationen synchronisieren (über Master-Daten Auflösung)
FunktionaerQualifikationTable.deleteWhere { funktionaerId eq funktionaer.funktionaerId }
funktionaer.qualifikationen.forEach { quali ->
FunktionaerQualifikationTable.insert {
it[funktionaerId] = funktionaer.funktionaerId
it[qualifikation] = quali
val typ = if (funktionaer.istRichter()) "RICHTER" else "PARCOURSBAUER"
funktionaer.qualifikationen.forEach { code ->
val masterId = QualifikationMasterTable
.selectAll().where { (QualifikationMasterTable.code eq code) and (QualifikationMasterTable.typ eq typ) }
.map { it[QualifikationMasterTable.id] }
.singleOrNull()
if (masterId != null) {
FunktionaerQualifikationTable.insert {
it[funktionaerId] = funktionaer.funktionaerId
it[qualifikationId] = masterId
}
} else {
log.warn("Qualifikation '{}' für Typ '{}' nicht in Master-Daten gefunden. Überspringe Zuordnung für Funktionär {}.",
code, typ, funktionaer.name)
}
}
@@ -53,11 +53,27 @@ object FunktionaerTable : Table("funktionaer") {
}
/**
* Exposed-Tabellendefinition für die Qualifikationen eines Funktionärs.
* Exposed-Tabellendefinition für die Qualifikation-Master-Daten.
*/
object QualifikationMasterTable : Table("qualifikation_master") {
val id = uuid("qualifikation_id")
val code = varchar("code", 10) // z.B. "D", "S", "SPF", "P1"
val bezeichnung = varchar("bezeichnung", 100) // z.B. "Dressur", "Springpferde"
val typ = varchar("typ", 20) // "RICHTER" oder "PARCOURSBAUER"
override val primaryKey = PrimaryKey(id)
init {
index("idx_qualifikation_code_typ", isUnique = true, code, typ)
}
}
/**
* Exposed-Tabellendefinition für die Zuordnung von Qualifikationen zu Funktionären (Join-Tabelle).
*/
object FunktionaerQualifikationTable : Table("funktionaer_qualifikation") {
val funktionaerId = uuid("funktionaer_id").references(FunktionaerTable.id)
val qualifikation = varchar("qualifikation", 20)
val qualifikationId = uuid("qualifikation_id").references(QualifikationMasterTable.id)
override val primaryKey = PrimaryKey(funktionaerId, qualifikation)
override val primaryKey = PrimaryKey(funktionaerId, qualifikationId)
}