HomeService fetchData 리팩토링 및 DB JOIN 기반 번역 적용
fetchData 함수에서 별도로 수행하던 번역 데이터 조회를 DB JOIN 및 COALESCE를 사용하도록 개선하여 성능을 최적화함. - AudioContentRepository, RankingRepository 등에 locale 파라미터 추가 - DB 레벨에서 번역된 제목을 조회하도록 쿼리 수정 - HomeService에서 불필요한 getTranslatedContentList 호출 제거
This commit is contained in:
@@ -14,7 +14,6 @@ import kr.co.vividnext.sodalive.content.series.ContentSeriesService
|
||||
import kr.co.vividnext.sodalive.content.series.GetSeriesListResponse
|
||||
import kr.co.vividnext.sodalive.content.series.translation.SeriesTranslationRepository
|
||||
import kr.co.vividnext.sodalive.content.theme.AudioContentThemeService
|
||||
import kr.co.vividnext.sodalive.content.translation.ContentTranslationRepository
|
||||
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek
|
||||
import kr.co.vividnext.sodalive.event.GetEventResponse
|
||||
import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository
|
||||
@@ -50,7 +49,6 @@ class HomeService(
|
||||
private val rankingRepository: RankingRepository,
|
||||
private val explorerQueryRepository: ExplorerQueryRepository,
|
||||
|
||||
private val contentTranslationRepository: ContentTranslationRepository,
|
||||
private val aiCharacterTranslationRepository: AiCharacterTranslationRepository,
|
||||
private val seriesTranslationRepository: SeriesTranslationRepository,
|
||||
|
||||
@@ -122,8 +120,6 @@ class HomeService(
|
||||
isAdult = isAdult
|
||||
)
|
||||
|
||||
val translatedLatestContentList = getTranslatedContentList(contentList = latestContentList)
|
||||
|
||||
val eventBannerList = GetEventResponse(
|
||||
totalCount = 0,
|
||||
eventList = emptyList()
|
||||
@@ -177,64 +173,12 @@ class HomeService(
|
||||
sort = ContentRankingSortType.REVENUE
|
||||
)
|
||||
|
||||
val contentRankingContentIds = contentRanking.map { it.contentId }
|
||||
val translatedContentRanking = if (contentRankingContentIds.isNotEmpty()) {
|
||||
val translations = contentTranslationRepository
|
||||
.findByContentIdInAndLocale(contentIds = contentRankingContentIds, locale = langContext.lang.code)
|
||||
.associateBy { it.contentId }
|
||||
|
||||
contentRanking.map { item ->
|
||||
val translatedTitle = translations[item.contentId]?.renderedPayload?.title
|
||||
if (translatedTitle.isNullOrBlank()) {
|
||||
item
|
||||
} else {
|
||||
item.copy(title = translatedTitle)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
contentRanking
|
||||
}
|
||||
|
||||
val recommendChannelList = recommendChannelService.getRecommendChannel(
|
||||
memberId = memberId,
|
||||
isAdult = isAdult,
|
||||
contentType = contentType
|
||||
)
|
||||
|
||||
/**
|
||||
* recommendChannelList의 콘텐츠 번역 데이터 조회
|
||||
*
|
||||
* languageCode != null
|
||||
* contentTranslationRepository를 이용해 번역 콘텐츠를 조회한다. - contentId, locale
|
||||
*
|
||||
* 한 번에 조회하고 contentId를 매핑하여 recommendChannelList의 콘텐츠 title을 번역 데이터로 변경한다
|
||||
*/
|
||||
val channelContentIds = recommendChannelList
|
||||
.flatMap { it.contentList }
|
||||
.map { it.contentId }
|
||||
.distinct()
|
||||
|
||||
val translatedRecommendChannelList = if (channelContentIds.isNotEmpty()) {
|
||||
val translations = contentTranslationRepository
|
||||
.findByContentIdInAndLocale(contentIds = channelContentIds, locale = langContext.lang.code)
|
||||
.associateBy { it.contentId }
|
||||
|
||||
recommendChannelList.map { channel ->
|
||||
val translatedContentList = channel.contentList.map { item ->
|
||||
val translatedTitle = translations[item.contentId]?.renderedPayload?.title
|
||||
if (translatedTitle.isNullOrBlank()) {
|
||||
item
|
||||
} else {
|
||||
item.copy(title = translatedTitle)
|
||||
}
|
||||
}
|
||||
|
||||
channel.copy(contentList = translatedContentList)
|
||||
}
|
||||
} else {
|
||||
recommendChannelList
|
||||
}
|
||||
|
||||
val freeContentList = getRandomizedContentList(
|
||||
memberId = memberId,
|
||||
isAdult = isAdult,
|
||||
@@ -248,8 +192,6 @@ class HomeService(
|
||||
isPointAvailableOnly = false
|
||||
)
|
||||
|
||||
val translatedFreeContentList = getTranslatedContentList(contentList = freeContentList)
|
||||
|
||||
// 포인트 사용가능 콘텐츠 리스트 - 랜덤으로 가져오기 (DB에서 isPointAvailable 조건 적용)
|
||||
val pointAvailableContentList = getRandomizedContentList(
|
||||
memberId = memberId,
|
||||
@@ -260,28 +202,26 @@ class HomeService(
|
||||
isPointAvailableOnly = true
|
||||
)
|
||||
|
||||
val translatedPointAvailableContentList = getTranslatedContentList(contentList = pointAvailableContentList)
|
||||
|
||||
val excludeContentIds = (
|
||||
translatedLatestContentList.map { it.contentId } +
|
||||
translatedContentRanking.map { it.contentId }
|
||||
latestContentList.map { it.contentId } +
|
||||
contentRanking.map { it.contentId }
|
||||
).distinct()
|
||||
|
||||
return GetHomeResponse(
|
||||
liveList = liveList,
|
||||
creatorRanking = creatorRanking,
|
||||
latestContentThemeList = latestContentThemeList,
|
||||
latestContentList = translatedLatestContentList,
|
||||
latestContentList = latestContentList,
|
||||
bannerList = bannerList,
|
||||
eventBannerList = eventBannerList,
|
||||
originalAudioDramaList = translatedOriginalAudioDramaList,
|
||||
auditionList = auditionList,
|
||||
dayOfWeekSeriesList = translatedDayOfWeekSeriesList,
|
||||
popularCharacters = translatedPopularCharacters,
|
||||
contentRanking = translatedContentRanking,
|
||||
recommendChannelList = translatedRecommendChannelList,
|
||||
freeContentList = translatedFreeContentList,
|
||||
pointAvailableContentList = translatedPointAvailableContentList,
|
||||
contentRanking = contentRanking,
|
||||
recommendChannelList = recommendChannelList,
|
||||
freeContentList = freeContentList,
|
||||
pointAvailableContentList = pointAvailableContentList,
|
||||
recommendContentList = getRecommendContentList(
|
||||
isAdultContentVisible = isAdultContentVisible,
|
||||
contentType = contentType,
|
||||
@@ -311,15 +251,13 @@ class HomeService(
|
||||
listOf(theme)
|
||||
}
|
||||
|
||||
val contentList = contentService.getLatestContentByTheme(
|
||||
return contentService.getLatestContentByTheme(
|
||||
memberId = memberId,
|
||||
theme = themeList,
|
||||
contentType = contentType,
|
||||
isFree = false,
|
||||
isAdult = isAdult
|
||||
)
|
||||
|
||||
return getTranslatedContentList(contentList = contentList)
|
||||
}
|
||||
|
||||
fun getDayOfWeekSeriesList(
|
||||
@@ -445,7 +383,7 @@ class HomeService(
|
||||
}
|
||||
}
|
||||
|
||||
return getTranslatedContentList(contentList = result.take(RECOMMEND_TARGET_SIZE).shuffled())
|
||||
return result.take(RECOMMEND_TARGET_SIZE).shuffled()
|
||||
}
|
||||
|
||||
private fun pickByTimeDecay(
|
||||
@@ -542,43 +480,7 @@ class HomeService(
|
||||
}
|
||||
}
|
||||
|
||||
return getTranslatedContentList(contentList = result.take(targetSize).shuffled())
|
||||
}
|
||||
|
||||
/**
|
||||
* 콘텐츠 리스트의 제목을 현재 언어(locale)에 맞춰 일괄 번역한다.
|
||||
*
|
||||
* 처리 절차:
|
||||
* - 입력된 콘텐츠들의 contentId 집합을 만들고, 요청 언어 코드(langContext.lang.code)로
|
||||
* contentTranslationRepository에서 번역 데이터를 한 번에 조회한다.
|
||||
* - 각 항목에 대해 번역된 제목이 존재하고 비어있지 않으면 title만 번역 값으로 교체한다.
|
||||
* - 번역이 없거나 공백이면 원본 항목을 그대로 반환한다.
|
||||
*
|
||||
* 성능:
|
||||
* - N건의 항목을 1회의 조회로 해결하기 위해 IN 쿼리를 사용한다.
|
||||
*
|
||||
* @param contentList 번역 대상 AudioContentMainItem 목록
|
||||
* @return 제목이 가능한 항목은 번역된 목록(불변 사본), 그 외는 원본 항목 유지
|
||||
*/
|
||||
private fun getTranslatedContentList(contentList: List<AudioContentMainItem>): List<AudioContentMainItem> {
|
||||
val contentIds = contentList.map { it.contentId }
|
||||
|
||||
return if (contentIds.isNotEmpty()) {
|
||||
val translations = contentTranslationRepository
|
||||
.findByContentIdInAndLocale(contentIds = contentIds, locale = langContext.lang.code)
|
||||
.associateBy { it.contentId }
|
||||
|
||||
contentList.map { item ->
|
||||
val translatedTitle = translations[item.contentId]?.renderedPayload?.title
|
||||
if (translatedTitle.isNullOrBlank()) {
|
||||
item
|
||||
} else {
|
||||
item.copy(title = translatedTitle)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
contentList
|
||||
}
|
||||
return result.take(targetSize).shuffled()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user