package roomescape.reservation.implement import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.data.jpa.domain.Specification import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component import roomescape.reservation.exception.ReservationErrorCode import roomescape.reservation.exception.ReservationException import roomescape.reservation.infrastructure.persistence.ReservationEntity import roomescape.reservation.infrastructure.persistence.ReservationRepository import roomescape.reservation.infrastructure.persistence.ReservationSearchSpecification import roomescape.reservation.infrastructure.persistence.ReservationStatus import roomescape.reservation.web.MyReservationRetrieveResponse import roomescape.theme.infrastructure.persistence.ThemeEntity import roomescape.time.infrastructure.persistence.TimeEntity import java.time.LocalDate private val log: KLogger = KotlinLogging.logger {} @Component class ReservationFinder( private val reservationRepository: ReservationRepository, private val reservationValidator: ReservationValidator, ) { fun findById(id: Long): ReservationEntity { log.debug { "[ReservationFinder.findById] 시작: id=$id" } return reservationRepository.findByIdOrNull(id) ?.also { log.debug { "[ReservationFinder.findById] 완료: reservationId=$id, date:${it.date}, timeId:${it.time.id}, themeId:${it.theme.id}" } } ?: run { log.warn { "[ReservationFinder.findById] 조회 실패: reservationId=$id" } throw ReservationException(ReservationErrorCode.RESERVATION_NOT_FOUND) } } fun findAllByStatuses(vararg statuses: ReservationStatus): List { log.debug { "[ReservationFinder.findAll] 시작: status=${statuses}" } val spec = ReservationSearchSpecification() .status(*statuses) .build() return reservationRepository.findAll(spec) .also { log.debug { "[ReservationFinder.findAll] ${it.size}개 예약 조회 완료: status=${statuses}" } } } fun findAllByDateAndTheme( date: LocalDate, theme: ThemeEntity ): List { log.debug { "[ReservationFinder.findAllByDateAndTheme] 시작: date=$date, themeId=${theme.id}" } return reservationRepository.findAllByDateAndTheme(date, theme) .also { log.debug { "[ReservationFinder.findAllByDateAndTheme] ${it.size}개 조회 완료: date=$date, themeId=${theme.id}" } } } fun findAllByMemberId(memberId: Long): List { log.debug { "[ReservationFinder.findAllByMemberId] 시작: memberId=${memberId}" } return reservationRepository.findAllByMemberId(memberId) .also { log.debug { "[ReservationFinder.findAllByMemberId] ${it.size}개 예약(대기) 조회 완료: memberId=${memberId}" } } } fun searchReservations( themeId: Long?, memberId: Long?, startFrom: LocalDate?, endAt: LocalDate?, ): List { reservationValidator.validateSearchDateRange(startFrom, endAt) val spec: Specification = ReservationSearchSpecification() .sameThemeId(themeId) .sameMemberId(memberId) .dateStartFrom(startFrom) .dateEndAt(endAt) .status(ReservationStatus.CONFIRMED, ReservationStatus.CONFIRMED_PAYMENT_REQUIRED) .build() return reservationRepository.findAll(spec) .also { log.debug { "[ReservationFinder.searchReservations] ${it.size}개 예약 조회 완료. " + "themeId=${themeId}, memberId=${memberId}, startFrom=${startFrom}, endAt=${endAt}" } } } fun isTimeReserved(time: TimeEntity): Boolean { log.debug { "[ReservationFinder.isTimeReserved] 시작: timeId=${time.id}, startAt=${time.startAt}" } return reservationRepository.existsByTime(time) .also { log.debug { "[ReservationFinder.isTimeReserved] 완료: isExist=$it, timeId=${time.id}, startAt=${time.startAt}" } } } }