package roomescape.time.business import com.github.f4b6a3.tsid.TsidFactory 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.reservation.infrastructure.persistence.ReservationEntity import roomescape.reservation.infrastructure.persistence.ReservationRepository import roomescape.time.exception.TimeErrorCode import roomescape.time.exception.TimeException import roomescape.time.infrastructure.persistence.TimeEntity import roomescape.time.infrastructure.persistence.TimeRepository import roomescape.time.web.* import java.time.LocalDate import java.time.LocalTime private val log = KotlinLogging.logger {} @Service class TimeService( private val tsidFactory: TsidFactory, private val timeRepository: TimeRepository, private val reservationRepository: ReservationRepository, ) { @Transactional(readOnly = true) fun findById(id: Long): TimeEntity { log.debug { "[TimeService.findById] 시간 조회 시작: timeId=$id" } return timeRepository.findByIdOrNull(id) ?.also { log.info { "[TimeService.findById] 시간 조회 완료: timeId=$id" } } ?: run { log.warn { "[TimeService.findById] 시간 조회 실패: timeId=$id" } throw TimeException(TimeErrorCode.TIME_NOT_FOUND) } } @Transactional(readOnly = true) fun findTimes(): TimeRetrieveListResponse { log.debug { "[TimeService.findTimes] 모든 시간 조회 시작" } return timeRepository.findAll() .also { log.info { "[TimeService.findTimes] ${it.size}개의 시간 조회 완료" } } .toResponse() } @Transactional fun createTime(request: TimeCreateRequest): TimeCreateResponse { log.debug { "[TimeService.createTime] 시간 생성 시작: startAt=${request.startAt}" } val startAt: LocalTime = request.startAt if (timeRepository.existsByStartAt(startAt)) { log.info { "[TimeService.createTime] 시간 생성 실패(시간 중복): startAt=$startAt" } throw TimeException(TimeErrorCode.TIME_DUPLICATED) } val time = TimeEntity( _id = tsidFactory.next(), startAt = request.startAt ) return timeRepository.save(time) .also { log.info { "[TimeService.createTime] 시간 생성 완료: timeId=${it.id}" } } .toCreateResponse() } @Transactional fun deleteTime(id: Long) { log.debug { "[TimeService.deleteTime] 시간 삭제 시작: timeId=$id" } val time: TimeEntity = findById(id) log.debug { "[TimeService.deleteTime] 시간이 ${time.startAt}인 모든 예약 조회 시작" } val reservations: List = reservationRepository.findAllByTime(time) log.debug { "[TimeService.deleteTime] 시간이 ${time.startAt}인 모든 ${reservations.size} 개의 예약 조회 완료" } if (reservations.isNotEmpty()) { log.info { "[TimeService.deleteTime] 시간 삭제 실패(예약이 있는 시간): timeId=$id" } throw TimeException(TimeErrorCode.TIME_ALREADY_RESERVED) } timeRepository.delete(time) .also { log.info { "[TimeService.deleteTime] 시간 삭제 완료: timeId=$id" } } } @Transactional(readOnly = true) fun findTimesWithAvailability(date: LocalDate, themeId: Long): TimeWithAvailabilityListResponse { log.debug { "[TimeService.findTimesWithAvailability] 예약 가능 시간 조회 시작: date=$date, themeId=$themeId" } log.debug { "[TimeService.findTimesWithAvailability] 모든 시간 조회 " } val allTimes = timeRepository.findAll() log.debug { "[TimeService.findTimesWithAvailability] ${allTimes.size}개의 시간 조회 완료" } log.debug { "[TimeService.findTimesWithAvailability] date=$date, themeId=$themeId 인 모든 예약 조회 시작" } val reservations: List = reservationRepository.findByDateAndThemeId(date, themeId) log.debug { "[TimeService.findTimesWithAvailability] date=$date, themeId=$themeId 인 ${reservations.size} 개의 예약 조회 완료" } return TimeWithAvailabilityListResponse(allTimes.map { time -> val isAvailable: Boolean = reservations.none { reservation -> reservation.time.id == time.id } TimeWithAvailabilityResponse(time.id!!, time.startAt, isAvailable) }).also { log.info { "[TimeService.findTimesWithAvailability] date=$date, themeId=$themeId 에 대한 예약 가능 여부가 담긴 모든 시간 조회 완료" } } } }