[#52] 만료 예약 / 일정 스케쥴링 작업 추가 및 동시성 처리를 위한 일부 코드 수정 #53

Merged
pricelees merged 18 commits from refactor/#52 into main 2025-10-04 08:40:37 +00:00
3 changed files with 44 additions and 8 deletions
Showing only changes of commit 599ac071d7 - Show all commits

View File

@ -18,6 +18,8 @@ import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
private val log: KLogger = KotlinLogging.logger {} private val log: KLogger = KotlinLogging.logger {}
@ -43,9 +45,16 @@ class ScheduleService(
@Transactional(readOnly = true) @Transactional(readOnly = true)
fun getStoreScheduleByDate(storeId: Long, date: LocalDate): ScheduleWithThemeListResponse { fun getStoreScheduleByDate(storeId: Long, date: LocalDate): ScheduleWithThemeListResponse {
log.info { "[ScheduleService.getStoreScheduleByDate] 매장 일정 조회: storeId=${storeId}, date=$date" } log.info { "[ScheduleService.getStoreScheduleByDate] 매장 일정 조회: storeId=${storeId}, date=$date" }
val currentDate = LocalDate.now()
if (date.isBefore(currentDate)) {
log.warn { "[ScheduleService.getStoreScheduleByDate] 이전 날짜 선택으로 인한 실패: date=${date}" }
throw ScheduleException(ScheduleErrorCode.PAST_DATE_TIME)
}
val schedules: List<ScheduleOverview> = val schedules: List<ScheduleOverview> =
scheduleRepository.findStoreSchedulesWithThemeByDate(storeId, date) scheduleRepository.findStoreSchedulesWithThemeByDate(storeId, date)
.filter { it.date.isAfter(date) || (it.date.isEqual(date) && it.time.isAfter(LocalTime.now())) }
return schedules.toResponse() return schedules.toResponse()
.also { .also {

View File

@ -17,8 +17,8 @@ interface ScheduleRepository : JpaRepository<ScheduleEntity, Long> {
WHERE WHERE
s.storeId = :storeId s.storeId = :storeId
AND s.date = :date AND s.date = :date
AND s.themeId = :themeId
AND s.time = :time AND s.time = :time
AND s.themeId = :themeId
""" """
) )
fun existsDuplicate(storeId: Long, date: LocalDate, themeId: Long, time: LocalTime): Boolean fun existsDuplicate(storeId: Long, date: LocalDate, themeId: Long, time: LocalTime): Boolean
@ -41,11 +41,13 @@ interface ScheduleRepository : JpaRepository<ScheduleEntity, Long> {
FROM FROM
ScheduleEntity s ScheduleEntity s
JOIN JOIN
ThemeEntity t ON t._id = s.themeId and (:themeId IS NULL OR t._id = :themeId) ThemeEntity t ON t._id = s.themeId
JOIN JOIN
StoreEntity st ON st._id = s.storeId and st._id = :storeId StoreEntity st ON st._id = s.storeId
WHERE WHERE
s.date = :date s.storeId = :storeId
AND s.date = :date
AND (:themeId IS NULL OR t._id = :themeId)
""" """
) )
fun findStoreSchedulesWithThemeByDate( fun findStoreSchedulesWithThemeByDate(

View File

@ -20,11 +20,12 @@ class ScheduleApiTest(
) : FunSpecSpringbootTest() { ) : FunSpecSpringbootTest() {
init { init {
context("특정 매장 + 날짜의 일정 및 테마 정보를 조회한다.") { context("특정 매장 + 날짜의 일정 및 테마 정보를 조회한다.") {
test("정상 응답") { test("날짜가 오늘이면 현재 시간 이후의 정보만 조회된다.") {
val size = 2 val size = 3
val date = LocalDate.now().plusDays(1) val date = LocalDate.now()
val store = dummyInitializer.createStore() val store = dummyInitializer.createStore()
initialize("조회를 위한 같은 날짜의 ${size}개의 일정 생성") {
initialize("조회를 위한 오늘 날짜의 현재 시간 이후인 ${size}개의 일정, 현재 시간 이전인 1개의 일정 생성") {
for (i in 1..size) { for (i in 1..size) {
dummyInitializer.createSchedule( dummyInitializer.createSchedule(
storeId = store.id, storeId = store.id,
@ -34,6 +35,14 @@ class ScheduleApiTest(
) )
) )
} }
dummyInitializer.createSchedule(
storeId = store.id,
request = ScheduleFixture.createRequest.copy(
date = date,
time = LocalTime.now().minusMinutes(1)
)
)
} }
runTest( runTest(
@ -50,6 +59,22 @@ class ScheduleApiTest(
} }
) )
} }
test("날짜가 오늘 이전이면 실패한다.") {
val store = initialize("매장 생성") {
dummyInitializer.createStore()
}
runTest(
on = {
get("/stores/${store.id}/schedules?date=${LocalDate.now().minusDays(1)}")
},
expect = {
statusCode(HttpStatus.BAD_REQUEST.value())
body("code", equalTo(ScheduleErrorCode.PAST_DATE_TIME.errorCode))
}
)
}
} }
context("일정을 ${ScheduleStatus.HOLD} 상태로 변경한다.") { context("일정을 ${ScheduleStatus.HOLD} 상태로 변경한다.") {