fix(recommend): 장르 추천에서 요청자를 제외한다
This commit is contained in:
@@ -933,6 +933,7 @@ class DefaultHomeRecommendationQueryRepository(
|
||||
and (:includeAdultGenres = true or c.is_adult = false)
|
||||
and m.is_active = true
|
||||
and m.role = 'CREATOR'
|
||||
and (:memberId is null or m.id <> :memberId)
|
||||
and not exists (
|
||||
select 1
|
||||
from creator_following cf
|
||||
@@ -1009,6 +1010,7 @@ class DefaultHomeRecommendationQueryRepository(
|
||||
and (:includeAdultGenres = true or c.is_adult = false)
|
||||
and m.is_active = true
|
||||
and m.role = 'CREATOR'
|
||||
and (:memberId is null or m.id <> :memberId)
|
||||
and not exists (
|
||||
select 1
|
||||
from creator_following cf
|
||||
@@ -1052,6 +1054,7 @@ class DefaultHomeRecommendationQueryRepository(
|
||||
and (:includeAdultGenres = true or c.is_adult = false)
|
||||
and m.is_active = true
|
||||
and m.role = 'CREATOR'
|
||||
and (:memberId is null or m.id <> :memberId)
|
||||
and not exists (
|
||||
select 1
|
||||
from creator_following cf
|
||||
|
||||
@@ -1427,6 +1427,108 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
||||
assertEquals(listOf(visibleCreator.id), recommendations.single().creators.map { it.creatorId })
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("장르 기반 크리에이터 추천은 요청자 본인만 있는 장르를 후보에서 제외한다")
|
||||
fun shouldExcludeRequesterOnlyGenreFromGenreCreatorRecommendations() {
|
||||
val viewer = saveMember("self-only-viewer", MemberRole.CREATOR)
|
||||
val fallbackCreator = saveMember("self-only-fallback", MemberRole.CREATOR)
|
||||
val selfTheme = saveTheme("self-only-theme")
|
||||
val fallbackTheme = saveTheme("self-only-fallback-theme")
|
||||
val selfContent = saveAudioContent(
|
||||
viewer,
|
||||
LocalDateTime.of(2026, 5, 30, 10, 0),
|
||||
isActive = true,
|
||||
theme = selfTheme
|
||||
)
|
||||
saveAudioContent(
|
||||
fallbackCreator,
|
||||
LocalDateTime.of(2026, 5, 30, 11, 0),
|
||||
isActive = true,
|
||||
theme = fallbackTheme
|
||||
)
|
||||
entityManager.persist(
|
||||
CreatorContentViewHistory(
|
||||
memberId = viewer.id!!,
|
||||
contentId = selfContent.id!!,
|
||||
genreId = 999L,
|
||||
viewedAt = LocalDateTime.of(2026, 5, 31, 10, 0)
|
||||
)
|
||||
)
|
||||
flushAndClear()
|
||||
|
||||
val recommendations = repository.findGenreCreatorRecommendations(
|
||||
memberId = viewer.id!!,
|
||||
includeAdultGenres = false,
|
||||
genreLimit = 1,
|
||||
creatorLimit = 8
|
||||
)
|
||||
|
||||
assertEquals(listOf(fallbackTheme.id), recommendations.map { it.genreId })
|
||||
assertEquals(listOf(fallbackCreator.id), recommendations.single().creators.map { it.creatorId })
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("장르 기반 크리에이터 추천은 요청자 본인을 제외한 뒤 대체 크리에이터로 8명을 채운다")
|
||||
fun shouldBackfillCreatorAfterExcludingRequesterFromGenreCreatorRecommendations() {
|
||||
val viewer = saveMember("self-backfill-viewer", MemberRole.CREATOR)
|
||||
val theme = saveTheme("self-backfill-theme")
|
||||
saveAudioContent(viewer, LocalDateTime.of(2026, 5, 30, 10, 0), isActive = true, theme = theme)
|
||||
val expectedCreators = (0..8).map { index ->
|
||||
saveMember("self-backfill-creator-$index", MemberRole.CREATOR).also { creator ->
|
||||
saveAudioContent(
|
||||
creator,
|
||||
LocalDateTime.of(2026, 5, 30, 11, 0).plusMinutes(index.toLong()),
|
||||
isActive = true,
|
||||
theme = theme
|
||||
)
|
||||
}
|
||||
}
|
||||
flushAndClear()
|
||||
|
||||
val recommendations = repository.findGenreCreatorRecommendations(
|
||||
memberId = viewer.id!!,
|
||||
includeAdultGenres = false,
|
||||
genreLimit = 1,
|
||||
creatorLimit = 8
|
||||
)
|
||||
val creatorIds = recommendations.single().creators.map { it.creatorId }
|
||||
|
||||
assertEquals(8, creatorIds.size)
|
||||
assertEquals(false, creatorIds.contains(viewer.id))
|
||||
assertEquals(true, creatorIds.all { it in expectedCreators.map { creator -> creator.id } })
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("장르 기반 크리에이터 추천은 요청자 본인 제외 후 대체가 없으면 가능한 크리에이터만 응답한다")
|
||||
fun shouldReturnAvailableCreatorsAfterExcludingRequesterFromGenreCreatorRecommendations() {
|
||||
val viewer = saveMember("self-partial-viewer", MemberRole.CREATOR)
|
||||
val theme = saveTheme("self-partial-theme")
|
||||
saveAudioContent(viewer, LocalDateTime.of(2026, 5, 30, 10, 0), isActive = true, theme = theme)
|
||||
val expectedCreators = (0 until 7).map { index ->
|
||||
saveMember("self-partial-creator-$index", MemberRole.CREATOR).also { creator ->
|
||||
saveAudioContent(
|
||||
creator,
|
||||
LocalDateTime.of(2026, 5, 30, 11, 0).plusMinutes(index.toLong()),
|
||||
isActive = true,
|
||||
theme = theme
|
||||
)
|
||||
}
|
||||
}
|
||||
flushAndClear()
|
||||
|
||||
val recommendations = repository.findGenreCreatorRecommendations(
|
||||
memberId = viewer.id!!,
|
||||
includeAdultGenres = false,
|
||||
genreLimit = 1,
|
||||
creatorLimit = 8
|
||||
)
|
||||
val creatorIds = recommendations.single().creators.map { it.creatorId }
|
||||
|
||||
assertEquals(7, creatorIds.size)
|
||||
assertEquals(false, creatorIds.contains(viewer.id))
|
||||
assertEquals(expectedCreators.map { it.id }.toSet(), creatorIds.toSet())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("장르 기반 크리에이터 추천은 series_genre가 아니라 content_theme 기준으로 그룹을 만든다")
|
||||
fun shouldGroupGenreCreatorRecommendationsByContentThemeNotSeriesGenre() {
|
||||
|
||||
@@ -526,6 +526,35 @@ class HomeRecommendationQueryServiceTest {
|
||||
assertEquals((101L..108L).toList(), recommendations[1].creators.map { it.creatorId })
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("장르 기반 크리에이터 추천은 장르의 추천 가능 크리에이터가 8명 미만이면 가능한 만큼 응답한다")
|
||||
fun shouldReturnAvailableCreatorsWhenGenreCreatorCountIsUnderLimit() {
|
||||
val availableCreators = (1L..7L).map { creatorId ->
|
||||
HomeGenreCreatorRecommendationRecord(
|
||||
creatorId = creatorId,
|
||||
creatorNickname = "available-$creatorId",
|
||||
creatorProfileImage = null
|
||||
)
|
||||
}
|
||||
port.genreCreatorRecommendations = listOf(
|
||||
HomeGenreCreatorRecommendationGroup(
|
||||
genreId = 1L,
|
||||
genreName = "partial-theme",
|
||||
creators = availableCreators
|
||||
)
|
||||
)
|
||||
|
||||
val recommendations = service.findGenreCreatorRecommendations(
|
||||
memberId = 100L,
|
||||
includeAdultGenres = false,
|
||||
genreLimit = 5,
|
||||
creatorLimit = 8
|
||||
)
|
||||
|
||||
assertEquals(listOf(1L), recommendations.map { it.genreId })
|
||||
assertEquals((1L..7L).toList(), recommendations.single().creators.map { it.creatorId })
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("장르 기반 크리에이터 추천은 조회 이력 장르는 중복 제거 후 8명 미만이어도 유지한다")
|
||||
fun shouldKeepViewedThemeWhenCreatorDeduplicationMakesThemeUnderfilled() {
|
||||
|
||||
Reference in New Issue
Block a user