fixing web-app
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
plugins {
|
||||
// kotlin("jvm")
|
||||
// kotlin("plugin.spring")
|
||||
// kotlin("plugin.jpa") version "2.1.21"
|
||||
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
alias(libs.plugins.kotlin.spring)
|
||||
|
||||
// KORREKTUR: Dieses Plugin ist entscheidend. Es schaltet den `springBoot`-Block
|
||||
// und alle Spring-Boot-spezifischen Gradle-Tasks frei.
|
||||
alias(libs.plugins.spring.boot)
|
||||
|
||||
// Dependency Management für konsistente Spring-Versionen
|
||||
alias(libs.plugins.spring.dependencyManagement)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(platform(projects.platform.platformBom))
|
||||
|
||||
implementation(projects.members.membersDomain)
|
||||
implementation(projects.members.membersApplication)
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.core.coreUtils)
|
||||
implementation(projects.infrastructure.cache.cacheApi)
|
||||
implementation(projects.infrastructure.eventStore.eventStoreApi)
|
||||
implementation(projects.infrastructure.messaging.messagingClient)
|
||||
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("org.postgresql:postgresql")
|
||||
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
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 com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
/**
|
||||
* Database implementation of MemberRepository using Exposed ORM.
|
||||
*/
|
||||
@Repository
|
||||
class MemberRepositoryImpl : MemberRepository {
|
||||
|
||||
override suspend fun findById(id: Uuid): Member? = DatabaseFactory.dbQuery {
|
||||
MemberTable.selectAll().where { MemberTable.id eq id }
|
||||
.map { rowToMember(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByMembershipNumber(membershipNumber: String): Member? = DatabaseFactory.dbQuery {
|
||||
MemberTable.selectAll().where { MemberTable.membershipNumber eq membershipNumber }
|
||||
.map { rowToMember(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByEmail(email: String): Member? = DatabaseFactory.dbQuery {
|
||||
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.selectAll().where {
|
||||
(MemberTable.firstName.lowerCase() like "%${searchTerm.lowercase()}%") or
|
||||
(MemberTable.lastName.lowerCase() like "%${searchTerm.lowercase()}%")
|
||||
}
|
||||
.limit(limit)
|
||||
.map { rowToMember(it) }
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(limit: Int, offset: Int): List<Member> = DatabaseFactory.dbQuery {
|
||||
MemberTable.selectAll().where { MemberTable.isActive eq true }
|
||||
.limit(limit, offset.toLong())
|
||||
.map { rowToMember(it) }
|
||||
}
|
||||
|
||||
override suspend fun findAll(limit: Int, offset: Int): List<Member> = DatabaseFactory.dbQuery {
|
||||
MemberTable.selectAll()
|
||||
.limit(limit, offset.toLong())
|
||||
.map { rowToMember(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByMembershipStartDateRange(startDate: LocalDate, endDate: LocalDate): List<Member> = DatabaseFactory.dbQuery {
|
||||
MemberTable.selectAll().where {
|
||||
(MemberTable.membershipStartDate greaterEq startDate) and
|
||||
(MemberTable.membershipStartDate lessEq endDate)
|
||||
}
|
||||
.map { rowToMember(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByMembershipEndDateRange(startDate: LocalDate, endDate: LocalDate): List<Member> = DatabaseFactory.dbQuery {
|
||||
MemberTable.selectAll().where {
|
||||
(MemberTable.membershipEndDate.isNotNull()) and
|
||||
(MemberTable.membershipEndDate greaterEq startDate) and
|
||||
(MemberTable.membershipEndDate lessEq endDate)
|
||||
}
|
||||
.map { rowToMember(it) }
|
||||
}
|
||||
|
||||
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.selectAll().where {
|
||||
(MemberTable.membershipEndDate.isNotNull()) and
|
||||
(MemberTable.membershipEndDate lessEq futureDate) and
|
||||
(MemberTable.isActive eq true)
|
||||
}
|
||||
.map { rowToMember(it) }
|
||||
}
|
||||
|
||||
override suspend fun save(member: Member): Member = DatabaseFactory.dbQuery {
|
||||
val existingMember = MemberTable.selectAll().where { MemberTable.id eq member.memberId }.singleOrNull()
|
||||
|
||||
if (existingMember != null) {
|
||||
// Update existing member
|
||||
MemberTable.update({ MemberTable.id eq member.memberId }) {
|
||||
it[firstName] = member.firstName
|
||||
it[lastName] = member.lastName
|
||||
it[email] = member.email
|
||||
it[phone] = member.phone
|
||||
it[dateOfBirth] = member.dateOfBirth
|
||||
it[membershipNumber] = member.membershipNumber
|
||||
it[membershipStartDate] = member.membershipStartDate
|
||||
it[membershipEndDate] = member.membershipEndDate
|
||||
it[isActive] = member.isActive
|
||||
it[address] = member.address
|
||||
it[emergencyContact] = member.emergencyContact
|
||||
it[updatedAt] = Clock.System.now()
|
||||
}
|
||||
} else {
|
||||
// Insert new member
|
||||
MemberTable.insert {
|
||||
it[id] = member.memberId
|
||||
it[firstName] = member.firstName
|
||||
it[lastName] = member.lastName
|
||||
it[email] = member.email
|
||||
it[phone] = member.phone
|
||||
it[dateOfBirth] = member.dateOfBirth
|
||||
it[membershipNumber] = member.membershipNumber
|
||||
it[membershipStartDate] = member.membershipStartDate
|
||||
it[membershipEndDate] = member.membershipEndDate
|
||||
it[isActive] = member.isActive
|
||||
it[address] = member.address
|
||||
it[emergencyContact] = member.emergencyContact
|
||||
}
|
||||
}
|
||||
member
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Uuid): Boolean = DatabaseFactory.dbQuery {
|
||||
MemberTable.deleteWhere { MemberTable.id eq id } > 0
|
||||
}
|
||||
|
||||
override suspend fun countActive(): Long = DatabaseFactory.dbQuery {
|
||||
MemberTable.selectAll().where { MemberTable.isActive eq true }.count()
|
||||
}
|
||||
|
||||
override suspend fun countAll(): Long = DatabaseFactory.dbQuery {
|
||||
MemberTable.selectAll().count()
|
||||
}
|
||||
|
||||
override suspend fun existsByMembershipNumber(membershipNumber: String, excludeMemberId: Uuid?): Boolean = DatabaseFactory.dbQuery {
|
||||
val query = if (excludeMemberId != null) {
|
||||
MemberTable.selectAll().where {
|
||||
(MemberTable.membershipNumber eq membershipNumber) and
|
||||
(MemberTable.id neq excludeMemberId)
|
||||
}
|
||||
} else {
|
||||
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.selectAll().where {
|
||||
(MemberTable.email.lowerCase() eq email.lowercase()) and
|
||||
(MemberTable.id neq excludeMemberId)
|
||||
}
|
||||
} else {
|
||||
MemberTable.selectAll().where { MemberTable.email.lowerCase() eq email.lowercase() }
|
||||
}
|
||||
query.count() > 0
|
||||
}
|
||||
|
||||
private fun rowToMember(row: ResultRow): Member {
|
||||
return Member(
|
||||
memberId = row[MemberTable.id],
|
||||
firstName = row[MemberTable.firstName],
|
||||
lastName = row[MemberTable.lastName],
|
||||
email = row[MemberTable.email],
|
||||
phone = row[MemberTable.phone],
|
||||
dateOfBirth = row[MemberTable.dateOfBirth],
|
||||
membershipNumber = row[MemberTable.membershipNumber],
|
||||
membershipStartDate = row[MemberTable.membershipStartDate],
|
||||
membershipEndDate = row[MemberTable.membershipEndDate],
|
||||
isActive = row[MemberTable.isActive],
|
||||
address = row[MemberTable.address],
|
||||
emergencyContact = row[MemberTable.emergencyContact]
|
||||
)
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package at.mocode.members.infrastructure.persistence
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.date
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.CurrentTimestamp
|
||||
|
||||
/**
|
||||
* Database table definition for members in the member management context.
|
||||
*
|
||||
* This table stores member information including personal details,
|
||||
* membership information, and contact details.
|
||||
*/
|
||||
object MemberTable : Table("members") {
|
||||
val id = uuid("id").autoGenerate()
|
||||
val firstName = varchar("first_name", 100)
|
||||
val lastName = varchar("last_name", 100)
|
||||
val email = varchar("email", 255).uniqueIndex()
|
||||
val phone = varchar("phone", 50).nullable()
|
||||
val dateOfBirth = date("date_of_birth").nullable()
|
||||
val membershipNumber = varchar("membership_number", 50).uniqueIndex()
|
||||
val membershipStartDate = date("membership_start_date")
|
||||
val membershipEndDate = date("membership_end_date").nullable()
|
||||
val isActive = bool("is_active").default(true)
|
||||
val address = varchar("address", 500).nullable()
|
||||
val emergencyContact = varchar("emergency_contact", 255).nullable()
|
||||
val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp)
|
||||
val updatedAt = timestamp("updated_at").defaultExpression(CurrentTimestamp)
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.members.domain.model.Member
|
||||
import at.mocode.members.domain.repository.MemberRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* In-memory implementation of MemberRepository for development and testing purposes.
|
||||
*/
|
||||
@Repository
|
||||
class InMemoryMemberRepository : MemberRepository {
|
||||
|
||||
private val members = ConcurrentHashMap<Uuid, Member>()
|
||||
|
||||
override suspend fun findById(id: Uuid): Member? {
|
||||
return members[id]
|
||||
}
|
||||
|
||||
override suspend fun findByMembershipNumber(membershipNumber: String): Member? {
|
||||
return members.values.find { it.membershipNumber == membershipNumber }
|
||||
}
|
||||
|
||||
override suspend fun findByEmail(email: String): Member? {
|
||||
return members.values.find { it.email.equals(email, ignoreCase = true) }
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<Member> {
|
||||
return members.values
|
||||
.filter {
|
||||
it.firstName.contains(searchTerm, ignoreCase = true) ||
|
||||
it.lastName.contains(searchTerm, ignoreCase = true)
|
||||
}
|
||||
.take(limit)
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(limit: Int, offset: Int): List<Member> {
|
||||
return members.values
|
||||
.filter { it.isActive }
|
||||
.drop(offset)
|
||||
.take(limit)
|
||||
}
|
||||
|
||||
override suspend fun findAll(limit: Int, offset: Int): List<Member> {
|
||||
return members.values
|
||||
.drop(offset)
|
||||
.take(limit)
|
||||
}
|
||||
|
||||
override suspend fun findByMembershipStartDateRange(startDate: LocalDate, endDate: LocalDate): List<Member> {
|
||||
return members.values
|
||||
.filter { it.membershipStartDate >= startDate && it.membershipStartDate <= endDate }
|
||||
}
|
||||
|
||||
override suspend fun findByMembershipEndDateRange(startDate: LocalDate, endDate: LocalDate): List<Member> {
|
||||
return members.values
|
||||
.filter { member ->
|
||||
member.membershipEndDate?.let { memberEndDate ->
|
||||
memberEndDate >= startDate && memberEndDate <= endDate
|
||||
} ?: false
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findMembersWithExpiringMembership(daysAhead: Int): List<Member> {
|
||||
// Simplified implementation - returns members with end dates set
|
||||
return members.values
|
||||
.filter { it.membershipEndDate != null }
|
||||
}
|
||||
|
||||
override suspend fun save(member: Member): Member {
|
||||
members[member.memberId] = member
|
||||
return member
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Uuid): Boolean {
|
||||
return members.remove(id) != null
|
||||
}
|
||||
|
||||
override suspend fun countActive(): Long {
|
||||
return members.values.count { it.isActive }.toLong()
|
||||
}
|
||||
|
||||
override suspend fun countAll(): Long {
|
||||
return members.size.toLong()
|
||||
}
|
||||
|
||||
override suspend fun existsByMembershipNumber(membershipNumber: String, excludeMemberId: Uuid?): Boolean {
|
||||
return members.values.any {
|
||||
it.membershipNumber == membershipNumber && it.memberId != excludeMemberId
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun existsByEmail(email: String, excludeMemberId: Uuid?): Boolean {
|
||||
return members.values.any {
|
||||
it.email.equals(email, ignoreCase = true) && it.memberId != excludeMemberId
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user