generated from pricelees/issue-pr-template
[#56] 예약 & 결제 프로세스 및 패키지 구조 재정의 #57
@ -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}" }
|
||||
}
|
||||
|
||||
@ -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}" }
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
|
||||
@ -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}" }
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<ScheduleWithThemeResponse>
|
||||
)
|
||||
|
||||
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<ScheduleWithThemeResponse>
|
||||
)
|
||||
|
||||
@ -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(
|
||||
@ -10,3 +11,7 @@ fun ScheduleOverview.toAdminSummaryResponse() = AdminScheduleSummaryResponse(
|
||||
endAt = this.getEndAt(),
|
||||
status = this.status
|
||||
)
|
||||
|
||||
fun List<ScheduleOverview>.toAdminSummaryResponse() = AdminScheduleSummaryListResponse(
|
||||
this.map { it.toAdminSummaryResponse() }
|
||||
)
|
||||
|
||||
@ -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<ScheduleOverview>.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<ScheduleOverview>.toResponse() = ScheduleWithThemeListResponse(
|
||||
this.map { it.toResponse() }
|
||||
fun List<ScheduleOverview>.toResponseWithTheme() = ScheduleWithThemeListResponse(
|
||||
this.map { it.toResponseWithTheme() }
|
||||
)
|
||||
|
||||
@ -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"
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user