번역된 테마로 콘텐츠를 조회해도 한글 테마처럼 처리하기

This commit is contained in:
2025-12-15 12:15:31 +09:00
parent c0c61da44b
commit dc0df81232
2 changed files with 136 additions and 2 deletions

View File

@@ -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<AudioContentMainItem> {
/**
* - 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<String>,
contentType: ContentType,
isFree: Boolean,
isAdult: Boolean,
isPointAvailableOnly: Boolean
): List<String> {
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()
}
}

View File

@@ -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<String>,
contentType: ContentType,
isAdult: Boolean
): List<String> {
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<ContentCreatorResponse> {