feat(home): 최근 소식 원천 데이터를 보강한다
This commit is contained in:
@@ -8,6 +8,8 @@ import com.querydsl.jpa.impl.JPAQueryFactory
|
||||
import kr.co.vividnext.sodalive.content.QAudioContent
|
||||
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.QCreatorCommunity
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.QCreatorCommunityComment.creatorCommunityComment
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.QCreatorCommunityLike.creatorCommunityLike
|
||||
import kr.co.vividnext.sodalive.extensions.toUtcIso
|
||||
import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
|
||||
import kr.co.vividnext.sodalive.member.MemberRole
|
||||
@@ -18,7 +20,10 @@ import kr.co.vividnext.sodalive.v2.common.domain.CreatorActivityType
|
||||
import kr.co.vividnext.sodalive.v2.common.domain.toCdnUrl
|
||||
import kr.co.vividnext.sodalive.v2.home.following.adapter.out.persistence.QHomeFollowingNewsInbox.homeFollowingNewsInbox
|
||||
import kr.co.vividnext.sodalive.v2.home.following.domain.FollowingNewsType
|
||||
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingCommunityPostNews
|
||||
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingContentNews
|
||||
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingCreator
|
||||
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingCreatorRankingNews
|
||||
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingLive
|
||||
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingNews
|
||||
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingSchedule
|
||||
@@ -116,50 +121,109 @@ class DefaultHomeFollowingQueryRepository(
|
||||
limit: Int
|
||||
): List<HomeFollowingNews> {
|
||||
val creator = QMember("newsCreator")
|
||||
return queryFactory
|
||||
val newsAudioContent = QAudioContent("recentNewsAudioContent")
|
||||
val newsCommunity = QCreatorCommunity("recentNewsCommunity")
|
||||
val rows = queryFactory
|
||||
.select(
|
||||
homeFollowingNewsInbox.id,
|
||||
homeFollowingNewsInbox.newsType,
|
||||
homeFollowingNewsInbox.creatorProfileImagePath,
|
||||
homeFollowingNewsInbox.creatorNickname,
|
||||
homeFollowingNewsInbox.title,
|
||||
homeFollowingNewsInbox.body,
|
||||
homeFollowingNewsInbox.thumbnailImagePath,
|
||||
creator.profileImage,
|
||||
creator.nickname,
|
||||
homeFollowingNewsInbox.targetId,
|
||||
homeFollowingNewsInbox.occurredAtUtc,
|
||||
homeFollowingNewsInbox.visibleFromAtUtc,
|
||||
homeFollowingNewsInbox.rank
|
||||
homeFollowingNewsInbox.rank,
|
||||
newsAudioContent.title,
|
||||
newsAudioContent.coverImage,
|
||||
newsCommunity.content,
|
||||
newsCommunity.imagePath,
|
||||
newsCommunity.createdAt
|
||||
)
|
||||
.from(homeFollowingNewsInbox)
|
||||
.join(creator).on(creator.id.eq(homeFollowingNewsInbox.creatorId))
|
||||
.leftJoin(newsAudioContent).on(
|
||||
homeFollowingNewsInbox.newsType.eq(FollowingNewsType.AUDIO_CONTENT),
|
||||
newsAudioContent.id.eq(homeFollowingNewsInbox.targetId)
|
||||
)
|
||||
.leftJoin(newsCommunity).on(
|
||||
homeFollowingNewsInbox.newsType.eq(FollowingNewsType.COMMUNITY_POST),
|
||||
newsCommunity.id.eq(homeFollowingNewsInbox.targetId)
|
||||
)
|
||||
.where(
|
||||
homeFollowingNewsInbox.memberId.eq(memberId),
|
||||
homeFollowingNewsInbox.isActive.isTrue,
|
||||
homeFollowingNewsInbox.visibleFromAtUtc.loe(nowUtc),
|
||||
creator.isActive.isTrue,
|
||||
creator.role.eq(MemberRole.CREATOR),
|
||||
activeFollowingCondition(memberId, homeFollowingNewsInbox.creatorId),
|
||||
adultNewsCondition(canViewAdultContent),
|
||||
notBlockedCreatorCondition(memberId, homeFollowingNewsInbox.creatorId),
|
||||
activeNewsTargetCondition()
|
||||
activeNewsTargetCondition(canViewAdultContent)
|
||||
)
|
||||
.orderBy(homeFollowingNewsInbox.visibleFromAtUtc.desc(), homeFollowingNewsInbox.id.desc())
|
||||
.limit(limit.toLong())
|
||||
.fetch()
|
||||
.map { row ->
|
||||
HomeFollowingNews(
|
||||
newsId = row.get(homeFollowingNewsInbox.id)!!.toString(),
|
||||
type = row.get(homeFollowingNewsInbox.newsType)!!,
|
||||
creatorProfileImageUrl = profileImageUrl(row.get(homeFollowingNewsInbox.creatorProfileImagePath)),
|
||||
creatorNickname = row.get(homeFollowingNewsInbox.creatorNickname)!!,
|
||||
title = row.get(homeFollowingNewsInbox.title)!!,
|
||||
body = row.get(homeFollowingNewsInbox.body)!!,
|
||||
thumbnailImageUrl = row.get(homeFollowingNewsInbox.thumbnailImagePath).toCdnUrl(cloudFrontHost),
|
||||
targetId = row.get(homeFollowingNewsInbox.targetId)!!,
|
||||
occurredAtUtc = row.get(homeFollowingNewsInbox.occurredAtUtc)!!.toUtcIso(),
|
||||
visibleFromAtUtc = row.get(homeFollowingNewsInbox.visibleFromAtUtc)!!.toUtcIso(),
|
||||
rank = row.get(homeFollowingNewsInbox.rank)
|
||||
val communityPostIds = rows
|
||||
.filter { it.get(homeFollowingNewsInbox.newsType) == FollowingNewsType.COMMUNITY_POST }
|
||||
.map { it.get(homeFollowingNewsInbox.targetId)!! }
|
||||
val likeCounts = communityLikeCounts(communityPostIds)
|
||||
val commentCounts = communityCommentCounts(communityPostIds)
|
||||
return rows.map { row -> row.toHomeFollowingNews(creator, newsAudioContent, newsCommunity, likeCounts, commentCounts) }
|
||||
}
|
||||
|
||||
private fun Tuple.toHomeFollowingNews(
|
||||
newsCreator: QMember,
|
||||
newsAudioContent: QAudioContent,
|
||||
newsCommunity: QCreatorCommunity,
|
||||
likeCounts: Map<Long, Int>,
|
||||
commentCounts: Map<Long, Int>
|
||||
): HomeFollowingNews {
|
||||
val type = get(homeFollowingNewsInbox.newsType)!!
|
||||
val targetId = get(homeFollowingNewsInbox.targetId)!!
|
||||
val creatorProfileImageUrl = profileImageUrl(get(newsCreator.profileImage))
|
||||
val creatorNickname = get(newsCreator.nickname)!!
|
||||
val visibleFromAtUtc = get(homeFollowingNewsInbox.visibleFromAtUtc)!!.toUtcIso()
|
||||
val rank = get(homeFollowingNewsInbox.rank)
|
||||
|
||||
return HomeFollowingNews(
|
||||
newsId = get(homeFollowingNewsInbox.id)!!.toString(),
|
||||
type = type,
|
||||
visibleFromAtUtc = visibleFromAtUtc,
|
||||
creatorRanking = if (type == FollowingNewsType.CREATOR_RANKING && rank != null) {
|
||||
HomeFollowingCreatorRankingNews(
|
||||
rank = rank,
|
||||
creatorId = targetId,
|
||||
nickname = creatorNickname,
|
||||
profileImageUrl = creatorProfileImageUrl
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
audioContent = if (type == FollowingNewsType.AUDIO_CONTENT) {
|
||||
HomeFollowingContentNews(
|
||||
contentId = targetId,
|
||||
contentImageUrl = get(newsAudioContent.coverImage).toCdnUrl(cloudFrontHost),
|
||||
title = get(newsAudioContent.title)!!,
|
||||
creatorProfileImageUrl = creatorProfileImageUrl,
|
||||
creatorNickname = creatorNickname
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
communityPost = if (type == FollowingNewsType.COMMUNITY_POST) {
|
||||
HomeFollowingCommunityPostNews(
|
||||
postId = targetId,
|
||||
creatorProfileImage = creatorProfileImageUrl,
|
||||
creatorNickname = creatorNickname,
|
||||
imageUrl = get(newsCommunity.imagePath).toCdnUrl(cloudFrontHost),
|
||||
content = get(newsCommunity.content)!!,
|
||||
createdAt = get(newsCommunity.createdAt)!!.toUtcIso(),
|
||||
likeCount = likeCounts[targetId] ?: 0,
|
||||
commentCount = commentCounts[targetId] ?: 0
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun findLiveSchedules(
|
||||
@@ -284,7 +348,44 @@ class DefaultHomeFollowingQueryRepository(
|
||||
return if (canViewAdultContent) null else homeFollowingNewsInbox.isAdult.isFalse
|
||||
}
|
||||
|
||||
private fun activeNewsTargetCondition(): BooleanExpression {
|
||||
private fun communityLikeCounts(postIds: List<Long>): Map<Long, Int> {
|
||||
if (postIds.isEmpty()) return emptyMap()
|
||||
|
||||
return queryFactory
|
||||
.select(creatorCommunityLike.creatorCommunity.id, creatorCommunityLike.id.count())
|
||||
.from(creatorCommunityLike)
|
||||
.where(
|
||||
creatorCommunityLike.creatorCommunity.id.`in`(postIds),
|
||||
creatorCommunityLike.isActive.isTrue
|
||||
)
|
||||
.groupBy(creatorCommunityLike.creatorCommunity.id)
|
||||
.fetch()
|
||||
.associate {
|
||||
it.get(creatorCommunityLike.creatorCommunity.id)!! to
|
||||
(it.get(creatorCommunityLike.id.count())?.toInt() ?: 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun communityCommentCounts(postIds: List<Long>): Map<Long, Int> {
|
||||
if (postIds.isEmpty()) return emptyMap()
|
||||
|
||||
return queryFactory
|
||||
.select(creatorCommunityComment.creatorCommunity.id, creatorCommunityComment.id.count())
|
||||
.from(creatorCommunityComment)
|
||||
.where(
|
||||
creatorCommunityComment.creatorCommunity.id.`in`(postIds),
|
||||
creatorCommunityComment.isActive.isTrue,
|
||||
creatorCommunityComment.parent.isNull
|
||||
)
|
||||
.groupBy(creatorCommunityComment.creatorCommunity.id)
|
||||
.fetch()
|
||||
.associate {
|
||||
it.get(creatorCommunityComment.creatorCommunity.id)!! to
|
||||
(it.get(creatorCommunityComment.id.count())?.toInt() ?: 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun activeNewsTargetCondition(canViewAdultContent: Boolean): BooleanExpression {
|
||||
val newsAudioContent = QAudioContent("newsAudioContent")
|
||||
val newsCommunity = QCreatorCommunity("newsCommunity")
|
||||
val activeAudioExists = JPAExpressions
|
||||
@@ -292,7 +393,8 @@ class DefaultHomeFollowingQueryRepository(
|
||||
.from(newsAudioContent)
|
||||
.where(
|
||||
newsAudioContent.id.eq(homeFollowingNewsInbox.targetId),
|
||||
newsAudioContent.isActive.isTrue
|
||||
newsAudioContent.isActive.isTrue,
|
||||
adultAudioNewsTargetCondition(canViewAdultContent, newsAudioContent)
|
||||
)
|
||||
.exists()
|
||||
val activeCommunityExists = JPAExpressions
|
||||
@@ -300,15 +402,44 @@ class DefaultHomeFollowingQueryRepository(
|
||||
.from(newsCommunity)
|
||||
.where(
|
||||
newsCommunity.id.eq(homeFollowingNewsInbox.targetId),
|
||||
newsCommunity.isActive.isTrue
|
||||
newsCommunity.isActive.isTrue,
|
||||
newsCommunity.price.loe(0),
|
||||
adultCommunityNewsTargetCondition(canViewAdultContent, newsCommunity)
|
||||
)
|
||||
.exists()
|
||||
|
||||
return homeFollowingNewsInbox.newsType.eq(FollowingNewsType.CREATOR_RANKING)
|
||||
.and(homeFollowingNewsInbox.rank.isNotNull)
|
||||
.or(homeFollowingNewsInbox.newsType.eq(FollowingNewsType.AUDIO_CONTENT).and(activeAudioExists))
|
||||
.or(homeFollowingNewsInbox.newsType.eq(FollowingNewsType.COMMUNITY_POST).and(activeCommunityExists))
|
||||
}
|
||||
|
||||
private fun adultAudioNewsTargetCondition(
|
||||
canViewAdultContent: Boolean,
|
||||
newsAudioContent: QAudioContent
|
||||
): BooleanExpression? {
|
||||
return if (canViewAdultContent) null else newsAudioContent.isAdult.isFalse
|
||||
}
|
||||
|
||||
private fun adultCommunityNewsTargetCondition(
|
||||
canViewAdultContent: Boolean,
|
||||
newsCommunity: QCreatorCommunity
|
||||
): BooleanExpression? {
|
||||
return if (canViewAdultContent) null else newsCommunity.isAdult.isFalse
|
||||
}
|
||||
|
||||
private fun activeFollowingCondition(memberId: Long, creatorIdPath: Expression<Long>): BooleanExpression {
|
||||
return JPAExpressions
|
||||
.selectOne()
|
||||
.from(creatorFollowing)
|
||||
.where(
|
||||
creatorFollowing.member.id.eq(memberId),
|
||||
creatorFollowing.creator.id.eq(creatorIdPath),
|
||||
creatorFollowing.isActive.isTrue
|
||||
)
|
||||
.exists()
|
||||
}
|
||||
|
||||
private fun notBlockedCreatorCondition(memberId: Long, creatorIdPath: Expression<Long>): BooleanExpression {
|
||||
val blockMember = QBlockMember("homeFollowingBlockMember")
|
||||
return JPAExpressions
|
||||
|
||||
Reference in New Issue
Block a user