Compare commits
3 Commits
236394e148
...
ba1844a6c2
| Author | SHA1 | Date | |
|---|---|---|---|
| ba1844a6c2 | |||
| 082f255773 | |||
| 04281817a5 |
@@ -17,7 +17,6 @@ class HomeController(private val service: HomeService) {
|
|||||||
@GetMapping
|
@GetMapping
|
||||||
fun fetchData(
|
fun fetchData(
|
||||||
@RequestParam timezone: String,
|
@RequestParam timezone: String,
|
||||||
@RequestParam(required = false) languageCode: String? = null,
|
|
||||||
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
|
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
|
||||||
@RequestParam("contentType", required = false) contentType: ContentType? = null,
|
@RequestParam("contentType", required = false) contentType: ContentType? = null,
|
||||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
@@ -25,7 +24,6 @@ class HomeController(private val service: HomeService) {
|
|||||||
ApiResponse.ok(
|
ApiResponse.ok(
|
||||||
service.fetchData(
|
service.fetchData(
|
||||||
timezone = timezone,
|
timezone = timezone,
|
||||||
languageCode = languageCode,
|
|
||||||
isAdultContentVisible = isAdultContentVisible ?: true,
|
isAdultContentVisible = isAdultContentVisible ?: true,
|
||||||
contentType = contentType ?: ContentType.ALL,
|
contentType = contentType ?: ContentType.ALL,
|
||||||
member
|
member
|
||||||
@@ -36,7 +34,6 @@ class HomeController(private val service: HomeService) {
|
|||||||
@GetMapping("/latest-content")
|
@GetMapping("/latest-content")
|
||||||
fun getLatestContentByTheme(
|
fun getLatestContentByTheme(
|
||||||
@RequestParam("theme") theme: String,
|
@RequestParam("theme") theme: String,
|
||||||
@RequestParam(required = false) languageCode: String? = null,
|
|
||||||
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
|
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
|
||||||
@RequestParam("contentType", required = false) contentType: ContentType? = null,
|
@RequestParam("contentType", required = false) contentType: ContentType? = null,
|
||||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
@@ -44,7 +41,6 @@ class HomeController(private val service: HomeService) {
|
|||||||
ApiResponse.ok(
|
ApiResponse.ok(
|
||||||
service.getLatestContentByTheme(
|
service.getLatestContentByTheme(
|
||||||
theme = theme,
|
theme = theme,
|
||||||
languageCode = languageCode,
|
|
||||||
isAdultContentVisible = isAdultContentVisible ?: true,
|
isAdultContentVisible = isAdultContentVisible ?: true,
|
||||||
contentType = contentType ?: ContentType.ALL,
|
contentType = contentType ?: ContentType.ALL,
|
||||||
member
|
member
|
||||||
@@ -74,15 +70,13 @@ class HomeController(private val service: HomeService) {
|
|||||||
fun getRecommendContents(
|
fun getRecommendContents(
|
||||||
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
|
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
|
||||||
@RequestParam("contentType", required = false) contentType: ContentType? = null,
|
@RequestParam("contentType", required = false) contentType: ContentType? = null,
|
||||||
@RequestParam(required = false) languageCode: String? = null,
|
|
||||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
) = run {
|
) = run {
|
||||||
ApiResponse.ok(
|
ApiResponse.ok(
|
||||||
service.getRecommendContentList(
|
service.getRecommendContentList(
|
||||||
isAdultContentVisible = isAdultContentVisible ?: true,
|
isAdultContentVisible = isAdultContentVisible ?: true,
|
||||||
contentType = contentType ?: ContentType.ALL,
|
contentType = contentType ?: ContentType.ALL,
|
||||||
member = member,
|
member = member
|
||||||
languageCode = languageCode
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package kr.co.vividnext.sodalive.api.home
|
package kr.co.vividnext.sodalive.api.home
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.audition.AuditionService
|
import kr.co.vividnext.sodalive.audition.AuditionService
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.dto.Character
|
||||||
import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterService
|
import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterService
|
||||||
import kr.co.vividnext.sodalive.chat.character.translate.AiCharacterTranslationRepository
|
import kr.co.vividnext.sodalive.chat.character.translate.AiCharacterTranslationRepository
|
||||||
import kr.co.vividnext.sodalive.content.AudioContentMainItem
|
import kr.co.vividnext.sodalive.content.AudioContentMainItem
|
||||||
@@ -16,6 +17,7 @@ import kr.co.vividnext.sodalive.content.translation.ContentTranslationRepository
|
|||||||
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek
|
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek
|
||||||
import kr.co.vividnext.sodalive.event.GetEventResponse
|
import kr.co.vividnext.sodalive.event.GetEventResponse
|
||||||
import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository
|
import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository
|
||||||
|
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||||
import kr.co.vividnext.sodalive.live.room.LiveRoomService
|
import kr.co.vividnext.sodalive.live.room.LiveRoomService
|
||||||
import kr.co.vividnext.sodalive.live.room.LiveRoomStatus
|
import kr.co.vividnext.sodalive.live.room.LiveRoomStatus
|
||||||
import kr.co.vividnext.sodalive.member.Member
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
@@ -52,6 +54,8 @@ class HomeService(
|
|||||||
private val contentTranslationRepository: ContentTranslationRepository,
|
private val contentTranslationRepository: ContentTranslationRepository,
|
||||||
private val aiCharacterTranslationRepository: AiCharacterTranslationRepository,
|
private val aiCharacterTranslationRepository: AiCharacterTranslationRepository,
|
||||||
|
|
||||||
|
private val langContext: LangContext,
|
||||||
|
|
||||||
@Value("\${cloud.aws.cloud-front.host}")
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
private val imageHost: String
|
private val imageHost: String
|
||||||
) {
|
) {
|
||||||
@@ -62,7 +66,6 @@ class HomeService(
|
|||||||
|
|
||||||
fun fetchData(
|
fun fetchData(
|
||||||
timezone: String,
|
timezone: String,
|
||||||
languageCode: String?,
|
|
||||||
isAdultContentVisible: Boolean,
|
isAdultContentVisible: Boolean,
|
||||||
contentType: ContentType,
|
contentType: ContentType,
|
||||||
member: Member?
|
member: Member?
|
||||||
@@ -117,36 +120,7 @@ class HomeService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
val translatedLatestContentList = getTranslatedContentList(contentList = latestContentList)
|
||||||
* latestContentList 번역 데이터 조회
|
|
||||||
*
|
|
||||||
* languageCode != null
|
|
||||||
* contentTranslationRepository를 이용해 번역 콘텐츠를 조회한다. - contentId, locale
|
|
||||||
*
|
|
||||||
* 한 번에 조회하고 contentId를 매핑하여 latestContentList의 title을 번역 데이터로 변경한다
|
|
||||||
*/
|
|
||||||
val translatedLatestContentList = if (!languageCode.isNullOrBlank()) {
|
|
||||||
val contentIds = latestContentList.map { it.contentId }
|
|
||||||
|
|
||||||
if (contentIds.isNotEmpty()) {
|
|
||||||
val translations = contentTranslationRepository
|
|
||||||
.findByContentIdInAndLocale(contentIds = contentIds, locale = languageCode)
|
|
||||||
.associateBy { it.contentId }
|
|
||||||
|
|
||||||
latestContentList.map { item ->
|
|
||||||
val translatedTitle = translations[item.contentId]?.renderedPayload?.title
|
|
||||||
if (translatedTitle.isNullOrBlank()) {
|
|
||||||
item
|
|
||||||
} else {
|
|
||||||
item.copy(title = translatedTitle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
latestContentList
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
latestContentList
|
|
||||||
}
|
|
||||||
|
|
||||||
val eventBannerList = GetEventResponse(
|
val eventBannerList = GetEventResponse(
|
||||||
totalCount = 0,
|
totalCount = 0,
|
||||||
@@ -175,39 +149,7 @@ class HomeService(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 인기 캐릭터 조회
|
// 인기 캐릭터 조회
|
||||||
val popularCharacters = characterService.getPopularCharacters()
|
val translatedPopularCharacters = getTranslatedAiCharacterList(aiCharacterList = characterService.getPopularCharacters())
|
||||||
|
|
||||||
/**
|
|
||||||
* popularCharacters 캐릭터 이름 번역 데이터 조회
|
|
||||||
*
|
|
||||||
* languageCode != null
|
|
||||||
* aiCharacterTranslationRepository 이용해 번역 콘텐츠를 조회한다. - characterId, locale
|
|
||||||
*
|
|
||||||
* 한 번에 조회하고 characterId 매핑하여 popularCharacters의 캐릭터 이름을 번역 데이터로 변경한다
|
|
||||||
*/
|
|
||||||
val translatedPopularCharacters = if (!languageCode.isNullOrBlank()) {
|
|
||||||
val characterIds = popularCharacters.map { it.characterId }
|
|
||||||
|
|
||||||
if (characterIds.isNotEmpty()) {
|
|
||||||
val translations = aiCharacterTranslationRepository
|
|
||||||
.findByCharacterIdInAndLocale(characterIds = characterIds, locale = languageCode)
|
|
||||||
.associateBy { it.characterId }
|
|
||||||
|
|
||||||
popularCharacters.map { character ->
|
|
||||||
val translatedName = translations[character.characterId]?.renderedPayload?.name
|
|
||||||
val translatedDesc = translations[character.characterId]?.renderedPayload?.description
|
|
||||||
if (translatedName.isNullOrBlank() || translatedDesc.isNullOrBlank()) {
|
|
||||||
character
|
|
||||||
} else {
|
|
||||||
character.copy(name = translatedName, description = translatedDesc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
popularCharacters
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
popularCharacters
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentDateTime = LocalDateTime.now()
|
val currentDateTime = LocalDateTime.now()
|
||||||
val startDate = currentDateTime
|
val startDate = currentDateTime
|
||||||
@@ -228,20 +170,10 @@ class HomeService(
|
|||||||
sort = ContentRankingSortType.REVENUE
|
sort = ContentRankingSortType.REVENUE
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
val contentRankingContentIds = contentRanking.map { it.contentId }
|
||||||
* contentRanking 번역 데이터 조회
|
val translatedContentRanking = if (contentRankingContentIds.isNotEmpty()) {
|
||||||
*
|
|
||||||
* languageCode != null
|
|
||||||
* contentTranslationRepository를 이용해 번역 콘텐츠를 조회한다. - contentId, locale
|
|
||||||
*
|
|
||||||
* 한 번에 조회하고 contentId를 매핑하여 contentRanking title을 번역 데이터로 변경한다
|
|
||||||
*/
|
|
||||||
val translatedContentRanking = if (!languageCode.isNullOrBlank()) {
|
|
||||||
val contentIds = contentRanking.map { it.contentId }
|
|
||||||
|
|
||||||
if (contentIds.isNotEmpty()) {
|
|
||||||
val translations = contentTranslationRepository
|
val translations = contentTranslationRepository
|
||||||
.findByContentIdInAndLocale(contentIds = contentIds, locale = languageCode)
|
.findByContentIdInAndLocale(contentIds = contentRankingContentIds, locale = langContext.lang.code)
|
||||||
.associateBy { it.contentId }
|
.associateBy { it.contentId }
|
||||||
|
|
||||||
contentRanking.map { item ->
|
contentRanking.map { item ->
|
||||||
@@ -255,9 +187,6 @@ class HomeService(
|
|||||||
} else {
|
} else {
|
||||||
contentRanking
|
contentRanking
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
contentRanking
|
|
||||||
}
|
|
||||||
|
|
||||||
val recommendChannelList = recommendChannelService.getRecommendChannel(
|
val recommendChannelList = recommendChannelService.getRecommendChannel(
|
||||||
memberId = memberId,
|
memberId = memberId,
|
||||||
@@ -273,15 +202,14 @@ class HomeService(
|
|||||||
*
|
*
|
||||||
* 한 번에 조회하고 contentId를 매핑하여 recommendChannelList의 콘텐츠 title을 번역 데이터로 변경한다
|
* 한 번에 조회하고 contentId를 매핑하여 recommendChannelList의 콘텐츠 title을 번역 데이터로 변경한다
|
||||||
*/
|
*/
|
||||||
val translatedRecommendChannelList = if (!languageCode.isNullOrBlank()) {
|
val channelContentIds = recommendChannelList
|
||||||
val contentIds = recommendChannelList
|
|
||||||
.flatMap { it.contentList }
|
.flatMap { it.contentList }
|
||||||
.map { it.contentId }
|
.map { it.contentId }
|
||||||
.distinct()
|
.distinct()
|
||||||
|
|
||||||
if (contentIds.isNotEmpty()) {
|
val translatedRecommendChannelList = if (channelContentIds.isNotEmpty()) {
|
||||||
val translations = contentTranslationRepository
|
val translations = contentTranslationRepository
|
||||||
.findByContentIdInAndLocale(contentIds = contentIds, locale = languageCode)
|
.findByContentIdInAndLocale(contentIds = channelContentIds, locale = langContext.lang.code)
|
||||||
.associateBy { it.contentId }
|
.associateBy { it.contentId }
|
||||||
|
|
||||||
recommendChannelList.map { channel ->
|
recommendChannelList.map { channel ->
|
||||||
@@ -299,9 +227,6 @@ class HomeService(
|
|||||||
} else {
|
} else {
|
||||||
recommendChannelList
|
recommendChannelList
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
recommendChannelList
|
|
||||||
}
|
|
||||||
|
|
||||||
val freeContentList = contentService.getLatestContentByTheme(
|
val freeContentList = contentService.getLatestContentByTheme(
|
||||||
theme = contentThemeService.getActiveThemeOfContent(
|
theme = contentThemeService.getActiveThemeOfContent(
|
||||||
@@ -321,36 +246,7 @@ class HomeService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
val translatedFreeContentList = getTranslatedContentList(contentList = freeContentList)
|
||||||
* freeContentList 번역 데이터 조회
|
|
||||||
*
|
|
||||||
* languageCode != null
|
|
||||||
* contentTranslationRepository를 이용해 번역 콘텐츠를 조회한다. - contentId, locale
|
|
||||||
*
|
|
||||||
* 한 번에 조회하고 contentId를 매핑하여 freeContentList title을 번역 데이터로 변경한다
|
|
||||||
*/
|
|
||||||
val translatedFreeContentList = if (!languageCode.isNullOrBlank()) {
|
|
||||||
val contentIds = freeContentList.map { it.contentId }
|
|
||||||
|
|
||||||
if (contentIds.isNotEmpty()) {
|
|
||||||
val translations = contentTranslationRepository
|
|
||||||
.findByContentIdInAndLocale(contentIds = contentIds, locale = languageCode)
|
|
||||||
.associateBy { it.contentId }
|
|
||||||
|
|
||||||
freeContentList.map { item ->
|
|
||||||
val translatedTitle = translations[item.contentId]?.renderedPayload?.title
|
|
||||||
if (translatedTitle.isNullOrBlank()) {
|
|
||||||
item
|
|
||||||
} else {
|
|
||||||
item.copy(title = translatedTitle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
freeContentList
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
freeContentList
|
|
||||||
}
|
|
||||||
|
|
||||||
// 포인트 사용가능 콘텐츠 리스트 - 랜덤으로 가져오기 (DB에서 isPointAvailable 조건 적용)
|
// 포인트 사용가능 콘텐츠 리스트 - 랜덤으로 가져오기 (DB에서 isPointAvailable 조건 적용)
|
||||||
val pointAvailableContentList = contentService.getLatestContentByTheme(
|
val pointAvailableContentList = contentService.getLatestContentByTheme(
|
||||||
@@ -368,36 +264,7 @@ class HomeService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
val translatedPointAvailableContentList = getTranslatedContentList(contentList = pointAvailableContentList)
|
||||||
* pointAvailableContentList 번역 데이터 조회
|
|
||||||
*
|
|
||||||
* languageCode != null
|
|
||||||
* contentTranslationRepository를 이용해 번역 콘텐츠를 조회한다. - contentId, locale
|
|
||||||
*
|
|
||||||
* 한 번에 조회하고 contentId를 매핑하여 pointAvailableContentList title을 번역 데이터로 변경한다
|
|
||||||
*/
|
|
||||||
val translatedPointAvailableContentList = if (!languageCode.isNullOrBlank()) {
|
|
||||||
val contentIds = pointAvailableContentList.map { it.contentId }
|
|
||||||
|
|
||||||
if (contentIds.isNotEmpty()) {
|
|
||||||
val translations = contentTranslationRepository
|
|
||||||
.findByContentIdInAndLocale(contentIds = contentIds, locale = languageCode)
|
|
||||||
.associateBy { it.contentId }
|
|
||||||
|
|
||||||
pointAvailableContentList.map { item ->
|
|
||||||
val translatedTitle = translations[item.contentId]?.renderedPayload?.title
|
|
||||||
if (translatedTitle.isNullOrBlank()) {
|
|
||||||
item
|
|
||||||
} else {
|
|
||||||
item.copy(title = translatedTitle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pointAvailableContentList
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pointAvailableContentList
|
|
||||||
}
|
|
||||||
|
|
||||||
val curationList = curationService.getContentCurationList(
|
val curationList = curationService.getContentCurationList(
|
||||||
tabId = 3L, // 기존에 사용하던 단편 탭의 큐레이션을 사용
|
tabId = 3L, // 기존에 사용하던 단편 탭의 큐레이션을 사용
|
||||||
@@ -424,8 +291,7 @@ class HomeService(
|
|||||||
recommendContentList = getRecommendContentList(
|
recommendContentList = getRecommendContentList(
|
||||||
isAdultContentVisible = isAdultContentVisible,
|
isAdultContentVisible = isAdultContentVisible,
|
||||||
contentType = contentType,
|
contentType = contentType,
|
||||||
member = member,
|
member = member
|
||||||
languageCode = languageCode
|
|
||||||
),
|
),
|
||||||
curationList = curationList
|
curationList = curationList
|
||||||
)
|
)
|
||||||
@@ -433,7 +299,6 @@ class HomeService(
|
|||||||
|
|
||||||
fun getLatestContentByTheme(
|
fun getLatestContentByTheme(
|
||||||
theme: String,
|
theme: String,
|
||||||
languageCode: String?,
|
|
||||||
isAdultContentVisible: Boolean,
|
isAdultContentVisible: Boolean,
|
||||||
contentType: ContentType,
|
contentType: ContentType,
|
||||||
member: Member?
|
member: Member?
|
||||||
@@ -464,38 +329,7 @@ class HomeService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return getTranslatedContentList(contentList = contentList)
|
||||||
* contentList 번역 데이터 조회
|
|
||||||
*
|
|
||||||
* languageCode != null
|
|
||||||
* contentTranslationRepository를 이용해 번역 콘텐츠를 조회한다. - contentId, locale
|
|
||||||
*
|
|
||||||
* 한 번에 조회하고 contentId를 매핑하여 contentList title을 번역 데이터로 변경한다
|
|
||||||
*/
|
|
||||||
val translatedContentList = if (!languageCode.isNullOrBlank()) {
|
|
||||||
val contentIds = contentList.map { it.contentId }
|
|
||||||
|
|
||||||
if (contentIds.isNotEmpty()) {
|
|
||||||
val translations = contentTranslationRepository
|
|
||||||
.findByContentIdInAndLocale(contentIds = contentIds, locale = languageCode)
|
|
||||||
.associateBy { it.contentId }
|
|
||||||
|
|
||||||
contentList.map { item ->
|
|
||||||
val translatedTitle = translations[item.contentId]?.renderedPayload?.title
|
|
||||||
if (translatedTitle.isNullOrBlank()) {
|
|
||||||
item
|
|
||||||
} else {
|
|
||||||
item.copy(title = translatedTitle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
contentList
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
contentList
|
|
||||||
}
|
|
||||||
|
|
||||||
return translatedContentList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDayOfWeekSeriesList(
|
fun getDayOfWeekSeriesList(
|
||||||
@@ -571,8 +405,7 @@ class HomeService(
|
|||||||
fun getRecommendContentList(
|
fun getRecommendContentList(
|
||||||
isAdultContentVisible: Boolean,
|
isAdultContentVisible: Boolean,
|
||||||
contentType: ContentType,
|
contentType: ContentType,
|
||||||
member: Member?,
|
member: Member?
|
||||||
languageCode: String? = null
|
|
||||||
): List<AudioContentMainItem> {
|
): List<AudioContentMainItem> {
|
||||||
val memberId = member?.id
|
val memberId = member?.id
|
||||||
val isAdult = member?.auth != null && isAdultContentVisible
|
val isAdult = member?.auth != null && isAdultContentVisible
|
||||||
@@ -607,23 +440,33 @@ class HomeService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return getTranslatedContentList(contentList = result)
|
||||||
* 추천 콘텐츠 번역 데이터 조회
|
}
|
||||||
*
|
|
||||||
* languageCode != null
|
|
||||||
* contentTranslationRepository를 이용해 번역 콘텐츠를 조회한다. - contentId, locale
|
|
||||||
*
|
|
||||||
* 한 번에 조회하고 contentId를 매핑하여 result의 title을 번역 데이터로 변경한다
|
|
||||||
*/
|
|
||||||
val translatedResult = if (!languageCode.isNullOrBlank()) {
|
|
||||||
val contentIds = result.map { it.contentId }
|
|
||||||
|
|
||||||
if (contentIds.isNotEmpty()) {
|
/**
|
||||||
|
* 콘텐츠 리스트의 제목을 현재 언어(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
|
val translations = contentTranslationRepository
|
||||||
.findByContentIdInAndLocale(contentIds = contentIds, locale = languageCode)
|
.findByContentIdInAndLocale(contentIds = contentIds, locale = langContext.lang.code)
|
||||||
.associateBy { it.contentId }
|
.associateBy { it.contentId }
|
||||||
|
|
||||||
result.map { item ->
|
contentList.map { item ->
|
||||||
val translatedTitle = translations[item.contentId]?.renderedPayload?.title
|
val translatedTitle = translations[item.contentId]?.renderedPayload?.title
|
||||||
if (translatedTitle.isNullOrBlank()) {
|
if (translatedTitle.isNullOrBlank()) {
|
||||||
item
|
item
|
||||||
@@ -632,12 +475,41 @@ class HomeService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result
|
contentList
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return translatedResult
|
/**
|
||||||
|
* AI 캐릭터 리스트의 이름/설명을 현재 언어(locale)에 맞춰 일괄 번역한다.
|
||||||
|
*
|
||||||
|
* 처리 절차:
|
||||||
|
* - characterId 목록을 추출하고, 요청 언어 코드로 aiCharacterTranslationRepository에서
|
||||||
|
* 번역 데이터를 한 번에 조회한다.
|
||||||
|
* - 각 캐릭터에 대해 name과 description 모두 번역 값이 존재하고 비어있지 않을 때에만
|
||||||
|
* 해당 필드를 교체한다. 둘 중 하나라도 없으면 원본 캐릭터를 그대로 유지한다.
|
||||||
|
*
|
||||||
|
* @param aiCharacterList 번역 대상 캐릭터 목록
|
||||||
|
* @return 가능한 경우 name/description이 번역된 캐릭터 목록, 그 외는 원본 유지
|
||||||
|
*/
|
||||||
|
private fun getTranslatedAiCharacterList(aiCharacterList: List<Character>): List<Character> {
|
||||||
|
val characterIds = aiCharacterList.map { it.characterId }
|
||||||
|
|
||||||
|
return if (characterIds.isNotEmpty()) {
|
||||||
|
val translations = aiCharacterTranslationRepository
|
||||||
|
.findByCharacterIdInAndLocale(characterIds = characterIds, locale = langContext.lang.code)
|
||||||
|
.associateBy { it.characterId }
|
||||||
|
|
||||||
|
aiCharacterList.map { character ->
|
||||||
|
val translatedName = translations[character.characterId]?.renderedPayload?.name
|
||||||
|
val translatedDesc = translations[character.characterId]?.renderedPayload?.description
|
||||||
|
if (translatedName.isNullOrBlank() || translatedDesc.isNullOrBlank()) {
|
||||||
|
character
|
||||||
|
} else {
|
||||||
|
character.copy(name = translatedName, description = translatedDesc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
aiCharacterList
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
package kr.co.vividnext.sodalive.configs
|
package kr.co.vividnext.sodalive.configs
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.i18n.LangInterceptor
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.web.servlet.config.annotation.CorsRegistry
|
import org.springframework.web.servlet.config.annotation.CorsRegistry
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
class WebConfig : WebMvcConfigurer {
|
class WebConfig(
|
||||||
|
private val langInterceptor: LangInterceptor
|
||||||
|
) : WebMvcConfigurer {
|
||||||
|
override fun addInterceptors(registry: InterceptorRegistry) {
|
||||||
|
registry.addInterceptor(langInterceptor).addPathPatterns("/**")
|
||||||
|
}
|
||||||
|
|
||||||
override fun addCorsMappings(registry: CorsRegistry) {
|
override fun addCorsMappings(registry: CorsRegistry) {
|
||||||
registry.addMapping("/**")
|
registry.addMapping("/**")
|
||||||
.allowedOrigins(
|
.allowedOrigins(
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ class ExplorerController(private val service: ExplorerService) {
|
|||||||
fun getCreatorProfile(
|
fun getCreatorProfile(
|
||||||
@PathVariable("id") creatorId: Long,
|
@PathVariable("id") creatorId: Long,
|
||||||
@RequestParam timezone: String,
|
@RequestParam timezone: String,
|
||||||
|
@RequestParam("languageCode", required = false) languageCode: String? = null,
|
||||||
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
|
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
|
||||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
) = run {
|
) = run {
|
||||||
@@ -60,6 +61,7 @@ class ExplorerController(private val service: ExplorerService) {
|
|||||||
service.getCreatorProfile(
|
service.getCreatorProfile(
|
||||||
creatorId = creatorId,
|
creatorId = creatorId,
|
||||||
timezone = timezone,
|
timezone = timezone,
|
||||||
|
languageCode = languageCode,
|
||||||
isAdultContentVisible = isAdultContentVisible ?: true,
|
isAdultContentVisible = isAdultContentVisible ?: true,
|
||||||
member = member
|
member = member
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.content.LanguageDetectEvent
|
|||||||
import kr.co.vividnext.sodalive.content.LanguageDetectTargetType
|
import kr.co.vividnext.sodalive.content.LanguageDetectTargetType
|
||||||
import kr.co.vividnext.sodalive.content.SortType
|
import kr.co.vividnext.sodalive.content.SortType
|
||||||
import kr.co.vividnext.sodalive.content.series.ContentSeriesService
|
import kr.co.vividnext.sodalive.content.series.ContentSeriesService
|
||||||
|
import kr.co.vividnext.sodalive.content.translation.ContentTranslationRepository
|
||||||
import kr.co.vividnext.sodalive.explorer.follower.GetFollowerListResponse
|
import kr.co.vividnext.sodalive.explorer.follower.GetFollowerListResponse
|
||||||
import kr.co.vividnext.sodalive.explorer.follower.GetFollowerListResponseItem
|
import kr.co.vividnext.sodalive.explorer.follower.GetFollowerListResponseItem
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.ChannelNotice
|
import kr.co.vividnext.sodalive.explorer.profile.ChannelNotice
|
||||||
@@ -46,6 +47,7 @@ class ExplorerService(
|
|||||||
private val seriesService: ContentSeriesService,
|
private val seriesService: ContentSeriesService,
|
||||||
|
|
||||||
private val applicationEventPublisher: ApplicationEventPublisher,
|
private val applicationEventPublisher: ApplicationEventPublisher,
|
||||||
|
private val contentTranslationRepository: ContentTranslationRepository,
|
||||||
|
|
||||||
@Value("\${cloud.aws.cloud-front.host}")
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
private val cloudFrontHost: String
|
private val cloudFrontHost: String
|
||||||
@@ -170,6 +172,7 @@ class ExplorerService(
|
|||||||
fun getCreatorProfile(
|
fun getCreatorProfile(
|
||||||
creatorId: Long,
|
creatorId: Long,
|
||||||
timezone: String,
|
timezone: String,
|
||||||
|
languageCode: String?,
|
||||||
isAdultContentVisible: Boolean,
|
isAdultContentVisible: Boolean,
|
||||||
member: Member
|
member: Member
|
||||||
): GetCreatorProfileResponse {
|
): GetCreatorProfileResponse {
|
||||||
@@ -233,6 +236,29 @@ class ExplorerService(
|
|||||||
listOf()
|
listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val translatedContentList = if (!languageCode.isNullOrBlank() && contentList.isNotEmpty()) {
|
||||||
|
val contentIds = contentList.map { it.contentId }
|
||||||
|
|
||||||
|
if (contentIds.isNotEmpty()) {
|
||||||
|
val translations = contentTranslationRepository
|
||||||
|
.findByContentIdInAndLocale(contentIds = contentIds, locale = languageCode)
|
||||||
|
.associateBy { it.contentId }
|
||||||
|
|
||||||
|
contentList.map { item ->
|
||||||
|
val translatedTitle = translations[item.contentId]?.renderedPayload?.title
|
||||||
|
if (translatedTitle.isNullOrBlank()) {
|
||||||
|
item
|
||||||
|
} else {
|
||||||
|
item.copy(title = translatedTitle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
contentList
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
contentList
|
||||||
|
}
|
||||||
|
|
||||||
// 크리에이터의 최신 오디오 콘텐츠 1개
|
// 크리에이터의 최신 오디오 콘텐츠 1개
|
||||||
val latestContent = if (isCreator) {
|
val latestContent = if (isCreator) {
|
||||||
audioContentService.getLatestCreatorAudioContent(creatorId, member, isAdultContentVisible)
|
audioContentService.getLatestCreatorAudioContent(creatorId, member, isAdultContentVisible)
|
||||||
@@ -333,7 +359,7 @@ class ExplorerService(
|
|||||||
userDonationRanking = memberDonationRanking,
|
userDonationRanking = memberDonationRanking,
|
||||||
similarCreatorList = similarCreatorList,
|
similarCreatorList = similarCreatorList,
|
||||||
liveRoomList = liveRoomList,
|
liveRoomList = liveRoomList,
|
||||||
contentList = contentList,
|
contentList = translatedContentList,
|
||||||
latestContent = latestContent,
|
latestContent = latestContent,
|
||||||
totalContentCount = totalContentCount,
|
totalContentCount = totalContentCount,
|
||||||
ownedContentCount = ownedContentCount,
|
ownedContentCount = ownedContentCount,
|
||||||
|
|||||||
22
src/main/kotlin/kr/co/vividnext/sodalive/i18n/Lang.kt
Normal file
22
src/main/kotlin/kr/co/vividnext/sodalive/i18n/Lang.kt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package kr.co.vividnext.sodalive.i18n
|
||||||
|
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
enum class Lang(val code: String, val locale: Locale) {
|
||||||
|
KO("ko", Locale.KOREAN),
|
||||||
|
EN("en", Locale.ENGLISH),
|
||||||
|
JA("ja", Locale.JAPANESE);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromAcceptLanguage(header: String?): Lang {
|
||||||
|
if (header.isNullOrBlank()) return KO
|
||||||
|
val two = header.trim().lowercase().take(2) // 앱은 2자리만 보내지만 안전하게 처리
|
||||||
|
return when (two) {
|
||||||
|
"ko" -> KO
|
||||||
|
"en" -> EN
|
||||||
|
"ja" -> JA
|
||||||
|
else -> KO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/main/kotlin/kr/co/vividnext/sodalive/i18n/LangContext.kt
Normal file
15
src/main/kotlin/kr/co/vividnext/sodalive/i18n/LangContext.kt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package kr.co.vividnext.sodalive.i18n
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import org.springframework.web.context.annotation.RequestScope
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequestScope
|
||||||
|
class LangContext {
|
||||||
|
var lang: Lang = Lang.KO
|
||||||
|
internal set
|
||||||
|
|
||||||
|
fun setLang(lang: Lang) {
|
||||||
|
this.lang = lang
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package kr.co.vividnext.sodalive.i18n
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor
|
||||||
|
import javax.servlet.http.HttpServletRequest
|
||||||
|
import javax.servlet.http.HttpServletResponse
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class LangInterceptor(
|
||||||
|
private val langContext: LangContext
|
||||||
|
) : HandlerInterceptor {
|
||||||
|
override fun preHandle(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
handler: Any
|
||||||
|
): Boolean {
|
||||||
|
val acceptLanguage = request.getHeader("Accept-Language")
|
||||||
|
langContext.setLang(Lang.fromAcceptLanguage(acceptLanguage))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user