feat(creator-channel): FanTalk 탭 조회 서비스를 구현한다
This commit is contained in:
@@ -1,14 +1,36 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.application
|
||||
|
||||
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.SodaMessageSource
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.MemberRole
|
||||
import kr.co.vividnext.sodalive.v2.common.domain.toCdnUrl
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.domain.CreatorChannelFanTalk
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.domain.CreatorChannelFanTalkQueryPolicy
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.domain.CreatorChannelFanTalkReply
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.domain.CreatorChannelFanTalkTab
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.port.out.CreatorChannelFanTalkCreatorRecord
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.port.out.CreatorChannelFanTalkQueryPort
|
||||
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.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 CreatorChannelFanTalkQueryService {
|
||||
class CreatorChannelFanTalkQueryService(
|
||||
private val queryPortProvider: ObjectProvider<CreatorChannelFanTalkQueryPort>,
|
||||
private val queryPolicy: CreatorChannelFanTalkQueryPolicy,
|
||||
private val messageSource: SodaMessageSource,
|
||||
private val langContext: LangContext,
|
||||
@Value("\${cloud.aws.cloud-front.host}")
|
||||
private val cloudFrontHost: String
|
||||
) {
|
||||
fun getFanTalkTab(
|
||||
creatorId: Long,
|
||||
viewer: Member,
|
||||
@@ -16,6 +38,79 @@ class CreatorChannelFanTalkQueryService {
|
||||
size: Int?,
|
||||
now: LocalDateTime = LocalDateTime.now()
|
||||
): CreatorChannelFanTalkTab {
|
||||
throw UnsupportedOperationException("CreatorChannelFanTalkQueryService is implemented in Phase 3")
|
||||
val fanTalkPage = 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 fetchedFanTalks = queryPort.findFanTalks(
|
||||
creatorId = creatorId,
|
||||
viewerId = viewerId,
|
||||
offset = fanTalkPage.offset,
|
||||
limit = fanTalkPage.fetchLimit
|
||||
)
|
||||
val fanTalkRecords = queryPolicy.limitItems(fetchedFanTalks, fanTalkPage)
|
||||
val repliesByParentId = findRepliesByParentId(queryPort, creatorId, fanTalkRecords)
|
||||
|
||||
return CreatorChannelFanTalkTab(
|
||||
fanTalkCount = queryPort.countFanTalks(creatorId, viewerId),
|
||||
fanTalks = fanTalkRecords.map { it.toDomain(repliesByParentId[it.fanTalkId].orEmpty()) },
|
||||
page = fanTalkPage,
|
||||
hasNext = queryPolicy.hasNext(fetchedFanTalks, fanTalkPage)
|
||||
)
|
||||
}
|
||||
|
||||
private fun validateCreatorRole(creator: CreatorChannelFanTalkCreatorRecord) {
|
||||
when (creator.role) {
|
||||
MemberRole.CREATOR -> return
|
||||
else -> throw SodaException(messageKey = "member.validation.creator_not_found")
|
||||
}
|
||||
}
|
||||
|
||||
private fun findRepliesByParentId(
|
||||
queryPort: CreatorChannelFanTalkQueryPort,
|
||||
creatorId: Long,
|
||||
fanTalkRecords: List<CreatorChannelFanTalkRecord>
|
||||
): Map<Long, List<CreatorChannelFanTalkReply>> {
|
||||
val parentFanTalkIds = fanTalkRecords.map { it.fanTalkId }
|
||||
if (parentFanTalkIds.isEmpty()) return emptyMap()
|
||||
return queryPort.findCreatorReplies(creatorId, parentFanTalkIds)
|
||||
.groupBy(
|
||||
keySelector = { it.parentFanTalkId },
|
||||
valueTransform = { it.toDomain() }
|
||||
)
|
||||
}
|
||||
|
||||
private fun CreatorChannelFanTalkRecord.toDomain(
|
||||
creatorReplies: List<CreatorChannelFanTalkReply>
|
||||
) = CreatorChannelFanTalk(
|
||||
fanTalkId = fanTalkId,
|
||||
writerId = writerId,
|
||||
writerNickname = writerNickname.removeDeletedNicknamePrefix(),
|
||||
writerProfileImageUrl = writerProfileImagePath.toCdnUrl(cloudFrontHost) ?: defaultProfileImageUrl(),
|
||||
content = content,
|
||||
createdAt = createdAt,
|
||||
creatorReplies = creatorReplies
|
||||
)
|
||||
|
||||
private fun CreatorChannelFanTalkReplyRecord.toDomain() = CreatorChannelFanTalkReply(
|
||||
fanTalkId = fanTalkId,
|
||||
writerId = writerId,
|
||||
writerNickname = writerNickname.removeDeletedNicknamePrefix(),
|
||||
writerProfileImageUrl = writerProfileImagePath.toCdnUrl(cloudFrontHost) ?: defaultProfileImageUrl(),
|
||||
content = content,
|
||||
createdAt = createdAt
|
||||
)
|
||||
|
||||
private fun defaultProfileImageUrl(): String = "$cloudFrontHost/profile/default-profile.png"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user