feat(creator-channel): FanTalk 탭 repository를 추가한다

This commit is contained in:
2026-06-22 15:52:03 +09:00
parent 2848f07573
commit 408a342f17
3 changed files with 370 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.adapter.out.persistence
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.port.out.CreatorChannelFanTalkQueryPort
interface CreatorChannelFanTalkQueryRepository : CreatorChannelFanTalkQueryPort

View File

@@ -0,0 +1,138 @@
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.adapter.out.persistence
import com.querydsl.core.types.Projections
import com.querydsl.core.types.dsl.BooleanExpression
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.explorer.profile.QCreatorCheers.creatorCheers
import kr.co.vividnext.sodalive.member.QMember.member
import kr.co.vividnext.sodalive.member.block.QBlockMember
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.port.out.CreatorChannelFanTalkCreatorRecord
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.port.out.CreatorChannelFanTalkRecord
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.port.out.CreatorChannelFanTalkReplyRecord
import org.springframework.stereotype.Repository
@Repository
class DefaultCreatorChannelFanTalkQueryRepository(
private val queryFactory: JPAQueryFactory
) : CreatorChannelFanTalkQueryRepository {
override fun findCreator(creatorId: Long, viewerId: Long?): CreatorChannelFanTalkCreatorRecord? {
val creator = queryFactory
.select(member.id, member.role, member.nickname)
.from(member)
.where(
member.id.eq(creatorId),
member.isActive.isTrue
)
.fetchFirst() ?: return null
return CreatorChannelFanTalkCreatorRecord(
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("creatorChannelFanTalkBlockMember")
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 countFanTalks(creatorId: Long, viewerId: Long): Int {
return queryFactory
.select(creatorCheers.id.count())
.from(creatorCheers)
.where(fanTalkCondition(creatorId, viewerId))
.fetchOne()
?.toInt()
?: 0
}
override fun findFanTalks(
creatorId: Long,
viewerId: Long,
offset: Long,
limit: Int
): List<CreatorChannelFanTalkRecord> {
return queryFactory
.select(
Projections.constructor(
CreatorChannelFanTalkRecord::class.java,
creatorCheers.id,
creatorCheers.member.id,
creatorCheers.member.nickname,
creatorCheers.member.profileImage,
creatorCheers.cheers,
creatorCheers.createdAt
)
)
.from(creatorCheers)
.where(fanTalkCondition(creatorId, viewerId))
.orderBy(creatorCheers.createdAt.desc(), creatorCheers.id.desc())
.offset(offset)
.limit(limit.toLong())
.fetch()
}
override fun findCreatorReplies(
creatorId: Long,
parentFanTalkIds: List<Long>
): List<CreatorChannelFanTalkReplyRecord> {
if (parentFanTalkIds.isEmpty()) return emptyList()
return queryFactory
.select(
Projections.constructor(
CreatorChannelFanTalkReplyRecord::class.java,
creatorCheers.id,
creatorCheers.parent.id,
creatorCheers.member.id,
creatorCheers.member.nickname,
creatorCheers.member.profileImage,
creatorCheers.cheers,
creatorCheers.createdAt
)
)
.from(creatorCheers)
.where(
creatorCheers.creator.id.eq(creatorId),
creatorCheers.member.id.eq(creatorId),
creatorCheers.isActive.isTrue,
creatorCheers.parent.id.`in`(parentFanTalkIds)
)
.orderBy(creatorCheers.createdAt.asc(), creatorCheers.id.asc())
.fetch()
}
private fun fanTalkCondition(creatorId: Long, viewerId: Long): BooleanExpression {
return creatorCheers.creator.id.eq(creatorId)
.and(creatorCheers.isActive.isTrue)
.and(creatorCheers.parent.isNull)
.and(notBlockedFanTalkWriterCondition(viewerId))
}
private fun notBlockedFanTalkWriterCondition(viewerId: Long): BooleanExpression {
val viewerBlock = QBlockMember("viewerBlockFanTalkTabWriter")
val writerBlock = QBlockMember("writerBlockFanTalkTabViewer")
return creatorCheers.member.id.notIn(
queryFactory
.select(viewerBlock.blockedMember.id)
.from(viewerBlock)
.where(viewerBlock.member.id.eq(viewerId), viewerBlock.isActive.isTrue)
).and(
creatorCheers.member.id.notIn(
queryFactory
.select(writerBlock.member.id)
.from(writerBlock)
.where(writerBlock.blockedMember.id.eq(viewerId), writerBlock.isActive.isTrue)
)
)
}
}