Umbau zu SCS
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.multiplatform)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm()
|
||||
js(IR) {
|
||||
browser()
|
||||
nodejs()
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
implementation(project(":shared-kernel"))
|
||||
implementation(project(":master-data"))
|
||||
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.kotlinx.datetime)
|
||||
implementation(libs.uuid)
|
||||
}
|
||||
|
||||
commonTest.dependencies {
|
||||
implementation(libs.kotlin.test)
|
||||
}
|
||||
|
||||
jvmMain.dependencies {
|
||||
implementation(libs.exposed.core)
|
||||
implementation(libs.exposed.dao)
|
||||
implementation(libs.exposed.jdbc)
|
||||
implementation(libs.exposed.kotlinDatetime)
|
||||
implementation(libs.ktor.server.core)
|
||||
implementation(libs.ktor.server.contentNegotiation)
|
||||
implementation(libs.ktor.server.serializationKotlinxJson)
|
||||
}
|
||||
|
||||
jsMain.dependencies {
|
||||
// Kotlin React dependencies with explicit versions
|
||||
implementation("org.jetbrains.kotlin-wrappers:kotlin-react:${libs.versions.kotlinWrappers.get()}")
|
||||
implementation("org.jetbrains.kotlin-wrappers:kotlin-emotion:${libs.versions.kotlinWrappers.get()}")
|
||||
|
||||
// NPM dependencies
|
||||
implementation(npm("react", "18.2.0"))
|
||||
implementation(npm("react-dom", "18.2.0"))
|
||||
implementation(npm("react-to-web-component", "2.0.2"))
|
||||
}
|
||||
}
|
||||
}
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
package at.mocode.members.application.usecase
|
||||
|
||||
import at.mocode.dto.base.ApiResponse
|
||||
import at.mocode.dto.base.ErrorDto
|
||||
import at.mocode.members.domain.model.DomPerson
|
||||
import at.mocode.members.domain.repository.PersonRepository
|
||||
import at.mocode.members.domain.repository.VereinRepository
|
||||
import at.mocode.members.domain.service.MasterDataService
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
/**
|
||||
* Use case for creating a new person in the member management context.
|
||||
*
|
||||
* This use case handles the business logic for person creation including:
|
||||
* - Validation of input data
|
||||
* - Checking for duplicate OEPS Satznummer
|
||||
* - Validation of referenced entities (club, country)
|
||||
* - Person creation and persistence
|
||||
*/
|
||||
class CreatePersonUseCase(
|
||||
private val personRepository: PersonRepository,
|
||||
private val vereinRepository: VereinRepository,
|
||||
private val masterDataService: MasterDataService
|
||||
) {
|
||||
|
||||
/**
|
||||
* Request data for creating a person.
|
||||
*/
|
||||
data class CreatePersonRequest(
|
||||
val oepsSatzNr: String?,
|
||||
val nachname: String,
|
||||
val vorname: String,
|
||||
val titel: String? = null,
|
||||
val geburtsdatum: kotlinx.datetime.LocalDate? = null,
|
||||
val geschlechtE: at.mocode.enums.GeschlechtE? = null,
|
||||
val nationalitaetLandId: com.benasher44.uuid.Uuid? = null,
|
||||
val feiId: String? = null,
|
||||
val telefon: String? = null,
|
||||
val email: String? = null,
|
||||
val strasse: String? = null,
|
||||
val plz: String? = null,
|
||||
val ort: String? = null,
|
||||
val adresszusatzZusatzinfo: String? = null,
|
||||
val stammVereinId: com.benasher44.uuid.Uuid? = null,
|
||||
val mitgliedsNummerBeiStammVerein: String? = null,
|
||||
val istGesperrt: Boolean = false,
|
||||
val sperrGrund: String? = null,
|
||||
val altersklasseOepsCodeRaw: String? = null,
|
||||
val istJungerReiterOepsFlag: Boolean = false,
|
||||
val kaderStatusOepsRaw: String? = null,
|
||||
val datenQuelle: at.mocode.enums.DatenQuelleE = at.mocode.enums.DatenQuelleE.MANUELL,
|
||||
val notizenIntern: String? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Response data for person creation.
|
||||
*/
|
||||
data class CreatePersonResponse(
|
||||
val person: DomPerson
|
||||
)
|
||||
|
||||
/**
|
||||
* Executes the create person use case.
|
||||
*
|
||||
* @param request The person creation request
|
||||
* @return ApiResponse containing the created person or error information
|
||||
*/
|
||||
suspend fun execute(request: CreatePersonRequest): ApiResponse<CreatePersonResponse> {
|
||||
try {
|
||||
// Validate required fields
|
||||
val validationErrors = validateRequest(request)
|
||||
if (validationErrors.isNotEmpty()) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "VALIDATION_ERROR",
|
||||
message = "Invalid input data",
|
||||
details = validationErrors
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Check for duplicate OEPS Satznummer
|
||||
if (request.oepsSatzNr != null) {
|
||||
if (personRepository.existsByOepsSatzNr(request.oepsSatzNr)) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "DUPLICATE_OEPS_SATZNR",
|
||||
message = "A person with this OEPS Satznummer already exists"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate referenced entities
|
||||
val entityValidationErrors = validateReferencedEntities(request)
|
||||
if (entityValidationErrors.isNotEmpty()) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INVALID_REFERENCES",
|
||||
message = "Referenced entities not found",
|
||||
details = entityValidationErrors
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Create the person
|
||||
val person = DomPerson(
|
||||
oepsSatzNr = request.oepsSatzNr,
|
||||
nachname = request.nachname,
|
||||
vorname = request.vorname,
|
||||
titel = request.titel,
|
||||
geburtsdatum = request.geburtsdatum,
|
||||
geschlechtE = request.geschlechtE,
|
||||
nationalitaetLandId = request.nationalitaetLandId,
|
||||
feiId = request.feiId,
|
||||
telefon = request.telefon,
|
||||
email = request.email,
|
||||
strasse = request.strasse,
|
||||
plz = request.plz,
|
||||
ort = request.ort,
|
||||
adresszusatzZusatzinfo = request.adresszusatzZusatzinfo,
|
||||
stammVereinId = request.stammVereinId,
|
||||
mitgliedsNummerBeiStammVerein = request.mitgliedsNummerBeiStammVerein,
|
||||
istGesperrt = request.istGesperrt,
|
||||
sperrGrund = request.sperrGrund,
|
||||
altersklasseOepsCodeRaw = request.altersklasseOepsCodeRaw,
|
||||
istJungerReiterOepsFlag = request.istJungerReiterOepsFlag,
|
||||
kaderStatusOepsRaw = request.kaderStatusOepsRaw,
|
||||
datenQuelle = request.datenQuelle,
|
||||
notizenIntern = request.notizenIntern,
|
||||
createdAt = Clock.System.now(),
|
||||
updatedAt = Clock.System.now()
|
||||
)
|
||||
|
||||
// Save the person
|
||||
val savedPerson = personRepository.save(person)
|
||||
|
||||
return ApiResponse(
|
||||
success = true,
|
||||
data = CreatePersonResponse(savedPerson)
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while creating the person: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateRequest(request: CreatePersonRequest): Map<String, String> {
|
||||
val errors = mutableMapOf<String, String>()
|
||||
|
||||
if (request.nachname.isBlank()) {
|
||||
errors["nachname"] = "Last name is required"
|
||||
}
|
||||
|
||||
if (request.vorname.isBlank()) {
|
||||
errors["vorname"] = "First name is required"
|
||||
}
|
||||
|
||||
if (request.oepsSatzNr != null && request.oepsSatzNr.length != 6) {
|
||||
errors["oepsSatzNr"] = "OEPS Satznummer must be exactly 6 digits"
|
||||
}
|
||||
|
||||
if (request.email != null && !isValidEmail(request.email)) {
|
||||
errors["email"] = "Invalid email format"
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
private suspend fun validateReferencedEntities(request: CreatePersonRequest): Map<String, String> {
|
||||
val errors = mutableMapOf<String, String>()
|
||||
|
||||
// Validate club reference
|
||||
if (request.stammVereinId != null) {
|
||||
val verein = vereinRepository.findById(request.stammVereinId)
|
||||
if (verein == null) {
|
||||
errors["stammVereinId"] = "Referenced club not found"
|
||||
}
|
||||
}
|
||||
|
||||
// Validate country reference
|
||||
if (request.nationalitaetLandId != null) {
|
||||
if (!masterDataService.countryExists(request.nationalitaetLandId)) {
|
||||
errors["nationalitaetLandId"] = "Referenced country not found"
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
private fun isValidEmail(email: String): Boolean {
|
||||
return email.contains("@") && email.contains(".")
|
||||
}
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
package at.mocode.members.application.usecase
|
||||
|
||||
import at.mocode.dto.base.ApiResponse
|
||||
import at.mocode.dto.base.ErrorDto
|
||||
import at.mocode.members.domain.model.DomVerein
|
||||
import at.mocode.members.domain.repository.VereinRepository
|
||||
import at.mocode.members.domain.service.MasterDataService
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
/**
|
||||
* Use case for creating a new club/association in the member management context.
|
||||
*
|
||||
* This use case handles the business logic for club creation including:
|
||||
* - Validation of input data
|
||||
* - Checking for duplicate OEPS Vereinsnummer
|
||||
* - Validation of referenced entities (country, state)
|
||||
* - Club creation and persistence
|
||||
*/
|
||||
class CreateVereinUseCase(
|
||||
private val vereinRepository: VereinRepository,
|
||||
private val masterDataService: MasterDataService
|
||||
) {
|
||||
|
||||
/**
|
||||
* Request data for creating a club.
|
||||
*/
|
||||
data class CreateVereinRequest(
|
||||
val oepsVereinsNr: String?,
|
||||
val name: String,
|
||||
val kuerzel: String? = null,
|
||||
val adresseStrasse: String? = null,
|
||||
val plz: String? = null,
|
||||
val ort: String? = null,
|
||||
val bundeslandId: com.benasher44.uuid.Uuid? = null,
|
||||
val landId: com.benasher44.uuid.Uuid,
|
||||
val emailAllgemein: String? = null,
|
||||
val telefonAllgemein: String? = null,
|
||||
val webseiteUrl: String? = null,
|
||||
val datenQuelle: at.mocode.enums.DatenQuelleE = at.mocode.enums.DatenQuelleE.MANUELL,
|
||||
val notizenIntern: String? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Response data for club creation.
|
||||
*/
|
||||
data class CreateVereinResponse(
|
||||
val verein: DomVerein
|
||||
)
|
||||
|
||||
/**
|
||||
* Executes the create club use case.
|
||||
*
|
||||
* @param request The club creation request
|
||||
* @return ApiResponse containing the created club or error information
|
||||
*/
|
||||
suspend fun execute(request: CreateVereinRequest): ApiResponse<CreateVereinResponse> {
|
||||
try {
|
||||
// Validate required fields
|
||||
val validationErrors = validateRequest(request)
|
||||
if (validationErrors.isNotEmpty()) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "VALIDATION_ERROR",
|
||||
message = "Invalid input data",
|
||||
details = validationErrors
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Check for duplicate OEPS Vereinsnummer
|
||||
if (request.oepsVereinsNr != null) {
|
||||
if (vereinRepository.existsByOepsVereinsNr(request.oepsVereinsNr)) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "DUPLICATE_OEPS_VEREINSNR",
|
||||
message = "A club with this OEPS Vereinsnummer already exists"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate referenced entities
|
||||
val entityValidationErrors = validateReferencedEntities(request)
|
||||
if (entityValidationErrors.isNotEmpty()) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INVALID_REFERENCES",
|
||||
message = "Referenced entities not found",
|
||||
details = entityValidationErrors
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Create the club
|
||||
val verein = DomVerein(
|
||||
oepsVereinsNr = request.oepsVereinsNr,
|
||||
name = request.name,
|
||||
kuerzel = request.kuerzel,
|
||||
adresseStrasse = request.adresseStrasse,
|
||||
plz = request.plz,
|
||||
ort = request.ort,
|
||||
bundeslandId = request.bundeslandId,
|
||||
landId = request.landId,
|
||||
emailAllgemein = request.emailAllgemein,
|
||||
telefonAllgemein = request.telefonAllgemein,
|
||||
webseiteUrl = request.webseiteUrl,
|
||||
datenQuelle = request.datenQuelle,
|
||||
notizenIntern = request.notizenIntern,
|
||||
createdAt = Clock.System.now(),
|
||||
updatedAt = Clock.System.now()
|
||||
)
|
||||
|
||||
// Save the club
|
||||
val savedVerein = vereinRepository.save(verein)
|
||||
|
||||
return ApiResponse(
|
||||
success = true,
|
||||
data = CreateVereinResponse(savedVerein)
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while creating the club: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateRequest(request: CreateVereinRequest): Map<String, String> {
|
||||
val errors = mutableMapOf<String, String>()
|
||||
|
||||
if (request.name.isBlank()) {
|
||||
errors["name"] = "Club name is required"
|
||||
}
|
||||
|
||||
if (request.oepsVereinsNr != null && request.oepsVereinsNr.length != 4) {
|
||||
errors["oepsVereinsNr"] = "OEPS Vereinsnummer must be exactly 4 digits"
|
||||
}
|
||||
|
||||
if (request.emailAllgemein != null && !isValidEmail(request.emailAllgemein)) {
|
||||
errors["emailAllgemein"] = "Invalid email format"
|
||||
}
|
||||
|
||||
if (request.webseiteUrl != null && !isValidUrl(request.webseiteUrl)) {
|
||||
errors["webseiteUrl"] = "Invalid URL format"
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
private suspend fun validateReferencedEntities(request: CreateVereinRequest): Map<String, String> {
|
||||
val errors = mutableMapOf<String, String>()
|
||||
|
||||
// Validate country reference (required)
|
||||
if (!masterDataService.countryExists(request.landId)) {
|
||||
errors["landId"] = "Referenced country not found"
|
||||
}
|
||||
|
||||
// Validate state reference (optional)
|
||||
if (request.bundeslandId != null) {
|
||||
if (!masterDataService.stateExists(request.bundeslandId)) {
|
||||
errors["bundeslandId"] = "Referenced state not found"
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
private fun isValidEmail(email: String): Boolean {
|
||||
return email.contains("@") && email.contains(".")
|
||||
}
|
||||
|
||||
private fun isValidUrl(url: String): Boolean {
|
||||
return url.startsWith("http://") || url.startsWith("https://")
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
package at.mocode.members.application.usecase
|
||||
|
||||
import at.mocode.dto.base.ApiResponse
|
||||
import at.mocode.dto.base.ErrorDto
|
||||
import at.mocode.members.domain.model.DomPerson
|
||||
import at.mocode.members.domain.repository.PersonRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Use case for retrieving person information from the member management context.
|
||||
*
|
||||
* This use case handles the business logic for person retrieval including:
|
||||
* - Finding persons by ID or OEPS Satznummer
|
||||
* - Searching persons by name
|
||||
* - Retrieving persons by club membership
|
||||
* - Listing active persons with pagination
|
||||
*/
|
||||
class GetPersonUseCase(
|
||||
private val personRepository: PersonRepository
|
||||
) {
|
||||
|
||||
/**
|
||||
* Request data for getting a person by ID.
|
||||
*/
|
||||
data class GetPersonByIdRequest(
|
||||
val personId: Uuid
|
||||
)
|
||||
|
||||
/**
|
||||
* Request data for getting a person by OEPS Satznummer.
|
||||
*/
|
||||
data class GetPersonByOepsSatzNrRequest(
|
||||
val oepsSatzNr: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Request data for searching persons by name.
|
||||
*/
|
||||
data class SearchPersonsByNameRequest(
|
||||
val searchTerm: String,
|
||||
val limit: Int = 50
|
||||
)
|
||||
|
||||
/**
|
||||
* Request data for getting persons by club.
|
||||
*/
|
||||
data class GetPersonsByClubRequest(
|
||||
val vereinId: Uuid
|
||||
)
|
||||
|
||||
/**
|
||||
* Request data for listing active persons.
|
||||
*/
|
||||
data class ListActivePersonsRequest(
|
||||
val limit: Int = 50,
|
||||
val offset: Int = 0
|
||||
)
|
||||
|
||||
/**
|
||||
* Response data for person retrieval operations.
|
||||
*/
|
||||
data class GetPersonResponse(
|
||||
val person: DomPerson
|
||||
)
|
||||
|
||||
/**
|
||||
* Response data for person list operations.
|
||||
*/
|
||||
data class GetPersonsResponse(
|
||||
val persons: List<DomPerson>,
|
||||
val total: Long? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets a person by their unique ID.
|
||||
*/
|
||||
suspend fun getById(request: GetPersonByIdRequest): ApiResponse<GetPersonResponse> {
|
||||
return try {
|
||||
val person = personRepository.findById(request.personId)
|
||||
if (person != null) {
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = GetPersonResponse(person)
|
||||
)
|
||||
} else {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "PERSON_NOT_FOUND",
|
||||
message = "Person with ID ${request.personId} not found"
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while retrieving the person: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a person by their OEPS Satznummer.
|
||||
*/
|
||||
suspend fun getByOepsSatzNr(request: GetPersonByOepsSatzNrRequest): ApiResponse<GetPersonResponse> {
|
||||
return try {
|
||||
if (request.oepsSatzNr.length != 6) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INVALID_OEPS_SATZNR",
|
||||
message = "OEPS Satznummer must be exactly 6 digits"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val person = personRepository.findByOepsSatzNr(request.oepsSatzNr)
|
||||
if (person != null) {
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = GetPersonResponse(person)
|
||||
)
|
||||
} else {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "PERSON_NOT_FOUND",
|
||||
message = "Person with OEPS Satznummer ${request.oepsSatzNr} not found"
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while retrieving the person: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches persons by name (first name or last name).
|
||||
*/
|
||||
suspend fun searchByName(request: SearchPersonsByNameRequest): ApiResponse<GetPersonsResponse> {
|
||||
return try {
|
||||
if (request.searchTerm.isBlank()) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INVALID_SEARCH_TERM",
|
||||
message = "Search term cannot be empty"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val persons = personRepository.findByName(request.searchTerm, request.limit)
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = GetPersonsResponse(persons)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while searching persons: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all persons belonging to a specific club.
|
||||
*/
|
||||
suspend fun getByClub(request: GetPersonsByClubRequest): ApiResponse<GetPersonsResponse> {
|
||||
return try {
|
||||
val persons = personRepository.findByStammVereinId(request.vereinId)
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = GetPersonsResponse(persons)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while retrieving club members: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists active persons with pagination.
|
||||
*/
|
||||
suspend fun listActive(request: ListActivePersonsRequest): ApiResponse<GetPersonsResponse> {
|
||||
return try {
|
||||
val persons = personRepository.findAllActive(request.limit, request.offset)
|
||||
val total = if (request.offset == 0) personRepository.countActive() else null
|
||||
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = GetPersonsResponse(persons, total)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while listing active persons: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
package at.mocode.members.application.usecase
|
||||
|
||||
import at.mocode.dto.base.ApiResponse
|
||||
import at.mocode.dto.base.ErrorDto
|
||||
import at.mocode.members.domain.model.DomVerein
|
||||
import at.mocode.members.domain.repository.VereinRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Use case for retrieving club/association information from the member management context.
|
||||
*
|
||||
* This use case handles the business logic for club retrieval including:
|
||||
* - Finding clubs by ID or OEPS Vereinsnummer
|
||||
* - Searching clubs by name
|
||||
* - Retrieving clubs by location or geographic region
|
||||
* - Listing active clubs with pagination
|
||||
*/
|
||||
class GetVereinUseCase(
|
||||
private val vereinRepository: VereinRepository
|
||||
) {
|
||||
|
||||
/**
|
||||
* Request data for getting a club by ID.
|
||||
*/
|
||||
data class GetVereinByIdRequest(
|
||||
val vereinId: Uuid
|
||||
)
|
||||
|
||||
/**
|
||||
* Request data for getting a club by OEPS Vereinsnummer.
|
||||
*/
|
||||
data class GetVereinByOepsVereinsNrRequest(
|
||||
val oepsVereinsNr: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Request data for searching clubs by name.
|
||||
*/
|
||||
data class SearchVereinsByNameRequest(
|
||||
val searchTerm: String,
|
||||
val limit: Int = 50
|
||||
)
|
||||
|
||||
/**
|
||||
* Request data for getting clubs by Bundesland.
|
||||
*/
|
||||
data class GetVereineByBundeslandRequest(
|
||||
val bundeslandId: Uuid
|
||||
)
|
||||
|
||||
/**
|
||||
* Request data for getting clubs by country.
|
||||
*/
|
||||
data class GetVereineByLandRequest(
|
||||
val landId: Uuid
|
||||
)
|
||||
|
||||
/**
|
||||
* Request data for searching clubs by location.
|
||||
*/
|
||||
data class SearchVereineByLocationRequest(
|
||||
val searchTerm: String,
|
||||
val limit: Int = 50
|
||||
)
|
||||
|
||||
/**
|
||||
* Request data for listing active clubs.
|
||||
*/
|
||||
data class ListActiveVereineRequest(
|
||||
val limit: Int = 50,
|
||||
val offset: Int = 0
|
||||
)
|
||||
|
||||
/**
|
||||
* Response data for club retrieval operations.
|
||||
*/
|
||||
data class GetVereinResponse(
|
||||
val verein: DomVerein
|
||||
)
|
||||
|
||||
/**
|
||||
* Response data for club list operations.
|
||||
*/
|
||||
data class GetVereineResponse(
|
||||
val vereine: List<DomVerein>,
|
||||
val total: Long? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets a club by its unique ID.
|
||||
*/
|
||||
suspend fun getById(request: GetVereinByIdRequest): ApiResponse<GetVereinResponse> {
|
||||
return try {
|
||||
val verein = vereinRepository.findById(request.vereinId)
|
||||
if (verein != null) {
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = GetVereinResponse(verein)
|
||||
)
|
||||
} else {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "VEREIN_NOT_FOUND",
|
||||
message = "Club with ID ${request.vereinId} not found"
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while retrieving the club: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a club by its OEPS Vereinsnummer.
|
||||
*/
|
||||
suspend fun getByOepsVereinsNr(request: GetVereinByOepsVereinsNrRequest): ApiResponse<GetVereinResponse> {
|
||||
return try {
|
||||
if (request.oepsVereinsNr.length != 4) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INVALID_OEPS_VEREINSNR",
|
||||
message = "OEPS Vereinsnummer must be exactly 4 digits"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val verein = vereinRepository.findByOepsVereinsNr(request.oepsVereinsNr)
|
||||
if (verein != null) {
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = GetVereinResponse(verein)
|
||||
)
|
||||
} else {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "VEREIN_NOT_FOUND",
|
||||
message = "Club with OEPS Vereinsnummer ${request.oepsVereinsNr} not found"
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while retrieving the club: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches clubs by name or abbreviation.
|
||||
*/
|
||||
suspend fun searchByName(request: SearchVereinsByNameRequest): ApiResponse<GetVereineResponse> {
|
||||
return try {
|
||||
if (request.searchTerm.isBlank()) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INVALID_SEARCH_TERM",
|
||||
message = "Search term cannot be empty"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val vereine = vereinRepository.findByName(request.searchTerm, request.limit)
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = GetVereineResponse(vereine)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while searching clubs: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all clubs in a specific Bundesland.
|
||||
*/
|
||||
suspend fun getByBundesland(request: GetVereineByBundeslandRequest): ApiResponse<GetVereineResponse> {
|
||||
return try {
|
||||
val vereine = vereinRepository.findByBundeslandId(request.bundeslandId)
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = GetVereineResponse(vereine)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while retrieving clubs by Bundesland: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all clubs in a specific country.
|
||||
*/
|
||||
suspend fun getByLand(request: GetVereineByLandRequest): ApiResponse<GetVereineResponse> {
|
||||
return try {
|
||||
val vereine = vereinRepository.findByLandId(request.landId)
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = GetVereineResponse(vereine)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while retrieving clubs by country: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches clubs by location (city or postal code).
|
||||
*/
|
||||
suspend fun searchByLocation(request: SearchVereineByLocationRequest): ApiResponse<GetVereineResponse> {
|
||||
return try {
|
||||
if (request.searchTerm.isBlank()) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INVALID_SEARCH_TERM",
|
||||
message = "Search term cannot be empty"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val vereine = vereinRepository.findByLocation(request.searchTerm, request.limit)
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = GetVereineResponse(vereine)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while searching clubs by location: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists active clubs with pagination.
|
||||
*/
|
||||
suspend fun listActive(request: ListActiveVereineRequest): ApiResponse<GetVereineResponse> {
|
||||
return try {
|
||||
val vereine = vereinRepository.findAllActive(request.limit, request.offset)
|
||||
val total = if (request.offset == 0) vereinRepository.countActive() else null
|
||||
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = GetVereineResponse(vereine, total)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while listing active clubs: ${e.message}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package at.mocode.members.domain.model
|
||||
|
||||
import at.mocode.enums.DatenQuelleE
|
||||
import at.mocode.enums.GeschlechtE
|
||||
import at.mocode.serializers.KotlinInstantSerializer
|
||||
import at.mocode.serializers.KotlinLocalDateSerializer
|
||||
import at.mocode.serializers.UuidSerializer
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Repräsentiert eine Person (Reiter, Funktionär, Kontaktperson etc.)
|
||||
* im Domänenmodell der Anwendung.
|
||||
*
|
||||
* Die Daten können aus dem OEPS ZNS-Import (`Person_ZNS_Staging`) stammen
|
||||
* oder manuell im System angelegt werden.
|
||||
*
|
||||
* @property personId Eindeutiger interner Identifikator für diese Person (UUID).
|
||||
* @property oepsSatzNr Die offizielle 6-stellige OEPS-Satznummer der Person, falls vorhanden. Eindeutig.
|
||||
* @property nachname Familienname der Person.
|
||||
* @property vorname Vorname der Person.
|
||||
* @property titel Akademischer Titel oder Anrede (z.B. Dr., Ing.).
|
||||
* @property geburtsdatum Geburtsdatum der Person.
|
||||
* @property geschlechtE Geschlecht der Person.
|
||||
* @property nationalitaetLandId Fremdschlüssel zur `LandDefinition` für die Nationalität.
|
||||
* @property feiId Optionale FEI-Identifikationsnummer der Person.
|
||||
* @property telefon Private oder geschäftliche Telefonnummer.
|
||||
* @property email Private oder geschäftliche E-Mail-Adresse.
|
||||
* @property strasse Straße und Hausnummer der Hauptadresse.
|
||||
* @property plz Postleitzahl der Hauptadresse.
|
||||
* @property ort Ortschaft der Hauptadresse.
|
||||
* @property adresszusatzZusatzinfo Weitere Adressinformationen.
|
||||
* @property stammVereinId Optionale Verknüpfung zum `DomVerein` (Stammverein der Person).
|
||||
* @property mitgliedsNummerBeiStammVerein Mitgliedsnummer der Person beim Stammverein.
|
||||
* @property istGesperrt Gibt an, ob die Person laut OEPS oder intern gesperrt ist.
|
||||
* @property sperrGrund Begründung für eine eventuelle Sperre.
|
||||
* @property altersklasseOepsCodeRaw Der Roh-Code für die Altersklasse aus dem ZNS-Import (z.B. "JG", "JR", "25").
|
||||
* Dient zur Ableitung oder als Information.
|
||||
* @property istJungerReiterOepsFlag Ob die Person im ZNS als "Junger Reiter" ("Y") gekennzeichnet ist.
|
||||
* @property kaderStatusOepsRaw Kaderkennzeichen aus dem ZNS-Import.
|
||||
* @property datenQuelle Gibt die Herkunft dieses Datensatzes an (z.B. OEPS_ZNS, MANUELL).
|
||||
* @property istAktiv Gibt an, ob dieser Personendatensatz aktuell aktiv ist.
|
||||
* @property notizenIntern Interne Anmerkungen oder Notizen zu dieser Person.
|
||||
* @property createdAt Zeitstempel der Erstellung dieses Datensatzes.
|
||||
* @property updatedAt Zeitstempel der letzten Aktualisierung dieses Datensatzes.
|
||||
*/
|
||||
@Serializable
|
||||
data class DomPerson(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val personId: Uuid = uuid4(),
|
||||
|
||||
var oepsSatzNr: String?, // Wird aus Person_ZNS_Staging.oepsSatzNrPerson befüllt, UNIQUE
|
||||
var nachname: String, // Wird aus Person_ZNS_Staging.familiennameRoh befüllt
|
||||
var vorname: String, // Wird aus Person_ZNS_Staging.vornameRoh befüllt
|
||||
var titel: String? = null, // Manuelle Eingabe ggf. später ZNS, falls vorhanden
|
||||
|
||||
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||
var geburtsdatum: LocalDate? = null, // Konvertiert aus Person_ZNS_Staging.geburtsdatumTextRoh
|
||||
|
||||
var geschlechtE: GeschlechtE? = null, // Konvertiert aus Person_ZNS_Staging.geschlechtCodeRoh
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
var nationalitaetLandId: Uuid? = null, // Aufgelöst aus Person_ZNS_Staging.nationalitaetCodeRoh via LandDefinition
|
||||
|
||||
var feiId: String? = null, // Wird aus Person_ZNS_Staging.feiIdPersonRoh befüllt
|
||||
|
||||
var telefon: String? = null, // Wird aus Person_ZNS_Staging.telefonRoh befüllt
|
||||
var email: String? = null, // Manuelle Eingabe, nicht in LIZENZ01.dat
|
||||
|
||||
// Adresse (manuelle Eingabe, nicht primär in LIZENZ01.dat für Person direkt)
|
||||
var strasse: String? = null,
|
||||
var plz: String? = null,
|
||||
var ort: String? = null,
|
||||
var adresszusatzZusatzinfo: String? = null,
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
var stammVereinId: Uuid? = null, // Aufgelöst aus Person_ZNS_Staging.vereinsnameOepsRoh & bundeslandCodeOepsRoh via DomVerein
|
||||
var mitgliedsNummerBeiStammVerein: String? = null, // Wird aus Person_ZNS_Staging.mitgliedNrVereinRoh befüllt
|
||||
|
||||
var istGesperrt: Boolean = false, // Konvertiert aus Person_ZNS_Staging.sperrlisteFlagOepsRoh ("S" -> true)
|
||||
var sperrGrund: String? = null, // Manuelle Eingabe
|
||||
|
||||
var altersklasseOepsCodeRaw: String? = null, // Speichert Roh-Code "JG", "JR", "25"
|
||||
var istJungerReiterOepsFlag: Boolean = false, // true wenn Roh-Code "Y"
|
||||
|
||||
var kaderStatusOepsRaw: String? = null, // Speichert Roh-Code (aktuell meist BLANK)
|
||||
|
||||
var datenQuelle: DatenQuelleE = DatenQuelleE.MANUELL,
|
||||
var istAktiv: Boolean = true,
|
||||
var notizenIntern: String? = null,
|
||||
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
val createdAt: Instant = Clock.System.now(),
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
var updatedAt: Instant = Clock.System.now()
|
||||
)
|
||||
@@ -0,0 +1,70 @@
|
||||
package at.mocode.members.domain.model
|
||||
|
||||
import at.mocode.enums.DatenQuelleE
|
||||
import at.mocode.serializers.KotlinInstantSerializer
|
||||
import at.mocode.serializers.UuidSerializer
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Repräsentiert einen Reitverein im Domänenmodell der Anwendung.
|
||||
*
|
||||
* Die Daten für einen Verein können aus dem OEPS ZNS-Import (`Verein_ZNS_Staging`)
|
||||
* stammen oder manuell im System angelegt werden (z.B. für ausländische Vereine).
|
||||
* Jeder Verein wird durch eine systeminterne UUID und die offizielle OEPS-Vereinsnummer
|
||||
* (falls vorhanden) eindeutig identifiziert.
|
||||
*
|
||||
* @property vereinId Eindeutiger interner Identifikator für diesen Verein (UUID).
|
||||
* @property oepsVereinsNr Die offizielle 4-stellige OEPS-Vereinsnummer. Sollte eindeutig sein, falls vorhanden.
|
||||
* @property name Der offizielle Name des Vereins.
|
||||
* @property kuerzel Ein optionales Kürzel oder eine Kurzbezeichnung für den Verein.
|
||||
* @property adresseStrasse Straße und Hausnummer des Vereinssitzes.
|
||||
* @property plz Postleitzahl des Vereinssitzes.
|
||||
* @property ort Ortschaft des Vereinssitzes.
|
||||
* @property bundeslandId Optionale Verknüpfung zur `BundeslandDefinition`. Für OEPS-Vereine
|
||||
* wird versucht, dies aus der ersten Ziffer der `oepsVereinsNr` abzuleiten.
|
||||
* @property landId Verknüpfung zur `LandDefinition`. Für OEPS-Vereine ist dies "Österreich".
|
||||
* @property emailAllgemein Allgemeine E-Mail-Adresse des Vereins.
|
||||
* @property telefonAllgemein Allgemeine Telefonnummer des Vereins.
|
||||
* @property webseiteUrl URL zur Webseite des Vereins.
|
||||
* @property datenQuelle Gibt die Herkunft dieses Datensatzes an (z.B. OEPS_ZNS, MANUELL).
|
||||
* @property istAktiv Gibt an, ob dieser Verein aktuell aktiv ist und im System verwendet werden kann.
|
||||
* @property notizenIntern Interne Anmerkungen oder Notizen zu diesem Verein.
|
||||
* @property createdAt Zeitstempel der Erstellung dieses Datensatzes.
|
||||
* @property updatedAt Zeitstempel der letzten Aktualisierung dieses Datensatzes.
|
||||
*/
|
||||
@Serializable
|
||||
data class DomVerein(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val vereinId: Uuid = uuid4(),
|
||||
|
||||
var oepsVereinsNr: String?, // Kann null sein für nicht-OEPS Vereine. Wenn gesetzt, erste Ziffer = Bundesland-Code.
|
||||
var name: String,
|
||||
var kuerzel: String? = null,
|
||||
|
||||
var adresseStrasse: String? = null,
|
||||
var plz: String? = null,
|
||||
var ort: String? = null,
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
var bundeslandId: Uuid? = null, // FK zu BundeslandDefinition.bundeslandId
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
var landId: Uuid, // FK zu LandDefinition.landId (jeder Verein ist in einem Land)
|
||||
|
||||
var emailAllgemein: String? = null,
|
||||
var telefonAllgemein: String? = null,
|
||||
var webseiteUrl: String? = null,
|
||||
|
||||
var datenQuelle: DatenQuelleE = DatenQuelleE.OEPS_ZNS, // default OEPS_ZNS
|
||||
var istAktiv: Boolean = true,
|
||||
var notizenIntern: String? = null,
|
||||
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
val createdAt: Instant = Clock.System.now(),
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
var updatedAt: Instant = Clock.System.now()
|
||||
)
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package at.mocode.members.domain.repository
|
||||
|
||||
import at.mocode.members.domain.model.DomPerson
|
||||
import com.benasher44.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Repository interface for Person domain operations.
|
||||
*
|
||||
* This interface defines the contract for person data access operations
|
||||
* without depending on specific implementation details (database, etc.).
|
||||
* Following the hexagonal architecture pattern, this interface belongs
|
||||
* to the domain layer and will be implemented in the infrastructure layer.
|
||||
*/
|
||||
interface PersonRepository {
|
||||
|
||||
/**
|
||||
* Finds a person by their unique ID.
|
||||
*
|
||||
* @param id The unique identifier of the person
|
||||
* @return The person if found, null otherwise
|
||||
*/
|
||||
suspend fun findById(id: Uuid): DomPerson?
|
||||
|
||||
/**
|
||||
* Finds a person by their OEPS Satznummer.
|
||||
*
|
||||
* @param oepsSatzNr The OEPS Satznummer (6-digit identifier)
|
||||
* @return The person if found, null otherwise
|
||||
*/
|
||||
suspend fun findByOepsSatzNr(oepsSatzNr: String): DomPerson?
|
||||
|
||||
/**
|
||||
* Finds all persons belonging to a specific club.
|
||||
*
|
||||
* @param vereinId The unique identifier of the club
|
||||
* @return List of persons belonging to the club
|
||||
*/
|
||||
suspend fun findByStammVereinId(vereinId: Uuid): List<DomPerson>
|
||||
|
||||
/**
|
||||
* Finds persons by name (partial match on first name or last name).
|
||||
*
|
||||
* @param searchTerm The search term to match against names
|
||||
* @param limit Maximum number of results to return
|
||||
* @return List of matching persons
|
||||
*/
|
||||
suspend fun findByName(searchTerm: String, limit: Int = 50): List<DomPerson>
|
||||
|
||||
/**
|
||||
* Finds all active persons.
|
||||
*
|
||||
* @param limit Maximum number of results to return
|
||||
* @param offset Number of records to skip for pagination
|
||||
* @return List of active persons
|
||||
*/
|
||||
suspend fun findAllActive(limit: Int = 50, offset: Int = 0): List<DomPerson>
|
||||
|
||||
/**
|
||||
* Saves a person (create or update).
|
||||
*
|
||||
* @param person The person to save
|
||||
* @return The saved person with updated timestamps
|
||||
*/
|
||||
suspend fun save(person: DomPerson): DomPerson
|
||||
|
||||
/**
|
||||
* Deletes a person by ID.
|
||||
*
|
||||
* @param id The unique identifier of the person to delete
|
||||
* @return true if the person was deleted, false if not found
|
||||
*/
|
||||
suspend fun delete(id: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Checks if a person with the given OEPS Satznummer exists.
|
||||
*
|
||||
* @param oepsSatzNr The OEPS Satznummer to check
|
||||
* @return true if a person with this number exists, false otherwise
|
||||
*/
|
||||
suspend fun existsByOepsSatzNr(oepsSatzNr: String): Boolean
|
||||
|
||||
/**
|
||||
* Counts the total number of active persons.
|
||||
*
|
||||
* @return The total count of active persons
|
||||
*/
|
||||
suspend fun countActive(): Long
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package at.mocode.members.domain.repository
|
||||
|
||||
import at.mocode.members.domain.model.DomVerein
|
||||
import com.benasher44.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Repository interface for Verein (Club/Association) domain operations.
|
||||
*
|
||||
* This interface defines the contract for club data access operations
|
||||
* without depending on specific implementation details (database, etc.).
|
||||
* Following the hexagonal architecture pattern, this interface belongs
|
||||
* to the domain layer and will be implemented in the infrastructure layer.
|
||||
*/
|
||||
interface VereinRepository {
|
||||
|
||||
/**
|
||||
* Finds a club by its unique ID.
|
||||
*
|
||||
* @param id The unique identifier of the club
|
||||
* @return The club if found, null otherwise
|
||||
*/
|
||||
suspend fun findById(id: Uuid): DomVerein?
|
||||
|
||||
/**
|
||||
* Finds a club by its OEPS Vereinsnummer.
|
||||
*
|
||||
* @param oepsVereinsNr The OEPS Vereinsnummer (4-digit identifier)
|
||||
* @return The club if found, null otherwise
|
||||
*/
|
||||
suspend fun findByOepsVereinsNr(oepsVereinsNr: String): DomVerein?
|
||||
|
||||
/**
|
||||
* Finds clubs by name (partial match).
|
||||
*
|
||||
* @param searchTerm The search term to match against club names
|
||||
* @param limit Maximum number of results to return
|
||||
* @return List of matching clubs
|
||||
*/
|
||||
suspend fun findByName(searchTerm: String, limit: Int = 50): List<DomVerein>
|
||||
|
||||
/**
|
||||
* Finds all clubs in a specific Bundesland (state).
|
||||
*
|
||||
* @param bundeslandId The unique identifier of the Bundesland
|
||||
* @return List of clubs in the specified Bundesland
|
||||
*/
|
||||
suspend fun findByBundeslandId(bundeslandId: Uuid): List<DomVerein>
|
||||
|
||||
/**
|
||||
* Finds all clubs in a specific country.
|
||||
*
|
||||
* @param landId The unique identifier of the country
|
||||
* @return List of clubs in the specified country
|
||||
*/
|
||||
suspend fun findByLandId(landId: Uuid): List<DomVerein>
|
||||
|
||||
/**
|
||||
* Finds all active clubs.
|
||||
*
|
||||
* @param limit Maximum number of results to return
|
||||
* @param offset Number of records to skip for pagination
|
||||
* @return List of active clubs
|
||||
*/
|
||||
suspend fun findAllActive(limit: Int = 50, offset: Int = 0): List<DomVerein>
|
||||
|
||||
/**
|
||||
* Finds clubs by location (city/postal code).
|
||||
*
|
||||
* @param searchTerm The search term to match against city or postal code
|
||||
* @param limit Maximum number of results to return
|
||||
* @return List of matching clubs
|
||||
*/
|
||||
suspend fun findByLocation(searchTerm: String, limit: Int = 50): List<DomVerein>
|
||||
|
||||
/**
|
||||
* Saves a club (create or update).
|
||||
*
|
||||
* @param verein The club to save
|
||||
* @return The saved club with updated timestamps
|
||||
*/
|
||||
suspend fun save(verein: DomVerein): DomVerein
|
||||
|
||||
/**
|
||||
* Deletes a club by ID.
|
||||
*
|
||||
* @param id The unique identifier of the club to delete
|
||||
* @return true if the club was deleted, false if not found
|
||||
*/
|
||||
suspend fun delete(id: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Checks if a club with the given OEPS Vereinsnummer exists.
|
||||
*
|
||||
* @param oepsVereinsNr The OEPS Vereinsnummer to check
|
||||
* @return true if a club with this number exists, false otherwise
|
||||
*/
|
||||
suspend fun existsByOepsVereinsNr(oepsVereinsNr: String): Boolean
|
||||
|
||||
/**
|
||||
* Counts the total number of active clubs.
|
||||
*
|
||||
* @return The total count of active clubs
|
||||
*/
|
||||
suspend fun countActive(): Long
|
||||
|
||||
/**
|
||||
* Counts the number of active clubs in a specific Bundesland.
|
||||
*
|
||||
* @param bundeslandId The unique identifier of the Bundesland
|
||||
* @return The count of active clubs in the specified Bundesland
|
||||
*/
|
||||
suspend fun countActiveByBundeslandId(bundeslandId: Uuid): Long
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package at.mocode.members.domain.service
|
||||
|
||||
import com.benasher44.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Service interface for accessing master data from other bounded contexts.
|
||||
*
|
||||
* This interface abstracts the communication with the master-data context,
|
||||
* following the Self-Contained Systems architecture principles by avoiding
|
||||
* direct repository dependencies between bounded contexts.
|
||||
*/
|
||||
interface MasterDataService {
|
||||
|
||||
/**
|
||||
* Data class representing country information.
|
||||
*/
|
||||
data class CountryInfo(
|
||||
val id: Uuid,
|
||||
val name: String,
|
||||
val code: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Data class representing state/bundesland information.
|
||||
*/
|
||||
data class StateInfo(
|
||||
val id: Uuid,
|
||||
val name: String,
|
||||
val code: String,
|
||||
val countryId: Uuid
|
||||
)
|
||||
|
||||
/**
|
||||
* Validates if a country exists by its ID.
|
||||
*
|
||||
* @param countryId The unique identifier of the country
|
||||
* @return true if the country exists, false otherwise
|
||||
*/
|
||||
suspend fun countryExists(countryId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Validates if a state/bundesland exists by its ID.
|
||||
*
|
||||
* @param stateId The unique identifier of the state
|
||||
* @return true if the state exists, false otherwise
|
||||
*/
|
||||
suspend fun stateExists(stateId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Gets country information by ID.
|
||||
*
|
||||
* @param countryId The unique identifier of the country
|
||||
* @return CountryInfo if found, null otherwise
|
||||
*/
|
||||
suspend fun getCountryById(countryId: Uuid): CountryInfo?
|
||||
|
||||
/**
|
||||
* Gets state information by ID.
|
||||
*
|
||||
* @param stateId The unique identifier of the state
|
||||
* @return StateInfo if found, null otherwise
|
||||
*/
|
||||
suspend fun getStateById(stateId: Uuid): StateInfo?
|
||||
|
||||
/**
|
||||
* Gets all available countries.
|
||||
*
|
||||
* @return List of all countries
|
||||
*/
|
||||
suspend fun getAllCountries(): List<CountryInfo>
|
||||
|
||||
/**
|
||||
* Gets all states for a specific country.
|
||||
*
|
||||
* @param countryId The unique identifier of the country
|
||||
* @return List of states in the specified country
|
||||
*/
|
||||
suspend fun getStatesByCountry(countryId: Uuid): List<StateInfo>
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.members.domain.model.DomPerson
|
||||
import at.mocode.members.domain.repository.PersonRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.toKotlinInstant
|
||||
import kotlinx.datetime.toKotlinLocalDate
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.like
|
||||
|
||||
/**
|
||||
* Exposed-based implementation of PersonRepository.
|
||||
*
|
||||
* This implementation provides data persistence for Person entities
|
||||
* using the Exposed SQL framework and PostgreSQL database.
|
||||
*/
|
||||
class PersonRepositoryImpl : PersonRepository {
|
||||
|
||||
override suspend fun findById(id: Uuid): DomPerson? {
|
||||
return PersonTable.select { PersonTable.id eq id }
|
||||
.map { rowToDomPerson(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByOepsSatzNr(oepsSatzNr: String): DomPerson? {
|
||||
return PersonTable.select { PersonTable.oepsSatzNr eq oepsSatzNr }
|
||||
.map { rowToDomPerson(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByStammVereinId(vereinId: Uuid): List<DomPerson> {
|
||||
return PersonTable.select { PersonTable.stammVereinId eq vereinId }
|
||||
.map { rowToDomPerson(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<DomPerson> {
|
||||
val searchPattern = "%$searchTerm%"
|
||||
return PersonTable.select {
|
||||
(PersonTable.nachname like searchPattern) or
|
||||
(PersonTable.vorname like searchPattern)
|
||||
}
|
||||
.limit(limit)
|
||||
.map { rowToDomPerson(it) }
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(limit: Int, offset: Int): List<DomPerson> {
|
||||
return PersonTable.select { PersonTable.istAktiv eq true }
|
||||
.limit(limit, offset.toLong())
|
||||
.map { rowToDomPerson(it) }
|
||||
}
|
||||
|
||||
override suspend fun save(person: DomPerson): DomPerson {
|
||||
val now = Clock.System.now()
|
||||
val updatedPerson = person.copy(updatedAt = now)
|
||||
|
||||
PersonTable.insertOrUpdate(PersonTable.id) {
|
||||
it[id] = person.personId
|
||||
it[oepsSatzNr] = person.oepsSatzNr
|
||||
it[nachname] = person.nachname
|
||||
it[vorname] = person.vorname
|
||||
it[titel] = person.titel
|
||||
it[geburtsdatum] = person.geburtsdatum
|
||||
it[geschlecht] = person.geschlechtE
|
||||
it[nationalitaetLandId] = person.nationalitaetLandId
|
||||
it[feiId] = person.feiId
|
||||
it[telefon] = person.telefon
|
||||
it[email] = person.email
|
||||
it[strasse] = person.strasse
|
||||
it[plz] = person.plz
|
||||
it[ort] = person.ort
|
||||
it[adresszusatzZusatzinfo] = person.adresszusatzZusatzinfo
|
||||
it[stammVereinId] = person.stammVereinId
|
||||
it[mitgliedsNummerBeiStammVerein] = person.mitgliedsNummerBeiStammVerein
|
||||
it[istGesperrt] = person.istGesperrt
|
||||
it[sperrGrund] = person.sperrGrund
|
||||
it[altersklasseOepsCodeRaw] = person.altersklasseOepsCodeRaw
|
||||
it[istJungerReiterOepsFlag] = person.istJungerReiterOepsFlag
|
||||
it[kaderStatusOepsRaw] = person.kaderStatusOepsRaw
|
||||
it[datenQuelle] = person.datenQuelle
|
||||
it[istAktiv] = person.istAktiv
|
||||
it[notizenIntern] = person.notizenIntern
|
||||
it[createdAt] = person.createdAt.toJavaInstant()
|
||||
it[updatedAt] = updatedPerson.updatedAt.toJavaInstant()
|
||||
}
|
||||
|
||||
return updatedPerson
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Uuid): Boolean {
|
||||
val deletedRows = PersonTable.deleteWhere { PersonTable.id eq id }
|
||||
return deletedRows > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByOepsSatzNr(oepsSatzNr: String): Boolean {
|
||||
return PersonTable.select { PersonTable.oepsSatzNr eq oepsSatzNr }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun countActive(): Long {
|
||||
return PersonTable.select { PersonTable.istAktiv eq true }
|
||||
.count()
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a database row to a DomPerson domain object.
|
||||
*/
|
||||
private fun rowToDomPerson(row: ResultRow): DomPerson {
|
||||
return DomPerson(
|
||||
personId = row[PersonTable.id].value,
|
||||
oepsSatzNr = row[PersonTable.oepsSatzNr],
|
||||
nachname = row[PersonTable.nachname],
|
||||
vorname = row[PersonTable.vorname],
|
||||
titel = row[PersonTable.titel],
|
||||
geburtsdatum = row[PersonTable.geburtsdatum],
|
||||
geschlechtE = row[PersonTable.geschlecht],
|
||||
nationalitaetLandId = row[PersonTable.nationalitaetLandId],
|
||||
feiId = row[PersonTable.feiId],
|
||||
telefon = row[PersonTable.telefon],
|
||||
email = row[PersonTable.email],
|
||||
strasse = row[PersonTable.strasse],
|
||||
plz = row[PersonTable.plz],
|
||||
ort = row[PersonTable.ort],
|
||||
adresszusatzZusatzinfo = row[PersonTable.adresszusatzZusatzinfo],
|
||||
stammVereinId = row[PersonTable.stammVereinId],
|
||||
mitgliedsNummerBeiStammVerein = row[PersonTable.mitgliedsNummerBeiStammVerein],
|
||||
istGesperrt = row[PersonTable.istGesperrt],
|
||||
sperrGrund = row[PersonTable.sperrGrund],
|
||||
altersklasseOepsCodeRaw = row[PersonTable.altersklasseOepsCodeRaw],
|
||||
istJungerReiterOepsFlag = row[PersonTable.istJungerReiterOepsFlag],
|
||||
kaderStatusOepsRaw = row[PersonTable.kaderStatusOepsRaw],
|
||||
datenQuelle = row[PersonTable.datenQuelle],
|
||||
istAktiv = row[PersonTable.istAktiv],
|
||||
notizenIntern = row[PersonTable.notizenIntern],
|
||||
createdAt = row[PersonTable.createdAt].toKotlinInstant(),
|
||||
updatedAt = row[PersonTable.updatedAt].toKotlinInstant()
|
||||
)
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.enums.DatenQuelleE
|
||||
import at.mocode.enums.GeschlechtE
|
||||
import org.jetbrains.exposed.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.date
|
||||
|
||||
/**
|
||||
* Exposed table definition for Person entities.
|
||||
*
|
||||
* This table represents the database schema for storing person data
|
||||
* in the member management bounded context.
|
||||
*/
|
||||
object PersonTable : UUIDTable("persons") {
|
||||
|
||||
// Basic person information
|
||||
val oepsSatzNr = varchar("oeps_satz_nr", 6).nullable().uniqueIndex()
|
||||
val nachname = varchar("nachname", 100)
|
||||
val vorname = varchar("vorname", 100)
|
||||
val titel = varchar("titel", 50).nullable()
|
||||
|
||||
// Personal details
|
||||
val geburtsdatum = date("geburtsdatum").nullable()
|
||||
val geschlecht = enumerationByName("geschlecht", 10, GeschlechtE::class).nullable()
|
||||
val nationalitaetLandId = uuid("nationalitaet_land_id").nullable()
|
||||
val feiId = varchar("fei_id", 20).nullable()
|
||||
|
||||
// Contact information
|
||||
val telefon = varchar("telefon", 50).nullable()
|
||||
val email = varchar("email", 100).nullable()
|
||||
|
||||
// Address information
|
||||
val strasse = varchar("strasse", 200).nullable()
|
||||
val plz = varchar("plz", 10).nullable()
|
||||
val ort = varchar("ort", 100).nullable()
|
||||
val adresszusatzZusatzinfo = varchar("adresszusatz_zusatzinfo", 200).nullable()
|
||||
|
||||
// Club membership
|
||||
val stammVereinId = uuid("stamm_verein_id").nullable()
|
||||
val mitgliedsNummerBeiStammVerein = varchar("mitglieds_nummer_bei_stamm_verein", 50).nullable()
|
||||
|
||||
// Status and restrictions
|
||||
val istGesperrt = bool("ist_gesperrt").default(false)
|
||||
val sperrGrund = varchar("sperr_grund", 500).nullable()
|
||||
|
||||
// OEPS specific data
|
||||
val altersklasseOepsCodeRaw = varchar("altersklasse_oeps_code_raw", 10).nullable()
|
||||
val istJungerReiterOepsFlag = bool("ist_junger_reiter_oeps_flag").default(false)
|
||||
val kaderStatusOepsRaw = varchar("kader_status_oeps_raw", 10).nullable()
|
||||
|
||||
// Metadata
|
||||
val datenQuelle = enumerationByName("daten_quelle", 20, DatenQuelleE::class).default(DatenQuelleE.MANUELL)
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val notizenIntern = text("notizen_intern").nullable()
|
||||
|
||||
// Audit fields
|
||||
val createdAt = datetime("created_at")
|
||||
val updatedAt = datetime("updated_at")
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.members.domain.model.DomVerein
|
||||
import at.mocode.members.domain.repository.VereinRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.toKotlinInstant
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.like
|
||||
|
||||
/**
|
||||
* Exposed-based implementation of VereinRepository.
|
||||
*
|
||||
* This implementation provides data persistence for Verein (Club/Association) entities
|
||||
* using the Exposed SQL framework and PostgreSQL database.
|
||||
*/
|
||||
class VereinRepositoryImpl : VereinRepository {
|
||||
|
||||
override suspend fun findById(id: Uuid): DomVerein? {
|
||||
return VereinTable.select { VereinTable.id eq id }
|
||||
.map { rowToDomVerein(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByOepsVereinsNr(oepsVereinsNr: String): DomVerein? {
|
||||
return VereinTable.select { VereinTable.oepsVereinsNr eq oepsVereinsNr }
|
||||
.map { rowToDomVerein(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<DomVerein> {
|
||||
val searchPattern = "%$searchTerm%"
|
||||
return VereinTable.select {
|
||||
(VereinTable.name like searchPattern) or
|
||||
(VereinTable.kuerzel like searchPattern)
|
||||
}
|
||||
.limit(limit)
|
||||
.map { rowToDomVerein(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByBundeslandId(bundeslandId: Uuid): List<DomVerein> {
|
||||
return VereinTable.select { VereinTable.bundeslandId eq bundeslandId }
|
||||
.map { rowToDomVerein(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByLandId(landId: Uuid): List<DomVerein> {
|
||||
return VereinTable.select { VereinTable.landId eq landId }
|
||||
.map { rowToDomVerein(it) }
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(limit: Int, offset: Int): List<DomVerein> {
|
||||
return VereinTable.select { VereinTable.istAktiv eq true }
|
||||
.limit(limit, offset.toLong())
|
||||
.map { rowToDomVerein(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByLocation(searchTerm: String, limit: Int): List<DomVerein> {
|
||||
val searchPattern = "%$searchTerm%"
|
||||
return VereinTable.select {
|
||||
(VereinTable.ort like searchPattern) or
|
||||
(VereinTable.plz like searchPattern)
|
||||
}
|
||||
.limit(limit)
|
||||
.map { rowToDomVerein(it) }
|
||||
}
|
||||
|
||||
override suspend fun save(verein: DomVerein): DomVerein {
|
||||
val now = Clock.System.now()
|
||||
val updatedVerein = verein.copy(updatedAt = now)
|
||||
|
||||
VereinTable.insertOrUpdate(VereinTable.id) {
|
||||
it[id] = verein.vereinId
|
||||
it[oepsVereinsNr] = verein.oepsVereinsNr
|
||||
it[name] = verein.name
|
||||
it[kuerzel] = verein.kuerzel
|
||||
it[adresseStrasse] = verein.adresseStrasse
|
||||
it[plz] = verein.plz
|
||||
it[ort] = verein.ort
|
||||
it[bundeslandId] = verein.bundeslandId
|
||||
it[landId] = verein.landId
|
||||
it[emailAllgemein] = verein.emailAllgemein
|
||||
it[telefonAllgemein] = verein.telefonAllgemein
|
||||
it[webseiteUrl] = verein.webseiteUrl
|
||||
it[datenQuelle] = verein.datenQuelle
|
||||
it[istAktiv] = verein.istAktiv
|
||||
it[notizenIntern] = verein.notizenIntern
|
||||
it[createdAt] = verein.createdAt.toJavaInstant()
|
||||
it[updatedAt] = updatedVerein.updatedAt.toJavaInstant()
|
||||
}
|
||||
|
||||
return updatedVerein
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Uuid): Boolean {
|
||||
val deletedRows = VereinTable.deleteWhere { VereinTable.id eq id }
|
||||
return deletedRows > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByOepsVereinsNr(oepsVereinsNr: String): Boolean {
|
||||
return VereinTable.select { VereinTable.oepsVereinsNr eq oepsVereinsNr }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun countActive(): Long {
|
||||
return VereinTable.select { VereinTable.istAktiv eq true }
|
||||
.count()
|
||||
}
|
||||
|
||||
override suspend fun countActiveByBundeslandId(bundeslandId: Uuid): Long {
|
||||
return VereinTable.select {
|
||||
(VereinTable.istAktiv eq true) and (VereinTable.bundeslandId eq bundeslandId)
|
||||
}
|
||||
.count()
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a database row to a DomVerein domain object.
|
||||
*/
|
||||
private fun rowToDomVerein(row: ResultRow): DomVerein {
|
||||
return DomVerein(
|
||||
vereinId = row[VereinTable.id].value,
|
||||
oepsVereinsNr = row[VereinTable.oepsVereinsNr],
|
||||
name = row[VereinTable.name],
|
||||
kuerzel = row[VereinTable.kuerzel],
|
||||
adresseStrasse = row[VereinTable.adresseStrasse],
|
||||
plz = row[VereinTable.plz],
|
||||
ort = row[VereinTable.ort],
|
||||
bundeslandId = row[VereinTable.bundeslandId],
|
||||
landId = row[VereinTable.landId],
|
||||
emailAllgemein = row[VereinTable.emailAllgemein],
|
||||
telefonAllgemein = row[VereinTable.telefonAllgemein],
|
||||
webseiteUrl = row[VereinTable.webseiteUrl],
|
||||
datenQuelle = row[VereinTable.datenQuelle],
|
||||
istAktiv = row[VereinTable.istAktiv],
|
||||
notizenIntern = row[VereinTable.notizenIntern],
|
||||
createdAt = row[VereinTable.createdAt].toKotlinInstant(),
|
||||
updatedAt = row[VereinTable.updatedAt].toKotlinInstant()
|
||||
)
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.enums.DatenQuelleE
|
||||
import org.jetbrains.exposed.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
|
||||
/**
|
||||
* Exposed table definition for Verein (Club/Association) entities.
|
||||
*
|
||||
* This table represents the database schema for storing club data
|
||||
* in the member management bounded context.
|
||||
*/
|
||||
object VereinTable : UUIDTable("vereine") {
|
||||
|
||||
// Basic club information
|
||||
val oepsVereinsNr = varchar("oeps_vereins_nr", 4).nullable().uniqueIndex()
|
||||
val name = varchar("name", 200)
|
||||
val kuerzel = varchar("kuerzel", 20).nullable()
|
||||
|
||||
// Address information
|
||||
val adresseStrasse = varchar("adresse_strasse", 200).nullable()
|
||||
val plz = varchar("plz", 10).nullable()
|
||||
val ort = varchar("ort", 100).nullable()
|
||||
|
||||
// Geographic references
|
||||
val bundeslandId = uuid("bundesland_id").nullable()
|
||||
val landId = uuid("land_id")
|
||||
|
||||
// Contact information
|
||||
val emailAllgemein = varchar("email_allgemein", 100).nullable()
|
||||
val telefonAllgemein = varchar("telefon_allgemein", 50).nullable()
|
||||
val webseiteUrl = varchar("webseite_url", 200).nullable()
|
||||
|
||||
// Metadata
|
||||
val datenQuelle = enumerationByName("daten_quelle", 20, DatenQuelleE::class).default(DatenQuelleE.OEPS_ZNS)
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val notizenIntern = text("notizen_intern").nullable()
|
||||
|
||||
// Audit fields
|
||||
val createdAt = datetime("created_at")
|
||||
val updatedAt = datetime("updated_at")
|
||||
}
|
||||
Reference in New Issue
Block a user