einige Ergänzungen
This commit is contained in:
+24
-25
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
+104
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user