feat(community): 게시물 댓글 조회 서비스를 추가한다
This commit is contained in:
@@ -2,15 +2,22 @@ package kr.co.vividnext.sodalive.v2.creator.channel.community.application
|
|||||||
|
|
||||||
import kr.co.vividnext.sodalive.aws.cloudfront.AudioContentCloudFront
|
import kr.co.vividnext.sodalive.aws.cloudfront.AudioContentCloudFront
|
||||||
import kr.co.vividnext.sodalive.common.SodaException
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.extensions.removeDeletedNicknamePrefix
|
||||||
import kr.co.vividnext.sodalive.i18n.LangContext
|
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||||
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
||||||
import kr.co.vividnext.sodalive.member.Member
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
import kr.co.vividnext.sodalive.member.MemberRole
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||||
import kr.co.vividnext.sodalive.v2.common.domain.toCdnUrl
|
import kr.co.vividnext.sodalive.v2.common.domain.toCdnUrl
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityComment
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityComments
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityPost
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityPost
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityPostDetail
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityQueryPolicy
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityQueryPolicy
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityReplies
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityReply
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityTab
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityTab
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityCommentRecord
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityCreatorRecord
|
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 kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityPostRecord
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityQueryPort
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityQueryPort
|
||||||
@@ -93,6 +100,115 @@ class CreatorChannelCommunityQueryService(
|
|||||||
.map { it.toDomain(viewerId) }
|
.map { it.toDomain(viewerId) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getCommunityPostDetail(
|
||||||
|
postId: Long,
|
||||||
|
viewer: Member,
|
||||||
|
now: LocalDateTime = LocalDateTime.now()
|
||||||
|
): CreatorChannelCommunityPostDetail {
|
||||||
|
val queryPort = queryPortProvider.getObject()
|
||||||
|
val viewerId = viewer.id!!
|
||||||
|
val canViewAdultContent = memberContentPreferenceService.canViewAdultContent(viewer)
|
||||||
|
val postRecord = queryPort.findCommunityPost(postId, viewerId, canViewAdultContent)
|
||||||
|
?: throw SodaException(messageKey = INVALID_COMMUNITY_REQUEST_MESSAGE_KEY)
|
||||||
|
|
||||||
|
validateNotBlocked(queryPort, viewerId, postRecord.creatorId)
|
||||||
|
|
||||||
|
return CreatorChannelCommunityPostDetail(
|
||||||
|
post = postRecord.toDomain(viewerId),
|
||||||
|
comments = getCommunityComments(postId, viewer, page = 0, size = 20, now = now)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCommunityComments(
|
||||||
|
postId: Long,
|
||||||
|
viewer: Member,
|
||||||
|
page: Int?,
|
||||||
|
size: Int?,
|
||||||
|
now: LocalDateTime = LocalDateTime.now()
|
||||||
|
): CreatorChannelCommunityComments {
|
||||||
|
val communityPage = queryPolicy.createPage(page, size)
|
||||||
|
val queryPort = queryPortProvider.getObject()
|
||||||
|
val viewerId = viewer.id!!
|
||||||
|
val canViewAdultContent = memberContentPreferenceService.canViewAdultContent(viewer)
|
||||||
|
val postRecord = queryPort.findCommunityPost(postId, viewerId, canViewAdultContent)
|
||||||
|
?: throw SodaException(messageKey = INVALID_COMMUNITY_REQUEST_MESSAGE_KEY)
|
||||||
|
|
||||||
|
validateNotBlocked(queryPort, viewerId, postRecord.creatorId)
|
||||||
|
|
||||||
|
if (!postRecord.isCommentAvailable) {
|
||||||
|
return CreatorChannelCommunityComments(
|
||||||
|
commentCount = 0,
|
||||||
|
comments = emptyList(),
|
||||||
|
page = communityPage,
|
||||||
|
hasNext = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val fetchedComments = queryPort.findCommunityComments(
|
||||||
|
postId = postId,
|
||||||
|
viewerId = viewerId,
|
||||||
|
creatorId = postRecord.creatorId,
|
||||||
|
offset = communityPage.offset,
|
||||||
|
limit = communityPage.fetchLimit
|
||||||
|
)
|
||||||
|
val limitedComments = queryPolicy.limitItems(fetchedComments, communityPage)
|
||||||
|
val latestReplies = queryPort.findLatestRepliesByCommentIds(
|
||||||
|
commentIds = limitedComments.map { it.commentId },
|
||||||
|
viewerId = viewerId
|
||||||
|
).associateBy { it.parentCommentId }
|
||||||
|
|
||||||
|
return CreatorChannelCommunityComments(
|
||||||
|
commentCount = queryPort.countCommunityComments(
|
||||||
|
postId = postId,
|
||||||
|
viewerId = viewerId,
|
||||||
|
creatorId = postRecord.creatorId
|
||||||
|
),
|
||||||
|
comments = limitedComments.map { it.toCommentDomain(latestReplies[it.commentId]?.toReplyDomain()) },
|
||||||
|
page = communityPage,
|
||||||
|
hasNext = queryPolicy.hasNext(fetchedComments, communityPage)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCommunityReplies(
|
||||||
|
commentId: Long,
|
||||||
|
viewer: Member,
|
||||||
|
page: Int?,
|
||||||
|
size: Int?,
|
||||||
|
now: LocalDateTime = LocalDateTime.now()
|
||||||
|
): CreatorChannelCommunityReplies {
|
||||||
|
val communityPage = queryPolicy.createPage(page, size)
|
||||||
|
val queryPort = queryPortProvider.getObject()
|
||||||
|
val viewerId = viewer.id!!
|
||||||
|
val canViewAdultContent = memberContentPreferenceService.canViewAdultContent(viewer)
|
||||||
|
val context = queryPort.findRootCommentContext(commentId, viewerId, canViewAdultContent)
|
||||||
|
?: throw SodaException(messageKey = INVALID_COMMUNITY_REQUEST_MESSAGE_KEY)
|
||||||
|
|
||||||
|
validateNotBlocked(queryPort, viewerId, context.creatorId)
|
||||||
|
|
||||||
|
if (!context.isCommentAvailable) {
|
||||||
|
return CreatorChannelCommunityReplies(
|
||||||
|
replyCount = 0,
|
||||||
|
replies = emptyList(),
|
||||||
|
page = communityPage,
|
||||||
|
hasNext = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val fetchedReplies = queryPort.findCommunityReplies(
|
||||||
|
commentId = commentId,
|
||||||
|
viewerId = viewerId,
|
||||||
|
offset = communityPage.offset,
|
||||||
|
limit = communityPage.fetchLimit
|
||||||
|
)
|
||||||
|
|
||||||
|
return CreatorChannelCommunityReplies(
|
||||||
|
replyCount = queryPort.countCommunityReplies(commentId, viewerId),
|
||||||
|
replies = queryPolicy.limitItems(fetchedReplies, communityPage).map { it.toReplyDomain() },
|
||||||
|
page = communityPage,
|
||||||
|
hasNext = queryPolicy.hasNext(fetchedReplies, communityPage)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun validateCreatorRole(creator: CreatorChannelCommunityCreatorRecord) {
|
private fun validateCreatorRole(creator: CreatorChannelCommunityCreatorRecord) {
|
||||||
when (creator.role) {
|
when (creator.role) {
|
||||||
MemberRole.CREATOR -> return
|
MemberRole.CREATOR -> return
|
||||||
@@ -100,6 +216,12 @@ class CreatorChannelCommunityQueryService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun validateNotBlocked(queryPort: CreatorChannelCommunityQueryPort, viewerId: Long, creatorId: Long) {
|
||||||
|
if (queryPort.existsBlockedBetween(viewerId, creatorId)) {
|
||||||
|
throw SodaException(messageKey = INVALID_COMMUNITY_REQUEST_MESSAGE_KEY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun CreatorChannelCommunityPostRecord.toDomain(viewerId: Long): CreatorChannelCommunityPost {
|
private fun CreatorChannelCommunityPostRecord.toDomain(viewerId: Long): CreatorChannelCommunityPost {
|
||||||
val canAccessPaidContent = price <= 0 || viewerId == creatorId || existOrdered
|
val canAccessPaidContent = price <= 0 || viewerId == creatorId || existOrdered
|
||||||
return CreatorChannelCommunityPost(
|
return CreatorChannelCommunityPost(
|
||||||
@@ -126,6 +248,29 @@ class CreatorChannelCommunityQueryService(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun CreatorChannelCommunityCommentRecord.toCommentDomain(
|
||||||
|
latestReply: CreatorChannelCommunityReply?
|
||||||
|
): CreatorChannelCommunityComment {
|
||||||
|
return CreatorChannelCommunityComment(
|
||||||
|
commentId = commentId,
|
||||||
|
writerProfileImageUrl = writerProfilePath.toCdnUrl(cloudFrontHost) ?: defaultProfileImageUrl(),
|
||||||
|
writerNickname = writerNickname.removeDeletedNicknamePrefix(),
|
||||||
|
content = content,
|
||||||
|
createdAt = createdAt,
|
||||||
|
latestReply = latestReply
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CreatorChannelCommunityCommentRecord.toReplyDomain(): CreatorChannelCommunityReply {
|
||||||
|
return CreatorChannelCommunityReply(
|
||||||
|
commentId = commentId,
|
||||||
|
writerProfileImageUrl = writerProfilePath.toCdnUrl(cloudFrontHost) ?: defaultProfileImageUrl(),
|
||||||
|
writerNickname = writerNickname.removeDeletedNicknamePrefix(),
|
||||||
|
content = content,
|
||||||
|
createdAt = createdAt
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun String?.toSignedAudioUrl(): String? {
|
private fun String?.toSignedAudioUrl(): String? {
|
||||||
if (isNullOrBlank()) return null
|
if (isNullOrBlank()) return null
|
||||||
return audioContentCloudFront.generateSignedURL(this, AUDIO_SIGNED_URL_EXPIRATION_MILLIS)
|
return audioContentCloudFront.generateSignedURL(this, AUDIO_SIGNED_URL_EXPIRATION_MILLIS)
|
||||||
@@ -135,5 +280,6 @@ class CreatorChannelCommunityQueryService(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val AUDIO_SIGNED_URL_EXPIRATION_MILLIS = 1000L * 60 * 30
|
private const val AUDIO_SIGNED_URL_EXPIRATION_MILLIS = 1000L * 60 * 30
|
||||||
|
private const val INVALID_COMMUNITY_REQUEST_MESSAGE_KEY = "creator.community.invalid_request_retry"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import kr.co.vividnext.sodalive.member.MemberProvider
|
|||||||
import kr.co.vividnext.sodalive.member.MemberRole
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityQueryPolicy
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityQueryPolicy
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityCommentContextRecord
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityCommentRecord
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityCreatorRecord
|
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 kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityPostRecord
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityQueryPort
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityQueryPort
|
||||||
@@ -173,6 +175,119 @@ class CreatorChannelCommunityQueryServiceTest {
|
|||||||
assertEquals(true, posts.single().isLiked)
|
assertEquals(true, posts.single().isLiked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("커뮤니티 상세는 게시글과 초기 댓글 및 최신 답글을 조립한다")
|
||||||
|
fun shouldAssembleCommunityPostDetailWithInitialComments() {
|
||||||
|
val port = FakeCreatorChannelCommunityQueryPort().apply {
|
||||||
|
communityPost = communityPostRecord(1L, price = 0, isLiked = true)
|
||||||
|
communityCommentCount = 21
|
||||||
|
communityComments = (1L..21L).map { communityCommentRecord(it) }
|
||||||
|
latestReplies = listOf(communityCommentRecord(100L, parentCommentId = 1L, writerNickname = "deleted_reply-writer"))
|
||||||
|
}
|
||||||
|
val service = createService(port)
|
||||||
|
val viewer = createMember(id = 10L)
|
||||||
|
|
||||||
|
val detail = service.getCommunityPostDetail(1L, viewer, LocalDateTime.of(2026, 7, 6, 10, 0))
|
||||||
|
|
||||||
|
assertEquals(1L, detail.post.postId)
|
||||||
|
assertEquals(true, detail.post.isLiked)
|
||||||
|
assertEquals(21, detail.comments.commentCount)
|
||||||
|
assertEquals(20, detail.comments.comments.size)
|
||||||
|
assertEquals(true, detail.comments.hasNext)
|
||||||
|
assertEquals("reply-writer", detail.comments.comments.first().latestReply?.writerNickname)
|
||||||
|
assertEquals("https://cdn.test/profile/default-profile.png", detail.comments.comments.first().writerProfileImageUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("댓글 불가 게시글은 댓글 port를 호출하지 않고 빈 댓글 응답을 반환한다")
|
||||||
|
fun shouldReturnEmptyCommentsWhenCommentUnavailable() {
|
||||||
|
val port = FakeCreatorChannelCommunityQueryPort().apply {
|
||||||
|
communityPost = communityPostRecord(1L, price = 0, isCommentAvailable = false)
|
||||||
|
}
|
||||||
|
val service = createService(port)
|
||||||
|
|
||||||
|
val comments = service.getCommunityComments(1L, createMember(id = 10L), -1, 100, LocalDateTime.of(2026, 7, 6, 10, 0))
|
||||||
|
|
||||||
|
assertEquals(0, comments.commentCount)
|
||||||
|
assertEquals(0, comments.comments.size)
|
||||||
|
assertEquals(0, comments.page.page)
|
||||||
|
assertEquals(50, comments.page.size)
|
||||||
|
assertEquals(false, comments.hasNext)
|
||||||
|
assertEquals(false, port.commentListCalled)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("커뮤니티 댓글은 page fallback과 최신 답글을 적용한다")
|
||||||
|
fun shouldApplyCommentPageFallbackAndLatestReply() {
|
||||||
|
val port = FakeCreatorChannelCommunityQueryPort().apply {
|
||||||
|
communityPost = communityPostRecord(1L, price = 0)
|
||||||
|
communityCommentCount = 60
|
||||||
|
communityComments = (1L..51L).map { communityCommentRecord(it, writerProfilePath = "profile/$it.png") }
|
||||||
|
latestReplies = listOf(communityCommentRecord(100L, parentCommentId = 2L))
|
||||||
|
}
|
||||||
|
val service = createService(port)
|
||||||
|
|
||||||
|
val comments = service.getCommunityComments(1L, createMember(id = 10L), -1, 100, LocalDateTime.of(2026, 7, 6, 10, 0))
|
||||||
|
|
||||||
|
assertEquals(0L, port.commentListOffset)
|
||||||
|
assertEquals(51, port.commentListLimit)
|
||||||
|
assertEquals((1L..50L).toList(), port.latestReplyCommentIds)
|
||||||
|
assertEquals(50, comments.comments.size)
|
||||||
|
assertEquals(true, comments.hasNext)
|
||||||
|
assertEquals("https://cdn.test/profile/1.png", comments.comments.first().writerProfileImageUrl)
|
||||||
|
assertEquals(100L, comments.comments[1].latestReply?.commentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("커뮤니티 답글은 부모 댓글 context를 확인하고 page fallback으로 조립한다")
|
||||||
|
fun shouldAssembleCommunityReplies() {
|
||||||
|
val port = FakeCreatorChannelCommunityQueryPort().apply {
|
||||||
|
rootCommentContext = CreatorChannelCommunityCommentContextRecord(1L, 10L, 1L, true)
|
||||||
|
communityReplyCount = 51
|
||||||
|
communityReplies = (1L..51L).map { communityCommentRecord(it, parentCommentId = 1L) }
|
||||||
|
}
|
||||||
|
val service = createService(port)
|
||||||
|
|
||||||
|
val replies = service.getCommunityReplies(1L, createMember(id = 10L), -1, 100, LocalDateTime.of(2026, 7, 6, 10, 0))
|
||||||
|
|
||||||
|
assertEquals(51, replies.replyCount)
|
||||||
|
assertEquals(50, replies.replies.size)
|
||||||
|
assertEquals(0L, port.replyListOffset)
|
||||||
|
assertEquals(51, port.replyListLimit)
|
||||||
|
assertEquals(true, replies.hasNext)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("부모 댓글 context가 보이지 않으면 커뮤니티 답글 조회는 예외를 던진다")
|
||||||
|
fun shouldThrowWhenRootCommentContextNotVisible() {
|
||||||
|
val port = FakeCreatorChannelCommunityQueryPort().apply { rootCommentContext = null }
|
||||||
|
val service = createService(port)
|
||||||
|
|
||||||
|
val exception = assertThrows(SodaException::class.java) {
|
||||||
|
service.getCommunityReplies(1L, createMember(id = 10L), null, null, LocalDateTime.of(2026, 7, 6, 10, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("creator.community.invalid_request_retry", exception.messageKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("댓글 불가 게시글의 답글 직접 조회는 빈 응답을 반환한다")
|
||||||
|
fun shouldReturnEmptyRepliesWhenCommentUnavailable() {
|
||||||
|
val port = FakeCreatorChannelCommunityQueryPort().apply {
|
||||||
|
rootCommentContext = CreatorChannelCommunityCommentContextRecord(1L, 10L, 1L, false)
|
||||||
|
communityReplyCount = 1
|
||||||
|
communityReplies = listOf(communityCommentRecord(2L, parentCommentId = 1L))
|
||||||
|
}
|
||||||
|
val service = createService(port)
|
||||||
|
|
||||||
|
val replies = service.getCommunityReplies(1L, createMember(id = 10L), null, null, LocalDateTime.of(2026, 7, 6, 10, 0))
|
||||||
|
|
||||||
|
assertEquals(0, replies.replyCount)
|
||||||
|
assertEquals(0, replies.replies.size)
|
||||||
|
assertEquals(false, replies.hasNext)
|
||||||
|
assertEquals(null, port.replyListOffset)
|
||||||
|
}
|
||||||
|
|
||||||
private fun createService(
|
private fun createService(
|
||||||
port: FakeCreatorChannelCommunityQueryPort,
|
port: FakeCreatorChannelCommunityQueryPort,
|
||||||
audioContentCloudFront: AudioContentCloudFront = Mockito.mock(AudioContentCloudFront::class.java),
|
audioContentCloudFront: AudioContentCloudFront = Mockito.mock(AudioContentCloudFront::class.java),
|
||||||
@@ -226,11 +341,29 @@ private class FakeCreatorChannelCommunityQueryPort : CreatorChannelCommunityQuer
|
|||||||
var blocked = false
|
var blocked = false
|
||||||
var communityPostCount = 1
|
var communityPostCount = 1
|
||||||
var communityPosts = listOf(communityPostRecord(1L, price = 0))
|
var communityPosts = listOf(communityPostRecord(1L, price = 0))
|
||||||
|
var communityPost: CreatorChannelCommunityPostRecord? = communityPostRecord(1L, price = 0)
|
||||||
var homeCommunityPosts = listOf(communityPostRecord(1L, price = 0))
|
var homeCommunityPosts = listOf(communityPostRecord(1L, price = 0))
|
||||||
|
var communityCommentCount = 0
|
||||||
|
var communityComments = emptyList<CreatorChannelCommunityCommentRecord>()
|
||||||
|
var latestReplies = emptyList<CreatorChannelCommunityCommentRecord>()
|
||||||
|
var rootCommentContext: CreatorChannelCommunityCommentContextRecord? = CreatorChannelCommunityCommentContextRecord(
|
||||||
|
commentId = 1L,
|
||||||
|
postId = 1L,
|
||||||
|
creatorId = 1L,
|
||||||
|
isCommentAvailable = true
|
||||||
|
)
|
||||||
|
var communityReplyCount = 0
|
||||||
|
var communityReplies = emptyList<CreatorChannelCommunityCommentRecord>()
|
||||||
var countCanViewAdultContent: Boolean? = null
|
var countCanViewAdultContent: Boolean? = null
|
||||||
var listCanViewAdultContent: Boolean? = null
|
var listCanViewAdultContent: Boolean? = null
|
||||||
var listOffset: Long? = null
|
var listOffset: Long? = null
|
||||||
var listLimit: Int? = null
|
var listLimit: Int? = null
|
||||||
|
var commentListCalled = false
|
||||||
|
var commentListOffset: Long? = null
|
||||||
|
var commentListLimit: Int? = null
|
||||||
|
var latestReplyCommentIds = emptyList<Long>()
|
||||||
|
var replyListOffset: Long? = null
|
||||||
|
var replyListLimit: Int? = null
|
||||||
var homeCreatorId: Long? = null
|
var homeCreatorId: Long? = null
|
||||||
var homeViewerId: Long? = null
|
var homeViewerId: Long? = null
|
||||||
var homeIsPinned: Boolean? = null
|
var homeIsPinned: Boolean? = null
|
||||||
@@ -277,6 +410,54 @@ private class FakeCreatorChannelCommunityQueryPort : CreatorChannelCommunityQuer
|
|||||||
homeLimit = limit
|
homeLimit = limit
|
||||||
return homeCommunityPosts
|
return homeCommunityPosts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun findCommunityPost(
|
||||||
|
postId: Long,
|
||||||
|
viewerId: Long,
|
||||||
|
canViewAdultContent: Boolean
|
||||||
|
): CreatorChannelCommunityPostRecord? = communityPost
|
||||||
|
|
||||||
|
override fun countCommunityComments(postId: Long, viewerId: Long, creatorId: Long): Int = communityCommentCount
|
||||||
|
|
||||||
|
override fun findCommunityComments(
|
||||||
|
postId: Long,
|
||||||
|
viewerId: Long,
|
||||||
|
creatorId: Long,
|
||||||
|
offset: Long,
|
||||||
|
limit: Int
|
||||||
|
): List<CreatorChannelCommunityCommentRecord> {
|
||||||
|
commentListCalled = true
|
||||||
|
commentListOffset = offset
|
||||||
|
commentListLimit = limit
|
||||||
|
return communityComments
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findLatestRepliesByCommentIds(
|
||||||
|
commentIds: List<Long>,
|
||||||
|
viewerId: Long
|
||||||
|
): List<CreatorChannelCommunityCommentRecord> {
|
||||||
|
latestReplyCommentIds = commentIds
|
||||||
|
return latestReplies
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findRootCommentContext(
|
||||||
|
commentId: Long,
|
||||||
|
viewerId: Long,
|
||||||
|
canViewAdultContent: Boolean
|
||||||
|
): CreatorChannelCommunityCommentContextRecord? = rootCommentContext
|
||||||
|
|
||||||
|
override fun countCommunityReplies(commentId: Long, viewerId: Long): Int = communityReplyCount
|
||||||
|
|
||||||
|
override fun findCommunityReplies(
|
||||||
|
commentId: Long,
|
||||||
|
viewerId: Long,
|
||||||
|
offset: Long,
|
||||||
|
limit: Int
|
||||||
|
): List<CreatorChannelCommunityCommentRecord> {
|
||||||
|
replyListOffset = offset
|
||||||
|
replyListLimit = limit
|
||||||
|
return communityReplies
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun communityPostRecord(
|
private fun communityPostRecord(
|
||||||
@@ -287,7 +468,8 @@ private fun communityPostRecord(
|
|||||||
creatorProfilePath: String? = "profile/$postId.png",
|
creatorProfilePath: String? = "profile/$postId.png",
|
||||||
imagePath: String? = "image/$postId.png",
|
imagePath: String? = "image/$postId.png",
|
||||||
audioPath: String? = "audio/$postId.mp3",
|
audioPath: String? = "audio/$postId.mp3",
|
||||||
isLiked: Boolean = false
|
isLiked: Boolean = false,
|
||||||
|
isCommentAvailable: Boolean = true
|
||||||
): CreatorChannelCommunityPostRecord {
|
): CreatorChannelCommunityPostRecord {
|
||||||
return CreatorChannelCommunityPostRecord(
|
return CreatorChannelCommunityPostRecord(
|
||||||
postId = postId,
|
postId = postId,
|
||||||
@@ -300,10 +482,28 @@ private fun communityPostRecord(
|
|||||||
price = price,
|
price = price,
|
||||||
createdAt = LocalDateTime.of(2026, 6, 21, 10, 0).plusMinutes(postId),
|
createdAt = LocalDateTime.of(2026, 6, 21, 10, 0).plusMinutes(postId),
|
||||||
existOrdered = existOrdered,
|
existOrdered = existOrdered,
|
||||||
isCommentAvailable = true,
|
isCommentAvailable = isCommentAvailable,
|
||||||
likeCount = postId.toInt(),
|
likeCount = postId.toInt(),
|
||||||
commentCount = postId.toInt() + 1,
|
commentCount = postId.toInt() + 1,
|
||||||
isPinned = postId == 1L,
|
isPinned = postId == 1L,
|
||||||
isLiked = isLiked
|
isLiked = isLiked
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun communityCommentRecord(
|
||||||
|
commentId: Long,
|
||||||
|
parentCommentId: Long? = null,
|
||||||
|
writerProfilePath: String? = null,
|
||||||
|
writerNickname: String = "writer-$commentId"
|
||||||
|
): CreatorChannelCommunityCommentRecord {
|
||||||
|
return CreatorChannelCommunityCommentRecord(
|
||||||
|
commentId = commentId,
|
||||||
|
parentCommentId = parentCommentId,
|
||||||
|
postId = 1L,
|
||||||
|
writerId = commentId + 100L,
|
||||||
|
writerProfilePath = writerProfilePath,
|
||||||
|
writerNickname = writerNickname,
|
||||||
|
content = "comment-$commentId",
|
||||||
|
createdAt = LocalDateTime.of(2026, 7, 6, 10, 0).plusMinutes(commentId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -742,4 +742,40 @@ private class FakeCreatorChannelCommunityQueryPort : CreatorChannelCommunityQuer
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun findCommunityPost(
|
||||||
|
postId: Long,
|
||||||
|
viewerId: Long,
|
||||||
|
canViewAdultContent: Boolean
|
||||||
|
): CreatorChannelCommunityPostRecord? = null
|
||||||
|
|
||||||
|
override fun countCommunityComments(postId: Long, viewerId: Long, creatorId: Long): Int = 0
|
||||||
|
|
||||||
|
override fun findCommunityComments(
|
||||||
|
postId: Long,
|
||||||
|
viewerId: Long,
|
||||||
|
creatorId: Long,
|
||||||
|
offset: Long,
|
||||||
|
limit: Int
|
||||||
|
) = emptyList<kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityCommentRecord>()
|
||||||
|
|
||||||
|
override fun findLatestRepliesByCommentIds(
|
||||||
|
commentIds: List<Long>,
|
||||||
|
viewerId: Long
|
||||||
|
) = emptyList<kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityCommentRecord>()
|
||||||
|
|
||||||
|
override fun findRootCommentContext(
|
||||||
|
commentId: Long,
|
||||||
|
viewerId: Long,
|
||||||
|
canViewAdultContent: Boolean
|
||||||
|
): kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityCommentContextRecord? = null
|
||||||
|
|
||||||
|
override fun countCommunityReplies(commentId: Long, viewerId: Long): Int = 0
|
||||||
|
|
||||||
|
override fun findCommunityReplies(
|
||||||
|
commentId: Long,
|
||||||
|
viewerId: Long,
|
||||||
|
offset: Long,
|
||||||
|
limit: Int
|
||||||
|
) = emptyList<kr.co.vividnext.sodalive.v2.creator.channel.community.port.out.CreatorChannelCommunityCommentRecord>()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user