test #388
@@ -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.GetSeriesListResponse
|
||||||
import kr.co.vividnext.sodalive.content.series.translation.SeriesTranslationRepository
|
import kr.co.vividnext.sodalive.content.series.translation.SeriesTranslationRepository
|
||||||
import kr.co.vividnext.sodalive.content.theme.AudioContentThemeService
|
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.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
|
||||||
@@ -50,7 +49,6 @@ class HomeService(
|
|||||||
private val rankingRepository: RankingRepository,
|
private val rankingRepository: RankingRepository,
|
||||||
private val explorerQueryRepository: ExplorerQueryRepository,
|
private val explorerQueryRepository: ExplorerQueryRepository,
|
||||||
|
|
||||||
private val contentTranslationRepository: ContentTranslationRepository,
|
|
||||||
private val aiCharacterTranslationRepository: AiCharacterTranslationRepository,
|
private val aiCharacterTranslationRepository: AiCharacterTranslationRepository,
|
||||||
private val seriesTranslationRepository: SeriesTranslationRepository,
|
private val seriesTranslationRepository: SeriesTranslationRepository,
|
||||||
|
|
||||||
@@ -122,8 +120,6 @@ class HomeService(
|
|||||||
isAdult = isAdult
|
isAdult = isAdult
|
||||||
)
|
)
|
||||||
|
|
||||||
val translatedLatestContentList = getTranslatedContentList(contentList = latestContentList)
|
|
||||||
|
|
||||||
val eventBannerList = GetEventResponse(
|
val eventBannerList = GetEventResponse(
|
||||||
totalCount = 0,
|
totalCount = 0,
|
||||||
eventList = emptyList()
|
eventList = emptyList()
|
||||||
@@ -177,64 +173,12 @@ class HomeService(
|
|||||||
sort = ContentRankingSortType.REVENUE
|
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(
|
val recommendChannelList = recommendChannelService.getRecommendChannel(
|
||||||
memberId = memberId,
|
memberId = memberId,
|
||||||
isAdult = isAdult,
|
isAdult = isAdult,
|
||||||
contentType = contentType
|
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(
|
val freeContentList = getRandomizedContentList(
|
||||||
memberId = memberId,
|
memberId = memberId,
|
||||||
isAdult = isAdult,
|
isAdult = isAdult,
|
||||||
@@ -248,8 +192,6 @@ class HomeService(
|
|||||||
isPointAvailableOnly = false
|
isPointAvailableOnly = false
|
||||||
)
|
)
|
||||||
|
|
||||||
val translatedFreeContentList = getTranslatedContentList(contentList = freeContentList)
|
|
||||||
|
|
||||||
// 포인트 사용가능 콘텐츠 리스트 - 랜덤으로 가져오기 (DB에서 isPointAvailable 조건 적용)
|
// 포인트 사용가능 콘텐츠 리스트 - 랜덤으로 가져오기 (DB에서 isPointAvailable 조건 적용)
|
||||||
val pointAvailableContentList = getRandomizedContentList(
|
val pointAvailableContentList = getRandomizedContentList(
|
||||||
memberId = memberId,
|
memberId = memberId,
|
||||||
@@ -260,28 +202,26 @@ class HomeService(
|
|||||||
isPointAvailableOnly = true
|
isPointAvailableOnly = true
|
||||||
)
|
)
|
||||||
|
|
||||||
val translatedPointAvailableContentList = getTranslatedContentList(contentList = pointAvailableContentList)
|
|
||||||
|
|
||||||
val excludeContentIds = (
|
val excludeContentIds = (
|
||||||
translatedLatestContentList.map { it.contentId } +
|
latestContentList.map { it.contentId } +
|
||||||
translatedContentRanking.map { it.contentId }
|
contentRanking.map { it.contentId }
|
||||||
).distinct()
|
).distinct()
|
||||||
|
|
||||||
return GetHomeResponse(
|
return GetHomeResponse(
|
||||||
liveList = liveList,
|
liveList = liveList,
|
||||||
creatorRanking = creatorRanking,
|
creatorRanking = creatorRanking,
|
||||||
latestContentThemeList = latestContentThemeList,
|
latestContentThemeList = latestContentThemeList,
|
||||||
latestContentList = translatedLatestContentList,
|
latestContentList = latestContentList,
|
||||||
bannerList = bannerList,
|
bannerList = bannerList,
|
||||||
eventBannerList = eventBannerList,
|
eventBannerList = eventBannerList,
|
||||||
originalAudioDramaList = translatedOriginalAudioDramaList,
|
originalAudioDramaList = translatedOriginalAudioDramaList,
|
||||||
auditionList = auditionList,
|
auditionList = auditionList,
|
||||||
dayOfWeekSeriesList = translatedDayOfWeekSeriesList,
|
dayOfWeekSeriesList = translatedDayOfWeekSeriesList,
|
||||||
popularCharacters = translatedPopularCharacters,
|
popularCharacters = translatedPopularCharacters,
|
||||||
contentRanking = translatedContentRanking,
|
contentRanking = contentRanking,
|
||||||
recommendChannelList = translatedRecommendChannelList,
|
recommendChannelList = recommendChannelList,
|
||||||
freeContentList = translatedFreeContentList,
|
freeContentList = freeContentList,
|
||||||
pointAvailableContentList = translatedPointAvailableContentList,
|
pointAvailableContentList = pointAvailableContentList,
|
||||||
recommendContentList = getRecommendContentList(
|
recommendContentList = getRecommendContentList(
|
||||||
isAdultContentVisible = isAdultContentVisible,
|
isAdultContentVisible = isAdultContentVisible,
|
||||||
contentType = contentType,
|
contentType = contentType,
|
||||||
@@ -311,15 +251,13 @@ class HomeService(
|
|||||||
listOf(theme)
|
listOf(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
val contentList = contentService.getLatestContentByTheme(
|
return contentService.getLatestContentByTheme(
|
||||||
memberId = memberId,
|
memberId = memberId,
|
||||||
theme = themeList,
|
theme = themeList,
|
||||||
contentType = contentType,
|
contentType = contentType,
|
||||||
isFree = false,
|
isFree = false,
|
||||||
isAdult = isAdult
|
isAdult = isAdult
|
||||||
)
|
)
|
||||||
|
|
||||||
return getTranslatedContentList(contentList = contentList)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDayOfWeekSeriesList(
|
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(
|
private fun pickByTimeDecay(
|
||||||
@@ -542,43 +480,7 @@ class HomeService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getTranslatedContentList(contentList = result.take(targetSize).shuffled())
|
return 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import kr.co.vividnext.sodalive.content.pin.QPinContent.pinContent
|
|||||||
import kr.co.vividnext.sodalive.content.playlist.AudioContentPlaylistContent
|
import kr.co.vividnext.sodalive.content.playlist.AudioContentPlaylistContent
|
||||||
import kr.co.vividnext.sodalive.content.playlist.QAudioContentPlaylistContent
|
import kr.co.vividnext.sodalive.content.playlist.QAudioContentPlaylistContent
|
||||||
import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme
|
import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme
|
||||||
|
import kr.co.vividnext.sodalive.content.translation.QContentTranslation.contentTranslation
|
||||||
import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series
|
import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series
|
||||||
import kr.co.vividnext.sodalive.creator.admin.content.series.QSeriesContent.seriesContent
|
import kr.co.vividnext.sodalive.creator.admin.content.series.QSeriesContent.seriesContent
|
||||||
import kr.co.vividnext.sodalive.event.QEvent.event
|
import kr.co.vividnext.sodalive.event.QEvent.event
|
||||||
@@ -188,7 +189,8 @@ interface AudioContentQueryRepository {
|
|||||||
isAdult: Boolean,
|
isAdult: Boolean,
|
||||||
orderByRandom: Boolean = false,
|
orderByRandom: Boolean = false,
|
||||||
isPointAvailableOnly: Boolean = false,
|
isPointAvailableOnly: Boolean = false,
|
||||||
excludeContentIds: List<Long> = emptyList()
|
excludeContentIds: List<Long> = emptyList(),
|
||||||
|
locale: String? = null
|
||||||
): List<AudioContentMainItem>
|
): List<AudioContentMainItem>
|
||||||
|
|
||||||
fun findContentByCurationId(
|
fun findContentByCurationId(
|
||||||
@@ -1331,7 +1333,8 @@ class AudioContentQueryRepositoryImpl(
|
|||||||
isAdult: Boolean,
|
isAdult: Boolean,
|
||||||
orderByRandom: Boolean,
|
orderByRandom: Boolean,
|
||||||
isPointAvailableOnly: Boolean,
|
isPointAvailableOnly: Boolean,
|
||||||
excludeContentIds: List<Long>
|
excludeContentIds: List<Long>,
|
||||||
|
locale: String?
|
||||||
): List<AudioContentMainItem> {
|
): List<AudioContentMainItem> {
|
||||||
val blockMemberCondition = if (memberId != null) {
|
val blockMemberCondition = if (memberId != null) {
|
||||||
blockMember.member.id.eq(member.id)
|
blockMember.member.id.eq(member.id)
|
||||||
@@ -1382,12 +1385,27 @@ class AudioContentQueryRepositoryImpl(
|
|||||||
where = where.and(audioContent.id.notIn(excludeContentIds))
|
where = where.and(audioContent.id.notIn(excludeContentIds))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val titleExpression = if (locale != null) {
|
||||||
|
val translatedTitle = Expressions.stringTemplate(
|
||||||
|
"JSON_EXTRACT({0}, '$.title')",
|
||||||
|
contentTranslation.renderedPayload
|
||||||
|
)
|
||||||
|
val coalesceTitle = Expressions.stringTemplate(
|
||||||
|
"COALESCE(NULLIF({0}, ''), {1})",
|
||||||
|
translatedTitle,
|
||||||
|
audioContent.title
|
||||||
|
)
|
||||||
|
coalesceTitle
|
||||||
|
} else {
|
||||||
|
audioContent.title
|
||||||
|
}
|
||||||
|
|
||||||
var select = queryFactory
|
var select = queryFactory
|
||||||
.select(
|
.select(
|
||||||
QAudioContentMainItem(
|
QAudioContentMainItem(
|
||||||
audioContent.id,
|
audioContent.id,
|
||||||
member.id,
|
member.id,
|
||||||
audioContent.title,
|
titleExpression,
|
||||||
audioContent.coverImage.prepend("/").prepend(imageHost),
|
audioContent.coverImage.prepend("/").prepend(imageHost),
|
||||||
member.nickname,
|
member.nickname,
|
||||||
audioContent.isPointAvailable
|
audioContent.isPointAvailable
|
||||||
@@ -1397,6 +1415,11 @@ class AudioContentQueryRepositoryImpl(
|
|||||||
.innerJoin(audioContent.member, member)
|
.innerJoin(audioContent.member, member)
|
||||||
.innerJoin(audioContent.theme, audioContentTheme)
|
.innerJoin(audioContent.theme, audioContentTheme)
|
||||||
|
|
||||||
|
if (locale != null) {
|
||||||
|
select = select.leftJoin(contentTranslation)
|
||||||
|
.on(contentTranslation.contentId.eq(audioContent.id).and(contentTranslation.locale.eq(locale)))
|
||||||
|
}
|
||||||
|
|
||||||
if (memberId != null) {
|
if (memberId != null) {
|
||||||
where = where.and(blockMember.id.isNull)
|
where = where.and(blockMember.id.isNull)
|
||||||
select = select.leftJoin(blockMember).on(blockMemberCondition)
|
select = select.leftJoin(blockMember).on(blockMemberCondition)
|
||||||
|
|||||||
@@ -1222,7 +1222,7 @@ class AudioContentService(
|
|||||||
isPointAvailableOnly = isPointAvailableOnly
|
isPointAvailableOnly = isPointAvailableOnly
|
||||||
)
|
)
|
||||||
|
|
||||||
val contentList = repository.getLatestContentByTheme(
|
return repository.getLatestContentByTheme(
|
||||||
memberId = memberId,
|
memberId = memberId,
|
||||||
theme = normalizedTheme,
|
theme = normalizedTheme,
|
||||||
contentType = contentType,
|
contentType = contentType,
|
||||||
@@ -1233,26 +1233,9 @@ class AudioContentService(
|
|||||||
isAdult = isAdult,
|
isAdult = isAdult,
|
||||||
orderByRandom = orderByRandom,
|
orderByRandom = orderByRandom,
|
||||||
isPointAvailableOnly = isPointAvailableOnly,
|
isPointAvailableOnly = isPointAvailableOnly,
|
||||||
excludeContentIds = excludeContentIds
|
excludeContentIds = excludeContentIds,
|
||||||
|
locale = langContext.lang.code
|
||||||
)
|
)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import kr.co.vividnext.sodalive.content.ContentType
|
|||||||
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
|
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
|
||||||
import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment
|
import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment
|
||||||
import kr.co.vividnext.sodalive.content.like.QAudioContentLike.audioContentLike
|
import kr.co.vividnext.sodalive.content.like.QAudioContentLike.audioContentLike
|
||||||
|
import kr.co.vividnext.sodalive.content.translation.QContentTranslation.contentTranslation
|
||||||
import kr.co.vividnext.sodalive.member.MemberRole
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
import kr.co.vividnext.sodalive.member.QMember.member
|
import kr.co.vividnext.sodalive.member.QMember.member
|
||||||
import kr.co.vividnext.sodalive.member.auth.QAuth.auth
|
import kr.co.vividnext.sodalive.member.auth.QAuth.auth
|
||||||
@@ -83,7 +84,8 @@ class RecommendChannelQueryRepository(
|
|||||||
fun getContentsByCreatorIdLikeDesc(
|
fun getContentsByCreatorIdLikeDesc(
|
||||||
creatorId: Long,
|
creatorId: Long,
|
||||||
memberId: Long?,
|
memberId: Long?,
|
||||||
isAdult: Boolean
|
isAdult: Boolean,
|
||||||
|
locale: String? = null
|
||||||
): List<RecommendChannelContentItem> {
|
): List<RecommendChannelContentItem> {
|
||||||
val blockMemberCondition = if (memberId != null) {
|
val blockMemberCondition = if (memberId != null) {
|
||||||
blockMember.member.id.eq(audioContent.member.id)
|
blockMember.member.id.eq(audioContent.member.id)
|
||||||
@@ -99,11 +101,26 @@ class RecommendChannelQueryRepository(
|
|||||||
where = where.and(audioContent.isAdult.isFalse)
|
where = where.and(audioContent.isAdult.isFalse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val titleExpression = if (locale != null) {
|
||||||
|
val translatedTitle = Expressions.stringTemplate(
|
||||||
|
"JSON_EXTRACT({0}, '$.title')",
|
||||||
|
contentTranslation.renderedPayload
|
||||||
|
)
|
||||||
|
val coalesceTitle = Expressions.stringTemplate(
|
||||||
|
"COALESCE(NULLIF({0}, ''), {1})",
|
||||||
|
translatedTitle,
|
||||||
|
audioContent.title
|
||||||
|
)
|
||||||
|
coalesceTitle
|
||||||
|
} else {
|
||||||
|
audioContent.title
|
||||||
|
}
|
||||||
|
|
||||||
var select = queryFactory
|
var select = queryFactory
|
||||||
.select(
|
.select(
|
||||||
QRecommendChannelContentItem(
|
QRecommendChannelContentItem(
|
||||||
audioContent.id,
|
audioContent.id,
|
||||||
audioContent.title,
|
titleExpression,
|
||||||
audioContent.coverImage.prepend("/").prepend(imageHost),
|
audioContent.coverImage.prepend("/").prepend(imageHost),
|
||||||
audioContentLike.id.countDistinct(),
|
audioContentLike.id.countDistinct(),
|
||||||
audioContentComment.id.countDistinct()
|
audioContentComment.id.countDistinct()
|
||||||
@@ -121,6 +138,14 @@ class RecommendChannelQueryRepository(
|
|||||||
.and(audioContentComment.isActive.isTrue)
|
.and(audioContentComment.isActive.isTrue)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (locale != null) {
|
||||||
|
select = select.leftJoin(contentTranslation)
|
||||||
|
.on(
|
||||||
|
contentTranslation.contentId.eq(audioContent.id)
|
||||||
|
.and(contentTranslation.locale.eq(locale))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (memberId != null) {
|
if (memberId != null) {
|
||||||
where = where.and(blockMember.id.isNull)
|
where = where.and(blockMember.id.isNull)
|
||||||
select = select.leftJoin(blockMember).on(blockMemberCondition)
|
select = select.leftJoin(blockMember).on(blockMemberCondition)
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
package kr.co.vividnext.sodalive.query.recommend
|
package kr.co.vividnext.sodalive.query.recommend
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.content.ContentType
|
import kr.co.vividnext.sodalive.content.ContentType
|
||||||
|
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||||
import org.springframework.cache.annotation.Cacheable
|
import org.springframework.cache.annotation.Cacheable
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
class RecommendChannelQueryService(private val repository: RecommendChannelQueryRepository) {
|
class RecommendChannelQueryService(
|
||||||
|
private val repository: RecommendChannelQueryRepository,
|
||||||
|
private val langContext: LangContext
|
||||||
|
) {
|
||||||
@Cacheable(
|
@Cacheable(
|
||||||
cacheNames = ["default"],
|
cacheNames = ["default"],
|
||||||
key = "'recommendChannel:' + (#memberId ?: 'guest') + ':' + #isAdult + ':' + #contentType"
|
key = "'recommendChannel:' + (#memberId ?: 'guest') + ':' + #isAdult + ':' + #contentType"
|
||||||
@@ -27,7 +31,8 @@ class RecommendChannelQueryService(private val repository: RecommendChannelQuery
|
|||||||
it.contentList = repository.getContentsByCreatorIdLikeDesc(
|
it.contentList = repository.getContentsByCreatorIdLikeDesc(
|
||||||
creatorId = it.channelId,
|
creatorId = it.channelId,
|
||||||
memberId = memberId,
|
memberId = memberId,
|
||||||
isAdult = isAdult
|
isAdult = isAdult,
|
||||||
|
locale = langContext.lang.code
|
||||||
)
|
)
|
||||||
|
|
||||||
it
|
it
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ class RankingRepository(
|
|||||||
offset: Long,
|
offset: Long,
|
||||||
limit: Long,
|
limit: Long,
|
||||||
sortType: String,
|
sortType: String,
|
||||||
theme: String = ""
|
theme: String = "",
|
||||||
|
locale: String? = null
|
||||||
): List<GetAudioContentRankingItem> {
|
): List<GetAudioContentRankingItem> {
|
||||||
val blockMemberCondition = if (memberId != null) {
|
val blockMemberCondition = if (memberId != null) {
|
||||||
blockMember.member.id.eq(member.id)
|
blockMember.member.id.eq(member.id)
|
||||||
@@ -79,6 +80,8 @@ class RankingRepository(
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val contentTranslation = kr.co.vividnext.sodalive.content.translation.QContentTranslation.contentTranslation
|
||||||
|
|
||||||
var where = audioContent.isActive.isTrue
|
var where = audioContent.isActive.isTrue
|
||||||
.and(audioContent.member.isActive.isTrue)
|
.and(audioContent.member.isActive.isTrue)
|
||||||
.and(audioContent.member.isNotNull)
|
.and(audioContent.member.isNotNull)
|
||||||
@@ -109,11 +112,26 @@ class RankingRepository(
|
|||||||
where = where.and(audioContentTheme.theme.eq(theme))
|
where = where.and(audioContentTheme.theme.eq(theme))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val titleExpression = if (locale != null) {
|
||||||
|
val translatedTitle = Expressions.stringTemplate(
|
||||||
|
"JSON_EXTRACT({0}, '$.title')",
|
||||||
|
contentTranslation.renderedPayload
|
||||||
|
)
|
||||||
|
val coalesceTitle = Expressions.stringTemplate(
|
||||||
|
"COALESCE(NULLIF({0}, ''), {1})",
|
||||||
|
translatedTitle,
|
||||||
|
audioContent.title
|
||||||
|
)
|
||||||
|
coalesceTitle
|
||||||
|
} else {
|
||||||
|
audioContent.title
|
||||||
|
}
|
||||||
|
|
||||||
var select = queryFactory
|
var select = queryFactory
|
||||||
.select(
|
.select(
|
||||||
QGetAudioContentRankingItem(
|
QGetAudioContentRankingItem(
|
||||||
audioContent.id,
|
audioContent.id,
|
||||||
audioContent.title,
|
titleExpression,
|
||||||
audioContent.coverImage.prepend("/").prepend(imageHost),
|
audioContent.coverImage.prepend("/").prepend(imageHost),
|
||||||
audioContentTheme.theme,
|
audioContentTheme.theme,
|
||||||
audioContent.price,
|
audioContent.price,
|
||||||
@@ -167,6 +185,11 @@ class RankingRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (locale != null) {
|
||||||
|
select = select.leftJoin(contentTranslation)
|
||||||
|
.on(contentTranslation.contentId.eq(audioContent.id).and(contentTranslation.locale.eq(locale)))
|
||||||
|
}
|
||||||
|
|
||||||
if (memberId != null) {
|
if (memberId != null) {
|
||||||
where = where.and(blockMember.id.isNull)
|
where = where.and(blockMember.id.isNull)
|
||||||
select = select.leftJoin(blockMember).on(blockMemberCondition)
|
select = select.leftJoin(blockMember).on(blockMemberCondition)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import kr.co.vividnext.sodalive.creator.admin.content.series.Series
|
|||||||
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.creator.admin.content.series.SeriesState
|
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesState
|
||||||
import kr.co.vividnext.sodalive.explorer.GetExplorerSectionResponse
|
import kr.co.vividnext.sodalive.explorer.GetExplorerSectionResponse
|
||||||
|
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||||
import org.springframework.beans.factory.annotation.Value
|
import org.springframework.beans.factory.annotation.Value
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
@@ -17,6 +18,7 @@ import java.time.LocalDateTime
|
|||||||
class RankingService(
|
class RankingService(
|
||||||
private val repository: RankingRepository,
|
private val repository: RankingRepository,
|
||||||
private val seriesContentRepository: ContentSeriesContentRepository,
|
private val seriesContentRepository: ContentSeriesContentRepository,
|
||||||
|
private val langContext: LangContext,
|
||||||
|
|
||||||
@Value("\${cloud.aws.cloud-front.host}")
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
private val imageHost: String
|
private val imageHost: String
|
||||||
@@ -59,7 +61,8 @@ class RankingService(
|
|||||||
offset = offset,
|
offset = offset,
|
||||||
limit = limit,
|
limit = limit,
|
||||||
sortType = sortType,
|
sortType = sortType,
|
||||||
theme = theme
|
theme = theme,
|
||||||
|
locale = langContext.lang.code
|
||||||
)
|
)
|
||||||
loopCount++
|
loopCount++
|
||||||
} while (contentList.size < 5 && loopCount < 5)
|
} while (contentList.size < 5 && loopCount < 5)
|
||||||
|
|||||||
Reference in New Issue
Block a user