feat(zns-importer): add ZNSImportService with tests and REST controller
- Created `ZnsImportService` to handle uploading, parsing, and persisting ZNS data from legacy `.zip` files. - Introduced corresponding test cases in `ZnsImportServiceTest` for handling edge cases including imports and updates. - Added REST controller `ZnsImportController` for initiating import jobs and retrieving their status. - Defined `ZnsImportResult` data structure for reporting results of import operations. - Established database configuration specific to ZNS importer for development profile. - Updated utility libraries with `FixedWidthLineReader` for fixed-width string parsing. - Refactored architecture by placing parser logic in `core:zns-parser` for reuse across backend and Compose Desktop app. Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
+266
@@ -0,0 +1,266 @@
|
||||
@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 org.jetbrains.exposed.v1.core.*
|
||||
import org.jetbrains.exposed.v1.core.statements.UpdateBuilder
|
||||
import org.jetbrains.exposed.v1.jdbc.deleteWhere
|
||||
import org.jetbrains.exposed.v1.jdbc.insert
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import org.jetbrains.exposed.v1.jdbc.update
|
||||
import kotlin.time.Clock
|
||||
import kotlin.uuid.Uuid
|
||||
import kotlin.uuid.toJavaUuid
|
||||
import kotlin.uuid.toKotlinUuid
|
||||
|
||||
/**
|
||||
* Exposed-basierte Implementierung des HorseRepository (v1-API).
|
||||
*/
|
||||
class HorseRepositoryImpl : HorseRepository {
|
||||
|
||||
override suspend fun findById(id: Uuid): DomPferd? = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.id eq id.toJavaUuid() }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByLebensnummer(lebensnummer: String): DomPferd? = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.lebensnummer eq lebensnummer }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByChipNummer(chipNummer: String): DomPferd? = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.chipNummer eq chipNummer }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByPassNummer(passNummer: String): DomPferd? = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.passNummer eq passNummer }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByOepsNummer(oepsNummer: String): DomPferd? = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.oepsNummer eq oepsNummer }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByFeiNummer(feiNummer: String): DomPferd? = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.feiNummer eq feiNummer }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<DomPferd> = transaction {
|
||||
val pattern = "%$searchTerm%"
|
||||
HorseTable.selectAll().where { HorseTable.pferdeName like pattern }
|
||||
.limit(limit)
|
||||
.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByOwnerId(ownerId: Uuid, activeOnly: Boolean): List<DomPferd> = transaction {
|
||||
HorseTable.selectAll().where {
|
||||
(HorseTable.besitzerId eq ownerId.toJavaUuid()).let {
|
||||
if (activeOnly) it and (HorseTable.istAktiv eq true) else it
|
||||
}
|
||||
}.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByResponsiblePersonId(responsiblePersonId: Uuid, activeOnly: Boolean): List<DomPferd> =
|
||||
transaction {
|
||||
HorseTable.selectAll().where {
|
||||
(HorseTable.verantwortlichePersonId eq responsiblePersonId.toJavaUuid()).let {
|
||||
if (activeOnly) it and (HorseTable.istAktiv eq true) else it
|
||||
}
|
||||
}.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByGeschlecht(
|
||||
geschlecht: PferdeGeschlechtE,
|
||||
activeOnly: Boolean,
|
||||
limit: Int
|
||||
): List<DomPferd> = transaction {
|
||||
HorseTable.selectAll().where {
|
||||
(HorseTable.geschlecht eq geschlecht).let {
|
||||
if (activeOnly) it and (HorseTable.istAktiv eq true) else it
|
||||
}
|
||||
}.limit(limit).map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByRasse(rasse: String, activeOnly: Boolean, limit: Int): List<DomPferd> = transaction {
|
||||
HorseTable.selectAll().where {
|
||||
(HorseTable.rasse eq rasse).let {
|
||||
if (activeOnly) it and (HorseTable.istAktiv eq true) else it
|
||||
}
|
||||
}.limit(limit).map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByBirthYear(birthYear: Int, activeOnly: Boolean): List<DomPferd> = transaction {
|
||||
HorseTable.selectAll()
|
||||
.map { rowToDomPferd(it) }
|
||||
.filter { it.geburtsdatum?.year == birthYear && (!activeOnly || it.istAktiv) }
|
||||
}
|
||||
|
||||
override suspend fun findByBirthYearRange(fromYear: Int, toYear: Int, activeOnly: Boolean): List<DomPferd> =
|
||||
transaction {
|
||||
HorseTable.selectAll()
|
||||
.map { rowToDomPferd(it) }
|
||||
.filter {
|
||||
val year = it.geburtsdatum?.year
|
||||
year != null && year in fromYear..toYear && (!activeOnly || it.istAktiv)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(limit: Int): List<DomPferd> = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.istAktiv eq true }
|
||||
.limit(limit)
|
||||
.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findOepsRegistered(activeOnly: Boolean): List<DomPferd> = transaction {
|
||||
HorseTable.selectAll().where {
|
||||
HorseTable.oepsNummer.isNotNull().let {
|
||||
if (activeOnly) it and (HorseTable.istAktiv eq true) else it
|
||||
}
|
||||
}.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun findFeiRegistered(activeOnly: Boolean): List<DomPferd> = transaction {
|
||||
HorseTable.selectAll().where {
|
||||
HorseTable.feiNummer.isNotNull().let {
|
||||
if (activeOnly) it and (HorseTable.istAktiv eq true) else it
|
||||
}
|
||||
}.map { rowToDomPferd(it) }
|
||||
}
|
||||
|
||||
override suspend fun save(horse: DomPferd): DomPferd = transaction {
|
||||
val existing = HorseTable.selectAll()
|
||||
.where { HorseTable.id eq horse.pferdId.toJavaUuid() }
|
||||
.singleOrNull()
|
||||
if (existing == null) {
|
||||
HorseTable.insert { applyToStatement(it, horse) }
|
||||
} else {
|
||||
HorseTable.update({ HorseTable.id eq horse.pferdId.toJavaUuid() }) {
|
||||
applyToStatement(it, horse.copy(updatedAt = Clock.System.now()))
|
||||
}
|
||||
}
|
||||
horse
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Uuid): Boolean = transaction {
|
||||
HorseTable.deleteWhere { HorseTable.id eq id.toJavaUuid() } > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByLebensnummer(lebensnummer: String): Boolean = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.lebensnummer eq lebensnummer }.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByChipNummer(chipNummer: String): Boolean = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.chipNummer eq chipNummer }.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByPassNummer(passNummer: String): Boolean = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.passNummer eq passNummer }.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByOepsNummer(oepsNummer: String): Boolean = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.oepsNummer eq oepsNummer }.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByFeiNummer(feiNummer: String): Boolean = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.feiNummer eq feiNummer }.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun countActive(): Long = transaction {
|
||||
HorseTable.selectAll().where { HorseTable.istAktiv eq true }.count()
|
||||
}
|
||||
|
||||
override suspend fun countByOwnerId(ownerId: Uuid, activeOnly: Boolean): Long = transaction {
|
||||
HorseTable.selectAll().where {
|
||||
(HorseTable.besitzerId eq ownerId.toJavaUuid()).let {
|
||||
if (activeOnly) it and (HorseTable.istAktiv eq true) else it
|
||||
}
|
||||
}.count()
|
||||
}
|
||||
|
||||
override suspend fun countOepsRegistered(activeOnly: Boolean): Long = transaction {
|
||||
HorseTable.selectAll().where {
|
||||
HorseTable.oepsNummer.isNotNull().let {
|
||||
if (activeOnly) it and (HorseTable.istAktiv eq true) else it
|
||||
}
|
||||
}.count()
|
||||
}
|
||||
|
||||
override suspend fun countFeiRegistered(activeOnly: Boolean): Long = transaction {
|
||||
HorseTable.selectAll().where {
|
||||
HorseTable.feiNummer.isNotNull().let {
|
||||
if (activeOnly) it and (HorseTable.istAktiv eq true) else it
|
||||
}
|
||||
}.count()
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Hilfsmethoden
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private fun rowToDomPferd(row: ResultRow): DomPferd = DomPferd(
|
||||
pferdId = row[HorseTable.id].value.toKotlinUuid(),
|
||||
pferdeName = row[HorseTable.pferdeName],
|
||||
geschlecht = row[HorseTable.geschlecht],
|
||||
geburtsdatum = row[HorseTable.geburtsdatum],
|
||||
rasse = row[HorseTable.rasse],
|
||||
farbe = row[HorseTable.farbe],
|
||||
besitzerId = row[HorseTable.besitzerId]?.toKotlinUuid(),
|
||||
verantwortlichePersonId = row[HorseTable.verantwortlichePersonId]?.toKotlinUuid(),
|
||||
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]
|
||||
)
|
||||
|
||||
private fun applyToStatement(statement: UpdateBuilder<*>, horse: DomPferd) {
|
||||
statement[HorseTable.id] = horse.pferdId.toJavaUuid()
|
||||
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?.toJavaUuid()
|
||||
statement[HorseTable.verantwortlichePersonId] = horse.verantwortlichePersonId?.toJavaUuid()
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user