diff --git a/src/main/kotlin/roomescape/schedule/business/ScheduleService.kt b/src/main/kotlin/roomescape/schedule/business/ScheduleService.kt index 12958b86..f2edfe9c 100644 --- a/src/main/kotlin/roomescape/schedule/business/ScheduleService.kt +++ b/src/main/kotlin/roomescape/schedule/business/ScheduleService.kt @@ -9,8 +9,12 @@ import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import roomescape.admin.business.AdminService import roomescape.common.config.next +import roomescape.common.dto.AuditInfo +import roomescape.common.dto.OperatorInfo +import roomescape.schedule.business.domain.ScheduleWithThemeSummary import roomescape.schedule.exception.ScheduleErrorCode import roomescape.schedule.infrastructure.persistence.ScheduleEntity +import roomescape.schedule.infrastructure.persistence.ScheduleEntityFactory import roomescape.schedule.infrastructure.persistence.ScheduleRepository import roomescape.schedule.infrastructure.persistence.ScheduleStatus import roomescape.schedule.web.* @@ -18,6 +22,15 @@ import java.time.LocalDate private val log: KLogger = KotlinLogging.logger {} +/** + * Structure: + * - Public: 모두가 접근 가능 + * - User: 회원(로그인된 사용자)가 사용 가능 + * - All-Admin: 모든 관리자가 사용 가능 + * - Store-Admin: 매장 관리자만 사용 가능 + * - Other-Service: 다른 서비스에서 호출하는 메서드 + * - Common: 공통 메서드 + */ @Service class ScheduleService( private val scheduleRepository: ScheduleRepository, @@ -25,73 +38,25 @@ class ScheduleService( private val tsidFactory: TsidFactory, private val adminService: AdminService ) { - + // ======================================== + // Public (인증 불필요) + // ======================================== @Transactional(readOnly = true) - fun findThemesByDate(date: LocalDate): AvailableThemeIdListResponse { - log.info { "[ScheduleService.findThemesByDate] 동일한 날짜의 모든 테마 조회: date=$date" } + fun getStoreScheduleByDate(storeId: Long, date: LocalDate): ScheduleWithThemeListResponse { + log.info { "[ScheduleService.getStoreScheduleByDate] 매장 일정 조회: storeId=${storeId}, date=$date" } - return AvailableThemeIdListResponse(scheduleRepository.findAllUniqueThemeIdByDate(date)) + val schedules: List = + scheduleRepository.findStoreSchedulesWithThemeByDate(storeId, date) + + return schedules.toResponse() .also { - log.info { "[ScheduleService.findThemesByDate] date=${date} 인 ${it.themeIds.size}개 테마 조회 완료" } - } - } - - @Transactional(readOnly = true) - fun findSchedules(date: LocalDate, themeId: Long): ScheduleListByDateResponse { - log.info { "[ScheduleService.findSchedules] 동일한 날짜와 테마인 모든 일정 조회: date=${date}, themeId=${themeId}" } - - return scheduleRepository.findAllByDateAndThemeId(date, themeId) - .toListResponse() - .also { - log.info { "[ScheduleService.findSchedules] date=${date}, themeId=${themeId} 인 ${it.schedules.size}개 일정 조회 완료" } - } - } - - @Transactional(readOnly = true) - fun findDetail(id: Long): ScheduleDetailResponse { - log.info { "[ScheduleService.findDetail] 일정 상세 정보조회 시작: id=$id" } - - val schedule: ScheduleEntity = findOrThrow(id) - - val createdBy = adminService.findOperatorOrUnknown(schedule.createdBy) - val updatedBy = adminService.findOperatorOrUnknown(schedule.updatedBy) - - return schedule.toDetailResponse(createdBy, updatedBy) - .also { - log.info { "[ScheduleService.findDetail] 일정 상세 조회 완료: id=$id" } - } - } - - @Transactional(readOnly = true) - fun findSummaryById(id: Long): ScheduleSummaryResponse { - log.info { "[ScheduleService.findDateTimeById] 일정 개요 조회 시작 : id=$id" } - - return findOrThrow(id).toSummaryResponse() - .also { - log.info { "[ScheduleService.findDateTimeById] 일정 개요 조회 완료: id=$id" } - } - } - - @Transactional - fun createSchedule(request: ScheduleCreateRequest): ScheduleCreateResponse { - log.info { "[ScheduleService.createSchedule] 일정 생성 시작: date=${request.date}, time=${request.time}, themeId=${request.themeId}" } - - scheduleValidator.validateCanCreate(request) - - val schedule = ScheduleEntity( - id = tsidFactory.next(), - date = request.date, - time = request.time, - themeId = request.themeId, - status = ScheduleStatus.AVAILABLE - ) - - return ScheduleCreateResponse(scheduleRepository.save(schedule).id) - .also { - log.info { "[ScheduleService.createSchedule] 일정 생성 완료: id=${it.id}" } + log.info { "[ScheduleService.getStoreScheduleByDate] storeId=${storeId}, date=$date 인 ${it.schedules.size}개 일정 조회 완료" } } } + // ======================================== + // User (회원 로그인 필요) + // ======================================== @Transactional fun holdSchedule(id: Long) { val schedule: ScheduleEntity = findOrThrow(id) @@ -104,6 +69,64 @@ class ScheduleService( throw ScheduleException(ScheduleErrorCode.SCHEDULE_NOT_AVAILABLE) } + // ======================================== + // All-Admin (본사, 매장 모두 사용가능) + // ======================================== + @Transactional(readOnly = true) + fun searchSchedules(storeId: Long, date: LocalDate?, themeId: Long?): AdminScheduleSummaryListResponse { + log.info { "[ScheduleService.searchSchedules] 일정 검색 시작: storeId=$storeId, date=$date, themeId=$themeId" } + + val searchDate = date ?: LocalDate.now() + + val schedules: List = + scheduleRepository.findStoreSchedulesWithThemeByDate(storeId, searchDate) + .filter { (themeId == null) || (it.themeId == themeId) } + .sortedBy { it.time } + + return schedules.toAdminSummaryListResponse() + .also { + log.info { "[ScheduleService.searchSchedules] ${it.schedules.size} 개의 일정 조회 완료" } + } + } + + @Transactional(readOnly = true) + fun findScheduleAuditOrUnknown(id: Long): AuditInfo { + log.info { "[ScheduleService.findDetail] 일정 감사 정보 조회 시작: id=$id" } + + val schedule: ScheduleEntity = findOrThrow(id) + + val createdBy: OperatorInfo = adminService.findOperatorOrUnknown(schedule.createdBy) + val updatedBy: OperatorInfo = adminService.findOperatorOrUnknown(schedule.updatedBy) + + return AuditInfo(schedule.createdAt, createdBy, schedule.updatedAt, updatedBy) + .also { log.info { "[ScheduleService.findDetail] 일정 감사 정보 조회 완료: id=$id" } } + } + + // ======================================== + // Store-Admin (매장 관리자 로그인 필요) + // ======================================== + @Transactional + fun createSchedule(storeId: Long, request: ScheduleCreateRequest): ScheduleCreateResponse { + log.info { "[ScheduleService.createSchedule] 일정 생성 시작: storeId=${storeId}, date=${request.date}, time=${request.time}, themeId=${request.themeId}" } + + scheduleValidator.validateCanCreate(storeId, request) + + val schedule = ScheduleEntityFactory.create( + id = tsidFactory.next(), + date = request.date, + time = request.time, + storeId = storeId, + themeId = request.themeId + ).also { + scheduleRepository.save(it) + } + + return ScheduleCreateResponse(schedule.id) + .also { + log.info { "[ScheduleService.createSchedule] 일정 생성 완료: id=${it.id}" } + } + } + @Transactional fun updateSchedule(id: Long, request: ScheduleUpdateRequest) { log.info { "[ScheduleService.updateSchedule] 일정 수정 시작: id=$id, request=${request}" } @@ -113,14 +136,11 @@ class ScheduleService( return } - val schedule: ScheduleEntity = findOrThrow(id) + val schedule: ScheduleEntity = findOrThrow(id).also { + scheduleValidator.validateCanUpdate(it, request) + } - scheduleValidator.validateCanUpdate(schedule, request) - - schedule.modifyIfNotNull( - request.time, - request.status - ).also { + schedule.modifyIfNotNull(request.time, request.status).also { log.info { "[ScheduleService.updateSchedule] 일정 수정 완료: id=$id, request=${request}" } } } @@ -129,15 +149,31 @@ class ScheduleService( fun deleteSchedule(id: Long) { log.info { "[ScheduleService.deleteSchedule] 일정 삭제 시작: id=$id" } - val schedule: ScheduleEntity = findOrThrow(id) - - scheduleValidator.validateCanDelete(schedule) + val schedule: ScheduleEntity = findOrThrow(id).also { + scheduleValidator.validateCanDelete(it) + } scheduleRepository.delete(schedule).also { log.info { "[ScheduleService.deleteSchedule] 일정 삭제 완료: id=$id" } } } + // ======================================== + // Other-Service (API 없이 다른 서비스에서 호출) + // ======================================== + @Transactional(readOnly = true) + fun findSummaryById(id: Long): ScheduleSummaryResponse { + log.info { "[ScheduleService.findDateTimeById] 일정 개요 조회 시작 : id=$id" } + + return findOrThrow(id).toSummaryResponse() + .also { + log.info { "[ScheduleService.findDateTimeById] 일정 개요 조회 완료: id=$id" } + } + } + + // ======================================== + // Common (공통 메서드) + // ======================================== private fun findOrThrow(id: Long): ScheduleEntity { log.info { "[ScheduleService.findOrThrow] 일정 조회 시작: id=$id" }