generated from pricelees/issue-pr-template
[#35] 결제 스키마 재정의 & 예약 조회 페이지 개선 #36
119
src/main/kotlin/roomescape/payment/implement/PaymentWriterV2.kt
Normal file
119
src/main/kotlin/roomescape/payment/implement/PaymentWriterV2.kt
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package roomescape.payment.implement
|
||||||
|
|
||||||
|
import com.github.f4b6a3.tsid.TsidFactory
|
||||||
|
import io.github.oshai.kotlinlogging.KLogger
|
||||||
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import roomescape.common.config.next
|
||||||
|
import roomescape.payment.exception.PaymentErrorCode
|
||||||
|
import roomescape.payment.exception.PaymentException
|
||||||
|
import roomescape.payment.infrastructure.client.v2.*
|
||||||
|
import roomescape.payment.infrastructure.common.PaymentMethod
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.CanceledPaymentRepositoryV2
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.CanceledPaymentEntityV2
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentDetailEntity
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentDetailRepository
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentEntityV2
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentRepositoryV2
|
||||||
|
import roomescape.reservation.web.ReservationCancelRequest
|
||||||
|
import roomescape.reservation.web.ReservationPaymentRequest
|
||||||
|
import roomescape.reservation.web.toPaymentConfirmRequest
|
||||||
|
|
||||||
|
private val log: KLogger = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class PaymentWriterV2(
|
||||||
|
private val paymentClient: TosspaymentClientV2,
|
||||||
|
private val paymentRepository: PaymentRepositoryV2,
|
||||||
|
private val paymentDetailRepository: PaymentDetailRepository,
|
||||||
|
private val canceledPaymentRepository: CanceledPaymentRepositoryV2,
|
||||||
|
private val tsidFactory: TsidFactory,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun requestConfirmPayment(
|
||||||
|
reservationId: Long,
|
||||||
|
request: ReservationPaymentRequest
|
||||||
|
): PaymentConfirmResponse {
|
||||||
|
log.debug { "[PaymentWriterV2.requestConfirmPayment] 결제 승인 요청 시작: reservationId=${reservationId}, paymentKey=${request.paymentKey}" }
|
||||||
|
|
||||||
|
return paymentClient.confirm(request.toPaymentConfirmRequest()).also {
|
||||||
|
log.debug { "[PaymentWriterV2.requestConfirmPayment] 결제 승인 요청 완료: response=$it" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPayment(
|
||||||
|
reservationId: Long,
|
||||||
|
request: ReservationPaymentRequest,
|
||||||
|
paymentConfirmResponse: PaymentConfirmResponse
|
||||||
|
): PaymentEntityV2 {
|
||||||
|
log.debug { "[PaymentWriterV2.createPayment] 결제 승인 및 결제 정보 저장 시작: reservationId=${reservationId}, paymentKey=${request.paymentKey}" }
|
||||||
|
|
||||||
|
return paymentConfirmResponse.toEntity(
|
||||||
|
id = tsidFactory.next(), reservationId, request.orderId, request.paymentType
|
||||||
|
).also {
|
||||||
|
paymentRepository.save(it)
|
||||||
|
saveDetail(paymentConfirmResponse, it.id)
|
||||||
|
log.debug { "[PaymentWriterV2.createPayment] 결제 승인 및 결제 정보 저장 완료: reservationId=${reservationId}, paymentId=${it.id}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveDetail(
|
||||||
|
paymentResponse: PaymentConfirmResponse,
|
||||||
|
paymentId: Long,
|
||||||
|
): PaymentDetailEntity {
|
||||||
|
val method: PaymentMethod = paymentResponse.method
|
||||||
|
val id = tsidFactory.next()
|
||||||
|
|
||||||
|
if (method == PaymentMethod.TRANSFER) {
|
||||||
|
return paymentDetailRepository.save(paymentResponse.toTransferDetailEntity(id, paymentId))
|
||||||
|
}
|
||||||
|
if (method == PaymentMethod.EASY_PAY && paymentResponse.card == null) {
|
||||||
|
return paymentDetailRepository.save(paymentResponse.toEasypayPrepaidDetailEntity(id, paymentId))
|
||||||
|
}
|
||||||
|
if (paymentResponse.card != null) {
|
||||||
|
return paymentDetailRepository.save(paymentResponse.toCardDetailEntity(id, paymentId))
|
||||||
|
}
|
||||||
|
throw PaymentException(PaymentErrorCode.NOT_SUPPORTED_PAYMENT_TYPE)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestCancelPayment(
|
||||||
|
reservationId: Long,
|
||||||
|
request: ReservationCancelRequest,
|
||||||
|
): PaymentCancelResponseV2 {
|
||||||
|
log.debug { "[PaymentWriterV2.requestConfirmPayment] 결제 취소 요청 시작: reservationId=$reservationId, request=${request}" }
|
||||||
|
|
||||||
|
val payment: PaymentEntityV2 = paymentRepository.findByReservationId(reservationId)
|
||||||
|
?: throw PaymentException(PaymentErrorCode.PAYMENT_NOT_FOUND)
|
||||||
|
|
||||||
|
val paymentCancelRequest = PaymentCancelRequestV2(
|
||||||
|
paymentKey = payment.paymentKey,
|
||||||
|
cancelReason = request.cancelReason,
|
||||||
|
amount = payment.totalAmount
|
||||||
|
)
|
||||||
|
|
||||||
|
return paymentClient.cancel(paymentCancelRequest).also {
|
||||||
|
log.debug { "[PaymentWriterV2.requestCancelPayment] 결제 취소 요청 완료: reservationId=${reservationId}, paymentKey=${payment.paymentKey}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createCanceledPayment(
|
||||||
|
memberId: Long,
|
||||||
|
reservationId: Long,
|
||||||
|
request: ReservationCancelRequest,
|
||||||
|
paymentCancelResponse: PaymentCancelResponseV2
|
||||||
|
) {
|
||||||
|
val payment: PaymentEntityV2= paymentRepository.findByReservationId(reservationId)
|
||||||
|
?.also { it.cancel() }
|
||||||
|
?: throw PaymentException(PaymentErrorCode.PAYMENT_NOT_FOUND)
|
||||||
|
|
||||||
|
val cancelDetail: CancelDetail = paymentCancelResponse.cancels
|
||||||
|
|
||||||
|
CanceledPaymentEntityV2(
|
||||||
|
id = tsidFactory.next(),
|
||||||
|
canceledAt = cancelDetail.canceledAt,
|
||||||
|
paymentId = payment.id,
|
||||||
|
canceledBy = memberId,
|
||||||
|
cancelReason = request.cancelReason
|
||||||
|
).also { canceledPaymentRepository.save(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,7 +10,7 @@ import java.time.OffsetDateTime
|
|||||||
|
|
||||||
data class PaymentCancelRequestV2(
|
data class PaymentCancelRequestV2(
|
||||||
val paymentKey: String,
|
val paymentKey: String,
|
||||||
val amount: Long,
|
val amount: Int,
|
||||||
val cancelReason: String
|
val cancelReason: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,19 +1,26 @@
|
|||||||
package roomescape.payment.infrastructure.client.v2
|
package roomescape.payment.infrastructure.client.v2
|
||||||
|
|
||||||
|
import roomescape.payment.exception.PaymentErrorCode
|
||||||
|
import roomescape.payment.exception.PaymentException
|
||||||
import roomescape.payment.infrastructure.common.*
|
import roomescape.payment.infrastructure.common.*
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentBankTransferDetailEntity
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentCardDetailEntity
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentEasypayPrepaidDetailEntity
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentEntityV2
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
|
|
||||||
data class PaymentConfirmRequest(
|
data class PaymentConfirmRequest(
|
||||||
val paymentKey: String,
|
val paymentKey: String,
|
||||||
val orderId: String,
|
val orderId: String,
|
||||||
val amount: Long,
|
val amount: Long,
|
||||||
val paymentType: PaymentType,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class PaymentConfirmResponse(
|
data class PaymentConfirmResponse(
|
||||||
val paymentKey: String,
|
val paymentKey: String,
|
||||||
val orderId: String,
|
val status: PaymentStatus,
|
||||||
val totalAmount: Int,
|
val totalAmount: Int,
|
||||||
|
val vat: Int,
|
||||||
|
val suppliedAmount: Int,
|
||||||
val method: PaymentMethod,
|
val method: PaymentMethod,
|
||||||
val card: CardDetail?,
|
val card: CardDetail?,
|
||||||
val easyPay: EasyPayDetail?,
|
val easyPay: EasyPayDetail?,
|
||||||
@ -22,6 +29,24 @@ data class PaymentConfirmResponse(
|
|||||||
val approvedAt: OffsetDateTime,
|
val approvedAt: OffsetDateTime,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun PaymentConfirmResponse.toEntity(
|
||||||
|
id: Long,
|
||||||
|
reservationId: Long,
|
||||||
|
orderId: String,
|
||||||
|
paymentType: PaymentType
|
||||||
|
) = PaymentEntityV2(
|
||||||
|
id = id,
|
||||||
|
reservationId = reservationId,
|
||||||
|
paymentKey = this.paymentKey,
|
||||||
|
orderId = orderId,
|
||||||
|
totalAmount = this.totalAmount,
|
||||||
|
requestedAt = this.requestedAt,
|
||||||
|
approvedAt = this.approvedAt,
|
||||||
|
type = paymentType,
|
||||||
|
method = this.method,
|
||||||
|
status = this.status,
|
||||||
|
)
|
||||||
|
|
||||||
data class CardDetail(
|
data class CardDetail(
|
||||||
val issuerCode: CardIssuerCode,
|
val issuerCode: CardIssuerCode,
|
||||||
val number: String,
|
val number: String,
|
||||||
@ -33,13 +58,67 @@ data class CardDetail(
|
|||||||
val installmentPlanMonths: Int
|
val installmentPlanMonths: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun PaymentConfirmResponse.toCardDetailEntity(id: Long, paymentId: Long): PaymentCardDetailEntity {
|
||||||
|
val cardDetail = this.card ?: throw PaymentException(PaymentErrorCode.PAYMENT_UNEXPECTED_ERROR)
|
||||||
|
|
||||||
|
return PaymentCardDetailEntity(
|
||||||
|
id = id,
|
||||||
|
paymentId = paymentId,
|
||||||
|
suppliedAmount = this.suppliedAmount,
|
||||||
|
vat = this.vat,
|
||||||
|
issuerCode = cardDetail.issuerCode,
|
||||||
|
cardType = cardDetail.cardType,
|
||||||
|
ownerType = cardDetail.ownerType,
|
||||||
|
amount = cardDetail.amount,
|
||||||
|
cardNumber = cardDetail.number,
|
||||||
|
approvalNumber = cardDetail.approveNo,
|
||||||
|
installmentPlanMonths = cardDetail.installmentPlanMonths,
|
||||||
|
isInterestFree = cardDetail.isInterestFree,
|
||||||
|
easypayProviderCode = this.easyPay?.provider,
|
||||||
|
easypayDiscountAmount = this.easyPay?.discountAmount,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
data class EasyPayDetail(
|
data class EasyPayDetail(
|
||||||
val provider: EasyPayCompanyCode,
|
val provider: EasyPayCompanyCode,
|
||||||
val amount: Int,
|
val amount: Int,
|
||||||
val discountAmount: Int,
|
val discountAmount: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun PaymentConfirmResponse.toEasypayPrepaidDetailEntity(
|
||||||
|
id: Long,
|
||||||
|
paymentId: Long
|
||||||
|
): PaymentEasypayPrepaidDetailEntity {
|
||||||
|
val easyPayDetail = this.easyPay ?: throw PaymentException(PaymentErrorCode.PAYMENT_UNEXPECTED_ERROR)
|
||||||
|
|
||||||
|
return PaymentEasypayPrepaidDetailEntity(
|
||||||
|
id = id,
|
||||||
|
paymentId = paymentId,
|
||||||
|
suppliedAmount = this.suppliedAmount,
|
||||||
|
vat = this.vat,
|
||||||
|
easypayProviderCode = easyPayDetail.provider,
|
||||||
|
amount = easyPayDetail.amount,
|
||||||
|
discountAmount = easyPayDetail.discountAmount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
data class TransferDetail(
|
data class TransferDetail(
|
||||||
val bankCode: BankCode,
|
val bankCode: BankCode,
|
||||||
val settlementStatus: String,
|
val settlementStatus: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun PaymentConfirmResponse.toTransferDetailEntity(
|
||||||
|
id: Long,
|
||||||
|
paymentId: Long
|
||||||
|
): PaymentBankTransferDetailEntity {
|
||||||
|
val transferDetail = this.transfer ?: throw PaymentException(PaymentErrorCode.PAYMENT_UNEXPECTED_ERROR)
|
||||||
|
|
||||||
|
return PaymentBankTransferDetailEntity(
|
||||||
|
id = id,
|
||||||
|
paymentId = paymentId,
|
||||||
|
suppliedAmount = this.suppliedAmount,
|
||||||
|
vat = this.vat,
|
||||||
|
bankCode = transferDetail.bankCode,
|
||||||
|
settlementStatus = transferDetail.settlementStatus
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -2,4 +2,7 @@ package roomescape.payment.infrastructure.persistence.v2
|
|||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
|
||||||
interface PaymentRepositoryV2: JpaRepository<PaymentEntityV2, Long>
|
interface PaymentRepositoryV2: JpaRepository<PaymentEntityV2, Long> {
|
||||||
|
|
||||||
|
fun findByReservationId(reservationId: Long): PaymentEntityV2?
|
||||||
|
}
|
||||||
|
|||||||
@ -0,0 +1,103 @@
|
|||||||
|
package roomescape.reservation.business
|
||||||
|
|
||||||
|
import io.github.oshai.kotlinlogging.KLogger
|
||||||
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
import jakarta.transaction.Transactional
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import roomescape.common.util.TransactionExecutionUtil
|
||||||
|
import roomescape.payment.implement.PaymentWriterV2
|
||||||
|
import roomescape.payment.infrastructure.client.v2.PaymentConfirmResponse
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentEntityV2
|
||||||
|
import roomescape.reservation.implement.ReservationFinder
|
||||||
|
import roomescape.reservation.implement.ReservationWriter
|
||||||
|
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
||||||
|
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||||
|
import roomescape.reservation.web.*
|
||||||
|
|
||||||
|
private val log: KLogger = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class ReservationWithPaymentServiceV2(
|
||||||
|
private val reservationWriter: ReservationWriter,
|
||||||
|
private val reservationFinder: ReservationFinder,
|
||||||
|
private val paymentWriter: PaymentWriterV2,
|
||||||
|
private val transactionExecutionUtil: TransactionExecutionUtil
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun createPendingReservation(memberId: Long, request: ReservationCreateRequest): ReservationCreateResponseV2 {
|
||||||
|
log.info {
|
||||||
|
"[ReservationWithPaymentServiceV2.createPendingReservation] " +
|
||||||
|
"PENDING 예약 저장 시작: memberId=$memberId, request=$request"
|
||||||
|
}
|
||||||
|
|
||||||
|
val reservation: ReservationEntity = reservationWriter.create(
|
||||||
|
date = request.date,
|
||||||
|
timeId = request.timeId,
|
||||||
|
themeId = request.themeId,
|
||||||
|
status = ReservationStatus.PENDING,
|
||||||
|
memberId = memberId,
|
||||||
|
requesterId = memberId
|
||||||
|
)
|
||||||
|
|
||||||
|
return reservation.toCreateResponseV2().also {
|
||||||
|
log.info {
|
||||||
|
"[ReservationWithPaymentServiceV2.createPendingReservation] " +
|
||||||
|
"PENDING 예약 저장 완료: reservationId=${reservation.id}, response=$it"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun payReservation(
|
||||||
|
memberId: Long,
|
||||||
|
reservationId: Long,
|
||||||
|
request: ReservationPaymentRequest
|
||||||
|
): ReservationPaymentResponse {
|
||||||
|
log.info {
|
||||||
|
"[ReservationWithPaymentServiceV2.payReservation] " +
|
||||||
|
"예약 결제 시작: memberId=$memberId, reservationId=$reservationId, request=$request"
|
||||||
|
}
|
||||||
|
|
||||||
|
val paymentConfirmResponse = paymentWriter.requestConfirmPayment(reservationId, request)
|
||||||
|
|
||||||
|
return transactionExecutionUtil.withNewTransaction(isReadOnly = false) {
|
||||||
|
savePayment(memberId, reservationId, request, paymentConfirmResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun savePayment(
|
||||||
|
memberId: Long,
|
||||||
|
reservationId: Long,
|
||||||
|
request: ReservationPaymentRequest,
|
||||||
|
paymentConfirmResponse: PaymentConfirmResponse
|
||||||
|
): ReservationPaymentResponse {
|
||||||
|
val reservation =
|
||||||
|
reservationFinder.findPendingReservation(reservationId, memberId).also { it.confirm() }
|
||||||
|
val payment: PaymentEntityV2 = paymentWriter.createPayment(
|
||||||
|
reservationId = reservationId,
|
||||||
|
request = request,
|
||||||
|
paymentConfirmResponse = paymentConfirmResponse
|
||||||
|
)
|
||||||
|
|
||||||
|
return ReservationPaymentResponse(reservationId, reservation.status, payment.id, payment.status)
|
||||||
|
.also { log.info { "[ReservationWithPaymentServiceV2.payReservation] 예약 결제 완료: response=${it}" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelReservation(
|
||||||
|
memberId: Long,
|
||||||
|
reservationId: Long,
|
||||||
|
request: ReservationCancelRequest
|
||||||
|
) {
|
||||||
|
log.info {
|
||||||
|
"[ReservationWithPaymentServiceV2.cancelReservation] " +
|
||||||
|
"예약 취소 시작: memberId=$memberId, reservationId=$reservationId, request=$request"
|
||||||
|
}
|
||||||
|
|
||||||
|
val paymentCancelResponse = paymentWriter.requestCancelPayment(reservationId, request)
|
||||||
|
|
||||||
|
transactionExecutionUtil.withNewTransaction(isReadOnly = false) {
|
||||||
|
paymentWriter.createCanceledPayment(memberId, reservationId, request, paymentCancelResponse)
|
||||||
|
reservationFinder.findById(reservationId).also { reservationWriter.cancelByUser(it, memberId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,4 +17,6 @@ enum class ReservationErrorCode(
|
|||||||
NOT_RESERVATION_OWNER(HttpStatus.FORBIDDEN, "R006", "타인의 예약은 취소할 수 없어요."),
|
NOT_RESERVATION_OWNER(HttpStatus.FORBIDDEN, "R006", "타인의 예약은 취소할 수 없어요."),
|
||||||
INVALID_SEARCH_DATE_RANGE(HttpStatus.BAD_REQUEST, "R007", "종료 날짜는 시작 날짜 이후여야 해요."),
|
INVALID_SEARCH_DATE_RANGE(HttpStatus.BAD_REQUEST, "R007", "종료 날짜는 시작 날짜 이후여야 해요."),
|
||||||
NO_PERMISSION(HttpStatus.FORBIDDEN, "R008", "접근 권한이 없어요."),
|
NO_PERMISSION(HttpStatus.FORBIDDEN, "R008", "접근 권한이 없어요."),
|
||||||
|
RESERVATION_NOT_PENDING(HttpStatus.BAD_REQUEST, "R009", "결제 대기 중인 예약이 아니에요."),
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,4 +91,14 @@ class ReservationFinder(
|
|||||||
return reservationRepository.existsByTime(time)
|
return reservationRepository.existsByTime(time)
|
||||||
.also { log.debug { "[ReservationFinder.isTimeReserved] 완료: isExist=$it, timeId=${time.id}, startAt=${time.startAt}" } }
|
.also { log.debug { "[ReservationFinder.isTimeReserved] 완료: isExist=$it, timeId=${time.id}, startAt=${time.startAt}" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun findPendingReservation(reservationId: Long, memberId: Long): ReservationEntity {
|
||||||
|
log.debug { "[ReservationFinder.findPendingReservationIfExists] 시작: reservationId=$reservationId, memberId=$memberId" }
|
||||||
|
|
||||||
|
return findById(reservationId).also {
|
||||||
|
reservationValidator.validateIsReservedByMemberAndPending(it, memberId)
|
||||||
|
}.also {
|
||||||
|
log.debug { "[ReservationFinder.findPendingReservationIfExists] 완료: reservationId=${it.id}, status=${it.status}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import roomescape.reservation.exception.ReservationException
|
|||||||
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationRepository
|
import roomescape.reservation.infrastructure.persistence.ReservationRepository
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationSearchSpecification
|
import roomescape.reservation.infrastructure.persistence.ReservationSearchSpecification
|
||||||
|
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||||
import roomescape.time.infrastructure.persistence.TimeEntity
|
import roomescape.time.infrastructure.persistence.TimeEntity
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
@ -141,4 +142,15 @@ class ReservationValidator(
|
|||||||
|
|
||||||
log.debug { "[ReservationValidator.validateAlreadyConfirmed] 완료: reservationId=$reservationId" }
|
log.debug { "[ReservationValidator.validateAlreadyConfirmed] 완료: reservationId=$reservationId" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun validateIsReservedByMemberAndPending(reservation: ReservationEntity, requesterId: Long) {
|
||||||
|
if (reservation.member.id != requesterId) {
|
||||||
|
log.error { "[ReservationValidator.validateIsReservedByMemberAndPending] 예약자 본인이 아님: reservationId=${reservation.id}, reservation.memberId=${reservation.member.id} requesterId=$requesterId" }
|
||||||
|
throw ReservationException(ReservationErrorCode.NOT_RESERVATION_OWNER)
|
||||||
|
}
|
||||||
|
if (reservation.status != ReservationStatus.PENDING) {
|
||||||
|
log.warn { "[ReservationValidator.validateIsReservedByMemberAndPending] 예약 상태가 대기 중이 아님: reservationId=${reservation.id}, status=${reservation.status}" }
|
||||||
|
throw ReservationException(ReservationErrorCode.RESERVATION_NOT_PENDING)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,6 @@ import io.github.oshai.kotlinlogging.KotlinLogging
|
|||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import roomescape.common.config.next
|
import roomescape.common.config.next
|
||||||
import roomescape.member.implement.MemberFinder
|
import roomescape.member.implement.MemberFinder
|
||||||
import roomescape.reservation.exception.ReservationErrorCode
|
|
||||||
import roomescape.reservation.exception.ReservationException
|
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationRepository
|
import roomescape.reservation.infrastructure.persistence.ReservationRepository
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||||
@ -101,4 +99,15 @@ class ReservationWriter(
|
|||||||
|
|
||||||
log.debug { "[ReservationWriter.confirm] 완료: reservationId=$reservationId, status=${ReservationStatus.CONFIRMED_PAYMENT_REQUIRED}" }
|
log.debug { "[ReservationWriter.confirm] 완료: reservationId=$reservationId, status=${ReservationStatus.CONFIRMED_PAYMENT_REQUIRED}" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancelByUser(reservation: ReservationEntity, requesterId: Long) {
|
||||||
|
log.debug { "[ReservationWriter.cancel] 예약 취소 시작: reservationId=${reservation.id}, requesterId=$requesterId" }
|
||||||
|
|
||||||
|
memberFinder.findById(requesterId)
|
||||||
|
.also { reservationValidator.validateDeleteAuthority(reservation, requester = it) }
|
||||||
|
|
||||||
|
reservation.cancelByUser().also {
|
||||||
|
log.debug { "[ReservationWriter.cancel] 예약 취소 완료: reservationId=${reservation.id}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,12 +43,23 @@ class ReservationEntity(
|
|||||||
fun isReservedBy(memberId: Long): Boolean {
|
fun isReservedBy(memberId: Long): Boolean {
|
||||||
return this.member.id == memberId
|
return this.member.id == memberId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancelByUser() {
|
||||||
|
this.status = ReservationStatus.CANCELED_BY_USER
|
||||||
|
}
|
||||||
|
|
||||||
|
fun confirm() {
|
||||||
|
this.status = ReservationStatus.CONFIRMED
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ReservationStatus {
|
enum class ReservationStatus {
|
||||||
CONFIRMED,
|
CONFIRMED,
|
||||||
CONFIRMED_PAYMENT_REQUIRED,
|
CONFIRMED_PAYMENT_REQUIRED,
|
||||||
|
PENDING,
|
||||||
WAITING,
|
WAITING,
|
||||||
|
CANCELED_BY_USER,
|
||||||
|
AUTOMATICALLY_CANCELED,
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user