package roomescape.schedule.business import ScheduleException import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Component import roomescape.schedule.exception.ScheduleErrorCode import roomescape.schedule.infrastructure.persistence.ScheduleEntity import roomescape.schedule.infrastructure.persistence.ScheduleRepository import roomescape.schedule.infrastructure.persistence.ScheduleStatus import roomescape.schedule.web.ScheduleCreateRequest import roomescape.schedule.web.ScheduleUpdateRequest import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime private val log: KLogger = KotlinLogging.logger {} @Component class ScheduleValidator( private val scheduleRepository: ScheduleRepository ) { fun validateCanDelete(schedule: ScheduleEntity) { val status: ScheduleStatus = schedule.status if (status !in listOf(ScheduleStatus.AVAILABLE, ScheduleStatus.BLOCKED)) { log.info { "[ScheduleValidator.validateCanDelete] 삭제 실패: id=${schedule.id} / status=${status}" } throw ScheduleException(ScheduleErrorCode.SCHEDULE_IN_USE) } } fun validateCanUpdate(schedule: ScheduleEntity, request: ScheduleUpdateRequest) { val date: LocalDate = schedule.date val time: LocalTime = request.time ?: schedule.time validateNotInPast(date, time) } fun validateCanCreate(storeId: Long, request: ScheduleCreateRequest) { val date: LocalDate = request.date val time: LocalTime = request.time val themeId: Long = request.themeId validateAlreadyExists(storeId, date, themeId, time) validateNotInPast(date, time) validateTimeNotConflict(storeId, request.date, request.themeId, request.time) } private fun validateAlreadyExists(storeId: Long, date: LocalDate, themeId: Long, time: LocalTime) { if (scheduleRepository.existsDuplicate(storeId, date, themeId, time)) { log.info { "[ScheduleValidator.validateAlreadyExists] 동일한 날짜, 테마, 시간 존재로 인한 실패: date=${date} / themeId=${themeId} / time=${time}" } throw ScheduleException(ScheduleErrorCode.SCHEDULE_ALREADY_EXISTS) } } private fun validateNotInPast(date: LocalDate, time: LocalTime) { val dateTime = LocalDateTime.of(date, time) if (dateTime.isBefore(LocalDateTime.now())) { log.info { "[ScheduleValidator.validateDateTime] 이전 시간 선택으로 인한 실패: date=${date} / time=${time}" } throw ScheduleException(ScheduleErrorCode.PAST_DATE_TIME) } } private fun validateTimeNotConflict(storeId: Long, date: LocalDate, themeId: Long, time: LocalTime) { scheduleRepository.findStoreSchedulesWithThemeByDate(storeId, date, themeId) .firstOrNull { it.containsTime(time) } ?.let { log.info { "[ScheduleValidator.validateTimeNotConflict] 시간이 겹치는 일정 존재: conflictSchedule(Id=${it.id}, time=${it.time}~${it.getEndAt()})" } throw ScheduleException(ScheduleErrorCode.SCHEDULE_TIME_CONFLICT) } } }