feat(audio-recommendation): 오디오 추천 응답 변환을 추가한다
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.audio.recommendation.application
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.audio.recommendation.dto.AudioRecommendationsResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.application.AudioRecommendationQueryService
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class AudioRecommendationFacade(
|
||||||
|
private val queryService: AudioRecommendationQueryService
|
||||||
|
) {
|
||||||
|
fun getRecommendations(member: Member?): AudioRecommendationsResponse {
|
||||||
|
return AudioRecommendationsResponse.from(queryService.getRecommendations(member))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.audio.recommendation.dto
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.common.dto.RecommendationBannerResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.AudioCard
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.AudioRecommendations
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.CommentedAudio
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.OriginalSeries
|
||||||
|
|
||||||
|
data class AudioRecommendationsResponse(
|
||||||
|
val banners: List<RecommendationBannerResponse>,
|
||||||
|
val originalSeries: List<OriginalSeriesResponse>,
|
||||||
|
val latestAudios: List<AudioCardResponse>,
|
||||||
|
val newAndHotAudios: List<AudioCardResponse>,
|
||||||
|
val freeAudios: List<AudioCardResponse>,
|
||||||
|
val pointAudios: List<AudioCardResponse>,
|
||||||
|
val mostCommentedAudios: List<CommentedAudioResponse>,
|
||||||
|
val recommendedAudios: List<AudioCardResponse>
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun from(recommendations: AudioRecommendations): AudioRecommendationsResponse {
|
||||||
|
return AudioRecommendationsResponse(
|
||||||
|
banners = recommendations.banners.map(RecommendationBannerResponse::from),
|
||||||
|
originalSeries = recommendations.originalSeries.map(OriginalSeriesResponse::from),
|
||||||
|
latestAudios = recommendations.latestAudios.map(AudioCardResponse::from),
|
||||||
|
newAndHotAudios = recommendations.newAndHotAudios.map(AudioCardResponse::from),
|
||||||
|
freeAudios = recommendations.freeAudios.map(AudioCardResponse::from),
|
||||||
|
pointAudios = recommendations.pointAudios.map(AudioCardResponse::from),
|
||||||
|
mostCommentedAudios = recommendations.mostCommentedAudios.map(CommentedAudioResponse::from),
|
||||||
|
recommendedAudios = recommendations.recommendedAudios.map(AudioCardResponse::from)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class OriginalSeriesResponse(
|
||||||
|
val seriesId: Long,
|
||||||
|
val coverImageUrl: String?
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun from(series: OriginalSeries): OriginalSeriesResponse {
|
||||||
|
return OriginalSeriesResponse(series.seriesId, series.coverImageUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class AudioCardResponse(
|
||||||
|
val audioContentId: Long,
|
||||||
|
val title: String,
|
||||||
|
val duration: String?,
|
||||||
|
val imageUrl: String?,
|
||||||
|
val price: Int,
|
||||||
|
@JsonProperty("isAdult")
|
||||||
|
val isAdult: Boolean,
|
||||||
|
@JsonProperty("isPointAvailable")
|
||||||
|
val isPointAvailable: Boolean,
|
||||||
|
@JsonProperty("isFirstContent")
|
||||||
|
val isFirstContent: Boolean,
|
||||||
|
@JsonProperty("isOriginalSeries")
|
||||||
|
val isOriginalSeries: Boolean,
|
||||||
|
val creatorNickname: String
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun from(audio: AudioCard): AudioCardResponse {
|
||||||
|
return AudioCardResponse(
|
||||||
|
audioContentId = audio.audioContentId,
|
||||||
|
title = audio.title,
|
||||||
|
duration = audio.duration,
|
||||||
|
imageUrl = audio.imageUrl,
|
||||||
|
price = audio.price,
|
||||||
|
isAdult = audio.isAdult,
|
||||||
|
isPointAvailable = audio.isPointAvailable,
|
||||||
|
isFirstContent = audio.isFirstContent,
|
||||||
|
isOriginalSeries = audio.isOriginalSeries,
|
||||||
|
creatorNickname = audio.creatorNickname
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class CommentedAudioResponse(
|
||||||
|
val audioContentId: Long,
|
||||||
|
val title: String,
|
||||||
|
val imageUrl: String?,
|
||||||
|
val latestComment: String,
|
||||||
|
val latestCommentWriterProfileImageUrl: String
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun from(audio: CommentedAudio): CommentedAudioResponse {
|
||||||
|
return CommentedAudioResponse(
|
||||||
|
audioContentId = audio.audioContentId,
|
||||||
|
title = audio.title,
|
||||||
|
imageUrl = audio.imageUrl,
|
||||||
|
latestComment = audio.latestComment,
|
||||||
|
latestCommentWriterProfileImageUrl = audio.latestCommentWriterProfileImageUrl
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.audio.recommendation.application
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.application.AudioRecommendationQueryService
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.AudioCard
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.AudioRecommendations
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.CommentedAudio
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.OriginalSeries
|
||||||
|
import kr.co.vividnext.sodalive.v2.common.domain.RecommendationBanner
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.Mockito
|
||||||
|
|
||||||
|
class AudioRecommendationFacadeTest {
|
||||||
|
private val objectMapper = jacksonObjectMapper()
|
||||||
|
private val queryService = Mockito.mock(AudioRecommendationQueryService::class.java)
|
||||||
|
private val facade = AudioRecommendationFacade(queryService)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("facade는 도메인 추천 결과를 모든 공개 응답 필드로 변환한다")
|
||||||
|
fun shouldConvertDomainRecommendationsToResponse() {
|
||||||
|
Mockito.doReturn(domain()).`when`(queryService).getRecommendations(null)
|
||||||
|
|
||||||
|
val response = facade.getRecommendations(null)
|
||||||
|
|
||||||
|
assertEquals(1, response.banners.size)
|
||||||
|
assertEquals(1, response.originalSeries.size)
|
||||||
|
assertEquals(1, response.latestAudios.size)
|
||||||
|
assertEquals(1, response.newAndHotAudios.size)
|
||||||
|
assertEquals(1, response.freeAudios.size)
|
||||||
|
assertEquals(1, response.pointAudios.size)
|
||||||
|
assertEquals(1, response.mostCommentedAudios.size)
|
||||||
|
assertEquals(1, response.recommendedAudios.size)
|
||||||
|
assertEquals(false, response.latestAudios[0].isOriginalSeries)
|
||||||
|
|
||||||
|
val json = objectMapper.readTree(objectMapper.writeValueAsString(response))
|
||||||
|
assertEquals(false, json["latestAudios"][0]["isAdult"].asBoolean())
|
||||||
|
assertEquals(true, json["latestAudios"][0]["isPointAvailable"].asBoolean())
|
||||||
|
assertEquals(true, json["latestAudios"][0]["isFirstContent"].asBoolean())
|
||||||
|
assertEquals(false, json["latestAudios"][0]["isOriginalSeries"].asBoolean())
|
||||||
|
assertEquals(false, json["latestAudios"][0].has("adult"))
|
||||||
|
assertEquals(false, json["latestAudios"][0].has("pointAvailable"))
|
||||||
|
assertEquals("latest comment", json["mostCommentedAudios"][0]["latestComment"].asText())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun domain(): AudioRecommendations {
|
||||||
|
val card = AudioCard(
|
||||||
|
audioContentId = 1L,
|
||||||
|
title = "audio",
|
||||||
|
duration = "00:01",
|
||||||
|
imageUrl = "https://cdn.test/audio.png",
|
||||||
|
price = 0,
|
||||||
|
isAdult = false,
|
||||||
|
isPointAvailable = true,
|
||||||
|
isFirstContent = true,
|
||||||
|
isOriginalSeries = false,
|
||||||
|
creatorNickname = "creator"
|
||||||
|
)
|
||||||
|
return AudioRecommendations(
|
||||||
|
banners = listOf(RecommendationBanner("https://cdn.test/banner.png", null, null, null, "https://link.test")),
|
||||||
|
originalSeries = listOf(OriginalSeries(2L, "https://cdn.test/series.png")),
|
||||||
|
latestAudios = listOf(card),
|
||||||
|
newAndHotAudios = listOf(card.copy(audioContentId = 3L)),
|
||||||
|
freeAudios = listOf(card.copy(audioContentId = 4L)),
|
||||||
|
pointAudios = listOf(card.copy(audioContentId = 5L)),
|
||||||
|
mostCommentedAudios = listOf(
|
||||||
|
CommentedAudio(
|
||||||
|
audioContentId = 6L,
|
||||||
|
title = "commented",
|
||||||
|
imageUrl = "https://cdn.test/commented.png",
|
||||||
|
latestComment = "latest comment",
|
||||||
|
latestCommentWriterProfileImageUrl = "https://cdn.test/profile.png"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
recommendedAudios = listOf(card.copy(audioContentId = 7L))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user