diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScorePolicy.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScorePolicy.kt index 73cb0e7a..83a65f2d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScorePolicy.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScorePolicy.kt @@ -9,7 +9,13 @@ class RecommendationScorePolicy { } fun calculateAiCharacterNewBoost(createdAt: LocalDateTime, now: LocalDateTime): Double { - return calculateNewBoost(createdAt, now) + val days = ChronoUnit.DAYS.between(createdAt.toLocalDate(), now.toLocalDate()) + return when { + days <= RecommendationScoreSpec.NEW_BOOST_10_DAY_LIMIT -> RecommendationScoreSpec.AI_NEW_BOOST_10_DAYS + days <= RecommendationScoreSpec.NEW_BOOST_20_DAY_LIMIT -> RecommendationScoreSpec.AI_NEW_BOOST_20_DAYS + days <= RecommendationScoreSpec.NEW_BOOST_30_DAY_LIMIT -> RecommendationScoreSpec.AI_NEW_BOOST_30_DAYS + else -> RecommendationScoreSpec.DEFAULT_NEW_BOOST + } } fun calculateDebutCreatorScore( @@ -28,11 +34,13 @@ class RecommendationScorePolicy { fun calculateAiChatScore( recentChatCount: Long, recentActiveUserCount: Long, + followIncreaseCount: Long, newBoost: Double ): Double { return ( - (RecommendationScoreSpec.AI_RECENT_CHAT_WEIGHT * recentChatCount) + - (RecommendationScoreSpec.AI_RECENT_ACTIVE_USER_WEIGHT * recentActiveUserCount) + (RecommendationScoreSpec.AI_CHAT_WEIGHT * recentChatCount) + + (RecommendationScoreSpec.AI_ACTIVE_USER_WEIGHT * recentActiveUserCount) + + (RecommendationScoreSpec.AI_FOLLOW_INCREASE_WEIGHT * followIncreaseCount) ) * newBoost } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScoreSpec.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScoreSpec.kt index 5a93090c..82a0d98b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScoreSpec.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScoreSpec.kt @@ -9,8 +9,9 @@ object RecommendationScoreSpec { const val DEBUT_CONTENT_ACTIVITY_WEIGHT = 0.3 const val DEBUT_COMMUNICATION_WEIGHT = 0.2 - const val AI_RECENT_CHAT_WEIGHT = 0.45 - const val AI_RECENT_ACTIVE_USER_WEIGHT = 0.35 + const val AI_CHAT_WEIGHT = 0.45 + const val AI_ACTIVE_USER_WEIGHT = 0.35 + const val AI_FOLLOW_INCREASE_WEIGHT = 0.20 const val CHEER_DONATION_AMOUNT_WEIGHT = 0.6 const val CHEER_FAN_TALK_WEIGHT = 0.3 @@ -23,5 +24,8 @@ object RecommendationScoreSpec { const val NEW_BOOST_10_DAYS = 1.5 const val NEW_BOOST_20_DAYS = 1.3 const val NEW_BOOST_30_DAYS = 1.2 + const val AI_NEW_BOOST_10_DAYS = 1.15 + const val AI_NEW_BOOST_20_DAYS = 1.10 + const val AI_NEW_BOOST_30_DAYS = 1.05 const val DEFAULT_NEW_BOOST = 1.0 } diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScorePolicyTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScorePolicyTest.kt index ffeee339..883a8f41 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScorePolicyTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScorePolicyTest.kt @@ -27,10 +27,20 @@ class RecommendationScorePolicyTest { fun shouldApplyAiCharacterNewBoostByCreatedDays() { val now = LocalDateTime.of(2026, 5, 30, 12, 0) - assertEquals(1.5, policy.calculateAiCharacterNewBoost(now.minusDays(10), now), 0.0001) - assertEquals(1.3, policy.calculateAiCharacterNewBoost(now.minusDays(20), now), 0.0001) - assertEquals(1.2, policy.calculateAiCharacterNewBoost(now.minusDays(30), now), 0.0001) + assertEquals(1.15, RecommendationScoreSpec.AI_NEW_BOOST_10_DAYS, 0.0001) + assertEquals(1.10, RecommendationScoreSpec.AI_NEW_BOOST_20_DAYS, 0.0001) + assertEquals(1.05, RecommendationScoreSpec.AI_NEW_BOOST_30_DAYS, 0.0001) + assertEquals(1.15, policy.calculateAiCharacterNewBoost(now, now), 0.0001) + assertEquals(1.15, policy.calculateAiCharacterNewBoost(now.minusDays(10), now), 0.0001) + assertEquals(1.10, policy.calculateAiCharacterNewBoost(now.minusDays(11), now), 0.0001) + assertEquals(1.10, policy.calculateAiCharacterNewBoost(now.minusDays(20), now), 0.0001) + assertEquals(1.05, policy.calculateAiCharacterNewBoost(now.minusDays(21), now), 0.0001) + assertEquals(1.05, policy.calculateAiCharacterNewBoost(now.minusDays(30), now), 0.0001) assertEquals(1.0, policy.calculateAiCharacterNewBoost(now.minusDays(31), now), 0.0001) + + assertEquals(1.5, policy.calculateCreatorNewBoost(now.minusDays(10), now), 0.0001) + assertEquals(1.3, policy.calculateCreatorNewBoost(now.minusDays(20), now), 0.0001) + assertEquals(1.2, policy.calculateCreatorNewBoost(now.minusDays(30), now), 0.0001) } @Test @@ -51,18 +61,18 @@ class RecommendationScorePolicyTest { } @Test - @DisplayName("AI 채팅 추천 점수는 이번 스프린트에서 팔로우 증가량을 제외한다") + @DisplayName("AI 채팅 추천 점수는 AI 발화 수, 활성 사용자 수, 팔로우 증가량 가중치와 신규 부스트를 적용한다") fun shouldCalculateAiChatScore() { - assertEquals(0.45, RecommendationScoreSpec.AI_RECENT_CHAT_WEIGHT, 0.0001) - assertEquals(0.35, RecommendationScoreSpec.AI_RECENT_ACTIVE_USER_WEIGHT, 0.0001) + assertEquals(0.45, RecommendationScoreSpec.AI_CHAT_WEIGHT, 0.0001) + assertEquals(0.35, RecommendationScoreSpec.AI_ACTIVE_USER_WEIGHT, 0.0001) + assertEquals(0.20, RecommendationScoreSpec.AI_FOLLOW_INCREASE_WEIGHT, 0.0001) - val score = policy.calculateAiChatScore( - recentChatCount = 100, - recentActiveUserCount = 20, - newBoost = 1.3 - ) - - assertEquals(67.6, score, 0.0001) + assertEquals(0.0, policy.calculateAiChatScore(0, 0, 0, 1.0), 0.0001) + assertEquals(4.5, policy.calculateAiChatScore(10, 0, 0, 1.0), 0.0001) + assertEquals(3.5, policy.calculateAiChatScore(0, 10, 0, 1.0), 0.0001) + assertEquals(2.0, policy.calculateAiChatScore(0, 0, 10, 1.0), 0.0001) + assertEquals(10.0, policy.calculateAiChatScore(10, 10, 10, 1.0), 0.0001) + assertEquals(11.5, policy.calculateAiChatScore(10, 10, 10, 1.15), 0.0001) } @Test