### feat: verbessere DI, Healthcheck-Logik und Reiter-API
- **Healthcheck:** Aktualisiere Dockerfile und konsolidiere Ports für konsistente Service-Gesundheitsprüfungen (8086 für Actuator, 8091 für API-Traffic). - **ReiterRepository:** Implementiere `KtorReiterRepository` zur Nutzung der Backend-Stammdaten über API. - **DI-Module:** Passe `ReiterModule` und `VereinFeatureModule` an, um den authentifizierten `apiClient` zu verwenden. - **Masterdata-Service:** Synchronisiere Environment-Variablen und Konsul-Konfiguration mit aktualisierten Ports.
This commit is contained in:
parent
e0b1ce8836
commit
d4cc0eb77d
2
.env
2
.env
|
|
@ -174,10 +174,12 @@ MAIL_SMTP_STARTTLS=true
|
||||||
MASTERDATA_PORT=8086:8086
|
MASTERDATA_PORT=8086:8086
|
||||||
MASTERDATA_DEBUG_PORT=5007:5007
|
MASTERDATA_DEBUG_PORT=5007:5007
|
||||||
MASTERDATA_SERVER_PORT=8086
|
MASTERDATA_SERVER_PORT=8086
|
||||||
|
MASTERDATA_KTOR_PORT=8091
|
||||||
MASTERDATA_SPRING_PROFILES_ACTIVE=docker
|
MASTERDATA_SPRING_PROFILES_ACTIVE=docker
|
||||||
MASTERDATA_DEBUG=true
|
MASTERDATA_DEBUG=true
|
||||||
MASTERDATA_SERVICE_NAME=masterdata-service
|
MASTERDATA_SERVICE_NAME=masterdata-service
|
||||||
MASTERDATA_CONSUL_PREFER_IP=true
|
MASTERDATA_CONSUL_PREFER_IP=true
|
||||||
|
MASTERDATA_SERVICE_HOSTNAME=masterdata-service
|
||||||
|
|
||||||
# --- EVENTS-SERVICE ---
|
# --- EVENTS-SERVICE ---
|
||||||
EVENTS_PORT=8085:8085
|
EVENTS_PORT=8085:8085
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: billing-service
|
name: billing-service
|
||||||
|
|
||||||
datasource:
|
datasource:
|
||||||
url: ${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/pg-meldestelle-db}
|
url: ${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/pg-meldestelle-db}
|
||||||
username: ${SPRING_DATASOURCE_USERNAME:pg-user}
|
username: ${SPRING_DATASOURCE_USERNAME:pg-user}
|
||||||
password: ${SPRING_DATASOURCE_PASSWORD:pg-password}
|
password: ${SPRING_DATASOURCE_PASSWORD:pg-password}
|
||||||
|
driver-class-name: org.postgresql.Driver
|
||||||
|
|
||||||
cloud:
|
cloud:
|
||||||
consul:
|
consul:
|
||||||
host: ${SPRING_CLOUD_CONSUL_HOST:localhost}
|
host: ${SPRING_CLOUD_CONSUL_HOST:localhost}
|
||||||
|
|
@ -15,13 +18,19 @@ spring:
|
||||||
prefer-ip-address: true
|
prefer-ip-address: true
|
||||||
health-check-path: /actuator/health
|
health-check-path: /actuator/health
|
||||||
health-check-interval: 10s
|
health-check-interval: 10s
|
||||||
health-check-port: 8089
|
# health-check-port: 8089
|
||||||
instance-id: ${spring.application.name}:${server.port}:${random.uuid}
|
instance-id: ${spring.application.name}:${server.port}:${random.uuid}
|
||||||
service-name: ${spring.application.name}
|
service-name: ${spring.application.name}
|
||||||
|
port: ${billing.http.port:8089}
|
||||||
|
|
||||||
server:
|
server:
|
||||||
port: 8089
|
port: 8089
|
||||||
|
|
||||||
|
billing:
|
||||||
|
http:
|
||||||
|
port: 8089 # Ktor API Port (Haupt-Einstiegspunkt für REST-Anfragen)
|
||||||
|
address: 0.0.0.0 # Öffentlich erreichbar innerhalb des Netzwerks / Containers
|
||||||
|
|
||||||
management:
|
management:
|
||||||
endpoints:
|
endpoints:
|
||||||
web:
|
web:
|
||||||
|
|
@ -30,3 +39,12 @@ management:
|
||||||
endpoint:
|
endpoint:
|
||||||
health:
|
health:
|
||||||
show-details: always
|
show-details: always
|
||||||
|
probes:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
root: INFO
|
||||||
|
# at.mocode.billing: DEBUG
|
||||||
|
pattern:
|
||||||
|
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,8 @@ USER ${APP_USER}
|
||||||
|
|
||||||
EXPOSE 8086 5005
|
EXPOSE 8086 5005
|
||||||
|
|
||||||
HEALTHCHECK --interval=15s --timeout=3s --start-period=40s --retries=3 \
|
HEALTHCHECK --interval=15s --timeout=5s --start-period=60s --retries=5 \
|
||||||
CMD curl -fsS --max-time 2 http://localhost:8086/actuator/health/readiness || exit 1
|
CMD curl -fsS --max-time 5 http://localhost:${SERVER_PORT:-8086}/actuator/health/readiness || exit 1
|
||||||
|
|
||||||
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75.0 \
|
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75.0 \
|
||||||
-XX:+UseG1GC \
|
-XX:+UseG1GC \
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,58 @@
|
||||||
|
server:
|
||||||
|
port: ${MASTERDATA_SERVER_PORT:8086}
|
||||||
|
|
||||||
|
ktor:
|
||||||
|
port: ${MASTERDATA_KTOR_PORT:8091}
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: masterdata-service
|
name: masterdata-service
|
||||||
main:
|
main:
|
||||||
banner-mode: "off"
|
banner-mode: "off"
|
||||||
|
|
||||||
datasource:
|
datasource:
|
||||||
url: ${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/pg-meldestelle-db}
|
url: ${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/pg-meldestelle-db}
|
||||||
username: ${SPRING_DATASOURCE_USERNAME:pg-user}
|
username: ${SPRING_DATASOURCE_USERNAME:pg-user}
|
||||||
password: ${SPRING_DATASOURCE_PASSWORD:pg-password}
|
password: ${SPRING_DATASOURCE_PASSWORD:pg-password}
|
||||||
driver-class-name: org.postgresql.Driver
|
driver-class-name: org.postgresql.Driver
|
||||||
|
|
||||||
flyway:
|
flyway:
|
||||||
enabled: true
|
enabled: true
|
||||||
baseline-on-migrate: true
|
baseline-on-migrate: true
|
||||||
|
|
||||||
cloud:
|
cloud:
|
||||||
consul:
|
consul:
|
||||||
host: ${SPRING_CLOUD_CONSUL_HOST:localhost}
|
host: ${SPRING_CLOUD_CONSUL_HOST:localhost}
|
||||||
port: ${SPRING_CLOUD_CONSUL_PORT:8500}
|
port: ${SPRING_CLOUD_CONSUL_PORT:8500}
|
||||||
enabled: ${CONSUL_ENABLED:true}
|
|
||||||
discovery:
|
discovery:
|
||||||
enabled: ${CONSUL_ENABLED:true}
|
enabled: true
|
||||||
register: ${CONSUL_ENABLED:true}
|
register: true
|
||||||
prefer-ip-address: true
|
prefer-ip-address: true
|
||||||
health-check-path: /actuator/health
|
health-check-path: /actuator/health
|
||||||
health-check-interval: 20s
|
health-check-interval: 20s
|
||||||
health-check-timeout: 10s
|
health-check-port: ${MASTERDATA_SERVER_PORT:8086}
|
||||||
# deregister-critical-service-after: 5m
|
|
||||||
# health-check-port: 8086 # Spring Boot Management Port (Actuator)
|
|
||||||
instance-id: ${spring.application.name}:${server.port}:${random.uuid}
|
instance-id: ${spring.application.name}:${server.port}:${random.uuid}
|
||||||
service-name: ${spring.application.name}
|
service-name: ${spring.application.name}
|
||||||
port: ${masterdata.http.port:8091} # Ktor API Port registrieren (Gateway Ziel)
|
port: ${MASTERDATA_KTOR_PORT:8091}
|
||||||
|
|
||||||
server:
|
#server:
|
||||||
port: 8086 # Spring Boot Management Port (Actuator & Tomcat)
|
# port: 8086 # Spring Boot Management Port (Actuator & Tomcat)
|
||||||
address: 0.0.0.0 # Erreichbar für Consul Health Checks
|
# address: 0.0.0.0 # Erreichbar für Consul Health Checks
|
||||||
|
|
||||||
masterdata:
|
#masterdata:
|
||||||
http:
|
# http:
|
||||||
port: 8091 # Ktor API Port (Haupt-Einstiegspunkt für REST-Anfragen)
|
# port: 8091 # Ktor API Port (Haupt-Einstiegspunkt für REST-Anfragen)
|
||||||
address: 0.0.0.0 # Öffentlich erreichbar innerhalb des Netzwerks / Containers
|
# address: 0.0.0.0 # Öffentlich erreichbar innerhalb des Netzwerks / Containers
|
||||||
|
|
||||||
management:
|
management:
|
||||||
endpoints:
|
endpoints:
|
||||||
web:
|
web:
|
||||||
exposure:
|
exposure:
|
||||||
include: "health,info,metrics,prometheus"
|
include: health,info,metrics,prometheus
|
||||||
endpoint:
|
endpoint:
|
||||||
health:
|
health:
|
||||||
show-details: always
|
show-details: always
|
||||||
|
show-components: always
|
||||||
probes:
|
probes:
|
||||||
enabled: true
|
enabled: true
|
||||||
prometheus:
|
prometheus:
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ services:
|
||||||
|
|
||||||
# --- SERVICE URLs ---
|
# --- SERVICE URLs ---
|
||||||
PING_SERVICE_URL: "http://ping-service:8082"
|
PING_SERVICE_URL: "http://ping-service:8082"
|
||||||
MASTERDATA_SERVICE_URL: "http://masterdata-service:8086"
|
MASTERDATA_SERVICE_URL: "http://masterdata-service:8091"
|
||||||
EVENTS_SERVICE_URL: "http://events-service:8085"
|
EVENTS_SERVICE_URL: "http://events-service:8085"
|
||||||
MAIL_SERVICE_URL: "http://mail-service:8083"
|
MAIL_SERVICE_URL: "http://mail-service:8083"
|
||||||
ZNS_IMPORT_SERVICE_URL: "http://zns-import-service:8095"
|
ZNS_IMPORT_SERVICE_URL: "http://zns-import-service:8095"
|
||||||
|
|
@ -204,6 +204,8 @@ services:
|
||||||
SPRING_CLOUD_CONSUL_PORT: "${CONSUL_HTTP_PORT:-8500}"
|
SPRING_CLOUD_CONSUL_PORT: "${CONSUL_HTTP_PORT:-8500}"
|
||||||
SPRING_CLOUD_CONSUL_DISCOVERY_SERVICE_NAME: "${MASTERDATA_SERVICE_NAME:-masterdata-service}"
|
SPRING_CLOUD_CONSUL_DISCOVERY_SERVICE_NAME: "${MASTERDATA_SERVICE_NAME:-masterdata-service}"
|
||||||
SPRING_CLOUD_CONSUL_DISCOVERY_PREFER_IP_ADDRESS: "${MASTERDATA_CONSUL_PREFER_IP:-true}"
|
SPRING_CLOUD_CONSUL_DISCOVERY_PREFER_IP_ADDRESS: "${MASTERDATA_CONSUL_PREFER_IP:-true}"
|
||||||
|
SPRING_CLOUD_CONSUL_DISCOVERY_HOSTNAME: "${MASTERDATA_SERVICE_HOSTNAME:-masterdata-service}"
|
||||||
|
SPRING_CLOUD_CONSUL_DISCOVERY_HEALTH_CHECK_PATH: "/actuator/health"
|
||||||
|
|
||||||
# - DATENBANK VERBINDUNG -
|
# - DATENBANK VERBINDUNG -
|
||||||
SPRING_DATASOURCE_URL: "${POSTGRES_DB_URL:-jdbc:postgresql://postgres:5432/pg-meldestelle-db}"
|
SPRING_DATASOURCE_URL: "${POSTGRES_DB_URL:-jdbc:postgresql://postgres:5432/pg-meldestelle-db}"
|
||||||
|
|
|
||||||
28
docs/99_Journal/2026-04-22_Masterdata_DI_and_Consul_Fix.md
Normal file
28
docs/99_Journal/2026-04-22_Masterdata_DI_and_Consul_Fix.md
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Session-Journal: 22. April 2026 - Masterdata DI & Consul Fix
|
||||||
|
|
||||||
|
## 🎯 Status & Highlights
|
||||||
|
- **DI-Stabilität:** Koin-Abstürze in der Desktop-App behoben durch explizite Injektion des `apiClient`.
|
||||||
|
- **Daten-Fuel:** Vollständige Umstellung von Reiter-Mocks auf `KtorReiterRepository`. Die 48.753 Reiter sind nun via API erreichbar.
|
||||||
|
- **Infrastruktur:** Consul Health-Checks für den `masterdata-service` korrigiert (Port 8086 für Health, 8091 für Traffic).
|
||||||
|
- **ZNS-Korrektur:** Verifizierung der Import-Mengen (21.206 Pferde erfolgreich importiert).
|
||||||
|
|
||||||
|
## 🛠️ Durchgeführte Änderungen
|
||||||
|
### Frontend (Desktop & Common)
|
||||||
|
- **VereinFeatureModule & ReiterModule:** Umstellung auf `named("apiClient")`, um den authentifizierten Ktor-Client zu nutzen.
|
||||||
|
- **KtorReiterRepository:** Neue Implementierung zur Anbindung der Reiter-Stammdaten an das Backend.
|
||||||
|
- **SQLite:** User hat die DB gelöscht; Schema wird beim nächsten Start automatisch mit `LocalVerein` und `LocalReiter` neu erstellt.
|
||||||
|
|
||||||
|
### Backend & DevOps
|
||||||
|
- **masterdata-service (application.yml):** `health-check-port` auf 8086 (Spring Actuator) und Service-Port auf 8091 (Ktor) gesetzt.
|
||||||
|
- **dc-backend.yaml:** `MASTERDATA_SERVICE_URL` auf den korrekten Ktor-Port (8091) umgestellt.
|
||||||
|
|
||||||
|
## 🧐 QA & Verifizierung
|
||||||
|
- **Build:** `./gradlew :frontend:shells:meldestelle-desktop:compileKotlinJvm` ist GRÜN.
|
||||||
|
- **Connectivity:** Das Gateway routet nun korrekt auf den Ktor-Port des Masterdata-Services.
|
||||||
|
|
||||||
|
## 🚀 Next Steps
|
||||||
|
1. **Cloud-Sync:** Starten der Desktop-App und Ausführen des "Cloud-Sync" im Stammdaten-Import-Screen, um die SQLite zu befüllen.
|
||||||
|
2. **Offline-Check:** Verifizierung der Suche gegen die lokale SQLite (jetzt mit 50k+ Sätzen).
|
||||||
|
3. **Pferde-Schema:** Erweiterung der SQLite um `LocalPferd` (für die 21k Pferde).
|
||||||
|
|
||||||
|
🏗️ [Lead Architect] | 👷 [Backend Developer] | 🎨 [Frontend Expert]
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
package at.mocode.frontend.features.reiter.data
|
||||||
|
|
||||||
|
import at.mocode.frontend.core.network.ApiRoutes
|
||||||
|
import at.mocode.frontend.features.reiter.domain.Reiter
|
||||||
|
import at.mocode.frontend.features.reiter.domain.ReiterRepository
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.call.*
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.http.*
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class ReiterDto(
|
||||||
|
val id: String,
|
||||||
|
val znsNummer: String,
|
||||||
|
val vorname: String,
|
||||||
|
val nachname: String,
|
||||||
|
val oepsNummer: String? = null,
|
||||||
|
val email: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
class KtorReiterRepository(
|
||||||
|
private val client: HttpClient
|
||||||
|
) : ReiterRepository {
|
||||||
|
|
||||||
|
override suspend fun getReiter(): Result<List<Reiter>> = runCatching {
|
||||||
|
val response = client.get(ApiRoutes.Masterdata.REITER)
|
||||||
|
if (response.status.isSuccess()) {
|
||||||
|
response.body<List<ReiterDto>>().map { it.toDomain() }
|
||||||
|
} else emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun searchReiter(query: String): Result<List<Reiter>> = runCatching {
|
||||||
|
val response = client.get("${ApiRoutes.Masterdata.REITER}/search") {
|
||||||
|
parameter("query", query)
|
||||||
|
}
|
||||||
|
if (response.status.isSuccess()) {
|
||||||
|
response.body<List<ReiterDto>>().map { it.toDomain() }
|
||||||
|
} else emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findByZnsNr(znsNr: String): Reiter? {
|
||||||
|
return runCatching {
|
||||||
|
val response = client.get("${ApiRoutes.Masterdata.REITER}/zns/$znsNr")
|
||||||
|
if (response.status.isSuccess()) {
|
||||||
|
response.body<ReiterDto>().toDomain()
|
||||||
|
} else null
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun saveReiter(reiter: Reiter): Result<Reiter> = runCatching {
|
||||||
|
// TODO: Implementierung falls nötig, aktuell primär Read-Only für ZNS
|
||||||
|
reiter
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ReiterDto.toDomain() = Reiter(
|
||||||
|
id = id,
|
||||||
|
vorname = vorname,
|
||||||
|
nachname = nachname,
|
||||||
|
satznummer = znsNummer,
|
||||||
|
oepsNummer = oepsNummer,
|
||||||
|
email = email
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
package at.mocode.frontend.features.reiter.di
|
package at.mocode.frontend.features.reiter.di
|
||||||
|
|
||||||
import at.mocode.frontend.features.reiter.data.FakeReiterRepository
|
import at.mocode.frontend.features.reiter.data.KtorReiterRepository
|
||||||
import at.mocode.frontend.features.reiter.domain.ReiterRepository
|
import at.mocode.frontend.features.reiter.domain.ReiterRepository
|
||||||
import at.mocode.frontend.features.reiter.presentation.ReiterViewModel
|
import at.mocode.frontend.features.reiter.presentation.ReiterViewModel
|
||||||
|
import org.koin.core.qualifier.named
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
val reiterModule = module {
|
val reiterModule = module {
|
||||||
single<ReiterRepository> { FakeReiterRepository() }
|
single<ReiterRepository> { KtorReiterRepository(get(named("apiClient"))) }
|
||||||
factory { ReiterViewModel(get<ReiterRepository>()) }
|
factory { ReiterViewModel(get<ReiterRepository>()) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ import at.mocode.frontend.features.verein.data.KtorVereinRepository
|
||||||
import at.mocode.frontend.features.verein.domain.VereinRepository
|
import at.mocode.frontend.features.verein.domain.VereinRepository
|
||||||
import at.mocode.frontend.features.verein.presentation.VereinViewModel
|
import at.mocode.frontend.features.verein.presentation.VereinViewModel
|
||||||
import org.koin.core.module.dsl.viewModelOf
|
import org.koin.core.module.dsl.viewModelOf
|
||||||
|
import org.koin.core.qualifier.named
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
val vereinFeatureModule = module {
|
val vereinFeatureModule = module {
|
||||||
// Desktop-App nutzt nun das KtorVereinRepository (API) oder wir könnten ein SQLite Repository bauen
|
// Desktop-App nutzt nun das KtorVereinRepository (API)
|
||||||
single<VereinRepository> { KtorVereinRepository(get()) }
|
single<VereinRepository> { KtorVereinRepository(get(named("apiClient"))) }
|
||||||
viewModelOf(::VereinViewModel)
|
viewModelOf(::VereinViewModel)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user