feat(content-ranking): 스냅샷 공개 조회 저장소를 추가한다
This commit is contained in:
@@ -1,20 +1,30 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.ranking.adapter.out.persistence
|
package kr.co.vividnext.sodalive.v2.ranking.adapter.out.persistence
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.BaseEntity
|
import kr.co.vividnext.sodalive.common.BaseEntity
|
||||||
|
import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingType
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import javax.persistence.Column
|
import javax.persistence.Column
|
||||||
import javax.persistence.Entity
|
import javax.persistence.Entity
|
||||||
|
import javax.persistence.EnumType
|
||||||
|
import javax.persistence.Enumerated
|
||||||
import javax.persistence.Table
|
import javax.persistence.Table
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "creator_ranking_snapshot")
|
@Table(name = "creator_ranking_snapshot")
|
||||||
class CreatorRankingSnapshot(
|
class CreatorRankingSnapshot(
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "ranking_type", nullable = false, updatable = false, length = 30)
|
||||||
|
val rankingType: CreatorRankingType,
|
||||||
|
|
||||||
@Column(name = "aggregation_start_at_utc", nullable = false, updatable = false)
|
@Column(name = "aggregation_start_at_utc", nullable = false, updatable = false)
|
||||||
val aggregationStartAtUtc: LocalDateTime,
|
val aggregationStartAtUtc: LocalDateTime,
|
||||||
|
|
||||||
@Column(name = "aggregation_end_at_utc", nullable = false, updatable = false)
|
@Column(name = "aggregation_end_at_utc", nullable = false, updatable = false)
|
||||||
val aggregationEndAtUtc: LocalDateTime,
|
val aggregationEndAtUtc: LocalDateTime,
|
||||||
|
|
||||||
|
@Column(name = "visible_from_at", nullable = false, updatable = false)
|
||||||
|
val visibleFromAtUtc: LocalDateTime,
|
||||||
|
|
||||||
@Column(name = "creator_id", nullable = false, updatable = false)
|
@Column(name = "creator_id", nullable = false, updatable = false)
|
||||||
val creatorId: Long,
|
val creatorId: Long,
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.ranking.adapter.out.persistence
|
package kr.co.vividnext.sodalive.v2.ranking.adapter.out.persistence
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingType
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
import org.springframework.data.jpa.repository.Query
|
import org.springframework.data.jpa.repository.Query
|
||||||
import org.springframework.data.repository.query.Param
|
import org.springframework.data.repository.query.Param
|
||||||
@@ -43,7 +44,50 @@ interface CreatorRankingSnapshotRepository : JpaRepository<CreatorRankingSnapsho
|
|||||||
)
|
)
|
||||||
fun findPreviousCompletedSnapshots(): List<CreatorRankingSnapshot>
|
fun findPreviousCompletedSnapshots(): List<CreatorRankingSnapshot>
|
||||||
|
|
||||||
fun deleteByAggregationStartAtUtcAndAggregationEndAtUtc(
|
@Query(
|
||||||
|
value = """
|
||||||
|
select *
|
||||||
|
from creator_ranking_snapshot crs
|
||||||
|
where crs.ranking_type = :rankingType
|
||||||
|
and crs.visible_from_at = (
|
||||||
|
select max(latest.visible_from_at)
|
||||||
|
from creator_ranking_snapshot latest
|
||||||
|
where latest.ranking_type = :rankingType
|
||||||
|
and latest.visible_from_at <= :nowUtc
|
||||||
|
)
|
||||||
|
order by crs.final_score desc
|
||||||
|
""",
|
||||||
|
nativeQuery = true
|
||||||
|
)
|
||||||
|
fun findLatestVisibleSnapshots(
|
||||||
|
@Param("rankingType") rankingType: String,
|
||||||
|
@Param("nowUtc") nowUtc: LocalDateTime
|
||||||
|
): List<CreatorRankingSnapshot>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
value = """
|
||||||
|
select *
|
||||||
|
from creator_ranking_snapshot crs
|
||||||
|
where crs.ranking_type = :rankingType
|
||||||
|
and crs.aggregation_start_at_utc = (
|
||||||
|
select max(previous.aggregation_start_at_utc)
|
||||||
|
from creator_ranking_snapshot previous
|
||||||
|
where previous.ranking_type = :rankingType
|
||||||
|
and previous.aggregation_start_at_utc < :currentAggregationStartAtUtc
|
||||||
|
and previous.visible_from_at <= :nowUtc
|
||||||
|
)
|
||||||
|
order by crs.final_score desc
|
||||||
|
""",
|
||||||
|
nativeQuery = true
|
||||||
|
)
|
||||||
|
fun findPreviousVisibleSnapshots(
|
||||||
|
@Param("rankingType") rankingType: String,
|
||||||
|
@Param("currentAggregationStartAtUtc") currentAggregationStartAtUtc: LocalDateTime,
|
||||||
|
@Param("nowUtc") nowUtc: LocalDateTime
|
||||||
|
): List<CreatorRankingSnapshot>
|
||||||
|
|
||||||
|
fun deleteByRankingTypeAndAggregationStartAtUtcAndAggregationEndAtUtc(
|
||||||
|
@Param("rankingType") rankingType: CreatorRankingType,
|
||||||
@Param("aggregationStartAtUtc") aggregationStartAtUtc: LocalDateTime,
|
@Param("aggregationStartAtUtc") aggregationStartAtUtc: LocalDateTime,
|
||||||
@Param("aggregationEndAtUtc") aggregationEndAtUtc: LocalDateTime
|
@Param("aggregationEndAtUtc") aggregationEndAtUtc: LocalDateTime
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.ranking.adapter.out.persistence
|
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.CreatorRankingSnapshotPort
|
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotPort
|
||||||
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotRecord
|
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotRecord
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
@@ -28,27 +29,51 @@ class DefaultCreatorRankingSnapshotRepository(
|
|||||||
return repository.findPreviousCompletedSnapshots().map { it.toRecord() }
|
return repository.findPreviousCompletedSnapshots().map { it.toRecord() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun findLatestVisibleSnapshots(
|
||||||
|
rankingType: CreatorRankingType,
|
||||||
|
nowUtc: LocalDateTime
|
||||||
|
): List<CreatorRankingSnapshotRecord> {
|
||||||
|
return repository.findLatestVisibleSnapshots(rankingType.name, nowUtc).map { it.toRecord() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findPreviousVisibleSnapshots(
|
||||||
|
rankingType: CreatorRankingType,
|
||||||
|
currentAggregationStartAtUtc: LocalDateTime,
|
||||||
|
nowUtc: LocalDateTime
|
||||||
|
): List<CreatorRankingSnapshotRecord> {
|
||||||
|
return repository.findPreviousVisibleSnapshots(
|
||||||
|
rankingType = rankingType.name,
|
||||||
|
currentAggregationStartAtUtc = currentAggregationStartAtUtc,
|
||||||
|
nowUtc = nowUtc
|
||||||
|
).map { it.toRecord() }
|
||||||
|
}
|
||||||
|
|
||||||
override fun isSnapshotTableEmpty(): Boolean {
|
override fun isSnapshotTableEmpty(): Boolean {
|
||||||
return repository.count() == 0L
|
return repository.count() == 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
override fun replaceSnapshots(
|
override fun replaceSnapshots(
|
||||||
|
rankingType: CreatorRankingType,
|
||||||
aggregationStartAtUtc: LocalDateTime,
|
aggregationStartAtUtc: LocalDateTime,
|
||||||
aggregationEndAtUtc: LocalDateTime,
|
aggregationEndAtUtc: LocalDateTime,
|
||||||
|
visibleFromAtUtc: LocalDateTime,
|
||||||
newSnapshots: List<CreatorRankingSnapshotRecord>
|
newSnapshots: List<CreatorRankingSnapshotRecord>
|
||||||
) {
|
) {
|
||||||
repository.deleteByAggregationStartAtUtcAndAggregationEndAtUtc(
|
repository.deleteByRankingTypeAndAggregationStartAtUtcAndAggregationEndAtUtc(
|
||||||
|
rankingType = rankingType,
|
||||||
aggregationStartAtUtc = aggregationStartAtUtc,
|
aggregationStartAtUtc = aggregationStartAtUtc,
|
||||||
aggregationEndAtUtc = aggregationEndAtUtc
|
aggregationEndAtUtc = aggregationEndAtUtc
|
||||||
)
|
)
|
||||||
repository.saveAll(newSnapshots.map { it.toEntity() })
|
repository.saveAll(newSnapshots.map { it.toEntity(rankingType, visibleFromAtUtc) })
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CreatorRankingSnapshot.toRecord(): CreatorRankingSnapshotRecord {
|
private fun CreatorRankingSnapshot.toRecord(): CreatorRankingSnapshotRecord {
|
||||||
return CreatorRankingSnapshotRecord(
|
return CreatorRankingSnapshotRecord(
|
||||||
|
rankingType = rankingType,
|
||||||
aggregationStartAtUtc = aggregationStartAtUtc,
|
aggregationStartAtUtc = aggregationStartAtUtc,
|
||||||
aggregationEndAtUtc = aggregationEndAtUtc,
|
aggregationEndAtUtc = aggregationEndAtUtc,
|
||||||
|
visibleFromAtUtc = visibleFromAtUtc,
|
||||||
creatorId = creatorId,
|
creatorId = creatorId,
|
||||||
nickname = nickname,
|
nickname = nickname,
|
||||||
profileImageUrl = profileImageUrl,
|
profileImageUrl = profileImageUrl,
|
||||||
@@ -69,10 +94,15 @@ class DefaultCreatorRankingSnapshotRepository(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CreatorRankingSnapshotRecord.toEntity(): CreatorRankingSnapshot {
|
private fun CreatorRankingSnapshotRecord.toEntity(
|
||||||
|
rankingType: CreatorRankingType,
|
||||||
|
visibleFromAtUtc: LocalDateTime
|
||||||
|
): CreatorRankingSnapshot {
|
||||||
return CreatorRankingSnapshot(
|
return CreatorRankingSnapshot(
|
||||||
|
rankingType = rankingType,
|
||||||
aggregationStartAtUtc = aggregationStartAtUtc,
|
aggregationStartAtUtc = aggregationStartAtUtc,
|
||||||
aggregationEndAtUtc = aggregationEndAtUtc,
|
aggregationEndAtUtc = aggregationEndAtUtc,
|
||||||
|
visibleFromAtUtc = visibleFromAtUtc,
|
||||||
creatorId = creatorId,
|
creatorId = creatorId,
|
||||||
nickname = nickname,
|
nickname = nickname,
|
||||||
profileImageUrl = profileImageUrl,
|
profileImageUrl = profileImageUrl,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.ranking.port.out
|
package kr.co.vividnext.sodalive.v2.ranking.port.out
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingType
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
interface CreatorRankingSnapshotPort {
|
interface CreatorRankingSnapshotPort {
|
||||||
@@ -12,18 +13,33 @@ interface CreatorRankingSnapshotPort {
|
|||||||
|
|
||||||
fun findPreviousCompletedSnapshots(): List<CreatorRankingSnapshotRecord>
|
fun findPreviousCompletedSnapshots(): List<CreatorRankingSnapshotRecord>
|
||||||
|
|
||||||
|
fun findLatestVisibleSnapshots(
|
||||||
|
rankingType: CreatorRankingType,
|
||||||
|
nowUtc: LocalDateTime
|
||||||
|
): List<CreatorRankingSnapshotRecord>
|
||||||
|
|
||||||
|
fun findPreviousVisibleSnapshots(
|
||||||
|
rankingType: CreatorRankingType,
|
||||||
|
currentAggregationStartAtUtc: LocalDateTime,
|
||||||
|
nowUtc: LocalDateTime
|
||||||
|
): List<CreatorRankingSnapshotRecord>
|
||||||
|
|
||||||
fun isSnapshotTableEmpty(): Boolean
|
fun isSnapshotTableEmpty(): Boolean
|
||||||
|
|
||||||
fun replaceSnapshots(
|
fun replaceSnapshots(
|
||||||
|
rankingType: CreatorRankingType,
|
||||||
aggregationStartAtUtc: LocalDateTime,
|
aggregationStartAtUtc: LocalDateTime,
|
||||||
aggregationEndAtUtc: LocalDateTime,
|
aggregationEndAtUtc: LocalDateTime,
|
||||||
|
visibleFromAtUtc: LocalDateTime,
|
||||||
newSnapshots: List<CreatorRankingSnapshotRecord>
|
newSnapshots: List<CreatorRankingSnapshotRecord>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class CreatorRankingSnapshotRecord(
|
data class CreatorRankingSnapshotRecord(
|
||||||
|
val rankingType: CreatorRankingType,
|
||||||
val aggregationStartAtUtc: LocalDateTime,
|
val aggregationStartAtUtc: LocalDateTime,
|
||||||
val aggregationEndAtUtc: LocalDateTime,
|
val aggregationEndAtUtc: LocalDateTime,
|
||||||
|
val visibleFromAtUtc: LocalDateTime,
|
||||||
val creatorId: Long,
|
val creatorId: Long,
|
||||||
val nickname: String,
|
val nickname: String,
|
||||||
val profileImageUrl: String?,
|
val profileImageUrl: String?,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.ranking.adapter.out.persistence
|
package kr.co.vividnext.sodalive.v2.ranking.adapter.out.persistence
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.configs.QueryDslConfig
|
import kr.co.vividnext.sodalive.configs.QueryDslConfig
|
||||||
|
import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingType
|
||||||
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotRecord
|
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotRecord
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.DisplayName
|
import org.junit.jupiter.api.DisplayName
|
||||||
@@ -37,8 +38,10 @@ class DefaultCreatorRankingSnapshotRepositoryTest @Autowired constructor(
|
|||||||
)
|
)
|
||||||
|
|
||||||
adapter.replaceSnapshots(
|
adapter.replaceSnapshots(
|
||||||
|
rankingType = CreatorRankingType.WEEKLY,
|
||||||
aggregationStartAtUtc = startAt,
|
aggregationStartAtUtc = startAt,
|
||||||
aggregationEndAtUtc = endAt,
|
aggregationEndAtUtc = endAt,
|
||||||
|
visibleFromAtUtc = endAt.plusHours(9),
|
||||||
newSnapshots = listOf(snapshotRecord(creatorId = 3L, aggregationStartAtUtc = startAt, aggregationEndAtUtc = endAt))
|
newSnapshots = listOf(snapshotRecord(creatorId = 3L, aggregationStartAtUtc = startAt, aggregationEndAtUtc = endAt))
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,6 +50,28 @@ class DefaultCreatorRankingSnapshotRepositoryTest @Autowired constructor(
|
|||||||
assertEquals(listOf(3L), adapter.findLatestSnapshots().map { it.creatorId })
|
assertEquals(listOf(3L), adapter.findLatestSnapshots().map { it.creatorId })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("스냅샷은 랭킹 타입과 공개 노출 시각을 저장하고 같은 타입/기간만 교체한다")
|
||||||
|
fun shouldPersistRankingTypeAndVisibleFromAtAndReplaceByTypeAndPeriod() {
|
||||||
|
val startAt = LocalDateTime.of(2026, 5, 31, 15, 0)
|
||||||
|
val endAt = LocalDateTime.of(2026, 6, 7, 15, 0)
|
||||||
|
val visibleFromAt = LocalDateTime.of(2026, 6, 8, 0, 0)
|
||||||
|
repository.save(snapshot(creatorId = 1L, aggregationStartAtUtc = startAt, aggregationEndAtUtc = endAt))
|
||||||
|
|
||||||
|
adapter.replaceSnapshots(
|
||||||
|
rankingType = CreatorRankingType.WEEKLY,
|
||||||
|
aggregationStartAtUtc = startAt,
|
||||||
|
aggregationEndAtUtc = endAt,
|
||||||
|
visibleFromAtUtc = visibleFromAt,
|
||||||
|
newSnapshots = listOf(snapshotRecord(creatorId = 2L, aggregationStartAtUtc = startAt, aggregationEndAtUtc = endAt))
|
||||||
|
)
|
||||||
|
|
||||||
|
val saved = repository.findAll().single()
|
||||||
|
assertEquals(CreatorRankingType.WEEKLY, saved.rankingType)
|
||||||
|
assertEquals(visibleFromAt, saved.visibleFromAtUtc)
|
||||||
|
assertEquals(2L, saved.creatorId)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("최신 완료 주차 스냅샷은 최신 종료 시각 기준으로 최종 점수 내림차순 조회한다")
|
@DisplayName("최신 완료 주차 스냅샷은 최신 종료 시각 기준으로 최종 점수 내림차순 조회한다")
|
||||||
fun shouldFindLatestSnapshotsByLatestAggregationEndAndFinalScoreDescending() {
|
fun shouldFindLatestSnapshotsByLatestAggregationEndAndFinalScoreDescending() {
|
||||||
@@ -90,6 +115,79 @@ class DefaultCreatorRankingSnapshotRepositoryTest @Autowired constructor(
|
|||||||
assertEquals(listOf(latestEndAt, latestEndAt, latestEndAt), latestSnapshots.map { it.aggregationEndAtUtc })
|
assertEquals(listOf(latestEndAt, latestEndAt, latestEndAt), latestSnapshots.map { it.aggregationEndAtUtc })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("최신 공개 스냅샷은 visibleFromAt이 현재 시각 이하인 최신 노출 시각 기준으로 조회한다")
|
||||||
|
fun shouldFindLatestVisibleSnapshotsByVisibleFromAt() {
|
||||||
|
val previousStartAt = LocalDateTime.of(2026, 5, 24, 15, 0)
|
||||||
|
val previousEndAt = LocalDateTime.of(2026, 5, 31, 15, 0)
|
||||||
|
val latestStartAt = LocalDateTime.of(2026, 5, 31, 15, 0)
|
||||||
|
val latestEndAt = LocalDateTime.of(2026, 6, 7, 15, 0)
|
||||||
|
repository.saveAll(
|
||||||
|
listOf(
|
||||||
|
snapshot(
|
||||||
|
creatorId = 1L,
|
||||||
|
aggregationStartAtUtc = previousStartAt,
|
||||||
|
aggregationEndAtUtc = previousEndAt,
|
||||||
|
visibleFromAtUtc = LocalDateTime.of(2026, 6, 1, 0, 0),
|
||||||
|
finalScore = 100.0
|
||||||
|
),
|
||||||
|
snapshot(
|
||||||
|
creatorId = 2L,
|
||||||
|
aggregationStartAtUtc = latestStartAt,
|
||||||
|
aggregationEndAtUtc = latestEndAt,
|
||||||
|
visibleFromAtUtc = LocalDateTime.of(2026, 6, 8, 0, 0),
|
||||||
|
finalScore = 200.0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val beforeVisible = adapter.findLatestVisibleSnapshots(
|
||||||
|
rankingType = CreatorRankingType.WEEKLY,
|
||||||
|
nowUtc = LocalDateTime.of(2026, 6, 7, 23, 59, 59)
|
||||||
|
)
|
||||||
|
val afterVisible = adapter.findLatestVisibleSnapshots(
|
||||||
|
rankingType = CreatorRankingType.WEEKLY,
|
||||||
|
nowUtc = LocalDateTime.of(2026, 6, 8, 0, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(listOf(1L), beforeVisible.map { it.creatorId })
|
||||||
|
assertEquals(listOf(2L), afterVisible.map { it.creatorId })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("직전 공개 스냅샷은 현재 공개 스냅샷보다 이전 집계 시작 시각 중 최신 공개 기준으로 조회한다")
|
||||||
|
fun shouldFindPreviousVisibleSnapshotsBeforeCurrentVisibleSnapshot() {
|
||||||
|
val oldestStartAt = LocalDateTime.of(2026, 5, 17, 15, 0)
|
||||||
|
val previousStartAt = LocalDateTime.of(2026, 5, 24, 15, 0)
|
||||||
|
val latestStartAt = LocalDateTime.of(2026, 5, 31, 15, 0)
|
||||||
|
repository.saveAll(
|
||||||
|
listOf(
|
||||||
|
snapshot(creatorId = 1L, aggregationStartAtUtc = oldestStartAt, aggregationEndAtUtc = oldestStartAt.plusWeeks(1)),
|
||||||
|
snapshot(
|
||||||
|
creatorId = 2L,
|
||||||
|
aggregationStartAtUtc = previousStartAt,
|
||||||
|
aggregationEndAtUtc = previousStartAt.plusWeeks(1),
|
||||||
|
finalScore = 300.0
|
||||||
|
),
|
||||||
|
snapshot(
|
||||||
|
creatorId = 3L,
|
||||||
|
aggregationStartAtUtc = previousStartAt,
|
||||||
|
aggregationEndAtUtc = previousStartAt.plusWeeks(1),
|
||||||
|
finalScore = 200.0
|
||||||
|
),
|
||||||
|
snapshot(creatorId = 4L, aggregationStartAtUtc = latestStartAt, aggregationEndAtUtc = latestStartAt.plusWeeks(1))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val previous = adapter.findPreviousVisibleSnapshots(
|
||||||
|
rankingType = CreatorRankingType.WEEKLY,
|
||||||
|
currentAggregationStartAtUtc = latestStartAt,
|
||||||
|
nowUtc = LocalDateTime.of(2026, 6, 8, 0, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(listOf(2L, 3L), previous.map { it.creatorId })
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("직전 완료 주차 스냅샷은 최신 종료 시각보다 이전인 가장 큰 종료 시각 기준으로 조회한다")
|
@DisplayName("직전 완료 주차 스냅샷은 최신 종료 시각보다 이전인 가장 큰 종료 시각 기준으로 조회한다")
|
||||||
fun shouldFindPreviousCompletedSnapshotsBeforeLatestPeriod() {
|
fun shouldFindPreviousCompletedSnapshotsBeforeLatestPeriod() {
|
||||||
@@ -181,7 +279,13 @@ class DefaultCreatorRankingSnapshotRepositoryTest @Autowired constructor(
|
|||||||
snapshotRecord(creatorId = 22L, finalScore = 500.0, aggregationStartAtUtc = startAt, aggregationEndAtUtc = endAt)
|
snapshotRecord(creatorId = 22L, finalScore = 500.0, aggregationStartAtUtc = startAt, aggregationEndAtUtc = endAt)
|
||||||
)
|
)
|
||||||
|
|
||||||
adapter.replaceSnapshots(startAt, endAt, candidates)
|
adapter.replaceSnapshots(
|
||||||
|
rankingType = CreatorRankingType.WEEKLY,
|
||||||
|
aggregationStartAtUtc = startAt,
|
||||||
|
aggregationEndAtUtc = endAt,
|
||||||
|
visibleFromAtUtc = endAt.plusHours(9),
|
||||||
|
newSnapshots = candidates
|
||||||
|
)
|
||||||
|
|
||||||
val latestSnapshots = adapter.findLatestSnapshots()
|
val latestSnapshots = adapter.findLatestSnapshots()
|
||||||
assertEquals(22, latestSnapshots.size)
|
assertEquals(22, latestSnapshots.size)
|
||||||
@@ -192,11 +296,14 @@ class DefaultCreatorRankingSnapshotRepositoryTest @Autowired constructor(
|
|||||||
creatorId: Long,
|
creatorId: Long,
|
||||||
finalScore: Double = 100.0,
|
finalScore: Double = 100.0,
|
||||||
aggregationStartAtUtc: LocalDateTime,
|
aggregationStartAtUtc: LocalDateTime,
|
||||||
aggregationEndAtUtc: LocalDateTime
|
aggregationEndAtUtc: LocalDateTime,
|
||||||
|
visibleFromAtUtc: LocalDateTime = aggregationEndAtUtc.plusHours(9)
|
||||||
): CreatorRankingSnapshot {
|
): CreatorRankingSnapshot {
|
||||||
return CreatorRankingSnapshot(
|
return CreatorRankingSnapshot(
|
||||||
|
rankingType = CreatorRankingType.WEEKLY,
|
||||||
aggregationStartAtUtc = aggregationStartAtUtc,
|
aggregationStartAtUtc = aggregationStartAtUtc,
|
||||||
aggregationEndAtUtc = aggregationEndAtUtc,
|
aggregationEndAtUtc = aggregationEndAtUtc,
|
||||||
|
visibleFromAtUtc = visibleFromAtUtc,
|
||||||
creatorId = creatorId,
|
creatorId = creatorId,
|
||||||
nickname = "creator-$creatorId",
|
nickname = "creator-$creatorId",
|
||||||
profileImageUrl = "profile-$creatorId.png",
|
profileImageUrl = "profile-$creatorId.png",
|
||||||
@@ -221,11 +328,14 @@ class DefaultCreatorRankingSnapshotRepositoryTest @Autowired constructor(
|
|||||||
creatorId: Long,
|
creatorId: Long,
|
||||||
finalScore: Double = 100.0,
|
finalScore: Double = 100.0,
|
||||||
aggregationStartAtUtc: LocalDateTime,
|
aggregationStartAtUtc: LocalDateTime,
|
||||||
aggregationEndAtUtc: LocalDateTime
|
aggregationEndAtUtc: LocalDateTime,
|
||||||
|
visibleFromAtUtc: LocalDateTime = aggregationEndAtUtc.plusHours(9)
|
||||||
): CreatorRankingSnapshotRecord {
|
): CreatorRankingSnapshotRecord {
|
||||||
return CreatorRankingSnapshotRecord(
|
return CreatorRankingSnapshotRecord(
|
||||||
|
rankingType = CreatorRankingType.WEEKLY,
|
||||||
aggregationStartAtUtc = aggregationStartAtUtc,
|
aggregationStartAtUtc = aggregationStartAtUtc,
|
||||||
aggregationEndAtUtc = aggregationEndAtUtc,
|
aggregationEndAtUtc = aggregationEndAtUtc,
|
||||||
|
visibleFromAtUtc = visibleFromAtUtc,
|
||||||
creatorId = creatorId,
|
creatorId = creatorId,
|
||||||
nickname = "creator-$creatorId",
|
nickname = "creator-$creatorId",
|
||||||
profileImageUrl = "profile-$creatorId.png",
|
profileImageUrl = "profile-$creatorId.png",
|
||||||
|
|||||||
Reference in New Issue
Block a user