test #426
@@ -4,8 +4,6 @@ import com.querydsl.core.types.Projections
|
||||
import com.querydsl.core.types.dsl.BooleanExpression
|
||||
import com.querydsl.core.types.dsl.Expressions
|
||||
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.chat.character.QChatCharacter.chatCharacter
|
||||
import kr.co.vividnext.sodalive.content.ContentType
|
||||
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
|
||||
@@ -15,9 +13,6 @@ 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.explorer.profile.QCreatorCheers.creatorCheers
|
||||
import kr.co.vividnext.sodalive.explorer.profile.channelDonation.QChannelDonationMessage.channelDonationMessage
|
||||
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.extensions.removeDeletedNicknamePrefix
|
||||
import kr.co.vividnext.sodalive.live.room.GenderRestriction
|
||||
import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
|
||||
@@ -30,7 +25,6 @@ import kr.co.vividnext.sodalive.member.following.QCreatorFollowing.creatorFollow
|
||||
import kr.co.vividnext.sodalive.v2.common.domain.CreatorActivityType
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.home.port.out.CreatorChannelActivityRecord
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.home.port.out.CreatorChannelAudioContentRecord
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.home.port.out.CreatorChannelCommunityPostRecord
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.home.port.out.CreatorChannelCreatorRecord
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.home.port.out.CreatorChannelDonationRecord
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.home.port.out.CreatorChannelFanTalkRecord
|
||||
@@ -207,90 +201,6 @@ class DefaultCreatorChannelHomeQueryRepository(
|
||||
.fetch()
|
||||
}
|
||||
|
||||
override fun findCommunityPosts(
|
||||
creatorId: Long,
|
||||
viewerId: Long?,
|
||||
isFixed: Boolean,
|
||||
canViewAdultContent: Boolean,
|
||||
limit: Int
|
||||
): List<CreatorChannelCommunityPostRecord> {
|
||||
val posts = queryFactory
|
||||
.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
|
||||
)
|
||||
.from(creatorCommunity)
|
||||
.where(
|
||||
creatorCommunity.member.id.eq(creatorId),
|
||||
creatorCommunity.member.isActive.isTrue,
|
||||
visibleCommunityPostCondition(viewerId),
|
||||
creatorCommunity.isFixed.eq(isFixed),
|
||||
fixedNoticeCondition(isFixed),
|
||||
adultCommunityCondition(canViewAdultContent)
|
||||
)
|
||||
.orderBy(
|
||||
if (isFixed) creatorCommunity.fixedAt.desc() else creatorCommunity.createdAt.desc(),
|
||||
creatorCommunity.id.desc()
|
||||
)
|
||||
.limit(limit.toLong())
|
||||
.fetch()
|
||||
|
||||
val postIds = posts.map { it.get(creatorCommunity.id)!! }
|
||||
val orderedPostIds = orderedCommunityPostIds(creatorId, viewerId, postIds)
|
||||
val likeCounts = communityLikeCounts(postIds)
|
||||
val commentCounts = communityCommentCounts(
|
||||
postIds = posts.filter { it.get(creatorCommunity.isCommentAvailable)!! }.map { it.get(creatorCommunity.id)!! },
|
||||
viewerId = viewerId,
|
||||
isContentCreator = viewerId == creatorId
|
||||
)
|
||||
|
||||
return posts
|
||||
.map {
|
||||
val postId = it.get(creatorCommunity.id)!!
|
||||
val postCreatorId = it.get(creatorCommunity.member.id)!!
|
||||
val isFixedPost = it.get(creatorCommunity.isFixed)!!
|
||||
val price = it.get(creatorCommunity.price)!!
|
||||
val existOrdered = postId in orderedPostIds
|
||||
val canAccessPaidContent = canAccessPaidCommunityContent(
|
||||
price = price,
|
||||
viewerId = viewerId,
|
||||
creatorId = postCreatorId,
|
||||
existOrdered = existOrdered
|
||||
)
|
||||
CreatorChannelCommunityPostRecord(
|
||||
postId = postId,
|
||||
creatorId = postCreatorId,
|
||||
creatorNickname = it.get(creatorCommunity.member.nickname)!!,
|
||||
creatorProfilePath = it.get(creatorCommunity.member.profileImage),
|
||||
imagePath = it.get(creatorCommunity.imagePath),
|
||||
audioPath = if (canAccessPaidContent) it.get(creatorCommunity.audioPath) else null,
|
||||
content = maskPaidCommunityContent(
|
||||
content = it.get(creatorCommunity.content)!!,
|
||||
canAccessPaidContent = canAccessPaidContent
|
||||
),
|
||||
price = price,
|
||||
date = if (isFixedPost) {
|
||||
it.get(creatorCommunity.fixedAt) ?: it.get(creatorCommunity.createdAt)!!
|
||||
} else {
|
||||
it.get(creatorCommunity.createdAt)!!
|
||||
},
|
||||
existOrdered = existOrdered,
|
||||
likeCount = likeCounts[postId] ?: 0,
|
||||
commentCount = commentCounts[postId] ?: 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun findSchedules(
|
||||
creatorId: Long,
|
||||
now: LocalDateTime,
|
||||
@@ -639,103 +549,6 @@ class DefaultCreatorChannelHomeQueryRepository(
|
||||
.fetchFirst()
|
||||
}
|
||||
|
||||
private fun orderedCommunityPostIds(creatorId: Long, viewerId: Long?, postIds: List<Long>): Set<Long> {
|
||||
if (viewerId == null || postIds.isEmpty()) return emptySet()
|
||||
if (viewerId == creatorId) return postIds.toSet()
|
||||
return queryFactory
|
||||
.select(useCan.communityPost.id)
|
||||
.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>, viewerId: Long?, isContentCreator: Boolean): Map<Long, Int> {
|
||||
if (postIds.isEmpty()) return emptyMap()
|
||||
var where = creatorCommunityComment.creatorCommunity.id.`in`(postIds)
|
||||
.and(creatorCommunityComment.isActive.isTrue)
|
||||
.and(creatorCommunityComment.parent.isNull)
|
||||
|
||||
if (viewerId != null) {
|
||||
where = where
|
||||
.and(creatorCommunityComment.member.id.notIn(blockedMemberIdSubQuery(viewerId)))
|
||||
.and(creatorCommunityComment.member.id.notIn(blockingMemberIdSubQuery(viewerId)))
|
||||
}
|
||||
|
||||
if (!isContentCreator) {
|
||||
where = where.and(
|
||||
creatorCommunityComment.isSecret.isFalse.or(
|
||||
viewerId?.let { creatorCommunityComment.member.id.eq(it) }
|
||||
?: creatorCommunityComment.isSecret.isFalse
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return queryFactory
|
||||
.select(creatorCommunityComment.creatorCommunity.id, creatorCommunityComment.id.count())
|
||||
.from(creatorCommunityComment)
|
||||
.where(where)
|
||||
.groupBy(creatorCommunityComment.creatorCommunity.id)
|
||||
.fetch()
|
||||
.associate {
|
||||
it.get(creatorCommunityComment.creatorCommunity.id)!! to
|
||||
(it.get(creatorCommunityComment.id.count())?.toInt() ?: 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun blockedMemberIdSubQuery(viewerId: Long) = QBlockMember("communityCommentViewerBlock").let { viewerBlock ->
|
||||
queryFactory
|
||||
.select(viewerBlock.blockedMember.id)
|
||||
.from(viewerBlock)
|
||||
.where(viewerBlock.member.id.eq(viewerId), viewerBlock.isActive.isTrue)
|
||||
}
|
||||
|
||||
private fun blockingMemberIdSubQuery(viewerId: Long) = QBlockMember("communityCommentWriterBlock").let { writerBlock ->
|
||||
queryFactory
|
||||
.select(writerBlock.member.id)
|
||||
.from(writerBlock)
|
||||
.where(writerBlock.blockedMember.id.eq(viewerId), writerBlock.isActive.isTrue)
|
||||
}
|
||||
|
||||
private fun canAccessPaidCommunityContent(
|
||||
price: Int,
|
||||
viewerId: Long?,
|
||||
creatorId: Long,
|
||||
existOrdered: Boolean
|
||||
): Boolean {
|
||||
return price <= 0 || viewerId == creatorId || existOrdered
|
||||
}
|
||||
|
||||
private fun maskPaidCommunityContent(content: String, canAccessPaidContent: Boolean): String {
|
||||
if (canAccessPaidContent) return content
|
||||
val length = content.codePointCount(0, content.length)
|
||||
val endIndex = if (length > 15) {
|
||||
content.offsetByCodePoints(0, 15)
|
||||
} else {
|
||||
content.offsetByCodePoints(0, length / 2)
|
||||
}
|
||||
return content.substring(0, endIndex).plus("...")
|
||||
}
|
||||
|
||||
private fun firstAudioDebutAt(creatorId: Long, now: LocalDateTime): LocalDateTime? {
|
||||
val firstThreeUploads = queryFactory
|
||||
.select(audioContent.releaseDate, audioContent.createdAt)
|
||||
@@ -793,31 +606,6 @@ class DefaultCreatorChannelHomeQueryRepository(
|
||||
return liveRoom.isAvailableJoinCreator.isTrue.or(liveRoom.member.id.eq(viewerId))
|
||||
}
|
||||
|
||||
private fun adultCommunityCondition(canViewAdultContent: Boolean): BooleanExpression? {
|
||||
return if (canViewAdultContent) null else creatorCommunity.isAdult.isFalse
|
||||
}
|
||||
|
||||
private fun fixedNoticeCondition(isFixed: Boolean): BooleanExpression? {
|
||||
return if (isFixed) creatorCommunity.fixedAt.isNotNull else null
|
||||
}
|
||||
|
||||
private fun visibleCommunityPostCondition(viewerId: Long?): BooleanExpression {
|
||||
val activePost = creatorCommunity.isActive.isTrue
|
||||
if (viewerId == null) return activePost
|
||||
return activePost.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()
|
||||
)
|
||||
}
|
||||
|
||||
private fun adultSeriesCondition(canViewAdultContent: Boolean): BooleanExpression? {
|
||||
return if (canViewAdultContent) null else series.isAdult.isFalse
|
||||
}
|
||||
|
||||
@@ -34,14 +34,6 @@ interface CreatorChannelHomeQueryPort {
|
||||
limit: Int = 8
|
||||
): List<CreatorChannelDonationRecord>
|
||||
|
||||
fun findCommunityPosts(
|
||||
creatorId: Long,
|
||||
viewerId: Long?,
|
||||
isFixed: Boolean,
|
||||
canViewAdultContent: Boolean,
|
||||
limit: Int = 3
|
||||
): List<CreatorChannelCommunityPostRecord>
|
||||
|
||||
fun findSchedules(
|
||||
creatorId: Long,
|
||||
now: LocalDateTime,
|
||||
@@ -140,21 +132,6 @@ data class CreatorChannelSeriesRecord(
|
||||
val isOriginal: Boolean
|
||||
)
|
||||
|
||||
data class CreatorChannelCommunityPostRecord(
|
||||
val postId: Long,
|
||||
val creatorId: Long,
|
||||
val creatorNickname: String,
|
||||
val creatorProfilePath: String?,
|
||||
val imagePath: String?,
|
||||
val audioPath: String?,
|
||||
val content: String,
|
||||
val price: Int,
|
||||
val date: LocalDateTime,
|
||||
val existOrdered: Boolean,
|
||||
val likeCount: Int,
|
||||
val commentCount: Int
|
||||
)
|
||||
|
||||
data class CreatorChannelFanTalkSummaryRecord(
|
||||
val totalCount: Int,
|
||||
val latestFanTalk: CreatorChannelFanTalkRecord?
|
||||
|
||||
@@ -2,8 +2,6 @@ package kr.co.vividnext.sodalive.v2.creator.channel.home.adapter.out.persistence
|
||||
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||
import kr.co.vividnext.sodalive.admin.content.series.genre.SeriesGenre
|
||||
import kr.co.vividnext.sodalive.can.use.CanUsage
|
||||
import kr.co.vividnext.sodalive.can.use.UseCan
|
||||
import kr.co.vividnext.sodalive.chat.character.ChatCharacter
|
||||
import kr.co.vividnext.sodalive.configs.QueryDslConfig
|
||||
import kr.co.vividnext.sodalive.content.AudioContent
|
||||
@@ -15,9 +13,6 @@ import kr.co.vividnext.sodalive.creator.admin.content.series.Series
|
||||
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesContent
|
||||
import kr.co.vividnext.sodalive.explorer.profile.CreatorCheers
|
||||
import kr.co.vividnext.sodalive.explorer.profile.channelDonation.ChannelDonationMessage
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunity
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.CreatorCommunityComment
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.CreatorCommunityLike
|
||||
import kr.co.vividnext.sodalive.live.room.GenderRestriction
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoom
|
||||
import kr.co.vividnext.sodalive.live.room.visit.LiveRoomVisit
|
||||
@@ -143,14 +138,10 @@ class DefaultCreatorChannelHomeQueryRepositoryTest @Autowired constructor(
|
||||
"audio queries in this repository must project required columns"
|
||||
)
|
||||
assertFalse(source.contains(".selectFrom(channelDonationMessage)"), "donation query must project required columns")
|
||||
assertFalse(source.contains(".selectFrom(creatorCommunity)"), "community query must project required columns")
|
||||
assertFalse(source.contains(".selectFrom(series)"), "series query must project required columns")
|
||||
assertFalse(source.contains(".select(series)"), "series query must not fetch full Series entity")
|
||||
assertFalse(source.contains(".selectFrom(creatorCheers)"), "fan talk latest query must project required columns")
|
||||
assertFalse(source.contains(".fetch()\n .size"), "counts must use DB count instead of fetching ids")
|
||||
assertFalse(source.contains("existsCommunityOrder("), "community orders must be bulk calculated")
|
||||
assertFalse(source.contains("countCommunityLikes("), "community likes must be bulk calculated")
|
||||
assertFalse(source.contains("countCommunityComments("), "community comments must be bulk calculated")
|
||||
assertFalse(source.contains("publishedSeriesContents("), "series contents must be bulk calculated")
|
||||
assertFalse(source.contains("hasNewSeriesContent("), "series new flags must be bulk calculated")
|
||||
assertTrue(
|
||||
@@ -214,17 +205,6 @@ class DefaultCreatorChannelHomeQueryRepositoryTest @Autowired constructor(
|
||||
saveOrder(viewer, creator, latestAudio, OrderType.KEEP)
|
||||
saveOrder(viewer, creator, listAudio, OrderType.RENTAL, endDate = now.plusDays(1))
|
||||
val donation = saveDonation(creator, donor, 500, now.minusHours(3), additionalMessage = "integrated thanks")
|
||||
val notice = saveCommunity(creator, isFixed = true, fixedAt = now.minusHours(4), price = 0)
|
||||
val community = saveCommunity(
|
||||
creator,
|
||||
isFixed = false,
|
||||
price = 100,
|
||||
imagePath = "community.png",
|
||||
audioPath = "community.mp3"
|
||||
)
|
||||
saveCommunityOrder(viewer, community, isRefund = false)
|
||||
saveCommunityLike(viewer, community, isActive = true)
|
||||
saveCommunityComment(viewer, community, isActive = true)
|
||||
val fanTalk = saveCheers(fan, creator, "integrated fan talk", isActive = true, now.minusMinutes(30))
|
||||
saveVisit(currentLive, viewer)
|
||||
flushAndClear()
|
||||
@@ -247,7 +227,6 @@ class DefaultCreatorChannelHomeQueryRepositoryTest @Autowired constructor(
|
||||
viewerId = viewer.id!!
|
||||
)
|
||||
val donations = repository.findChannelDonations(creator.id!!, viewer.id!!, now, limit = 8)
|
||||
val notices = repository.findCommunityPosts(creator.id!!, viewer.id!!, isFixed = true, false, limit = 3)
|
||||
val schedules = repository.findSchedules(
|
||||
creator.id!!,
|
||||
now,
|
||||
@@ -266,7 +245,6 @@ class DefaultCreatorChannelHomeQueryRepositoryTest @Autowired constructor(
|
||||
limit = 9
|
||||
)
|
||||
val seriesRecords = repository.findSeries(creator.id!!, viewer.id!!, now, false, ContentType.ALL, limit = 8)
|
||||
val communities = repository.findCommunityPosts(creator.id!!, viewer.id!!, isFixed = false, false, limit = 3)
|
||||
val fanTalkSummary = repository.findFanTalkSummary(creator.id!!, viewer.id!!)
|
||||
val activity = repository.findActivity(creator.id!!, now)
|
||||
val sns = repository.findSns(creator.id!!)
|
||||
@@ -279,7 +257,6 @@ class DefaultCreatorChannelHomeQueryRepositoryTest @Autowired constructor(
|
||||
assertFalse(latestAudioRecord.isRented)
|
||||
assertEquals(listOf(donation.can), donations.map { it.can })
|
||||
assertEquals("integrated thanks", donations.single().message)
|
||||
assertEquals(listOf(notice.id), notices.map { it.postId })
|
||||
assertEquals(listOf(liveSchedule.id, audioSchedule.id), schedules.map { it.targetId })
|
||||
assertEquals(listOf(CreatorActivityType.LIVE, CreatorActivityType.AUDIO), schedules.map { it.type })
|
||||
assertEquals(listOf(listAudio.id, firstAudio.id), audioContents.map { it.audioContentId })
|
||||
@@ -287,10 +264,6 @@ class DefaultCreatorChannelHomeQueryRepositoryTest @Autowired constructor(
|
||||
assertEquals(listOf(true, false), audioContents.map { it.isRented })
|
||||
assertEquals(listOf(series.id), seriesRecords.map { it.seriesId })
|
||||
assertEquals(true, seriesRecords.single().isOriginal)
|
||||
assertEquals(listOf(community.id), communities.map { it.postId })
|
||||
assertEquals(1, communities.single().likeCount)
|
||||
assertEquals(1, communities.single().commentCount)
|
||||
assertTrue(communities.single().existOrdered)
|
||||
assertEquals(1, fanTalkSummary.totalCount)
|
||||
assertEquals(fanTalk.id, fanTalkSummary.latestFanTalk!!.fanTalkId)
|
||||
assertEquals(now.minusDays(3), activity.debutDate)
|
||||
@@ -642,54 +615,6 @@ class DefaultCreatorChannelHomeQueryRepositoryTest @Autowired constructor(
|
||||
assertTrue(records.single().isFirstContent)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("채널 후원, 공지, 커뮤니티, 팬 Talk는 기존 전체보기 의미에 맞는 요약을 조회한다")
|
||||
fun shouldFindDonationsCommunitiesAndFanTalkSummary() {
|
||||
val now = LocalDateTime.of(2026, 6, 12, 12, 0)
|
||||
val creator = saveMember("community-creator", MemberRole.CREATOR)
|
||||
val viewer = saveMember("community-viewer", MemberRole.USER)
|
||||
val donor = saveMember("community-donor", MemberRole.USER)
|
||||
val blockedWriter = saveMember("blocked-talk-writer", MemberRole.USER)
|
||||
val donation = saveDonation(creator, donor, 300, now.minusDays(1))
|
||||
saveDonation(creator, donor, 100, now.minusMonths(1))
|
||||
val notice = saveCommunity(creator, isFixed = true, fixedAt = now.minusHours(1), price = 0)
|
||||
saveCommunity(creator, isFixed = true, fixedAt = null, price = 0)
|
||||
val post = saveCommunity(creator, isFixed = false, price = 100, imagePath = "community.png", audioPath = "community.mp3")
|
||||
saveCommunityLike(viewer, post, isActive = true)
|
||||
saveCommunityComment(viewer, post, isActive = true)
|
||||
saveCommunityOrder(viewer, post, isRefund = false)
|
||||
val latestTalk = saveCheers(viewer, creator, "latest", isActive = true, now.minusMinutes(1))
|
||||
saveCheers(blockedWriter, creator, "blocked", isActive = true, now)
|
||||
saveBlock(viewer, blockedWriter)
|
||||
flushAndClear()
|
||||
|
||||
val donations = repository.findChannelDonations(creator.id!!, viewer.id!!, now, limit = 8)
|
||||
val notices = repository.findCommunityPosts(
|
||||
creator.id!!,
|
||||
viewer.id!!,
|
||||
isFixed = true,
|
||||
canViewAdultContent = false,
|
||||
limit = 3
|
||||
)
|
||||
val posts = repository.findCommunityPosts(
|
||||
creator.id!!,
|
||||
viewer.id!!,
|
||||
isFixed = false,
|
||||
canViewAdultContent = false,
|
||||
limit = 3
|
||||
)
|
||||
val fanTalk = repository.findFanTalkSummary(creator.id!!, viewer.id!!)
|
||||
|
||||
assertEquals(listOf(donation.can), donations.map { it.can })
|
||||
assertEquals(listOf(notice.id), notices.map { it.postId })
|
||||
assertEquals(listOf(post.id), posts.map { it.postId })
|
||||
assertEquals(1, posts.single().likeCount)
|
||||
assertEquals(1, posts.single().commentCount)
|
||||
assertTrue(posts.single().existOrdered)
|
||||
assertEquals(1, fanTalk.totalCount)
|
||||
assertEquals(latestTalk.id, fanTalk.latestFanTalk!!.fanTalkId)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("팬 Talk 요약은 활성 최상위 글 전체 개수와 최신 1개만 조회한다")
|
||||
fun shouldSummarizeFanTalkWithTotalCountAndLatestOnly() {
|
||||
@@ -709,181 +634,6 @@ class DefaultCreatorChannelHomeQueryRepositoryTest @Autowired constructor(
|
||||
assertEquals("latest", summary.latestFanTalk!!.content)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("커뮤니티는 성인 정책과 작성자 본인의 구매 여부 의미를 반영한다")
|
||||
fun shouldFilterAdultCommunityAndTreatCreatorAsOrdered() {
|
||||
val creator = saveMember("adult-community-creator", MemberRole.CREATOR)
|
||||
val visiblePost = saveCommunity(creator, isFixed = false, price = 100)
|
||||
saveCommunity(creator, isFixed = false, price = 100, isAdult = true)
|
||||
flushAndClear()
|
||||
|
||||
val viewerPosts = repository.findCommunityPosts(
|
||||
creator.id!!,
|
||||
viewerId = creator.id!!,
|
||||
isFixed = false,
|
||||
canViewAdultContent = false,
|
||||
limit = 3
|
||||
)
|
||||
|
||||
assertEquals(listOf(visiblePost.id), viewerPosts.map { it.postId })
|
||||
assertTrue(viewerPosts.single().existOrdered)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("유료 커뮤니티는 비구매자에게 본문을 축약하고 오디오를 숨긴다")
|
||||
fun shouldMaskPaidCommunityContentAndAudioForNonBuyer() {
|
||||
val creator = saveMember("paid-community-creator", MemberRole.CREATOR)
|
||||
val viewer = saveMember("paid-community-viewer", MemberRole.USER)
|
||||
val content = "12345678901234567890"
|
||||
saveCommunity(
|
||||
creator,
|
||||
isFixed = false,
|
||||
price = 100,
|
||||
audioPath = "paid-audio.mp3",
|
||||
content = content
|
||||
)
|
||||
flushAndClear()
|
||||
|
||||
val posts = repository.findCommunityPosts(
|
||||
creator.id!!,
|
||||
viewerId = viewer.id!!,
|
||||
isFixed = false,
|
||||
canViewAdultContent = false,
|
||||
limit = 3
|
||||
)
|
||||
|
||||
assertEquals("123456789012345...", posts.single().content)
|
||||
assertEquals(null, posts.single().audioPath)
|
||||
assertFalse(posts.single().existOrdered)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("유료 커뮤니티는 구매자와 작성자에게 본문과 오디오를 노출한다")
|
||||
fun shouldExposePaidCommunityContentAndAudioForBuyerAndCreator() {
|
||||
val creator = saveMember("paid-community-owner", MemberRole.CREATOR)
|
||||
val buyer = saveMember("paid-community-buyer", MemberRole.USER)
|
||||
val post = saveCommunity(
|
||||
creator,
|
||||
isFixed = false,
|
||||
price = 100,
|
||||
audioPath = "paid-visible.mp3",
|
||||
content = "paid full content"
|
||||
)
|
||||
saveCommunityOrder(buyer, post, isRefund = false)
|
||||
flushAndClear()
|
||||
|
||||
val buyerPosts = repository.findCommunityPosts(
|
||||
creator.id!!,
|
||||
viewerId = buyer.id!!,
|
||||
isFixed = false,
|
||||
canViewAdultContent = false,
|
||||
limit = 3
|
||||
)
|
||||
val creatorPosts = repository.findCommunityPosts(
|
||||
creator.id!!,
|
||||
viewerId = creator.id!!,
|
||||
isFixed = false,
|
||||
canViewAdultContent = false,
|
||||
limit = 3
|
||||
)
|
||||
|
||||
assertEquals("paid full content", buyerPosts.single().content)
|
||||
assertEquals("paid-visible.mp3", buyerPosts.single().audioPath)
|
||||
assertTrue(buyerPosts.single().existOrdered)
|
||||
assertEquals("paid full content", creatorPosts.single().content)
|
||||
assertEquals("paid-visible.mp3", creatorPosts.single().audioPath)
|
||||
assertTrue(creatorPosts.single().existOrdered)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("구매한 유료 커뮤니티는 크리에이터가 삭제해도 구매자에게 조회된다")
|
||||
fun shouldExposeDeletedPaidCommunityContentToBuyer() {
|
||||
val creator = saveMember("deleted-paid-community-creator", MemberRole.CREATOR)
|
||||
val buyer = saveMember("deleted-paid-community-buyer", MemberRole.USER)
|
||||
val nonBuyer = saveMember("deleted-paid-community-non-buyer", MemberRole.USER)
|
||||
val post = saveCommunity(
|
||||
creator,
|
||||
isFixed = false,
|
||||
price = 100,
|
||||
audioPath = "deleted-paid.mp3",
|
||||
content = "deleted paid content",
|
||||
isActive = false
|
||||
)
|
||||
saveCommunityOrder(buyer, post, isRefund = false)
|
||||
flushAndClear()
|
||||
|
||||
val buyerPosts = repository.findCommunityPosts(
|
||||
creator.id!!,
|
||||
viewerId = buyer.id!!,
|
||||
isFixed = false,
|
||||
canViewAdultContent = false,
|
||||
limit = 3
|
||||
)
|
||||
val nonBuyerPosts = repository.findCommunityPosts(
|
||||
creator.id!!,
|
||||
viewerId = nonBuyer.id!!,
|
||||
isFixed = false,
|
||||
canViewAdultContent = false,
|
||||
limit = 3
|
||||
)
|
||||
|
||||
assertEquals(listOf(post.id), buyerPosts.map { it.postId })
|
||||
assertEquals("deleted paid content", buyerPosts.single().content)
|
||||
assertEquals("deleted-paid.mp3", buyerPosts.single().audioPath)
|
||||
assertTrue(buyerPosts.single().existOrdered)
|
||||
assertEquals(emptyList<Long>(), nonBuyerPosts.map { it.postId })
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("커뮤니티 댓글 수는 기존 목록처럼 보이는 최상위 댓글만 계산한다")
|
||||
fun shouldCountVisibleRootCommunityCommentsOnly() {
|
||||
val creator = saveMember("comment-count-creator", MemberRole.CREATOR)
|
||||
val viewer = saveMember("comment-count-viewer", MemberRole.USER)
|
||||
val blockedWriter = saveMember("comment-count-blocked", MemberRole.USER)
|
||||
val blockingWriter = saveMember("comment-count-blocking", MemberRole.USER)
|
||||
val secretWriter = saveMember("comment-count-secret", MemberRole.USER)
|
||||
val post = saveCommunity(creator, isFixed = false, price = 0)
|
||||
val visibleRoot = saveCommunityComment(viewer, post, isActive = true)
|
||||
saveCommunityComment(viewer, post, isActive = true, parent = visibleRoot)
|
||||
saveCommunityComment(viewer, post, isActive = false)
|
||||
saveCommunityComment(blockedWriter, post, isActive = true)
|
||||
saveCommunityComment(blockingWriter, post, isActive = true)
|
||||
saveCommunityComment(secretWriter, post, isActive = true, isSecret = true)
|
||||
saveBlock(viewer, blockedWriter)
|
||||
saveBlock(blockingWriter, viewer)
|
||||
flushAndClear()
|
||||
|
||||
val posts = repository.findCommunityPosts(
|
||||
creator.id!!,
|
||||
viewerId = viewer.id!!,
|
||||
isFixed = false,
|
||||
canViewAdultContent = false,
|
||||
limit = 3
|
||||
)
|
||||
|
||||
assertEquals(1, posts.single().commentCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("커뮤니티 댓글 수는 댓글 불가 게시글이면 기존 목록처럼 0으로 계산한다")
|
||||
fun shouldReturnZeroCommentCountWhenCommunityCommentUnavailable() {
|
||||
val creator = saveMember("comment-unavailable-creator", MemberRole.CREATOR)
|
||||
val viewer = saveMember("comment-unavailable-viewer", MemberRole.USER)
|
||||
val post = saveCommunity(creator, isFixed = false, price = 0, isCommentAvailable = false)
|
||||
saveCommunityComment(viewer, post, isActive = true)
|
||||
flushAndClear()
|
||||
|
||||
val posts = repository.findCommunityPosts(
|
||||
creator.id!!,
|
||||
viewerId = viewer.id!!,
|
||||
isFixed = false,
|
||||
canViewAdultContent = false,
|
||||
limit = 3
|
||||
)
|
||||
|
||||
assertEquals(0, posts.single().commentCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("채널 후원은 KST 기준 이번 달과 크리에이터의 비밀 후원 열람을 반영한다")
|
||||
fun shouldFindKstMonthDonationsAndExposeSecretDonationToCreator() {
|
||||
@@ -1421,34 +1171,6 @@ class DefaultCreatorChannelHomeQueryRepositoryTest @Autowired constructor(
|
||||
return donation
|
||||
}
|
||||
|
||||
private fun saveCommunity(
|
||||
creator: Member,
|
||||
isFixed: Boolean,
|
||||
fixedAt: LocalDateTime? = null,
|
||||
price: Int,
|
||||
imagePath: String? = null,
|
||||
audioPath: String? = null,
|
||||
isAdult: Boolean = false,
|
||||
isCommentAvailable: Boolean = true,
|
||||
content: String = "community",
|
||||
isActive: Boolean = true
|
||||
): CreatorCommunity {
|
||||
val community = CreatorCommunity(
|
||||
content = content,
|
||||
price = price,
|
||||
isCommentAvailable = isCommentAvailable,
|
||||
isAdult = isAdult,
|
||||
audioPath = audioPath,
|
||||
imagePath = imagePath,
|
||||
isActive = isActive,
|
||||
isFixed = isFixed,
|
||||
fixedAt = fixedAt
|
||||
)
|
||||
community.member = creator
|
||||
entityManager.persist(community)
|
||||
return community
|
||||
}
|
||||
|
||||
private fun saveAuth(member: Member, gender: Int): Auth {
|
||||
val auth = Auth(
|
||||
name = member.nickname,
|
||||
@@ -1462,37 +1184,6 @@ class DefaultCreatorChannelHomeQueryRepositoryTest @Autowired constructor(
|
||||
return auth
|
||||
}
|
||||
|
||||
private fun saveCommunityLike(member: Member, community: CreatorCommunity, isActive: Boolean): CreatorCommunityLike {
|
||||
val like = CreatorCommunityLike(isActive = isActive)
|
||||
like.member = member
|
||||
like.creatorCommunity = community
|
||||
entityManager.persist(like)
|
||||
return like
|
||||
}
|
||||
|
||||
private fun saveCommunityComment(
|
||||
member: Member,
|
||||
community: CreatorCommunity,
|
||||
isActive: Boolean,
|
||||
isSecret: Boolean = false,
|
||||
parent: CreatorCommunityComment? = null
|
||||
): CreatorCommunityComment {
|
||||
val comment = CreatorCommunityComment(comment = "comment", isSecret = isSecret, isActive = isActive)
|
||||
comment.member = member
|
||||
comment.creatorCommunity = community
|
||||
comment.parent = parent
|
||||
entityManager.persist(comment)
|
||||
return comment
|
||||
}
|
||||
|
||||
private fun saveCommunityOrder(member: Member, community: CreatorCommunity, isRefund: Boolean): UseCan {
|
||||
val useCan = UseCan(CanUsage.PAID_COMMUNITY_POST, community.price, rewardCan = 0, isRefund = isRefund)
|
||||
useCan.member = member
|
||||
useCan.communityPost = community
|
||||
entityManager.persist(useCan)
|
||||
return useCan
|
||||
}
|
||||
|
||||
private fun saveOrder(
|
||||
member: Member,
|
||||
creator: Member,
|
||||
|
||||
Reference in New Issue
Block a user