From 791ce2b8d353516dae34b630eb8b26c748f3b296 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 19 Jun 2026 21:44:34 +0900 Subject: [PATCH] =?UTF-8?q?fix(creator-channel):=20=EC=98=A4=EB=94=94?= =?UTF-8?q?=EC=98=A4=20=ED=83=AD=20=ED=85=8C=EB=A7=88=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=EC=A1=B0=EA=B1=B4=EC=9D=84=20=EC=A0=81=EC=9A=A9=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...faultCreatorChannelAudioQueryRepository.kt | 15 ++++++--- .../port/out/CreatorChannelAudioQueryPort.kt | 7 +++- ...tCreatorChannelAudioQueryRepositoryTest.kt | 33 ++++++++++++++++++- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/audio/adapter/out/persistence/DefaultCreatorChannelAudioQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/audio/adapter/out/persistence/DefaultCreatorChannelAudioQueryRepository.kt index 9d8e9b34..9e96259e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/audio/adapter/out/persistence/DefaultCreatorChannelAudioQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/audio/adapter/out/persistence/DefaultCreatorChannelAudioQueryRepository.kt @@ -69,17 +69,24 @@ class DefaultCreatorChannelAudioQueryRepository( .fetchFirst() } - override fun findAudioThemes(locale: String): List { + override fun findAudioThemes( + creatorId: Long, + now: LocalDateTime, + canViewAdultContent: Boolean, + locale: String + ): List { val themeTranslation = QContentThemeTranslation("audioThemeTranslation") return queryFactory - .select(audioContentTheme.id, audioContentTheme.theme, themeTranslation.theme) - .from(audioContentTheme) + .select(audioContentTheme.id, audioContentTheme.theme, themeTranslation.theme, audioContentTheme.orders) + .distinct() + .from(audioContent) + .innerJoin(audioContent.theme, audioContentTheme) .leftJoin(themeTranslation) .on( themeTranslation.contentThemeId.eq(audioContentTheme.id), themeTranslation.locale.eq(locale) ) - .where(audioContentTheme.isActive.isTrue) + .where(audioContentCondition(creatorId, themeId = null, now, canViewAdultContent)) .orderBy(audioContentTheme.orders.asc(), audioContentTheme.id.asc()) .fetch() .map { diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/audio/port/out/CreatorChannelAudioQueryPort.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/audio/port/out/CreatorChannelAudioQueryPort.kt index 26394421..dfa848b1 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/audio/port/out/CreatorChannelAudioQueryPort.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/audio/port/out/CreatorChannelAudioQueryPort.kt @@ -11,7 +11,12 @@ interface CreatorChannelAudioQueryPort { fun findActiveThemeId(themeId: Long): Long? - fun findAudioThemes(locale: String): List + fun findAudioThemes( + creatorId: Long, + now: LocalDateTime, + canViewAdultContent: Boolean, + locale: String + ): List fun countAudioContents( creatorId: Long, diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/audio/adapter/out/persistence/DefaultCreatorChannelAudioQueryRepositoryTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/audio/adapter/out/persistence/DefaultCreatorChannelAudioQueryRepositoryTest.kt index 44697d9b..7cb2ba6e 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/audio/adapter/out/persistence/DefaultCreatorChannelAudioQueryRepositoryTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/audio/adapter/out/persistence/DefaultCreatorChannelAudioQueryRepositoryTest.kt @@ -43,19 +43,25 @@ class DefaultCreatorChannelAudioQueryRepositoryTest @Autowired constructor( @Test @DisplayName("크리에이터, 차단 관계, 활성 테마, 테마 번역 fallback을 조회한다") fun shouldFindCreatorBlockAndThemesWithTranslationFallback() { + val now = LocalDateTime.of(2026, 6, 19, 12, 0) val viewer = saveMember("audio-viewer", MemberRole.USER) val creator = saveMember("audio-creator", MemberRole.CREATOR) val translatedTheme = saveTheme("수면", orders = 2) val blankTranslatedTheme = saveTheme("집중", orders = 1) + val emptyTheme = saveTheme("빈테마", orders = 3) val inactiveTheme = saveTheme("비활성", isActive = false) saveThemeTranslation(translatedTheme, "en", "Sleep") saveThemeTranslation(blankTranslatedTheme, "en", " ") + saveThemeTranslation(emptyTheme, "en", "Empty") saveThemeTranslation(inactiveTheme, "en", "Inactive") + saveAudioContent(creator, now.minusDays(1), false, translatedTheme) + saveAudioContent(creator, now.minusDays(2), false, blankTranslatedTheme) + saveAudioContent(creator, now.minusDays(3), false, inactiveTheme) saveBlock(creator, viewer) flushAndClear() val record = repository.findCreator(creator.id!!, viewer.id!!) - val themes = repository.findAudioThemes("en") + val themes = repository.findAudioThemes(creator.id!!, now, canViewAdultContent = false, "en") assertEquals(creator.id, record!!.creatorId) assertEquals(MemberRole.CREATOR, record.role) @@ -66,6 +72,31 @@ class DefaultCreatorChannelAudioQueryRepositoryTest @Autowired constructor( assertEquals(listOf("집중", "Sleep"), themes.map { it.themeName }) } + @Test + @DisplayName("테마 목록은 크리에이터의 조회 가능한 공개 오디오 콘텐츠가 있는 테마만 반환한다") + fun shouldFindAudioThemesOnlyWhenCreatorHasVisibleAudioContent() { + val now = LocalDateTime.of(2026, 6, 19, 12, 0) + val creator = saveMember("theme-filter-creator", MemberRole.CREATOR) + val otherCreator = saveMember("theme-filter-other-creator", MemberRole.CREATOR) + val publicTheme = saveTheme("공개", orders = 1) + val adultOnlyTheme = saveTheme("성인전용", orders = 2) + val otherCreatorTheme = saveTheme("다른크리에이터", orders = 3) + val futureOnlyTheme = saveTheme("예약전용", orders = 4) + val durationMissingTheme = saveTheme("길이없음", orders = 5) + saveAudioContent(creator, now.minusDays(1), false, publicTheme) + saveAudioContent(creator, now.minusDays(1), true, adultOnlyTheme) + saveAudioContent(otherCreator, now.minusDays(1), false, otherCreatorTheme) + saveAudioContent(creator, now.plusDays(1), false, futureOnlyTheme) + saveAudioContent(creator, now.minusDays(1), false, durationMissingTheme).duration = null + flushAndClear() + + val nonAdultThemes = repository.findAudioThemes(creator.id!!, now, canViewAdultContent = false, "ko") + val adultVisibleThemes = repository.findAudioThemes(creator.id!!, now, canViewAdultContent = true, "ko") + + assertEquals(listOf(publicTheme.id), nonAdultThemes.map { it.themeId }) + assertEquals(listOf(publicTheme.id, adultOnlyTheme.id), adultVisibleThemes.map { it.themeId }) + } + @Test @DisplayName("오디오 콘텐츠 count는 공개 조건, 성인 노출 정책, 활성 themeId 필터를 공유한다") fun shouldCountPublicAudioContentsWithFilters() {