docs: Migrationsplan für Projekt-Restrukturierung hinzugefügt
- Detaillierter Plan zur Migration von alter zu neuer Modulstruktur - Umfasst Überführung von shared-kernel zu core-Modulen - Definiert Migration von Fachdomänen zu bounded contexts: * master-data → masterdata-Module * member-management → members-Module * horse-registry → horses-Module * event-management → events-Module - Beschreibt Verlagerung von api-gateway zu infrastructure/gateway - Strukturiert nach Domain-driven Design Prinzipien - Berücksichtigt Clean Architecture Layering (domain, application, infrastructure, api)
This commit is contained in:
+239
@@ -0,0 +1,239 @@
|
||||
package at.mocode.masterdata.infrastructure.persistence
|
||||
|
||||
import at.mocode.core.domain.model.SparteE
|
||||
import at.mocode.masterdata.domain.model.AltersklasseDefinition
|
||||
import at.mocode.masterdata.domain.repository.AltersklasseRepository
|
||||
import at.mocode.core.utils.database.DatabaseFactory
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toInstant
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
|
||||
/**
|
||||
* Implementierung des AltersklasseRepository für die Datenbankzugriffe.
|
||||
*
|
||||
* Diese Implementierung verwendet Exposed SQL für den Datenbankzugriff
|
||||
* und mappt zwischen der AltersklasseDefinition Domain-Entität und der AltersklasseTable.
|
||||
*/
|
||||
class AltersklasseRepositoryImpl : AltersklasseRepository {
|
||||
|
||||
/**
|
||||
* Konvertiert eine Datenbankzeile in ein Domain-Objekt.
|
||||
*/
|
||||
private fun rowToAltersklasseDefinition(row: ResultRow): AltersklasseDefinition {
|
||||
return AltersklasseDefinition(
|
||||
altersklasseId = row[AltersklasseTable.id],
|
||||
altersklasseCode = row[AltersklasseTable.altersklasseCode],
|
||||
bezeichnung = row[AltersklasseTable.bezeichnung],
|
||||
minAlter = row[AltersklasseTable.minAlter],
|
||||
maxAlter = row[AltersklasseTable.maxAlter],
|
||||
stichtagRegelText = row[AltersklasseTable.stichtagRegelText],
|
||||
sparteFilter = row[AltersklasseTable.sparteFilter]?.let { SparteE.valueOf(it) },
|
||||
geschlechtFilter = row[AltersklasseTable.geschlechtFilter],
|
||||
oetoRegelReferenzId = row[AltersklasseTable.oetoRegelReferenzId],
|
||||
istAktiv = row[AltersklasseTable.istAktiv],
|
||||
createdAt = row[AltersklasseTable.createdAt].toInstant(TimeZone.UTC),
|
||||
updatedAt = row[AltersklasseTable.updatedAt].toInstant(TimeZone.UTC)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun findById(id: Uuid): AltersklasseDefinition? = DatabaseFactory.dbQuery {
|
||||
AltersklasseTable.selectAll().where { AltersklasseTable.id eq id }
|
||||
.map(::rowToAltersklasseDefinition)
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByCode(altersklasseCode: String): AltersklasseDefinition? = DatabaseFactory.dbQuery {
|
||||
AltersklasseTable.selectAll().where { AltersklasseTable.altersklasseCode eq altersklasseCode }
|
||||
.map(::rowToAltersklasseDefinition)
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<AltersklasseDefinition> = DatabaseFactory.dbQuery {
|
||||
val pattern = "%$searchTerm%"
|
||||
AltersklasseTable.selectAll().where { AltersklasseTable.bezeichnung like pattern }
|
||||
.limit(limit)
|
||||
.map(::rowToAltersklasseDefinition)
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(sparteFilter: SparteE?, geschlechtFilter: Char?): List<AltersklasseDefinition> = DatabaseFactory.dbQuery {
|
||||
val query = AltersklasseTable.selectAll().where { AltersklasseTable.istAktiv eq true }
|
||||
|
||||
sparteFilter?.let { sparte ->
|
||||
query.andWhere {
|
||||
(AltersklasseTable.sparteFilter eq sparte.name) or (AltersklasseTable.sparteFilter.isNull())
|
||||
}
|
||||
}
|
||||
|
||||
geschlechtFilter?.let { geschlecht ->
|
||||
query.andWhere {
|
||||
(AltersklasseTable.geschlechtFilter eq geschlecht) or (AltersklasseTable.geschlechtFilter.isNull())
|
||||
}
|
||||
}
|
||||
|
||||
query.orderBy(AltersklasseTable.bezeichnung to SortOrder.ASC)
|
||||
.map(::rowToAltersklasseDefinition)
|
||||
}
|
||||
|
||||
override suspend fun findApplicableForAge(age: Int, sparteFilter: SparteE?, geschlechtFilter: Char?): List<AltersklasseDefinition> = DatabaseFactory.dbQuery {
|
||||
val query = AltersklasseTable.selectAll().where { AltersklasseTable.istAktiv eq true }
|
||||
|
||||
// Age range filter
|
||||
query.andWhere {
|
||||
(AltersklasseTable.minAlter.isNull() or (AltersklasseTable.minAlter lessEq age)) and
|
||||
(AltersklasseTable.maxAlter.isNull() or (AltersklasseTable.maxAlter greaterEq age))
|
||||
}
|
||||
|
||||
sparteFilter?.let { sparte ->
|
||||
query.andWhere {
|
||||
(AltersklasseTable.sparteFilter eq sparte.name) or (AltersklasseTable.sparteFilter.isNull())
|
||||
}
|
||||
}
|
||||
|
||||
geschlechtFilter?.let { geschlecht ->
|
||||
query.andWhere {
|
||||
(AltersklasseTable.geschlechtFilter eq geschlecht) or (AltersklasseTable.geschlechtFilter.isNull())
|
||||
}
|
||||
}
|
||||
|
||||
query.orderBy(AltersklasseTable.bezeichnung to SortOrder.ASC)
|
||||
.map(::rowToAltersklasseDefinition)
|
||||
}
|
||||
|
||||
override suspend fun findBySparte(sparte: SparteE, activeOnly: Boolean): List<AltersklasseDefinition> = DatabaseFactory.dbQuery {
|
||||
val query = AltersklasseTable.selectAll().where {
|
||||
(AltersklasseTable.sparteFilter eq sparte.name) or (AltersklasseTable.sparteFilter.isNull())
|
||||
}
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { AltersklasseTable.istAktiv eq true }
|
||||
}
|
||||
|
||||
query.orderBy(AltersklasseTable.bezeichnung to SortOrder.ASC)
|
||||
.map(::rowToAltersklasseDefinition)
|
||||
}
|
||||
|
||||
override suspend fun findByGeschlecht(geschlecht: Char, activeOnly: Boolean): List<AltersklasseDefinition> = DatabaseFactory.dbQuery {
|
||||
val query = AltersklasseTable.selectAll().where {
|
||||
(AltersklasseTable.geschlechtFilter eq geschlecht) or (AltersklasseTable.geschlechtFilter.isNull())
|
||||
}
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { AltersklasseTable.istAktiv eq true }
|
||||
}
|
||||
|
||||
query.orderBy(AltersklasseTable.bezeichnung to SortOrder.ASC)
|
||||
.map(::rowToAltersklasseDefinition)
|
||||
}
|
||||
|
||||
override suspend fun findByAgeRange(minAge: Int?, maxAge: Int?, activeOnly: Boolean): List<AltersklasseDefinition> = DatabaseFactory.dbQuery {
|
||||
val query = AltersklasseTable.selectAll()
|
||||
|
||||
minAge?.let { min ->
|
||||
query.andWhere {
|
||||
(AltersklasseTable.maxAlter.isNull()) or (AltersklasseTable.maxAlter greaterEq min)
|
||||
}
|
||||
}
|
||||
|
||||
maxAge?.let { max ->
|
||||
query.andWhere {
|
||||
(AltersklasseTable.minAlter.isNull()) or (AltersklasseTable.minAlter lessEq max)
|
||||
}
|
||||
}
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { AltersklasseTable.istAktiv eq true }
|
||||
}
|
||||
|
||||
query.orderBy(AltersklasseTable.bezeichnung to SortOrder.ASC)
|
||||
.map(::rowToAltersklasseDefinition)
|
||||
}
|
||||
|
||||
override suspend fun findByOetoRegelReferenz(oetoRegelReferenzId: Uuid): List<AltersklasseDefinition> = DatabaseFactory.dbQuery {
|
||||
AltersklasseTable.selectAll().where { AltersklasseTable.oetoRegelReferenzId eq oetoRegelReferenzId }
|
||||
.orderBy(AltersklasseTable.bezeichnung to SortOrder.ASC)
|
||||
.map(::rowToAltersklasseDefinition)
|
||||
}
|
||||
|
||||
override suspend fun save(altersklasse: AltersklasseDefinition): AltersklasseDefinition = DatabaseFactory.dbQuery {
|
||||
val now = Clock.System.now()
|
||||
val existingAltersklasse = AltersklasseTable.selectAll().where { AltersklasseTable.id eq altersklasse.altersklasseId }.singleOrNull()
|
||||
|
||||
if (existingAltersklasse == null) {
|
||||
// Insert a new age class
|
||||
AltersklasseTable.insert { stmt ->
|
||||
stmt[id] = altersklasse.altersklasseId
|
||||
stmt[altersklasseCode] = altersklasse.altersklasseCode
|
||||
stmt[bezeichnung] = altersklasse.bezeichnung
|
||||
stmt[minAlter] = altersklasse.minAlter
|
||||
stmt[maxAlter] = altersklasse.maxAlter
|
||||
stmt[stichtagRegelText] = altersklasse.stichtagRegelText
|
||||
stmt[sparteFilter] = altersklasse.sparteFilter?.name
|
||||
stmt[geschlechtFilter] = altersklasse.geschlechtFilter
|
||||
stmt[oetoRegelReferenzId] = altersklasse.oetoRegelReferenzId
|
||||
stmt[istAktiv] = altersklasse.istAktiv
|
||||
stmt[createdAt] = altersklasse.createdAt.toLocalDateTime(TimeZone.UTC)
|
||||
stmt[updatedAt] = now.toLocalDateTime(TimeZone.UTC)
|
||||
}
|
||||
} else {
|
||||
// Update existing age class
|
||||
AltersklasseTable.update({ AltersklasseTable.id eq altersklasse.altersklasseId }) { stmt ->
|
||||
stmt[altersklasseCode] = altersklasse.altersklasseCode
|
||||
stmt[bezeichnung] = altersklasse.bezeichnung
|
||||
stmt[minAlter] = altersklasse.minAlter
|
||||
stmt[maxAlter] = altersklasse.maxAlter
|
||||
stmt[stichtagRegelText] = altersklasse.stichtagRegelText
|
||||
stmt[sparteFilter] = altersklasse.sparteFilter?.name
|
||||
stmt[geschlechtFilter] = altersklasse.geschlechtFilter
|
||||
stmt[oetoRegelReferenzId] = altersklasse.oetoRegelReferenzId
|
||||
stmt[istAktiv] = altersklasse.istAktiv
|
||||
stmt[updatedAt] = now.toLocalDateTime(TimeZone.UTC)
|
||||
}
|
||||
}
|
||||
|
||||
altersklasse.copy(updatedAt = now)
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Uuid): Boolean = DatabaseFactory.dbQuery {
|
||||
AltersklasseTable.deleteWhere { AltersklasseTable.id eq id } > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByCode(altersklasseCode: String): Boolean = DatabaseFactory.dbQuery {
|
||||
AltersklasseTable.selectAll().where { AltersklasseTable.altersklasseCode eq altersklasseCode }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun countActive(sparteFilter: SparteE?): Long = DatabaseFactory.dbQuery {
|
||||
val query = AltersklasseTable.selectAll().where { AltersklasseTable.istAktiv eq true }
|
||||
|
||||
sparteFilter?.let { sparte ->
|
||||
query.andWhere {
|
||||
(AltersklasseTable.sparteFilter eq sparte.name) or (AltersklasseTable.sparteFilter.isNull())
|
||||
}
|
||||
}
|
||||
|
||||
query.count()
|
||||
}
|
||||
|
||||
override suspend fun isEligible(altersklasseId: Uuid, age: Int, geschlecht: Char): Boolean = DatabaseFactory.dbQuery {
|
||||
val altersklasse = AltersklasseTable.selectAll().where {
|
||||
(AltersklasseTable.id eq altersklasseId) and (AltersklasseTable.istAktiv eq true)
|
||||
}.singleOrNull()
|
||||
|
||||
if (altersklasse == null) return@dbQuery false
|
||||
|
||||
// Check age eligibility
|
||||
val minAlter = altersklasse[AltersklasseTable.minAlter]
|
||||
val maxAlter = altersklasse[AltersklasseTable.maxAlter]
|
||||
val ageEligible = (minAlter == null || age >= minAlter) && (maxAlter == null || age <= maxAlter)
|
||||
|
||||
// Check gender eligibility
|
||||
val geschlechtFilter = altersklasse[AltersklasseTable.geschlechtFilter]
|
||||
val genderEligible = geschlechtFilter == null || geschlechtFilter == geschlecht
|
||||
|
||||
ageEligible && genderEligible
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package at.mocode.masterdata.infrastructure.persistence
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.CurrentDateTime
|
||||
|
||||
/**
|
||||
* Exposed-Tabellendefinition für die Altersklasse-Entität (Altersklassendefinitionen).
|
||||
*
|
||||
* Diese Tabelle speichert alle Informationen zu Altersklassen für Teilnehmer
|
||||
* entsprechend der AltersklasseDefinition Domain-Entität.
|
||||
*/
|
||||
object AltersklasseTable : Table("altersklasse") {
|
||||
val id = uuid("id").autoGenerate()
|
||||
val altersklasseCode = varchar("altersklasse_code", 50).uniqueIndex()
|
||||
val bezeichnung = varchar("bezeichnung", 200)
|
||||
val minAlter = integer("min_alter").nullable()
|
||||
val maxAlter = integer("max_alter").nullable()
|
||||
val stichtagRegelText = varchar("stichtag_regel_text", 500).nullable()
|
||||
val sparteFilter = varchar("sparte_filter", 50).nullable() // Enum as string
|
||||
val geschlechtFilter = char("geschlecht_filter").nullable()
|
||||
val oetoRegelReferenzId = uuid("oeto_regel_referenz_id").nullable()
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val createdAt = datetime("created_at").defaultExpression(CurrentDateTime)
|
||||
val updatedAt = datetime("updated_at").defaultExpression(CurrentDateTime)
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
|
||||
init {
|
||||
// Index for performance on common queries
|
||||
index(customIndexName = "idx_altersklasse_aktiv", columns = arrayOf(istAktiv))
|
||||
index(customIndexName = "idx_altersklasse_sparte", columns = arrayOf(sparteFilter))
|
||||
index(customIndexName = "idx_altersklasse_geschlecht", columns = arrayOf(geschlechtFilter))
|
||||
index(customIndexName = "idx_altersklasse_alter", columns = arrayOf(minAlter, maxAlter))
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package at.mocode.masterdata.infrastructure.persistence
|
||||
|
||||
import at.mocode.masterdata.domain.model.BundeslandDefinition
|
||||
import at.mocode.masterdata.domain.repository.BundeslandRepository
|
||||
import at.mocode.core.utils.database.DatabaseFactory
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toInstant
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
|
||||
/**
|
||||
* Implementierung des BundeslandRepository für die Datenbankzugriffe.
|
||||
*
|
||||
* Diese Implementierung verwendet Exposed SQL für den Datenbankzugriff
|
||||
* und mappt zwischen der BundeslandDefinition Domain-Entität und der BundeslandTable.
|
||||
*/
|
||||
class BundeslandRepositoryImpl : BundeslandRepository {
|
||||
|
||||
/**
|
||||
* Konvertiert eine Datenbankzeile in ein Domain-Objekt.
|
||||
*/
|
||||
private fun rowToBundeslandDefinition(row: ResultRow): BundeslandDefinition {
|
||||
return BundeslandDefinition(
|
||||
bundeslandId = row[BundeslandTable.id],
|
||||
landId = row[BundeslandTable.landId],
|
||||
oepsCode = row[BundeslandTable.oepsCode],
|
||||
iso3166_2_Code = row[BundeslandTable.iso3166_2_Code],
|
||||
name = row[BundeslandTable.name],
|
||||
kuerzel = row[BundeslandTable.kuerzel],
|
||||
wappenUrl = row[BundeslandTable.wappenUrl],
|
||||
istAktiv = row[BundeslandTable.istAktiv],
|
||||
sortierReihenfolge = row[BundeslandTable.sortierReihenfolge],
|
||||
createdAt = row[BundeslandTable.createdAt].toInstant(TimeZone.UTC),
|
||||
updatedAt = row[BundeslandTable.updatedAt].toInstant(TimeZone.UTC)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun findById(id: Uuid): BundeslandDefinition? = DatabaseFactory.dbQuery {
|
||||
BundeslandTable.selectAll().where { BundeslandTable.id eq id }
|
||||
.map(::rowToBundeslandDefinition)
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByOepsCode(oepsCode: String, landId: Uuid): BundeslandDefinition? = DatabaseFactory.dbQuery {
|
||||
BundeslandTable.selectAll().where {
|
||||
(BundeslandTable.oepsCode eq oepsCode) and (BundeslandTable.landId eq landId)
|
||||
}
|
||||
.map(::rowToBundeslandDefinition)
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByIso3166_2_Code(iso3166_2_Code: String): BundeslandDefinition? = DatabaseFactory.dbQuery {
|
||||
BundeslandTable.selectAll().where { BundeslandTable.iso3166_2_Code eq iso3166_2_Code }
|
||||
.map(::rowToBundeslandDefinition)
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByCountry(landId: Uuid, activeOnly: Boolean, orderBySortierung: Boolean): List<BundeslandDefinition> = DatabaseFactory.dbQuery {
|
||||
val query = BundeslandTable.selectAll().where { BundeslandTable.landId eq landId }
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { BundeslandTable.istAktiv eq true }
|
||||
}
|
||||
|
||||
if (orderBySortierung) {
|
||||
query.orderBy(BundeslandTable.sortierReihenfolge to SortOrder.ASC, BundeslandTable.name to SortOrder.ASC)
|
||||
} else {
|
||||
query.orderBy(BundeslandTable.name to SortOrder.ASC)
|
||||
}
|
||||
|
||||
query.map(::rowToBundeslandDefinition)
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, landId: Uuid?, limit: Int): List<BundeslandDefinition> = DatabaseFactory.dbQuery {
|
||||
val pattern = "%$searchTerm%"
|
||||
val query = BundeslandTable.selectAll().where { BundeslandTable.name like pattern }
|
||||
|
||||
landId?.let {
|
||||
query.andWhere { BundeslandTable.landId eq it }
|
||||
}
|
||||
|
||||
query.limit(limit).map(::rowToBundeslandDefinition)
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(orderBySortierung: Boolean): List<BundeslandDefinition> = DatabaseFactory.dbQuery {
|
||||
val query = BundeslandTable.selectAll().where { BundeslandTable.istAktiv eq true }
|
||||
|
||||
if (orderBySortierung) {
|
||||
query.orderBy(BundeslandTable.sortierReihenfolge to SortOrder.ASC, BundeslandTable.name to SortOrder.ASC)
|
||||
} else {
|
||||
query.orderBy(BundeslandTable.name to SortOrder.ASC)
|
||||
}
|
||||
|
||||
query.map(::rowToBundeslandDefinition)
|
||||
}
|
||||
|
||||
override suspend fun save(bundesland: BundeslandDefinition): BundeslandDefinition = DatabaseFactory.dbQuery {
|
||||
val now = Clock.System.now()
|
||||
val existingBundesland = BundeslandTable.selectAll().where { BundeslandTable.id eq bundesland.bundeslandId }.singleOrNull()
|
||||
|
||||
if (existingBundesland == null) {
|
||||
// Insert a new federal state
|
||||
BundeslandTable.insert { stmt ->
|
||||
stmt[id] = bundesland.bundeslandId
|
||||
stmt[landId] = bundesland.landId
|
||||
stmt[oepsCode] = bundesland.oepsCode
|
||||
stmt[iso3166_2_Code] = bundesland.iso3166_2_Code
|
||||
stmt[name] = bundesland.name
|
||||
stmt[kuerzel] = bundesland.kuerzel
|
||||
stmt[wappenUrl] = bundesland.wappenUrl
|
||||
stmt[istAktiv] = bundesland.istAktiv
|
||||
stmt[sortierReihenfolge] = bundesland.sortierReihenfolge
|
||||
stmt[createdAt] = bundesland.createdAt.toLocalDateTime(TimeZone.UTC)
|
||||
stmt[updatedAt] = now.toLocalDateTime(TimeZone.UTC)
|
||||
}
|
||||
} else {
|
||||
// Update existing federal state
|
||||
BundeslandTable.update({ BundeslandTable.id eq bundesland.bundeslandId }) { stmt ->
|
||||
stmt[landId] = bundesland.landId
|
||||
stmt[oepsCode] = bundesland.oepsCode
|
||||
stmt[iso3166_2_Code] = bundesland.iso3166_2_Code
|
||||
stmt[name] = bundesland.name
|
||||
stmt[kuerzel] = bundesland.kuerzel
|
||||
stmt[wappenUrl] = bundesland.wappenUrl
|
||||
stmt[istAktiv] = bundesland.istAktiv
|
||||
stmt[sortierReihenfolge] = bundesland.sortierReihenfolge
|
||||
stmt[updatedAt] = now.toLocalDateTime(TimeZone.UTC)
|
||||
}
|
||||
}
|
||||
|
||||
bundesland.copy(updatedAt = now)
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Uuid): Boolean = DatabaseFactory.dbQuery {
|
||||
BundeslandTable.deleteWhere { BundeslandTable.id eq id } > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByOepsCode(oepsCode: String, landId: Uuid): Boolean = DatabaseFactory.dbQuery {
|
||||
BundeslandTable.selectAll().where {
|
||||
(BundeslandTable.oepsCode eq oepsCode) and (BundeslandTable.landId eq landId)
|
||||
}.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByIso3166_2_Code(iso3166_2_Code: String): Boolean = DatabaseFactory.dbQuery {
|
||||
BundeslandTable.selectAll().where { BundeslandTable.iso3166_2_Code eq iso3166_2_Code }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun countActiveByCountry(landId: Uuid): Long = DatabaseFactory.dbQuery {
|
||||
BundeslandTable.selectAll().where {
|
||||
(BundeslandTable.landId eq landId) and (BundeslandTable.istAktiv eq true)
|
||||
}.count()
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package at.mocode.masterdata.infrastructure.persistence
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.CurrentDateTime
|
||||
|
||||
/**
|
||||
* Exposed-Tabellendefinition für die Bundesland-Entität (Bundesländer/Regionen).
|
||||
*
|
||||
* Diese Tabelle speichert alle Informationen zu Bundesländern und subnationalen
|
||||
* Verwaltungseinheiten entsprechend der BundeslandDefinition Domain-Entität.
|
||||
*/
|
||||
object BundeslandTable : Table("bundesland") {
|
||||
val id = uuid("id").autoGenerate()
|
||||
val landId = uuid("land_id").references(LandTable.id)
|
||||
val oepsCode = varchar("oeps_code", 10).nullable()
|
||||
val iso3166_2_Code = varchar("iso_3166_2_code", 10).nullable()
|
||||
val name = varchar("name", 100)
|
||||
val kuerzel = varchar("kuerzel", 10).nullable()
|
||||
val wappenUrl = varchar("wappen_url", 500).nullable()
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val sortierReihenfolge = integer("sortier_reihenfolge").nullable()
|
||||
val createdAt = datetime("created_at").defaultExpression(CurrentDateTime)
|
||||
val updatedAt = datetime("updated_at").defaultExpression(CurrentDateTime)
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
|
||||
init {
|
||||
// Unique constraint for OEPS code per country
|
||||
uniqueIndex("uk_bundesland_oeps_land", oepsCode, landId)
|
||||
// Unique constraint for ISO 3166-2 code globally
|
||||
uniqueIndex("uk_bundesland_iso3166_2", iso3166_2_Code)
|
||||
}
|
||||
}
|
||||
+35
-23
@@ -14,6 +14,9 @@ import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
|
||||
/**
|
||||
* Implementierung des LandRepository für die Datenbankzugriffe.
|
||||
*
|
||||
* Diese Implementierung verwendet Exposed SQL für den Datenbankzugriff
|
||||
* und mappt zwischen der LandDefinition Domain-Entität und der LandTable.
|
||||
*/
|
||||
class LandRepositoryImpl : LandRepository {
|
||||
|
||||
@@ -25,14 +28,16 @@ class LandRepositoryImpl : LandRepository {
|
||||
landId = row[LandTable.id],
|
||||
isoAlpha2Code = row[LandTable.isoAlpha2Code],
|
||||
isoAlpha3Code = row[LandTable.isoAlpha3Code],
|
||||
nameDeutsch = row[LandTable.nameDe],
|
||||
nameEnglisch = row[LandTable.nameEn],
|
||||
isoNumerischerCode = row[LandTable.isoNumerischerCode],
|
||||
nameDeutsch = row[LandTable.nameDeutsch],
|
||||
nameEnglisch = row[LandTable.nameEnglisch],
|
||||
wappenUrl = row[LandTable.wappenUrl],
|
||||
istEuMitglied = row[LandTable.istEuMitglied],
|
||||
istEwrMitglied = row[LandTable.istEwrMitglied],
|
||||
sortierReihenfolge = row[LandTable.sortierReihenfolge],
|
||||
istAktiv = row[LandTable.istAktiv],
|
||||
createdAt = row[LandTable.erstelltAm].toInstant(TimeZone.UTC),
|
||||
updatedAt = row[LandTable.geaendertAm].toInstant(TimeZone.UTC)
|
||||
sortierReihenfolge = row[LandTable.sortierReihenfolge],
|
||||
createdAt = row[LandTable.createdAt].toInstant(TimeZone.UTC),
|
||||
updatedAt = row[LandTable.updatedAt].toInstant(TimeZone.UTC)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -56,7 +61,10 @@ class LandRepositoryImpl : LandRepository {
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<LandDefinition> = DatabaseFactory.dbQuery {
|
||||
val pattern = "%$searchTerm%"
|
||||
LandTable.selectAll().where { (LandTable.nameDe like pattern) or (LandTable.nameEn like pattern) }
|
||||
LandTable.selectAll().where {
|
||||
(LandTable.nameDeutsch like pattern) or
|
||||
(LandTable.nameEnglisch like pattern)
|
||||
}
|
||||
.limit(limit)
|
||||
.map(::rowToLandDefinition)
|
||||
}
|
||||
@@ -65,9 +73,9 @@ class LandRepositoryImpl : LandRepository {
|
||||
val query = LandTable.selectAll().where { LandTable.istAktiv eq true }
|
||||
|
||||
if (orderBySortierung) {
|
||||
query.orderBy(LandTable.sortierReihenfolge to SortOrder.ASC, LandTable.nameDe to SortOrder.ASC)
|
||||
query.orderBy(LandTable.sortierReihenfolge to SortOrder.ASC, LandTable.nameDeutsch to SortOrder.ASC)
|
||||
} else {
|
||||
query.orderBy(LandTable.nameDe to SortOrder.ASC)
|
||||
query.orderBy(LandTable.nameDeutsch to SortOrder.ASC)
|
||||
}
|
||||
|
||||
query.map(::rowToLandDefinition)
|
||||
@@ -75,13 +83,13 @@ class LandRepositoryImpl : LandRepository {
|
||||
|
||||
override suspend fun findEuMembers(): List<LandDefinition> = DatabaseFactory.dbQuery {
|
||||
LandTable.selectAll().where { (LandTable.istEuMitglied eq true) and (LandTable.istAktiv eq true) }
|
||||
.orderBy(LandTable.sortierReihenfolge to SortOrder.ASC, LandTable.nameDe to SortOrder.ASC)
|
||||
.orderBy(LandTable.sortierReihenfolge to SortOrder.ASC, LandTable.nameDeutsch to SortOrder.ASC)
|
||||
.map(::rowToLandDefinition)
|
||||
}
|
||||
|
||||
override suspend fun findEwrMembers(): List<LandDefinition> = DatabaseFactory.dbQuery {
|
||||
LandTable.selectAll().where { (LandTable.istEwrMitglied eq true) and (LandTable.istAktiv eq true) }
|
||||
.orderBy(LandTable.sortierReihenfolge to SortOrder.ASC, LandTable.nameDe to SortOrder.ASC)
|
||||
.orderBy(LandTable.sortierReihenfolge to SortOrder.ASC, LandTable.nameDeutsch to SortOrder.ASC)
|
||||
.map(::rowToLandDefinition)
|
||||
}
|
||||
|
||||
@@ -95,27 +103,31 @@ class LandRepositoryImpl : LandRepository {
|
||||
stmt[id] = land.landId
|
||||
stmt[isoAlpha2Code] = land.isoAlpha2Code
|
||||
stmt[isoAlpha3Code] = land.isoAlpha3Code
|
||||
stmt[nameDe] = land.nameDeutsch
|
||||
stmt[nameEn] = land.nameEnglisch ?: ""
|
||||
stmt[istEuMitglied] = land.istEuMitglied ?: false
|
||||
stmt[istEwrMitglied] = land.istEwrMitglied ?: false
|
||||
stmt[sortierReihenfolge] = land.sortierReihenfolge ?: 999
|
||||
stmt[isoNumerischerCode] = land.isoNumerischerCode
|
||||
stmt[nameDeutsch] = land.nameDeutsch
|
||||
stmt[nameEnglisch] = land.nameEnglisch
|
||||
stmt[wappenUrl] = land.wappenUrl
|
||||
stmt[istEuMitglied] = land.istEuMitglied
|
||||
stmt[istEwrMitglied] = land.istEwrMitglied
|
||||
stmt[istAktiv] = land.istAktiv
|
||||
stmt[erstelltAm] = land.createdAt.toLocalDateTime(TimeZone.UTC)
|
||||
stmt[geaendertAm] = now.toLocalDateTime(TimeZone.UTC)
|
||||
stmt[sortierReihenfolge] = land.sortierReihenfolge
|
||||
stmt[createdAt] = land.createdAt.toLocalDateTime(TimeZone.UTC)
|
||||
stmt[updatedAt] = now.toLocalDateTime(TimeZone.UTC)
|
||||
}
|
||||
} else {
|
||||
// Update existing country
|
||||
LandTable.update({ LandTable.id eq land.landId }) { stmt ->
|
||||
stmt[isoAlpha2Code] = land.isoAlpha2Code
|
||||
stmt[isoAlpha3Code] = land.isoAlpha3Code
|
||||
stmt[nameDe] = land.nameDeutsch
|
||||
stmt[nameEn] = land.nameEnglisch ?: ""
|
||||
stmt[istEuMitglied] = land.istEuMitglied ?: false
|
||||
stmt[istEwrMitglied] = land.istEwrMitglied ?: false
|
||||
stmt[sortierReihenfolge] = land.sortierReihenfolge ?: 999
|
||||
stmt[isoNumerischerCode] = land.isoNumerischerCode
|
||||
stmt[nameDeutsch] = land.nameDeutsch
|
||||
stmt[nameEnglisch] = land.nameEnglisch
|
||||
stmt[wappenUrl] = land.wappenUrl
|
||||
stmt[istEuMitglied] = land.istEuMitglied
|
||||
stmt[istEwrMitglied] = land.istEwrMitglied
|
||||
stmt[istAktiv] = land.istAktiv
|
||||
stmt[geaendertAm] = now.toLocalDateTime(TimeZone.UTC)
|
||||
stmt[sortierReihenfolge] = land.sortierReihenfolge
|
||||
stmt[updatedAt] = now.toLocalDateTime(TimeZone.UTC)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-7
@@ -6,19 +6,24 @@ import org.jetbrains.exposed.sql.kotlin.datetime.CurrentDateTime
|
||||
|
||||
/**
|
||||
* Exposed-Tabellendefinition für die Land-Entität (Länderstammdaten).
|
||||
*
|
||||
* Diese Tabelle speichert alle Informationen zu Ländern/Nationen entsprechend
|
||||
* der LandDefinition Domain-Entität.
|
||||
*/
|
||||
object LandTable : Table("land") {
|
||||
val id = uuid("id").autoGenerate()
|
||||
val isoAlpha2Code = varchar("iso_alpha2_code", 2).uniqueIndex()
|
||||
val isoAlpha3Code = varchar("iso_alpha3_code", 3).uniqueIndex()
|
||||
val nameDe = varchar("name_de", 100)
|
||||
val nameEn = varchar("name_en", 100)
|
||||
val istEuMitglied = bool("ist_eu_mitglied").default(false)
|
||||
val istEwrMitglied = bool("ist_ewr_mitglied").default(false)
|
||||
val sortierReihenfolge = integer("sortier_reihenfolge").default(999)
|
||||
val isoNumerischerCode = varchar("iso_numerischer_code", 3).nullable()
|
||||
val nameDeutsch = varchar("name_deutsch", 100)
|
||||
val nameEnglisch = varchar("name_englisch", 100).nullable()
|
||||
val wappenUrl = varchar("wappen_url", 500).nullable()
|
||||
val istEuMitglied = bool("ist_eu_mitglied").nullable()
|
||||
val istEwrMitglied = bool("ist_ewr_mitglied").nullable()
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val erstelltAm = datetime("erstellt_am").defaultExpression(CurrentDateTime)
|
||||
val geaendertAm = datetime("geaendert_am").defaultExpression(CurrentDateTime)
|
||||
val sortierReihenfolge = integer("sortier_reihenfolge").nullable()
|
||||
val createdAt = datetime("created_at").defaultExpression(CurrentDateTime)
|
||||
val updatedAt = datetime("updated_at").defaultExpression(CurrentDateTime)
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
}
|
||||
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
package at.mocode.masterdata.infrastructure.persistence
|
||||
|
||||
import at.mocode.core.domain.model.PlatzTypE
|
||||
import at.mocode.masterdata.domain.model.Platz
|
||||
import at.mocode.masterdata.domain.repository.PlatzRepository
|
||||
import at.mocode.core.utils.database.DatabaseFactory
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toInstant
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
|
||||
/**
|
||||
* Implementierung des PlatzRepository für die Datenbankzugriffe.
|
||||
*
|
||||
* Diese Implementierung verwendet Exposed SQL für den Datenbankzugriff
|
||||
* und mappt zwischen der Platz Domain-Entität und der PlatzTable.
|
||||
*/
|
||||
class PlatzRepositoryImpl : PlatzRepository {
|
||||
|
||||
/**
|
||||
* Konvertiert eine Datenbankzeile in ein Domain-Objekt.
|
||||
*/
|
||||
private fun rowToPlatz(row: ResultRow): Platz {
|
||||
return Platz(
|
||||
id = row[PlatzTable.id],
|
||||
turnierId = row[PlatzTable.turnierId],
|
||||
name = row[PlatzTable.name],
|
||||
dimension = row[PlatzTable.dimension],
|
||||
boden = row[PlatzTable.boden],
|
||||
typ = PlatzTypE.valueOf(row[PlatzTable.typ]),
|
||||
istAktiv = row[PlatzTable.istAktiv],
|
||||
sortierReihenfolge = row[PlatzTable.sortierReihenfolge],
|
||||
createdAt = row[PlatzTable.createdAt].toInstant(TimeZone.UTC),
|
||||
updatedAt = row[PlatzTable.updatedAt].toInstant(TimeZone.UTC)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun findById(id: Uuid): Platz? = DatabaseFactory.dbQuery {
|
||||
PlatzTable.selectAll().where { PlatzTable.id eq id }
|
||||
.map(::rowToPlatz)
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByTournament(turnierId: Uuid, activeOnly: Boolean, orderBySortierung: Boolean): List<Platz> = DatabaseFactory.dbQuery {
|
||||
val query = PlatzTable.selectAll().where { PlatzTable.turnierId eq turnierId }
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { PlatzTable.istAktiv eq true }
|
||||
}
|
||||
|
||||
if (orderBySortierung) {
|
||||
query.orderBy(PlatzTable.sortierReihenfolge to SortOrder.ASC, PlatzTable.name to SortOrder.ASC)
|
||||
} else {
|
||||
query.orderBy(PlatzTable.name to SortOrder.ASC)
|
||||
}
|
||||
|
||||
query.map(::rowToPlatz)
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, turnierId: Uuid?, limit: Int): List<Platz> = DatabaseFactory.dbQuery {
|
||||
val pattern = "%$searchTerm%"
|
||||
val query = PlatzTable.selectAll().where { PlatzTable.name like pattern }
|
||||
|
||||
turnierId?.let {
|
||||
query.andWhere { PlatzTable.turnierId eq it }
|
||||
}
|
||||
|
||||
query.limit(limit)
|
||||
.orderBy(PlatzTable.name to SortOrder.ASC)
|
||||
.map(::rowToPlatz)
|
||||
}
|
||||
|
||||
override suspend fun findByType(typ: PlatzTypE, turnierId: Uuid?, activeOnly: Boolean): List<Platz> = DatabaseFactory.dbQuery {
|
||||
val query = PlatzTable.selectAll().where { PlatzTable.typ eq typ.name }
|
||||
|
||||
turnierId?.let {
|
||||
query.andWhere { PlatzTable.turnierId eq it }
|
||||
}
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { PlatzTable.istAktiv eq true }
|
||||
}
|
||||
|
||||
query.orderBy(PlatzTable.name to SortOrder.ASC)
|
||||
.map(::rowToPlatz)
|
||||
}
|
||||
|
||||
override suspend fun findByGroundType(boden: String, turnierId: Uuid?, activeOnly: Boolean): List<Platz> = DatabaseFactory.dbQuery {
|
||||
val query = PlatzTable.selectAll().where { PlatzTable.boden eq boden }
|
||||
|
||||
turnierId?.let {
|
||||
query.andWhere { PlatzTable.turnierId eq it }
|
||||
}
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { PlatzTable.istAktiv eq true }
|
||||
}
|
||||
|
||||
query.orderBy(PlatzTable.name to SortOrder.ASC)
|
||||
.map(::rowToPlatz)
|
||||
}
|
||||
|
||||
override suspend fun findByDimensions(dimension: String, turnierId: Uuid?, activeOnly: Boolean): List<Platz> = DatabaseFactory.dbQuery {
|
||||
val query = PlatzTable.selectAll().where { PlatzTable.dimension eq dimension }
|
||||
|
||||
turnierId?.let {
|
||||
query.andWhere { PlatzTable.turnierId eq it }
|
||||
}
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { PlatzTable.istAktiv eq true }
|
||||
}
|
||||
|
||||
query.orderBy(PlatzTable.name to SortOrder.ASC)
|
||||
.map(::rowToPlatz)
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(orderBySortierung: Boolean): List<Platz> = DatabaseFactory.dbQuery {
|
||||
val query = PlatzTable.selectAll().where { PlatzTable.istAktiv eq true }
|
||||
|
||||
if (orderBySortierung) {
|
||||
query.orderBy(PlatzTable.sortierReihenfolge to SortOrder.ASC, PlatzTable.name to SortOrder.ASC)
|
||||
} else {
|
||||
query.orderBy(PlatzTable.name to SortOrder.ASC)
|
||||
}
|
||||
|
||||
query.map(::rowToPlatz)
|
||||
}
|
||||
|
||||
override suspend fun findSuitableForDiscipline(
|
||||
requiredType: PlatzTypE,
|
||||
requiredDimensions: String?,
|
||||
turnierId: Uuid?
|
||||
): List<Platz> = DatabaseFactory.dbQuery {
|
||||
val query = PlatzTable.selectAll().where {
|
||||
(PlatzTable.typ eq requiredType.name) and (PlatzTable.istAktiv eq true)
|
||||
}
|
||||
|
||||
requiredDimensions?.let { dimensions ->
|
||||
query.andWhere { PlatzTable.dimension eq dimensions }
|
||||
}
|
||||
|
||||
turnierId?.let {
|
||||
query.andWhere { PlatzTable.turnierId eq it }
|
||||
}
|
||||
|
||||
query.orderBy(PlatzTable.sortierReihenfolge to SortOrder.ASC, PlatzTable.name to SortOrder.ASC)
|
||||
.map(::rowToPlatz)
|
||||
}
|
||||
|
||||
override suspend fun save(platz: Platz): Platz = DatabaseFactory.dbQuery {
|
||||
val now = Clock.System.now()
|
||||
val existingPlatz = PlatzTable.selectAll().where { PlatzTable.id eq platz.id }.singleOrNull()
|
||||
|
||||
if (existingPlatz == null) {
|
||||
// Insert a new venue
|
||||
PlatzTable.insert { stmt ->
|
||||
stmt[id] = platz.id
|
||||
stmt[turnierId] = platz.turnierId
|
||||
stmt[name] = platz.name
|
||||
stmt[dimension] = platz.dimension
|
||||
stmt[boden] = platz.boden
|
||||
stmt[typ] = platz.typ.name
|
||||
stmt[istAktiv] = platz.istAktiv
|
||||
stmt[sortierReihenfolge] = platz.sortierReihenfolge
|
||||
stmt[createdAt] = platz.createdAt.toLocalDateTime(TimeZone.UTC)
|
||||
stmt[updatedAt] = now.toLocalDateTime(TimeZone.UTC)
|
||||
}
|
||||
} else {
|
||||
// Update existing venue
|
||||
PlatzTable.update({ PlatzTable.id eq platz.id }) { stmt ->
|
||||
stmt[turnierId] = platz.turnierId
|
||||
stmt[name] = platz.name
|
||||
stmt[dimension] = platz.dimension
|
||||
stmt[boden] = platz.boden
|
||||
stmt[typ] = platz.typ.name
|
||||
stmt[istAktiv] = platz.istAktiv
|
||||
stmt[sortierReihenfolge] = platz.sortierReihenfolge
|
||||
stmt[updatedAt] = now.toLocalDateTime(TimeZone.UTC)
|
||||
}
|
||||
}
|
||||
|
||||
platz.copy(updatedAt = now)
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Uuid): Boolean = DatabaseFactory.dbQuery {
|
||||
PlatzTable.deleteWhere { PlatzTable.id eq id } > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByNameAndTournament(name: String, turnierId: Uuid): Boolean = DatabaseFactory.dbQuery {
|
||||
PlatzTable.selectAll().where {
|
||||
(PlatzTable.name eq name) and (PlatzTable.turnierId eq turnierId)
|
||||
}.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun countActiveByTournament(turnierId: Uuid): Long = DatabaseFactory.dbQuery {
|
||||
PlatzTable.selectAll().where {
|
||||
(PlatzTable.turnierId eq turnierId) and (PlatzTable.istAktiv eq true)
|
||||
}.count()
|
||||
}
|
||||
|
||||
override suspend fun countByTypeAndTournament(typ: PlatzTypE, turnierId: Uuid, activeOnly: Boolean): Long = DatabaseFactory.dbQuery {
|
||||
val query = PlatzTable.selectAll().where {
|
||||
(PlatzTable.typ eq typ.name) and (PlatzTable.turnierId eq turnierId)
|
||||
}
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { PlatzTable.istAktiv eq true }
|
||||
}
|
||||
|
||||
query.count()
|
||||
}
|
||||
|
||||
override suspend fun findAvailableForTimeSlot(turnierId: Uuid, startTime: String?, endTime: String?): List<Platz> = DatabaseFactory.dbQuery {
|
||||
// For now, this returns all active venues for the tournament
|
||||
// This can be extended when venue scheduling functionality is implemented
|
||||
val query = PlatzTable.selectAll().where {
|
||||
(PlatzTable.turnierId eq turnierId) and (PlatzTable.istAktiv eq true)
|
||||
}
|
||||
|
||||
// TODO: Add time slot availability logic when scheduling is implemented
|
||||
// This would involve joining with a scheduling/booking table to check availability
|
||||
|
||||
query.orderBy(PlatzTable.sortierReihenfolge to SortOrder.ASC, PlatzTable.name to SortOrder.ASC)
|
||||
.map(::rowToPlatz)
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package at.mocode.masterdata.infrastructure.persistence
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.CurrentDateTime
|
||||
|
||||
/**
|
||||
* Exposed-Tabellendefinition für die Platz-Entität (Turnierplätze/Wettkampfstätten).
|
||||
*
|
||||
* Diese Tabelle speichert alle Informationen zu Plätzen und Arenen
|
||||
* entsprechend der Platz Domain-Entität.
|
||||
*/
|
||||
object PlatzTable : Table("platz") {
|
||||
val id = uuid("id").autoGenerate()
|
||||
val turnierId = uuid("turnier_id") // Foreign key to tournament (not enforced here as tournament might be in different module)
|
||||
val name = varchar("name", 200)
|
||||
val dimension = varchar("dimension", 50).nullable()
|
||||
val boden = varchar("boden", 100).nullable()
|
||||
val typ = varchar("typ", 50) // Enum as string
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val sortierReihenfolge = integer("sortier_reihenfolge").nullable()
|
||||
val createdAt = datetime("created_at").defaultExpression(CurrentDateTime)
|
||||
val updatedAt = datetime("updated_at").defaultExpression(CurrentDateTime)
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
|
||||
init {
|
||||
// Index for performance on common queries
|
||||
index(customIndexName = "idx_platz_turnier", columns = arrayOf(turnierId))
|
||||
index(customIndexName = "idx_platz_aktiv", columns = arrayOf(istAktiv))
|
||||
index(customIndexName = "idx_platz_typ", columns = arrayOf(typ))
|
||||
index(customIndexName = "idx_platz_turnier_aktiv", columns = arrayOf(turnierId, istAktiv))
|
||||
|
||||
// Unique constraint for name per tournament
|
||||
uniqueIndex("uk_platz_name_turnier", name, turnierId)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user