generated from pricelees/issue-pr-template
[#66] 결제 & 예약 확정 로직 수정 #67
@ -0,0 +1,34 @@
|
|||||||
|
package com.sangdol.roomescape.reservation.business.event
|
||||||
|
|
||||||
|
import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationRepository
|
||||||
|
import io.github.oshai.kotlinlogging.KLogger
|
||||||
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
import org.springframework.context.event.EventListener
|
||||||
|
import org.springframework.scheduling.annotation.Async
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
private val log: KLogger = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class ReservationEventListener(
|
||||||
|
private val reservationRepository: ReservationRepository
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Async
|
||||||
|
@EventListener
|
||||||
|
@Transactional
|
||||||
|
fun handleReservationConfirmEvent(event: ReservationConfirmEvent) {
|
||||||
|
val reservationId = event.reservationId
|
||||||
|
|
||||||
|
log.info { "[handleReservationConfirmEvent] 예약 확정 이벤트 수신: reservationId=${reservationId}" }
|
||||||
|
val modifiedRows = reservationRepository.confirmReservation(Instant.now(), reservationId)
|
||||||
|
|
||||||
|
if (modifiedRows == 0) {
|
||||||
|
log.warn { "[handleReservationConfirmEvent] 예상치 못한 예약 확정 실패 - 변경된 row 없음: reservationId=${reservationId}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info { "[handleReservationConfirmEvent] 예약 확정 이벤트 처리 완료" }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -48,4 +48,23 @@ interface ReservationRepository : JpaRepository<ReservationEntity, Long> {
|
|||||||
""", nativeQuery = true
|
""", nativeQuery = true
|
||||||
)
|
)
|
||||||
fun expirePendingReservations(@Param("now") now: Instant, @Param("reservationIds") reservationIds: List<Long>): Int
|
fun expirePendingReservations(@Param("now") now: Instant, @Param("reservationIds") reservationIds: List<Long>): Int
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
UPDATE
|
||||||
|
reservation r
|
||||||
|
JOIN
|
||||||
|
schedule s ON r.schedule_id = s.id AND s.status = 'HOLD'
|
||||||
|
SET
|
||||||
|
r.status = 'CONFIRMED',
|
||||||
|
r.updated_at = :now,
|
||||||
|
s.status = 'RESERVED',
|
||||||
|
s.hold_expired_at = NULL
|
||||||
|
WHERE
|
||||||
|
r.id = :id
|
||||||
|
AND r.status = 'PAYMENT_IN_PROGRESS'
|
||||||
|
""", nativeQuery = true
|
||||||
|
)
|
||||||
|
fun confirmReservation(@Param("now") now: Instant, @Param("id") id: Long): Int
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,45 @@
|
|||||||
|
package com.sangdol.roomescape.reservation.business.event
|
||||||
|
|
||||||
|
import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationRepository
|
||||||
|
import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||||
|
import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository
|
||||||
|
import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus
|
||||||
|
import com.sangdol.roomescape.supports.FunSpecSpringbootTest
|
||||||
|
import io.kotest.assertions.assertSoftly
|
||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.matchers.nulls.shouldNotBeNull
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
|
||||||
|
class ReservationEventListenerTest(
|
||||||
|
private val reservationEventListener: ReservationEventListener,
|
||||||
|
private val reservationRepository: ReservationRepository,
|
||||||
|
private val scheduleRepository: ScheduleRepository
|
||||||
|
) : FunSpecSpringbootTest() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
test("예약 확정 이벤트를 처리한다.") {
|
||||||
|
val pendingReservation = dummyInitializer.createPendingReservation(testAuthUtil.defaultUser()).also {
|
||||||
|
it.status = ReservationStatus.PAYMENT_IN_PROGRESS
|
||||||
|
reservationRepository.saveAndFlush(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
val reservationConfirmEvent = ReservationConfirmEvent(pendingReservation.id)
|
||||||
|
|
||||||
|
reservationEventListener.handleReservationConfirmEvent(reservationConfirmEvent).also {
|
||||||
|
Thread.sleep(100)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertSoftly(reservationRepository.findByIdOrNull(pendingReservation.id)) {
|
||||||
|
this.shouldNotBeNull()
|
||||||
|
this.status shouldBe ReservationStatus.CONFIRMED
|
||||||
|
}
|
||||||
|
|
||||||
|
assertSoftly(scheduleRepository.findByIdOrNull(pendingReservation.scheduleId)) {
|
||||||
|
this.shouldNotBeNull()
|
||||||
|
this.status shouldBe ScheduleStatus.RESERVED
|
||||||
|
this.holdExpiredAt shouldBe null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user