feat(creator): 채널 라이브 탭 조회 서비스를 추가한다
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.channel.live.application
|
||||
|
||||
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.Gender
|
||||
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.ContentSort
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.live.domain.CreatorChannelAudioContent
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.live.domain.CreatorChannelLive
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.live.domain.CreatorChannelLiveReplayQueryPolicy
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.live.domain.CreatorChannelLiveTab
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.live.port.out.CreatorChannelAudioContentRecord
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.live.port.out.CreatorChannelCreatorRecord
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.live.port.out.CreatorChannelLiveQueryPort
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.live.port.out.CreatorChannelLiveRecord
|
||||
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 CreatorChannelLiveQueryService(
|
||||
private val queryPortProvider: ObjectProvider<CreatorChannelLiveQueryPort>,
|
||||
private val queryPolicy: CreatorChannelLiveReplayQueryPolicy,
|
||||
private val memberContentPreferenceService: MemberContentPreferenceService,
|
||||
private val messageSource: SodaMessageSource,
|
||||
private val langContext: LangContext,
|
||||
@Value("\${cloud.aws.cloud-front.host}")
|
||||
private val cloudFrontHost: String
|
||||
) {
|
||||
fun getLiveTab(
|
||||
creatorId: Long,
|
||||
viewer: Member,
|
||||
sort: ContentSort,
|
||||
page: Int,
|
||||
size: Int,
|
||||
now: LocalDateTime = LocalDateTime.now()
|
||||
): CreatorChannelLiveTab {
|
||||
val livePage = 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 isViewerCreator = viewerId == creatorId
|
||||
val effectiveViewerGender = viewer.effectiveGender()
|
||||
val fetchedContents = queryPort.findLiveReplayAudioContents(
|
||||
creatorId = creatorId,
|
||||
viewerId = viewerId,
|
||||
now = now,
|
||||
canViewAdultContent = canViewAdultContent,
|
||||
sort = sort,
|
||||
offset = livePage.offset,
|
||||
limit = livePage.fetchLimit
|
||||
)
|
||||
|
||||
return CreatorChannelLiveTab(
|
||||
liveReplayContentCount = queryPort.countLiveReplayAudioContents(
|
||||
creatorId = creatorId,
|
||||
now = now,
|
||||
canViewAdultContent = canViewAdultContent
|
||||
),
|
||||
currentLive = queryPort.findCurrentLive(
|
||||
creatorId = creatorId,
|
||||
now = now,
|
||||
canViewAdultContent = canViewAdultContent,
|
||||
viewerId = viewerId,
|
||||
isViewerCreator = isViewerCreator,
|
||||
effectiveViewerGender = effectiveViewerGender
|
||||
)?.toDomain(),
|
||||
liveReplayContents = queryPolicy.limitItems(fetchedContents, livePage).map { it.toDomain() },
|
||||
sort = sort,
|
||||
page = livePage,
|
||||
hasNext = queryPolicy.hasNext(fetchedContents, livePage)
|
||||
)
|
||||
}
|
||||
|
||||
private fun validateCreatorRole(creator: CreatorChannelCreatorRecord) {
|
||||
when (creator.role) {
|
||||
MemberRole.CREATOR -> return
|
||||
else -> throw SodaException(messageKey = "member.validation.creator_not_found")
|
||||
}
|
||||
}
|
||||
|
||||
private fun Member.effectiveGender(): Gender {
|
||||
auth?.let { return if (it.gender == 1) Gender.MALE else Gender.FEMALE }
|
||||
return gender
|
||||
}
|
||||
|
||||
private fun CreatorChannelLiveRecord.toDomain() = CreatorChannelLive(
|
||||
liveId = liveId,
|
||||
title = title,
|
||||
coverImageUrl = coverImagePath.toCdnUrl(),
|
||||
beginDateTime = beginDateTime,
|
||||
price = price,
|
||||
isAdult = isAdult
|
||||
)
|
||||
|
||||
private fun CreatorChannelAudioContentRecord.toDomain() = CreatorChannelAudioContent(
|
||||
audioContentId = audioContentId,
|
||||
title = title,
|
||||
duration = duration,
|
||||
imageUrl = imagePath.toCdnUrl(),
|
||||
price = price,
|
||||
isAdult = isAdult,
|
||||
isPointAvailable = isPointAvailable,
|
||||
isFirstContent = isFirstContent,
|
||||
publishedAt = publishedAt,
|
||||
seriesName = seriesName,
|
||||
isOriginalSeries = isOriginalSeries,
|
||||
isOwned = isOwned,
|
||||
isRented = isRented
|
||||
)
|
||||
|
||||
private fun String?.toCdnUrl(): String? {
|
||||
if (isNullOrBlank()) return null
|
||||
if (startsWith("https://") || startsWith("http://")) return this
|
||||
return "$cloudFrontHost/$this"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.channel.live.port.out
|
||||
|
||||
import kr.co.vividnext.sodalive.member.Gender
|
||||
import kr.co.vividnext.sodalive.member.MemberRole
|
||||
import kr.co.vividnext.sodalive.v2.common.domain.ContentSort
|
||||
import java.time.LocalDateTime
|
||||
|
||||
interface CreatorChannelLiveQueryPort {
|
||||
fun findCreator(creatorId: Long, viewerId: Long?): CreatorChannelCreatorRecord?
|
||||
|
||||
fun existsBlockedBetween(viewerId: Long, creatorId: Long): Boolean
|
||||
|
||||
fun findCurrentLive(
|
||||
creatorId: Long,
|
||||
now: LocalDateTime,
|
||||
canViewAdultContent: Boolean,
|
||||
viewerId: Long?,
|
||||
isViewerCreator: Boolean,
|
||||
effectiveViewerGender: Gender?
|
||||
): CreatorChannelLiveRecord?
|
||||
|
||||
fun countLiveReplayAudioContents(
|
||||
creatorId: Long,
|
||||
now: LocalDateTime,
|
||||
canViewAdultContent: Boolean
|
||||
): Int
|
||||
|
||||
fun findLiveReplayAudioContents(
|
||||
creatorId: Long,
|
||||
viewerId: Long?,
|
||||
now: LocalDateTime,
|
||||
canViewAdultContent: Boolean,
|
||||
sort: ContentSort,
|
||||
offset: Long,
|
||||
limit: Int
|
||||
): List<CreatorChannelAudioContentRecord>
|
||||
}
|
||||
|
||||
data class CreatorChannelCreatorRecord(
|
||||
val creatorId: Long,
|
||||
val role: MemberRole,
|
||||
val nickname: String
|
||||
)
|
||||
|
||||
data class CreatorChannelLiveRecord(
|
||||
val liveId: Long,
|
||||
val title: String,
|
||||
val coverImagePath: String?,
|
||||
val beginDateTime: LocalDateTime,
|
||||
val price: Int,
|
||||
val isAdult: Boolean
|
||||
)
|
||||
|
||||
data class CreatorChannelAudioContentRecord(
|
||||
val audioContentId: Long,
|
||||
val title: String,
|
||||
val duration: String?,
|
||||
val imagePath: String?,
|
||||
val price: Int,
|
||||
val isAdult: Boolean,
|
||||
val isPointAvailable: Boolean,
|
||||
val isFirstContent: Boolean,
|
||||
val publishedAt: LocalDateTime,
|
||||
val seriesName: String?,
|
||||
val isOriginalSeries: Boolean?,
|
||||
val isOwned: Boolean,
|
||||
val isRented: Boolean
|
||||
)
|
||||
Reference in New Issue
Block a user