diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/application/CreatorRankingSnapshotRefreshService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/application/CreatorRankingSnapshotRefreshService.kt index 77fb8c02..b96bbdf4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/application/CreatorRankingSnapshotRefreshService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/ranking/application/CreatorRankingSnapshotRefreshService.kt @@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.v2.ranking.application import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingPeriodPolicy import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingScorePolicy import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingSnapshotCandidate +import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingType import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingUtcRange import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingAggregationPort import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingAggregationResult @@ -29,6 +30,7 @@ class CreatorRankingSnapshotRefreshService( val startedAt = System.currentTimeMillis() val period = periodPolicy.resolveLastCompletedWeek(now) val utcRange = periodPolicy.toUtcRange(period) + val visibleFromAtUtc = periodPolicy.resolveVisibleFromAtUtc(period.endExclusiveKst) runCatching { val aggregationResult = aggregationPort.aggregateCandidateResult( startInclusiveUtc = utcRange.startInclusiveUtc, @@ -39,8 +41,10 @@ class CreatorRankingSnapshotRefreshService( .takeRankedBoundary(limit = SNAPSHOT_LIMIT) snapshotPort.replaceSnapshots( + rankingType = CreatorRankingType.WEEKLY, aggregationStartAtUtc = utcRange.startInclusiveUtc, aggregationEndAtUtc = utcRange.endExclusiveUtc, + visibleFromAtUtc = visibleFromAtUtc, newSnapshots = snapshots ) aggregationResult.toLogCounts(storedCount = snapshots.size) @@ -124,8 +128,10 @@ class CreatorRankingSnapshotRefreshService( ) return CreatorRankingSnapshotRecord( + rankingType = CreatorRankingType.WEEKLY, aggregationStartAtUtc = utcRange.startInclusiveUtc, aggregationEndAtUtc = utcRange.endExclusiveUtc, + visibleFromAtUtc = utcRange.endExclusiveUtc.plusHours(9), creatorId = creatorId, nickname = nickname, profileImageUrl = profileImageUrl, diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/ranking/application/CreatorRankingSnapshotRefreshServiceTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/ranking/application/CreatorRankingSnapshotRefreshServiceTest.kt index f43fa67e..8a7fa528 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/v2/ranking/application/CreatorRankingSnapshotRefreshServiceTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/ranking/application/CreatorRankingSnapshotRefreshServiceTest.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.v2.ranking.application import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingSnapshotCandidate +import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingType import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingAggregationPort import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingAggregationResult import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotPort @@ -49,6 +50,8 @@ class CreatorRankingSnapshotRefreshServiceTest { assertEquals(LocalDateTime.of(2026, 6, 7, 15, 0, 0), aggregationPort.endExclusiveUtc) assertEquals(aggregationPort.startInclusiveUtc, snapshotPort.aggregationStartAtUtc) assertEquals(aggregationPort.endExclusiveUtc, snapshotPort.aggregationEndAtUtc) + assertEquals(CreatorRankingType.WEEKLY, snapshotPort.rankingType) + assertEquals(LocalDateTime.of(2026, 6, 8, 0, 0), snapshotPort.visibleFromAtUtc) assertEquals(85.0, stored.contentLiveScore, 0.0001) assertEquals(7.0, stored.engagementScore, 0.0001) assertEquals(19.8, stored.supportScore, 0.0001) @@ -240,8 +243,10 @@ private class FakeCreatorRankingAggregationPort : CreatorRankingAggregationPort private class FakeCreatorRankingSnapshotPort : CreatorRankingSnapshotPort { val snapshots = mutableListOf() + var rankingType: CreatorRankingType? = null var aggregationStartAtUtc: LocalDateTime? = null var aggregationEndAtUtc: LocalDateTime? = null + var visibleFromAtUtc: LocalDateTime? = null override fun findSnapshotsByAggregationPeriod( aggregationStartAtUtc: LocalDateTime, @@ -256,15 +261,30 @@ private class FakeCreatorRankingSnapshotPort : CreatorRankingSnapshotPort { override fun findPreviousCompletedSnapshots(): List = snapshots + override fun findLatestVisibleSnapshots( + rankingType: CreatorRankingType, + nowUtc: LocalDateTime + ): List = snapshots + + override fun findPreviousVisibleSnapshots( + rankingType: CreatorRankingType, + currentAggregationStartAtUtc: LocalDateTime, + nowUtc: LocalDateTime + ): List = snapshots + override fun isSnapshotTableEmpty(): Boolean = snapshots.isEmpty() override fun replaceSnapshots( + rankingType: CreatorRankingType, aggregationStartAtUtc: LocalDateTime, aggregationEndAtUtc: LocalDateTime, + visibleFromAtUtc: LocalDateTime, newSnapshots: List ) { + this.rankingType = rankingType this.aggregationStartAtUtc = aggregationStartAtUtc this.aggregationEndAtUtc = aggregationEndAtUtc + this.visibleFromAtUtc = visibleFromAtUtc snapshots.removeIf { it.aggregationStartAtUtc == aggregationStartAtUtc && it.aggregationEndAtUtc == aggregationEndAtUtc }