From c9e35f2ed400cad87551aa6ed4010d83951ff0eb Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 10 Jul 2026 06:05:27 +0900 Subject: [PATCH] =?UTF-8?q?feat(home):=20=EC=9D=91=EC=9B=90=20=ED=81=AC?= =?UTF-8?q?=EB=A6=AC=EC=97=90=EC=9D=B4=ED=84=B0=20=EC=A0=90=EC=88=98=20?= =?UTF-8?q?=EC=A0=95=EC=B1=85=EC=9D=84=20=EB=B3=80=EA=B2=BD=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/RecommendationScorePolicy.kt | 10 ++++++ .../domain/RecommendationScoreSpec.kt | 5 ++- .../domain/RecommendationScorePolicyTest.kt | 35 ++++++++++++++----- 3 files changed, 41 insertions(+), 9 deletions(-) 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 83a65f2d..42d4b2b4 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 @@ -18,6 +18,16 @@ class RecommendationScorePolicy { } } + fun calculateCheerCreatorNewBoost(debutAt: LocalDateTime, now: LocalDateTime): Double { + val days = ChronoUnit.DAYS.between(debutAt.toLocalDate(), now.toLocalDate()) + return when { + days <= RecommendationScoreSpec.NEW_BOOST_10_DAY_LIMIT -> RecommendationScoreSpec.CHEER_NEW_BOOST_10_DAYS + days <= RecommendationScoreSpec.NEW_BOOST_20_DAY_LIMIT -> RecommendationScoreSpec.CHEER_NEW_BOOST_20_DAYS + days <= RecommendationScoreSpec.NEW_BOOST_30_DAY_LIMIT -> RecommendationScoreSpec.CHEER_NEW_BOOST_30_DAYS + else -> RecommendationScoreSpec.DEFAULT_NEW_BOOST + } + } + fun calculateDebutCreatorScore( followIncrease: Long, contentActivityScore: Long, 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 82a0d98b..e4700dac 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 @@ -13,7 +13,7 @@ object RecommendationScoreSpec { 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_DONATION_AMOUNT_WEIGHT = 0.45 const val CHEER_FAN_TALK_WEIGHT = 0.3 const val CHEER_DONATION_COUNT_WEIGHT = 0.1 @@ -27,5 +27,8 @@ object RecommendationScoreSpec { 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 CHEER_NEW_BOOST_10_DAYS = 1.15 + const val CHEER_NEW_BOOST_20_DAYS = 1.10 + const val CHEER_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 883a8f41..e1389cad 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 @@ -78,18 +78,37 @@ class RecommendationScorePolicyTest { @Test @DisplayName("최근 응원 추천 점수는 후원 금액, 팬 Talk 수, 후원 수에 가중치를 적용한다") fun shouldCalculateCheerScore() { - assertEquals(0.6, RecommendationScoreSpec.CHEER_DONATION_AMOUNT_WEIGHT, 0.0001) + assertEquals(0.45, RecommendationScoreSpec.CHEER_DONATION_AMOUNT_WEIGHT, 0.0001) assertEquals(0.3, RecommendationScoreSpec.CHEER_FAN_TALK_WEIGHT, 0.0001) assertEquals(0.1, RecommendationScoreSpec.CHEER_DONATION_COUNT_WEIGHT, 0.0001) - val score = policy.calculateCheerScore( - donationAmount = 1000, - fanTalkCount = 20, - donationCount = 10, - newBoost = 1.2 - ) + assertEquals(0.0, policy.calculateCheerScore(0, 0, 0, 1.0), 0.0001) + assertEquals(45.0, policy.calculateCheerScore(100, 0, 0, 1.0), 0.0001) + assertEquals(3.0, policy.calculateCheerScore(0, 10, 0, 1.0), 0.0001) + assertEquals(1.0, policy.calculateCheerScore(0, 0, 10, 1.0), 0.0001) + assertEquals(49.0, policy.calculateCheerScore(100, 10, 10, 1.0), 0.0001) + assertEquals(56.35, policy.calculateCheerScore(100, 10, 10, 1.15), 0.0001) + } - assertEquals(728.4, score, 0.0001) + @Test + @DisplayName("최근 응원 크리에이터 신규 부스트는 데뷔일 기준 10일/20일/30일 구간을 적용한다") + fun shouldApplyCheerCreatorNewBoostByDebutDays() { + val now = LocalDateTime.of(2026, 5, 30, 12, 0) + + assertEquals(1.15, RecommendationScoreSpec.CHEER_NEW_BOOST_10_DAYS, 0.0001) + assertEquals(1.10, RecommendationScoreSpec.CHEER_NEW_BOOST_20_DAYS, 0.0001) + assertEquals(1.05, RecommendationScoreSpec.CHEER_NEW_BOOST_30_DAYS, 0.0001) + assertEquals(1.15, policy.calculateCheerCreatorNewBoost(now, now), 0.0001) + assertEquals(1.15, policy.calculateCheerCreatorNewBoost(now.minusDays(10), now), 0.0001) + assertEquals(1.10, policy.calculateCheerCreatorNewBoost(now.minusDays(11), now), 0.0001) + assertEquals(1.10, policy.calculateCheerCreatorNewBoost(now.minusDays(20), now), 0.0001) + assertEquals(1.05, policy.calculateCheerCreatorNewBoost(now.minusDays(21), now), 0.0001) + assertEquals(1.05, policy.calculateCheerCreatorNewBoost(now.minusDays(30), now), 0.0001) + assertEquals(1.0, policy.calculateCheerCreatorNewBoost(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