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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user