generated from pricelees/issue-pr-template
[#35] 결제 스키마 재정의 & 예약 조회 페이지 개선 #36
@ -0,0 +1,54 @@
|
|||||||
|
package roomescape.reservation.business
|
||||||
|
|
||||||
|
import io.github.oshai.kotlinlogging.KLogger
|
||||||
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import roomescape.payment.implement.PaymentFinderV2
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.CanceledPaymentEntityV2
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentDetailEntity
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentEntityV2
|
||||||
|
import roomescape.reservation.implement.ReservationFinder
|
||||||
|
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
||||||
|
import roomescape.reservation.web.ReservationDetailRetrieveResponse
|
||||||
|
import roomescape.reservation.web.ReservationSummaryRetrieveListResponse
|
||||||
|
import roomescape.reservation.web.toCancelDetailResponse
|
||||||
|
import roomescape.reservation.web.toPaymentDetailResponse
|
||||||
|
import roomescape.reservation.web.toReservationDetailRetrieveResponse
|
||||||
|
import roomescape.reservation.web.toRetrieveResponse
|
||||||
|
import roomescape.reservation.web.toSummaryListResponse
|
||||||
|
|
||||||
|
private val log: KLogger = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class MyReservationFindService(
|
||||||
|
private val reservationFinder: ReservationFinder,
|
||||||
|
private val paymentFinder: PaymentFinderV2
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
fun findReservationsByMemberId(memberId: Long): ReservationSummaryRetrieveListResponse {
|
||||||
|
log.debug { "[ReservationFindServiceV2.findReservationsByMemberId] 시작: memberId=$memberId" }
|
||||||
|
|
||||||
|
return reservationFinder.findAllByMemberIdV2(memberId)
|
||||||
|
.toSummaryListResponse()
|
||||||
|
.also { log.info { "[ReservationFindServiceV2.findReservationsByMemberId] ${it.reservations.size}개의 예약 조회 완료: memberId=$memberId" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
fun showReservationDetails(reservationId: Long): ReservationDetailRetrieveResponse {
|
||||||
|
log.debug { "[ReservationFindServiceV2.showReservationDetails] 시작: reservationId=$reservationId" }
|
||||||
|
|
||||||
|
val reservation: ReservationEntity = reservationFinder.findById(reservationId)
|
||||||
|
val payment: PaymentEntityV2 = paymentFinder.findPaymentByReservationId(reservationId)
|
||||||
|
val paymentDetail: PaymentDetailEntity = paymentFinder.findPaymentDetailByPaymentId(payment.id)
|
||||||
|
val canceledPayment: CanceledPaymentEntityV2? = paymentFinder.findCanceledPaymentByPaymentIdOrNull(payment.id)
|
||||||
|
|
||||||
|
return reservation.toReservationDetailRetrieveResponse(
|
||||||
|
payment = payment.toRetrieveResponse(detail = paymentDetail.toPaymentDetailResponse()),
|
||||||
|
cancellation = canceledPayment?.toCancelDetailResponse()
|
||||||
|
).also {
|
||||||
|
log.info { "[ReservationFindServiceV2.showReservationDetails] 예약 상세 조회 완료: reservationId=$reservationId" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package roomescape.reservation.docs
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponses
|
||||||
|
import org.springframework.http.ResponseEntity
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import roomescape.auth.web.support.LoginRequired
|
||||||
|
import roomescape.auth.web.support.MemberId
|
||||||
|
import roomescape.common.dto.response.CommonApiResponse
|
||||||
|
import roomescape.reservation.web.ReservationDetailRetrieveResponse
|
||||||
|
import roomescape.reservation.web.ReservationSummaryRetrieveListResponse
|
||||||
|
|
||||||
|
interface MyReservationAPI {
|
||||||
|
@LoginRequired
|
||||||
|
@Operation(summary = "내 예약 개요 조회", tags = ["로그인이 필요한 API"])
|
||||||
|
@ApiResponses(
|
||||||
|
ApiResponse(responseCode = "200", description = "성공"),
|
||||||
|
)
|
||||||
|
fun findAllMyReservations(
|
||||||
|
@MemberId @Parameter(hidden = true) memberId: Long
|
||||||
|
): ResponseEntity<CommonApiResponse<ReservationSummaryRetrieveListResponse>>
|
||||||
|
|
||||||
|
@LoginRequired
|
||||||
|
@Operation(summary = "예약 상세 조회", tags = ["로그인이 필요한 API"])
|
||||||
|
@ApiResponses(
|
||||||
|
ApiResponse(responseCode = "200", description = "성공"),
|
||||||
|
)
|
||||||
|
fun showReservationDetails(
|
||||||
|
@PathVariable("id") reservationId: Long
|
||||||
|
): ResponseEntity<CommonApiResponse<ReservationDetailRetrieveResponse>>
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package roomescape.reservation.web
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter
|
||||||
|
import org.springframework.http.ResponseEntity
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import roomescape.auth.web.support.MemberId
|
||||||
|
import roomescape.common.dto.response.CommonApiResponse
|
||||||
|
import roomescape.reservation.business.MyReservationFindService
|
||||||
|
import roomescape.reservation.docs.MyReservationAPI
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
class MyReservationController(
|
||||||
|
private val reservationFindService: MyReservationFindService
|
||||||
|
) : MyReservationAPI {
|
||||||
|
|
||||||
|
@GetMapping("/v2/reservations")
|
||||||
|
override fun findAllMyReservations(
|
||||||
|
@MemberId @Parameter(hidden=true) memberId: Long
|
||||||
|
): ResponseEntity<CommonApiResponse<ReservationSummaryRetrieveListResponse>> {
|
||||||
|
val response = reservationFindService.findReservationsByMemberId(memberId)
|
||||||
|
|
||||||
|
return ResponseEntity.ok(CommonApiResponse(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/v2/reservations/{id}/details")
|
||||||
|
override fun showReservationDetails(
|
||||||
|
@PathVariable("id") reservationId: Long
|
||||||
|
): ResponseEntity<CommonApiResponse<ReservationDetailRetrieveResponse>> {
|
||||||
|
val response = reservationFindService.showReservationDetails(reservationId)
|
||||||
|
|
||||||
|
return ResponseEntity.ok(CommonApiResponse(response))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,196 @@
|
|||||||
|
package roomescape.reservation.web
|
||||||
|
|
||||||
|
import roomescape.member.infrastructure.persistence.MemberEntity
|
||||||
|
import roomescape.payment.exception.PaymentErrorCode
|
||||||
|
import roomescape.payment.exception.PaymentException
|
||||||
|
import roomescape.payment.infrastructure.common.PaymentStatus
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.CanceledPaymentEntityV2
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentBankTransferDetailEntity
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentCardDetailEntity
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentDetailEntity
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentEasypayPrepaidDetailEntity
|
||||||
|
import roomescape.payment.infrastructure.persistence.v2.PaymentEntityV2
|
||||||
|
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
||||||
|
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||||
|
import roomescape.reservation.web.PaymentDetailResponse.BankTransferDetailResponse
|
||||||
|
import roomescape.reservation.web.PaymentDetailResponse.CardDetailResponse
|
||||||
|
import roomescape.reservation.web.PaymentDetailResponse.EasyPayPrepaidDetailResponse
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.LocalTime
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import kotlin.Int
|
||||||
|
|
||||||
|
data class ReservationSummaryRetrieveResponse(
|
||||||
|
val id: Long,
|
||||||
|
val themeName: String,
|
||||||
|
val date: LocalDate,
|
||||||
|
val startAt: LocalTime,
|
||||||
|
val status: ReservationStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
fun ReservationEntity.toReservationSummaryRetrieveResponse(): ReservationSummaryRetrieveResponse {
|
||||||
|
return ReservationSummaryRetrieveResponse(
|
||||||
|
id = this.id!!,
|
||||||
|
themeName = this.theme.name,
|
||||||
|
date = this.date,
|
||||||
|
startAt = this.time.startAt,
|
||||||
|
status = this.status
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ReservationSummaryRetrieveListResponse(
|
||||||
|
val reservations: List<ReservationSummaryRetrieveResponse>
|
||||||
|
)
|
||||||
|
|
||||||
|
fun List<ReservationEntity>.toSummaryListResponse(): ReservationSummaryRetrieveListResponse {
|
||||||
|
return ReservationSummaryRetrieveListResponse(
|
||||||
|
reservations = this.map { it.toReservationSummaryRetrieveResponse() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ReservationDetailRetrieveResponse(
|
||||||
|
val id: Long,
|
||||||
|
val user: UserDetailRetrieveResponse,
|
||||||
|
val themeName: String,
|
||||||
|
val date: LocalDate,
|
||||||
|
val startAt: LocalTime,
|
||||||
|
val applicationDateTime: LocalDateTime,
|
||||||
|
val payment: PaymentRetrieveResponse,
|
||||||
|
val cancellation: PaymentCancelDetailResponse? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class UserDetailRetrieveResponse(
|
||||||
|
val id: Long,
|
||||||
|
val name: String,
|
||||||
|
val email: String
|
||||||
|
)
|
||||||
|
|
||||||
|
fun MemberEntity.toUserDetailRetrieveResponse(): UserDetailRetrieveResponse {
|
||||||
|
return UserDetailRetrieveResponse(
|
||||||
|
id = this.id!!,
|
||||||
|
name = this.name,
|
||||||
|
email = this.email
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ReservationEntity.toReservationDetailRetrieveResponse(
|
||||||
|
payment: PaymentRetrieveResponse,
|
||||||
|
cancellation: PaymentCancelDetailResponse? = null
|
||||||
|
): ReservationDetailRetrieveResponse {
|
||||||
|
return ReservationDetailRetrieveResponse(
|
||||||
|
id = this.id!!,
|
||||||
|
user = this.member.toUserDetailRetrieveResponse(),
|
||||||
|
themeName = this.theme.name,
|
||||||
|
date = this.date,
|
||||||
|
startAt = this.time.startAt,
|
||||||
|
applicationDateTime = this.createdAt!!,
|
||||||
|
payment = payment,
|
||||||
|
cancellation = cancellation,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class PaymentRetrieveResponse(
|
||||||
|
val orderId: String,
|
||||||
|
val totalAmount: Int,
|
||||||
|
val method: String,
|
||||||
|
val status: PaymentStatus,
|
||||||
|
val requestedAt: OffsetDateTime,
|
||||||
|
val approvedAt: OffsetDateTime,
|
||||||
|
val detail: PaymentDetailResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun PaymentEntityV2.toRetrieveResponse(detail: PaymentDetailResponse): PaymentRetrieveResponse {
|
||||||
|
return PaymentRetrieveResponse(
|
||||||
|
orderId = this.orderId,
|
||||||
|
totalAmount = this.totalAmount,
|
||||||
|
method = this.method.koreanName,
|
||||||
|
status = this.status,
|
||||||
|
requestedAt = this.requestedAt,
|
||||||
|
approvedAt = this.approvedAt,
|
||||||
|
detail = detail
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class PaymentDetailResponse {
|
||||||
|
|
||||||
|
data class CardDetailResponse(
|
||||||
|
val type: String = "CARD",
|
||||||
|
val issuerCode: String,
|
||||||
|
val cardType: String,
|
||||||
|
val ownerType: String,
|
||||||
|
val cardNumber: String,
|
||||||
|
val amount: Int,
|
||||||
|
val approvalNumber: String,
|
||||||
|
val installmentPlanMonths: Int,
|
||||||
|
val easypayProviderName: String?,
|
||||||
|
val easypayDiscountAmount: Int?,
|
||||||
|
) : PaymentDetailResponse()
|
||||||
|
|
||||||
|
|
||||||
|
data class BankTransferDetailResponse(
|
||||||
|
val type: String = "BANK_TRANSFER",
|
||||||
|
val bankName: String,
|
||||||
|
) : PaymentDetailResponse()
|
||||||
|
|
||||||
|
|
||||||
|
data class EasyPayPrepaidDetailResponse(
|
||||||
|
val type: String = "EASYPAY_PREPAID",
|
||||||
|
val providerName: String,
|
||||||
|
val amount: Int,
|
||||||
|
val discountAmount: Int,
|
||||||
|
) : PaymentDetailResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PaymentDetailEntity.toPaymentDetailResponse(): PaymentDetailResponse {
|
||||||
|
return when (this) {
|
||||||
|
is PaymentCardDetailEntity -> this.toCardDetailResponse()
|
||||||
|
is PaymentBankTransferDetailEntity -> this.toBankTransferDetailResponse()
|
||||||
|
is PaymentEasypayPrepaidDetailEntity -> this.toEasyPayPrepaidDetailResponse()
|
||||||
|
else -> throw PaymentException(PaymentErrorCode.NOT_SUPPORTED_PAYMENT_TYPE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PaymentCardDetailEntity.toCardDetailResponse(): CardDetailResponse {
|
||||||
|
return CardDetailResponse(
|
||||||
|
issuerCode = this.issuerCode.koreanName,
|
||||||
|
cardType = this.cardType.koreanName,
|
||||||
|
ownerType = this.ownerType.koreanName,
|
||||||
|
cardNumber = this.cardNumber,
|
||||||
|
amount = this.amount,
|
||||||
|
approvalNumber = this.approvalNumber,
|
||||||
|
installmentPlanMonths = this.installmentPlanMonths,
|
||||||
|
easypayProviderName = this.easypayProviderCode?.koreanName,
|
||||||
|
easypayDiscountAmount = this.easypayDiscountAmount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PaymentBankTransferDetailEntity.toBankTransferDetailResponse(): BankTransferDetailResponse {
|
||||||
|
return BankTransferDetailResponse(
|
||||||
|
bankName = this.bankCode.koreanName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PaymentEasypayPrepaidDetailEntity.toEasyPayPrepaidDetailResponse(): EasyPayPrepaidDetailResponse {
|
||||||
|
return EasyPayPrepaidDetailResponse(
|
||||||
|
providerName = this.easypayProviderCode.koreanName,
|
||||||
|
amount = this.amount,
|
||||||
|
discountAmount = this.discountAmount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class PaymentCancelDetailResponse(
|
||||||
|
val cancellationRequestedAt: LocalDateTime,
|
||||||
|
val cancellationApprovedAt: OffsetDateTime?,
|
||||||
|
val cancelReason: String,
|
||||||
|
val canceledBy: Long,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun CanceledPaymentEntityV2.toCancelDetailResponse(): PaymentCancelDetailResponse {
|
||||||
|
return PaymentCancelDetailResponse(
|
||||||
|
cancellationRequestedAt = this.requestedAt,
|
||||||
|
cancellationApprovedAt = this.canceledAt,
|
||||||
|
cancelReason = this.cancelReason,
|
||||||
|
canceledBy = this.canceledBy
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user