feat(creator-channel): 커뮤니티 탭 repository를 추가한다
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.channel.community.adapter.out.persistence
|
||||
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityQueryPort
|
||||
|
||||
interface CreatorChannelCommunityQueryRepository : CreatorChannelCommunityQueryPort
|
||||
@@ -0,0 +1,340 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.channel.community.adapter.out.persistence
|
||||
|
||||
import com.querydsl.core.Tuple
|
||||
import com.querydsl.core.types.OrderSpecifier
|
||||
import com.querydsl.core.types.dsl.BooleanExpression
|
||||
import com.querydsl.core.types.dsl.CaseBuilder
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||
import kr.co.vividnext.sodalive.can.use.CanUsage
|
||||
import kr.co.vividnext.sodalive.can.use.QUseCan.useCan
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.QCreatorCommunity.creatorCommunity
|
||||
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.member.QMember.member
|
||||
import kr.co.vividnext.sodalive.member.block.QBlockMember
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityCreatorRecord
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityPostRecord
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
class DefaultCreatorChannelCommunityQueryRepository(
|
||||
private val queryFactory: JPAQueryFactory
|
||||
) : CreatorChannelCommunityQueryRepository {
|
||||
override fun findCreator(creatorId: Long, viewerId: Long?): CreatorChannelCommunityCreatorRecord? {
|
||||
val creator = queryFactory
|
||||
.select(member.id, member.role, member.nickname)
|
||||
.from(member)
|
||||
.where(
|
||||
member.id.eq(creatorId),
|
||||
member.isActive.isTrue
|
||||
)
|
||||
.fetchFirst() ?: return null
|
||||
|
||||
return CreatorChannelCommunityCreatorRecord(
|
||||
creatorId = creator.get(member.id)!!,
|
||||
role = creator.get(member.role)!!,
|
||||
nickname = creator.get(member.nickname)!!
|
||||
)
|
||||
}
|
||||
|
||||
override fun existsBlockedBetween(viewerId: Long, creatorId: Long): Boolean {
|
||||
val blockMember = QBlockMember("creatorChannelCommunityBlockMember")
|
||||
return queryFactory
|
||||
.select(blockMember.id)
|
||||
.from(blockMember)
|
||||
.where(
|
||||
blockMember.isActive.isTrue,
|
||||
blockMember.member.id.eq(viewerId).and(blockMember.blockedMember.id.eq(creatorId))
|
||||
.or(blockMember.member.id.eq(creatorId).and(blockMember.blockedMember.id.eq(viewerId)))
|
||||
)
|
||||
.fetchFirst() != null
|
||||
}
|
||||
|
||||
override fun countCommunityPosts(
|
||||
creatorId: Long,
|
||||
viewerId: Long,
|
||||
canViewAdultContent: Boolean
|
||||
): Int {
|
||||
return queryFactory
|
||||
.select(creatorCommunity.id.count())
|
||||
.from(creatorCommunity)
|
||||
.where(communityPostCondition(creatorId, canViewAdultContent))
|
||||
.fetchOne()
|
||||
?.toInt()
|
||||
?: 0
|
||||
}
|
||||
|
||||
override fun findCommunityPosts(
|
||||
creatorId: Long,
|
||||
viewerId: Long,
|
||||
canViewAdultContent: Boolean,
|
||||
offset: Long,
|
||||
limit: Int
|
||||
): List<CreatorChannelCommunityPostRecord> {
|
||||
val rows = queryFactory
|
||||
.selectCommunityPostRow()
|
||||
.from(creatorCommunity)
|
||||
.where(communityPostCondition(creatorId, canViewAdultContent))
|
||||
.orderBy(
|
||||
CaseBuilder()
|
||||
.`when`(creatorCommunity.isFixed.isTrue)
|
||||
.then(1)
|
||||
.otherwise(0)
|
||||
.desc(),
|
||||
creatorCommunity.fixedAt.desc().nullsLast(),
|
||||
CaseBuilder()
|
||||
.`when`(creatorCommunity.isFixed.isTrue)
|
||||
.then(creatorCommunity.id)
|
||||
.otherwise(0L)
|
||||
.desc(),
|
||||
creatorCommunity.createdAt.desc(),
|
||||
creatorCommunity.id.desc()
|
||||
)
|
||||
.offset(offset)
|
||||
.limit(limit.toLong())
|
||||
.fetch()
|
||||
|
||||
return rows.toCommunityPostRecords(creatorId, viewerId)
|
||||
}
|
||||
|
||||
override fun findHomeCommunityPosts(
|
||||
creatorId: Long,
|
||||
viewerId: Long,
|
||||
isPinned: Boolean,
|
||||
canViewAdultContent: Boolean,
|
||||
limit: Int
|
||||
): List<CreatorChannelCommunityPostRecord> {
|
||||
val rows = queryFactory
|
||||
.selectCommunityPostRow()
|
||||
.from(creatorCommunity)
|
||||
.where(
|
||||
homeCommunityPostCondition(creatorId, viewerId, canViewAdultContent),
|
||||
creatorCommunity.isFixed.eq(isPinned),
|
||||
pinnedPostCondition(isPinned)
|
||||
)
|
||||
.orderBy(*homeCommunityPostOrder(isPinned))
|
||||
.limit(limit.toLong())
|
||||
.fetch()
|
||||
|
||||
return rows.toCommunityPostRecords(creatorId, viewerId)
|
||||
}
|
||||
|
||||
private fun JPAQueryFactory.selectCommunityPostRow() = select(
|
||||
creatorCommunity.id,
|
||||
creatorCommunity.member.id,
|
||||
creatorCommunity.member.nickname,
|
||||
creatorCommunity.member.profileImage,
|
||||
creatorCommunity.imagePath,
|
||||
creatorCommunity.audioPath,
|
||||
creatorCommunity.content,
|
||||
creatorCommunity.price,
|
||||
creatorCommunity.createdAt,
|
||||
creatorCommunity.fixedAt,
|
||||
creatorCommunity.isFixed,
|
||||
creatorCommunity.isCommentAvailable
|
||||
)
|
||||
|
||||
private fun communityPostCondition(creatorId: Long, canViewAdultContent: Boolean): BooleanExpression {
|
||||
val condition = creatorCommunity.isActive.isTrue
|
||||
.and(creatorCommunity.member.id.eq(creatorId))
|
||||
.and(creatorCommunity.member.isActive.isTrue)
|
||||
|
||||
return if (canViewAdultContent) condition else condition.and(creatorCommunity.isAdult.isFalse)
|
||||
}
|
||||
|
||||
private fun homeCommunityPostCondition(
|
||||
creatorId: Long,
|
||||
viewerId: Long,
|
||||
canViewAdultContent: Boolean
|
||||
): BooleanExpression {
|
||||
val condition = creatorCommunity.member.id.eq(creatorId)
|
||||
.and(creatorCommunity.member.isActive.isTrue)
|
||||
.and(
|
||||
creatorCommunity.isActive.isTrue.or(
|
||||
queryFactory
|
||||
.select(useCan.id)
|
||||
.from(useCan)
|
||||
.where(
|
||||
useCan.member.id.eq(viewerId),
|
||||
useCan.communityPost.id.eq(creatorCommunity.id),
|
||||
useCan.canUsage.eq(CanUsage.PAID_COMMUNITY_POST),
|
||||
useCan.isRefund.isFalse
|
||||
)
|
||||
.exists()
|
||||
)
|
||||
)
|
||||
|
||||
return if (canViewAdultContent) condition else condition.and(creatorCommunity.isAdult.isFalse)
|
||||
}
|
||||
|
||||
private fun pinnedPostCondition(isPinned: Boolean): BooleanExpression? {
|
||||
return if (isPinned) creatorCommunity.fixedAt.isNotNull else null
|
||||
}
|
||||
|
||||
private fun homeCommunityPostOrder(isPinned: Boolean): Array<OrderSpecifier<*>> {
|
||||
return if (isPinned) {
|
||||
arrayOf(creatorCommunity.fixedAt.desc(), creatorCommunity.id.desc())
|
||||
} else {
|
||||
arrayOf(creatorCommunity.createdAt.desc(), creatorCommunity.id.desc())
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<Tuple>.toCommunityPostRecords(
|
||||
creatorId: Long,
|
||||
viewerId: Long
|
||||
): List<CreatorChannelCommunityPostRecord> {
|
||||
val postIds = map { it.postId }
|
||||
val orderedPostIds = orderedCommunityPostIds(creatorId, viewerId, postIds)
|
||||
val likeCounts = communityLikeCounts(postIds)
|
||||
val commentAvailablePostIds = filter { it.isCommentAvailable }.map { it.postId }
|
||||
val commentCounts = communityCommentCounts(commentAvailablePostIds, creatorId, viewerId)
|
||||
|
||||
return map { row ->
|
||||
val postId = row.postId
|
||||
val isPinned = row.isPinned
|
||||
CreatorChannelCommunityPostRecord(
|
||||
postId = postId,
|
||||
creatorId = row.creatorId,
|
||||
creatorNickname = row.creatorNickname,
|
||||
creatorProfilePath = row.creatorProfilePath,
|
||||
imagePath = row.imagePath,
|
||||
audioPath = row.audioPath,
|
||||
content = row.content,
|
||||
price = row.price,
|
||||
createdAt = row.createdAt,
|
||||
existOrdered = postId in orderedPostIds,
|
||||
isCommentAvailable = row.isCommentAvailable,
|
||||
likeCount = likeCounts[postId] ?: 0,
|
||||
commentCount = commentCounts[postId] ?: 0,
|
||||
isPinned = isPinned
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun orderedCommunityPostIds(
|
||||
creatorId: Long,
|
||||
viewerId: Long,
|
||||
postIds: List<Long>
|
||||
): Set<Long> {
|
||||
if (postIds.isEmpty()) return emptySet()
|
||||
if (creatorId == viewerId) return postIds.toSet()
|
||||
|
||||
return queryFactory
|
||||
.select(useCan.communityPost.id)
|
||||
.distinct()
|
||||
.from(useCan)
|
||||
.where(
|
||||
useCan.member.id.eq(viewerId),
|
||||
useCan.communityPost.id.`in`(postIds),
|
||||
useCan.canUsage.eq(CanUsage.PAID_COMMUNITY_POST),
|
||||
useCan.isRefund.isFalse
|
||||
)
|
||||
.fetch()
|
||||
.toSet()
|
||||
}
|
||||
|
||||
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>,
|
||||
creatorId: Long,
|
||||
viewerId: 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,
|
||||
visibleSecretCommentCondition(creatorId, viewerId),
|
||||
notBlockedCommentWriterCondition(viewerId)
|
||||
)
|
||||
.groupBy(creatorCommunityComment.creatorCommunity.id)
|
||||
.fetch()
|
||||
.associate {
|
||||
it.get(creatorCommunityComment.creatorCommunity.id)!! to
|
||||
(it.get(creatorCommunityComment.id.count())?.toInt() ?: 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visibleSecretCommentCondition(creatorId: Long, viewerId: Long): BooleanExpression {
|
||||
return creatorCommunityComment.isSecret.isFalse
|
||||
.or(
|
||||
creatorCommunityComment.creatorCommunity.member.id.eq(creatorId)
|
||||
.and(creatorCommunityComment.creatorCommunity.member.id.eq(viewerId))
|
||||
)
|
||||
.or(creatorCommunityComment.member.id.eq(viewerId))
|
||||
}
|
||||
|
||||
private fun notBlockedCommentWriterCondition(viewerId: Long): BooleanExpression {
|
||||
val viewerBlock = QBlockMember("communityCommentViewerBlockWriter")
|
||||
val writerBlock = QBlockMember("communityCommentWriterBlockViewer")
|
||||
return creatorCommunityComment.member.id.notIn(
|
||||
queryFactory
|
||||
.select(viewerBlock.blockedMember.id)
|
||||
.from(viewerBlock)
|
||||
.where(viewerBlock.member.id.eq(viewerId), viewerBlock.isActive.isTrue)
|
||||
).and(
|
||||
creatorCommunityComment.member.id.notIn(
|
||||
queryFactory
|
||||
.select(writerBlock.member.id)
|
||||
.from(writerBlock)
|
||||
.where(writerBlock.blockedMember.id.eq(viewerId), writerBlock.isActive.isTrue)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private val Tuple.postId: Long
|
||||
get() = get(creatorCommunity.id)!!
|
||||
|
||||
private val Tuple.creatorId: Long
|
||||
get() = get(creatorCommunity.member.id)!!
|
||||
|
||||
private val Tuple.creatorNickname: String
|
||||
get() = get(creatorCommunity.member.nickname)!!
|
||||
|
||||
private val Tuple.creatorProfilePath: String?
|
||||
get() = get(creatorCommunity.member.profileImage)
|
||||
|
||||
private val Tuple.imagePath: String?
|
||||
get() = get(creatorCommunity.imagePath)
|
||||
|
||||
private val Tuple.audioPath: String?
|
||||
get() = get(creatorCommunity.audioPath)
|
||||
|
||||
private val Tuple.content: String
|
||||
get() = get(creatorCommunity.content)!!
|
||||
|
||||
private val Tuple.price: Int
|
||||
get() = get(creatorCommunity.price)!!
|
||||
|
||||
private val Tuple.createdAt
|
||||
get() = get(creatorCommunity.createdAt)!!
|
||||
|
||||
private val Tuple.fixedAt
|
||||
get() = get(creatorCommunity.fixedAt)
|
||||
|
||||
private val Tuple.isPinned: Boolean
|
||||
get() = get(creatorCommunity.isFixed)!!
|
||||
|
||||
private val Tuple.isCommentAvailable: Boolean
|
||||
get() = get(creatorCommunity.isCommentAvailable)!!
|
||||
}
|
||||
Reference in New Issue
Block a user