From 7b0ebcc6dc7f9f3c0dc966b1a97bd7ef11ebca76 Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 7 Oct 2025 18:33:44 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20ScheduleService=20/=20AdminSchedule?= =?UTF-8?q?Service=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../schedule/business/AdminScheduleService.kt | 134 ++++++++++++++++++ .../schedule/business/ScheduleService.kt | 125 +--------------- .../schedule/web/AdminScheduleController.kt | 13 +- 3 files changed, 142 insertions(+), 130 deletions(-) create mode 100644 service/src/main/kotlin/com/sangdol/roomescape/schedule/business/AdminScheduleService.kt diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/AdminScheduleService.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/AdminScheduleService.kt new file mode 100644 index 00000000..d5f3d014 --- /dev/null +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/AdminScheduleService.kt @@ -0,0 +1,134 @@ +package com.sangdol.roomescape.schedule.business + +import com.sangdol.common.persistence.IDGenerator +import com.sangdol.common.utils.KoreaDate +import com.sangdol.roomescape.admin.business.AdminService +import com.sangdol.roomescape.common.types.AuditingInfo +import com.sangdol.roomescape.common.types.Auditor +import com.sangdol.roomescape.schedule.business.domain.ScheduleOverview +import com.sangdol.roomescape.schedule.dto.AdminScheduleSummaryListResponse +import com.sangdol.roomescape.schedule.dto.ScheduleCreateRequest +import com.sangdol.roomescape.schedule.dto.ScheduleCreateResponse +import com.sangdol.roomescape.schedule.dto.ScheduleUpdateRequest +import com.sangdol.roomescape.schedule.exception.ScheduleErrorCode +import com.sangdol.roomescape.schedule.exception.ScheduleException +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntityFactory +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository +import com.sangdol.roomescape.schedule.mapper.toAdminSummaryListResponse +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.time.LocalDate + +private val log: KLogger = KotlinLogging.logger {} + +@Service +class AdminScheduleService( + private val scheduleRepository: ScheduleRepository, + private val scheduleValidator: ScheduleValidator, + private val idGenerator: IDGenerator, + private val adminService: AdminService +) { + // ======================================== + // All-Admin (본사, 매장 모두 사용가능) + // ======================================== + @Transactional(readOnly = true) + fun searchSchedules(storeId: Long, date: LocalDate?, themeId: Long?): AdminScheduleSummaryListResponse { + log.info { "[searchSchedules] 일정 검색 시작: storeId=$storeId, date=$date, themeId=$themeId" } + + val searchDate = date ?: KoreaDate.today() + + val schedules: List = + scheduleRepository.findStoreSchedulesWithThemeByDate(storeId, searchDate) + .filter { (themeId == null) || (it.themeId == themeId) } + .sortedBy { it.time } + + return schedules.toAdminSummaryListResponse() + .also { + log.info { "[searchSchedules] ${it.schedules.size} 개의 일정 조회 완료" } + } + } + + @Transactional(readOnly = true) + fun findScheduleAudit(id: Long): AuditingInfo { + log.info { "[findDetail] 일정 감사 정보 조회 시작: id=$id" } + + val schedule: ScheduleEntity = findOrThrow(id) + + val createdBy: Auditor = adminService.findOperatorOrUnknown(schedule.createdBy) + val updatedBy: Auditor = adminService.findOperatorOrUnknown(schedule.updatedBy) + + return AuditingInfo(schedule.createdAt, createdBy, schedule.updatedAt, updatedBy) + .also { log.info { "[findDetail] 일정 감사 정보 조회 완료: id=$id" } } + } + + // ======================================== + // Store-Admin (매장 관리자 로그인 필요) + // ======================================== + @Transactional + fun createSchedule(storeId: Long, request: ScheduleCreateRequest): ScheduleCreateResponse { + log.info { "[createSchedule] 일정 생성 시작: storeId=${storeId}, date=${request.date}, time=${request.time}, themeId=${request.themeId}" } + + scheduleValidator.validateCanCreate(storeId, request) + + val schedule = ScheduleEntityFactory.create( + id = idGenerator.create(), + date = request.date, + time = request.time, + storeId = storeId, + themeId = request.themeId + ).also { + scheduleRepository.save(it) + } + + return ScheduleCreateResponse(schedule.id) + .also { + log.info { "[createSchedule] 일정 생성 완료: id=${it.id}" } + } + } + + @Transactional + fun updateSchedule(id: Long, request: ScheduleUpdateRequest) { + log.info { "[updateSchedule] 일정 수정 시작: id=$id, request=${request}" } + + if (request.isAllParamsNull()) { + log.info { "[updateSchedule] 일정 변경 사항 없음: id=$id" } + return + } + + val schedule: ScheduleEntity = findOrThrow(id).also { + scheduleValidator.validateCanUpdate(it, request) + } + + schedule.modifyIfNotNull(request.time, request.status).also { + log.info { "[updateSchedule] 일정 수정 완료: id=$id, request=${request}" } + } + } + + @Transactional + fun deleteSchedule(id: Long) { + log.info { "[deleteSchedule] 일정 삭제 시작: id=$id" } + + val schedule: ScheduleEntity = findOrThrow(id).also { + scheduleValidator.validateCanDelete(it) + } + + scheduleRepository.delete(schedule).also { + log.info { "[deleteSchedule] 일정 삭제 완료: id=$id" } + } + } + + private fun findOrThrow(id: Long): ScheduleEntity { + log.info { "[findOrThrow] 일정 조회 시작: id=$id" } + + return scheduleRepository.findByIdOrNull(id) + ?.also { log.info { "[findOrThrow] 일정 조회 완료: id=$id" } } + ?: run { + log.warn { "[findOrThrow] 일정 조회 실패. id=$id" } + throw ScheduleException(ScheduleErrorCode.SCHEDULE_NOT_FOUND) + } + } +} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt index 9acf630c..a661e17d 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt @@ -34,25 +34,11 @@ import java.time.LocalTime private val log: KLogger = KotlinLogging.logger {} -/** - * Structure: - * - Public: 모두가 접근 가능 - * - User: 회원(로그인된 사용자)가 사용 가능 - * - All-Admin: 모든 관리자가 사용 가능 - * - Store-Admin: 매장 관리자만 사용 가능 - * - Other-Service: 다른 서비스에서 호출하는 메서드 - * - Common: 공통 메서드 - */ @Service class ScheduleService( private val scheduleRepository: ScheduleRepository, - private val scheduleValidator: ScheduleValidator, - private val idGenerator: IDGenerator, - private val adminService: AdminService + private val scheduleValidator: ScheduleValidator ) { - // ======================================== - // Public (인증 불필요) - // ======================================== @Transactional(readOnly = true) fun getStoreScheduleByDate(storeId: Long, date: LocalDate): ScheduleWithThemeListResponse { log.info { "[getStoreScheduleByDate] 매장 일정 조회: storeId=${storeId}, date=$date" } @@ -75,9 +61,6 @@ class ScheduleService( } } - // ======================================== - // User (회원 로그인 필요) - // ======================================== @Transactional fun holdSchedule(id: Long) { log.info { "[holdSchedule] 일정 Holding 시작: id=$id" } @@ -95,98 +78,6 @@ class ScheduleService( } } - // ======================================== - // All-Admin (본사, 매장 모두 사용가능) - // ======================================== - @Transactional(readOnly = true) - fun searchSchedules(storeId: Long, date: LocalDate?, themeId: Long?): AdminScheduleSummaryListResponse { - log.info { "[searchSchedules] 일정 검색 시작: storeId=$storeId, date=$date, themeId=$themeId" } - - val searchDate = date ?: KoreaDate.today() - - val schedules: List = - scheduleRepository.findStoreSchedulesWithThemeByDate(storeId, searchDate) - .filter { (themeId == null) || (it.themeId == themeId) } - .sortedBy { it.time } - - return schedules.toAdminSummaryListResponse() - .also { - log.info { "[searchSchedules] ${it.schedules.size} 개의 일정 조회 완료" } - } - } - - @Transactional(readOnly = true) - fun findScheduleAudit(id: Long): AuditingInfo { - log.info { "[findDetail] 일정 감사 정보 조회 시작: id=$id" } - - val schedule: ScheduleEntity = findOrThrow(id) - - val createdBy: Auditor = adminService.findOperatorOrUnknown(schedule.createdBy) - val updatedBy: Auditor = adminService.findOperatorOrUnknown(schedule.updatedBy) - - return AuditingInfo(schedule.createdAt, createdBy, schedule.updatedAt, updatedBy) - .also { log.info { "[findDetail] 일정 감사 정보 조회 완료: id=$id" } } - } - - // ======================================== - // Store-Admin (매장 관리자 로그인 필요) - // ======================================== - @Transactional - fun createSchedule(storeId: Long, request: ScheduleCreateRequest): ScheduleCreateResponse { - log.info { "[createSchedule] 일정 생성 시작: storeId=${storeId}, date=${request.date}, time=${request.time}, themeId=${request.themeId}" } - - scheduleValidator.validateCanCreate(storeId, request) - - val schedule = ScheduleEntityFactory.create( - id = idGenerator.create(), - date = request.date, - time = request.time, - storeId = storeId, - themeId = request.themeId - ).also { - scheduleRepository.save(it) - } - - return ScheduleCreateResponse(schedule.id) - .also { - log.info { "[createSchedule] 일정 생성 완료: id=${it.id}" } - } - } - - @Transactional - fun updateSchedule(id: Long, request: ScheduleUpdateRequest) { - log.info { "[updateSchedule] 일정 수정 시작: id=$id, request=${request}" } - - if (request.isAllParamsNull()) { - log.info { "[updateSchedule] 일정 변경 사항 없음: id=$id" } - return - } - - val schedule: ScheduleEntity = findOrThrow(id).also { - scheduleValidator.validateCanUpdate(it, request) - } - - schedule.modifyIfNotNull(request.time, request.status).also { - log.info { "[updateSchedule] 일정 수정 완료: id=$id, request=${request}" } - } - } - - @Transactional - fun deleteSchedule(id: Long) { - log.info { "[deleteSchedule] 일정 삭제 시작: id=$id" } - - val schedule: ScheduleEntity = findOrThrow(id).also { - scheduleValidator.validateCanDelete(it) - } - - scheduleRepository.delete(schedule).also { - log.info { "[deleteSchedule] 일정 삭제 완료: id=$id" } - } - } - - // ======================================== - // Other-Service (API 없이 다른 서비스에서 호출) - // ======================================== @Transactional(readOnly = true) fun findSummaryWithLock(id: Long): ScheduleSummaryResponse { log.info { "[findDateTimeById] 일정 개요 조회 시작 : id=$id" } @@ -222,20 +113,6 @@ class ScheduleService( } } - // ======================================== - // Common (공통 메서드) - // ======================================== - private fun findOrThrow(id: Long): ScheduleEntity { - log.info { "[findOrThrow] 일정 조회 시작: id=$id" } - - return scheduleRepository.findByIdOrNull(id) - ?.also { log.info { "[findOrThrow] 일정 조회 완료: id=$id" } } - ?: run { - log.warn { "[findOrThrow] 일정 조회 실패. id=$id" } - throw ScheduleException(ScheduleErrorCode.SCHEDULE_NOT_FOUND) - } - } - private fun findForUpdateOrThrow(id: Long): ScheduleEntity { log.info { "[findForUpdateOrThrow] 일정 LOCK + 조회 시작: id=$id" } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt index 444bea51..d52556ba 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt @@ -2,6 +2,7 @@ package com.sangdol.roomescape.schedule.web import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.common.types.AuditingInfo +import com.sangdol.roomescape.schedule.business.AdminScheduleService import com.sangdol.roomescape.schedule.business.ScheduleService import com.sangdol.roomescape.schedule.docs.AdminScheduleAPI import com.sangdol.roomescape.schedule.dto.AdminScheduleSummaryListResponse @@ -17,7 +18,7 @@ import java.time.LocalDate @RestController @RequestMapping("/admin") class AdminScheduleController( - private val scheduleService: ScheduleService, + private val adminScheduleService: AdminScheduleService, ) : AdminScheduleAPI { @GetMapping("/stores/{storeId}/schedules") override fun searchSchedules( @@ -25,7 +26,7 @@ class AdminScheduleController( @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") date: LocalDate?, @RequestParam(required = false) themeId: Long?, ): ResponseEntity> { - val response = scheduleService.searchSchedules(storeId, date, themeId) + val response = adminScheduleService.searchSchedules(storeId, date, themeId) return ResponseEntity.ok(CommonApiResponse(response)) } @@ -34,7 +35,7 @@ class AdminScheduleController( override fun findScheduleAudit( @PathVariable("id") id: Long ): ResponseEntity> { - val response = scheduleService.findScheduleAudit(id) + val response = adminScheduleService.findScheduleAudit(id) return ResponseEntity.ok(CommonApiResponse(response)) } @@ -44,7 +45,7 @@ class AdminScheduleController( @PathVariable("storeId") storeId: Long, @Valid @RequestBody request: ScheduleCreateRequest ): ResponseEntity> { - val response = scheduleService.createSchedule(storeId, request) + val response = adminScheduleService.createSchedule(storeId, request) return ResponseEntity.ok(CommonApiResponse(response)) } @@ -54,7 +55,7 @@ class AdminScheduleController( @PathVariable("id") id: Long, @Valid @RequestBody request: ScheduleUpdateRequest ): ResponseEntity> { - scheduleService.updateSchedule(id, request) + adminScheduleService.updateSchedule(id, request) return ResponseEntity.ok(CommonApiResponse(Unit)) } @@ -63,7 +64,7 @@ class AdminScheduleController( override fun deleteSchedule( @PathVariable("id") id: Long ): ResponseEntity> { - scheduleService.deleteSchedule(id) + adminScheduleService.deleteSchedule(id) return ResponseEntity.noContent().build() }