diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt index f26baa16..498b6ced 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt @@ -11,7 +11,6 @@ import com.sangdol.roomescape.reservation.web.* import com.sangdol.roomescape.schedule.business.ScheduleService import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus import com.sangdol.roomescape.schedule.web.ScheduleOverviewResponse -import com.sangdol.roomescape.schedule.web.ScheduleUpdateRequest import com.sangdol.roomescape.theme.business.ThemeService import com.sangdol.roomescape.user.business.UserService import com.sangdol.roomescape.user.web.UserContactResponse @@ -58,9 +57,10 @@ class ReservationService( run { reservation.confirm() - scheduleService.updateSchedule( - reservation.scheduleId, - ScheduleUpdateRequest(status = ScheduleStatus.RESERVED) + scheduleService.changeStatus( + scheduleId = reservation.scheduleId, + currentStatus = ScheduleStatus.HOLD, + changeStatus = ScheduleStatus.RESERVED ) }.also { log.info { "[ReservationService.confirmReservation] Pending 예약 확정 완료: reservationId=${id}" } @@ -74,9 +74,10 @@ class ReservationService( val reservation: ReservationEntity = findOrThrow(reservationId) run { - scheduleService.updateSchedule( - reservation.scheduleId, - ScheduleUpdateRequest(status = ScheduleStatus.AVAILABLE) + scheduleService.changeStatus( + scheduleId = reservation.scheduleId, + currentStatus = ScheduleStatus.RESERVED, + changeStatus = ScheduleStatus.AVAILABLE ) saveCanceledReservation(user, reservation, request.cancelReason) reservation.cancel() @@ -148,6 +149,7 @@ class ReservationService( status = CanceledReservationStatus.COMPLETED ).also { canceledReservationRepository.save(it) + } } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt index 0bb2d24a..02325f72 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt @@ -10,6 +10,7 @@ import com.sangdol.roomescape.schedule.exception.ScheduleErrorCode import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntityFactory import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus import com.sangdol.roomescape.schedule.web.* import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging @@ -66,7 +67,11 @@ class ScheduleService( @Transactional fun holdSchedule(id: Long) { log.info { "[ScheduleService.holdSchedule] 일정 Holding 시작: id=$id" } - val result: Int = scheduleRepository.holdAvailableScheduleById(id) + val result: Int = scheduleRepository.changeStatus( + id = id, + currentStatus = ScheduleStatus.AVAILABLE, + changeStatus = ScheduleStatus.HOLD + ) if (result == 0) { log.info { "[ScheduleService.holdSchedule] Holding된 일정 없음: id=${id}, count = " } @@ -188,6 +193,15 @@ class ScheduleService( return overview.toOverviewResponse() } + @Transactional + fun changeStatus(scheduleId: Long, currentStatus: ScheduleStatus, changeStatus: ScheduleStatus) { + log.info { "[ScheduleService.reserveSchedule] 일정 상태 변경 시작: id=${scheduleId}, currentStatus=${currentStatus}, changeStatus=${changeStatus}" } + + scheduleRepository.changeStatus(scheduleId, currentStatus, changeStatus).also { + log.info { "[ScheduleService.reserveSchedule] 일정 상태 변경 완료: id=${scheduleId}, currentStatus=${currentStatus}, changeStatus=${changeStatus}" } + } + } + // ======================================== // Common (공통 메서드) // ======================================== diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleRepository.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleRepository.kt index c0d92c1a..ceca6f16 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleRepository.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleRepository.kt @@ -1,9 +1,9 @@ package com.sangdol.roomescape.schedule.infrastructure.persistence -import org.springframework.data.jpa.repository.JpaRepository -import org.springframework.data.jpa.repository.Query import com.sangdol.roomescape.schedule.business.domain.ScheduleOverview +import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Modifying +import org.springframework.data.jpa.repository.Query import java.time.LocalDate import java.time.LocalTime @@ -85,15 +85,22 @@ interface ScheduleRepository : JpaRepository { fun findOverviewByIdOrNull(id: Long): ScheduleOverview? @Modifying - @Query(""" + @Query( + """ UPDATE ScheduleEntity s SET - s.status = 'HOLD' + s.status = :changeStatus, + s.holdExpiredAt = CASE + WHEN :changeStatus = com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus.HOLD + THEN CURRENT_TIMESTAMP + 5 MINUTE + ELSE NULL + END WHERE s._id = :id AND - s.status = 'AVAILABLE' - """) - fun holdAvailableScheduleById(id: Long): Int + s.status = :currentStatus + """ + ) + fun changeStatus(id: Long, currentStatus: ScheduleStatus, changeStatus: ScheduleStatus): Int } diff --git a/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleServiceTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleServiceTest.kt index 51f47d75..1440e7fb 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleServiceTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleServiceTest.kt @@ -10,6 +10,7 @@ import com.sangdol.roomescape.supports.FunSpecSpringbootTest import com.sangdol.roomescape.supports.IDGenerator import com.sangdol.roomescape.supports.initialize import io.kotest.assertions.assertSoftly +import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe @@ -48,7 +49,7 @@ class ScheduleServiceTest( } } - test("유저가 일정을 Holding 하는 경우에는 updatedAt, updatedBy 컬럼이 변경되지 않는다.") { + test("유저가 일정을 Holding 상태로 변경하는 경우에는 holdExpiredAt 컬럼이 5분 뒤로 지정되며, updatedAt, updatedBy 컬럼이 변경되지 않는다.") { val createdScheduleId: Long = createSchedule() initialize("updatedBy 변경 확인을 위한 MDC 재설정") { @@ -64,6 +65,30 @@ class ScheduleServiceTest( this.shouldNotBeNull() this.updatedAt shouldBe this.createdAt this.updatedBy shouldBe this.createdBy + this.holdExpiredAt.shouldNotBeNull() + } + } + + test("유저가 일정을 Holding이 아닌 다른 상태로 변경 경우에는 holdExpiredAt 컬럼이 null로 지정되며, updatedAt, updatedBy 컬럼이 변경되지 않는다.") { + val createdScheduleId: Long = createSchedule() + + initialize("updatedBy 변경 확인을 위한 MDC 재설정") { + MdcPrincipalIdUtil.clear() + IDGenerator.create().also { + MdcPrincipalIdUtil.set(it.toString()) + } + } + + scheduleService.holdSchedule(createdScheduleId).also { + scheduleRepository.findByIdOrNull(createdScheduleId)!!.holdExpiredAt.shouldNotBeNull() + } + + scheduleService.changeStatus(createdScheduleId, ScheduleStatus.HOLD, ScheduleStatus.RESERVED).also { + assertSoftly(scheduleRepository.findByIdOrNull(createdScheduleId)!!) { + this.updatedAt shouldBe this.createdAt + this.updatedBy shouldBe this.createdBy + this.holdExpiredAt.shouldBeNull() + } } } }