feat(home): 응원 크리에이터 집계 쿼리를 변경한다
This commit is contained in:
@@ -12,7 +12,7 @@ import javax.persistence.JoinColumn
|
|||||||
import javax.persistence.ManyToOne
|
import javax.persistence.ManyToOne
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
data class UseCanCalculate(
|
class UseCanCalculate(
|
||||||
val can: Int,
|
val can: Int,
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
|||||||
+18
-10
@@ -570,9 +570,10 @@ class DefaultHomeRecommendationQueryRepository(
|
|||||||
|
|
||||||
override fun findCheerCreatorSnapshots(
|
override fun findCheerCreatorSnapshots(
|
||||||
windowStart: LocalDateTime,
|
windowStart: LocalDateTime,
|
||||||
snapshotAt: LocalDateTime,
|
windowEndExclusive: LocalDateTime,
|
||||||
limit: Int
|
limit: Int
|
||||||
): List<RecommendationSnapshotRecord> {
|
): List<RecommendationSnapshotRecord> {
|
||||||
|
val snapshotAt = windowEndExclusive.minusSeconds(1)
|
||||||
val sql = """
|
val sql = """
|
||||||
with creator_debut as (
|
with creator_debut as (
|
||||||
select debut_events.creator_id as creator_id, min(debut_events.debut_at) as debut_at
|
select debut_events.creator_id as creator_id, min(debut_events.debut_at) as debut_at
|
||||||
@@ -595,14 +596,14 @@ class DefaultHomeRecommendationQueryRepository(
|
|||||||
donation_stats as (
|
donation_stats as (
|
||||||
select ucc.recipient_creator_id as creator_id,
|
select ucc.recipient_creator_id as creator_id,
|
||||||
coalesce(sum(ucc.can), 0) as donation_amount,
|
coalesce(sum(ucc.can), 0) as donation_amount,
|
||||||
count(ucc.id) as donation_count
|
count(distinct ucc.use_can_id) as donation_count
|
||||||
from use_can_calculate ucc
|
from use_can_calculate ucc
|
||||||
join use_can uc on uc.id = ucc.use_can_id
|
join use_can uc on uc.id = ucc.use_can_id
|
||||||
where ucc.status = 'RECEIVED'
|
where ucc.status = 'RECEIVED'
|
||||||
and uc.is_refund = false
|
and uc.is_refund = false
|
||||||
and uc.can_usage = 'CHANNEL_DONATION'
|
and uc.can_usage = 'CHANNEL_DONATION'
|
||||||
and ucc.created_at >= :windowStart
|
and ucc.created_at >= :windowStart
|
||||||
and ucc.created_at <= :snapshotAt
|
and ucc.created_at < :windowEndExclusive
|
||||||
group by ucc.recipient_creator_id
|
group by ucc.recipient_creator_id
|
||||||
),
|
),
|
||||||
fan_talk_stats as (
|
fan_talk_stats as (
|
||||||
@@ -610,17 +611,17 @@ class DefaultHomeRecommendationQueryRepository(
|
|||||||
from creator_cheers ch
|
from creator_cheers ch
|
||||||
where ch.is_active = true
|
where ch.is_active = true
|
||||||
and ch.created_at >= :windowStart
|
and ch.created_at >= :windowStart
|
||||||
and ch.created_at <= :snapshotAt
|
and ch.created_at < :windowEndExclusive
|
||||||
group by ch.creator_id
|
group by ch.creator_id
|
||||||
)
|
)
|
||||||
select m.id as target_id,
|
select m.id as target_id,
|
||||||
((coalesce(ds.donation_amount, 0) * ${RecommendationScoreSpec.CHEER_DONATION_AMOUNT_WEIGHT} +
|
((coalesce(ds.donation_amount, 0) * ${RecommendationScoreSpec.CHEER_DONATION_AMOUNT_WEIGHT} +
|
||||||
coalesce(fts.fan_talk_count, 0) * ${RecommendationScoreSpec.CHEER_FAN_TALK_WEIGHT} +
|
coalesce(fts.fan_talk_count, 0) * ${RecommendationScoreSpec.CHEER_FAN_TALK_WEIGHT} +
|
||||||
coalesce(ds.donation_count, 0) * ${RecommendationScoreSpec.CHEER_DONATION_COUNT_WEIGHT}) *
|
coalesce(ds.donation_count, 0) * ${RecommendationScoreSpec.CHEER_DONATION_COUNT_WEIGHT}) *
|
||||||
case
|
case
|
||||||
when cd.debut_at >= :boost10Start then ${RecommendationScoreSpec.NEW_BOOST_10_DAYS}
|
when cd.debut_at >= :boost10Start then ${RecommendationScoreSpec.CHEER_NEW_BOOST_10_DAYS}
|
||||||
when cd.debut_at >= :boost20Start then ${RecommendationScoreSpec.NEW_BOOST_20_DAYS}
|
when cd.debut_at >= :boost20Start then ${RecommendationScoreSpec.CHEER_NEW_BOOST_20_DAYS}
|
||||||
when cd.debut_at >= :boost30Start then ${RecommendationScoreSpec.NEW_BOOST_30_DAYS}
|
when cd.debut_at >= :boost30Start then ${RecommendationScoreSpec.CHEER_NEW_BOOST_30_DAYS}
|
||||||
else ${RecommendationScoreSpec.DEFAULT_NEW_BOOST}
|
else ${RecommendationScoreSpec.DEFAULT_NEW_BOOST}
|
||||||
end) as score,
|
end) as score,
|
||||||
rand() as random_tie_breaker
|
rand() as random_tie_breaker
|
||||||
@@ -635,7 +636,14 @@ class DefaultHomeRecommendationQueryRepository(
|
|||||||
limit :limit
|
limit :limit
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
return executeSnapshotQuery(sql, RecommendedSectionType.CHEER_CREATOR, windowStart, snapshotAt, limit)
|
return executeSnapshotQuery(
|
||||||
|
sql,
|
||||||
|
RecommendedSectionType.CHEER_CREATOR,
|
||||||
|
windowStart,
|
||||||
|
snapshotAt,
|
||||||
|
limit,
|
||||||
|
windowEndExclusive
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findPopularCommunitySnapshots(
|
override fun findPopularCommunitySnapshots(
|
||||||
|
|||||||
+1
-1
@@ -46,7 +46,7 @@ interface HomeRecommendationQueryPort {
|
|||||||
|
|
||||||
fun findCheerCreatorSnapshots(
|
fun findCheerCreatorSnapshots(
|
||||||
windowStart: LocalDateTime,
|
windowStart: LocalDateTime,
|
||||||
snapshotAt: LocalDateTime,
|
windowEndExclusive: LocalDateTime,
|
||||||
limit: Int
|
limit: Int
|
||||||
): List<RecommendationSnapshotRecord>
|
): List<RecommendationSnapshotRecord>
|
||||||
|
|
||||||
|
|||||||
+58
-28
@@ -773,32 +773,34 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("최근 응원 스냅샷은 CHANNEL_DONATION 후원 금액과 후원 수만 집계한다")
|
@DisplayName("최근 응원 스냅샷은 전날 half-open 범위의 CHANNEL_DONATION 금액과 distinct 후원 수만 집계한다")
|
||||||
fun shouldFindCheerCreatorSnapshotsWithChannelDonationOnly() {
|
fun shouldFindCheerCreatorSnapshotsWithChannelDonationOnly() {
|
||||||
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
|
val windowStart = LocalDateTime.of(2026, 5, 28, 15, 0)
|
||||||
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
|
val windowEndExclusive = LocalDateTime.of(2026, 5, 29, 15, 0)
|
||||||
|
val snapshotAt = windowEndExclusive.minusSeconds(1)
|
||||||
val creator = saveMember("cheer-creator", MemberRole.CREATOR)
|
val creator = saveMember("cheer-creator", MemberRole.CREATOR)
|
||||||
val donor = saveMember("cheer-donor", MemberRole.USER)
|
val donor = saveMember("cheer-donor", MemberRole.USER)
|
||||||
saveAudioContent(creator, LocalDateTime.of(2026, 5, 20, 12, 0), isActive = true)
|
saveAudioContent(creator, LocalDateTime.of(2026, 5, 20, 12, 0), isActive = true)
|
||||||
saveLiveRoom(creator, LocalDateTime.of(2026, 5, 10, 12, 0), channelName = "cheer-channel")
|
saveLiveRoom(creator, LocalDateTime.of(2026, 5, 10, 12, 0), channelName = "cheer-channel")
|
||||||
|
|
||||||
saveUseCanCalculate(
|
val firstDonation = saveUseCanCalculate(
|
||||||
donor,
|
donor,
|
||||||
creator,
|
creator,
|
||||||
CanUsage.CHANNEL_DONATION,
|
CanUsage.CHANNEL_DONATION,
|
||||||
can = 100,
|
can = 100,
|
||||||
status = UseCanCalculateStatus.RECEIVED,
|
status = UseCanCalculateStatus.RECEIVED,
|
||||||
isRefund = false,
|
isRefund = false,
|
||||||
createdAt = windowStart.plusDays(1)
|
createdAt = windowStart.plusHours(1)
|
||||||
)
|
)
|
||||||
|
saveUseCanCalculateForUseCan(firstDonation.useCan!!, creator, can = 50, createdAt = windowStart.plusHours(2))
|
||||||
saveUseCanCalculate(
|
saveUseCanCalculate(
|
||||||
donor,
|
donor,
|
||||||
creator,
|
creator,
|
||||||
CanUsage.CHANNEL_DONATION,
|
CanUsage.CHANNEL_DONATION,
|
||||||
can = 50,
|
can = 999,
|
||||||
status = UseCanCalculateStatus.RECEIVED,
|
status = UseCanCalculateStatus.RECEIVED,
|
||||||
isRefund = false,
|
isRefund = false,
|
||||||
createdAt = snapshotAt
|
createdAt = windowEndExclusive
|
||||||
)
|
)
|
||||||
saveUseCanCalculate(
|
saveUseCanCalculate(
|
||||||
donor,
|
donor,
|
||||||
@@ -807,7 +809,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
can = 1_000,
|
can = 1_000,
|
||||||
status = UseCanCalculateStatus.RECEIVED,
|
status = UseCanCalculateStatus.RECEIVED,
|
||||||
isRefund = false,
|
isRefund = false,
|
||||||
createdAt = windowStart.plusDays(1)
|
createdAt = windowStart.plusHours(1)
|
||||||
)
|
)
|
||||||
saveUseCanCalculate(
|
saveUseCanCalculate(
|
||||||
donor,
|
donor,
|
||||||
@@ -816,7 +818,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
can = 1_000,
|
can = 1_000,
|
||||||
status = UseCanCalculateStatus.RECEIVED,
|
status = UseCanCalculateStatus.RECEIVED,
|
||||||
isRefund = false,
|
isRefund = false,
|
||||||
createdAt = windowStart.plusDays(1)
|
createdAt = windowStart.plusHours(1)
|
||||||
)
|
)
|
||||||
saveUseCanCalculate(
|
saveUseCanCalculate(
|
||||||
donor,
|
donor,
|
||||||
@@ -825,7 +827,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
can = 1_000,
|
can = 1_000,
|
||||||
status = UseCanCalculateStatus.RECEIVED,
|
status = UseCanCalculateStatus.RECEIVED,
|
||||||
isRefund = false,
|
isRefund = false,
|
||||||
createdAt = windowStart.plusDays(1)
|
createdAt = windowStart.plusHours(1)
|
||||||
)
|
)
|
||||||
saveUseCanCalculate(
|
saveUseCanCalculate(
|
||||||
donor,
|
donor,
|
||||||
@@ -834,7 +836,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
can = 1_000,
|
can = 1_000,
|
||||||
status = UseCanCalculateStatus.REFUND,
|
status = UseCanCalculateStatus.REFUND,
|
||||||
isRefund = false,
|
isRefund = false,
|
||||||
createdAt = windowStart.plusDays(1)
|
createdAt = windowStart.plusHours(1)
|
||||||
)
|
)
|
||||||
saveUseCanCalculate(
|
saveUseCanCalculate(
|
||||||
donor,
|
donor,
|
||||||
@@ -843,7 +845,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
can = 1_000,
|
can = 1_000,
|
||||||
status = UseCanCalculateStatus.RECEIVED,
|
status = UseCanCalculateStatus.RECEIVED,
|
||||||
isRefund = true,
|
isRefund = true,
|
||||||
createdAt = windowStart.plusDays(1)
|
createdAt = windowStart.plusHours(1)
|
||||||
)
|
)
|
||||||
saveUseCanCalculate(
|
saveUseCanCalculate(
|
||||||
donor,
|
donor,
|
||||||
@@ -861,28 +863,29 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
can = 1_000,
|
can = 1_000,
|
||||||
status = UseCanCalculateStatus.RECEIVED,
|
status = UseCanCalculateStatus.RECEIVED,
|
||||||
isRefund = false,
|
isRefund = false,
|
||||||
createdAt = windowStart.plusDays(1)
|
createdAt = windowStart.plusHours(1)
|
||||||
)
|
)
|
||||||
val cheer1 = saveCreatorCheers(donor, creator, isActive = true)
|
val cheer1 = saveCreatorCheers(donor, creator, isActive = true)
|
||||||
val cheer2 = saveCreatorCheers(donor, creator, isActive = true)
|
val cheer2 = saveCreatorCheers(donor, creator, isActive = true)
|
||||||
val inactiveCheer = saveCreatorCheers(donor, creator, isActive = false)
|
val inactiveCheer = saveCreatorCheers(donor, creator, isActive = false)
|
||||||
updateCreatedAt("CreatorCheers", cheer1.id!!, windowStart.plusDays(1))
|
updateCreatedAt("CreatorCheers", cheer1.id!!, windowStart.plusHours(1))
|
||||||
updateCreatedAt("CreatorCheers", cheer2.id!!, snapshotAt)
|
updateCreatedAt("CreatorCheers", cheer2.id!!, windowEndExclusive)
|
||||||
updateCreatedAt("CreatorCheers", inactiveCheer.id!!, windowStart.plusDays(1))
|
updateCreatedAt("CreatorCheers", inactiveCheer.id!!, windowStart.plusHours(1))
|
||||||
updateCreatedAt("Member", creator.id!!, LocalDateTime.of(2026, 5, 10, 12, 0))
|
updateCreatedAt("Member", creator.id!!, LocalDateTime.of(2026, 5, 10, 12, 0))
|
||||||
flushAndClear()
|
flushAndClear()
|
||||||
|
|
||||||
val snapshots = repository.findCheerCreatorSnapshots(windowStart, snapshotAt, limit = 10)
|
val snapshots = repository.findCheerCreatorSnapshots(windowStart, windowEndExclusive, limit = 10)
|
||||||
|
|
||||||
val expectedScore = scorePolicy.calculateCheerScore(
|
val expectedScore = scorePolicy.calculateCheerScore(
|
||||||
donationAmount = 150,
|
donationAmount = 150,
|
||||||
fanTalkCount = 2,
|
fanTalkCount = 1,
|
||||||
donationCount = 2,
|
donationCount = 1,
|
||||||
newBoost = scorePolicy.calculateCreatorNewBoost(LocalDateTime.of(2026, 5, 10, 12, 0), snapshotAt)
|
newBoost = scorePolicy.calculateCheerCreatorNewBoost(LocalDateTime.of(2026, 5, 10, 12, 0), snapshotAt)
|
||||||
)
|
)
|
||||||
assertEquals(1, snapshots.size)
|
assertEquals(1, snapshots.size)
|
||||||
assertEquals(RecommendedSectionType.CHEER_CREATOR, snapshots.single().sectionType)
|
assertEquals(RecommendedSectionType.CHEER_CREATOR, snapshots.single().sectionType)
|
||||||
assertEquals(creator.id, snapshots.single().targetId)
|
assertEquals(creator.id, snapshots.single().targetId)
|
||||||
|
assertEquals(snapshotAt, snapshots.single().snapshotAt)
|
||||||
assertEquals(expectedScore, snapshots.single().score, 0.0001)
|
assertEquals(expectedScore, snapshots.single().score, 0.0001)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -891,6 +894,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
fun shouldFindCheerCreatorSnapshotsWithDbScoreOrderAndLimit() {
|
fun shouldFindCheerCreatorSnapshotsWithDbScoreOrderAndLimit() {
|
||||||
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
|
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
|
||||||
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
|
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
|
||||||
|
val windowEndExclusive = snapshotAt.plusSeconds(1)
|
||||||
val donor = saveMember("cheer-score-donor", MemberRole.USER)
|
val donor = saveMember("cheer-score-donor", MemberRole.USER)
|
||||||
val oldCreator = saveMember("old-cheer-score-creator", MemberRole.CREATOR)
|
val oldCreator = saveMember("old-cheer-score-creator", MemberRole.CREATOR)
|
||||||
val newCreator = saveMember("new-cheer-score-creator", MemberRole.CREATOR)
|
val newCreator = saveMember("new-cheer-score-creator", MemberRole.CREATOR)
|
||||||
@@ -916,17 +920,18 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
)
|
)
|
||||||
flushAndClear()
|
flushAndClear()
|
||||||
|
|
||||||
val snapshots = repository.findCheerCreatorSnapshots(windowStart, snapshotAt, limit = 1)
|
val snapshots = repository.findCheerCreatorSnapshots(windowStart, windowEndExclusive, limit = 1)
|
||||||
|
|
||||||
val expectedScore = scorePolicy.calculateCheerScore(
|
val expectedScore = scorePolicy.calculateCheerScore(
|
||||||
donationAmount = 2,
|
donationAmount = 2,
|
||||||
fanTalkCount = 0,
|
fanTalkCount = 0,
|
||||||
donationCount = 1,
|
donationCount = 1,
|
||||||
newBoost = scorePolicy.calculateCreatorNewBoost(LocalDateTime.of(2026, 5, 20, 0, 0), snapshotAt)
|
newBoost = scorePolicy.calculateCheerCreatorNewBoost(LocalDateTime.of(2026, 5, 20, 0, 0), snapshotAt)
|
||||||
)
|
)
|
||||||
assertEquals(1, snapshots.size)
|
assertEquals(1, snapshots.size)
|
||||||
assertEquals(RecommendedSectionType.CHEER_CREATOR, snapshots.single().sectionType)
|
assertEquals(RecommendedSectionType.CHEER_CREATOR, snapshots.single().sectionType)
|
||||||
assertEquals(newCreator.id, snapshots.single().targetId)
|
assertEquals(newCreator.id, snapshots.single().targetId)
|
||||||
|
assertEquals(snapshotAt, snapshots.single().snapshotAt)
|
||||||
assertEquals(expectedScore, snapshots.single().score, 0.0001)
|
assertEquals(expectedScore, snapshots.single().score, 0.0001)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1046,6 +1051,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
fun shouldExcludeCreatorSnapshotsWithoutActualDebutAt() {
|
fun shouldExcludeCreatorSnapshotsWithoutActualDebutAt() {
|
||||||
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
|
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
|
||||||
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
|
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
|
||||||
|
val windowEndExclusive = snapshotAt.plusSeconds(1)
|
||||||
val creator = saveMember("creator-without-debut", MemberRole.CREATOR)
|
val creator = saveMember("creator-without-debut", MemberRole.CREATOR)
|
||||||
val donor = saveMember("donor-without-debut", MemberRole.USER)
|
val donor = saveMember("donor-without-debut", MemberRole.USER)
|
||||||
val community = saveCommunity(creator, isCommentAvailable = true)
|
val community = saveCommunity(creator, isCommentAvailable = true)
|
||||||
@@ -1063,7 +1069,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
updateCreatedAt("CreatorCommunityLike", like.id!!, windowStart.plusDays(1))
|
updateCreatedAt("CreatorCommunityLike", like.id!!, windowStart.plusDays(1))
|
||||||
flushAndClear()
|
flushAndClear()
|
||||||
|
|
||||||
val cheerSnapshots = repository.findCheerCreatorSnapshots(windowStart, snapshotAt, limit = 10)
|
val cheerSnapshots = repository.findCheerCreatorSnapshots(windowStart, windowEndExclusive, limit = 10)
|
||||||
val communitySnapshots = repository.findPopularCommunitySnapshots(windowStart, snapshotAt, limit = 10)
|
val communitySnapshots = repository.findPopularCommunitySnapshots(windowStart, snapshotAt, limit = 10)
|
||||||
|
|
||||||
assertEquals(emptyList<Any>(), cheerSnapshots)
|
assertEquals(emptyList<Any>(), cheerSnapshots)
|
||||||
@@ -1075,6 +1081,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
fun shouldUseActualDebutAtInsteadOfMemberCreatedAtForCreatorSnapshots() {
|
fun shouldUseActualDebutAtInsteadOfMemberCreatedAtForCreatorSnapshots() {
|
||||||
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
|
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
|
||||||
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
|
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
|
||||||
|
val windowEndExclusive = snapshotAt.plusSeconds(1)
|
||||||
val memberCreatedAt = LocalDateTime.of(2026, 1, 1, 0, 0)
|
val memberCreatedAt = LocalDateTime.of(2026, 1, 1, 0, 0)
|
||||||
val firstLiveAt = LocalDateTime.of(2026, 5, 10, 12, 0)
|
val firstLiveAt = LocalDateTime.of(2026, 5, 10, 12, 0)
|
||||||
val firstContentAt = LocalDateTime.of(2026, 5, 20, 12, 0)
|
val firstContentAt = LocalDateTime.of(2026, 5, 20, 12, 0)
|
||||||
@@ -1098,14 +1105,14 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
updateCreatedAt("CreatorCommunityLike", like.id!!, windowStart.plusDays(1))
|
updateCreatedAt("CreatorCommunityLike", like.id!!, windowStart.plusDays(1))
|
||||||
flushAndClear()
|
flushAndClear()
|
||||||
|
|
||||||
val cheerSnapshot = repository.findCheerCreatorSnapshots(windowStart, snapshotAt, limit = 10).single()
|
val cheerSnapshot = repository.findCheerCreatorSnapshots(windowStart, windowEndExclusive, limit = 10).single()
|
||||||
val communitySnapshot = repository.findPopularCommunitySnapshots(windowStart, snapshotAt, limit = 10).single()
|
val communitySnapshot = repository.findPopularCommunitySnapshots(windowStart, snapshotAt, limit = 10).single()
|
||||||
|
|
||||||
val expectedCheerScore = scorePolicy.calculateCheerScore(
|
val expectedCheerScore = scorePolicy.calculateCheerScore(
|
||||||
donationAmount = 100,
|
donationAmount = 100,
|
||||||
fanTalkCount = 0,
|
fanTalkCount = 0,
|
||||||
donationCount = 1,
|
donationCount = 1,
|
||||||
newBoost = scorePolicy.calculateCreatorNewBoost(firstLiveAt, snapshotAt)
|
newBoost = scorePolicy.calculateCheerCreatorNewBoost(firstLiveAt, snapshotAt)
|
||||||
)
|
)
|
||||||
val expectedCommunityScore = scorePolicy.calculateCommunityScore(
|
val expectedCommunityScore = scorePolicy.calculateCommunityScore(
|
||||||
likeCount = 1,
|
likeCount = 1,
|
||||||
@@ -1114,6 +1121,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
newBoost = scorePolicy.calculateCreatorNewBoost(firstLiveAt, snapshotAt)
|
newBoost = scorePolicy.calculateCreatorNewBoost(firstLiveAt, snapshotAt)
|
||||||
)
|
)
|
||||||
assertEquals(expectedCheerScore, cheerSnapshot.score, 0.0001)
|
assertEquals(expectedCheerScore, cheerSnapshot.score, 0.0001)
|
||||||
|
assertEquals(snapshotAt, cheerSnapshot.snapshotAt)
|
||||||
assertEquals(expectedCommunityScore, communitySnapshot.score, 0.0001)
|
assertEquals(expectedCommunityScore, communitySnapshot.score, 0.0001)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1122,6 +1130,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
fun shouldExcludeBlankChannelNameLiveFromSnapshotDebutAt() {
|
fun shouldExcludeBlankChannelNameLiveFromSnapshotDebutAt() {
|
||||||
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
|
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
|
||||||
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
|
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
|
||||||
|
val windowEndExclusive = snapshotAt.plusSeconds(1)
|
||||||
val blankLiveAt = LocalDateTime.of(2026, 5, 1, 12, 0)
|
val blankLiveAt = LocalDateTime.of(2026, 5, 1, 12, 0)
|
||||||
val contentDebutAt = LocalDateTime.of(2026, 5, 20, 12, 0)
|
val contentDebutAt = LocalDateTime.of(2026, 5, 20, 12, 0)
|
||||||
val creator = saveMember("blank-live-debut-creator", MemberRole.CREATOR)
|
val creator = saveMember("blank-live-debut-creator", MemberRole.CREATOR)
|
||||||
@@ -1143,14 +1152,14 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
updateCreatedAt("CreatorCommunityLike", like.id!!, windowStart.plusDays(1))
|
updateCreatedAt("CreatorCommunityLike", like.id!!, windowStart.plusDays(1))
|
||||||
flushAndClear()
|
flushAndClear()
|
||||||
|
|
||||||
val cheerSnapshot = repository.findCheerCreatorSnapshots(windowStart, snapshotAt, limit = 10).single()
|
val cheerSnapshot = repository.findCheerCreatorSnapshots(windowStart, windowEndExclusive, limit = 10).single()
|
||||||
val communitySnapshot = repository.findPopularCommunitySnapshots(windowStart, snapshotAt, limit = 10).single()
|
val communitySnapshot = repository.findPopularCommunitySnapshots(windowStart, snapshotAt, limit = 10).single()
|
||||||
|
|
||||||
val expectedCheerScore = scorePolicy.calculateCheerScore(
|
val expectedCheerScore = scorePolicy.calculateCheerScore(
|
||||||
donationAmount = 100,
|
donationAmount = 100,
|
||||||
fanTalkCount = 0,
|
fanTalkCount = 0,
|
||||||
donationCount = 1,
|
donationCount = 1,
|
||||||
newBoost = scorePolicy.calculateCreatorNewBoost(contentDebutAt, snapshotAt)
|
newBoost = scorePolicy.calculateCheerCreatorNewBoost(contentDebutAt, snapshotAt)
|
||||||
)
|
)
|
||||||
val expectedCommunityScore = scorePolicy.calculateCommunityScore(
|
val expectedCommunityScore = scorePolicy.calculateCommunityScore(
|
||||||
likeCount = 1,
|
likeCount = 1,
|
||||||
@@ -1159,6 +1168,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
newBoost = scorePolicy.calculateCreatorNewBoost(contentDebutAt, snapshotAt)
|
newBoost = scorePolicy.calculateCreatorNewBoost(contentDebutAt, snapshotAt)
|
||||||
)
|
)
|
||||||
assertEquals(expectedCheerScore, cheerSnapshot.score, 0.0001)
|
assertEquals(expectedCheerScore, cheerSnapshot.score, 0.0001)
|
||||||
|
assertEquals(snapshotAt, cheerSnapshot.snapshotAt)
|
||||||
assertEquals(expectedCommunityScore, communitySnapshot.score, 0.0001)
|
assertEquals(expectedCommunityScore, communitySnapshot.score, 0.0001)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2194,7 +2204,7 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
status: UseCanCalculateStatus,
|
status: UseCanCalculateStatus,
|
||||||
isRefund: Boolean,
|
isRefund: Boolean,
|
||||||
createdAt: LocalDateTime
|
createdAt: LocalDateTime
|
||||||
) {
|
): UseCanCalculate {
|
||||||
val useCan = UseCan(canUsage = usage, can = can, rewardCan = 0, isRefund = isRefund)
|
val useCan = UseCan(canUsage = usage, can = can, rewardCan = 0, isRefund = isRefund)
|
||||||
useCan.member = donor
|
useCan.member = donor
|
||||||
entityManager.persist(useCan)
|
entityManager.persist(useCan)
|
||||||
@@ -2204,6 +2214,26 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
entityManager.persist(calculate)
|
entityManager.persist(calculate)
|
||||||
entityManager.flush()
|
entityManager.flush()
|
||||||
updateCreatedAt("UseCanCalculate", calculate.id!!, createdAt)
|
updateCreatedAt("UseCanCalculate", calculate.id!!, createdAt)
|
||||||
|
return calculate
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveUseCanCalculateForUseCan(
|
||||||
|
useCan: UseCan,
|
||||||
|
creator: Member,
|
||||||
|
can: Int,
|
||||||
|
createdAt: LocalDateTime
|
||||||
|
): UseCanCalculate {
|
||||||
|
val calculate = UseCanCalculate(
|
||||||
|
can = can,
|
||||||
|
paymentGateway = PaymentGateway.PG,
|
||||||
|
status = UseCanCalculateStatus.RECEIVED
|
||||||
|
)
|
||||||
|
calculate.useCan = useCan
|
||||||
|
calculate.recipientCreatorId = creator.id
|
||||||
|
entityManager.persist(calculate)
|
||||||
|
entityManager.flush()
|
||||||
|
updateCreatedAt("UseCanCalculate", calculate.id!!, createdAt)
|
||||||
|
return calculate
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveCreatorCheers(member: Member, creator: Member, isActive: Boolean): CreatorCheers {
|
private fun saveCreatorCheers(member: Member, creator: Member, isActive: Boolean): CreatorCheers {
|
||||||
|
|||||||
Reference in New Issue
Block a user