107 lines
4.8 KiB
Kotlin

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<ReservationEntity> = 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<ReservationEntity> = 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 에 대한 예약 가능 여부가 담긴 모든 시간 조회 완료"
}
}
}
}