-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[YS-130] feat: 기간이 지난 공고 state 변경 스케줄링 구현 (#35)
* feat: implement experiment post lists filtering API with pagination - 필터링, offset-based 페이지네이션 위한 도메인 모델 정의: `CustomFilter` `Pagination` - querydsl 라이브러리 사용하여 동적 쿼리 작성, 다양한 필터 조건 처리 - entity.enum → entity.enums: 예약어 인식 이슈로 패키지 명 변경 - ExperimentPostMapper에서 입출력값 dto ↔ 유즈케이스 input/output으로 전환 * test: add test codes for experiment post filtering API usecase - 실험공고 필터링 API 유즈케이스에 대한 유닛 테스트코드 작성 * fix: add unreflected source files * fix: update '_ALL' option to return all posts on that region * fix: update code review's feedbacks - 코드 리뷰 피드백 반영 - import` 문 활용 → 유즈케이스 내부 변환 로직의 경우 중복 네이밍 이슈로 필요 - 검증 로직 service로 책임 이전 → SRP 원칙 준수 - 필터링 중 `method` → `matchType` 으로 변수명 변경 - 유즈케이스 내부의 `@Schema` 어노테이션 제거 * refact: delegate domain converter to ExperimentMapper * refact: rename usecase's class to suffix * refact: rename API endpoint query parameter → * test: update test codes for refactoring * refact: update misunderstood requirement to meet the busniess logic - '실험 방법' = '대면/비대면/전체'로 필터링하도록 수정 - '전체' 선택 시 쿼리에 미반영되도록 쿼리부분 수정 * fix: add ExperimentMapperTest codes to meet the test coverage * refact: refactor validator codes to upgrade readability * feat: add WebSecurityConfig codes to allow permit without authentication * fix: fix QueryDSL generation issues with entity and enum * test: add test codes to meet the test coverage guage * feat: implement experiment state scheudler using Quartz libraries - 만료된 게시글의 상태를 1일 주기로 정기적 업데이트하기 위해 Quartz 스케줄러 구현 - `ExpiredExperimentPostJob` 에서 `ExperimentPostCustomRepository` 직접 호출→ `recruitDone` 상태 업데이트 * feat: add scheduling jobs to application layer for clean architecture - `ExpiredExperimentPostJob` 에서 `ExperimentPostService` 를 호출하여 로직 처리 - 재사용성, API 확장성과 비즈니스 로직 캡슐화를 위한 코드 리팩터링 - 로깅 추가: 스케줄링 작업 완료 시 recuritDone = true 된 ExperimentPost 수 기록 * test: add test codes for experiment post status scheduler * refact: delete final lines to specify eof line * feat: specify Quartz time zone `Asia/Seoul` - 서버의 JVM 시간대와 Quartz 라이브러리의 시간대를 `Asia/Seoul` 로 명시화하였습니다. --------- Co-authored-by: jisu <[email protected]>
- Loading branch information
Showing
11 changed files
with
182 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
...in/com/dobby/backend/application/usecase/experiment/UpdateExpiredExperimentPostUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
src/main/kotlin/com/dobby/backend/infrastructure/config/SchedulerConfig.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
.inTimeZone(TimeZone.getTimeZone("Asia/Seoul")) | ||
) | ||
.build() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
src/main/kotlin/com/dobby/backend/infrastructure/scheduler/ExpiredExperimentPostJob.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.") | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
...otlin/com/dobby/backend/application/usecase/experiment/UpdateExperimentPostUseCaseTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) } | ||
} | ||
} | ||
} | ||
}) |