Compare commits
2 Commits
fd5e37c4d5
...
c53daa926a
| Author | SHA1 | Date | |
|---|---|---|---|
| c53daa926a | |||
| 7b35831d8c |
26
backend/services/clubs/clubs-domain/build.gradle.kts
Normal file
26
backend/services/clubs/clubs-domain/build.gradle.kts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm()
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
kotlin.srcDir("src/main/kotlin")
|
||||
dependencies {
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
implementation(libs.kotlinx.datetime)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
kotlin.srcDir("src/test/kotlin")
|
||||
dependencies {
|
||||
implementation(projects.platform.platformTesting)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
package at.mocode.clubs.domain.model
|
||||
|
||||
import at.mocode.core.domain.model.DatenQuelleE
|
||||
import at.mocode.core.domain.serialization.KotlinInstantSerializer
|
||||
import at.mocode.core.domain.serialization.UuidSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Domain model representing a club (Verein) in the registry system.
|
||||
*
|
||||
* @property clubId Unique internal identifier (UUID).
|
||||
* @property vereinsNummer ÖPS club number (from ZNS VEREIN01.dat).
|
||||
* @property name Club name.
|
||||
* @property datenQuelle Source of the data.
|
||||
* @property createdAt Timestamp when this record was created.
|
||||
* @property updatedAt Timestamp when this record was last updated.
|
||||
*/
|
||||
@Serializable
|
||||
data class DomClub(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val clubId: Uuid = Uuid.random(),
|
||||
|
||||
val vereinsNummer: String,
|
||||
val name: String,
|
||||
|
||||
val istAktiv: Boolean = true,
|
||||
val datenQuelle: DatenQuelleE = DatenQuelleE.IMPORT_ZNS,
|
||||
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
val createdAt: Instant = Clock.System.now(),
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
var updatedAt: Instant = Clock.System.now()
|
||||
)
|
||||
23
backend/services/clubs/clubs-infrastructure/build.gradle.kts
Normal file
23
backend/services/clubs/clubs-infrastructure/build.gradle.kts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
plugins {
|
||||
alias(libs.plugins.kotlinJvm)
|
||||
alias(libs.plugins.kotlinSpring)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.platform.platformDependencies)
|
||||
implementation(projects.clubs.clubsDomain)
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
|
||||
implementation(libs.spring.boot.starter.web)
|
||||
|
||||
implementation(libs.exposed.core)
|
||||
implementation(libs.exposed.dao)
|
||||
implementation(libs.exposed.jdbc)
|
||||
implementation(libs.exposed.kotlin.datetime)
|
||||
|
||||
runtimeOnly(libs.postgresql.driver)
|
||||
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package at.mocode.clubs.infrastructure.persistence
|
||||
|
||||
import org.jetbrains.exposed.v1.core.dao.id.java.UUIDTable
|
||||
import org.jetbrains.exposed.v1.datetime.timestamp
|
||||
|
||||
/**
|
||||
* Tabelle für importierte Vereine aus dem ZNS-Datenbestand (VEREIN01.dat).
|
||||
* Wird ausschließlich als Dev-Seed-Daten verwendet.
|
||||
*/
|
||||
object ZnsClubTable : UUIDTable("zns_clubs") {
|
||||
val vereinsNummer = varchar("vereins_nummer", 4).uniqueIndex()
|
||||
val name = varchar("name", 50)
|
||||
val createdAt = timestamp("created_at")
|
||||
}
|
||||
38
backend/services/clubs/clubs-service/build.gradle.kts
Normal file
38
backend/services/clubs/clubs-service/build.gradle.kts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
plugins {
|
||||
alias(libs.plugins.kotlinJvm)
|
||||
alias(libs.plugins.kotlinSpring)
|
||||
alias(libs.plugins.spring.boot)
|
||||
alias(libs.plugins.spring.dependencyManagement)
|
||||
}
|
||||
|
||||
springBoot {
|
||||
mainClass.set("at.mocode.clubs.service.ClubsServiceApplicationKt")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.platform.platformDependencies)
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
implementation(projects.clubs.clubsDomain)
|
||||
implementation(projects.clubs.clubsInfrastructure)
|
||||
|
||||
implementation(libs.spring.boot.starter.web)
|
||||
implementation(libs.spring.boot.starter.validation)
|
||||
implementation(libs.spring.boot.starter.actuator)
|
||||
|
||||
implementation(libs.exposed.core)
|
||||
implementation(libs.exposed.dao)
|
||||
implementation(libs.exposed.jdbc)
|
||||
implementation(libs.exposed.kotlin.datetime)
|
||||
implementation(libs.hikari.cp)
|
||||
runtimeOnly(libs.postgresql.driver)
|
||||
testRuntimeOnly(libs.h2.driver)
|
||||
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
testImplementation(libs.spring.boot.starter.test)
|
||||
testImplementation(libs.logback.classic)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package at.mocode.clubs.service
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
|
||||
@SpringBootApplication
|
||||
@ComponentScan(
|
||||
basePackages = [
|
||||
"at.mocode.clubs.service",
|
||||
"at.mocode.clubs.infrastructure"
|
||||
]
|
||||
)
|
||||
class ClubsServiceApplication
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
runApplication<ClubsServiceApplication>(*args)
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package at.mocode.clubs.service.config
|
||||
|
||||
import at.mocode.clubs.infrastructure.persistence.ZnsClubTable
|
||||
import jakarta.annotation.PostConstruct
|
||||
import org.jetbrains.exposed.v1.jdbc.Database
|
||||
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Profile
|
||||
|
||||
@Configuration
|
||||
@Profile("dev")
|
||||
class ClubsDatabaseConfiguration(
|
||||
@Value("\${spring.datasource.url}") private val jdbcUrl: String,
|
||||
@Value("\${spring.datasource.username}") private val username: String,
|
||||
@Value("\${spring.datasource.password}") private val password: String
|
||||
) {
|
||||
private val log = LoggerFactory.getLogger(ClubsDatabaseConfiguration::class.java)
|
||||
|
||||
@PostConstruct
|
||||
fun initializeDatabase() {
|
||||
log.info("Initialisiere Datenbank-Schema für Clubs-Service...")
|
||||
Database.connect(jdbcUrl, user = username, password = password)
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(ZnsClubTable)
|
||||
log.info("Datenbank-Schema erfolgreich initialisiert")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package at.mocode.clubs.service.dev
|
||||
|
||||
import at.mocode.clubs.infrastructure.persistence.ZnsClubTable
|
||||
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
|
||||
import org.jetbrains.exposed.v1.jdbc.batchInsert
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.boot.CommandLineRunner
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.stereotype.Component
|
||||
import java.io.File
|
||||
import kotlin.time.Clock
|
||||
|
||||
/**
|
||||
* Dev-only Seeder: Importiert VEREIN01.dat (ZNS) → zns_clubs.
|
||||
* Aktivierung: Umgebungsvariable ZNS_DATA_DIR setzen + Profil "dev".
|
||||
*/
|
||||
@Component
|
||||
@Profile("dev")
|
||||
class ZnsClubSeeder(
|
||||
@Value("\${zns.data.dir:#{null}}") private val znsDataDir: String?
|
||||
) : CommandLineRunner {
|
||||
|
||||
private val log = LoggerFactory.getLogger(ZnsClubSeeder::class.java)
|
||||
|
||||
override fun run(vararg args: String) {
|
||||
if (znsDataDir == null) {
|
||||
log.info("ZNS_DATA_DIR nicht gesetzt – ZnsClubSeeder wird übersprungen.")
|
||||
return
|
||||
}
|
||||
val dir = File(znsDataDir)
|
||||
if (!dir.exists() || !dir.isDirectory) {
|
||||
log.warn("ZNS_DATA_DIR '{}' existiert nicht oder ist kein Verzeichnis.", znsDataDir)
|
||||
return
|
||||
}
|
||||
log.info("Starte ZNS-Vereine-Import aus: {}", dir.absolutePath)
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(ZnsClubTable)
|
||||
}
|
||||
seedClubs(dir)
|
||||
log.info("ZNS-Vereine-Import abgeschlossen.")
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// VEREIN01.dat → zns_clubs
|
||||
// Format:
|
||||
// [0-3] VereinsNr (4 Zeichen)
|
||||
// [4-53] Name (50 Zeichen)
|
||||
// -------------------------------------------------------------------------
|
||||
private fun seedClubs(dir: File) {
|
||||
val file = File(dir, "VEREIN01.dat")
|
||||
if (!file.exists()) {
|
||||
log.warn("VEREIN01.dat nicht gefunden – Vereine werden übersprungen.")
|
||||
return
|
||||
}
|
||||
|
||||
data class ClubRow(val nr: String, val name: String)
|
||||
|
||||
val rows = file.readLines(Charsets.ISO_8859_1)
|
||||
.filter { it.length >= 4 }
|
||||
.map { line ->
|
||||
ClubRow(
|
||||
nr = line.substring(0, 4).trim(),
|
||||
name = line.substring(4).trim().take(50)
|
||||
)
|
||||
}
|
||||
.filter { it.nr.isNotBlank() }
|
||||
|
||||
val now = Clock.System.now()
|
||||
transaction {
|
||||
ZnsClubTable.batchInsert(rows, ignore = true) { row ->
|
||||
this[ZnsClubTable.vereinsNummer] = row.nr
|
||||
this[ZnsClubTable.name] = row.name
|
||||
this[ZnsClubTable.createdAt] = now
|
||||
}
|
||||
}
|
||||
log.info("Vereine importiert: {} Datensätze", rows.size)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
plugins {
|
||||
kotlin("jvm")
|
||||
alias(libs.plugins.kotlinJvm)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,26 @@
|
|||
plugins {
|
||||
kotlin("jvm")
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
kotlin {
|
||||
jvm()
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
kotlin.srcDir("src/main/kotlin")
|
||||
dependencies {
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
implementation(libs.kotlinx.datetime)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
kotlin.srcDir("src/test/kotlin")
|
||||
dependencies {
|
||||
implementation(projects.platform.platformTesting)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
package at.mocode.horses.domain.model
|
||||
|
||||
import at.mocode.core.domain.model.PferdeGeschlechtE
|
||||
import at.mocode.core.domain.model.DatenQuelleE
|
||||
import at.mocode.core.domain.model.PferdeGeschlechtE
|
||||
import at.mocode.core.domain.serialization.KotlinInstantSerializer
|
||||
import at.mocode.core.domain.serialization.UuidSerializer
|
||||
import kotlin.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.todayIn
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Domain model representing a horse in the registry system.
|
||||
|
|
@ -129,7 +129,7 @@ data class DomPferd(
|
|||
*/
|
||||
fun getAge(): Int? {
|
||||
return geburtsdatum?.let { birthDate ->
|
||||
val today = kotlinx.datetime.Clock.System.todayIn(kotlinx.datetime.TimeZone.currentSystemDefault())
|
||||
val today = kotlin.time.Clock.System.todayIn(kotlinx.datetime.TimeZone.currentSystemDefault())
|
||||
var age = today.year - birthDate.year
|
||||
|
||||
// Check if birthday has occurred this year
|
||||
|
|
@ -167,6 +167,6 @@ data class DomPferd(
|
|||
* Creates a copy of this horse with updated timestamp.
|
||||
*/
|
||||
fun withUpdatedTimestamp(): DomPferd {
|
||||
return this.copy(updatedAt = Clock.System.now())
|
||||
return this.copy(updatedAt = kotlin.time.Clock.System.now())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
plugins {
|
||||
// KORREKTUR: Alle Plugins werden jetzt konsistent über den Version Catalog geladen.
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
alias(libs.plugins.kotlin.spring)
|
||||
// Das JPA-Plugin wird jetzt ebenfalls zentral verwaltet.
|
||||
alias(libs.plugins.kotlin.jpa)
|
||||
alias(libs.plugins.kotlinJvm)
|
||||
alias(libs.plugins.kotlinSpring)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Interne Module
|
||||
implementation(projects.platform.platformDependencies)
|
||||
implementation(projects.horses.horsesDomain)
|
||||
implementation(projects.horses.horsesApplication)
|
||||
// horses-common: ON HOLD
|
||||
// implementation(projects.horses.horsesCommon)
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
implementation(projects.infrastructure.cache.cacheApi)
|
||||
implementation(projects.infrastructure.eventStore.eventStoreApi)
|
||||
implementation(projects.infrastructure.messaging.messagingClient)
|
||||
|
||||
// KORREKTUR: Alle externen Abhängigkeiten werden jetzt über den Version Catalog bezogen.
|
||||
// Spring
|
||||
implementation(libs.spring.boot.starter.web)
|
||||
|
||||
// Spring Data JPA
|
||||
implementation(libs.spring.boot.starter.data.jpa)
|
||||
// Datenbank via Exposed
|
||||
implementation(libs.exposed.core)
|
||||
implementation(libs.exposed.dao)
|
||||
implementation(libs.exposed.jdbc)
|
||||
implementation(libs.exposed.kotlin.datetime)
|
||||
|
||||
// Datenbank-Treiber
|
||||
runtimeOnly(libs.postgresql.driver)
|
||||
|
|
|
|||
|
|
@ -1,336 +0,0 @@
|
|||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
package at.mocode.horses.infrastructure.persistence
|
||||
|
||||
import at.mocode.core.domain.model.PferdeGeschlechtE
|
||||
import at.mocode.horses.domain.model.DomPferd
|
||||
import at.mocode.horses.domain.repository.HorseRepository
|
||||
import at.mocode.core.utils.database.DatabaseFactory
|
||||
import kotlin.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
/**
|
||||
* PostgreSQL implementation of the HorseRepository using Exposed ORM.
|
||||
*
|
||||
* This implementation provides database operations for horse entities,
|
||||
* mapping between the domain model (DomPferd) and the database table (HorseTable).
|
||||
*/
|
||||
@Repository
|
||||
class HorseRepositoryImpl : HorseRepository {
|
||||
|
||||
override suspend fun findById(id: Uuid): DomPferd? = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.id eq id }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByLebensnummer(lebensnummer: String): DomPferd? = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.lebensnummer eq lebensnummer }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByChipNummer(chipNummer: String): DomPferd? = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.chipNummer eq chipNummer }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByPassNummer(passNummer: String): DomPferd? = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.passNummer eq passNummer }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByOepsNummer(oepsNummer: String): DomPferd? = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.oepsNummer eq oepsNummer }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByFeiNummer(feiNummer: String): DomPferd? = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.feiNummer eq feiNummer }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<DomPferd> = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.pferdeName like "%$searchTerm%" }
|
||||
.orderBy(HorseTable.pferdeName to SortOrder.ASC)
|
||||
.limit(limit)
|
||||
.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByOwnerId(ownerId: Uuid, activeOnly: Boolean): List<DomPferd> = DatabaseFactory.dbQuery {
|
||||
val query = HorseTable.selectAll().where { HorseTable.besitzerId eq ownerId }
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { HorseTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
}.orderBy(HorseTable.pferdeName to SortOrder.ASC)
|
||||
.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByResponsiblePersonId(responsiblePersonId: Uuid, activeOnly: Boolean): List<DomPferd> = DatabaseFactory.dbQuery {
|
||||
val query = HorseTable.selectAll().where { HorseTable.verantwortlichePersonId eq responsiblePersonId }
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { HorseTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
}.orderBy(HorseTable.pferdeName to SortOrder.ASC)
|
||||
.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByGeschlecht(geschlecht: PferdeGeschlechtE, activeOnly: Boolean, limit: Int): List<DomPferd> = DatabaseFactory.dbQuery {
|
||||
val query = HorseTable.selectAll().where { HorseTable.geschlecht eq geschlecht }
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { HorseTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
}.orderBy(HorseTable.pferdeName to SortOrder.ASC)
|
||||
.limit(limit)
|
||||
.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByRasse(rasse: String, activeOnly: Boolean, limit: Int): List<DomPferd> = DatabaseFactory.dbQuery {
|
||||
val query = HorseTable.selectAll().where { HorseTable.rasse eq rasse }
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { HorseTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
}.orderBy(HorseTable.pferdeName to SortOrder.ASC)
|
||||
.limit(limit)
|
||||
.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByBirthYear(birthYear: Int, activeOnly: Boolean): List<DomPferd> = DatabaseFactory.dbQuery {
|
||||
val query = HorseTable.selectAll().where {
|
||||
HorseTable.geburtsdatum.isNotNull() and
|
||||
(CustomFunction(
|
||||
"EXTRACT",
|
||||
IntegerColumnType(),
|
||||
stringLiteral("YEAR FROM "),
|
||||
HorseTable.geburtsdatum
|
||||
) eq birthYear)
|
||||
}
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { HorseTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
}.orderBy(HorseTable.pferdeName to SortOrder.ASC)
|
||||
.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByBirthYearRange(fromYear: Int, toYear: Int, activeOnly: Boolean): List<DomPferd> = DatabaseFactory.dbQuery {
|
||||
val query = HorseTable.selectAll().where {
|
||||
HorseTable.geburtsdatum.isNotNull() and
|
||||
(CustomFunction(
|
||||
"EXTRACT",
|
||||
IntegerColumnType(),
|
||||
stringLiteral("YEAR FROM "),
|
||||
HorseTable.geburtsdatum
|
||||
) greaterEq fromYear) and
|
||||
(CustomFunction(
|
||||
"EXTRACT",
|
||||
IntegerColumnType(),
|
||||
stringLiteral("YEAR FROM "),
|
||||
HorseTable.geburtsdatum
|
||||
) lessEq toYear)
|
||||
}
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { HorseTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
}.orderBy(HorseTable.geburtsdatum, SortOrder.DESC)
|
||||
.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(limit: Int): List<DomPferd> = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.istAktiv eq true }
|
||||
.orderBy(HorseTable.pferdeName to SortOrder.ASC)
|
||||
.limit(limit)
|
||||
.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findOepsRegistered(activeOnly: Boolean): List<DomPferd> = DatabaseFactory.dbQuery {
|
||||
val query = HorseTable.selectAll().where { HorseTable.oepsNummer.isNotNull() }
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { HorseTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
}.orderBy(HorseTable.pferdeName to SortOrder.ASC)
|
||||
.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findFeiRegistered(activeOnly: Boolean): List<DomPferd> = DatabaseFactory.dbQuery {
|
||||
val query = HorseTable.selectAll().where { HorseTable.feiNummer.isNotNull() }
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { HorseTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
}.orderBy(HorseTable.pferdeName to SortOrder.ASC)
|
||||
.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun save(horse: DomPferd): DomPferd = DatabaseFactory.dbQuery {
|
||||
val now = Clock.System.now()
|
||||
val existingHorse = findById(horse.pferdId)
|
||||
|
||||
if (existingHorse != null) {
|
||||
// Update existing horse
|
||||
val updatedHorse = horse.copy(updatedAt = now)
|
||||
HorseTable.update({ HorseTable.id eq horse.pferdId }) {
|
||||
domPferdToStatement(it, updatedHorse)
|
||||
}
|
||||
updatedHorse
|
||||
} else {
|
||||
// Insert a new horse
|
||||
HorseTable.insert {
|
||||
it[id] = horse.pferdId
|
||||
domPferdToStatement(it, horse.copy(updatedAt = now))
|
||||
}
|
||||
horse.copy(updatedAt = now)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Uuid): Boolean = DatabaseFactory.dbQuery {
|
||||
val deletedRows = HorseTable.deleteWhere { HorseTable.id eq id }
|
||||
deletedRows > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByLebensnummer(lebensnummer: String): Boolean = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.lebensnummer eq lebensnummer }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByChipNummer(chipNummer: String): Boolean = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.chipNummer eq chipNummer }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByPassNummer(passNummer: String): Boolean = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.passNummer eq passNummer }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByOepsNummer(oepsNummer: String): Boolean = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.oepsNummer eq oepsNummer }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByFeiNummer(feiNummer: String): Boolean = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.feiNummer eq feiNummer }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun countActive(): Long = DatabaseFactory.dbQuery {
|
||||
HorseTable.selectAll().where { HorseTable.istAktiv eq true }
|
||||
.count()
|
||||
}
|
||||
|
||||
override suspend fun countByOwnerId(ownerId: Uuid, activeOnly: Boolean): Long = DatabaseFactory.dbQuery {
|
||||
val query = HorseTable.selectAll().where { HorseTable.besitzerId eq ownerId }
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { HorseTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
}.count()
|
||||
}
|
||||
|
||||
override suspend fun countOepsRegistered(activeOnly: Boolean): Long = DatabaseFactory.dbQuery {
|
||||
val query = HorseTable.selectAll().where {
|
||||
HorseTable.oepsNummer.isNotNull() and (HorseTable.oepsNummer neq "")
|
||||
}
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { HorseTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
}.count()
|
||||
}
|
||||
|
||||
override suspend fun countFeiRegistered(activeOnly: Boolean): Long = DatabaseFactory.dbQuery {
|
||||
val query = HorseTable.selectAll().where {
|
||||
HorseTable.feiNummer.isNotNull() and (HorseTable.feiNummer neq "")
|
||||
}
|
||||
|
||||
if (activeOnly) {
|
||||
query.andWhere { HorseTable.istAktiv eq true }
|
||||
} else {
|
||||
query
|
||||
}.count()
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a database row to a DomPferd domain object.
|
||||
*/
|
||||
private fun rowToDomPferd(row: ResultRow): DomPferd {
|
||||
return DomPferd(
|
||||
pferdId = row[HorseTable.id].value,
|
||||
pferdeName = row[HorseTable.pferdeName],
|
||||
geschlecht = row[HorseTable.geschlecht],
|
||||
geburtsdatum = row[HorseTable.geburtsdatum],
|
||||
rasse = row[HorseTable.rasse],
|
||||
farbe = row[HorseTable.farbe],
|
||||
besitzerId = row[HorseTable.besitzerId],
|
||||
verantwortlichePersonId = row[HorseTable.verantwortlichePersonId],
|
||||
zuechterName = row[HorseTable.zuechterName],
|
||||
zuchtbuchNummer = row[HorseTable.zuchtbuchNummer],
|
||||
lebensnummer = row[HorseTable.lebensnummer],
|
||||
chipNummer = row[HorseTable.chipNummer],
|
||||
passNummer = row[HorseTable.passNummer],
|
||||
oepsNummer = row[HorseTable.oepsNummer],
|
||||
feiNummer = row[HorseTable.feiNummer],
|
||||
vaterName = row[HorseTable.vaterName],
|
||||
mutterName = row[HorseTable.mutterName],
|
||||
mutterVaterName = row[HorseTable.mutterVaterName],
|
||||
stockmass = row[HorseTable.stockmass],
|
||||
istAktiv = row[HorseTable.istAktiv],
|
||||
bemerkungen = row[HorseTable.bemerkungen],
|
||||
datenQuelle = row[HorseTable.datenQuelle],
|
||||
createdAt = row[HorseTable.createdAt],
|
||||
updatedAt = row[HorseTable.updatedAt]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a DomPferd domain object to database statement values.
|
||||
*/
|
||||
private fun domPferdToStatement(statement: UpdateBuilder<*>, horse: DomPferd) {
|
||||
statement[HorseTable.pferdeName] = horse.pferdeName
|
||||
statement[HorseTable.geschlecht] = horse.geschlecht
|
||||
statement[HorseTable.geburtsdatum] = horse.geburtsdatum
|
||||
statement[HorseTable.rasse] = horse.rasse
|
||||
statement[HorseTable.farbe] = horse.farbe
|
||||
statement[HorseTable.besitzerId] = horse.besitzerId
|
||||
statement[HorseTable.verantwortlichePersonId] = horse.verantwortlichePersonId
|
||||
statement[HorseTable.zuechterName] = horse.zuechterName
|
||||
statement[HorseTable.zuchtbuchNummer] = horse.zuchtbuchNummer
|
||||
statement[HorseTable.lebensnummer] = horse.lebensnummer
|
||||
statement[HorseTable.chipNummer] = horse.chipNummer
|
||||
statement[HorseTable.passNummer] = horse.passNummer
|
||||
statement[HorseTable.oepsNummer] = horse.oepsNummer
|
||||
statement[HorseTable.feiNummer] = horse.feiNummer
|
||||
statement[HorseTable.vaterName] = horse.vaterName
|
||||
statement[HorseTable.mutterName] = horse.mutterName
|
||||
statement[HorseTable.mutterVaterName] = horse.mutterVaterName
|
||||
statement[HorseTable.stockmass] = horse.stockmass
|
||||
statement[HorseTable.istAktiv] = horse.istAktiv
|
||||
statement[HorseTable.bemerkungen] = horse.bemerkungen
|
||||
statement[HorseTable.datenQuelle] = horse.datenQuelle
|
||||
statement[HorseTable.createdAt] = horse.createdAt
|
||||
statement[HorseTable.updatedAt] = horse.updatedAt
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package at.mocode.horses.infrastructure.persistence
|
||||
|
||||
import at.mocode.core.domain.model.PferdeGeschlechtE
|
||||
import at.mocode.core.domain.model.DatenQuelleE
|
||||
import at.mocode.core.domain.model.PferdeGeschlechtE
|
||||
import org.jetbrains.exposed.v1.core.dao.id.java.UUIDTable
|
||||
import org.jetbrains.exposed.v1.core.kotlin.datetime.date
|
||||
import org.jetbrains.exposed.v1.core.kotlin.datetime.timestamp
|
||||
import org.jetbrains.exposed.v1.core.javaUUID
|
||||
import org.jetbrains.exposed.v1.core.java.javaUUID
|
||||
import org.jetbrains.exposed.v1.datetime.date
|
||||
import org.jetbrains.exposed.v1.datetime.timestamp
|
||||
|
||||
/**
|
||||
* Database table definition for horses in the horse-registry context.
|
||||
|
|
@ -13,7 +13,7 @@ import org.jetbrains.exposed.v1.core.javaUUID
|
|||
* This table stores all horse information including identification,
|
||||
* ownership, breeding data, and administrative information.
|
||||
*/
|
||||
object HorseTable : UUIDTable("horses") {
|
||||
object HorseTable : UUIDTable("zns_horses") {
|
||||
// Basic Information
|
||||
val pferdeName = varchar("pferde_name", 255)
|
||||
val geschlecht = enumerationByName<PferdeGeschlechtE>("geschlecht", 20)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
plugins {
|
||||
// KORREKTUR: Alle Plugins werden jetzt konsistent über den Version Catalog geladen.
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
alias(libs.plugins.kotlin.spring)
|
||||
alias(libs.plugins.kotlinJvm)
|
||||
alias(libs.plugins.kotlinSpring)
|
||||
alias(libs.plugins.spring.boot)
|
||||
alias(libs.plugins.spring.dependencyManagement)
|
||||
}
|
||||
|
||||
// Der springBoot-Block konfiguriert die Anwendung, wenn sie als JAR-Datei ausgeführt wird.
|
||||
springBoot {
|
||||
mainClass.set("at.mocode.horses.service.HorsesServiceApplicationKt")
|
||||
}
|
||||
|
|
@ -17,16 +15,9 @@ dependencies {
|
|||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
implementation(projects.horses.horsesDomain)
|
||||
implementation(projects.horses.horsesApplication)
|
||||
// horses-common: ON HOLD – veraltete API-Referenzen
|
||||
// implementation(projects.horses.horsesCommon)
|
||||
implementation(projects.horses.horsesInfrastructure)
|
||||
implementation(projects.horses.horsesApi)
|
||||
|
||||
// Infrastruktur-Clients
|
||||
implementation(projects.infrastructure.cache.redisCache)
|
||||
implementation(projects.infrastructure.messaging.messagingClient)
|
||||
implementation(projects.infrastructure.monitoring.monitoringClient)
|
||||
|
||||
// KORREKTUR: Alle externen Abhängigkeiten werden jetzt über den Version Catalog bezogen.
|
||||
|
||||
// Spring Boot Starters
|
||||
implementation(libs.spring.boot.starter.web)
|
||||
|
|
@ -42,11 +33,10 @@ dependencies {
|
|||
runtimeOnly(libs.postgresql.driver)
|
||||
testRuntimeOnly(libs.h2.driver)
|
||||
|
||||
|
||||
// Testing
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
testImplementation(libs.spring.boot.starter.test)
|
||||
testImplementation(libs.logback.classic) // SLF4J provider for tests
|
||||
testImplementation(libs.logback.classic)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
package at.mocode.horses.service.config
|
||||
|
||||
import at.mocode.horses.application.usecase.CreateHorseUseCase
|
||||
import at.mocode.horses.application.usecase.TransactionalCreateHorseUseCase
|
||||
import at.mocode.horses.application.usecase.UpdateHorseUseCase
|
||||
import at.mocode.horses.application.usecase.DeleteHorseUseCase
|
||||
import at.mocode.horses.application.usecase.GetHorseUseCase
|
||||
import at.mocode.horses.domain.repository.HorseRepository
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
/**
|
||||
* Application configuration for the Horses Service.
|
||||
*
|
||||
* This configuration wires the use cases as Spring beans.
|
||||
*/
|
||||
@Configuration
|
||||
class ApplicationConfiguration {
|
||||
|
||||
/**
|
||||
* Creates the CreateHorseUseCase as a Spring bean.
|
||||
*/
|
||||
@Bean
|
||||
fun createHorseUseCase(horseRepository: HorseRepository): CreateHorseUseCase {
|
||||
return CreateHorseUseCase(horseRepository)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the TransactionalCreateHorseUseCase as a Spring bean.
|
||||
* This version ensures all database operations run within a single transaction.
|
||||
*/
|
||||
@Bean
|
||||
fun transactionalCreateHorseUseCase(horseRepository: HorseRepository): TransactionalCreateHorseUseCase {
|
||||
return TransactionalCreateHorseUseCase(horseRepository)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the UpdateHorseUseCase as a Spring bean.
|
||||
*/
|
||||
@Bean
|
||||
fun updateHorseUseCase(horseRepository: HorseRepository): UpdateHorseUseCase {
|
||||
return UpdateHorseUseCase(horseRepository)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the DeleteHorseUseCase as a Spring bean.
|
||||
*/
|
||||
@Bean
|
||||
fun deleteHorseUseCase(horseRepository: HorseRepository): DeleteHorseUseCase {
|
||||
return DeleteHorseUseCase(horseRepository)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the GetHorseUseCase as a Spring bean.
|
||||
*/
|
||||
@Bean
|
||||
fun getHorseUseCase(horseRepository: HorseRepository): GetHorseUseCase {
|
||||
return GetHorseUseCase(horseRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +1,37 @@
|
|||
package at.mocode.horses.service.config
|
||||
|
||||
import at.mocode.core.utils.database.DatabaseConfig
|
||||
import at.mocode.core.utils.database.DatabaseFactory
|
||||
import at.mocode.horses.infrastructure.persistence.HorseTable
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
import at.mocode.horses.infrastructure.persistence.ZnsClubTable
|
||||
import at.mocode.horses.infrastructure.persistence.ZnsPersonTable
|
||||
import jakarta.annotation.PostConstruct
|
||||
import org.jetbrains.exposed.v1.jdbc.Database
|
||||
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.stereotype.Component
|
||||
import jakarta.annotation.PostConstruct
|
||||
import jakarta.annotation.PreDestroy
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.jetbrains.exposed.sql.SchemaUtils
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
/**
|
||||
* Database configuration for the Horses Service.
|
||||
*
|
||||
* This configuration ensures that Database.connect() is called properly
|
||||
* before any Exposed operations are performed.
|
||||
* Minimale Datenbank-Konfiguration für den Horses-Service (Dev-Profil).
|
||||
* Verbindet sich direkt via JDBC und legt die Tabellen an.
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("!test")
|
||||
class HorsesDatabaseConfiguration {
|
||||
|
||||
@Profile("dev")
|
||||
class HorsesDatabaseConfiguration(
|
||||
@Value("\${spring.datasource.url}") private val jdbcUrl: String,
|
||||
@Value("\${spring.datasource.username}") private val username: String,
|
||||
@Value("\${spring.datasource.password}") private val password: String
|
||||
) {
|
||||
private val log = LoggerFactory.getLogger(HorsesDatabaseConfiguration::class.java)
|
||||
|
||||
@PostConstruct
|
||||
fun initializeDatabase() {
|
||||
log.info("Initializing database schema for Horses Service...")
|
||||
|
||||
try {
|
||||
// Database connection is already initialized by the gateway
|
||||
// Only initialize the schema for this service
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(HorseTable)
|
||||
log.info("Horse database schema initialized successfully")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
log.error("Failed to initialize database schema", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
fun closeDatabase() {
|
||||
log.info("Closing database connection for Horses Service...")
|
||||
try {
|
||||
DatabaseFactory.close()
|
||||
log.info("Database connection closed successfully")
|
||||
} catch (e: Exception) {
|
||||
log.error("Error closing database connection", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test-specific database configuration.
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("test")
|
||||
class HorsesTestDatabaseConfiguration {
|
||||
|
||||
private val log = LoggerFactory.getLogger(HorsesTestDatabaseConfiguration::class.java)
|
||||
|
||||
@PostConstruct
|
||||
fun initializeTestDatabase() {
|
||||
log.info("Initializing test database connection for Horses Service...")
|
||||
|
||||
try {
|
||||
// Use H2 in-memory database for tests
|
||||
val testConfig = DatabaseConfig(
|
||||
jdbcUrl = "jdbc:h2:mem:horses_test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE",
|
||||
username = "sa",
|
||||
password = "",
|
||||
driverClassName = "org.h2.Driver",
|
||||
maxPoolSize = 5,
|
||||
minPoolSize = 1,
|
||||
autoMigrate = true
|
||||
)
|
||||
|
||||
DatabaseFactory.init(testConfig)
|
||||
log.info("Test database connection initialized successfully")
|
||||
|
||||
// Initialize database schema for tests
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(HorseTable)
|
||||
log.info("Test horse database schema initialized successfully")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
log.error("Failed to initialize test database connection", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
fun closeTestDatabase() {
|
||||
log.info("Closing test database connection for Horses Service...")
|
||||
try {
|
||||
DatabaseFactory.close()
|
||||
log.info("Test database connection closed successfully")
|
||||
} catch (e: Exception) {
|
||||
log.error("Error closing test database connection", e)
|
||||
log.info("Initialisiere Datenbank-Schema für Horses-Service...")
|
||||
Database.connect(jdbcUrl, user = username, password = password)
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(HorseTable, ZnsPersonTable, ZnsClubTable)
|
||||
log.info("Datenbank-Schema erfolgreich initialisiert")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
26
backend/services/officials/officials-domain/build.gradle.kts
Normal file
26
backend/services/officials/officials-domain/build.gradle.kts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm()
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
kotlin.srcDir("src/main/kotlin")
|
||||
dependencies {
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
implementation(libs.kotlinx.datetime)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
kotlin.srcDir("src/test/kotlin")
|
||||
dependencies {
|
||||
implementation(projects.platform.platformTesting)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
package at.mocode.officials.domain.model
|
||||
|
||||
import at.mocode.core.domain.model.DatenQuelleE
|
||||
import at.mocode.core.domain.serialization.KotlinInstantSerializer
|
||||
import at.mocode.core.domain.serialization.UuidSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Domain model representing an official (Richter) in the registry system.
|
||||
*
|
||||
* @property officialId Unique internal identifier (UUID).
|
||||
* @property richterNummer ÖPS official number (from ZNS RICHT01.dat).
|
||||
* @property name Full name of the official.
|
||||
* @property qualifikation Qualification/class code (e.g. "GA", "G3").
|
||||
* @property datenQuelle Source of the data.
|
||||
* @property createdAt Timestamp when this record was created.
|
||||
* @property updatedAt Timestamp when this record was last updated.
|
||||
*/
|
||||
@Serializable
|
||||
data class DomOfficial(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val officialId: Uuid = Uuid.random(),
|
||||
|
||||
val richterNummer: String,
|
||||
val name: String,
|
||||
val qualifikation: String? = null,
|
||||
|
||||
val istAktiv: Boolean = true,
|
||||
val datenQuelle: DatenQuelleE = DatenQuelleE.IMPORT_ZNS,
|
||||
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
val createdAt: Instant = Clock.System.now(),
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
var updatedAt: Instant = Clock.System.now()
|
||||
)
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
plugins {
|
||||
alias(libs.plugins.kotlinJvm)
|
||||
alias(libs.plugins.kotlinSpring)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.platform.platformDependencies)
|
||||
implementation(projects.officials.officialsDomain)
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
|
||||
implementation(libs.spring.boot.starter.web)
|
||||
|
||||
implementation(libs.exposed.core)
|
||||
implementation(libs.exposed.dao)
|
||||
implementation(libs.exposed.jdbc)
|
||||
implementation(libs.exposed.kotlin.datetime)
|
||||
|
||||
runtimeOnly(libs.postgresql.driver)
|
||||
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package at.mocode.officials.infrastructure.persistence
|
||||
|
||||
import org.jetbrains.exposed.v1.core.dao.id.java.UUIDTable
|
||||
import org.jetbrains.exposed.v1.datetime.timestamp
|
||||
|
||||
/**
|
||||
* Tabelle für importierte Richter aus dem ZNS-Datenbestand (RICHT01.dat).
|
||||
* Wird ausschließlich als Dev-Seed-Daten verwendet.
|
||||
*/
|
||||
object ZnsOfficialTable : UUIDTable("zns_officials") {
|
||||
val richterNummer = varchar("richter_nummer", 6).uniqueIndex()
|
||||
val name = varchar("name", 80)
|
||||
val qualifikation = varchar("qualifikation", 5).nullable()
|
||||
val createdAt = timestamp("created_at")
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
plugins {
|
||||
alias(libs.plugins.kotlinJvm)
|
||||
alias(libs.plugins.kotlinSpring)
|
||||
alias(libs.plugins.spring.boot)
|
||||
alias(libs.plugins.spring.dependencyManagement)
|
||||
}
|
||||
|
||||
springBoot {
|
||||
mainClass.set("at.mocode.officials.service.OfficialsServiceApplicationKt")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.platform.platformDependencies)
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
implementation(projects.officials.officialsDomain)
|
||||
implementation(projects.officials.officialsInfrastructure)
|
||||
|
||||
implementation(libs.spring.boot.starter.web)
|
||||
implementation(libs.spring.boot.starter.validation)
|
||||
implementation(libs.spring.boot.starter.actuator)
|
||||
|
||||
implementation(libs.exposed.core)
|
||||
implementation(libs.exposed.dao)
|
||||
implementation(libs.exposed.jdbc)
|
||||
implementation(libs.exposed.kotlin.datetime)
|
||||
implementation(libs.hikari.cp)
|
||||
runtimeOnly(libs.postgresql.driver)
|
||||
testRuntimeOnly(libs.h2.driver)
|
||||
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
testImplementation(libs.spring.boot.starter.test)
|
||||
testImplementation(libs.logback.classic)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package at.mocode.officials.service
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
|
||||
@SpringBootApplication
|
||||
@ComponentScan(
|
||||
basePackages = [
|
||||
"at.mocode.officials.service",
|
||||
"at.mocode.officials.infrastructure"
|
||||
]
|
||||
)
|
||||
class OfficialsServiceApplication
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
runApplication<OfficialsServiceApplication>(*args)
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package at.mocode.officials.service.config
|
||||
|
||||
import at.mocode.officials.infrastructure.persistence.ZnsOfficialTable
|
||||
import jakarta.annotation.PostConstruct
|
||||
import org.jetbrains.exposed.v1.jdbc.Database
|
||||
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Profile
|
||||
|
||||
@Configuration
|
||||
@Profile("dev")
|
||||
class OfficialsDatabaseConfiguration(
|
||||
@Value("\${spring.datasource.url}") private val jdbcUrl: String,
|
||||
@Value("\${spring.datasource.username}") private val username: String,
|
||||
@Value("\${spring.datasource.password}") private val password: String
|
||||
) {
|
||||
private val log = LoggerFactory.getLogger(OfficialsDatabaseConfiguration::class.java)
|
||||
|
||||
@PostConstruct
|
||||
fun initializeDatabase() {
|
||||
log.info("Initialisiere Datenbank-Schema für Officials-Service...")
|
||||
Database.connect(jdbcUrl, user = username, password = password)
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(ZnsOfficialTable)
|
||||
log.info("Datenbank-Schema erfolgreich initialisiert")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package at.mocode.officials.service.dev
|
||||
|
||||
import at.mocode.officials.infrastructure.persistence.ZnsOfficialTable
|
||||
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
|
||||
import org.jetbrains.exposed.v1.jdbc.batchInsert
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.boot.CommandLineRunner
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.stereotype.Component
|
||||
import java.io.File
|
||||
import kotlin.time.Clock
|
||||
|
||||
/**
|
||||
* Dev-only Seeder: Importiert RICHT01.dat (ZNS) → zns_officials.
|
||||
* Aktivierung: Umgebungsvariable ZNS_DATA_DIR setzen + Profil "dev".
|
||||
*/
|
||||
@Component
|
||||
@Profile("dev")
|
||||
class ZnsOfficialSeeder(
|
||||
@Value("\${zns.data.dir:#{null}}") private val znsDataDir: String?
|
||||
) : CommandLineRunner {
|
||||
|
||||
private val log = LoggerFactory.getLogger(ZnsOfficialSeeder::class.java)
|
||||
|
||||
override fun run(vararg args: String) {
|
||||
if (znsDataDir == null) {
|
||||
log.info("ZNS_DATA_DIR nicht gesetzt – ZnsOfficialSeeder wird übersprungen.")
|
||||
return
|
||||
}
|
||||
val dir = File(znsDataDir)
|
||||
if (!dir.exists() || !dir.isDirectory) {
|
||||
log.warn("ZNS_DATA_DIR '{}' existiert nicht oder ist kein Verzeichnis.", znsDataDir)
|
||||
return
|
||||
}
|
||||
log.info("Starte ZNS-Richter-Import aus: {}", dir.absolutePath)
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(ZnsOfficialTable)
|
||||
}
|
||||
seedOfficials(dir)
|
||||
log.info("ZNS-Richter-Import abgeschlossen.")
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// RICHT01.dat → zns_officials
|
||||
// Format (~109 Zeichen):
|
||||
// [0] 'Y' (Kennzeichen)
|
||||
// [1-7] RichterNr (6 Zeichen)
|
||||
// [7-87] Name (80 Zeichen)
|
||||
// [87-89] Qualifikation (z.B. "GA", "G3")
|
||||
// -------------------------------------------------------------------------
|
||||
private fun seedOfficials(dir: File) {
|
||||
val file = File(dir, "RICHT01.dat")
|
||||
if (!file.exists()) {
|
||||
log.warn("RICHT01.dat nicht gefunden – Richter werden übersprungen.")
|
||||
return
|
||||
}
|
||||
|
||||
data class OfficialRow(
|
||||
val richterNr: String,
|
||||
val name: String,
|
||||
val qualifikation: String?
|
||||
)
|
||||
|
||||
val rows = file.readLines(Charsets.ISO_8859_1)
|
||||
.filter { it.length >= 7 && it.startsWith("Y") }
|
||||
.mapNotNull { line ->
|
||||
val richterNr = line.substring(1, 7).trim()
|
||||
if (richterNr.isBlank()) return@mapNotNull null
|
||||
val name = line.safeSubstring(7, 87).trim()
|
||||
val qualifikation = line.safeSubstring(87, 92).trim().takeIf { it.isNotBlank() }
|
||||
OfficialRow(richterNr, name, qualifikation)
|
||||
}
|
||||
|
||||
val now = Clock.System.now()
|
||||
transaction {
|
||||
ZnsOfficialTable.batchInsert(rows, ignore = true) { row ->
|
||||
this[ZnsOfficialTable.richterNummer] = row.richterNr
|
||||
this[ZnsOfficialTable.name] = row.name
|
||||
this[ZnsOfficialTable.qualifikation] = row.qualifikation
|
||||
this[ZnsOfficialTable.createdAt] = now
|
||||
}
|
||||
}
|
||||
log.info("Richter importiert: {} Datensätze", rows.size)
|
||||
}
|
||||
|
||||
private fun String.safeSubstring(start: Int, end: Int): String =
|
||||
if (this.length >= end) this.substring(start, end) else if (this.length > start) this.substring(start) else ""
|
||||
}
|
||||
26
backend/services/persons/persons-domain/build.gradle.kts
Normal file
26
backend/services/persons/persons-domain/build.gradle.kts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm()
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
kotlin.srcDir("src/main/kotlin")
|
||||
dependencies {
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
implementation(libs.kotlinx.datetime)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
kotlin.srcDir("src/test/kotlin")
|
||||
dependencies {
|
||||
implementation(projects.platform.platformTesting)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
package at.mocode.persons.domain.model
|
||||
|
||||
import at.mocode.core.domain.model.DatenQuelleE
|
||||
import at.mocode.core.domain.serialization.KotlinInstantSerializer
|
||||
import at.mocode.core.domain.serialization.UuidSerializer
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Domain model representing a person (rider/member) in the registry system.
|
||||
*
|
||||
* @property personId Unique internal identifier (UUID).
|
||||
* @property lizenzNummer ÖPS license number (from ZNS LIZENZ01.dat).
|
||||
* @property nachname Last name.
|
||||
* @property vorname First name.
|
||||
* @property geschlecht Gender code (M/W).
|
||||
* @property geburtsdatum Date of birth.
|
||||
* @property nation Nation code (e.g. AUT).
|
||||
* @property lizenzKlasse License class (e.g. R1, R2, RD2).
|
||||
* @property mitgliedsNummer Membership number.
|
||||
* @property vereinsNummer Club number.
|
||||
* @property vereinsName Club name.
|
||||
* @property datenQuelle Source of the data.
|
||||
* @property createdAt Timestamp when this record was created.
|
||||
* @property updatedAt Timestamp when this record was last updated.
|
||||
*/
|
||||
@Serializable
|
||||
data class DomPerson(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val personId: Uuid = Uuid.random(),
|
||||
|
||||
val lizenzNummer: String,
|
||||
val nachname: String,
|
||||
val vorname: String,
|
||||
|
||||
val geschlecht: String? = null,
|
||||
val geburtsdatum: LocalDate? = null,
|
||||
val nation: String? = null,
|
||||
val lizenzKlasse: String? = null,
|
||||
val mitgliedsNummer: String? = null,
|
||||
val vereinsNummer: String? = null,
|
||||
val vereinsName: String? = null,
|
||||
|
||||
val istAktiv: Boolean = true,
|
||||
val datenQuelle: DatenQuelleE = DatenQuelleE.IMPORT_ZNS,
|
||||
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
val createdAt: Instant = Clock.System.now(),
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
var updatedAt: Instant = Clock.System.now()
|
||||
) {
|
||||
fun getDisplayName(): String = "$vorname $nachname"
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
plugins {
|
||||
alias(libs.plugins.kotlinJvm)
|
||||
alias(libs.plugins.kotlinSpring)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.platform.platformDependencies)
|
||||
implementation(projects.persons.personsDomain)
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
|
||||
implementation(libs.spring.boot.starter.web)
|
||||
|
||||
implementation(libs.exposed.core)
|
||||
implementation(libs.exposed.dao)
|
||||
implementation(libs.exposed.jdbc)
|
||||
implementation(libs.exposed.kotlin.datetime)
|
||||
|
||||
runtimeOnly(libs.postgresql.driver)
|
||||
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package at.mocode.persons.infrastructure.persistence
|
||||
|
||||
import org.jetbrains.exposed.v1.core.dao.id.java.UUIDTable
|
||||
import org.jetbrains.exposed.v1.datetime.date
|
||||
import org.jetbrains.exposed.v1.datetime.timestamp
|
||||
|
||||
/**
|
||||
* Tabelle für importierte Personen/Reiter aus dem ZNS-Datenbestand (LIZENZ01.dat).
|
||||
* Wird ausschließlich als Dev-Seed-Daten verwendet.
|
||||
*/
|
||||
object ZnsPersonTable : UUIDTable("zns_persons") {
|
||||
val lizenzNummer = varchar("lizenz_nummer", 10).uniqueIndex()
|
||||
val nachname = varchar("nachname", 50)
|
||||
val vorname = varchar("vorname", 25)
|
||||
val vereinsNummer = varchar("vereins_nummer", 4).nullable()
|
||||
val vereinsName = varchar("vereins_name", 50).nullable()
|
||||
val nation = varchar("nation", 3).nullable()
|
||||
val lizenzKlasse = varchar("lizenz_klasse", 6).nullable()
|
||||
val mitgliedsNummer = varchar("mitglieds_nummer", 13).nullable()
|
||||
val geschlecht = varchar("geschlecht", 1).nullable()
|
||||
val geburtsdatum = date("geburtsdatum").nullable()
|
||||
val createdAt = timestamp("created_at")
|
||||
}
|
||||
38
backend/services/persons/persons-service/build.gradle.kts
Normal file
38
backend/services/persons/persons-service/build.gradle.kts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
plugins {
|
||||
alias(libs.plugins.kotlinJvm)
|
||||
alias(libs.plugins.kotlinSpring)
|
||||
alias(libs.plugins.spring.boot)
|
||||
alias(libs.plugins.spring.dependencyManagement)
|
||||
}
|
||||
|
||||
springBoot {
|
||||
mainClass.set("at.mocode.persons.service.PersonsServiceApplicationKt")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.platform.platformDependencies)
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
implementation(projects.persons.personsDomain)
|
||||
implementation(projects.persons.personsInfrastructure)
|
||||
|
||||
implementation(libs.spring.boot.starter.web)
|
||||
implementation(libs.spring.boot.starter.validation)
|
||||
implementation(libs.spring.boot.starter.actuator)
|
||||
|
||||
implementation(libs.exposed.core)
|
||||
implementation(libs.exposed.dao)
|
||||
implementation(libs.exposed.jdbc)
|
||||
implementation(libs.exposed.kotlin.datetime)
|
||||
implementation(libs.hikari.cp)
|
||||
runtimeOnly(libs.postgresql.driver)
|
||||
testRuntimeOnly(libs.h2.driver)
|
||||
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
testImplementation(libs.spring.boot.starter.test)
|
||||
testImplementation(libs.logback.classic)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package at.mocode.persons.service
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
|
||||
@SpringBootApplication
|
||||
@ComponentScan(
|
||||
basePackages = [
|
||||
"at.mocode.persons.service",
|
||||
"at.mocode.persons.infrastructure"
|
||||
]
|
||||
)
|
||||
class PersonsServiceApplication
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
runApplication<PersonsServiceApplication>(*args)
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package at.mocode.persons.service.config
|
||||
|
||||
import at.mocode.persons.infrastructure.persistence.ZnsPersonTable
|
||||
import jakarta.annotation.PostConstruct
|
||||
import org.jetbrains.exposed.v1.jdbc.Database
|
||||
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Profile
|
||||
|
||||
@Configuration
|
||||
@Profile("dev")
|
||||
class PersonsDatabaseConfiguration(
|
||||
@Value("\${spring.datasource.url}") private val jdbcUrl: String,
|
||||
@Value("\${spring.datasource.username}") private val username: String,
|
||||
@Value("\${spring.datasource.password}") private val password: String
|
||||
) {
|
||||
private val log = LoggerFactory.getLogger(PersonsDatabaseConfiguration::class.java)
|
||||
|
||||
@PostConstruct
|
||||
fun initializeDatabase() {
|
||||
log.info("Initialisiere Datenbank-Schema für Persons-Service...")
|
||||
Database.connect(jdbcUrl, user = username, password = password)
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(ZnsPersonTable)
|
||||
log.info("Datenbank-Schema erfolgreich initialisiert")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
package at.mocode.persons.service.dev
|
||||
|
||||
import at.mocode.persons.infrastructure.persistence.ZnsPersonTable
|
||||
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
|
||||
import org.jetbrains.exposed.v1.jdbc.batchInsert
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.boot.CommandLineRunner
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.stereotype.Component
|
||||
import java.io.File
|
||||
import kotlin.time.Clock
|
||||
|
||||
/**
|
||||
* Dev-only Seeder: Importiert LIZENZ01.dat (ZNS) → zns_persons.
|
||||
* Aktivierung: Umgebungsvariable ZNS_DATA_DIR setzen + Profil "dev".
|
||||
*/
|
||||
@Component
|
||||
@Profile("dev")
|
||||
class ZnsPersonSeeder(
|
||||
@Value("\${zns.data.dir:#{null}}") private val znsDataDir: String?
|
||||
) : CommandLineRunner {
|
||||
|
||||
private val log = LoggerFactory.getLogger(ZnsPersonSeeder::class.java)
|
||||
|
||||
override fun run(vararg args: String) {
|
||||
if (znsDataDir == null) {
|
||||
log.info("ZNS_DATA_DIR nicht gesetzt – ZnsPersonSeeder wird übersprungen.")
|
||||
return
|
||||
}
|
||||
val dir = File(znsDataDir)
|
||||
if (!dir.exists() || !dir.isDirectory) {
|
||||
log.warn("ZNS_DATA_DIR '{}' existiert nicht oder ist kein Verzeichnis.", znsDataDir)
|
||||
return
|
||||
}
|
||||
log.info("Starte ZNS-Personen-Import aus: {}", dir.absolutePath)
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(ZnsPersonTable)
|
||||
}
|
||||
seedPersons(dir)
|
||||
log.info("ZNS-Personen-Import abgeschlossen.")
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// LIZENZ01.dat → zns_persons
|
||||
// Format (220 Zeichen):
|
||||
// [0-5] LizenzNr
|
||||
// [6-55] Nachname
|
||||
// [56-80] Vorname
|
||||
// [81-83] VereinsNr
|
||||
// [84-133] VereinsName
|
||||
// [134-136] Nation
|
||||
// [137-143] LizenzKlasse (z.B. "R1", "R2", "RD2")
|
||||
// [144-156] MitgliedsNr
|
||||
// [157-165] Telefon (nicht gespeichert)
|
||||
// [166-169] Jahr
|
||||
// [170] Geschlecht (M/W)
|
||||
// [171-178] Geburtsdatum (YYYYMMDD)
|
||||
// -------------------------------------------------------------------------
|
||||
private fun seedPersons(dir: File) {
|
||||
val file = File(dir, "LIZENZ01.dat")
|
||||
if (!file.exists()) {
|
||||
log.warn("LIZENZ01.dat nicht gefunden – Personen werden übersprungen.")
|
||||
return
|
||||
}
|
||||
|
||||
data class PersonRow(
|
||||
val lizenzNr: String,
|
||||
val nachname: String,
|
||||
val vorname: String,
|
||||
val vereinsNr: String?,
|
||||
val vereinsName: String?,
|
||||
val nation: String?,
|
||||
val lizenzKlasse: String?,
|
||||
val mitgliedsNr: String?,
|
||||
val geschlecht: String?,
|
||||
val geburtsdatum: java.time.LocalDate?
|
||||
)
|
||||
|
||||
val rows = file.readLines(Charsets.ISO_8859_1)
|
||||
.filter { it.length >= 6 }
|
||||
.mapNotNull { line ->
|
||||
val lizenzNr = line.substring(0, 6).trim()
|
||||
if (lizenzNr.isBlank()) return@mapNotNull null
|
||||
|
||||
val nachname = line.safeSubstring(6, 56).trim()
|
||||
val vorname = line.safeSubstring(56, 81).trim()
|
||||
val vereinsNr = line.safeSubstring(81, 84).trim().takeIf { it.isNotBlank() }
|
||||
val vereinsName = line.safeSubstring(84, 134).trim().takeIf { it.isNotBlank() }
|
||||
val nation = line.safeSubstring(134, 137).trim().takeIf { it.isNotBlank() }
|
||||
val lizenzKlasse = line.safeSubstring(137, 144).trim().takeIf { it.isNotBlank() }
|
||||
val mitgliedsNr = line.safeSubstring(144, 157).trim().takeIf { it.isNotBlank() }
|
||||
val geschlecht = line.safeSubstring(170, 171).trim().takeIf { it.isNotBlank() }
|
||||
val gebDatStr = line.safeSubstring(171, 179).trim()
|
||||
val gebDat = parseDate(gebDatStr)
|
||||
|
||||
PersonRow(
|
||||
lizenzNr, nachname, vorname, vereinsNr, vereinsName,
|
||||
nation, lizenzKlasse, mitgliedsNr, geschlecht, gebDat
|
||||
)
|
||||
}
|
||||
|
||||
val now = Clock.System.now()
|
||||
transaction {
|
||||
ZnsPersonTable.batchInsert(rows, ignore = true) { row ->
|
||||
this[ZnsPersonTable.lizenzNummer] = row.lizenzNr
|
||||
this[ZnsPersonTable.nachname] = row.nachname
|
||||
this[ZnsPersonTable.vorname] = row.vorname
|
||||
this[ZnsPersonTable.vereinsNummer] = row.vereinsNr
|
||||
this[ZnsPersonTable.vereinsName] = row.vereinsName
|
||||
this[ZnsPersonTable.nation] = row.nation
|
||||
this[ZnsPersonTable.lizenzKlasse] = row.lizenzKlasse
|
||||
this[ZnsPersonTable.mitgliedsNummer] = row.mitgliedsNr
|
||||
this[ZnsPersonTable.geschlecht] = row.geschlecht
|
||||
this[ZnsPersonTable.geburtsdatum] = row.geburtsdatum?.let {
|
||||
kotlinx.datetime.LocalDate(it.year, it.monthValue, it.dayOfMonth)
|
||||
}
|
||||
this[ZnsPersonTable.createdAt] = now
|
||||
}
|
||||
}
|
||||
log.info("Personen importiert: {} Datensätze", rows.size)
|
||||
}
|
||||
|
||||
private fun String.safeSubstring(start: Int, end: Int): String =
|
||||
if (this.length >= end) this.substring(start, end) else if (this.length > start) this.substring(start) else ""
|
||||
|
||||
private fun parseDate(s: String): java.time.LocalDate? {
|
||||
if (s.length < 8 || s.all { it == '0' }) return null
|
||||
return try {
|
||||
java.time.LocalDate.of(s.substring(0, 4).toInt(), s.substring(4, 6).toInt(), s.substring(6, 8).toInt())
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -65,6 +65,17 @@ enum class VerifikationsStatusE {
|
|||
KORREKTUR_ERFORDERLICH
|
||||
}
|
||||
|
||||
/**
|
||||
* Geschlecht eines Pferdes gemäß ÖTO/ZNS-Klassifikation.
|
||||
*/
|
||||
@Serializable
|
||||
enum class PferdeGeschlechtE {
|
||||
HENGST,
|
||||
STUTE,
|
||||
WALLACH,
|
||||
UNBEKANNT
|
||||
}
|
||||
|
||||
/**
|
||||
* Processing states for workflows and tasks.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,128 @@
|
|||
# Detaillierte Bewerbs-Parameter: Springen und Dressur
|
||||
|
||||
Dieses Dokument beschreibt die genauen Parameter, die in der Turnier-Ausschreibung für die einzelnen Bewerbe der Sparten
|
||||
Springen (CSN) und Dressur (CDN) definiert werden müssen, basierend auf der aktuellen ÖTO.
|
||||
|
||||
---
|
||||
|
||||
## 1. Verständnis eines Ausschreibungs-Beispiels
|
||||
|
||||
Ein typischer Bewerb im Ausschreibungs-Text sieht oft so aus:
|
||||
> **6 Stilspringprüfung 80 cm J RV: § 204/4 CSN-C Neu**
|
||||
> **1. Abt. lizenzfrei 2.Abt. R1 und Reiter mit 5 & 6 jährigen Pferden**
|
||||
|
||||
### Aufschlüsselung der Parameter:
|
||||
|
||||
* **`6`**: **Bewerbsnummer**. Eine fortlaufende Nummer zur eindeutigen Identifikation des Bewerbs (Prüfung Nr. 6 des
|
||||
Turniers).
|
||||
* **`Stilspringprüfung`**: **Art der Prüfung**. Es geht hier nicht rein nach Fehlern und Zeit, sondern der Reiter wird
|
||||
von Richtern mit einer Wertnote für seinen Sitz, seine Einwirkung und den Rhythmus beurteilt.
|
||||
* **`80 cm`**: **Klasse / Maximale Hindernishöhe**. In diesem Fall entspricht dies der Einsteigerklasse (E bzw. E0).
|
||||
* **`J`**: **Startbuchstabe**. Nach diesem Buchstaben wird die Startreihenfolge alphabetisch gelost (oft nach dem Namen
|
||||
des Pferdes). Ist der Buchstabe "J", startet das Pferd "Jolly Jumper" als erstes, danach "Karino" usw., bis am Ende
|
||||
das Pferd "Ikarus" startet.
|
||||
* **`RV: § 204/4`**: **Richtverfahren**. Der direkte Verweis auf den entsprechenden Paragraphen der ÖTO. Hier bedeutet
|
||||
dies: Stilspringprüfung mit Wertnoten von 0,0 bis 10,0.
|
||||
* **`CSN-C Neu`**: **Turnierkategorie**. Nationales Springturnier, Kategorie C-Neu. Das ist eine Einsteiger-Kategorie,
|
||||
bei der auch Reiter ohne Lizenz (nur mit Reiterpass) antreten dürfen. Es gibt hier kein Preisgeld, nur Sachpreise und
|
||||
Schleifen.
|
||||
* **`1. Abt. lizenzfrei`**: **Abteilung 1 (Unterteilung)**. Diese Abteilung (erste Siegerehrung/Platzierung) ist
|
||||
exklusiv für Reiter, die noch keine Turnierlizenz haben.
|
||||
* **`2. Abt. R1 und Reiter mit 5 & 6 jährigen Pferden`**: **Abteilung 2**. Die zweite Abteilung wertet alle Reiter mit
|
||||
der Einstiegslizenz (R1) sowie erfahrene Reiter, wenn sie junge Nachwuchspferde reiten. So wird Fairness garantiert.
|
||||
|
||||
---
|
||||
|
||||
## 2. Sparte Springen (CSN) im Detail
|
||||
|
||||
### 2.1 Die Klassen (Höhen für Großpferde)
|
||||
|
||||
Die Klassen geben die maximale Höhe der Hindernisse an:
|
||||
|
||||
* **Klasse E0 (Einsteiger):** 60 bis 90 cm
|
||||
* **Klasse A (Leicht):** 105 bis 110 cm
|
||||
* **Klasse L (Mittelleicht):** 115 bis 120 cm
|
||||
* **Klasse LM (Leicht-Mittelschwer):** 125 bis 130 cm
|
||||
* **Klasse M (Mittelschwer):** 135 cm
|
||||
* **Klasse S (Schwer):** 140 bis 160 cm
|
||||
*(Hinweis: Für Ponys sind die Höhen reduziert und die Abstände in Kombinationen verkürzt)*
|
||||
|
||||
### 2.2 Die Richtverfahren (RV) gemäß § 204 ÖTO
|
||||
|
||||
Das Richtverfahren definiert, wie Fehler und Zeiten gewertet werden.
|
||||
|
||||
#### Richtverfahren A (Standardspringprüfung)
|
||||
|
||||
Es gibt Strafpunkte (Fehler) für Abwürfe (4 Fehlerpunkte) und Verweigerungen/Ungehorsam (4 Fehlerpunkte beim ersten Mal,
|
||||
beim zweiten Mal ab 115 cm Ausschluss). Zeitüberschreitungen über die "Erlaubte Zeit" geben Zeitfehler (0,25 Punkte pro
|
||||
Sekunde).
|
||||
|
||||
* **A1:** Es gibt keine Zeitwertung. Alle fehlerfreien Ritte (gleiche Punktezahl) sind ex aequo auf Platz 1.
|
||||
* **A2:** Fehler und Zeit. Bei Punktegleichheit gewinnt die schnellere Umlaufzeit.
|
||||
* **A3 (Idealzeit):** Eine Idealzeit (meist Erlaubte Zeit minus 10%) wird definiert. Es gewinnt der Reiter, der am
|
||||
nächsten an der Idealzeit ist (darunter oder darüber). Schützt vor "Bolzen" in Anfängerprüfungen.
|
||||
* **AM3, AM4, AM5, AM6:** Standardspringen **mit Stechen**. Wer im Grundparcours fehlerfrei bleibt, reitet danach einen
|
||||
verkürzten Stechparcours auf Zeit.
|
||||
|
||||
#### Richtverfahren C (Zeitspringen)
|
||||
|
||||
Fehlerpunkte gibt es nicht. Für jeden Abwurf werden stattdessen **Strafsekunden** (meist 4 Sekunden) zur gerittenen Zeit
|
||||
addiert. Die schnellste Endzeit gewinnt.
|
||||
|
||||
### 2.3 Spezial-Richtverfahren / Prüfungsarten
|
||||
|
||||
* **Einlaufspringprüfung (§ 218):** Ein Trainingsbewerb (RV A1). Es gibt keine Platzierung. Jeder fehlerfreie Reiter
|
||||
bekommt eine braune "Clear-Round"-Schleife.
|
||||
* **Punktespringprüfung (§ 219):** Man sammelt Pluspunkte für fehlerfreie Sprünge. Am Ende steht oft ein schwieriger "
|
||||
Joker-Sprung", der doppelte Punkte oder bei einem Fehler doppelten Abzug bringt.
|
||||
* **2-Phasenspringprüfung (§ 220):** Der Parcours ist in zwei Teile geteilt. Wer Phase 1 fehlerfrei schafft, reitet ohne
|
||||
anzuhalten sofort in Phase 2 (die meist auf Zeit geht). Wer in Phase 1 patzt, wird abgeglockt.
|
||||
* **Stilspringprüfung:** Bewertung mit Wertnoten von 0 bis 10. Abzüge erfolgen für Ungehorsam (-0,5 oder -1,0) und
|
||||
Hindernisfehler (-0,5).
|
||||
|
||||
---
|
||||
|
||||
## 3. Sparte Dressur (CDN) im Detail
|
||||
|
||||
### 3.1 Die Klassen und Aufgaben
|
||||
|
||||
Die Dressur wird nach vorgegebenen "Aufgaben" geritten (z.B. "Aufgabe A2", "FEI Grand Prix"), die die zu reitenden
|
||||
Hufschlagfiguren exakt vorgeben.
|
||||
|
||||
* **Klasse A:** Grundlagen, Hufschlagfiguren, einfache Galoppwechsel über den Trab.
|
||||
* **Klasse L:** Beginnende Versammlung, Kurzkehrt, Außengalopp.
|
||||
* **Klasse LM:** Schulterherein, fliegende Wechsel können vorkommen. (Ab hier wahlweise Trense oder Kandare).
|
||||
* **Klasse M:** Traversalen, fliegende Galoppwechsel. (Kandarenpflicht).
|
||||
* **Klasse S:** Pirouetten, Serienwechsel (z.B. Wechsel alle 2 Sprünge), Piaffe, Passage.
|
||||
|
||||
### 3.2 Die Richtverfahren (RV) gemäß § 104 ÖTO
|
||||
|
||||
Das Richtverfahren definiert die Sitzverteilung und Art der Notengebung der Richter.
|
||||
|
||||
#### Richtverfahren A (Gemeinsames Richten)
|
||||
|
||||
Typisch für untere bis mittlere Klassen. Die Richtergruppe (die zusammen bei "C" sitzt) einigt sich auf **eine
|
||||
gemeinsame Wertnote** zwischen 0,0 und 10,0 (z.B. 7,4).
|
||||
|
||||
* *Verreiten:* Wird mit einem Abzug von der Gesamtnote bestraft (1. Mal: -0,2 / 2. Mal: -0,4).
|
||||
|
||||
#### Richtverfahren B (Getrenntes Richten)
|
||||
|
||||
Typisch ab Klasse M und bei Meisterschaften. Mindestens drei Richter sitzen an verschiedenen Stellen (C, H, M) und
|
||||
werten völlig unabhängig voneinander. Jede einzelne Lektion wird mit einer Note (0 bis 10) bewertet. Am Ende werden die
|
||||
Punkte addiert und in einen Prozentwert umgerechnet (z.B. 68,542 %).
|
||||
|
||||
* *Verreiten:* Führt zu Abzügen bei der Gesamtpunktezahl bei jedem Richter (1. Mal: -2 Pkt. pro Richter / 2. Mal: -4
|
||||
Pkt. pro Richter).
|
||||
|
||||
*Wichtig für alle Dressurprüfungen:* Ein **drittes Verreiten** führt unausweichlich zum Ausschluss (Abglocken) des
|
||||
Teilnehmers.
|
||||
|
||||
### 3.3 Sonderformen
|
||||
|
||||
* **Musikkür:** Wird immer nach RV B gerichtet. Hierbei werden zwei getrennte Notensets vergeben: Eine Note für die *
|
||||
*Technik** (Wurden alle geforderten Lektionen korrekt gezeigt?) und eine Note für die **Künstlerische Ausführung** (
|
||||
Choreographie, Musikinterpretation, Schwierigkeitsgrad).
|
||||
* **Dressurreiterprüfung / Dressurpferdeprüfung:** Ähnlich wie im Stilspringen zählt hier primär der Sitz und die
|
||||
Einwirkung des Reiters (Reiterprüfung) bzw. das Potenzial und die Grundgangarten des jungen Pferdes (Pferdeprüfung).
|
||||
Wird in der Regel nach RV A (Wertnoten) gerichtet.
|
||||
261
docs/04_Agents/Logs/2026-03-23_ZNS_Importer_Backend_Services.md
Normal file
261
docs/04_Agents/Logs/2026-03-23_ZNS_Importer_Backend_Services.md
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
---
|
||||
type: Log
|
||||
agent: Curator
|
||||
date: 2026-03-23
|
||||
status: COMPLETED
|
||||
topics:
|
||||
- ZNS-Importer
|
||||
- Backend-Services
|
||||
- Dev-Seeder
|
||||
- Testdaten
|
||||
---
|
||||
|
||||
# 🧹 Session Log: 23. März 2026 – ZNS-Importer & Backend-Services
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
Diese Session hatte das Ziel, **echte Testdaten aus dem ZNS-System (Zentrales Nennsystem des OEPS)** für die
|
||||
Frontend-Entwicklung bereitzustellen. Dazu wurden vier Backend-Services aufgebaut, die die ZNS-Rohdaten
|
||||
(Fixbreiten-Flat-Files) in eine lokale PostgreSQL-Datenbank importieren.
|
||||
|
||||
---
|
||||
|
||||
## Kontext: Was ist ZNS?
|
||||
|
||||
Das **ZNS (Zentrales Nennsystem)** ist das Stammdaten-System des OEPS (Österreichischer Pferdesport-Verband).
|
||||
Es liefert Rohdaten als **Fixbreiten-Flat-Files** (`.dat`) – ein klassisches Format älterer Verbandssysteme.
|
||||
|
||||
Die Rohdaten liegen unter: `docs/OePS/ZNS/`
|
||||
|
||||
| Datei | Inhalt | Datensätze |
|
||||
|----------------------|-----------------------------------|------------|
|
||||
| `PFERDE01.dat` | Pferde-Stammdaten | ~tausende |
|
||||
| `LIZENZ01.dat` | Reiter / Personen / Lizenzen | ~tausende |
|
||||
| `VEREIN01.dat` | Vereine / Clubs | ~hunderte |
|
||||
| `RICHT01.dat` | Richter / Offizielle | ~hunderte |
|
||||
| `ISLANDPFERDE01.dat` | Islandpferde (separates Register) | – |
|
||||
| `VOLT01.dat` | Voltigier-Daten | – |
|
||||
|
||||
---
|
||||
|
||||
## Was wurde gebaut?
|
||||
|
||||
### Strategische Entscheidung: Dev-Seeder, kein Produktions-Importer
|
||||
|
||||
Es wurde bewusst **kein produktiver Import-Service** gebaut, sondern ein **Dev-Seeder** (`@Profile("dev")`).
|
||||
Das bedeutet:
|
||||
|
||||
- Der Seeder läuft **nur im `dev`-Profil** – nie in Produktion
|
||||
- Die Tabellen tragen das Präfix `zns_` – klar erkennbar als Import-Rohdaten
|
||||
- Der Seeder ist **wegwerfbar** – der saubere Produktions-Importer folgt in Phase 3
|
||||
|
||||
### Vier neue Backend-Services
|
||||
|
||||
Alle Services folgen der gleichen Struktur wie der bestehende `horses`-Service:
|
||||
|
||||
```
|
||||
backend/services/
|
||||
├── horses/ ✅ Pferde (PFERDE01.dat → zns_horses)
|
||||
├── persons/ ✅ Personen (LIZENZ01.dat → zns_persons)
|
||||
├── clubs/ ✅ Vereine (VEREIN01.dat → zns_clubs)
|
||||
└── officials/ ✅ Richter (RICHT01.dat → zns_officials)
|
||||
```
|
||||
|
||||
Jeder Service besteht aus drei Modulen:
|
||||
|
||||
| Modul | Inhalt |
|
||||
|--------------------|-------------------------------------------------------------------|
|
||||
| `*-domain` | Domain-Modell (`DomPferd`, `DomPerson`, `DomClub`, `DomOfficial`) |
|
||||
| `*-infrastructure` | Datenbank-Tabelle (`ZnsHorseTable`, `ZnsPersonTable`, etc.) |
|
||||
| `*-service` | Spring Boot App + `DatabaseConfiguration` + `ZnsXxxSeeder` |
|
||||
|
||||
### Datenbank-Tabellen (ZNS-Präfix)
|
||||
|
||||
| Service | Tabelle | Quelle |
|
||||
|---------------------|-----------------|----------------|
|
||||
| `horses-service` | `zns_horses` | `PFERDE01.dat` |
|
||||
| `persons-service` | `zns_persons` | `LIZENZ01.dat` |
|
||||
| `clubs-service` | `zns_clubs` | `VEREIN01.dat` |
|
||||
| `officials-service` | `zns_officials` | `RICHT01.dat` |
|
||||
|
||||
Das `zns_`-Präfix macht sofort klar: **Diese Daten kommen aus dem ZNS-Import** und sind noch nicht das
|
||||
saubere Domain-Modell.
|
||||
|
||||
### Fixbreiten-Parser
|
||||
|
||||
Jeder Seeder enthält einen eigenen Fixbreiten-Parser für das jeweilige `.dat`-Format.
|
||||
Beispiel `LIZENZ01.dat` (220 Zeichen pro Zeile):
|
||||
|
||||
```
|
||||
[0-5] LizenzNr
|
||||
[6-55] Nachname
|
||||
[56-80] Vorname
|
||||
[81-83] VereinsNr
|
||||
[84-133] VereinsName
|
||||
[134-136] Nation
|
||||
[137-143] LizenzKlasse (R1, R2, RD2, ...)
|
||||
[144-156] MitgliedsNr
|
||||
[170] Geschlecht (M/W)
|
||||
[171-178] Geburtsdatum (YYYYMMDD)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Betriebsanleitung: So startest du die Services
|
||||
|
||||
### Voraussetzungen
|
||||
|
||||
1. **PostgreSQL läuft lokal** (via Docker Compose):
|
||||
```bash
|
||||
docker compose -f dc-infra.yaml up -d
|
||||
```
|
||||
Standard-Verbindung: `jdbc:postgresql://localhost:5432/meldestelle` (User/PW: `meldestelle`)
|
||||
|
||||
2. **ZNS-Dateien sind vorhanden** unter `docs/OePS/ZNS/`
|
||||
```
|
||||
docs/OePS/ZNS/
|
||||
├── PFERDE01.dat
|
||||
├── LIZENZ01.dat
|
||||
├── VEREIN01.dat
|
||||
└── RICHT01.dat
|
||||
```
|
||||
|
||||
### Service starten & Daten importieren
|
||||
|
||||
Jeden Service **einmalig** mit dem `dev`-Profil starten. Der Seeder läuft automatisch beim Start.
|
||||
|
||||
```bash
|
||||
# Pferde importieren (PFERDE01.dat → zns_horses)
|
||||
ZNS_DATA_DIR=$(pwd)/docs/OePS/ZNS \
|
||||
./gradlew :horses:horses-service:bootRun \
|
||||
--args='--spring.profiles.active=dev'
|
||||
|
||||
# Personen/Reiter importieren (LIZENZ01.dat → zns_persons)
|
||||
ZNS_DATA_DIR=$(pwd)/docs/OePS/ZNS \
|
||||
./gradlew :persons:persons-service:bootRun \
|
||||
--args='--spring.profiles.active=dev'
|
||||
|
||||
# Vereine importieren (VEREIN01.dat → zns_clubs)
|
||||
ZNS_DATA_DIR=$(pwd)/docs/OePS/ZNS \
|
||||
./gradlew :clubs:clubs-service:bootRun \
|
||||
--args='--spring.profiles.active=dev'
|
||||
|
||||
# Richter importieren (RICHT01.dat → zns_officials)
|
||||
ZNS_DATA_DIR=$(pwd)/docs/OePS/ZNS \
|
||||
./gradlew :officials:officials-service:bootRun \
|
||||
--args='--spring.profiles.active=dev'
|
||||
```
|
||||
|
||||
> **Hinweis:** Die Services können nach dem Import wieder gestoppt werden (`Ctrl+C`).
|
||||
> Die Daten bleiben in der PostgreSQL-Datenbank erhalten.
|
||||
|
||||
### Datenbank-Verbindung prüfen (optional)
|
||||
|
||||
```bash
|
||||
# Direkt via psql
|
||||
psql -h localhost -U meldestelle -d meldestelle -c "SELECT COUNT(*) FROM zns_horses;"
|
||||
psql -h localhost -U meldestelle -d meldestelle -c "SELECT COUNT(*) FROM zns_persons;"
|
||||
psql -h localhost -U meldestelle -d meldestelle -c "SELECT COUNT(*) FROM zns_clubs;"
|
||||
psql -h localhost -U meldestelle -d meldestelle -c "SELECT COUNT(*) FROM zns_officials;"
|
||||
```
|
||||
|
||||
### Datenbank zurücksetzen (neu seeden)
|
||||
|
||||
Falls die DB neu aufgesetzt werden muss (z.B. nach Schema-Änderungen):
|
||||
|
||||
```bash
|
||||
# Tabellen droppen (in psql)
|
||||
psql -h localhost -U meldestelle -d meldestelle -c "
|
||||
DROP TABLE IF EXISTS zns_horses, zns_persons, zns_clubs, zns_officials;
|
||||
"
|
||||
# Dann Services neu starten (siehe oben) – Tabellen werden automatisch neu angelegt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Für das Frontend: Wie kommen die Daten an?
|
||||
|
||||
### Aktueller Stand (Dev-Phase)
|
||||
|
||||
Die Daten liegen in der **lokalen PostgreSQL-DB**. Das Frontend kann sie über die jeweiligen
|
||||
Service-APIs abrufen – sobald die REST-Endpoints implementiert sind.
|
||||
|
||||
> **Nächster Schritt für das Frontend-Team:**
|
||||
> Die Services haben noch **keine REST-API** (kein `-api`-Modul aktiv).
|
||||
> Für schnellen Datenzugriff kann das Frontend direkt die DB abfragen (via Backend-Gateway)
|
||||
> oder die API-Module werden als nächstes aktiviert.
|
||||
|
||||
### Empfohlene Reihenfolge für die nächsten Schritte
|
||||
|
||||
| Priorität | Aufgabe | Service |
|
||||
|-----------|--------------------------------|--------------------------------------|
|
||||
| 🔴 Hoch | REST-API für Pferde-Abfrage | `horses-api` aktivieren |
|
||||
| 🔴 Hoch | REST-API für Personen-Abfrage | `persons-api` bauen |
|
||||
| 🟡 Mittel | REST-API für Vereine | `clubs-api` bauen |
|
||||
| 🟡 Mittel | REST-API für Richter | `officials-api` bauen |
|
||||
| 🟢 Später | Produktions-Importer (Phase 3) | `ZnsImportService` mit REST-Endpoint |
|
||||
|
||||
---
|
||||
|
||||
## Technische Details
|
||||
|
||||
### Gradle-Module (settings.gradle.kts)
|
||||
|
||||
```kotlin
|
||||
// Alle vier Services sind registriert:
|
||||
include(":horses:horses-domain")
|
||||
include(":horses:horses-infrastructure")
|
||||
include(":horses:horses-service")
|
||||
|
||||
include(":persons:persons-domain")
|
||||
include(":persons:persons-infrastructure")
|
||||
include(":persons:persons-service")
|
||||
|
||||
include(":clubs:clubs-domain")
|
||||
include(":clubs:clubs-infrastructure")
|
||||
include(":clubs:clubs-service")
|
||||
|
||||
include(":officials:officials-domain")
|
||||
include(":officials:officials-infrastructure")
|
||||
include(":officials:officials-service")
|
||||
```
|
||||
|
||||
### Build-Verifikation
|
||||
|
||||
```bash
|
||||
./gradlew \
|
||||
:horses:horses-service:compileKotlin \
|
||||
:horses:horses-infrastructure:compileKotlin \
|
||||
:horses:horses-domain:compileKotlinJvm \
|
||||
:persons:persons-domain:compileKotlinJvm \
|
||||
:persons:persons-infrastructure:compileKotlin \
|
||||
:persons:persons-service:compileKotlin \
|
||||
:clubs:clubs-domain:compileKotlinJvm \
|
||||
:clubs:clubs-infrastructure:compileKotlin \
|
||||
:clubs:clubs-service:compileKotlin \
|
||||
:officials:officials-domain:compileKotlinJvm \
|
||||
:officials:officials-infrastructure:compileKotlin \
|
||||
:officials:officials-service:compileKotlin
|
||||
# → BUILD SUCCESSFUL ✅
|
||||
```
|
||||
|
||||
### Bekannte Einschränkungen / ON HOLD
|
||||
|
||||
| Modul | Status | Grund |
|
||||
|----------------------|---------|----------------------------------------|
|
||||
| `horses-api` | ON HOLD | Ktor-basiert, wird separat aktiviert |
|
||||
| `horses-common` | ON HOLD | Veraltete API-Referenzen |
|
||||
| `entries-service` | ON HOLD | Pausiert bis Domain-Workshop (Phase 3) |
|
||||
| Produktions-Importer | GEPLANT | Phase 3 – nach Domain-Workshop |
|
||||
|
||||
---
|
||||
|
||||
## Erreichte Meilensteine dieser Session
|
||||
|
||||
- ✅ `ZnsDataSeeder` für `horses` gebaut (PFERDE01.dat → `zns_horses`)
|
||||
- ✅ `ZnsPersonSeeder` für `persons` gebaut (LIZENZ01.dat → `zns_persons`)
|
||||
- ✅ `ZnsClubSeeder` für `clubs` gebaut (VEREIN01.dat → `zns_clubs`)
|
||||
- ✅ `ZnsOfficialSeeder` für `officials` gebaut (RICHT01.dat → `zns_officials`)
|
||||
- ✅ Alle Tabellen einheitlich mit `zns_`-Präfix benannt
|
||||
- ✅ Alle vier Services kompilieren erfolgreich (BUILD SUCCESSFUL)
|
||||
- ✅ `settings.gradle.kts` vollständig aktualisiert
|
||||
BIN
docs/Neumarkt2026/Ausschreibung-26128_2026-03-23_11-05.png
Normal file
BIN
docs/Neumarkt2026/Ausschreibung-26128_2026-03-23_11-05.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 138 KiB |
BIN
docs/Neumarkt2026/Ausschreibung-26129_2026-03-23_11-05.png
Normal file
BIN
docs/Neumarkt2026/Ausschreibung-26129_2026-03-23_11-05.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 140 KiB |
1342
docs/OePS/ZNS/ISLANDPFERDE01.dat
Normal file
1342
docs/OePS/ZNS/ISLANDPFERDE01.dat
Normal file
File diff suppressed because it is too large
Load Diff
48753
docs/OePS/ZNS/LIZENZ01.dat
Normal file
48753
docs/OePS/ZNS/LIZENZ01.dat
Normal file
File diff suppressed because it is too large
Load Diff
21206
docs/OePS/ZNS/PFERDE01.dat
Normal file
21206
docs/OePS/ZNS/PFERDE01.dat
Normal file
File diff suppressed because it is too large
Load Diff
382
docs/OePS/ZNS/RICHT01.dat
Normal file
382
docs/OePS/ZNS/RICHT01.dat
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
Y135894Helmreich Marilena GA
|
||||
Y001832Paar Karl G3
|
||||
Y019911Wakonig Manfred GA
|
||||
Y200085Neunteufel Bernhard GA
|
||||
Y002112Resch Friedrich G3
|
||||
Y215034Stumpauer David GA
|
||||
Y002280Schimanek Peter K. GA
|
||||
Y002600St”glehner Karl GA
|
||||
Y002703Trausner Ulfried G1
|
||||
Y034065Weidinger Karl GA
|
||||
Y045697Riedl Andreas G3,GGB,IGCK
|
||||
Y058545Pulsinger Roland G2
|
||||
Y059962Gaber Franz GA
|
||||
Y065772Weberhofer Gernot G3,GGB
|
||||
Y006689Scheiblhofer Ernst GA
|
||||
Y702876Voglreiter Josef G2
|
||||
Y803485Auer Magdalena GA
|
||||
Y079182J„ger Martin GA
|
||||
Y009624Kuttelwascher Hubert GPC,PL3
|
||||
Y009386Giuli-Besso Eran P3
|
||||
Y000098Bauer Anton Martin P1
|
||||
Y801252Fuchs Hannes P1,PL1
|
||||
Y802543Mayer Mirjam PA
|
||||
Y803329Mayer Laura PA
|
||||
Y081099™ttl Silvia PA
|
||||
Y806356Schw„rzler Philipp P1
|
||||
Y072509Berger Hans-Peter PA
|
||||
Y000744Gschlenk Gregor GPC,P3,PL1
|
||||
Y069350Peikert Nina PA
|
||||
Y072094Bamberger-Stoiber Andreas GPC,P3,PL3
|
||||
Y067760Prilepeck Catharina P1
|
||||
Y061935Farthofer Isabella P1
|
||||
Y614933Riedler Verena Victoria PA
|
||||
Y061226Englbrecht Peter PA
|
||||
Y603891Birklbauer Gerald PA
|
||||
Y609325Jahn Bianca P1
|
||||
Y061003Kurt Yurdaer PA
|
||||
Y005923Feichtinger Franz PA
|
||||
Y053720Baischer Andreas P3
|
||||
Y056907Skiba-Hofinger Tobias PA
|
||||
Y053343Walter Helga P1
|
||||
Y052040K”rner Gert PA
|
||||
Y053206Englbrecht Roland PA
|
||||
Y051795Bernard Clemens PA
|
||||
Y004483Lindl Hannes P3
|
||||
Y420763Roámann Alexandra PA
|
||||
Y426920Mesaric Izidor PA
|
||||
Y428602Friedrich Leonhard PA
|
||||
Y424605Seehofer Sonja P2,PL1
|
||||
Y041645Schellenbauer Walter P3
|
||||
Y406513Imrek Lina P2,PL1
|
||||
Y042250Gr<EFBFBD>nling Josef P1
|
||||
Y423196Schemeth David PA
|
||||
Y423775Paier Lisa P1
|
||||
Y406364Mandl Robert P1
|
||||
Y000041Aschenbrenner Franz PA
|
||||
Y035245Steurer Klaus PA
|
||||
Y401287Thaller Carmen PA
|
||||
Y401482H<EFBFBD>bler David P3,PL1
|
||||
Y040279Schiermayr Franz GPC,P3,PL3
|
||||
Y000034Appe Adi GPC,PL3
|
||||
Y035473Madl Franz PL3
|
||||
Y037624Tschaitschmann Mario P1
|
||||
Y032936Simanek Andreas P3,PL1
|
||||
Y000033Andresek Thomas PL2
|
||||
Y003010Ziller Rupert P3
|
||||
Y031099Wohlmuther Hans Peter PA
|
||||
Y029642L”ffler Roland PA
|
||||
Y028034Oberwasserlechner Petra P2
|
||||
Y027084Krenn Armin P3,PL2
|
||||
Y002713Tschaitschmann Udo P3
|
||||
Y027110Sironi Tatjana PA
|
||||
Y024803Steinbrecher Michael P2
|
||||
Y024828Wallishauser Marcus PA
|
||||
Y002385Schorn Peter P2
|
||||
Y227595Stachl Daniel PA
|
||||
Y226220Roubal Nicole PA
|
||||
Y226992Klas Simone P1
|
||||
Y222808Lazar Philipp PA
|
||||
Y002211Salusek Andreas Christian P3,PL2
|
||||
Y021383Domaingo Ulrich P1
|
||||
Y002129Riedler Helmut P3
|
||||
Y021771Reitetschl„ger Kurt PL1
|
||||
Y211374Zak Klemens PA
|
||||
Y209067H”rler Nicole PA
|
||||
Y204075Hruschka Barbara P1
|
||||
Y200171Wippl Gerhard PA
|
||||
Y200606Sonntag Miriam P1
|
||||
Y001939Platzer Hans J”rg P3,PL1
|
||||
Y019467Sommerhuber Dieter P1
|
||||
Y166085Eidmann Sonja PA
|
||||
Y001034Bartoschek Ewald PA
|
||||
Y017141Lemmerer Manfred P3
|
||||
Y150399Novotny Noah PA
|
||||
Y001337Kr<EFBFBD>gl Erich P1
|
||||
Y013938Knoll Christian PA
|
||||
Y143167Imrek Christopher P1
|
||||
Y001065Kainz Sascha PA
|
||||
Y010902Rust Manfred PA
|
||||
Y011716Zainlinger Karl P3,PL1
|
||||
Y106622Schranz Christian P3
|
||||
Y101161Brandst„tter Markus P3,PL2
|
||||
X010128Zitterbart Rainer PI-A
|
||||
X100020Gaiotti Elena W
|
||||
X100101Salomon Rebekka W
|
||||
X010068Barosch Peter GAR-VO,VO
|
||||
X100704Vietor Ilja DIR,DPF,SPF,SS
|
||||
X001029Jarc Andreas W
|
||||
X001030Jarc Hilde GAR-DI,GAR-WE,IDIR,W,WIR
|
||||
X001023Meusburger Sabine DPF,DSGP,SPF,SS
|
||||
X001032Jauck Robert SPF,SS*
|
||||
X001061Kager Franz DPF,DSGP,GAR-SP,GAR-VS,SPF
|
||||
X001061Kager Franz SS*,VS
|
||||
X001196Koffmahn Elisabeth DPF,DSGP
|
||||
X012045Krippl Rudolf M
|
||||
X001067Kainz Gerhard F
|
||||
X011787Haiden Claus IPI
|
||||
X001112Keiblinger Brigitta DPF,DSGP,SPF,SS,VS,VSILEV1
|
||||
X011257Kotzab-Wallentin Elisabeth DL,DPF
|
||||
X113963Lechner Rachel MG-N
|
||||
X001149Kissmann Claudia DL,DPF,GAR-SP,SPF,SS*
|
||||
X001330Kroneder Aimee DL,DPF
|
||||
X132300Lindqvist Anna DIR
|
||||
X001307Kreupl Rudolf SPF,SS*
|
||||
X130813H”pfner Andreas PI-A
|
||||
X128482Sevensma Susan DM
|
||||
X128897Montgomery Patrick MG-N
|
||||
X122491Borstnar Nina PI-B
|
||||
X001227Komarek Reiner DPF,DSGP
|
||||
X012325Hannak Manuela W
|
||||
X001255Bachinger Eva Maria DPF,DSGP
|
||||
X148575De Wolff van Westerrode Eduard DIOR
|
||||
X148587Hansaghy Peter DIR
|
||||
X149744Trudenberger Ernst TREC
|
||||
X014346Schubert Renate DM,DPF,GAR-SP,SPF,SS*
|
||||
X139551Fore Liselotte DIOR
|
||||
X139552Mc Mullen Elizabeth DIOR
|
||||
X139553Hoevenaars Susan DIOR
|
||||
X014122Rathausky Elfriede DM,SS*
|
||||
X001416Lechner-Gebhard Jeannette DPF,DSGP
|
||||
X001428Leitenberger Hans GAR-SP,SPF,SS*
|
||||
X013938Knoll Christian SILEV1,SPF,SS
|
||||
X134784Prasser Ulrike DL,DPF,SL-K
|
||||
X001390Lang Thomas DILEV4,DPF,DSGP
|
||||
X013372Hess Ingrid DPF,DSGP,SPF,SS
|
||||
X013605M<EFBFBD>llner Elisabeth VO
|
||||
X000138Berger Anita SL
|
||||
X015274Moser Georg F
|
||||
X015404Thomanek Astrid DPF,DS,SPF,SS,VL
|
||||
X001541Mandl Victoire DPF,DSGP,M,SL,SPF
|
||||
X015831Semlitsch Gerhard SL-K
|
||||
X015865G”dl Anita IDIR
|
||||
X015139Haberl Gabriela SL,SPF
|
||||
X014528Klein Monika SILEV3,SPF
|
||||
X001598Mayr Ernst M
|
||||
X015995Winter Martina DL,DPF,SS
|
||||
X015999Schmalhardt B„rbel DL,DPF,SPF,SS
|
||||
X001724M<EFBFBD>ller Wolf G. DM,DPF,GAR-VS,SPF,SS,VSILEV3
|
||||
X000017Akinbiyi Akinkunmi DL,SL
|
||||
X017040Mayer-Rabl Ursula DM,DPF,M
|
||||
X001720M<EFBFBD>ller Lieselotte SILEV3,SPF,SS
|
||||
X172283Sanders van Gansewinkel Mariette DIOR
|
||||
X016178Kribernegg-Heuáerer Anita DL,SPF,SS
|
||||
X016047Payer Birgit DPF,DS
|
||||
X016613Hoyos Piet IPI
|
||||
X016661Auer Josef IPI
|
||||
X016715Sgustav Alexander IPI
|
||||
X169292Kjartansd¢ttir Hr”nn PI-A
|
||||
X017464Brandst„tter Marinda DL
|
||||
X015669Moáhammer Franz F
|
||||
X018457Alleithner Margit DIST
|
||||
X001847Kleindienst-Passweg Susanna DPF,DSGP,M,SPF,SS
|
||||
X001949Pointl Albert F,FV
|
||||
X019594Gasperl Regina TREC
|
||||
X019802Auinger Hans G<>nther PI-A
|
||||
X188835Christensen Kurt DIOR
|
||||
X188836McClain Kari DIOR
|
||||
X019911Wakonig Manfred TREC
|
||||
X020180Eichinger-Kniely Katrin Maria DM,GAR-VS,SS,VSILEV3
|
||||
X202084Zauner Andrea W
|
||||
X202872Dell Cornelia IVLEV3,VO
|
||||
X002008Prochazka Karin Verena SL,SPF
|
||||
X201125Dobretsberger Andrea F
|
||||
X201412Steindl Wolfgang F
|
||||
X201557Jungwirth Markus DILEV2,DPF,DSGP
|
||||
X020168Erjawetz Konstantin DM
|
||||
X020169Erjawetz Hans DM,DPF
|
||||
X203375Artner Denise VO
|
||||
X020377Cekoni-Hutter Beate TREC
|
||||
X203782Zimmermann Florian E. DS
|
||||
X002003Knasm<EFBFBD>ller-Prinz Ulrike DL,SL
|
||||
X002061Adelsberger-Streimelweger Renate DL,DPF,SL,SPF
|
||||
X206206Luschin Nikolaus VO
|
||||
X002085Regger Harald DPF,DSGP,SPF,SS
|
||||
X204100Strohmaier Alexandra DM
|
||||
X204715Lixl Ariane W
|
||||
X204872Kirchl Karoline W
|
||||
X205513Oskarsson Trausti IPI
|
||||
X209350Weber Viktoria PI-A
|
||||
X021023Schwendimann Claudia TREC
|
||||
X021040Tentschert Brigitte GAR-TR,TREC
|
||||
X220050Csandl Stefan IVLEV2,VO
|
||||
X217076Urbitsch Philipp W
|
||||
X021765Vormair Anita DM,DPF,SL,SPF
|
||||
X002129Riedler Helmut SL,SPF
|
||||
X021593Gampe-Benedict Ingrid TREC
|
||||
X220161Bejdl Ines GAR-SP,SILEV3,SPF
|
||||
X224333Huber-Tentschert Erich TREC
|
||||
X022491Vorraber Franz W
|
||||
X000234Breza Heinz DPF,DSGP
|
||||
X023405Hazrati Elfriede DPF,DSGP,SL,SPF
|
||||
X002273Schiele Barbara DPF,DSGP
|
||||
X227620Wagenlechner Janine VO
|
||||
X002385Schorn Peter DM,GAR-SP,SILEV3,SPF,VS
|
||||
X023862Auinger Monika DM
|
||||
X213643Chmelik-Wimmer Hannah PI-A
|
||||
X000240Brosig Renate DL,GAR-SP,SILEV1,SPF,SS*
|
||||
X002406Schwab Alice DILEV4,DPF,DSGP
|
||||
X002438Seisenbacher Gerhard DPF,DSGP,SPF,SS
|
||||
X002558Steinacher Alfred FIRK,GAR-FA
|
||||
X002567Steiner Christian DPF,DSGP,GAR-VS,SPF,SS,VSILEV3
|
||||
X024973Schulz Dennis WIRK
|
||||
X025751Kroneis Sonja W
|
||||
X002628Stuckel Marita DM,DPF,GAR-SP,SPF,SS*
|
||||
X025912Galler Marion SL,SPF
|
||||
X002670Thaler Claudia DPF,DSGP,VL
|
||||
X026712Grinschgl Harald GAR-DI,IDIR
|
||||
X002675Max-Theurer Elisabeth DILEV4,DPF
|
||||
X002681Thomasser Walter DM,DPF,SPF,SS,VL
|
||||
X002684Thunhart Hubertus F
|
||||
X027471Hengge Barbara W
|
||||
X002759Vlach Edith TREC
|
||||
X027110Sironi Tatjana DL,SPF,SS
|
||||
X000274Graf Deborah DPF,DS
|
||||
X002873Werni Walter M
|
||||
X028745Gaube Hannes GAR-WE,W,WIRK
|
||||
X028905Kriechbaumer Friedrich TREC
|
||||
X002901Zoher Petra DM,DPF,SPF,SS,WE-N
|
||||
X028551Zobl-Wessely Petra SL,SPF
|
||||
X000286Croy Ferdinand DM,DPF,GAR-VS,M,SPF,SS,VS
|
||||
X028177Migl Sandra WE-IR,WE-NR
|
||||
X029315Istinger Thomas SPF,SS
|
||||
X002946Wolf Heinz-Dieter DM,DPF,SPF,SS
|
||||
X029531Knotter Doris IVLEV4
|
||||
X031133Attorf Daniela DM,DPF,SL,SPF
|
||||
X312319Szedenik Wolfgang F
|
||||
X031534Pirhofer Rudolf F
|
||||
X003020Zobl-Wessely Peter DL,SILEV1,SPF,SS*
|
||||
X030809Kreiner Eva Maria GAR-VO,VO
|
||||
X029985Ferschl Alexandra DL
|
||||
X003007Ziemianski Margerita SPF,SS
|
||||
X032561Holler Esther PI-A
|
||||
X033190Schilling Peter SL
|
||||
X031875Galbavy Elisa DL
|
||||
X000032Andrejs Elisabeth DL,IVLEV3,SL
|
||||
X037884Wachs Michaela DM,DPF,SPF,SS,VL
|
||||
X036139Pramendorfer Monika F
|
||||
X036219Reichl-Rys Katharina DL
|
||||
X036330Zuschrader Michael TREC
|
||||
X037441Rappold Verena PI-B
|
||||
X037445Sager Gerrit PI-A
|
||||
X034935Petschina Rupert IPI
|
||||
X035110Schuster Alexandra DM,DPF
|
||||
X035245Steurer Klaus DPF,DSGP,GAR-SP,SPF,SS*
|
||||
X033908Brandner Tamara DS,SL
|
||||
X040322Fock Georg Klaus WIRK
|
||||
X003996Kronbichler Josef F
|
||||
X039986Reischauer Christian GAR-PI,IPI
|
||||
X038289Schmitz Vera DPF,DS,SPF,SS
|
||||
X038477Stangel-Sapergia Sandra W
|
||||
X038705Entner Daniela DIST,DM,DPF
|
||||
X038710Kriechbaumer Margarete TREC
|
||||
X038714Kriechbaumer Richard III TREC
|
||||
X038850Standeker Elke DPF,DS,M
|
||||
X039181Hoyos Thordis PI-C
|
||||
X039590Krausgruber Sylvia DS
|
||||
X039648Greisberger Veronika VO-K
|
||||
X038051Niederbichler Wilfried TREC
|
||||
X041308Rebel Manfred GAR-VO,VO
|
||||
X041490Cichini Gert DM,DPF,SL,SPF
|
||||
X004064Waldbauer-Schall Elisabeth DPF,DSGP
|
||||
X040513Grafenberger Daniela F-K
|
||||
X040578Wessely-Trupp Anja Luise DL
|
||||
X042404Stoiser Alois SL-K
|
||||
X424346Fries Gabriele DL,GAR-SP,SS*
|
||||
X424393Sagmeister Valentina W
|
||||
X423207Kehrer Gottfried TREC
|
||||
X423228Frank Mario TREC
|
||||
X406833Burg Ernst TREC
|
||||
X407534Hoffelner Daniela DM,DPF
|
||||
X040964Spadinger Frank GAR-VO,IVLEV4
|
||||
X420063K”berl Iris TREC
|
||||
X420286Audunsson Valdimar IPI
|
||||
X043474Vorraber Johanna W
|
||||
X004362Alleithner Peter DIST
|
||||
X004396Gruber Waltraud DPF,DSGP,GAR-SP,GAR-VS,M
|
||||
X004396Gruber Waltraud SPF,SS,VS
|
||||
X004407Hoyos Johannes GAR-PI,IPI
|
||||
X425469Egger Ulrike TREC
|
||||
X042626Quell Manuela TREC
|
||||
X000433Eisnecker Judith DM,DPF,GAR-VS,SPF,SS,VSILEV3
|
||||
X042944Morawitz Markus WIRK
|
||||
X004424Nischelwitzer Helga DL,SPF,SS,VL
|
||||
X421136Puschitz Gerald W
|
||||
X044785Ledl Gerhard PI-A
|
||||
X004483Lindl Hannes GAR-SP,SPF,SS
|
||||
X046298Sprinzl Gerhard FV
|
||||
X046370Wallner Karin DM,DPF
|
||||
X046422Kronfuss Katharina DL
|
||||
X045434Dorls Solveig PI-A
|
||||
X004600Steiner Fritz F,GAR-SP,SILEV3,SPF,WE-NR
|
||||
X005195Wachs Roland DM,DPF,GAR-SP,SPF,SS*
|
||||
X004762Federczuk Regina GAR-VO,VO
|
||||
X051859Kermer Christian GAR-VO,MG-N,VO
|
||||
X050532Haraldsson Iris Maria PI-B
|
||||
X050997Mantler Daniela GAR-WE,W
|
||||
X051188Kovac Sophie IPI
|
||||
X051547M<EFBFBD>ller Petra DM,DPF
|
||||
X048473Neumeister Walter DM
|
||||
X049187Kirsteuer Manuela W
|
||||
X500906Szente-Varga Paul TREC
|
||||
X050321Rust Wolfgang DL,SL-K
|
||||
X052570Rischnig Marion DPF,DSGP,SPF,SS
|
||||
X005369Eisenst„dter Hardy SS
|
||||
X005800Radl B„rbel FV
|
||||
X058534Scheifinger Wolfgang GAR-TR,TREC
|
||||
X055624Piffl Eva DL,SL-K
|
||||
X054407Fertsak Ulrike PI-A
|
||||
X005923Feichtinger Franz FIRK,FV
|
||||
X601798Grafenhofer Ilona DPF,DSGP
|
||||
X602994Piber Carina PI-B
|
||||
X055068Stickelberger Josef FIRK,FV,GAR-FA
|
||||
X061003Kurt Yurdaer SPF,SS
|
||||
X611405Hochstraáer Christina DM,SL,SPF
|
||||
X605981Hengster Michaela DL,SL,SPF
|
||||
X607134Trudenberger Alexandra TREC
|
||||
X060831Jelinski Susanne GAR-PI,IPI
|
||||
X608387Kriechbaumer Michael TREC
|
||||
X612921Pichler Andreas TREC
|
||||
X614119Auinger Gaby DM
|
||||
X061422Busam Petra GAR-PI,IPI
|
||||
X059973Scherz Hubert F
|
||||
X065600Mandl Veronika VO-K
|
||||
X062618Csar Wolfgang FIR
|
||||
X062792Oberhumer Julia DM
|
||||
X063165Ettl Bernhard F
|
||||
X063651Schellenbauer Sabrina SL-K
|
||||
X068608Kubinger Oliver IPI
|
||||
X068132Kalteis Katharina DL
|
||||
X006697Schlederer-Mayr Ingeborg PI-A
|
||||
X000672Gl<EFBFBD>ck-Ragnarsson Claudia PI-A
|
||||
X071891Tschojer Maddalena TREC
|
||||
X070029Tschojer Oswald TREC
|
||||
X007023Baumgarten Herwig TREC
|
||||
X006944Alber Karin VO
|
||||
X069497Skerget Ferdinand W
|
||||
X069080Bieber Heinz FIRK,FV,GAR-FA
|
||||
X703538Jelinek Yvonne DL,SL-K
|
||||
X703857Br”tzner Michaela DL
|
||||
X070905Montgomery Verena MG-N
|
||||
X071454Trudenberger Susanne TREC
|
||||
X071528Zrnjevic Patrick PI-A
|
||||
X007173Choc Petra DPF,DS,SL,SPF
|
||||
X074590M”schl Sylvia DL,DPF,SL,SPF
|
||||
X075121Res Michaela TREC
|
||||
X000741Gr<EFBFBD>ner Wolfgang DPF,DS,SL,SPF
|
||||
X007778Aigner Harald FV
|
||||
X007817Holzleitner-Schinko Ines DPF,DSGP
|
||||
X078316Strilka Julia DM
|
||||
X008074Seipel Ursula DL,DPF,GAR-SP,M,SILEV3,SPF
|
||||
X000093Bauer Christian DM,DPF
|
||||
X084296Matt Susanna VO
|
||||
X088418Sachslehner Stephanie SL-K
|
||||
X089040Oschounig Natascha W
|
||||
X000909Hoffmann Ludwig DPF,DSGP,SPF,SS
|
||||
X000875Hiesel Gerhard A. GAR-SP,SPF,SS
|
||||
X087580Gstinig Silvia TREC
|
||||
X811174Gastl Petra PI-B
|
||||
X008116Hudec-Semeleder Elisabeth DILEV2,DPF,DSGP
|
||||
X810139K”ck Daniel DL-K
|
||||
X800549Thomann Klaus SPF,SS
|
||||
X009947Katschker Sylvia GAR-WE,WIR
|
||||
1427
docs/OePS/ZNS/VEREIN01.dat
Normal file
1427
docs/OePS/ZNS/VEREIN01.dat
Normal file
File diff suppressed because it is too large
Load Diff
1379
docs/OePS/ZNS/VOLT01.dat
Normal file
1379
docs/OePS/ZNS/VOLT01.dat
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -73,6 +73,52 @@ include(":backend:services:entries:entries-api")
|
|||
// Code liegt im Branch: feature/entries-service
|
||||
// include(":backend:services:entries:entries-service")
|
||||
|
||||
// --- HORSES (Pferde-Verwaltung) ---
|
||||
// Namespace ':horses:*' damit projects.horses.* Accessors funktionieren
|
||||
include(":horses")
|
||||
project(":horses").projectDir = file("backend/services/horses")
|
||||
include(":horses:horses-domain")
|
||||
// horses-common: ON HOLD – veraltete API-Referenzen
|
||||
// include(":horses:horses-common")
|
||||
include(":horses:horses-infrastructure")
|
||||
// horses-api: ON HOLD – Ktor-basiert, wird separat aktiviert
|
||||
// include(":horses:horses-api")
|
||||
include(":horses:horses-service")
|
||||
project(":horses:horses-domain").projectDir = file("backend/services/horses/horses-domain")
|
||||
// project(":horses:horses-common").projectDir = file("backend/services/horses/horses-common")
|
||||
project(":horses:horses-infrastructure").projectDir = file("backend/services/horses/horses-infrastructure")
|
||||
project(":horses:horses-service").projectDir = file("backend/services/horses/horses-service")
|
||||
|
||||
// --- PERSONS (Personen/Reiter) ---
|
||||
include(":persons")
|
||||
project(":persons").projectDir = file("backend/services/persons")
|
||||
include(":persons:persons-domain")
|
||||
include(":persons:persons-infrastructure")
|
||||
include(":persons:persons-service")
|
||||
project(":persons:persons-domain").projectDir = file("backend/services/persons/persons-domain")
|
||||
project(":persons:persons-infrastructure").projectDir = file("backend/services/persons/persons-infrastructure")
|
||||
project(":persons:persons-service").projectDir = file("backend/services/persons/persons-service")
|
||||
|
||||
// --- CLUBS (Vereine) ---
|
||||
include(":clubs")
|
||||
project(":clubs").projectDir = file("backend/services/clubs")
|
||||
include(":clubs:clubs-domain")
|
||||
include(":clubs:clubs-infrastructure")
|
||||
include(":clubs:clubs-service")
|
||||
project(":clubs:clubs-domain").projectDir = file("backend/services/clubs/clubs-domain")
|
||||
project(":clubs:clubs-infrastructure").projectDir = file("backend/services/clubs/clubs-infrastructure")
|
||||
project(":clubs:clubs-service").projectDir = file("backend/services/clubs/clubs-service")
|
||||
|
||||
// --- OFFICIALS (Richter) ---
|
||||
include(":officials")
|
||||
project(":officials").projectDir = file("backend/services/officials")
|
||||
include(":officials:officials-domain")
|
||||
include(":officials:officials-infrastructure")
|
||||
include(":officials:officials-service")
|
||||
project(":officials:officials-domain").projectDir = file("backend/services/officials/officials-domain")
|
||||
project(":officials:officials-infrastructure").projectDir = file("backend/services/officials/officials-infrastructure")
|
||||
project(":officials:officials-service").projectDir = file("backend/services/officials/officials-service")
|
||||
|
||||
// --- PING (Ping Service) ---
|
||||
include(":backend:services:ping:ping-service")
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user