From 07263426b20f5d5d0006d64c4d2556c601e37a0a Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 7 Oct 2025 19:32:08 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20schedule=EC=97=90=EC=84=9C=EC=9D=98?= =?UTF-8?q?=20DTO=20=EC=8A=A4=ED=8E=99=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business/ReservationService.kt | 17 +++++-- .../business/ReservationValidator.kt | 8 ++-- .../reservation/web/ReservationDto.kt | 17 ++++--- .../schedule/business/AdminScheduleService.kt | 10 +--- .../schedule/business/ScheduleService.kt | 31 ++++-------- .../schedule/dto/ScheduleFindDTO.kt | 47 +++++++++++-------- .../mapper/AdminScheduleMappingExtensions.kt | 7 ++- .../mapper/ScheduleMappingExtensions.kt | 34 ++++---------- .../roomescape/schedule/ScheduleApiTest.kt | 28 ++++++++--- .../schedule/ScheduleServiceTest.kt | 6 ++- 10 files changed, 107 insertions(+), 98 deletions(-) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt index 48a680f6..298c72d9 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt @@ -9,8 +9,9 @@ import com.sangdol.roomescape.reservation.exception.ReservationException import com.sangdol.roomescape.reservation.infrastructure.persistence.* import com.sangdol.roomescape.reservation.web.* import com.sangdol.roomescape.schedule.business.ScheduleService +import com.sangdol.roomescape.schedule.dto.ScheduleStateResponse +import com.sangdol.roomescape.schedule.dto.ScheduleWithThemeAndStoreResponse import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus -import com.sangdol.roomescape.schedule.web.ScheduleOverviewResponse import com.sangdol.roomescape.theme.business.ThemeService import com.sangdol.roomescape.user.business.UserService import com.sangdol.roomescape.user.dto.UserContactResponse @@ -43,7 +44,7 @@ class ReservationService( log.info { "[createPendingReservation] Pending 예약 생성 시작: schedule=${request.scheduleId}" } run { - val schedule = scheduleService.findSummaryWithLock(request.scheduleId) + val schedule: ScheduleStateResponse = scheduleService.findStateWithLock(request.scheduleId) val theme = themeService.findInfoById(schedule.themeId) reservationValidator.validateCanCreate(schedule, theme, request) @@ -103,8 +104,16 @@ class ReservationService( ) return ReservationOverviewListResponse(reservations.map { - val schedule: ScheduleOverviewResponse = scheduleService.findScheduleOverviewById(it.scheduleId) - it.toOverviewResponse(schedule) + val response: ScheduleWithThemeAndStoreResponse = scheduleService.findWithThemeAndStore(it.scheduleId) + val schedule = response.schedule + + it.toOverviewResponse( + scheduleDate = schedule.date, + scheduleStartFrom = schedule.startFrom, + scheduleEndAt = schedule.endAt, + storeName = response.theme.name, + themeName = response.store.name + ) }).also { log.info { "[findSummaryByMemberId] ${it.reservations.size}개의 예약 조회 완료: userId=${user.id}" } } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationValidator.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationValidator.kt index 2574408f..0cc6abc6 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationValidator.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationValidator.kt @@ -5,8 +5,8 @@ import com.sangdol.common.utils.toKoreaDateTime import com.sangdol.roomescape.reservation.exception.ReservationErrorCode import com.sangdol.roomescape.reservation.exception.ReservationException import com.sangdol.roomescape.reservation.web.PendingReservationCreateRequest +import com.sangdol.roomescape.schedule.dto.ScheduleStateResponse import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus -import com.sangdol.roomescape.schedule.dto.ScheduleSummaryResponse import com.sangdol.roomescape.theme.dto.ThemeInfoResponse import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging @@ -20,14 +20,14 @@ private val log: KLogger = KotlinLogging.logger {} class ReservationValidator { fun validateCanCreate( - schedule: ScheduleSummaryResponse, + schedule: ScheduleStateResponse, theme: ThemeInfoResponse, request: PendingReservationCreateRequest ) { validateSchedule(schedule) validateReservationInfo(theme, request) } - private fun validateSchedule(schedule: ScheduleSummaryResponse) { + private fun validateSchedule(schedule: ScheduleStateResponse) { if (schedule.status != ScheduleStatus.HOLD) { log.info { "[validateCanCreate] ${schedule.status}로의 일정 상태 변경에 따른 실패" } throw ReservationException(ReservationErrorCode.EXPIRED_HELD_SCHEDULE) @@ -40,7 +40,7 @@ class ReservationValidator { throw ReservationException(ReservationErrorCode.EXPIRED_HELD_SCHEDULE) } - val scheduleDateTime = LocalDateTime.of(schedule.date, schedule.time) + val scheduleDateTime = LocalDateTime.of(schedule.date, schedule.startFrom) val nowDateTime = KoreaDateTime.now() if (scheduleDateTime.isBefore(nowDateTime)) { log.info { "[validateCanCreate] 과거 시간인 일정으로 인한 실패: scheduleDateTime=${scheduleDateTime}(KST), now=${nowDateTime}" } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationDto.kt index c932910d..762a56fc 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationDto.kt @@ -3,7 +3,6 @@ package com.sangdol.roomescape.reservation.web import com.sangdol.roomescape.payment.web.PaymentWithDetailResponse import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationEntity import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus -import com.sangdol.roomescape.schedule.web.ScheduleOverviewResponse import com.sangdol.roomescape.user.dto.UserContactResponse import jakarta.validation.constraints.NotEmpty import java.time.Instant @@ -46,14 +45,18 @@ data class ReservationOverviewResponse( ) fun ReservationEntity.toOverviewResponse( - schedule: ScheduleOverviewResponse + scheduleDate: LocalDate, + scheduleStartFrom: LocalTime, + scheduleEndAt: LocalTime, + storeName: String, + themeName: String ) = ReservationOverviewResponse( id = this.id, - storeName = schedule.storeName, - themeName = schedule.themeName, - date = schedule.date, - startFrom = schedule.startFrom, - endAt = schedule.endAt, + storeName = storeName, + themeName = themeName, + date = scheduleDate, + startFrom = scheduleStartFrom, + endAt = scheduleEndAt, status = this.status ) 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 index d5f3d014..cf400d8e 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/AdminScheduleService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/AdminScheduleService.kt @@ -15,7 +15,7 @@ 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 com.sangdol.roomescape.schedule.mapper.toAdminSummaryResponse import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.data.repository.findByIdOrNull @@ -32,9 +32,6 @@ class AdminScheduleService( 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" } @@ -46,7 +43,7 @@ class AdminScheduleService( .filter { (themeId == null) || (it.themeId == themeId) } .sortedBy { it.time } - return schedules.toAdminSummaryListResponse() + return schedules.toAdminSummaryResponse() .also { log.info { "[searchSchedules] ${it.schedules.size} 개의 일정 조회 완료" } } @@ -65,9 +62,6 @@ class AdminScheduleService( .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}" } 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 a661e17d..0c7e1576 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 @@ -1,32 +1,21 @@ package com.sangdol.roomescape.schedule.business -import com.sangdol.common.persistence.IDGenerator import com.sangdol.common.utils.KoreaDate import com.sangdol.common.utils.KoreaTime -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.ScheduleOverviewResponse -import com.sangdol.roomescape.schedule.dto.ScheduleSummaryResponse -import com.sangdol.roomescape.schedule.dto.ScheduleUpdateRequest +import com.sangdol.roomescape.schedule.dto.ScheduleStateResponse +import com.sangdol.roomescape.schedule.dto.ScheduleWithThemeAndStoreResponse import com.sangdol.roomescape.schedule.dto.ScheduleWithThemeListResponse -import com.sangdol.roomescape.schedule.mapper.toAdminSummaryListResponse -import com.sangdol.roomescape.schedule.mapper.toOverviewResponse -import com.sangdol.roomescape.schedule.mapper.toResponse -import com.sangdol.roomescape.schedule.mapper.toSummaryResponse 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.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.schedule.mapper.toResponseWithTheme +import com.sangdol.roomescape.schedule.mapper.toResponseWithThemeAndStore +import com.sangdol.roomescape.schedule.mapper.toStateResponse 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 @@ -55,7 +44,7 @@ class ScheduleService( scheduleRepository.findStoreSchedulesWithThemeByDate(storeId, date) .filter { it.date.isAfter(currentDate) || it.time.isAfter(currentTime) } - return schedules.toResponse() + return schedules.toResponseWithTheme() .also { log.info { "[getStoreScheduleByDate] storeId=${storeId}, date=$date 인 ${it.schedules.size}개 일정 조회 완료" } } @@ -79,7 +68,7 @@ class ScheduleService( } @Transactional(readOnly = true) - fun findSummaryWithLock(id: Long): ScheduleSummaryResponse { + fun findStateWithLock(id: Long): ScheduleStateResponse { log.info { "[findDateTimeById] 일정 개요 조회 시작 : id=$id" } val schedule: ScheduleEntity = scheduleRepository.findByIdForUpdate(id) @@ -88,20 +77,20 @@ class ScheduleService( throw ScheduleException(ScheduleErrorCode.SCHEDULE_NOT_FOUND) } - return schedule.toSummaryResponse() + return schedule.toStateResponse() .also { log.info { "[findDateTimeById] 일정 개요 조회 완료: id=$id" } } } @Transactional(readOnly = true) - fun findScheduleOverviewById(id: Long): ScheduleOverviewResponse { + fun findWithThemeAndStore(id: Long): ScheduleWithThemeAndStoreResponse { val overview: ScheduleOverview = scheduleRepository.findOverviewByIdOrNull(id) ?: run { log.warn { "[findScheduleOverview] 일정 개요 조회 실패: id=$id" } throw ScheduleException(ScheduleErrorCode.SCHEDULE_NOT_FOUND) } - return overview.toOverviewResponse() + return overview.toResponseWithThemeAndStore() } @Transactional diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/dto/ScheduleFindDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/dto/ScheduleFindDTO.kt index 652a90c3..aa838731 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/dto/ScheduleFindDTO.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/dto/ScheduleFindDTO.kt @@ -1,40 +1,47 @@ package com.sangdol.roomescape.schedule.dto import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus -import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty import java.time.Instant import java.time.LocalDate import java.time.LocalTime -data class ScheduleWithThemeResponse( +data class ScheduleResponse( val id: Long, + val date: LocalDate, val startFrom: LocalTime, val endAt: LocalTime, - val themeId: Long, - val themeName: String, - val themeDifficulty: Difficulty, - val status: ScheduleStatus + val status: ScheduleStatus, ) -data class ScheduleWithThemeListResponse( - val schedules: List -) - -data class ScheduleSummaryResponse( +data class ScheduleStateResponse( val date: LocalDate, - val time: LocalTime, + val startFrom: LocalTime, val themeId: Long, val status: ScheduleStatus, val holdExpiredAt: Instant? = null ) -data class ScheduleOverviewResponse( +data class ScheduleThemeInfo( val id: Long, - val storeId: Long, - val storeName: String, - val date: LocalDate, - val startFrom: LocalTime, - val endAt: LocalTime, - val themeId: Long, - val themeName: String, + val name: String +) + +data class ScheduleStoreInfo( + val id: Long, + val name: String +) + +data class ScheduleWithThemeResponse( + val schedule: ScheduleResponse, + val theme: ScheduleThemeInfo, +) + +data class ScheduleWithThemeAndStoreResponse( + val schedule: ScheduleResponse, + val theme: ScheduleThemeInfo, + val store: ScheduleStoreInfo +) + +data class ScheduleWithThemeListResponse( + val schedules: List ) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/mapper/AdminScheduleMappingExtensions.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/mapper/AdminScheduleMappingExtensions.kt index 4e0b6e50..d864cf6f 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/mapper/AdminScheduleMappingExtensions.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/mapper/AdminScheduleMappingExtensions.kt @@ -1,6 +1,7 @@ package com.sangdol.roomescape.schedule.mapper import com.sangdol.roomescape.schedule.business.domain.ScheduleOverview +import com.sangdol.roomescape.schedule.dto.AdminScheduleSummaryListResponse import com.sangdol.roomescape.schedule.dto.AdminScheduleSummaryResponse fun ScheduleOverview.toAdminSummaryResponse() = AdminScheduleSummaryResponse( @@ -9,4 +10,8 @@ fun ScheduleOverview.toAdminSummaryResponse() = AdminScheduleSummaryResponse( startFrom = this.time, endAt = this.getEndAt(), status = this.status -) \ No newline at end of file +) + +fun List.toAdminSummaryResponse() = AdminScheduleSummaryListResponse( + this.map { it.toAdminSummaryResponse() } +) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/mapper/ScheduleMappingExtensions.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/mapper/ScheduleMappingExtensions.kt index 2482631c..38b349e5 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/mapper/ScheduleMappingExtensions.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/mapper/ScheduleMappingExtensions.kt @@ -4,39 +4,25 @@ import com.sangdol.roomescape.schedule.business.domain.ScheduleOverview import com.sangdol.roomescape.schedule.dto.* import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity -fun ScheduleOverview.toOverviewResponse() = ScheduleOverviewResponse( - id = this.id, - storeId = this.storeId, - storeName = this.storeName, +fun ScheduleEntity.toStateResponse() = ScheduleStateResponse( date = this.date, startFrom = this.time, - endAt = this.getEndAt(), - themeId = this.themeId, - themeName = this.themeName, -) - -fun ScheduleEntity.toSummaryResponse() = ScheduleSummaryResponse( - date = this.date, - time = this.time, themeId = this.themeId, status = this.status, holdExpiredAt = this.holdExpiredAt ) -fun ScheduleOverview.toResponse() = ScheduleWithThemeResponse( - id = this.id, - startFrom = this.time, - endAt = this.getEndAt(), - themeId = this.themeId, - themeName = this.themeName, - themeDifficulty = this.themeDifficulty, - status = this.status +fun ScheduleOverview.toResponseWithThemeAndStore() = ScheduleWithThemeAndStoreResponse( + schedule = ScheduleResponse(this.id, this.date, this.time, this.getEndAt(), this.status), + theme = ScheduleThemeInfo(this.themeId, this.themeName), + store = ScheduleStoreInfo(this.storeId, this.storeName), ) -fun List.toAdminSummaryListResponse() = AdminScheduleSummaryListResponse( - this.map { it.toAdminSummaryResponse() } +fun ScheduleOverview.toResponseWithTheme() = ScheduleWithThemeResponse( + schedule = ScheduleResponse(this.id, this.date, this.time, this.getEndAt(), this.status), + theme = ScheduleThemeInfo(this.themeId, this.themeName), ) -fun List.toResponse() = ScheduleWithThemeListResponse( - this.map { it.toResponse() } +fun List.toResponseWithTheme() = ScheduleWithThemeListResponse( + this.map { it.toResponseWithTheme() } ) diff --git a/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleApiTest.kt index 94f8d4e8..b169a0a2 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleApiTest.kt @@ -58,16 +58,30 @@ class ScheduleApiTest( body("data.schedules.size()", equalTo(size)) assertProperties( props = setOf( - "id", - "startFrom", - "endAt", - "themeId", - "themeName", - "themeDifficulty", - "status" + "schedule", + "theme" ), propsNameIfList = "schedules" ) + + assertProperties( + props = setOf( + "id", + "date", + "startFrom", + "endAt", + "status" + ), + propsNameIfList = "schedules.schedule" + ) + + assertProperties( + props = setOf( + "id", + "name", + ), + propsNameIfList = "schedules.theme" + ) } ) } diff --git a/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleServiceTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleServiceTest.kt index ae2da5b7..7a4a1db1 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleServiceTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleServiceTest.kt @@ -1,6 +1,7 @@ package com.sangdol.roomescape.schedule import com.sangdol.common.utils.MdcPrincipalIdUtil +import com.sangdol.roomescape.schedule.business.AdminScheduleService import com.sangdol.roomescape.schedule.business.ScheduleService import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus @@ -19,6 +20,7 @@ import java.time.LocalDate import java.time.LocalTime class ScheduleServiceTest( + private val adminScheduleService: AdminScheduleService, private val scheduleService: ScheduleService, private val scheduleRepository: ScheduleRepository ) : FunSpecSpringbootTest() { @@ -37,7 +39,7 @@ class ScheduleServiceTest( } } - scheduleService.updateSchedule( + adminScheduleService.updateSchedule( createdScheduleId, ScheduleUpdateRequest(status = ScheduleStatus.RESERVED) ) @@ -107,7 +109,7 @@ class ScheduleServiceTest( store.id to theme.id } - return scheduleService.createSchedule( + return adminScheduleService.createSchedule( storeId = storeId, request = ScheduleCreateRequest( date = LocalDate.now().plusDays(1),