Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.wafflestudio.spring2025.domain.registration.repository.RegistrationRe
import com.wafflestudio.spring2025.domain.registration.service.WaitlistReconciliationService
import com.wafflestudio.spring2025.domain.user.repository.UserRepository
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
Expand Down Expand Up @@ -96,10 +97,9 @@ class EventService(
val event = getEventByPublicId(publicId)
val eventId = requireNotNull(event.id) { "Event id is null: publicId=$publicId" }

val creatorUser =
userRepository.findById(event.createdBy).orElseThrow {
EventNotFoundException()
}
val userIdsToFetch = listOfNotNull(event.createdBy, requesterId).distinct()
val usersById = userRepository.findAllById(userIdsToFetch).associateBy { it.id!! }
val creatorUser = usersById[event.createdBy] ?: throw EventNotFoundException()

val myReg =
if (requesterId == null) {
Expand All @@ -121,17 +121,16 @@ class EventService(
.countByEventIdAndStatus(eventID = eventId, registrationStatus = RegistrationStatus.WAITLISTED)
.toInt()

val totalApplicants = confirmedCount + waitlistedCount

val waitlistPosition: Int? =
if (myReg?.status == RegistrationStatus.WAITLISTED) {
val waitlistedRegs =
registrationRepository.findByEventIdAndStatusOrderByCreatedAtAsc(
eventID = eventId,
registrationStatus = RegistrationStatus.WAITLISTED,
)
val idx = waitlistedRegs.indexOfFirst { it.id == myReg.id }
if (idx >= 0) idx + 1 else null
registrationRepository
.findWaitlistPositionsByRegistrationPublicIds(
eventId = eventId,
status = RegistrationStatus.WAITLISTED,
registrationPublicIds = listOf(myReg.registrationPublicId),
).firstOrNull()
?.waitlistNumber
?.toInt()
} else {
null
}
Expand All @@ -150,10 +149,7 @@ class EventService(
}
}

val viewerName: String? =
requesterId?.let {
userRepository.findById(it).orElse(null)?.name
}
val viewerName: String? = requesterId?.let { usersById[it]?.name }

val viewer =
if (viewerStatus == ViewerStatus.NONE) {
Expand Down Expand Up @@ -184,18 +180,17 @@ class EventService(
registrationEndsAt = event.registrationEndsAt,
)

val confirmedRegs =
val previewRegs =
registrationRepository.findByEventIdAndStatusOrderByCreatedAtAsc(
eventID = eventId,
registrationStatus = RegistrationStatus.CONFIRMED,
pageable = Pageable.ofSize(5),
)

val previewRegs = confirmedRegs.take(5)

val previewUserIds =
previewRegs.mapNotNull { it.userId }.distinct()

val usersById =
val previewUsersById =
userRepository.findAllById(previewUserIds).associateBy { it.id!! }

val guestsPreview =
Expand All @@ -208,7 +203,7 @@ class EventService(
profileImage = null,
)
} else {
usersById[uid]?.let {
previewUsersById[uid]?.let {
GuestPreview(
id = it.id!!,
name = it.name,
Expand Down Expand Up @@ -276,19 +271,31 @@ class EventService(
val sliced = fetched.take(pageSize)
val nextCursor = sliced.lastOrNull()?.createdAt

val eventIds = sliced.map { requireNotNull(it.id) }

val confirmedCounts =
if (eventIds.isEmpty()) {
emptyMap()
} else {
registrationRepository
.countByEventIdsAndStatuses(eventIds = eventIds, listOf(RegistrationStatus.CONFIRMED))
.associate { it.eventId to it.totalCount.toInt() }
}

val waitlistedCounts =
if (eventIds.isEmpty()) {
emptyMap()
} else {
registrationRepository
.countByEventIdsAndStatuses(eventIds = eventIds, listOf(RegistrationStatus.WAITLISTED))
.associate { it.eventId to it.totalCount.toInt() }
}

val responses =
sliced.map { event ->
val eventId = requireNotNull(event.id)

val confirmedCount =
registrationRepository
.countByEventIdAndStatus(eventID = eventId, registrationStatus = RegistrationStatus.CONFIRMED)
.toInt()

val waitlistedCount =
registrationRepository
.countByEventIdAndStatus(eventID = eventId, registrationStatus = RegistrationStatus.WAITLISTED)
.toInt()
val confirmedCount = confirmedCounts[eventId] ?: 0
val waitlistedCount = waitlistedCounts[eventId] ?: 0

MyEventResponse(
publicId = event.publicId,
Expand Down Expand Up @@ -378,9 +385,10 @@ class EventService(

// 알림 대상: CONFIRMED + WAITLISTED (BANNED 제외)
val registrationsToNotify =
registrationRepository
.findByEventId(eventId)
.filter { it.status == RegistrationStatus.CONFIRMED || it.status == RegistrationStatus.WAITLISTED }
registrationRepository.findByEventIdAndStatusIn(
eventID = eventId,
statuses = listOf(RegistrationStatus.CONFIRMED, RegistrationStatus.WAITLISTED),
)

// 이메일 데이터 구성 (삭제 전에 user 정보 조회)
val hostUser = userRepository.findById(event.createdBy).orElse(null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ interface RegistrationRepository :
eventId: Long,
): Registration?

fun findByEventIdAndStatusIn(
eventID: Long,
statuses: Collection<RegistrationStatus>,
): List<Registration>

fun countByEventId(eventID: Long): Long

fun countByEventIdAndStatus(
Expand All @@ -66,6 +71,12 @@ interface RegistrationRepository :
registrationStatus: RegistrationStatus,
): List<Registration>

fun findByEventIdAndStatusOrderByCreatedAtAsc(
eventID: Long,
registrationStatus: RegistrationStatus,
pageable: Pageable,
): List<Registration>

@Query(
"""
SELECT * FROM registrations
Expand Down Expand Up @@ -175,7 +186,7 @@ interface RegistrationRepository :
ranked.waitlist_number AS waitlist_number
FROM (
SELECT r.registration_public_id,
ROW_NUMBER() OVER (ORDER BY r.created_at ASC, r.registration_public_id ASC) AS waitlist_number
ROW_NUMBER() OVER (ORDER BY r.created_at ASC, r.id ASC) AS waitlist_number
FROM registrations r
WHERE r.event_id = :eventId AND r.status = :status
) ranked
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.wafflestudio.spring2025.domain.user.model.User
import com.wafflestudio.spring2025.domain.user.repository.UserRepository
import org.springframework.dao.DuplicateKeyException
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.transaction.support.TransactionSynchronization
Expand Down Expand Up @@ -96,6 +97,11 @@ RegistrationService(
registrationRepository.findByUserIdAndEventId(userId, eventPk)
}

val userIdsToFetch = listOfNotNull(lockedEvent.createdBy, userId).distinct()
val fetchedUsersById = userRepository.findAllById(userIdsToFetch).associateBy { it.id!! }
val hostEmail = fetchedUsersById[lockedEvent.createdBy]?.email
val user = userId?.let { fetchedUsersById[it] }

val saved =
try {
if (existingRegistration != null) {
Expand All @@ -112,8 +118,7 @@ RegistrationService(
-> throw RegistrationConflictException(RegistrationErrorCode.REGISTRATION_ALREADY_EXISTS)
}
} else {
val hostEmail = userRepository.findById(lockedEvent.createdBy).orElse(null)?.email
val registrationEmail = userId?.let { userRepository.findById(it).orElse(null)?.email } ?: guestEmail
val registrationEmail = user?.email ?: guestEmail

if (hostEmail != null && hostEmail == registrationEmail) {
throw RegistrationValidationException(RegistrationErrorCode.REGISTRATION_BLOCKED_HOST)
Expand Down Expand Up @@ -143,19 +148,11 @@ RegistrationService(
throw RegistrationConflictException(RegistrationErrorCode.REGISTRATION_ALREADY_EXISTS)
}

val waitlistedNumber: Int? =
if (saved.status == RegistrationStatus.WAITLISTED) {
registrationRepository
.countByEventIdAndStatus(eventPk, RegistrationStatus.WAITLISTED)
.toInt()
} else {
null
}

val user =
userId?.let { id ->
userRepository.findById(id).orElse(null)
}
val waitlistedCount =
registrationRepository
.countByEventIdAndStatus(eventPk, RegistrationStatus.WAITLISTED)
.toInt()
val waitlistedNumber: Int? = if (saved.status == RegistrationStatus.WAITLISTED) waitlistedCount else null

val recipientEmail = user?.email ?: guestEmail

Expand All @@ -164,10 +161,6 @@ RegistrationService(
registrationRepository
.countByEventIdAndStatus(eventPk, RegistrationStatus.CONFIRMED)
.toInt()
val waitlistedCount =
registrationRepository
.countByEventIdAndStatus(eventPk, RegistrationStatus.WAITLISTED)
.toInt()
val totalCount = confirmedCount + waitlistedCount

val emailData =
Expand Down Expand Up @@ -260,8 +253,6 @@ RegistrationService(
}
}

eventLockRepository.lockById(registration.eventId)

if (!isRegistrationEnabled(event)) {
throw RegistrationValidationException(RegistrationErrorCode.NOT_WITHIN_REGISTRATION_WINDOW)
}
Expand Down Expand Up @@ -585,13 +576,14 @@ RegistrationService(
val status = registration.status
val waitlistPosition =
if (status == RegistrationStatus.WAITLISTED) {
val waitlistedRegs =
registrationRepository.findByEventIdAndStatusOrderByCreatedAtAsc(
registration.eventId,
RegistrationStatus.WAITLISTED,
)
val idx = waitlistedRegs.indexOfFirst { it.id == registration.id }
if (idx >= 0) idx + 1 else 0
registrationRepository
.findWaitlistPositionsByRegistrationPublicIds(
eventId = registration.eventId,
status = RegistrationStatus.WAITLISTED,
registrationPublicIds = listOf(registration.registrationPublicId),
).firstOrNull()
?.waitlistNumber
?.toInt() ?: 0
} else {
0
}
Expand Down Expand Up @@ -755,31 +747,34 @@ RegistrationService(
val available = capacity - confirmed
if (available <= 0) return

val waitlistedRegs =
val totalWaitlisted =
registrationRepository.countByEventIdAndStatus(eventId, RegistrationStatus.WAITLISTED).toInt()

val promoted =
registrationRepository.findByEventIdAndStatusOrderByCreatedAtAsc(
eventId,
RegistrationStatus.WAITLISTED,
Pageable.ofSize(available),
)

val waitlistNumbers =
waitlistedRegs.withIndex().associate { indexed ->
indexed.value.registrationPublicId to (indexed.index + 1)
promoted.withIndex().associate { (index, reg) ->
reg.registrationPublicId to (index + 1)
}

val promoted = waitlistedRegs.take(available)
promoted.forEach { it.status = RegistrationStatus.CONFIRMED }
registrationRepository.saveAll(promoted)

val confirmedAfter = confirmed + promoted.size
val remainingWaitlisted = waitlistedRegs.size - promoted.size
val remainingWaitlisted = totalWaitlisted - promoted.size
val totalCount = confirmedAfter + remainingWaitlisted

val promotedUserIds = promoted.mapNotNull { it.userId }.distinct()
val promotedUsersById = userRepository.findAllById(promotedUserIds).associateBy { it.id!! }

val emailDataList =
promoted.mapNotNull { registration ->
val user: User? =
registration.userId?.let { uid ->
userRepository.findById(uid).orElse(null)
}
val user: User? = registration.userId?.let { promotedUsersById[it] }

val recipientEmail = user?.email ?: registration.guestEmail
val recipientName = user?.name ?: registration.guestName ?: "참여자"
Expand Down
Loading