feat(masterdata): add controllers, services, and repositories for Reiter, Horse, and Verein domains
- Introduced entities `ReiterController`, `HorseController`, and `VereinController`, with associated REST routes. - Implemented upsert functionality for `Reiter`, `Horse`, and `Verein` repositories. - Added services for `Altersklasse` calculations and integrated them into the domain layer. - Updated database schema to include `ReiterTable`, `HorseTable`, `VereinTable`, and `FunktionaerTable`. - Refactored `masterdataApiModule` to register new domain controllers. - Adjusted Ktor server and Spring configurations to support new domains. Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
+11
-8
@@ -1,24 +1,24 @@
|
||||
package at.mocode.masterdata.api
|
||||
|
||||
import at.mocode.masterdata.api.plugins.IdempotencyPlugin
|
||||
import at.mocode.masterdata.api.rest.AltersklasseController
|
||||
import at.mocode.masterdata.api.rest.BundeslandController
|
||||
import at.mocode.masterdata.api.rest.CountryController
|
||||
import at.mocode.masterdata.api.rest.PlatzController
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.routing.routing
|
||||
import at.mocode.masterdata.api.rest.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
/**
|
||||
* Ktor-Modul für den Masterdata-Bounded-Context.
|
||||
*
|
||||
* - Installiert das IdempotencyPlugin (Header „Idempotency-Key“) global.
|
||||
* - Registriert alle Masterdata-Routen (Country, Bundesland, Altersklasse, Platz).
|
||||
* - Registriert alle Masterdata-Routen (Country, Bundesland, Altersklasse, Platz, Reiter, Horse, Verein).
|
||||
*/
|
||||
fun Application.masterdataApiModule(
|
||||
countryController: CountryController,
|
||||
bundeslandController: BundeslandController,
|
||||
altersklasseController: AltersklasseController,
|
||||
platzController: PlatzController
|
||||
platzController: PlatzController,
|
||||
reiterController: ReiterController,
|
||||
horseController: HorseController,
|
||||
vereinController: VereinController
|
||||
) {
|
||||
// Installiere das Idempotency-Plugin global für alle Routen
|
||||
IdempotencyPlugin.install(this)
|
||||
@@ -29,5 +29,8 @@ fun Application.masterdataApiModule(
|
||||
with(bundeslandController) { registerRoutes() }
|
||||
with(altersklasseController) { registerRoutes() }
|
||||
with(platzController) { registerRoutes() }
|
||||
with(reiterController) { registerRoutes() }
|
||||
with(horseController) { registerRoutes() }
|
||||
with(vereinController) { registerRoutes() }
|
||||
}
|
||||
}
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
package at.mocode.masterdata.api.rest
|
||||
|
||||
import at.mocode.core.domain.serialization.InstantSerializer
|
||||
import at.mocode.core.domain.serialization.LocalDateSerializer
|
||||
import at.mocode.masterdata.domain.model.DomPferd
|
||||
import at.mocode.masterdata.domain.repository.HorseRepository
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Instant
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Controller für Pferde-bezogene REST-Endpunkte.
|
||||
*/
|
||||
class HorseController(private val horseRepository: HorseRepository) {
|
||||
|
||||
@Serializable
|
||||
data class HorseDto(
|
||||
val pferdId: String,
|
||||
val pferdeName: String,
|
||||
val geschlecht: String,
|
||||
@Serializable(with = LocalDateSerializer::class)
|
||||
val geburtsdatum: LocalDate? = null,
|
||||
val rasse: String? = null,
|
||||
val lebensnummer: String? = null,
|
||||
val oepsNummer: String? = null,
|
||||
val feiNummer: String? = null,
|
||||
val istAktiv: Boolean,
|
||||
@Serializable(with = InstantSerializer::class)
|
||||
val updatedAt: Instant
|
||||
)
|
||||
|
||||
fun Route.registerRoutes() {
|
||||
route("/horse") {
|
||||
/**
|
||||
* Sucht Pferde nach Name.
|
||||
*/
|
||||
get("/search") {
|
||||
val query = call.request.queryParameters["q"] ?: ""
|
||||
val results = horseRepository.findByName(query)
|
||||
call.respond(results.map { it.toDto() })
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruft ein spezifisches Pferd ab.
|
||||
*/
|
||||
get("/{id}") {
|
||||
val idStr = call.parameters["id"] ?: return@get call.respond(HttpStatusCode.BadRequest)
|
||||
val id = try {
|
||||
Uuid.parse(idStr)
|
||||
} catch (e: Exception) {
|
||||
return@get call.respond(HttpStatusCode.BadRequest)
|
||||
}
|
||||
|
||||
val pferd = horseRepository.findById(id)
|
||||
if (pferd != null) {
|
||||
call.respond(pferd.toDto())
|
||||
} else {
|
||||
call.respond(HttpStatusCode.NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sucht ein Pferd nach seiner Lebensnummer.
|
||||
*/
|
||||
get("/lebensnummer/{nr}") {
|
||||
val nr = call.parameters["nr"] ?: return@get call.respond(HttpStatusCode.BadRequest)
|
||||
val pferd = horseRepository.findByLebensnummer(nr)
|
||||
if (pferd != null) {
|
||||
call.respond(pferd.toDto())
|
||||
} else {
|
||||
call.respond(HttpStatusCode.NotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DomPferd.toDto() = HorseDto(
|
||||
pferdId = pferdId.toString(),
|
||||
pferdeName = pferdeName,
|
||||
geschlecht = geschlecht.name,
|
||||
geburtsdatum = geburtsdatum,
|
||||
rasse = rasse,
|
||||
lebensnummer = lebensnummer,
|
||||
oepsNummer = oepsNummer,
|
||||
feiNummer = feiNummer,
|
||||
istAktiv = istAktiv,
|
||||
updatedAt = updatedAt
|
||||
)
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
package at.mocode.masterdata.api.rest
|
||||
|
||||
import at.mocode.core.domain.serialization.InstantSerializer
|
||||
import at.mocode.core.domain.serialization.LocalDateSerializer
|
||||
import at.mocode.masterdata.domain.model.DomReiter
|
||||
import at.mocode.masterdata.domain.repository.ReiterRepository
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Instant
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Controller für Reiter-bezogene REST-Endpunkte.
|
||||
*/
|
||||
class ReiterController(private val reiterRepository: ReiterRepository) {
|
||||
|
||||
@Serializable
|
||||
data class ReiterDto(
|
||||
val reiterId: String,
|
||||
val satznummer: String,
|
||||
val nachname: String,
|
||||
val vorname: String,
|
||||
@Serializable(with = LocalDateSerializer::class)
|
||||
val geburtsdatum: LocalDate? = null,
|
||||
val lizenzNummer: String? = null,
|
||||
val lizenzKlasse: String,
|
||||
val startkartAktiv: Boolean,
|
||||
val nation: String? = null,
|
||||
val vereinsName: String? = null,
|
||||
@Serializable(with = InstantSerializer::class)
|
||||
val updatedAt: Instant
|
||||
)
|
||||
|
||||
fun Route.registerRoutes() {
|
||||
route("/reiter") {
|
||||
/**
|
||||
* Sucht Reiter nach Name oder Satznummer.
|
||||
*/
|
||||
get("/search") {
|
||||
val query = call.request.queryParameters["q"] ?: ""
|
||||
val results = reiterRepository.findByName(query)
|
||||
call.respond(results.map { it.toDto() })
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruft einen spezifischen Reiter ab.
|
||||
*/
|
||||
get("/{id}") {
|
||||
val idStr = call.parameters["id"] ?: return@get call.respond(HttpStatusCode.BadRequest)
|
||||
val id = try {
|
||||
Uuid.parse(idStr)
|
||||
} catch (e: Exception) {
|
||||
return@get call.respond(HttpStatusCode.BadRequest)
|
||||
}
|
||||
|
||||
val reiter = reiterRepository.findById(id)
|
||||
if (reiter != null) {
|
||||
call.respond(reiter.toDto())
|
||||
} else {
|
||||
call.respond(HttpStatusCode.NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sucht einen Reiter nach seiner Satznummer.
|
||||
*/
|
||||
get("/satznummer/{nr}") {
|
||||
val nr = call.parameters["nr"] ?: return@get call.respond(HttpStatusCode.BadRequest)
|
||||
val reiter = reiterRepository.findBySatznummer(nr)
|
||||
if (reiter != null) {
|
||||
call.respond(reiter.toDto())
|
||||
} else {
|
||||
call.respond(HttpStatusCode.NotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DomReiter.toDto() = ReiterDto(
|
||||
reiterId = reiterId.toString(),
|
||||
satznummer = satznummer,
|
||||
nachname = nachname,
|
||||
vorname = vorname,
|
||||
geburtsdatum = geburtsdatum,
|
||||
lizenzNummer = lizenzNummer,
|
||||
lizenzKlasse = lizenzKlasse.name,
|
||||
startkartAktiv = startkartAktiv,
|
||||
nation = nation,
|
||||
vereinsName = vereinsName,
|
||||
updatedAt = updatedAt
|
||||
)
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
package at.mocode.masterdata.api.rest
|
||||
|
||||
import at.mocode.core.domain.serialization.InstantSerializer
|
||||
import at.mocode.masterdata.domain.model.DomVerein
|
||||
import at.mocode.masterdata.domain.repository.VereinRepository
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Controller für Vereins-bezogene REST-Endpunkte.
|
||||
*/
|
||||
class VereinController(private val vereinRepository: VereinRepository) {
|
||||
|
||||
@Serializable
|
||||
data class VereinDto(
|
||||
val vereinId: String,
|
||||
val vereinsNummer: String,
|
||||
val name: String,
|
||||
val kurzname: String? = null,
|
||||
val bundesland: String,
|
||||
val ort: String? = null,
|
||||
val istVeranstalter: Boolean,
|
||||
val istAktiv: Boolean,
|
||||
@Serializable(with = InstantSerializer::class)
|
||||
val updatedAt: kotlin.time.Instant
|
||||
)
|
||||
|
||||
fun Route.registerRoutes() {
|
||||
route("/verein") {
|
||||
/**
|
||||
* Sucht Vereine nach Name oder Kurzname.
|
||||
*/
|
||||
get("/search") {
|
||||
val query = call.request.queryParameters["q"] ?: ""
|
||||
val results = vereinRepository.findByName(query)
|
||||
call.respond(results.map { it.toDto() })
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruft einen spezifischen Verein ab.
|
||||
*/
|
||||
get("/{id}") {
|
||||
val idStr = call.parameters["id"] ?: return@get call.respond(HttpStatusCode.BadRequest)
|
||||
val id = try {
|
||||
Uuid.parse(idStr)
|
||||
} catch (e: Exception) {
|
||||
return@get call.respond(HttpStatusCode.BadRequest)
|
||||
}
|
||||
|
||||
val verein = vereinRepository.findById(id)
|
||||
if (verein != null) {
|
||||
call.respond(verein.toDto())
|
||||
} else {
|
||||
call.respond(HttpStatusCode.NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sucht einen Verein nach seiner Vereinsnummer.
|
||||
*/
|
||||
get("/nummer/{nr}") {
|
||||
val nr = call.parameters["nr"] ?: return@get call.respond(HttpStatusCode.BadRequest)
|
||||
val verein = vereinRepository.findByVereinsNummer(nr)
|
||||
if (verein != null) {
|
||||
call.respond(verein.toDto())
|
||||
} else {
|
||||
call.respond(HttpStatusCode.NotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DomVerein.toDto() = VereinDto(
|
||||
vereinId = vereinId.toString(),
|
||||
vereinsNummer = vereinsNummer,
|
||||
name = name,
|
||||
kurzname = kurzname,
|
||||
bundesland = bundesland ?: "",
|
||||
ort = ort,
|
||||
istVeranstalter = istVeranstalter,
|
||||
istAktiv = istAktiv,
|
||||
updatedAt = updatedAt
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user