From 10020b03d6af6252990dc0de8fee97e487c48c11 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 10 Jul 2026 14:44:29 +0900 Subject: [PATCH] =?UTF-8?q?feat(home):=20=ED=81=AC=EB=A6=AC=EC=97=90?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EB=9E=AD=ED=82=B9=20fallback=20job=20?= =?UTF-8?q?=EC=A7=91=EA=B3=84=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreatorRankingSnapshotJobRepository.kt | 9 +++++ ...aultCreatorRankingSnapshotJobRepository.kt | 16 ++++++++ .../port/out/CreatorRankingSnapshotJobPort.kt | 10 ++++- ...CreatorRankingSnapshotJobRepositoryTest.kt | 39 +++++++++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/adapter/out/persistence/CreatorRankingSnapshotJobRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/adapter/out/persistence/CreatorRankingSnapshotJobRepository.kt index 302fbc6a..5d797f0e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/adapter/out/persistence/CreatorRankingSnapshotJobRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/adapter/out/persistence/CreatorRankingSnapshotJobRepository.kt @@ -1,6 +1,8 @@ package kr.co.vividnext.sodalive.v2.ranking.adapter.out.persistence +import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingType import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotJobStatus +import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotJobTrigger import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Lock import org.springframework.data.jpa.repository.Query @@ -18,4 +20,11 @@ interface CreatorRankingSnapshotJobRepository : JpaRepository ): List + + fun countByRankingTypeAndAggregationStartAtUtcAndAggregationEndAtUtcAndTrigger( + rankingType: CreatorRankingType, + aggregationStartAtUtc: LocalDateTime, + aggregationEndAtUtc: LocalDateTime, + trigger: CreatorRankingSnapshotJobTrigger + ): Long } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/adapter/out/persistence/DefaultCreatorRankingSnapshotJobRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/adapter/out/persistence/DefaultCreatorRankingSnapshotJobRepository.kt index 9560a794..83df97b3 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/adapter/out/persistence/DefaultCreatorRankingSnapshotJobRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/adapter/out/persistence/DefaultCreatorRankingSnapshotJobRepository.kt @@ -1,8 +1,10 @@ package kr.co.vividnext.sodalive.v2.ranking.adapter.out.persistence +import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingType import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotJobPort import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotJobRecord import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotJobStatus +import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotJobTrigger import org.springframework.stereotype.Repository import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime @@ -32,6 +34,20 @@ class DefaultCreatorRankingSnapshotJobRepository( ).map { it.toRecord() } } + override fun countByRankingTypeAndPeriodAndTrigger( + rankingType: CreatorRankingType, + aggregationStartAtUtc: LocalDateTime, + aggregationEndAtUtc: LocalDateTime, + trigger: CreatorRankingSnapshotJobTrigger + ): Long { + return repository.countByRankingTypeAndAggregationStartAtUtcAndAggregationEndAtUtcAndTrigger( + rankingType = rankingType, + aggregationStartAtUtc = aggregationStartAtUtc, + aggregationEndAtUtc = aggregationEndAtUtc, + trigger = trigger + ) + } + @Transactional override fun markProcessing(jobId: Long, processingStartedAt: LocalDateTime): CreatorRankingSnapshotJobRecord? { val job = repository.findByIdForUpdate(jobId) ?: return null diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/port/out/CreatorRankingSnapshotJobPort.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/port/out/CreatorRankingSnapshotJobPort.kt index adb6ef8b..e7622c35 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/port/out/CreatorRankingSnapshotJobPort.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/port/out/CreatorRankingSnapshotJobPort.kt @@ -14,6 +14,13 @@ interface CreatorRankingSnapshotJobPort { statuses: List ): List + fun countByRankingTypeAndPeriodAndTrigger( + rankingType: CreatorRankingType, + aggregationStartAtUtc: LocalDateTime, + aggregationEndAtUtc: LocalDateTime, + trigger: CreatorRankingSnapshotJobTrigger + ): Long + fun markProcessing(jobId: Long, processingStartedAt: LocalDateTime): CreatorRankingSnapshotJobRecord? fun markDone(jobId: Long, processedAt: LocalDateTime): CreatorRankingSnapshotJobRecord? @@ -32,7 +39,8 @@ enum class CreatorRankingSnapshotJobStatus { enum class CreatorRankingSnapshotJobTrigger { SCHEDULED, - MANUAL + MANUAL, + FALLBACK } data class CreatorRankingSnapshotJobRecord( diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/ranking/adapter/out/persistence/DefaultCreatorRankingSnapshotJobRepositoryTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/ranking/adapter/out/persistence/DefaultCreatorRankingSnapshotJobRepositoryTest.kt index ac382278..8cadf831 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/v2/ranking/adapter/out/persistence/DefaultCreatorRankingSnapshotJobRepositoryTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/ranking/adapter/out/persistence/DefaultCreatorRankingSnapshotJobRepositoryTest.kt @@ -138,4 +138,43 @@ class DefaultCreatorRankingSnapshotJobRepositoryTest @Autowired constructor( assertEquals(LocalDateTime.of(2026, 6, 8, 7, 30), unchanged?.processingStartedAt) assertEquals(LocalDateTime.of(2026, 6, 8, 7, 31), unchanged?.processedAt) } + + @Test + @DisplayName("fallback 제한 count는 같은 기간의 FALLBACK trigger만 계산한다") + fun shouldCountFallbackJobsByPeriodAndTriggerOnly() { + val startAt = LocalDateTime.of(2026, 5, 31, 15, 0) + val endAt = LocalDateTime.of(2026, 6, 7, 15, 0) + adapter.save(job(startAt, endAt, CreatorRankingSnapshotJobTrigger.FALLBACK)) + adapter.save(job(startAt, endAt, CreatorRankingSnapshotJobTrigger.FALLBACK)) + adapter.save(job(startAt, endAt, CreatorRankingSnapshotJobTrigger.SCHEDULED)) + adapter.save(job(startAt, endAt, CreatorRankingSnapshotJobTrigger.MANUAL)) + adapter.save(job(startAt.minusWeeks(1), endAt.minusWeeks(1), CreatorRankingSnapshotJobTrigger.FALLBACK)) + + val count = adapter.countByRankingTypeAndPeriodAndTrigger( + rankingType = CreatorRankingType.WEEKLY, + aggregationStartAtUtc = startAt, + aggregationEndAtUtc = endAt, + trigger = CreatorRankingSnapshotJobTrigger.FALLBACK + ) + + assertEquals(2L, count) + } + + private fun job( + startAt: LocalDateTime, + endAt: LocalDateTime, + trigger: CreatorRankingSnapshotJobTrigger + ): CreatorRankingSnapshotJobRecord { + return CreatorRankingSnapshotJobRecord( + rankingType = CreatorRankingType.WEEKLY, + aggregationStartAtUtc = startAt, + aggregationEndAtUtc = endAt, + visibleFromAtUtc = endAt.plusHours(9), + trigger = trigger, + status = CreatorRankingSnapshotJobStatus.FAILED, + lastError = null, + processingStartedAt = null, + processedAt = null + ) + } }