feat(subbanner): 홈과 콘텐츠 추천 응답에 서브 배너를 통합한다

This commit is contained in:
2026-10-29 23:23:20 +09:00
parent a30196e0c9
commit 73aa6da7f0
12 changed files with 587 additions and 5 deletions
@@ -4,14 +4,22 @@ import kr.co.vividnext.sodalive.i18n.Lang
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.v2.api.content.recommendation.dto.AudioRecommendationsResponse
import kr.co.vividnext.sodalive.v2.content.recommendation.application.AudioRecommendationQueryService
import kr.co.vividnext.sodalive.v2.subbanner.application.SubBannerService
import kr.co.vividnext.sodalive.v2.subbanner.domain.SubBannerPlacement
import kr.co.vividnext.sodalive.v2.subbanner.dto.SubBannerResponse
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
@Component
class AudioRecommendationFacade(
private val queryService: AudioRecommendationQueryService
private val queryService: AudioRecommendationQueryService,
private val subBannerService: SubBannerService,
@Value("\${cloud.aws.cloud-front.host}") private val cloudFrontHost: String
) {
fun getRecommendations(member: Member?, lang: Lang): AudioRecommendationsResponse {
val result = queryService.getRecommendations(member, lang)
return AudioRecommendationsResponse.from(result)
val subBanners = subBannerService.findVisibleByPlacement(SubBannerPlacement.CONTENT_RECOMMENDATION)
.map { SubBannerResponse.from(it, cloudFrontHost) }
return AudioRecommendationsResponse.from(result, subBanners)
}
}
@@ -6,9 +6,11 @@ import kr.co.vividnext.sodalive.v2.content.recommendation.domain.AudioCard
import kr.co.vividnext.sodalive.v2.content.recommendation.domain.AudioRecommendations
import kr.co.vividnext.sodalive.v2.content.recommendation.domain.CommentedAudio
import kr.co.vividnext.sodalive.v2.content.recommendation.domain.OriginalSeries
import kr.co.vividnext.sodalive.v2.subbanner.dto.SubBannerResponse
data class AudioRecommendationsResponse(
val banners: List<RecommendationBannerResponse>,
val subBanners: List<SubBannerResponse>,
val originalSeries: List<OriginalSeriesResponse>,
val latestAudios: List<AudioCardResponse>,
val newAndHotAudios: List<AudioCardResponse>,
@@ -18,9 +20,10 @@ data class AudioRecommendationsResponse(
val recommendedAudios: List<AudioCardResponse>
) {
companion object {
fun from(recommendations: AudioRecommendations): AudioRecommendationsResponse {
fun from(recommendations: AudioRecommendations, subBanners: List<SubBannerResponse>): AudioRecommendationsResponse {
return AudioRecommendationsResponse(
banners = recommendations.banners.map(RecommendationBannerResponse::from),
subBanners = subBanners,
originalSeries = recommendations.originalSeries.map(OriginalSeriesResponse::from),
latestAudios = recommendations.latestAudios.map(AudioCardResponse::from),
newAndHotAudios = recommendations.newAndHotAudios.map(AudioCardResponse::from),
@@ -30,6 +30,9 @@ import kr.co.vividnext.sodalive.v2.recommendation.port.out.HomeLiveRecommendatio
import kr.co.vividnext.sodalive.v2.recommendation.port.out.HomePopularCommunityRecommendationRecord
import kr.co.vividnext.sodalive.v2.recommendation.port.out.RecentDebutCreatorRecord
import kr.co.vividnext.sodalive.v2.recommendation.port.out.RecentlyActiveCreatorRecord
import kr.co.vividnext.sodalive.v2.subbanner.application.SubBannerService
import kr.co.vividnext.sodalive.v2.subbanner.domain.SubBannerPlacement
import kr.co.vividnext.sodalive.v2.subbanner.dto.SubBannerResponse
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
@@ -39,6 +42,7 @@ import java.time.LocalDateTime
class HomeRecommendationFacade(
private val queryService: HomeRecommendationQueryService,
private val memberContentPreferenceService: MemberContentPreferenceService,
private val subBannerService: SubBannerService,
@Value("\${cloud.aws.cloud-front.host}")
private val cloudFrontHost: String
) {
@@ -68,6 +72,8 @@ class HomeRecommendationFacade(
lang = bannerLang
)
.map { RecommendationBannerResponse.from(it.toBanner()) },
subBanners = subBannerService.findVisibleByPlacement(SubBannerPlacement.HOME_RECOMMENDATION)
.map { SubBannerResponse.from(it, cloudFrontHost) },
recentlyActiveCreators = queryService.findRecentlyActiveCreators(
HOME_ACTIVE_CREATOR_LIMIT,
member?.id,
@@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.v2.api.home.dto.recommendation
import com.fasterxml.jackson.annotation.JsonProperty
import kr.co.vividnext.sodalive.v2.api.common.dto.RecommendationBannerResponse
import kr.co.vividnext.sodalive.v2.subbanner.dto.SubBannerResponse
import java.time.LocalDateTime
import java.time.ZoneOffset
@@ -20,6 +21,7 @@ internal fun profileImageUrl(cloudFrontHost: String, path: String?): String {
data class HomeRecommendationResponse(
val lives: List<HomeLiveItem>,
val banners: List<RecommendationBannerResponse>,
val subBanners: List<SubBannerResponse>,
val recentlyActiveCreators: List<HomeActiveCreatorItem>,
val recentDebutCreators: List<HomeCreatorItem>,
val firstAudioContents: List<HomeFirstAudioContentItem>,
@@ -0,0 +1,24 @@
package kr.co.vividnext.sodalive.v2.subbanner.dto
import kr.co.vividnext.sodalive.v2.subbanner.domain.SubBanner
import kr.co.vividnext.sodalive.v2.subbanner.domain.SubBannerPlacement
data class SubBannerResponse(
val id: Long,
val placement: SubBannerPlacement,
val imageUrl: String,
val webUrl: String?,
val androidDeepLink: String?,
val iosDeepLink: String?
) {
companion object {
fun from(banner: SubBanner, cloudFrontHost: String): SubBannerResponse = SubBannerResponse(
id = requireNotNull(banner.id),
placement = banner.placement,
imageUrl = "$cloudFrontHost/${banner.imagePath}",
webUrl = banner.webUrl,
androidDeepLink = banner.androidDeepLink,
iosDeepLink = banner.iosDeepLink
)
}
}