-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[YS-130] feat: 기간이 지난 공고 state 변경 스케줄링 구현 #35
Changes from all commits
71bed6e
d6f6c66
47e41da
5e2d49f
f27bbdc
e330a38
fc437ad
12ee7f1
d8fdc9c
b243be7
b4500f5
1894eed
0942256
fb1e6e3
5559259
71b7638
8980490
50a764b
1c61795
e07f600
88ebd65
419b985
7d01e56
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.dobby.backend.application.usecase.experiment | ||
|
||
import com.dobby.backend.application.usecase.UseCase | ||
import com.dobby.backend.domain.gateway.experiment.ExperimentPostGateway | ||
import java.time.LocalDate | ||
|
||
class UpdateExpiredExperimentPostUseCase( | ||
private val experimentPostGateway: ExperimentPostGateway | ||
) : UseCase<UpdateExpiredExperimentPostUseCase.Input, UpdateExpiredExperimentPostUseCase.Output> { | ||
data class Input( | ||
val currentDate : LocalDate | ||
) | ||
data class Output( | ||
val affectedRowsCount: Long | ||
) | ||
|
||
override fun execute(input: Input): Output { | ||
return Output( | ||
experimentPostGateway.updateExperimentPostStatus(input.currentDate) | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,30 @@ | ||||||||||||||||
package com.dobby.backend.infrastructure.config | ||||||||||||||||
|
||||||||||||||||
import com.dobby.backend.infrastructure.scheduler.ExpiredExperimentPostJob | ||||||||||||||||
import org.quartz.* | ||||||||||||||||
import org.springframework.context.annotation.Bean | ||||||||||||||||
import org.springframework.context.annotation.Configuration | ||||||||||||||||
import java.util.* | ||||||||||||||||
|
||||||||||||||||
@Configuration | ||||||||||||||||
class SchedulerConfig { | ||||||||||||||||
@Bean | ||||||||||||||||
fun expiredExperimentPostJobDetail(): JobDetail { | ||||||||||||||||
return JobBuilder.newJob(ExpiredExperimentPostJob::class.java) | ||||||||||||||||
.withIdentity("expired_experiment_post_job") | ||||||||||||||||
.storeDurably() | ||||||||||||||||
.build() | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
@Bean | ||||||||||||||||
fun expiredExperimentPostTrigger(): Trigger { | ||||||||||||||||
return TriggerBuilder.newTrigger() | ||||||||||||||||
.forJob(expiredExperimentPostJobDetail()) | ||||||||||||||||
.withIdentity("expired_experiment_post_trigger") | ||||||||||||||||
.withSchedule( | ||||||||||||||||
CronScheduleBuilder.dailyAtHourAndMinute(0, 0) | ||||||||||||||||
Comment on lines
+23
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Quartz 스케줄러는 기본적으로 시스템의 기본 시간대를 따르는데 설정하지 않아도 한국 시간대에 동작할 수 있지만, 안정성과 명시성을 위해 명확히 타임존을 설정하는 것은 어떨까요??
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 해당 부분을 저희 '대규모 설계 시스템 기초 스터디'에서 7주차(어제자 발표)로 공부한 TimeZone 이슈에서 명시할 필요성이 있다는 것을 느꼈습니다! |
||||||||||||||||
.inTimeZone(TimeZone.getTimeZone("Asia/Seoul")) | ||||||||||||||||
) | ||||||||||||||||
.build() | ||||||||||||||||
} | ||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.dobby.backend.infrastructure.scheduler | ||
|
||
import com.dobby.backend.application.service.ExperimentPostService | ||
import com.dobby.backend.application.usecase.experiment.UpdateExpiredExperimentPostUseCase | ||
import org.quartz.Job | ||
import org.quartz.JobExecutionContext | ||
import org.slf4j.Logger | ||
import org.slf4j.LoggerFactory | ||
import org.springframework.stereotype.Component | ||
import java.time.LocalDate | ||
|
||
@Component | ||
class ExpiredExperimentPostJob( | ||
private val experimentPostService: ExperimentPostService | ||
) : Job{ | ||
companion object { | ||
private val logger : Logger = LoggerFactory.getLogger(ExpiredExperimentPostJob::class.java) | ||
} | ||
|
||
override fun execute(context: JobExecutionContext) { | ||
val input = UpdateExpiredExperimentPostUseCase.Input( | ||
LocalDate.now() | ||
) | ||
val output = experimentPostService.updateExpiredExperimentPosts(input) | ||
logger.info("${output.affectedRowsCount} expired posts have been updated during scheduling jobs.") | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import com.dobby.backend.application.usecase.experiment.UpdateExpiredExperimentPostUseCase | ||
import com.dobby.backend.domain.gateway.experiment.ExperimentPostGateway | ||
import io.kotest.assertions.throwables.shouldThrow | ||
import io.kotest.core.spec.style.BehaviorSpec | ||
import io.kotest.matchers.shouldBe | ||
import io.mockk.clearMocks | ||
import io.mockk.every | ||
import io.mockk.mockk | ||
import io.mockk.verify | ||
import java.time.LocalDate | ||
|
||
class UpdateExperimentPostUseCaseTest : BehaviorSpec({ | ||
|
||
val experimentPostGateway = mockk<ExperimentPostGateway>() | ||
val updateExpiredExperimentPostUseCase = UpdateExpiredExperimentPostUseCase(experimentPostGateway) | ||
|
||
given("게시글이 만료되어 상태가 업데이트되는 경우") { | ||
val currentDate = LocalDate.of(2025, 1, 16) | ||
val input = UpdateExpiredExperimentPostUseCase.Input(currentDate) | ||
every { experimentPostGateway.updateExperimentPostStatus(any()) } returns 5L | ||
|
||
`when`("Execute를 호출하면") { | ||
then("UseCase는 업데이트된 게시글 수를 반환해야 한다") { | ||
val output = updateExpiredExperimentPostUseCase.execute(input) | ||
output.affectedRowsCount shouldBe 5L | ||
verify(exactly = 1) { experimentPostGateway.updateExperimentPostStatus(currentDate) } | ||
} | ||
} | ||
} | ||
|
||
given("업데이트할 게시글이 없는 경우") { | ||
val currentDate = LocalDate.of(2025, 1, 17) | ||
val input = UpdateExpiredExperimentPostUseCase.Input(currentDate) | ||
|
||
// Mock 설정을 각 테스트에서 바로 적용하지 않고 초기화 시 설정 | ||
every { experimentPostGateway.updateExperimentPostStatus(currentDate) } returns 0L | ||
|
||
`when`("Execute를 호출하면") { | ||
then("UseCase는 0을 반환해야 한다") { | ||
val output = updateExpiredExperimentPostUseCase.execute(input) | ||
output.affectedRowsCount shouldBe 0L | ||
verify(exactly = 1) { experimentPostGateway.updateExperimentPostStatus(currentDate) } | ||
} | ||
} | ||
} | ||
|
||
given("Gateway에서 예외를 던지면") { | ||
val currentDate = LocalDate.of(2025, 1, 18) | ||
val input = UpdateExpiredExperimentPostUseCase.Input(currentDate) | ||
val exceptionMessage = "Database error" | ||
|
||
// 예외를 던지는 Mock 설정 | ||
every { experimentPostGateway.updateExperimentPostStatus(currentDate) } throws RuntimeException(exceptionMessage) | ||
|
||
`when`("Execute를 호출하면") { | ||
then("UseCase는 예외를 상위로 전달해야 한다") { | ||
val exception = shouldThrow<RuntimeException> { | ||
updateExpiredExperimentPostUseCase.execute(input) | ||
} | ||
|
||
exception.message shouldBe exceptionMessage | ||
verify(exactly = 1) { experimentPostGateway.updateExperimentPostStatus(currentDate) } | ||
} | ||
} | ||
} | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도
기간이 지난 공고 state를 변경하는 작업
은 가볍다고 생각해서 quartz 구현 방식이 좋은 거 같아요 👍