feat(home): 홈 추천 조회 로그와 회원 컨텍스트를 전달한다
This commit is contained in:
@@ -25,6 +25,7 @@ import kr.co.vividnext.sodalive.v2.recommend.port.out.HomeLiveRecommendationReco
|
||||
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.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.stereotype.Component
|
||||
import java.time.LocalDateTime
|
||||
@@ -36,78 +37,176 @@ class HomeRecommendationFacade(
|
||||
@Value("\${cloud.aws.cloud-front.host}")
|
||||
private val cloudFrontHost: String
|
||||
) {
|
||||
fun getHomeRecommendations(member: Member?): HomeRecommendationResponse {
|
||||
val now = LocalDateTime.now()
|
||||
val includeAdult = resolveAdultVisibility(member)
|
||||
private val log = LoggerFactory.getLogger(javaClass)
|
||||
|
||||
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
|
||||
fun getHomeRecommendations(member: Member?): HomeRecommendationResponse {
|
||||
val startedAt = System.currentTimeMillis()
|
||||
return runCatching {
|
||||
val now = LocalDateTime.now()
|
||||
val includeAdult = resolveAdultVisibility(member)
|
||||
|
||||
HomeRecommendationResponse(
|
||||
lives = queryService.findLiveRecommendations(
|
||||
limit = HOME_LIVE_LIMIT,
|
||||
memberId = member?.id,
|
||||
includeAdultLives = includeAdult
|
||||
).map { it.toItem() },
|
||||
banners = queryService.findHomeBanners(HOME_BANNER_LIMIT, member?.id).map { it.toItem() },
|
||||
recentlyActiveCreators = queryService.findRecentlyActiveCreators(
|
||||
HOME_ACTIVE_CREATOR_LIMIT,
|
||||
member?.id,
|
||||
includeAdult
|
||||
)
|
||||
.map { it.toItem() },
|
||||
recentDebutCreators = queryService.findRecentDebutCreators(
|
||||
now,
|
||||
limit = HOME_RECENT_DEBUT_CREATOR_LIMIT,
|
||||
memberId = member?.id,
|
||||
includeAdultContents = includeAdult
|
||||
)
|
||||
.map { it.toItem() },
|
||||
firstAudioContents = queryService.findFirstAudioContents(
|
||||
now,
|
||||
limit = HOME_FIRST_AUDIO_CONTENT_LIMIT,
|
||||
memberId = member?.id,
|
||||
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, member?.id)
|
||||
.map { it.toCreatorItem() },
|
||||
popularCommunities = queryService.findPopularCommunityRecommendations(
|
||||
limit = HOME_POPULAR_COMMUNITY_LIMIT,
|
||||
memberId = member?.id,
|
||||
includeAdultCommunities = includeAdult
|
||||
).map { it.toItem() }
|
||||
)
|
||||
.map { it.toItem() },
|
||||
firstAudioContents = queryService.findFirstAudioContents(
|
||||
now,
|
||||
limit = HOME_FIRST_AUDIO_CONTENT_LIMIT,
|
||||
includeAdultContents = includeAdult
|
||||
}.onSuccess { response ->
|
||||
log.info(
|
||||
"event=home_recommendations_query_success memberId={} elapsedMs={} emptySections={}",
|
||||
member?.id,
|
||||
System.currentTimeMillis() - startedAt,
|
||||
response.emptySections()
|
||||
)
|
||||
.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() }
|
||||
)
|
||||
}.onFailure { ex ->
|
||||
log.warn(
|
||||
"event=home_recommendations_query_failure memberId={} elapsedMs={} error={}",
|
||||
member?.id,
|
||||
System.currentTimeMillis() - startedAt,
|
||||
ex.message,
|
||||
ex
|
||||
)
|
||||
}.getOrThrow()
|
||||
}
|
||||
|
||||
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() }
|
||||
val startedAt = System.currentTimeMillis()
|
||||
return runCatching {
|
||||
val fetched = queryService.findLiveRecommendations(
|
||||
offset = page.toOffset(size),
|
||||
limit = size + 1,
|
||||
memberId = member.id,
|
||||
includeAdultLives = resolveAdultVisibility(member)
|
||||
)
|
||||
fetched.toPage(page, size) { it.toItem() }
|
||||
}.onSuccess {
|
||||
logPageSuccess("LIVE", member, page, size, it.items.size, System.currentTimeMillis() - startedAt)
|
||||
}.onFailure { ex ->
|
||||
logPageFailure("LIVE", member, page, size, startedAt, ex)
|
||||
}.getOrThrow()
|
||||
}
|
||||
|
||||
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() }
|
||||
val startedAt = System.currentTimeMillis()
|
||||
return runCatching {
|
||||
val fetched = queryService.findRecentDebutCreators(
|
||||
now = LocalDateTime.now(),
|
||||
offset = page.toOffset(size),
|
||||
limit = size + 1,
|
||||
memberId = member.id,
|
||||
includeAdultContents = resolveAdultVisibility(member)
|
||||
)
|
||||
fetched.toPage(page, size) { it.toItem() }
|
||||
}.onSuccess {
|
||||
logPageSuccess("DEBUT_CREATOR", member, page, size, it.items.size, System.currentTimeMillis() - startedAt)
|
||||
}.onFailure { ex ->
|
||||
logPageFailure("DEBUT_CREATOR", member, page, size, startedAt, ex)
|
||||
}.getOrThrow()
|
||||
}
|
||||
|
||||
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() }
|
||||
val startedAt = System.currentTimeMillis()
|
||||
return runCatching {
|
||||
val fetched = queryService.findFirstAudioContents(
|
||||
now = LocalDateTime.now(),
|
||||
offset = page.toOffset(size),
|
||||
limit = size + 1,
|
||||
memberId = member.id,
|
||||
includeAdultContents = resolveAdultVisibility(member)
|
||||
)
|
||||
fetched.toPage(page, size) { it.toItem() }
|
||||
}.onSuccess {
|
||||
logPageSuccess("FIRST_AUDIO_CONTENT", member, page, size, it.items.size, System.currentTimeMillis() - startedAt)
|
||||
}.onFailure { ex ->
|
||||
logPageFailure("FIRST_AUDIO_CONTENT", member, page, size, startedAt, ex)
|
||||
}.getOrThrow()
|
||||
}
|
||||
|
||||
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() }
|
||||
val startedAt = System.currentTimeMillis()
|
||||
return runCatching {
|
||||
val fetched = queryService.findAiCharacterRecommendations(offset = page.toOffset(size), limit = size + 1)
|
||||
fetched.toPage(page, size) { it.toItem() }
|
||||
}.onSuccess {
|
||||
logPageSuccess("AI_CHARACTER", member, page, size, it.items.size, System.currentTimeMillis() - startedAt)
|
||||
}.onFailure { ex ->
|
||||
logPageFailure("AI_CHARACTER", member, page, size, startedAt, ex)
|
||||
}.getOrThrow()
|
||||
}
|
||||
|
||||
private fun logPageSuccess(section: String, member: Member, page: Int, size: Int, itemCount: Int, elapsedMs: Long) {
|
||||
log.info(
|
||||
"event=home_recommendations_page_query_success section={} memberId={} page={} size={} itemCount={} elapsedMs={}",
|
||||
section,
|
||||
member.id,
|
||||
page,
|
||||
size,
|
||||
itemCount,
|
||||
elapsedMs
|
||||
)
|
||||
}
|
||||
|
||||
private fun logPageFailure(section: String, member: Member, page: Int, size: Int, startedAt: Long, ex: Throwable) {
|
||||
log.warn(
|
||||
"event=home_recommendations_page_query_failure section={} memberId={} page={} size={} elapsedMs={} error={}",
|
||||
section,
|
||||
member.id,
|
||||
page,
|
||||
size,
|
||||
System.currentTimeMillis() - startedAt,
|
||||
ex.message,
|
||||
ex
|
||||
)
|
||||
}
|
||||
|
||||
private fun HomeRecommendationResponse.emptySections(): List<String> {
|
||||
return buildList {
|
||||
if (lives.isEmpty()) add("lives")
|
||||
if (banners.isEmpty()) add("banners")
|
||||
if (recentlyActiveCreators.isEmpty()) add("recentlyActiveCreators")
|
||||
if (recentDebutCreators.isEmpty()) add("recentDebutCreators")
|
||||
if (firstAudioContents.isEmpty()) add("firstAudioContents")
|
||||
if (aiCharacters.isEmpty()) add("aiCharacters")
|
||||
if (genreCreators.isEmpty()) add("genreCreators")
|
||||
if (cheerCreators.isEmpty()) add("cheerCreators")
|
||||
if (popularCommunities.isEmpty()) add("popularCommunities")
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveAdultVisibility(member: Member?): Boolean {
|
||||
|
||||
Reference in New Issue
Block a user