Compare commits

..

2 Commits

2 changed files with 152 additions and 4 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

@@ -6,6 +6,8 @@ import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType
import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse
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
@@ -22,6 +24,9 @@ class AudioContentMainService(
private val repository: AudioContentRepository,
private val blockMemberRepository: BlockMemberRepository,
private val audioContentThemeRepository: AudioContentThemeQueryRepository,
private val audioContentThemeService: AudioContentThemeService,
private val contentThemeTranslationRepository: ContentThemeTranslationRepository,
private val contentTranslationRepository: ContentTranslationRepository,
@@ -31,9 +36,21 @@ class AudioContentMainService(
private val imageHost: String
) {
@Transactional(readOnly = true)
@Cacheable(cacheNames = ["default"], key = "'themeList:' + ':' + #isAdult")
fun getThemeList(isAdult: Boolean, contentType: ContentType): List<String> {
return audioContentThemeRepository.getActiveThemeOfContent(isAdult = isAdult, contentType = contentType)
/**
* 콘텐츠 테마 조회
*
* - langContext에 따라 기본 한국어 데이터 혹은 번역된 콘텐츠 테마를 조회해야 함
*
* - 번역된 테마 데이터가 없다면 번역하여 반환
* - 번역된 데이터가 있다면 번역된 데이터를 조회하여 반환
*/
// 표시용 테마 목록은 언어 컨텍스트에 따라 번역된 값을 반환해야 한다.
// AudioContentThemeService가 번역/저장을 처리하므로 이를 사용한다.
return audioContentThemeService.getActiveThemeOfContent(
isAdult = isAdult,
contentType = contentType
)
}
@Transactional(readOnly = true)
@@ -66,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
@@ -76,6 +97,12 @@ class AudioContentMainService(
listOf(theme)
}
val themeList = normalizeThemeForQuery(
themes = themeListRaw,
contentType = contentType,
isAdult = isAdult
)
val totalCount = repository.totalCountNewContentFor2Weeks(
themeList,
memberId = member.id!!,
@@ -114,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> {