feat(home): 홈 추천 통합 facade를 추가한다
This commit is contained in:
@@ -0,0 +1,226 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.home.application
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||||
|
import kr.co.vividnext.sodalive.member.contentpreference.isAdultVisibleByPolicy
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.home.dto.HomeActiveCreatorItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.home.dto.HomeAiCharacterItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.home.dto.HomeBannerItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.home.dto.HomeCreatorItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.home.dto.HomeFirstAudioContentItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.home.dto.HomeGenreCreatorGroupItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.home.dto.HomeLiveItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.home.dto.HomePopularCommunityItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.home.dto.HomeRecommendationPageResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.home.dto.HomeRecommendationResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.home.dto.imageUrl
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.home.dto.toUtcIso
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommend.application.HomeRecommendationQueryService
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommend.port.out.HomeAiCharacterRecommendationRecord
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommend.port.out.HomeBannerRecommendationRecord
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommend.port.out.HomeCheerCreatorRecommendationRecord
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommend.port.out.HomeFirstAudioContentRecord
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommend.port.out.HomeGenreCreatorRecommendationGroup
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommend.port.out.HomeLiveRecommendationRecord
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommend.port.out.HomePopularCommunityRecommendationRecord
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommend.port.out.RecentDebutCreatorRecord
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommend.port.out.RecentlyActiveCreatorRecord
|
||||||
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class HomeRecommendationFacade(
|
||||||
|
private val queryService: HomeRecommendationQueryService,
|
||||||
|
private val memberContentPreferenceService: MemberContentPreferenceService,
|
||||||
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
|
private val cloudFrontHost: String
|
||||||
|
) {
|
||||||
|
fun getHomeRecommendations(member: Member?): HomeRecommendationResponse {
|
||||||
|
val now = LocalDateTime.now()
|
||||||
|
val includeAdult = resolveAdultVisibility(member)
|
||||||
|
|
||||||
|
return HomeRecommendationResponse(
|
||||||
|
lives = queryService.findLiveRecommendations(
|
||||||
|
limit = HOME_LIVE_LIMIT,
|
||||||
|
includeAdultLives = includeAdult
|
||||||
|
).map { it.toItem() },
|
||||||
|
banners = queryService.findHomeBanners(HOME_BANNER_LIMIT).map { it.toItem() },
|
||||||
|
recentlyActiveCreators = queryService.findRecentlyActiveCreators(HOME_ACTIVE_CREATOR_LIMIT, includeAdult)
|
||||||
|
.map { it.toItem() },
|
||||||
|
recentDebutCreators = queryService.findRecentDebutCreators(
|
||||||
|
now,
|
||||||
|
limit = HOME_RECENT_DEBUT_CREATOR_LIMIT,
|
||||||
|
includeAdultContents = includeAdult
|
||||||
|
)
|
||||||
|
.map { it.toItem() },
|
||||||
|
firstAudioContents = queryService.findFirstAudioContents(
|
||||||
|
now,
|
||||||
|
limit = HOME_FIRST_AUDIO_CONTENT_LIMIT,
|
||||||
|
includeAdultContents = includeAdult
|
||||||
|
)
|
||||||
|
.map { it.toItem() },
|
||||||
|
aiCharacters = queryService.findAiCharacterRecommendations(limit = HOME_AI_CHARACTER_LIMIT).map { it.toItem() },
|
||||||
|
genreCreators = queryService.findGenreCreatorRecommendations(
|
||||||
|
memberId = member?.id,
|
||||||
|
includeAdultGenres = includeAdult,
|
||||||
|
genreLimit = HOME_GENRE_CREATOR_GENRE_LIMIT,
|
||||||
|
creatorLimit = HOME_GENRE_CREATOR_CREATOR_LIMIT
|
||||||
|
).map { it.toItem() },
|
||||||
|
cheerCreators = queryService.findCheerCreatorRecommendations(HOME_CHEER_CREATOR_LIMIT)
|
||||||
|
.map { it.toCreatorItem() },
|
||||||
|
popularCommunities = queryService.findPopularCommunityRecommendations(
|
||||||
|
limit = HOME_POPULAR_COMMUNITY_LIMIT,
|
||||||
|
includeAdultCommunities = includeAdult
|
||||||
|
).map { it.toItem() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLives(member: Member, page: Int, size: Int): HomeRecommendationPageResponse<HomeLiveItem> {
|
||||||
|
val fetched = queryService.findLiveRecommendations(
|
||||||
|
offset = page.toOffset(size),
|
||||||
|
limit = size + 1,
|
||||||
|
includeAdultLives = resolveAdultVisibility(member)
|
||||||
|
)
|
||||||
|
return fetched.toPage(page, size) { it.toItem() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRecentDebutCreators(member: Member, page: Int, size: Int): HomeRecommendationPageResponse<HomeCreatorItem> {
|
||||||
|
val fetched = queryService.findRecentDebutCreators(
|
||||||
|
now = LocalDateTime.now(),
|
||||||
|
offset = page.toOffset(size),
|
||||||
|
limit = size + 1,
|
||||||
|
includeAdultContents = resolveAdultVisibility(member)
|
||||||
|
)
|
||||||
|
return fetched.toPage(page, size) { it.toItem() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFirstAudioContents(member: Member, page: Int, size: Int): HomeRecommendationPageResponse<HomeFirstAudioContentItem> {
|
||||||
|
val fetched = queryService.findFirstAudioContents(
|
||||||
|
now = LocalDateTime.now(),
|
||||||
|
offset = page.toOffset(size),
|
||||||
|
limit = size + 1,
|
||||||
|
includeAdultContents = resolveAdultVisibility(member)
|
||||||
|
)
|
||||||
|
return fetched.toPage(page, size) { it.toItem() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAiCharacters(member: Member, page: Int, size: Int): HomeRecommendationPageResponse<HomeAiCharacterItem> {
|
||||||
|
val fetched = queryService.findAiCharacterRecommendations(offset = page.toOffset(size), limit = size + 1)
|
||||||
|
return fetched.toPage(page, size) { it.toItem() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveAdultVisibility(member: Member?): Boolean {
|
||||||
|
if (member == null) return false
|
||||||
|
val preference = memberContentPreferenceService.initializeDefaultPreference(member)
|
||||||
|
return isAdultVisibleByPolicy(member, preference.isAdultContentVisible)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Int.toOffset(size: Int): Int = this * size
|
||||||
|
|
||||||
|
private fun <S, T> List<S>.toPage(
|
||||||
|
page: Int,
|
||||||
|
size: Int,
|
||||||
|
transform: (S) -> T
|
||||||
|
): HomeRecommendationPageResponse<T> {
|
||||||
|
val items = this.take(size).map(transform)
|
||||||
|
val hasNext = this.size > size
|
||||||
|
return HomeRecommendationPageResponse(items = items, page = page, size = size, hasNext = hasNext)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun HomeLiveRecommendationRecord.toItem() = HomeLiveItem(
|
||||||
|
liveRoomId = liveRoomId,
|
||||||
|
creatorId = creatorId,
|
||||||
|
creatorNickname = creatorNickname,
|
||||||
|
creatorProfileImage = imageUrl(cloudFrontHost, creatorProfileImage),
|
||||||
|
title = title,
|
||||||
|
coverImage = imageUrl(cloudFrontHost, coverImage),
|
||||||
|
beginDateTime = beginDateTime.toUtcIso(),
|
||||||
|
channelName = channelName
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun HomeBannerRecommendationRecord.toItem() = HomeBannerItem(
|
||||||
|
bannerId = bannerId,
|
||||||
|
type = type,
|
||||||
|
thumbnailImage = imageUrl(cloudFrontHost, thumbnailImage),
|
||||||
|
eventId = eventId,
|
||||||
|
creatorId = creatorId,
|
||||||
|
seriesId = seriesId,
|
||||||
|
link = link
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun RecentlyActiveCreatorRecord.toItem() = HomeActiveCreatorItem(
|
||||||
|
creatorId = creatorId,
|
||||||
|
creatorNickname = creatorNickname,
|
||||||
|
creatorProfileImage = imageUrl(cloudFrontHost, creatorProfileImage),
|
||||||
|
activityType = activityType.name,
|
||||||
|
activityAt = activityAt.toUtcIso(),
|
||||||
|
targetId = targetId
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun RecentDebutCreatorRecord.toItem() = HomeCreatorItem(
|
||||||
|
creatorId = creatorId,
|
||||||
|
creatorNickname = creatorNickname,
|
||||||
|
creatorProfileImage = imageUrl(cloudFrontHost, creatorProfileImage)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun HomeFirstAudioContentRecord.toItem() = HomeFirstAudioContentItem(
|
||||||
|
contentId = contentId,
|
||||||
|
creatorId = creatorId,
|
||||||
|
creatorNickname = creatorNickname,
|
||||||
|
creatorProfileImage = imageUrl(cloudFrontHost, creatorProfileImage),
|
||||||
|
title = title,
|
||||||
|
coverImage = imageUrl(cloudFrontHost, coverImage),
|
||||||
|
releaseDate = releaseDate.toUtcIso()
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun HomeAiCharacterRecommendationRecord.toItem() = HomeAiCharacterItem(
|
||||||
|
characterId = characterId,
|
||||||
|
name = name,
|
||||||
|
description = description,
|
||||||
|
totalChatCount = totalChatCount,
|
||||||
|
originalWorkTitle = originalWorkTitle
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun HomeGenreCreatorRecommendationGroup.toItem() = HomeGenreCreatorGroupItem(
|
||||||
|
genreId = genreId,
|
||||||
|
genreName = genreName,
|
||||||
|
creators = creators.map {
|
||||||
|
HomeCreatorItem(
|
||||||
|
creatorId = it.creatorId,
|
||||||
|
creatorNickname = it.creatorNickname,
|
||||||
|
creatorProfileImage = imageUrl(cloudFrontHost, it.creatorProfileImage)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun HomeCheerCreatorRecommendationRecord.toCreatorItem() = HomeCreatorItem(
|
||||||
|
creatorId = creatorId,
|
||||||
|
creatorNickname = creatorNickname,
|
||||||
|
creatorProfileImage = imageUrl(cloudFrontHost, creatorProfileImage)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun HomePopularCommunityRecommendationRecord.toItem() = HomePopularCommunityItem(
|
||||||
|
communityId = communityId,
|
||||||
|
creatorId = creatorId,
|
||||||
|
creatorNickname = creatorNickname,
|
||||||
|
creatorProfileImage = imageUrl(cloudFrontHost, creatorProfileImage),
|
||||||
|
content = content,
|
||||||
|
createdAt = createdAt.toUtcIso(),
|
||||||
|
likeCount = likeCount,
|
||||||
|
commentCount = commentCount
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val HOME_LIVE_LIMIT = 20
|
||||||
|
private const val HOME_BANNER_LIMIT = 20
|
||||||
|
private const val HOME_ACTIVE_CREATOR_LIMIT = 10
|
||||||
|
private const val HOME_RECENT_DEBUT_CREATOR_LIMIT = 10
|
||||||
|
private const val HOME_FIRST_AUDIO_CONTENT_LIMIT = 10
|
||||||
|
private const val HOME_AI_CHARACTER_LIMIT = 10
|
||||||
|
private const val HOME_GENRE_CREATOR_GENRE_LIMIT = 5
|
||||||
|
private const val HOME_GENRE_CREATOR_CREATOR_LIMIT = 8
|
||||||
|
private const val HOME_CHEER_CREATOR_LIMIT = 8
|
||||||
|
private const val HOME_POPULAR_COMMUNITY_LIMIT = 10
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user