feat(creator-channel): 커뮤니티 탭 조회 서비스를 추가한다
This commit is contained in:
@@ -0,0 +1,140 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.creator.channel.community.application
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.aws.cloudfront.AudioContentCloudFront
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||||
|
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
|
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||||
|
import kr.co.vividnext.sodalive.member.contentpreference.isAdultVisibleByPolicy
|
||||||
|
import kr.co.vividnext.sodalive.v2.common.domain.toCdnUrl
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityPost
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityQueryPolicy
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityTab
|
||||||
|
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.CreatorChannelCommunityQueryPort
|
||||||
|
import org.springframework.beans.factory.ObjectProvider
|
||||||
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
class CreatorChannelCommunityQueryService(
|
||||||
|
private val queryPortProvider: ObjectProvider<CreatorChannelCommunityQueryPort>,
|
||||||
|
private val queryPolicy: CreatorChannelCommunityQueryPolicy,
|
||||||
|
private val memberContentPreferenceService: MemberContentPreferenceService,
|
||||||
|
private val audioContentCloudFront: AudioContentCloudFront,
|
||||||
|
private val messageSource: SodaMessageSource,
|
||||||
|
private val langContext: LangContext,
|
||||||
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
|
private val cloudFrontHost: String
|
||||||
|
) {
|
||||||
|
fun getCommunityTab(
|
||||||
|
creatorId: Long,
|
||||||
|
viewer: Member,
|
||||||
|
page: Int?,
|
||||||
|
size: Int?,
|
||||||
|
now: LocalDateTime = LocalDateTime.now()
|
||||||
|
): CreatorChannelCommunityTab {
|
||||||
|
val communityPage = queryPolicy.createPage(page, size)
|
||||||
|
val queryPort = queryPortProvider.getObject()
|
||||||
|
val viewerId = viewer.id!!
|
||||||
|
val creator = queryPort.findCreator(creatorId, viewerId)
|
||||||
|
?: throw SodaException(messageKey = "member.validation.user_not_found")
|
||||||
|
|
||||||
|
if (queryPort.existsBlockedBetween(viewerId, creatorId)) {
|
||||||
|
val messageTemplate = messageSource
|
||||||
|
.getMessage("explorer.creator.blocked_access", langContext.lang)
|
||||||
|
.orEmpty()
|
||||||
|
throw SodaException(message = String.format(messageTemplate, creator.nickname))
|
||||||
|
}
|
||||||
|
|
||||||
|
validateCreatorRole(creator)
|
||||||
|
|
||||||
|
val preference = memberContentPreferenceService.getStoredPreference(viewer)
|
||||||
|
val canViewAdultContent = isAdultVisibleByPolicy(viewer, preference.isAdultContentVisible)
|
||||||
|
val fetchedPosts = queryPort.findCommunityPosts(
|
||||||
|
creatorId = creatorId,
|
||||||
|
viewerId = viewerId,
|
||||||
|
canViewAdultContent = canViewAdultContent,
|
||||||
|
offset = communityPage.offset,
|
||||||
|
limit = communityPage.fetchLimit
|
||||||
|
)
|
||||||
|
|
||||||
|
return CreatorChannelCommunityTab(
|
||||||
|
communityPostCount = queryPort.countCommunityPosts(
|
||||||
|
creatorId = creatorId,
|
||||||
|
viewerId = viewerId,
|
||||||
|
canViewAdultContent = canViewAdultContent
|
||||||
|
),
|
||||||
|
communityPosts = queryPolicy.limitItems(fetchedPosts, communityPage).map { it.toDomain(viewerId) },
|
||||||
|
page = communityPage,
|
||||||
|
hasNext = queryPolicy.hasNext(fetchedPosts, communityPage)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findHomeCommunityPosts(
|
||||||
|
creatorId: Long,
|
||||||
|
viewerId: Long,
|
||||||
|
isPinned: Boolean,
|
||||||
|
canViewAdultContent: Boolean,
|
||||||
|
limit: Int
|
||||||
|
): List<CreatorChannelCommunityPost> {
|
||||||
|
return queryPortProvider.getObject()
|
||||||
|
.findHomeCommunityPosts(
|
||||||
|
creatorId = creatorId,
|
||||||
|
viewerId = viewerId,
|
||||||
|
isPinned = isPinned,
|
||||||
|
canViewAdultContent = canViewAdultContent,
|
||||||
|
limit = limit
|
||||||
|
)
|
||||||
|
.map { it.toDomain(viewerId) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateCreatorRole(creator: CreatorChannelCommunityCreatorRecord) {
|
||||||
|
when (creator.role) {
|
||||||
|
MemberRole.CREATOR -> return
|
||||||
|
else -> throw SodaException(messageKey = "member.validation.creator_not_found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CreatorChannelCommunityPostRecord.toDomain(viewerId: Long): CreatorChannelCommunityPost {
|
||||||
|
val canAccessPaidContent = price <= 0 || viewerId == creatorId || existOrdered
|
||||||
|
return CreatorChannelCommunityPost(
|
||||||
|
postId = postId,
|
||||||
|
creatorId = creatorId,
|
||||||
|
creatorNickname = creatorNickname,
|
||||||
|
creatorProfileUrl = creatorProfilePath.toCdnUrl(cloudFrontHost) ?: defaultProfileImageUrl(),
|
||||||
|
imageUrl = if (canAccessPaidContent) imagePath.toCdnUrl(cloudFrontHost) else null,
|
||||||
|
audioUrl = if (canAccessPaidContent) audioPath.toSignedAudioUrl() else null,
|
||||||
|
content = queryPolicy.maskPaidContent(
|
||||||
|
content = content,
|
||||||
|
price = price,
|
||||||
|
isCreatorSelf = viewerId == creatorId,
|
||||||
|
existOrdered = existOrdered
|
||||||
|
),
|
||||||
|
price = price,
|
||||||
|
createdAt = createdAt,
|
||||||
|
existOrdered = existOrdered || viewerId == creatorId,
|
||||||
|
isCommentAvailable = isCommentAvailable,
|
||||||
|
likeCount = likeCount,
|
||||||
|
commentCount = commentCount,
|
||||||
|
isPinned = isPinned
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String?.toSignedAudioUrl(): String? {
|
||||||
|
if (isNullOrBlank()) return null
|
||||||
|
return audioContentCloudFront.generateSignedURL(this, AUDIO_SIGNED_URL_EXPIRATION_MILLIS)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun defaultProfileImageUrl(): String = "$cloudFrontHost/profile/default-profile.png"
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val AUDIO_SIGNED_URL_EXPIRATION_MILLIS = 1000L * 60 * 30
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,314 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.creator.channel.community.application
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.aws.cloudfront.AudioContentCloudFront
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.content.ContentType
|
||||||
|
import kr.co.vividnext.sodalive.i18n.Lang
|
||||||
|
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||||
|
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberProvider
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
|
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||||
|
import kr.co.vividnext.sodalive.member.contentpreference.ViewerContentPreference
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.domain.CreatorChannelCommunityQueryPolicy
|
||||||
|
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.CreatorChannelCommunityQueryPort
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertNull
|
||||||
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import org.springframework.beans.factory.ObjectProvider
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
class CreatorChannelCommunityQueryServiceTest {
|
||||||
|
@Test
|
||||||
|
@DisplayName("커뮤니티 탭 서비스는 요청 fallback과 조회 컨텍스트를 port에 전달하고 탭을 조립한다")
|
||||||
|
fun shouldResolveRequestFallbacksAndAssembleCommunityTab() {
|
||||||
|
val port = FakeCreatorChannelCommunityQueryPort().apply {
|
||||||
|
communityPostCount = 60
|
||||||
|
communityPosts = (1L..51L).map { communityPostRecord(it, price = 0) }
|
||||||
|
}
|
||||||
|
val audioContentCloudFront = Mockito.mock(AudioContentCloudFront::class.java)
|
||||||
|
Mockito.`when`(audioContentCloudFront.generateSignedURL("audio/1.mp3", 1000 * 60 * 30))
|
||||||
|
.thenReturn("https://signed.test/audio/1.mp3")
|
||||||
|
val service = createService(port, audioContentCloudFront, canViewAdultContent = false)
|
||||||
|
val viewer = createMember(id = 10L)
|
||||||
|
val now = LocalDateTime.of(2026, 6, 21, 10, 0)
|
||||||
|
|
||||||
|
val tab = service.getCommunityTab(
|
||||||
|
creatorId = 1L,
|
||||||
|
viewer = viewer,
|
||||||
|
page = -1,
|
||||||
|
size = 100,
|
||||||
|
now = now
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(60, tab.communityPostCount)
|
||||||
|
assertEquals(0, tab.page.page)
|
||||||
|
assertEquals(50, tab.page.size)
|
||||||
|
assertEquals(0L, port.listOffset)
|
||||||
|
assertEquals(51, port.listLimit)
|
||||||
|
assertEquals(false, port.listCanViewAdultContent)
|
||||||
|
assertEquals(false, port.countCanViewAdultContent)
|
||||||
|
assertEquals(50, tab.communityPosts.size)
|
||||||
|
assertTrue(tab.hasNext)
|
||||||
|
assertEquals("https://cdn.test/profile/1.png", tab.communityPosts.first().creatorProfileUrl)
|
||||||
|
assertEquals("https://cdn.test/image/1.png", tab.communityPosts.first().imageUrl)
|
||||||
|
assertEquals("https://signed.test/audio/1.mp3", tab.communityPosts.first().audioUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("creatorId에 해당하는 회원이 없으면 user_not_found 예외를 던진다")
|
||||||
|
fun shouldThrowUserNotFoundWhenCreatorMemberDoesNotExist() {
|
||||||
|
val port = FakeCreatorChannelCommunityQueryPort().apply { creator = null }
|
||||||
|
val service = createService(port)
|
||||||
|
val viewer = createMember(id = 10L)
|
||||||
|
|
||||||
|
val exception = assertThrows(SodaException::class.java) {
|
||||||
|
service.getCommunityTab(1L, viewer, null, null, LocalDateTime.of(2026, 6, 21, 10, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("member.validation.user_not_found", exception.messageKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("대상 회원 role이 CREATOR가 아니면 creator_not_found 예외를 던진다")
|
||||||
|
fun shouldThrowCreatorNotFoundWhenMemberIsNotCreator() {
|
||||||
|
val port = FakeCreatorChannelCommunityQueryPort().apply { creator = creator?.copy(role = MemberRole.USER) }
|
||||||
|
val service = createService(port)
|
||||||
|
val viewer = createMember(id = 10L)
|
||||||
|
|
||||||
|
val exception = assertThrows(SodaException::class.java) {
|
||||||
|
service.getCommunityTab(1L, viewer, null, null, LocalDateTime.of(2026, 6, 21, 10, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("member.validation.creator_not_found", exception.messageKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("차단 관계가 있으면 기존 차단 메시지 예외를 던진다")
|
||||||
|
fun shouldThrowBlockedAccessWhenViewerAndTargetAreBlocked() {
|
||||||
|
val port = FakeCreatorChannelCommunityQueryPort().apply { blocked = true }
|
||||||
|
val service = createService(port)
|
||||||
|
val viewer = createMember(id = 10L)
|
||||||
|
|
||||||
|
val exception = assertThrows(SodaException::class.java) {
|
||||||
|
service.getCommunityTab(1L, viewer, null, null, LocalDateTime.of(2026, 6, 21, 10, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNull(exception.messageKey)
|
||||||
|
assertEquals("Channel access is restricted at creator's request.", exception.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("커뮤니티 게시글은 접근 권한에 따라 이미지와 오디오와 본문을 조립한다")
|
||||||
|
fun shouldAssembleCommunityPostAssetsByAccessPolicy() {
|
||||||
|
val port = FakeCreatorChannelCommunityQueryPort().apply {
|
||||||
|
communityPosts = listOf(
|
||||||
|
communityPostRecord(1L, price = 0, existOrdered = false),
|
||||||
|
communityPostRecord(2L, price = 100, existOrdered = true),
|
||||||
|
communityPostRecord(3L, price = 100, existOrdered = false),
|
||||||
|
communityPostRecord(4L, creatorId = 10L, price = 100, existOrdered = false, creatorProfilePath = null),
|
||||||
|
communityPostRecord(5L, price = 0, imagePath = " ", audioPath = null)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val audioContentCloudFront = Mockito.mock(AudioContentCloudFront::class.java)
|
||||||
|
Mockito.`when`(audioContentCloudFront.generateSignedURL("audio/1.mp3", 1000 * 60 * 30))
|
||||||
|
.thenReturn("https://signed.test/audio/1.mp3")
|
||||||
|
Mockito.`when`(audioContentCloudFront.generateSignedURL("audio/2.mp3", 1000 * 60 * 30))
|
||||||
|
.thenReturn("https://signed.test/audio/2.mp3")
|
||||||
|
Mockito.`when`(audioContentCloudFront.generateSignedURL("audio/4.mp3", 1000 * 60 * 30))
|
||||||
|
.thenReturn("https://signed.test/audio/4.mp3")
|
||||||
|
val service = createService(port, audioContentCloudFront)
|
||||||
|
val viewer = createMember(id = 10L)
|
||||||
|
|
||||||
|
val posts = service.getCommunityTab(1L, viewer, null, null, LocalDateTime.of(2026, 6, 21, 10, 0))
|
||||||
|
.communityPosts
|
||||||
|
|
||||||
|
assertEquals("https://cdn.test/image/1.png", posts[0].imageUrl)
|
||||||
|
assertEquals("https://signed.test/audio/1.mp3", posts[0].audioUrl)
|
||||||
|
assertEquals("content-1", posts[0].content)
|
||||||
|
assertEquals("https://cdn.test/image/2.png", posts[1].imageUrl)
|
||||||
|
assertEquals("https://signed.test/audio/2.mp3", posts[1].audioUrl)
|
||||||
|
assertNull(posts[2].imageUrl)
|
||||||
|
assertNull(posts[2].audioUrl)
|
||||||
|
assertEquals("cont...", posts[2].content)
|
||||||
|
assertEquals("https://cdn.test/image/4.png", posts[3].imageUrl)
|
||||||
|
assertEquals("https://signed.test/audio/4.mp3", posts[3].audioUrl)
|
||||||
|
assertEquals("https://cdn.test/profile/default-profile.png", posts[3].creatorProfileUrl)
|
||||||
|
assertEquals(true, posts[3].existOrdered)
|
||||||
|
assertNull(posts[4].imageUrl)
|
||||||
|
assertNull(posts[4].audioUrl)
|
||||||
|
Mockito.verify(audioContentCloudFront, Mockito.never()).generateSignedURL("audio/3.mp3", 1000 * 60 * 30)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("홈 커뮤니티 요약 조회는 탭 전체 검증 없이 받은 조건으로 목록을 조립한다")
|
||||||
|
fun shouldAssembleHomeCommunityPostsWithoutTabValidation() {
|
||||||
|
val port = FakeCreatorChannelCommunityQueryPort().apply {
|
||||||
|
creator = null
|
||||||
|
blocked = true
|
||||||
|
homeCommunityPosts = listOf(communityPostRecord(1L, price = 0))
|
||||||
|
}
|
||||||
|
val service = createService(port)
|
||||||
|
|
||||||
|
val posts = service.findHomeCommunityPosts(
|
||||||
|
creatorId = 1L,
|
||||||
|
viewerId = 10L,
|
||||||
|
isPinned = true,
|
||||||
|
canViewAdultContent = false,
|
||||||
|
limit = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(1, posts.size)
|
||||||
|
assertEquals(1L, port.homeCreatorId)
|
||||||
|
assertEquals(10L, port.homeViewerId)
|
||||||
|
assertEquals(true, port.homeIsPinned)
|
||||||
|
assertEquals(false, port.homeCanViewAdultContent)
|
||||||
|
assertEquals(3, port.homeLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createService(
|
||||||
|
port: FakeCreatorChannelCommunityQueryPort,
|
||||||
|
audioContentCloudFront: AudioContentCloudFront = Mockito.mock(AudioContentCloudFront::class.java),
|
||||||
|
canViewAdultContent: Boolean = true
|
||||||
|
): CreatorChannelCommunityQueryService {
|
||||||
|
val preferenceService = Mockito.mock(MemberContentPreferenceService::class.java)
|
||||||
|
Mockito.`when`(
|
||||||
|
preferenceService.getStoredPreference(Mockito.any(Member::class.java) ?: createMember(id = 0L))
|
||||||
|
).thenReturn(
|
||||||
|
ViewerContentPreference(
|
||||||
|
countryCode = "US",
|
||||||
|
isAdultContentVisible = canViewAdultContent,
|
||||||
|
contentType = ContentType.ALL,
|
||||||
|
isAdult = canViewAdultContent
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val langContext = LangContext()
|
||||||
|
langContext.setLang(Lang.EN)
|
||||||
|
return CreatorChannelCommunityQueryService(
|
||||||
|
queryPortProvider = FixedCreatorChannelCommunityQueryPortProvider(port),
|
||||||
|
queryPolicy = CreatorChannelCommunityQueryPolicy(),
|
||||||
|
memberContentPreferenceService = preferenceService,
|
||||||
|
audioContentCloudFront = audioContentCloudFront,
|
||||||
|
messageSource = SodaMessageSource(),
|
||||||
|
langContext = langContext,
|
||||||
|
cloudFrontHost = "https://cdn.test"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMember(id: Long): Member {
|
||||||
|
return Member(
|
||||||
|
email = "member$id@test.com",
|
||||||
|
password = "password",
|
||||||
|
nickname = "member$id",
|
||||||
|
provider = MemberProvider.EMAIL
|
||||||
|
).apply { this.id = id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FixedCreatorChannelCommunityQueryPortProvider(
|
||||||
|
private val port: CreatorChannelCommunityQueryPort
|
||||||
|
) : ObjectProvider<CreatorChannelCommunityQueryPort> {
|
||||||
|
override fun getObject(vararg args: Any?): CreatorChannelCommunityQueryPort = port
|
||||||
|
|
||||||
|
override fun getIfAvailable(): CreatorChannelCommunityQueryPort = port
|
||||||
|
|
||||||
|
override fun getIfUnique(): CreatorChannelCommunityQueryPort = port
|
||||||
|
|
||||||
|
override fun getObject(): CreatorChannelCommunityQueryPort = port
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FakeCreatorChannelCommunityQueryPort : CreatorChannelCommunityQueryPort {
|
||||||
|
var creator: CreatorChannelCommunityCreatorRecord? = CreatorChannelCommunityCreatorRecord(
|
||||||
|
creatorId = 1L,
|
||||||
|
role = MemberRole.CREATOR,
|
||||||
|
nickname = "creator"
|
||||||
|
)
|
||||||
|
var blocked = false
|
||||||
|
var communityPostCount = 1
|
||||||
|
var communityPosts = listOf(communityPostRecord(1L, price = 0))
|
||||||
|
var homeCommunityPosts = listOf(communityPostRecord(1L, price = 0))
|
||||||
|
var countCanViewAdultContent: Boolean? = null
|
||||||
|
var listCanViewAdultContent: Boolean? = null
|
||||||
|
var listOffset: Long? = null
|
||||||
|
var listLimit: Int? = null
|
||||||
|
var homeCreatorId: Long? = null
|
||||||
|
var homeViewerId: Long? = null
|
||||||
|
var homeIsPinned: Boolean? = null
|
||||||
|
var homeCanViewAdultContent: Boolean? = null
|
||||||
|
var homeLimit: Int? = null
|
||||||
|
|
||||||
|
override fun findCreator(creatorId: Long, viewerId: Long?): CreatorChannelCommunityCreatorRecord? = creator
|
||||||
|
|
||||||
|
override fun existsBlockedBetween(viewerId: Long, creatorId: Long): Boolean = blocked
|
||||||
|
|
||||||
|
override fun countCommunityPosts(
|
||||||
|
creatorId: Long,
|
||||||
|
viewerId: Long,
|
||||||
|
canViewAdultContent: Boolean
|
||||||
|
): Int {
|
||||||
|
countCanViewAdultContent = canViewAdultContent
|
||||||
|
return communityPostCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findCommunityPosts(
|
||||||
|
creatorId: Long,
|
||||||
|
viewerId: Long,
|
||||||
|
canViewAdultContent: Boolean,
|
||||||
|
offset: Long,
|
||||||
|
limit: Int
|
||||||
|
): List<CreatorChannelCommunityPostRecord> {
|
||||||
|
listCanViewAdultContent = canViewAdultContent
|
||||||
|
listOffset = offset
|
||||||
|
listLimit = limit
|
||||||
|
return communityPosts
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findHomeCommunityPosts(
|
||||||
|
creatorId: Long,
|
||||||
|
viewerId: Long,
|
||||||
|
isPinned: Boolean,
|
||||||
|
canViewAdultContent: Boolean,
|
||||||
|
limit: Int
|
||||||
|
): List<CreatorChannelCommunityPostRecord> {
|
||||||
|
homeCreatorId = creatorId
|
||||||
|
homeViewerId = viewerId
|
||||||
|
homeIsPinned = isPinned
|
||||||
|
homeCanViewAdultContent = canViewAdultContent
|
||||||
|
homeLimit = limit
|
||||||
|
return homeCommunityPosts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun communityPostRecord(
|
||||||
|
postId: Long,
|
||||||
|
creatorId: Long = 1L,
|
||||||
|
price: Int,
|
||||||
|
existOrdered: Boolean = false,
|
||||||
|
creatorProfilePath: String? = "profile/$postId.png",
|
||||||
|
imagePath: String? = "image/$postId.png",
|
||||||
|
audioPath: String? = "audio/$postId.mp3"
|
||||||
|
): CreatorChannelCommunityPostRecord {
|
||||||
|
return CreatorChannelCommunityPostRecord(
|
||||||
|
postId = postId,
|
||||||
|
creatorId = creatorId,
|
||||||
|
creatorNickname = "creator-$creatorId",
|
||||||
|
creatorProfilePath = creatorProfilePath,
|
||||||
|
imagePath = imagePath,
|
||||||
|
audioPath = audioPath,
|
||||||
|
content = "content-$postId",
|
||||||
|
price = price,
|
||||||
|
createdAt = LocalDateTime.of(2026, 6, 21, 10, 0).plusMinutes(postId),
|
||||||
|
existOrdered = existOrdered,
|
||||||
|
isCommentAvailable = true,
|
||||||
|
likeCount = postId.toInt(),
|
||||||
|
commentCount = postId.toInt() + 1,
|
||||||
|
isPinned = postId == 1L
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user