einige Ergänzungen

This commit is contained in:
2025-07-25 23:16:16 +02:00
parent 4c382e64a5
commit 7e0b56a247
70 changed files with 7795 additions and 1894 deletions
@@ -1,12 +1,11 @@
package at.mocode.members.infrastructure.persistence
import at.mocode.core.utils.database.DatabaseFactory
import at.mocode.members.domain.model.Member
import at.mocode.members.domain.repository.MemberRepository
import at.mocode.core.utils.database.DatabaseFactory
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuidFrom
import kotlinx.datetime.LocalDate
import kotlinx.datetime.Clock
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import org.jetbrains.exposed.sql.*
@@ -20,34 +19,34 @@ import org.springframework.stereotype.Repository
class MemberRepositoryImpl : MemberRepository {
override suspend fun findById(id: Uuid): Member? = DatabaseFactory.dbQuery {
MemberTable.select { MemberTable.id eq id }
MemberTable.selectAll().where { MemberTable.id eq id }
.map { rowToMember(it) }
.singleOrNull()
}
override suspend fun findByMembershipNumber(membershipNumber: String): Member? = DatabaseFactory.dbQuery {
MemberTable.select { MemberTable.membershipNumber eq membershipNumber }
MemberTable.selectAll().where { MemberTable.membershipNumber eq membershipNumber }
.map { rowToMember(it) }
.singleOrNull()
}
override suspend fun findByEmail(email: String): Member? = DatabaseFactory.dbQuery {
MemberTable.select { MemberTable.email.lowerCase() eq email.lowercase() }
MemberTable.selectAll().where { MemberTable.email.lowerCase() eq email.lowercase() }
.map { rowToMember(it) }
.singleOrNull()
}
override suspend fun findByName(searchTerm: String, limit: Int): List<Member> = DatabaseFactory.dbQuery {
MemberTable.select {
MemberTable.selectAll().where {
(MemberTable.firstName.lowerCase() like "%${searchTerm.lowercase()}%") or
(MemberTable.lastName.lowerCase() like "%${searchTerm.lowercase()}%")
(MemberTable.lastName.lowerCase() like "%${searchTerm.lowercase()}%")
}
.limit(limit)
.map { rowToMember(it) }
}
override suspend fun findAllActive(limit: Int, offset: Int): List<Member> = DatabaseFactory.dbQuery {
MemberTable.select { MemberTable.isActive eq true }
MemberTable.selectAll().where { MemberTable.isActive eq true }
.limit(limit, offset.toLong())
.map { rowToMember(it) }
}
@@ -59,18 +58,18 @@ class MemberRepositoryImpl : MemberRepository {
}
override suspend fun findByMembershipStartDateRange(startDate: LocalDate, endDate: LocalDate): List<Member> = DatabaseFactory.dbQuery {
MemberTable.select {
MemberTable.selectAll().where {
(MemberTable.membershipStartDate greaterEq startDate) and
(MemberTable.membershipStartDate lessEq endDate)
(MemberTable.membershipStartDate lessEq endDate)
}
.map { rowToMember(it) }
}
override suspend fun findByMembershipEndDateRange(startDate: LocalDate, endDate: LocalDate): List<Member> = DatabaseFactory.dbQuery {
MemberTable.select {
MemberTable.selectAll().where {
(MemberTable.membershipEndDate.isNotNull()) and
(MemberTable.membershipEndDate greaterEq startDate) and
(MemberTable.membershipEndDate lessEq endDate)
(MemberTable.membershipEndDate greaterEq startDate) and
(MemberTable.membershipEndDate lessEq endDate)
}
.map { rowToMember(it) }
}
@@ -78,16 +77,16 @@ class MemberRepositoryImpl : MemberRepository {
override suspend fun findMembersWithExpiringMembership(daysAhead: Int): List<Member> = DatabaseFactory.dbQuery {
val currentDate = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date
val futureDate = LocalDate(currentDate.year, currentDate.month, currentDate.dayOfMonth + daysAhead)
MemberTable.select {
MemberTable.selectAll().where {
(MemberTable.membershipEndDate.isNotNull()) and
(MemberTable.membershipEndDate lessEq futureDate) and
(MemberTable.isActive eq true)
(MemberTable.membershipEndDate lessEq futureDate) and
(MemberTable.isActive eq true)
}
.map { rowToMember(it) }
}
override suspend fun save(member: Member): Member = DatabaseFactory.dbQuery {
val existingMember = MemberTable.select { MemberTable.id eq member.memberId }.singleOrNull()
val existingMember = MemberTable.selectAll().where { MemberTable.id eq member.memberId }.singleOrNull()
if (existingMember != null) {
// Update existing member
@@ -130,7 +129,7 @@ class MemberRepositoryImpl : MemberRepository {
}
override suspend fun countActive(): Long = DatabaseFactory.dbQuery {
MemberTable.select { MemberTable.isActive eq true }.count()
MemberTable.selectAll().where { MemberTable.isActive eq true }.count()
}
override suspend fun countAll(): Long = DatabaseFactory.dbQuery {
@@ -139,24 +138,24 @@ class MemberRepositoryImpl : MemberRepository {
override suspend fun existsByMembershipNumber(membershipNumber: String, excludeMemberId: Uuid?): Boolean = DatabaseFactory.dbQuery {
val query = if (excludeMemberId != null) {
MemberTable.select {
MemberTable.selectAll().where {
(MemberTable.membershipNumber eq membershipNumber) and
(MemberTable.id neq excludeMemberId)
(MemberTable.id neq excludeMemberId)
}
} else {
MemberTable.select { MemberTable.membershipNumber eq membershipNumber }
MemberTable.selectAll().where { MemberTable.membershipNumber eq membershipNumber }
}
query.count() > 0
}
override suspend fun existsByEmail(email: String, excludeMemberId: Uuid?): Boolean = DatabaseFactory.dbQuery {
val query = if (excludeMemberId != null) {
MemberTable.select {
MemberTable.selectAll().where {
(MemberTable.email.lowerCase() eq email.lowercase()) and
(MemberTable.id neq excludeMemberId)
(MemberTable.id neq excludeMemberId)
}
} else {
MemberTable.select { MemberTable.email.lowerCase() eq email.lowercase() }
MemberTable.selectAll().where { MemberTable.email.lowerCase() eq email.lowercase() }
}
query.count() > 0
}
+9
View File
@@ -11,6 +11,8 @@ springBoot {
dependencies {
implementation(projects.platform.platformDependencies)
implementation(projects.core.coreDomain)
implementation(projects.core.coreUtils)
implementation(projects.members.membersDomain)
implementation(projects.members.membersApplication)
implementation(projects.members.membersInfrastructure)
@@ -26,7 +28,14 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui")
// Database dependencies
implementation("org.jetbrains.exposed:exposed-core")
implementation("org.jetbrains.exposed:exposed-dao")
implementation("org.jetbrains.exposed:exposed-jdbc")
implementation("org.jetbrains.exposed:exposed-kotlin-datetime")
implementation("com.zaxxer:HikariCP")
runtimeOnly("org.postgresql:postgresql")
testRuntimeOnly("com.h2database:h2")
testImplementation(projects.platform.platformTesting)
}
@@ -0,0 +1,104 @@
package at.mocode.members.service.config
import at.mocode.core.utils.database.DatabaseConfig
import at.mocode.core.utils.database.DatabaseFactory
import at.mocode.members.infrastructure.persistence.MemberTable
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import jakarta.annotation.PostConstruct
import jakarta.annotation.PreDestroy
import org.slf4j.LoggerFactory
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.transactions.transaction
/**
* Database configuration for the Members Service.
*
* This configuration ensures that Database.connect() is called properly
* before any Exposed operations are performed.
*/
@Configuration
@Profile("!test")
class MembersDatabaseConfiguration {
private val log = LoggerFactory.getLogger(MembersDatabaseConfiguration::class.java)
@PostConstruct
fun initializeDatabase() {
log.info("Initializing database schema for Members Service...")
try {
// Database connection is already initialized by the gateway
// Only initialize the schema for this service
transaction {
SchemaUtils.createMissingTablesAndColumns(MemberTable)
log.info("Members database schema initialized successfully")
}
} catch (e: Exception) {
log.error("Failed to initialize database schema", e)
throw e
}
}
@PreDestroy
fun closeDatabase() {
log.info("Closing database connection for Members Service...")
try {
DatabaseFactory.close()
log.info("Database connection closed successfully")
} catch (e: Exception) {
log.error("Error closing database connection", e)
}
}
}
/**
* Test-specific database configuration.
*/
@Configuration
@Profile("test")
class MembersTestDatabaseConfiguration {
private val log = LoggerFactory.getLogger(MembersTestDatabaseConfiguration::class.java)
@PostConstruct
fun initializeTestDatabase() {
log.info("Initializing test database connection for Members Service...")
try {
// Use H2 in-memory database for tests
val testConfig = DatabaseConfig(
jdbcUrl = "jdbc:h2:mem:members_test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE",
username = "sa",
password = "",
driverClassName = "org.h2.Driver",
maxPoolSize = 5,
minPoolSize = 1,
autoMigrate = true
)
DatabaseFactory.init(testConfig)
log.info("Test database connection initialized successfully")
// Initialize database schema for tests
transaction {
SchemaUtils.createMissingTablesAndColumns(MemberTable)
log.info("Test members database schema initialized successfully")
}
} catch (e: Exception) {
log.error("Failed to initialize test database connection", e)
throw e
}
}
@PreDestroy
fun closeTestDatabase() {
log.info("Closing test database connection for Members Service...")
try {
DatabaseFactory.close()
log.info("Test database connection closed successfully")
} catch (e: Exception) {
log.error("Error closing test database connection", e)
}
}
}