Remove deprecated ZnsLegacyParsersTest.kt, synchronize database schema with Exposed domain models (migration V010), add license-related fields to Reiter, integrate updated LicenseMatrixService fallback logic, improve ZnsImportService with file archiving, and add ZNS testing runbook.

This commit is contained in:
2026-04-06 01:45:49 +02:00
parent e94dc5a803
commit aa9e2da3a3
13 changed files with 260 additions and 283 deletions
@@ -1,7 +1,13 @@
package at.mocode.zns.import.service
import at.mocode.masterdata.domain.repository.FunktionaerRepository
import at.mocode.masterdata.domain.repository.HorseRepository
import at.mocode.masterdata.domain.repository.ReiterRepository
import at.mocode.masterdata.domain.repository.VereinRepository
import at.mocode.zns.importer.ZnsImportService
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
@SpringBootApplication
@@ -11,7 +17,18 @@ import org.springframework.context.annotation.ComponentScan
"at.mocode.masterdata.infrastructure"
]
)
class ZnsImportServiceApplication
class ZnsImportServiceApplication {
@Bean
fun znsImportService(
vereinRepository: VereinRepository,
reiterRepository: ReiterRepository,
horseRepository: HorseRepository,
funktionaerRepository: FunktionaerRepository
): ZnsImportService {
return ZnsImportService(vereinRepository, reiterRepository, horseRepository, funktionaerRepository)
}
}
fun main(args: Array<String>) {
runApplication<ZnsImportServiceApplication>(*args)
@@ -5,7 +5,7 @@ import java.util.concurrent.ConcurrentHashMap
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
enum class ImportJobStatus { AUSSTEHEND, ENTPACKEN, LADE_VEREINE, LADE_REITER, LADE_PFERDE, LADE_RICHTER, ABGESCHLOSSEN, FEHLER }
enum class ImportJobStatus { AUSSTEHEND, ENTPACKEN, VERARBEITUNG, ABGESCHLOSSEN, FEHLER }
data class ImportJob(
val jobId: String,
@@ -1,23 +1,21 @@
package at.mocode.zns.import.service.job
import at.mocode.masterdata.domain.repository.VereinRepository
import at.mocode.masterdata.domain.repository.HorseRepository
import at.mocode.masterdata.domain.repository.FunktionaerRepository
import at.mocode.masterdata.domain.repository.ReiterRepository
import at.mocode.zns.importer.ZnsImportService
import at.mocode.zns.importer.ZnsImportResult
import at.mocode.zns.importer.ZnsImportService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.io.File
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
@Service
class ZnsImportOrchestrator(
private val vereinRepository: VereinRepository,
private val reiterRepository: ReiterRepository,
private val horseRepository: HorseRepository,
private val funktionaerRepository: FunktionaerRepository,
private val jobRegistry: ImportJobRegistry
private val service: ZnsImportService,
private val jobRegistry: ImportJobRegistry,
@Value("\${app.zns.archive-path}") private val archivePath: String
) {
private val scope = CoroutineScope(Dispatchers.IO)
@@ -26,38 +24,15 @@ class ZnsImportOrchestrator(
runCatching {
jobRegistry.aktualisiereStatus(jobId, ImportJobStatus.ENTPACKEN, "Entpacke ZIP-Datei...", 5)
val service = ZnsImportService(vereinRepository, reiterRepository, horseRepository, funktionaerRepository)
// Archivierung
archiviereZip(zipBytes)
val dateien = service.extrahiereDateien(zipBytes.inputStream())
jobRegistry.aktualisiereStatus(jobId, ImportJobStatus.LADE_VEREINE, "Lade Vereine...", 20)
val vereineResult = service.importiereVereine(dateien["VEREIN01.DAT"] ?: emptyList(), mutableListOf())
jobRegistry.aktualisiereStatus(jobId, ImportJobStatus.LADE_REITER, "Lade Reiter...", 40)
val reiterResult = service.importiereReiter(dateien["LIZENZ01.DAT"] ?: emptyList(), mutableListOf(), mutableListOf())
jobRegistry.aktualisiereStatus(jobId, ImportJobStatus.LADE_PFERDE, "Lade Pferde...", 60)
val pferdeResult = service.importierePferde(dateien["PFERDE01.DAT"] ?: emptyList(), mutableListOf())
jobRegistry.aktualisiereStatus(jobId, ImportJobStatus.LADE_RICHTER, "Lade Funktionäre...", 80)
val richterResult = service.importiereFunktionaere(dateien["RICHT01.DAT"] ?: emptyList(), mutableListOf(), mutableListOf())
val result = ZnsImportResult(
vereineImportiert = vereineResult.first,
vereineAktualisiert = vereineResult.second,
reiterImportiert = reiterResult.first,
reiterAktualisiert = reiterResult.second,
pferdeImportiert = pferdeResult.first,
pferdeAktualisiert = pferdeResult.second,
richterImportiert = richterResult.first,
richterAktualisiert = richterResult.second
)
jobRegistry.aktualisiereStatus(jobId, ImportJobStatus.VERARBEITUNG, "Verarbeite ZNS-Daten...", 20)
val result = service.importiereZip(zipBytes.inputStream())
jobRegistry.aktualisiereStatus(
jobId, ImportJobStatus.ABGESCHLOSSEN,
"Import abgeschlossen: ${result.vereineImportiert} Vereine, " +
"${result.reiterImportiert} Reiter, ${result.pferdeImportiert} Pferde, " +
"${result.richterImportiert} Richter importiert.", 100
result.zusammenfassung(), 100
)
jobRegistry.findeJob(jobId)?.let { job ->
@@ -70,4 +45,18 @@ class ZnsImportOrchestrator(
}
}
}
private fun archiviereZip(bytes: ByteArray) {
try {
val dir = File(archivePath)
if (!dir.exists()) dir.mkdirs()
val timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"))
val archiveFile = File(dir, "zns_import_$timestamp.zip")
archiveFile.writeBytes(bytes)
} catch (e: Exception) {
// Archivierung schlägt fehl -> Loggen aber Import nicht abbrechen
println("[WARN] Archivierung der ZNS-Datei fehlgeschlagen: ${e.message}")
}
}
}
@@ -42,3 +42,5 @@ management:
app:
service-name: ${spring.application.name}
zns:
archive-path: ${ZNS_ARCHIVE_PATH:/data/zns/archive}