feat(recommend): 홈 장르 추천 크리에이터 중복 보충을 개선한다

This commit is contained in:
2026-06-04 17:23:03 +09:00
parent 81f1bcc4ef
commit 7606796fe3
2 changed files with 175 additions and 7 deletions

View File

@@ -374,7 +374,7 @@ class HomeRecommendationQueryServiceTest {
assertEquals(100L, port.genreCreatorMemberId)
assertEquals(true, port.genreCreatorIncludeAdultGenres)
assertEquals(5, port.genreCreatorGenreLimit)
assertEquals(40, port.genreCreatorCreatorLimit)
assertEquals(8, port.genreCreatorCreatorLimit)
assertEquals(listOf(10L, 11L), recommendations[0].creators.map { it.creatorId })
assertEquals(listOf(12L, 13L), recommendations[1].creators.map { it.creatorId })
}
@@ -429,6 +429,150 @@ class HomeRecommendationQueryServiceTest {
assertEquals(false, recommendations.any { it.creators.isEmpty() })
}
@Test
@DisplayName("장르 기반 크리에이터 추천은 중복 제거 후 랜덤 보충 장르가 8명이 되지 않으면 뒤 후보로 대체한다")
fun shouldBackfillRandomThemesWhenCreatorDeduplicationMakesThemeUnderfilled() {
val sharedCreators = (1L..8L).map { creatorId ->
HomeGenreCreatorRecommendationRecord(
creatorId = creatorId,
creatorNickname = "shared-$creatorId",
creatorProfileImage = null
)
}
val uniqueCreators = (101L..108L).map { creatorId ->
HomeGenreCreatorRecommendationRecord(
creatorId = creatorId,
creatorNickname = "unique-$creatorId",
creatorProfileImage = null
)
}
port.genreCreatorRecommendations = listOf(
HomeGenreCreatorRecommendationGroup(
genreId = 1L,
genreName = "first-random-theme",
creators = sharedCreators
),
HomeGenreCreatorRecommendationGroup(
genreId = 2L,
genreName = "underfilled-after-dedup-theme",
creators = sharedCreators
),
HomeGenreCreatorRecommendationGroup(
genreId = 3L,
genreName = "backfill-random-theme",
creators = uniqueCreators
)
)
val recommendations = service.findGenreCreatorRecommendations(
memberId = null,
includeAdultGenres = false,
genreLimit = 2,
creatorLimit = 8
)
assertEquals(listOf(1L, 3L), recommendations.map { it.genreId })
assertEquals(true, recommendations.all { it.creators.size == 8 })
}
@Test
@DisplayName("장르 기반 크리에이터 추천은 보류된 부분 후보의 크리에이터를 뒤 충분 후보에서 중복 제거하지 않는다")
fun shouldNotConsumeCreatorsFromDeferredPartialFallbackCandidates() {
val firstCreators = (1L..8L).map { creatorId ->
HomeGenreCreatorRecommendationRecord(
creatorId = creatorId,
creatorNickname = "first-$creatorId",
creatorProfileImage = null
)
}
val partialUniqueCreator = HomeGenreCreatorRecommendationRecord(
creatorId = 101L,
creatorNickname = "partial-unique",
creatorProfileImage = null
)
val fullBackfillCreators = (101L..108L).map { creatorId ->
HomeGenreCreatorRecommendationRecord(
creatorId = creatorId,
creatorNickname = "full-$creatorId",
creatorProfileImage = null
)
}
port.genreCreatorRecommendations = listOf(
HomeGenreCreatorRecommendationGroup(
genreId = 1L,
genreName = "first-random-theme",
creators = firstCreators
),
HomeGenreCreatorRecommendationGroup(
genreId = 2L,
genreName = "deferred-partial-theme",
creators = firstCreators.take(7) + partialUniqueCreator
),
HomeGenreCreatorRecommendationGroup(
genreId = 3L,
genreName = "full-backfill-theme",
creators = fullBackfillCreators
)
)
val recommendations = service.findGenreCreatorRecommendations(
memberId = null,
includeAdultGenres = false,
genreLimit = 2,
creatorLimit = 8
)
assertEquals(listOf(1L, 3L), recommendations.map { it.genreId })
assertEquals((101L..108L).toList(), recommendations[1].creators.map { it.creatorId })
}
@Test
@DisplayName("장르 기반 크리에이터 추천은 조회 이력 장르는 중복 제거 후 8명 미만이어도 유지한다")
fun shouldKeepViewedThemeWhenCreatorDeduplicationMakesThemeUnderfilled() {
val sharedCreators = (1L..8L).map { creatorId ->
HomeGenreCreatorRecommendationRecord(
creatorId = creatorId,
creatorNickname = "shared-$creatorId",
creatorProfileImage = null
)
}
val uniqueCreators = (101L..108L).map { creatorId ->
HomeGenreCreatorRecommendationRecord(
creatorId = creatorId,
creatorNickname = "unique-$creatorId",
creatorProfileImage = null
)
}
port.genreCreatorRecommendations = listOf(
HomeGenreCreatorRecommendationGroup(
genreId = 1L,
genreName = "first-random-theme",
creators = sharedCreators
),
HomeGenreCreatorRecommendationGroup(
genreId = 2L,
genreName = "viewed-theme",
creators = sharedCreators + uniqueCreators.first(),
isViewedTheme = true
),
HomeGenreCreatorRecommendationGroup(
genreId = 3L,
genreName = "backfill-random-theme",
creators = uniqueCreators
)
)
val recommendations = service.findGenreCreatorRecommendations(
memberId = 100L,
includeAdultGenres = false,
genreLimit = 2,
creatorLimit = 8
)
assertEquals(listOf(1L, 2L), recommendations.map { it.genreId })
assertEquals(1, recommendations[1].creators.size)
}
private class FakeHomeRecommendationQueryPort : HomeRecommendationQueryPort {
var liveLimit: Int? = null
var liveOffset: Int? = null