feat: 결제 & 예약 확정 과정에서 필요한 검증 로직 추가

This commit is contained in:
이상진 2025-10-07 22:28:02 +09:00
parent 985efbe0a3
commit 6be9ae7efe

View File

@ -0,0 +1,76 @@
package com.sangdol.roomescape.order.business
import com.sangdol.common.utils.KoreaDateTime
import com.sangdol.roomescape.order.exception.OrderErrorCode
import com.sangdol.roomescape.order.exception.OrderException
import com.sangdol.roomescape.order.infrastructure.persistence.PaymentAttemptRepository
import com.sangdol.roomescape.reservation.dto.ReservationStateResponse
import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus
import com.sangdol.roomescape.schedule.dto.ScheduleStateResponse
import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus
import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.stereotype.Component
import java.time.Instant
import java.time.LocalDateTime
private val log: KLogger = KotlinLogging.logger {}
@Component
class OrderValidator(
private val paymentAttemptRepository: PaymentAttemptRepository
) {
fun validateCanConfirm(
reservation: ReservationStateResponse,
schedule: ScheduleStateResponse
) {
if (paymentAttemptRepository.isSuccessAttemptExists(reservation.id)) {
log.info { "[validateCanConfirm] 이미 결제 완료된 예약: id=${reservation.id}" }
throw OrderException(OrderErrorCode.BOOKING_ALREADY_COMPLETED)
}
validateReservationStatus(reservation)
validateScheduleStatus(schedule)
}
private fun validateReservationStatus(reservation: ReservationStateResponse) {
when (reservation.status) {
ReservationStatus.CONFIRMED -> {
log.info { "[validateCanConfirm] 이미 확정된 예약: id=${reservation.id}" }
throw OrderException(OrderErrorCode.BOOKING_ALREADY_COMPLETED)
}
ReservationStatus.EXPIRED -> {
log.info { "[validateCanConfirm] 만료된 예약 예약: id=${reservation.id}" }
throw OrderException(OrderErrorCode.EXPIRED_RESERVATION)
}
ReservationStatus.CANCELED -> {
log.info { "[validateCanConfirm] 취소된 예약 예약: id=${reservation.id}" }
throw OrderException(OrderErrorCode.CANCELED_RESERVATION)
}
ReservationStatus.PENDING -> {
val pendingExpiredAt = reservation.createdAt.plusSeconds(5 * 60)
val now = Instant.now()
if (now.isAfter(pendingExpiredAt)) {
log.info { "[validateCanConfirm] Pending 예약 시간 내 미결제로 인한 실패: id=${reservation.id}, expiredAt=${pendingExpiredAt}, now=${now}" }
throw OrderException(OrderErrorCode.BOOKING_PAYMENT_TIMEOUT)
}
}
else -> {}
}
}
private fun validateScheduleStatus(schedule: ScheduleStateResponse) {
if (schedule.status != ScheduleStatus.HOLD) {
log.info { "[validateScheduleStatus] 일정 상태 오류: status=${schedule.status}" }
throw OrderException(OrderErrorCode.EXPIRED_RESERVATION)
}
val scheduleDateTime = LocalDateTime.of(schedule.date, schedule.startFrom)
val nowDateTime = KoreaDateTime.now()
if (scheduleDateTime.isBefore(nowDateTime)) {
log.info { "[validateScheduleStatus] 과거 시간인 일정으로 인한 실패: scheduleDateTime=${scheduleDateTime}(KST), now=${nowDateTime}(KST)" }
throw OrderException(OrderErrorCode.PAST_SCHEDULE)
}
}
}