feat(recommend): 홈 추천 스냅샷 집계 쿼리를 추가한다

This commit is contained in:
2026-05-31 00:57:46 +09:00
parent 2edd486524
commit 58e59c5cb4
4 changed files with 923 additions and 0 deletions

View File

@@ -0,0 +1,626 @@
package kr.co.vividnext.sodalive.v2.recommend.adapter.out.persistence
import kr.co.vividnext.sodalive.can.payment.PaymentGateway
import kr.co.vividnext.sodalive.can.use.CanUsage
import kr.co.vividnext.sodalive.can.use.UseCan
import kr.co.vividnext.sodalive.can.use.UseCanCalculate
import kr.co.vividnext.sodalive.can.use.UseCanCalculateStatus
import kr.co.vividnext.sodalive.chat.character.ChatCharacter
import kr.co.vividnext.sodalive.chat.room.ChatMessage
import kr.co.vividnext.sodalive.chat.room.ChatParticipant
import kr.co.vividnext.sodalive.chat.room.ChatRoom
import kr.co.vividnext.sodalive.chat.room.ParticipantType
import kr.co.vividnext.sodalive.configs.QueryDslConfig
import kr.co.vividnext.sodalive.content.AudioContent
import kr.co.vividnext.sodalive.content.theme.AudioContentTheme
import kr.co.vividnext.sodalive.explorer.profile.CreatorCheers
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunity
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.CreatorCommunityComment
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.CreatorCommunityLike
import kr.co.vividnext.sodalive.live.room.LiveRoom
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.MemberRole
import kr.co.vividnext.sodalive.member.following.CreatorFollowing
import kr.co.vividnext.sodalive.v2.recommend.domain.RecommendationScorePolicy
import kr.co.vividnext.sodalive.v2.recommend.domain.RecommendedSectionType
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.context.annotation.Import
import java.time.LocalDateTime
import javax.persistence.EntityManager
@DataJpaTest(
properties = [
"spring.cache.type=none",
"spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;NON_KEYWORDS=VALUE"
]
)
@Import(QueryDslConfig::class)
class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
private val entityManager: EntityManager
) {
private val repository = DefaultHomeRecommendationQueryRepository(entityManager)
private val scorePolicy = RecommendationScorePolicy()
@Test
@DisplayName("AI 캐릭터 스냅샷은 AI 발화 수와 중복 없는 활성 사용자 수를 집계하고 팔로우 증가량은 제외한다")
fun shouldFindAiCharacterSnapshotsWithoutFollowIncrease() {
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
val user1 = saveMember("ai-user-1", MemberRole.USER)
val user2 = saveMember("ai-user-2", MemberRole.USER)
val character = saveCharacter("character-1", isActive = true)
val inactiveCharacter = saveCharacter("character-2", isActive = false)
val room = saveChatRoom("room-1")
val otherRoom = saveChatRoom("room-2")
val userParticipant1 = saveParticipant(room, ParticipantType.USER, member = user1)
val userParticipant2 = saveParticipant(room, ParticipantType.USER, member = user2)
val characterParticipant = saveParticipant(room, ParticipantType.CHARACTER, character = character)
val inactiveCharacterUser = saveParticipant(otherRoom, ParticipantType.USER, member = user1)
saveParticipant(otherRoom, ParticipantType.CHARACTER, character = inactiveCharacter)
val message1 = saveMessage(room, userParticipant1, "message-1", isActive = true)
val message2 = saveMessage(room, userParticipant1, "message-2", isActive = true)
val message3 = saveMessage(room, userParticipant2, "message-3", isActive = true)
val characterMessage1 = saveMessage(room, characterParticipant, "character-message-1", isActive = true)
val characterMessage2 = saveMessage(room, characterParticipant, "character-message-2", isActive = true)
val inactiveMessage = saveMessage(room, characterParticipant, "message-4", isActive = false)
val oldMessage = saveMessage(room, characterParticipant, "message-5", isActive = true)
val inactiveCharacterMessage = saveMessage(otherRoom, inactiveCharacterUser, "message-6", isActive = true)
updateCreatedAt("ChatMessage", message1.id!!, windowStart.plusDays(1))
updateCreatedAt("ChatMessage", message2.id!!, windowStart.plusDays(2))
updateCreatedAt("ChatMessage", message3.id!!, snapshotAt)
updateCreatedAt("ChatMessage", characterMessage1.id!!, windowStart.plusDays(1))
updateCreatedAt("ChatMessage", characterMessage2.id!!, snapshotAt)
updateCreatedAt("ChatMessage", inactiveMessage.id!!, windowStart.plusDays(3))
updateCreatedAt("ChatMessage", oldMessage.id!!, windowStart.minusSeconds(1))
updateCreatedAt("ChatMessage", inactiveCharacterMessage.id!!, windowStart.plusDays(1))
updateCreatedAt("ChatCharacter", character.id!!, LocalDateTime.of(2026, 5, 20, 12, 0))
flushAndClear()
val snapshots = repository.findAiCharacterSnapshots(windowStart, snapshotAt, limit = 10)
val expectedScore = scorePolicy.calculateAiChatScore(
recentChatCount = 2,
recentActiveUserCount = 2,
newBoost = scorePolicy.calculateAiCharacterNewBoost(LocalDateTime.of(2026, 5, 20, 12, 0), snapshotAt)
)
assertEquals(1, snapshots.size)
assertEquals(RecommendedSectionType.AI_CHARACTER, snapshots.single().sectionType)
assertEquals(character.id, snapshots.single().targetId)
assertEquals(expectedScore, snapshots.single().score, 0.0001)
assertEquals(snapshotAt, snapshots.single().snapshotAt)
}
@Test
@DisplayName("AI 캐릭터 스냅샷은 DB에서 최종 점수를 계산한 뒤 정렬하고 limit을 적용한다")
fun shouldFindAiCharacterSnapshotsWithDbScoreOrderAndLimit() {
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
val user = saveMember("ai-score-user", MemberRole.USER)
val oldHighActivityCharacter = saveCharacter("old-high-activity", isActive = true)
val newLowerActivityCharacter = saveCharacter("new-lower-activity", isActive = true)
val oldRoom = saveChatRoom("ai-score-old-room")
val newRoom = saveChatRoom("ai-score-new-room")
val oldUserParticipant = saveParticipant(oldRoom, ParticipantType.USER, member = user)
val oldCharacterParticipant = saveParticipant(oldRoom, ParticipantType.CHARACTER, character = oldHighActivityCharacter)
val newUserParticipant = saveParticipant(newRoom, ParticipantType.USER, member = user)
val newCharacterParticipant = saveParticipant(newRoom, ParticipantType.CHARACTER, character = newLowerActivityCharacter)
repeat(3) { index ->
updateCreatedAt(
"ChatMessage",
saveMessage(oldRoom, oldCharacterParticipant, "old-character-$index", true).id!!,
windowStart.plusDays(1)
)
}
updateCreatedAt(
"ChatMessage",
saveMessage(oldRoom, oldUserParticipant, "old-user", true).id!!,
windowStart.plusDays(1)
)
repeat(2) { index ->
updateCreatedAt(
"ChatMessage",
saveMessage(newRoom, newCharacterParticipant, "new-character-$index", true).id!!,
windowStart.plusDays(1)
)
}
updateCreatedAt("ChatMessage", saveMessage(newRoom, newUserParticipant, "new-user", true).id!!, windowStart.plusDays(1))
updateCreatedAt("ChatCharacter", oldHighActivityCharacter.id!!, LocalDateTime.of(2026, 4, 1, 0, 0))
updateCreatedAt("ChatCharacter", newLowerActivityCharacter.id!!, LocalDateTime.of(2026, 5, 20, 0, 0))
flushAndClear()
val snapshots = repository.findAiCharacterSnapshots(windowStart, snapshotAt, limit = 1)
val expectedScore = scorePolicy.calculateAiChatScore(
recentChatCount = 2,
recentActiveUserCount = 1,
newBoost = scorePolicy.calculateAiCharacterNewBoost(LocalDateTime.of(2026, 5, 20, 0, 0), snapshotAt)
)
assertEquals(1, snapshots.size)
assertEquals(RecommendedSectionType.AI_CHARACTER, snapshots.single().sectionType)
assertEquals(newLowerActivityCharacter.id, snapshots.single().targetId)
assertEquals(expectedScore, snapshots.single().score, 0.0001)
assertEquals(snapshotAt, snapshots.single().snapshotAt)
}
@Test
@DisplayName("최근 응원 스냅샷은 CHANNEL_DONATION 후원 금액과 후원 수만 집계한다")
fun shouldFindCheerCreatorSnapshotsWithChannelDonationOnly() {
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
val creator = saveMember("cheer-creator", MemberRole.CREATOR)
val donor = saveMember("cheer-donor", MemberRole.USER)
saveAudioContent(creator, LocalDateTime.of(2026, 5, 20, 12, 0), isActive = true)
saveLiveRoom(creator, LocalDateTime.of(2026, 5, 10, 12, 0), channelName = "cheer-channel")
saveUseCanCalculate(
donor,
creator,
CanUsage.CHANNEL_DONATION,
can = 100,
status = UseCanCalculateStatus.RECEIVED,
isRefund = false,
createdAt = windowStart.plusDays(1)
)
saveUseCanCalculate(
donor,
creator,
CanUsage.CHANNEL_DONATION,
can = 50,
status = UseCanCalculateStatus.RECEIVED,
isRefund = false,
createdAt = snapshotAt
)
saveUseCanCalculate(
donor,
creator,
CanUsage.DONATION,
can = 1_000,
status = UseCanCalculateStatus.RECEIVED,
isRefund = false,
createdAt = windowStart.plusDays(1)
)
saveUseCanCalculate(
donor,
creator,
CanUsage.SPIN_ROULETTE,
can = 1_000,
status = UseCanCalculateStatus.RECEIVED,
isRefund = false,
createdAt = windowStart.plusDays(1)
)
saveUseCanCalculate(
donor,
creator,
CanUsage.LIVE,
can = 1_000,
status = UseCanCalculateStatus.RECEIVED,
isRefund = false,
createdAt = windowStart.plusDays(1)
)
saveUseCanCalculate(
donor,
creator,
CanUsage.CHANNEL_DONATION,
can = 1_000,
status = UseCanCalculateStatus.REFUND,
isRefund = false,
createdAt = windowStart.plusDays(1)
)
saveUseCanCalculate(
donor,
creator,
CanUsage.CHANNEL_DONATION,
can = 1_000,
status = UseCanCalculateStatus.RECEIVED,
isRefund = true,
createdAt = windowStart.plusDays(1)
)
saveUseCanCalculate(
donor,
creator,
CanUsage.CHANNEL_DONATION,
can = 1_000,
status = UseCanCalculateStatus.RECEIVED,
isRefund = false,
createdAt = windowStart.minusSeconds(1)
)
saveUseCanCalculate(
donor,
null,
CanUsage.CHANNEL_DONATION,
can = 1_000,
status = UseCanCalculateStatus.RECEIVED,
isRefund = false,
createdAt = windowStart.plusDays(1)
)
val cheer1 = saveCreatorCheers(donor, creator, isActive = true)
val cheer2 = saveCreatorCheers(donor, creator, isActive = true)
val inactiveCheer = saveCreatorCheers(donor, creator, isActive = false)
updateCreatedAt("CreatorCheers", cheer1.id!!, windowStart.plusDays(1))
updateCreatedAt("CreatorCheers", cheer2.id!!, snapshotAt)
updateCreatedAt("CreatorCheers", inactiveCheer.id!!, windowStart.plusDays(1))
updateCreatedAt("Member", creator.id!!, LocalDateTime.of(2026, 5, 10, 12, 0))
flushAndClear()
val snapshots = repository.findCheerCreatorSnapshots(windowStart, snapshotAt, limit = 10)
val expectedScore = scorePolicy.calculateCheerScore(
donationAmount = 150,
fanTalkCount = 2,
donationCount = 2,
newBoost = scorePolicy.calculateCreatorNewBoost(LocalDateTime.of(2026, 5, 10, 12, 0), snapshotAt)
)
assertEquals(1, snapshots.size)
assertEquals(RecommendedSectionType.CHEER_CREATOR, snapshots.single().sectionType)
assertEquals(creator.id, snapshots.single().targetId)
assertEquals(expectedScore, snapshots.single().score, 0.0001)
}
@Test
@DisplayName("최근 응원 스냅샷은 DB에서 최종 점수를 계산한 뒤 정렬하고 limit을 적용한다")
fun shouldFindCheerCreatorSnapshotsWithDbScoreOrderAndLimit() {
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
val donor = saveMember("cheer-score-donor", MemberRole.USER)
val oldCreator = saveMember("old-cheer-score-creator", MemberRole.CREATOR)
val newCreator = saveMember("new-cheer-score-creator", MemberRole.CREATOR)
saveLiveRoom(oldCreator, LocalDateTime.of(2026, 4, 1, 0, 0), channelName = "old-cheer")
saveLiveRoom(newCreator, LocalDateTime.of(2026, 5, 20, 0, 0), channelName = "new-cheer")
saveUseCanCalculate(
donor,
oldCreator,
CanUsage.CHANNEL_DONATION,
2,
UseCanCalculateStatus.RECEIVED,
false,
windowStart.plusDays(1)
)
saveUseCanCalculate(
donor,
newCreator,
CanUsage.CHANNEL_DONATION,
2,
UseCanCalculateStatus.RECEIVED,
false,
windowStart.plusDays(1)
)
flushAndClear()
val snapshots = repository.findCheerCreatorSnapshots(windowStart, snapshotAt, limit = 1)
val expectedScore = scorePolicy.calculateCheerScore(
donationAmount = 2,
fanTalkCount = 0,
donationCount = 1,
newBoost = scorePolicy.calculateCreatorNewBoost(LocalDateTime.of(2026, 5, 20, 0, 0), snapshotAt)
)
assertEquals(1, snapshots.size)
assertEquals(RecommendedSectionType.CHEER_CREATOR, snapshots.single().sectionType)
assertEquals(newCreator.id, snapshots.single().targetId)
assertEquals(expectedScore, snapshots.single().score, 0.0001)
}
@Test
@DisplayName("인기 커뮤니티 스냅샷은 좋아요 댓글 팔로워 수를 distinct로 집계하고 댓글 불가 게시글은 댓글 수 0으로 계산한다")
fun shouldFindPopularCommunitySnapshotsWithDistinctCounts() {
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
val creator = saveMember("community-creator", MemberRole.CREATOR)
val member1 = saveMember("community-member-1", MemberRole.USER)
val member2 = saveMember("community-member-2", MemberRole.USER)
saveAudioContent(creator, LocalDateTime.of(2026, 5, 5, 12, 0), isActive = true)
saveLiveRoom(creator, LocalDateTime.of(2026, 4, 1, 12, 0), channelName = "community-channel")
val post = saveCommunity(creator, isCommentAvailable = true)
val commentDisabledPost = saveCommunity(creator, isCommentAvailable = false)
val like1 = saveCommunityLike(member1, post, isActive = true)
val like2 = saveCommunityLike(member2, post, isActive = true)
val inactiveLike = saveCommunityLike(member1, post, isActive = false)
val comment1 = saveCommunityComment(member1, post, isActive = true)
val comment2 = saveCommunityComment(member2, post, isActive = true)
val inactiveComment = saveCommunityComment(member1, post, isActive = false)
saveFollowing(member1, creator, isActive = true)
saveFollowing(member2, creator, isActive = true)
saveFollowing(member1, creator, isActive = false)
val disabledComment = saveCommunityComment(member1, commentDisabledPost, isActive = true)
updateCreatedAt("CreatorCommunity", post.id!!, windowStart.plusDays(1))
updateCreatedAt("CreatorCommunity", commentDisabledPost.id!!, windowStart.plusDays(1))
updateCreatedAt("CreatorCommunityLike", like1.id!!, windowStart.plusDays(1))
updateCreatedAt("CreatorCommunityLike", like2.id!!, snapshotAt)
updateCreatedAt("CreatorCommunityLike", inactiveLike.id!!, windowStart.plusDays(1))
updateCreatedAt("CreatorCommunityComment", comment1.id!!, windowStart.plusDays(1))
updateCreatedAt("CreatorCommunityComment", comment2.id!!, snapshotAt)
updateCreatedAt("CreatorCommunityComment", inactiveComment.id!!, windowStart.plusDays(1))
updateCreatedAt("CreatorCommunityComment", disabledComment.id!!, windowStart.plusDays(1))
updateCreatedAt("Member", creator.id!!, LocalDateTime.of(2026, 4, 1, 12, 0))
flushAndClear()
val snapshots = repository.findPopularCommunitySnapshots(windowStart, snapshotAt, limit = 10)
.associateBy { it.targetId }
val expectedPostScore = scorePolicy.calculateCommunityScore(
likeCount = 2,
commentCount = 2,
followerCount = 2,
newBoost = scorePolicy.calculateCreatorNewBoost(LocalDateTime.of(2026, 4, 1, 12, 0), snapshotAt)
)
val expectedCommentDisabledScore = scorePolicy.calculateCommunityScore(
likeCount = 0,
commentCount = 0,
followerCount = 2,
newBoost = scorePolicy.calculateCreatorNewBoost(LocalDateTime.of(2026, 4, 1, 12, 0), snapshotAt)
)
assertEquals(expectedPostScore, snapshots[post.id]!!.score, 0.0001)
assertEquals(expectedCommentDisabledScore, snapshots[commentDisabledPost.id]!!.score, 0.0001)
}
@Test
@DisplayName("인기 커뮤니티 스냅샷은 DB에서 최종 점수를 계산한 뒤 정렬하고 limit을 적용한다")
fun shouldFindPopularCommunitySnapshotsWithDbScoreOrderAndLimit() {
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
val liker = saveMember("community-score-liker", MemberRole.USER)
val oldCreator = saveMember("old-community-score-creator", MemberRole.CREATOR)
val newCreator = saveMember("new-community-score-creator", MemberRole.CREATOR)
saveLiveRoom(oldCreator, LocalDateTime.of(2026, 4, 1, 0, 0), channelName = "old-community")
saveLiveRoom(newCreator, LocalDateTime.of(2026, 5, 20, 0, 0), channelName = "new-community")
val oldPost = saveCommunity(oldCreator, isCommentAvailable = true)
val newPost = saveCommunity(newCreator, isCommentAvailable = true)
val oldLike = saveCommunityLike(liker, oldPost, isActive = true)
val newLike = saveCommunityLike(liker, newPost, isActive = true)
updateCreatedAt("CreatorCommunity", oldPost.id!!, windowStart.plusDays(1))
updateCreatedAt("CreatorCommunity", newPost.id!!, windowStart.plusDays(1))
updateCreatedAt("CreatorCommunityLike", oldLike.id!!, windowStart.plusDays(1))
updateCreatedAt("CreatorCommunityLike", newLike.id!!, windowStart.plusDays(1))
flushAndClear()
val snapshots = repository.findPopularCommunitySnapshots(windowStart, snapshotAt, limit = 1)
val expectedScore = scorePolicy.calculateCommunityScore(
likeCount = 1,
commentCount = 0,
followerCount = 0,
newBoost = scorePolicy.calculateCreatorNewBoost(LocalDateTime.of(2026, 5, 20, 0, 0), snapshotAt)
)
assertEquals(1, snapshots.size)
assertEquals(RecommendedSectionType.POPULAR_COMMUNITY, snapshots.single().sectionType)
assertEquals(newPost.id, snapshots.single().targetId)
assertEquals(expectedScore, snapshots.single().score, 0.0001)
}
@Test
@DisplayName("실제 데뷔일이 없는 크리에이터는 최근 응원과 인기 커뮤니티 스냅샷에서 제외한다")
fun shouldExcludeCreatorSnapshotsWithoutActualDebutAt() {
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
val creator = saveMember("creator-without-debut", MemberRole.CREATOR)
val donor = saveMember("donor-without-debut", MemberRole.USER)
val community = saveCommunity(creator, isCommentAvailable = true)
val like = saveCommunityLike(donor, community, isActive = true)
saveUseCanCalculate(
donor,
creator,
CanUsage.CHANNEL_DONATION,
can = 100,
status = UseCanCalculateStatus.RECEIVED,
isRefund = false,
createdAt = windowStart.plusDays(1)
)
updateCreatedAt("CreatorCommunity", community.id!!, windowStart.plusDays(1))
updateCreatedAt("CreatorCommunityLike", like.id!!, windowStart.plusDays(1))
flushAndClear()
val cheerSnapshots = repository.findCheerCreatorSnapshots(windowStart, snapshotAt, limit = 10)
val communitySnapshots = repository.findPopularCommunitySnapshots(windowStart, snapshotAt, limit = 10)
assertEquals(emptyList<Any>(), cheerSnapshots)
assertEquals(emptyList<Any>(), communitySnapshots)
}
@Test
@DisplayName("최근 응원과 인기 커뮤니티 스냅샷은 Member.createdAt이 아니라 실제 데뷔일을 사용한다")
fun shouldUseActualDebutAtInsteadOfMemberCreatedAtForCreatorSnapshots() {
val windowStart = LocalDateTime.of(2026, 5, 23, 0, 0)
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
val memberCreatedAt = LocalDateTime.of(2026, 1, 1, 0, 0)
val firstLiveAt = LocalDateTime.of(2026, 5, 10, 12, 0)
val firstContentAt = LocalDateTime.of(2026, 5, 20, 12, 0)
val creator = saveMember("actual-debut-creator", MemberRole.CREATOR)
val donor = saveMember("actual-debut-donor", MemberRole.USER)
saveLiveRoom(creator, firstLiveAt, channelName = "actual-debut-channel")
saveAudioContent(creator, firstContentAt, isActive = true)
saveUseCanCalculate(
donor,
creator,
CanUsage.CHANNEL_DONATION,
can = 100,
status = UseCanCalculateStatus.RECEIVED,
isRefund = false,
createdAt = windowStart.plusDays(1)
)
val community = saveCommunity(creator, isCommentAvailable = true)
val like = saveCommunityLike(donor, community, isActive = true)
updateCreatedAt("Member", creator.id!!, memberCreatedAt)
updateCreatedAt("CreatorCommunity", community.id!!, windowStart.plusDays(1))
updateCreatedAt("CreatorCommunityLike", like.id!!, windowStart.plusDays(1))
flushAndClear()
val cheerSnapshot = repository.findCheerCreatorSnapshots(windowStart, snapshotAt, limit = 10).single()
val communitySnapshot = repository.findPopularCommunitySnapshots(windowStart, snapshotAt, limit = 10).single()
val expectedCheerScore = scorePolicy.calculateCheerScore(
donationAmount = 100,
fanTalkCount = 0,
donationCount = 1,
newBoost = scorePolicy.calculateCreatorNewBoost(firstLiveAt, snapshotAt)
)
val expectedCommunityScore = scorePolicy.calculateCommunityScore(
likeCount = 1,
commentCount = 0,
followerCount = 0,
newBoost = scorePolicy.calculateCreatorNewBoost(firstLiveAt, snapshotAt)
)
assertEquals(expectedCheerScore, cheerSnapshot.score, 0.0001)
assertEquals(expectedCommunityScore, communitySnapshot.score, 0.0001)
}
private fun saveMember(nickname: String, role: MemberRole): Member {
val member = Member(
email = "$nickname@test.com",
password = "password",
nickname = nickname,
role = role
)
entityManager.persist(member)
return member
}
private fun saveCharacter(name: String, isActive: Boolean): ChatCharacter {
val character = ChatCharacter(
characterUUID = "$name-uuid",
name = name,
description = "description",
systemPrompt = "system",
isActive = isActive
)
entityManager.persist(character)
return character
}
private fun saveChatRoom(sessionId: String): ChatRoom {
val room = ChatRoom(sessionId = sessionId, title = sessionId)
entityManager.persist(room)
return room
}
private fun saveParticipant(
room: ChatRoom,
type: ParticipantType,
member: Member? = null,
character: ChatCharacter? = null
): ChatParticipant {
val participant = ChatParticipant(chatRoom = room, participantType = type, member = member, character = character)
entityManager.persist(participant)
return participant
}
private fun saveMessage(room: ChatRoom, participant: ChatParticipant, message: String, isActive: Boolean): ChatMessage {
val chatMessage = ChatMessage(message = message, chatRoom = room, participant = participant, isActive = isActive)
entityManager.persist(chatMessage)
return chatMessage
}
private fun saveUseCanCalculate(
donor: Member,
creator: Member?,
usage: CanUsage,
can: Int,
status: UseCanCalculateStatus,
isRefund: Boolean,
createdAt: LocalDateTime
) {
val useCan = UseCan(canUsage = usage, can = can, rewardCan = 0, isRefund = isRefund)
useCan.member = donor
entityManager.persist(useCan)
val calculate = UseCanCalculate(can = can, paymentGateway = PaymentGateway.PG, status = status)
calculate.useCan = useCan
calculate.recipientCreatorId = creator?.id
entityManager.persist(calculate)
entityManager.flush()
updateCreatedAt("UseCanCalculate", calculate.id!!, createdAt)
}
private fun saveCreatorCheers(member: Member, creator: Member, isActive: Boolean): CreatorCheers {
val cheers = CreatorCheers(cheers = "cheers", languageCode = "ko", isActive = isActive)
cheers.member = member
cheers.creator = creator
entityManager.persist(cheers)
return cheers
}
private fun saveCommunity(creator: Member, isCommentAvailable: Boolean): CreatorCommunity {
val community = CreatorCommunity(
content = "content",
price = 0,
isCommentAvailable = isCommentAvailable,
isAdult = false
)
community.member = creator
entityManager.persist(community)
return community
}
private fun saveAudioContent(creator: Member, releaseDate: LocalDateTime, isActive: Boolean): AudioContent {
val theme = AudioContentTheme(
theme = "theme-${creator.nickname}-$releaseDate",
image = "theme-${creator.nickname}-$releaseDate.png"
)
entityManager.persist(theme)
val content = AudioContent(
title = "content-${creator.nickname}-$releaseDate",
detail = "detail",
languageCode = "ko",
releaseDate = releaseDate
)
content.member = creator
content.theme = theme
content.isActive = isActive
entityManager.persist(content)
return content
}
private fun saveLiveRoom(creator: Member, beginDateTime: LocalDateTime, channelName: String?): LiveRoom {
val room = LiveRoom(
title = "live-${creator.nickname}-$beginDateTime",
notice = "notice",
beginDateTime = beginDateTime,
numberOfPeople = 0,
isAdult = false
)
room.member = creator
room.channelName = channelName
entityManager.persist(room)
return room
}
private fun saveCommunityLike(member: Member, community: CreatorCommunity, isActive: Boolean): CreatorCommunityLike {
val like = CreatorCommunityLike(isActive = isActive)
like.member = member
like.creatorCommunity = community
entityManager.persist(like)
return like
}
private fun saveCommunityComment(member: Member, community: CreatorCommunity, isActive: Boolean): CreatorCommunityComment {
val comment = CreatorCommunityComment(comment = "comment", isActive = isActive)
comment.member = member
comment.creatorCommunity = community
entityManager.persist(comment)
return comment
}
private fun saveFollowing(member: Member, creator: Member, isActive: Boolean): CreatorFollowing {
val following = CreatorFollowing(isActive = isActive)
following.member = member
following.creator = creator
entityManager.persist(following)
return following
}
private fun updateCreatedAt(entityName: String, id: Long, createdAt: LocalDateTime) {
entityManager.createQuery("update $entityName e set e.createdAt = :createdAt where e.id = :id")
.setParameter("createdAt", createdAt)
.setParameter("id", id)
.executeUpdate()
}
private fun flushAndClear() {
entityManager.flush()
entityManager.clear()
}
}