diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt index bdc6122..e443568 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt @@ -21,6 +21,7 @@ import kr.co.vividnext.sodalive.content.order.OrderType import kr.co.vividnext.sodalive.content.pin.PinContent import kr.co.vividnext.sodalive.content.pin.PinContentRepository import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository +import kr.co.vividnext.sodalive.content.theme.translation.ContentThemeTranslationRepository import kr.co.vividnext.sodalive.content.translation.ContentTranslation import kr.co.vividnext.sodalive.content.translation.ContentTranslationPayload import kr.co.vividnext.sodalive.content.translation.ContentTranslationRepository @@ -75,6 +76,8 @@ class AudioContentService( private val langContext: LangContext, + private val contentThemeTranslationRepository: ContentThemeTranslationRepository, + @Value("\${cloud.aws.s3.content-bucket}") private val audioContentBucket: String, @@ -1140,8 +1143,20 @@ class AudioContentService( orderByRandom: Boolean = false, isPointAvailableOnly: Boolean = false ): List { + /** + * - AS-IS theme은 한글만 처리하도록 되어 있음 + * - TO-BE 번역된 theme이 들어와도 동일한 동작을 하도록 처리 + */ + val normalizedTheme = normalizeThemeForQuery( + themes = theme, + contentType = contentType, + isFree = isFree, + isAdult = isAdult, + isPointAvailableOnly = isPointAvailableOnly + ) + val contentList = repository.getLatestContentByTheme( - theme = theme, + theme = normalizedTheme, contentType = contentType, offset = offset, limit = limit, @@ -1170,4 +1185,60 @@ class AudioContentService( contentList } } + + /** + * theme 파라미터로 번역된 테마명이 들어와도 한글 원문 테마로 조회되도록 정규화한다. + * - 현재 언어(locale)에 해당하는 테마 번역 목록을 활성 테마 집합과 매칭하여 역매핑한다. + * - 입력이 이미 한글인 경우 그대로 유지한다. + * - 매칭 실패 시 원본 값을 유지한다. + */ + private fun normalizeThemeForQuery( + themes: List, + contentType: ContentType, + isFree: Boolean, + isAdult: Boolean, + isPointAvailableOnly: Boolean + ): List { + if (themes.isEmpty()) return themes + + val themesWithIds = themeQueryRepository.getActiveThemeWithIdsOfContent( + isAdult = isAdult, + isFree = isFree, + isPointAvailableOnly = isPointAvailableOnly, + contentType = contentType + ) + + if (themesWithIds.isEmpty()) return themes + + val idByKorean = themesWithIds.associate { it.theme to it.id } + val koreanById = themesWithIds.associate { it.id to it.theme } + + val locale = langContext.lang.code + // 번역 테마를 역매핑하기 위해 현재 locale의 번역 목록을 조회 + val translatedByTextToId = run { + val ids = themesWithIds.map { it.id } + if (ids.isEmpty()) { + emptyMap() + } else { + contentThemeTranslationRepository + .findByContentThemeIdInAndLocale(ids, locale) + .associate { it.theme to it.contentThemeId } + } + } + + return themes.asSequence() + .map { input -> + when { + idByKorean.containsKey(input) -> input // 이미 한글 원문 + translatedByTextToId.containsKey(input) -> { + val id = translatedByTextToId[input]!! + koreanById[id] ?: input + } + + else -> input + } + } + .distinct() + .toList() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt index b226f1e..9317544 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt @@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerRespons import kr.co.vividnext.sodalive.content.main.curation.GetAudioContentCurationResponse import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository import kr.co.vividnext.sodalive.content.theme.AudioContentThemeService +import kr.co.vividnext.sodalive.content.theme.translation.ContentThemeTranslationRepository import kr.co.vividnext.sodalive.content.translation.ContentTranslationRepository import kr.co.vividnext.sodalive.event.EventItem import kr.co.vividnext.sodalive.i18n.LangContext @@ -25,6 +26,8 @@ class AudioContentMainService( private val audioContentThemeRepository: AudioContentThemeQueryRepository, private val audioContentThemeService: AudioContentThemeService, + private val contentThemeTranslationRepository: ContentThemeTranslationRepository, + private val contentTranslationRepository: ContentTranslationRepository, private val langContext: LangContext, @@ -80,8 +83,12 @@ class AudioContentMainService( member: Member, pageable: Pageable ): GetNewContentAllResponse { + /** + * - AS-IS theme은 한글만 처리하도록 되어 있음 + * - TO-BE 번역된 theme이 들어와도 동일한 동작을 하도록 처리 + */ val isAdult = member.auth != null && isAdultContentVisible - val themeList = if (theme.isBlank()) { + val themeListRaw = if (theme.isBlank()) { audioContentThemeRepository.getActiveThemeOfContent( isAdult = isAdult, contentType = contentType @@ -90,6 +97,12 @@ class AudioContentMainService( listOf(theme) } + val themeList = normalizeThemeForQuery( + themes = themeListRaw, + contentType = contentType, + isAdult = isAdult + ) + val totalCount = repository.totalCountNewContentFor2Weeks( themeList, memberId = member.id!!, @@ -128,6 +141,56 @@ class AudioContentMainService( return GetNewContentAllResponse(totalCount, translatedContentList) } + /** + * 번역된 테마명이 들어와도 한글 원문 테마로 조회되도록 정규화한다. + */ + private fun normalizeThemeForQuery( + themes: List, + contentType: ContentType, + isAdult: Boolean + ): List { + if (themes.isEmpty()) return themes + + val themesWithIds = audioContentThemeRepository.getActiveThemeWithIdsOfContent( + isAdult = isAdult, + isFree = false, + isPointAvailableOnly = false, + contentType = contentType + ) + + if (themesWithIds.isEmpty()) return themes + + val idByKorean = themesWithIds.associate { it.theme to it.id } + val koreanById = themesWithIds.associate { it.id to it.theme } + + val locale = langContext.lang.code + val translatedByTextToId = run { + val ids = themesWithIds.map { it.id } + if (ids.isEmpty()) { + emptyMap() + } else { + contentThemeTranslationRepository + .findByContentThemeIdInAndLocale(ids, locale) + .associate { it.theme to it.contentThemeId } + } + } + + return themes.asSequence() + .map { input -> + when { + idByKorean.containsKey(input) -> input + translatedByTextToId.containsKey(input) -> { + val id = translatedByTextToId[input]!! + koreanById[id] ?: input + } + + else -> input + } + } + .distinct() + .toList() + } + @Transactional(readOnly = true) @Cacheable(cacheNames = ["default"], key = "'newContentUploadCreatorList:' + #memberId + ':' + #isAdult") fun getNewContentUploadCreatorList(memberId: Long, isAdult: Boolean): List {