feat(content-ranking): 스냅샷 갱신에 공개 시각을 반영한다

This commit is contained in:
2026-06-24 23:46:40 +09:00
parent f2ea82f4a4
commit 30b687737e
2 changed files with 26 additions and 0 deletions

View File

@@ -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.CreatorRankingPeriodPolicy
import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingScorePolicy 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.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.domain.CreatorRankingUtcRange
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingAggregationPort 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.CreatorRankingAggregationResult
@@ -29,6 +30,7 @@ class CreatorRankingSnapshotRefreshService(
val startedAt = System.currentTimeMillis() val startedAt = System.currentTimeMillis()
val period = periodPolicy.resolveLastCompletedWeek(now) val period = periodPolicy.resolveLastCompletedWeek(now)
val utcRange = periodPolicy.toUtcRange(period) val utcRange = periodPolicy.toUtcRange(period)
val visibleFromAtUtc = periodPolicy.resolveVisibleFromAtUtc(period.endExclusiveKst)
runCatching { runCatching {
val aggregationResult = aggregationPort.aggregateCandidateResult( val aggregationResult = aggregationPort.aggregateCandidateResult(
startInclusiveUtc = utcRange.startInclusiveUtc, startInclusiveUtc = utcRange.startInclusiveUtc,
@@ -39,8 +41,10 @@ class CreatorRankingSnapshotRefreshService(
.takeRankedBoundary(limit = SNAPSHOT_LIMIT) .takeRankedBoundary(limit = SNAPSHOT_LIMIT)
snapshotPort.replaceSnapshots( snapshotPort.replaceSnapshots(
rankingType = CreatorRankingType.WEEKLY,
aggregationStartAtUtc = utcRange.startInclusiveUtc, aggregationStartAtUtc = utcRange.startInclusiveUtc,
aggregationEndAtUtc = utcRange.endExclusiveUtc, aggregationEndAtUtc = utcRange.endExclusiveUtc,
visibleFromAtUtc = visibleFromAtUtc,
newSnapshots = snapshots newSnapshots = snapshots
) )
aggregationResult.toLogCounts(storedCount = snapshots.size) aggregationResult.toLogCounts(storedCount = snapshots.size)
@@ -124,8 +128,10 @@ class CreatorRankingSnapshotRefreshService(
) )
return CreatorRankingSnapshotRecord( return CreatorRankingSnapshotRecord(
rankingType = CreatorRankingType.WEEKLY,
aggregationStartAtUtc = utcRange.startInclusiveUtc, aggregationStartAtUtc = utcRange.startInclusiveUtc,
aggregationEndAtUtc = utcRange.endExclusiveUtc, aggregationEndAtUtc = utcRange.endExclusiveUtc,
visibleFromAtUtc = utcRange.endExclusiveUtc.plusHours(9),
creatorId = creatorId, creatorId = creatorId,
nickname = nickname, nickname = nickname,
profileImageUrl = profileImageUrl, profileImageUrl = profileImageUrl,

View File

@@ -1,6 +1,7 @@
package kr.co.vividnext.sodalive.v2.ranking.application 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.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.CreatorRankingAggregationPort
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingAggregationResult import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingAggregationResult
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotPort 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(LocalDateTime.of(2026, 6, 7, 15, 0, 0), aggregationPort.endExclusiveUtc)
assertEquals(aggregationPort.startInclusiveUtc, snapshotPort.aggregationStartAtUtc) assertEquals(aggregationPort.startInclusiveUtc, snapshotPort.aggregationStartAtUtc)
assertEquals(aggregationPort.endExclusiveUtc, snapshotPort.aggregationEndAtUtc) 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(85.0, stored.contentLiveScore, 0.0001)
assertEquals(7.0, stored.engagementScore, 0.0001) assertEquals(7.0, stored.engagementScore, 0.0001)
assertEquals(19.8, stored.supportScore, 0.0001) assertEquals(19.8, stored.supportScore, 0.0001)
@@ -240,8 +243,10 @@ private class FakeCreatorRankingAggregationPort : CreatorRankingAggregationPort
private class FakeCreatorRankingSnapshotPort : CreatorRankingSnapshotPort { private class FakeCreatorRankingSnapshotPort : CreatorRankingSnapshotPort {
val snapshots = mutableListOf<CreatorRankingSnapshotRecord>() val snapshots = mutableListOf<CreatorRankingSnapshotRecord>()
var rankingType: CreatorRankingType? = null
var aggregationStartAtUtc: LocalDateTime? = null var aggregationStartAtUtc: LocalDateTime? = null
var aggregationEndAtUtc: LocalDateTime? = null var aggregationEndAtUtc: LocalDateTime? = null
var visibleFromAtUtc: LocalDateTime? = null
override fun findSnapshotsByAggregationPeriod( override fun findSnapshotsByAggregationPeriod(
aggregationStartAtUtc: LocalDateTime, aggregationStartAtUtc: LocalDateTime,
@@ -256,15 +261,30 @@ private class FakeCreatorRankingSnapshotPort : CreatorRankingSnapshotPort {
override fun findPreviousCompletedSnapshots(): List<CreatorRankingSnapshotRecord> = snapshots override fun findPreviousCompletedSnapshots(): List<CreatorRankingSnapshotRecord> = snapshots
override fun findLatestVisibleSnapshots(
rankingType: CreatorRankingType,
nowUtc: LocalDateTime
): List<CreatorRankingSnapshotRecord> = snapshots
override fun findPreviousVisibleSnapshots(
rankingType: CreatorRankingType,
currentAggregationStartAtUtc: LocalDateTime,
nowUtc: LocalDateTime
): List<CreatorRankingSnapshotRecord> = snapshots
override fun isSnapshotTableEmpty(): Boolean = snapshots.isEmpty() override fun isSnapshotTableEmpty(): Boolean = snapshots.isEmpty()
override fun replaceSnapshots( override fun replaceSnapshots(
rankingType: CreatorRankingType,
aggregationStartAtUtc: LocalDateTime, aggregationStartAtUtc: LocalDateTime,
aggregationEndAtUtc: LocalDateTime, aggregationEndAtUtc: LocalDateTime,
visibleFromAtUtc: LocalDateTime,
newSnapshots: List<CreatorRankingSnapshotRecord> newSnapshots: List<CreatorRankingSnapshotRecord>
) { ) {
this.rankingType = rankingType
this.aggregationStartAtUtc = aggregationStartAtUtc this.aggregationStartAtUtc = aggregationStartAtUtc
this.aggregationEndAtUtc = aggregationEndAtUtc this.aggregationEndAtUtc = aggregationEndAtUtc
this.visibleFromAtUtc = visibleFromAtUtc
snapshots.removeIf { snapshots.removeIf {
it.aggregationStartAtUtc == aggregationStartAtUtc && it.aggregationEndAtUtc == aggregationEndAtUtc it.aggregationStartAtUtc == aggregationStartAtUtc && it.aggregationEndAtUtc == aggregationEndAtUtc
} }