generated from pricelees/issue-pr-template
refactor: 예약 API 변경에 따른 서비스 로직 재정의
This commit is contained in:
parent
9e8cb87641
commit
ddf366c587
@ -0,0 +1,163 @@
|
|||||||
|
package roomescape.reservation.business
|
||||||
|
|
||||||
|
import com.github.f4b6a3.tsid.TsidFactory
|
||||||
|
import io.github.oshai.kotlinlogging.KLogger
|
||||||
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import roomescape.common.config.next
|
||||||
|
import roomescape.member.business.MemberService
|
||||||
|
import roomescape.member.infrastructure.persistence.Role
|
||||||
|
import roomescape.member.web.MemberSummaryRetrieveResponse
|
||||||
|
import roomescape.payment.business.PaymentService
|
||||||
|
import roomescape.payment.web.PaymentRetrieveResponse
|
||||||
|
import roomescape.reservation.exception.ReservationErrorCode
|
||||||
|
import roomescape.reservation.exception.ReservationException
|
||||||
|
import roomescape.reservation.infrastructure.persistence.*
|
||||||
|
import roomescape.reservation.web.*
|
||||||
|
import roomescape.schedule.business.ScheduleService
|
||||||
|
import roomescape.schedule.infrastructure.persistence.ScheduleStatus
|
||||||
|
import roomescape.schedule.web.ScheduleSummaryResponse
|
||||||
|
import roomescape.schedule.web.ScheduleUpdateRequest
|
||||||
|
import roomescape.theme.business.ThemeServiceV2
|
||||||
|
import roomescape.theme.web.ThemeRetrieveResponseV2
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
private val log: KLogger = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class ReservationService(
|
||||||
|
private val reservationRepository: ReservationRepository,
|
||||||
|
private val scheduleService: ScheduleService,
|
||||||
|
private val memberService: MemberService,
|
||||||
|
private val themeService: ThemeServiceV2,
|
||||||
|
private val canceledReservationRepository: CanceledReservationRepository,
|
||||||
|
private val tsidFactory: TsidFactory,
|
||||||
|
private val paymentService: PaymentService
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun createPendingReservation(
|
||||||
|
memberId: Long,
|
||||||
|
request: PendingReservationCreateRequest
|
||||||
|
): PendingReservationCreateResponse {
|
||||||
|
log.info { "[ReservationService.createPendingReservation] Pending 예약 생성 시작: schedule=${request.scheduleId}" }
|
||||||
|
|
||||||
|
val reservation: ReservationEntity = request.toEntity(
|
||||||
|
id = tsidFactory.next(),
|
||||||
|
memberId = memberId
|
||||||
|
)
|
||||||
|
|
||||||
|
return PendingReservationCreateResponse(reservationRepository.save(reservation).id)
|
||||||
|
.also { "[ReservationService.createPendingReservation] Pending 예약 생성 완료: reservationId=${it}, schedule=${request.scheduleId}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun confirmReservation(id: Long) {
|
||||||
|
log.info { "[ReservationService.confirmReservation] Pending 예약 확정 시작: reservationId=${id}" }
|
||||||
|
val reservation: ReservationEntity = findOrThrow(id)
|
||||||
|
|
||||||
|
if (reservation.status != ReservationStatus.PENDING) {
|
||||||
|
log.warn { "[ReservationService.confirmReservation] 예약이 Pending 상태가 아님: reservationId=${id}, status=${reservation.status}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
run {
|
||||||
|
reservation.confirm()
|
||||||
|
scheduleService.updateSchedule(
|
||||||
|
reservation.scheduleId,
|
||||||
|
ScheduleUpdateRequest(status = ScheduleStatus.RESERVED)
|
||||||
|
)
|
||||||
|
}.also {
|
||||||
|
log.info { "[ReservationService.confirmReservation] Pending 예약 확정 완료: reservationId=${id}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun cancelReservation(memberId: Long, reservationId: Long, request: ReservationCancelRequest) {
|
||||||
|
log.info { "[ReservationService.cancelReservation] 예약 취소 시작: memberId=${memberId}, reservationId=${reservationId}" }
|
||||||
|
|
||||||
|
val reservation: ReservationEntity = findOrThrow(reservationId)
|
||||||
|
val member: MemberSummaryRetrieveResponse = memberService.findSummaryById(memberId)
|
||||||
|
|
||||||
|
run {
|
||||||
|
scheduleService.updateSchedule(
|
||||||
|
reservation.scheduleId,
|
||||||
|
ScheduleUpdateRequest(status = ScheduleStatus.AVAILABLE)
|
||||||
|
)
|
||||||
|
saveCanceledReservation(member, reservation, request.cancelReason)
|
||||||
|
reservation.cancel()
|
||||||
|
}.also {
|
||||||
|
log.info { "[ReservationService.cancelReservation] 예약 취소 완료: reservationId=${reservationId}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
fun findSummaryByMemberId(memberId: Long): ReservationSummaryRetrieveListResponse {
|
||||||
|
log.info { "[ReservationService.findSummaryByMemberId] 예약 조회 시작: memberId=${memberId}" }
|
||||||
|
|
||||||
|
val reservations: List<ReservationEntity> = reservationRepository.findAllByMemberId(memberId)
|
||||||
|
|
||||||
|
return ReservationSummaryRetrieveListResponse(reservations.map {
|
||||||
|
val schedule: ScheduleSummaryResponse = scheduleService.findSummaryById(it.scheduleId)
|
||||||
|
val theme: ThemeRetrieveResponseV2 = themeService.findById(schedule.themeId)
|
||||||
|
|
||||||
|
ReservationSummaryRetrieveResponse(
|
||||||
|
id = it.id,
|
||||||
|
themeName = theme.name,
|
||||||
|
date = schedule.date,
|
||||||
|
startAt = schedule.time,
|
||||||
|
status = it.status
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
fun findDetailById(id: Long): ReservationDetailRetrieveResponse {
|
||||||
|
log.info { "[ReservationService.findDetailById] 예약 상세 조회 시작: reservationId=${id}" }
|
||||||
|
|
||||||
|
val reservation: ReservationEntity = findOrThrow(id)
|
||||||
|
val member: MemberSummaryRetrieveResponse = memberService.findSummaryById(reservation.memberId)
|
||||||
|
val paymentDetail: PaymentRetrieveResponse = paymentService.findDetailByReservationId(id)
|
||||||
|
|
||||||
|
return reservation.toReservationDetailRetrieveResponse(
|
||||||
|
member = member,
|
||||||
|
payment = paymentDetail
|
||||||
|
).also {
|
||||||
|
log.info { "[ReservationService.findDetailById] 예약 상세 조회 완료: reservationId=${id}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findOrThrow(id: Long): ReservationEntity {
|
||||||
|
log.info { "[ReservationService.findOrThrow] 예약 조회 시작: reservationId=${id}" }
|
||||||
|
|
||||||
|
return reservationRepository.findByIdOrNull(id)
|
||||||
|
?.also { log.info { "[ReservationService.findOrThrow] 예약 조회 완료: reservationId=${id}" } }
|
||||||
|
?: run {
|
||||||
|
log.warn { "[ReservationService.findOrThrow] 예약 조회 실패: reservationId=${id}" }
|
||||||
|
throw ReservationException(ReservationErrorCode.RESERVATION_NOT_FOUND)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveCanceledReservation(
|
||||||
|
member: MemberSummaryRetrieveResponse,
|
||||||
|
reservation: ReservationEntity,
|
||||||
|
cancelReason: String
|
||||||
|
) {
|
||||||
|
if (member.role != Role.ADMIN && reservation.memberId != member.id) {
|
||||||
|
log.warn { "[ReservationService.createCanceledPayment] 예약자 본인 또는 관리자가 아닌 회원의 취소 요청: reservationId=${reservation.id}, memberId=${member.id}" }
|
||||||
|
throw ReservationException(ReservationErrorCode.NO_PERMISSION_TO_CANCEL_RESERVATION)
|
||||||
|
}
|
||||||
|
|
||||||
|
CanceledReservationEntity(
|
||||||
|
id = tsidFactory.next(),
|
||||||
|
reservationId = reservation.id,
|
||||||
|
canceledBy = member.id,
|
||||||
|
cancelReason = cancelReason,
|
||||||
|
canceledAt = LocalDateTime.now(),
|
||||||
|
status = CanceledReservationStatus.PROCESSING
|
||||||
|
).also {
|
||||||
|
canceledReservationRepository.save(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,75 +1,8 @@
|
|||||||
package roomescape.reservation.infrastructure.persistence
|
package roomescape.reservation.infrastructure.persistence
|
||||||
|
|
||||||
import org.springframework.data.jpa.domain.Specification
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor
|
|
||||||
import org.springframework.data.jpa.repository.Modifying
|
|
||||||
import org.springframework.data.jpa.repository.Query
|
|
||||||
import org.springframework.data.repository.query.Param
|
|
||||||
import roomescape.reservation.web.MyReservationRetrieveResponse
|
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
|
||||||
import roomescape.time.infrastructure.persistence.TimeEntity
|
|
||||||
import java.time.LocalDate
|
|
||||||
|
|
||||||
interface ReservationRepository
|
interface ReservationRepository : JpaRepository<ReservationEntity, Long> {
|
||||||
: JpaRepository<ReservationEntity, Long>, JpaSpecificationExecutor<ReservationEntity> {
|
|
||||||
fun findAllByTime(time: TimeEntity): List<ReservationEntity>
|
|
||||||
fun existsByTime(time: TimeEntity): Boolean
|
|
||||||
|
|
||||||
fun findByDateAndThemeId(date: LocalDate, themeId: Long): List<ReservationEntity>
|
fun findAllByMemberId(memberId: Long): List<ReservationEntity>
|
||||||
|
|
||||||
@Modifying
|
|
||||||
@Query(
|
|
||||||
"""
|
|
||||||
UPDATE ReservationEntity r
|
|
||||||
SET r.status = :status
|
|
||||||
WHERE r._id = :_id
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
fun updateStatusByReservationId(
|
|
||||||
@Param(value = "_id") reservationId: Long,
|
|
||||||
@Param(value = "status") statusForChange: ReservationStatus
|
|
||||||
): Int
|
|
||||||
|
|
||||||
@Query(
|
|
||||||
"""
|
|
||||||
SELECT EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM ReservationEntity r2
|
|
||||||
WHERE r2._id = :_id
|
|
||||||
AND EXISTS (
|
|
||||||
SELECT 1 FROM ReservationEntity r
|
|
||||||
WHERE r.theme._id = r2.theme._id
|
|
||||||
AND r.time._id = r2.time._id
|
|
||||||
AND r.date = r2.date
|
|
||||||
AND r.status != 'WAITING'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
fun isExistConfirmedReservation(@Param("_id") reservationId: Long): Boolean
|
|
||||||
|
|
||||||
@Query(
|
|
||||||
"""
|
|
||||||
SELECT new roomescape.reservation.web.MyReservationRetrieveResponse(
|
|
||||||
r._id,
|
|
||||||
t.name,
|
|
||||||
r.date,
|
|
||||||
r.time.startAt,
|
|
||||||
r.status,
|
|
||||||
(SELECT COUNT (r2) * 1L FROM ReservationEntity r2 WHERE r2.theme = r.theme AND r2.date = r.date AND r2.time = r.time AND r2._id < r._id),
|
|
||||||
p.paymentKey,
|
|
||||||
p.totalAmount
|
|
||||||
)
|
|
||||||
FROM ReservationEntity r
|
|
||||||
JOIN r.theme t
|
|
||||||
LEFT JOIN PaymentEntity p
|
|
||||||
ON p.reservation = r
|
|
||||||
WHERE r.member._id = :memberId
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
fun findAllByMemberId(memberId: Long): List<MyReservationRetrieveResponse>
|
|
||||||
fun findAllByDateAndTheme(date: LocalDate, theme: ThemeEntity): List<ReservationEntity>
|
|
||||||
|
|
||||||
fun findAllByMember_Id(memberId: Long): List<ReservationEntity>
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user