test #426
@@ -0,0 +1,73 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.audio.recommendation.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.audio.recommendation.domain.AudioRecommendationVisibility
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.AudioRecommendations
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.port.out.AudioRecommendationQueryPort
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendedSectionType
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class AudioRecommendationQueryService(
|
||||||
|
private val queryPort: AudioRecommendationQueryPort,
|
||||||
|
private val memberContentPreferenceService: MemberContentPreferenceService
|
||||||
|
) {
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
fun getRecommendations(member: Member?): AudioRecommendations {
|
||||||
|
val now = LocalDateTime.now()
|
||||||
|
val canViewAdultContent = canViewAdultContent(member)
|
||||||
|
return AudioRecommendations(
|
||||||
|
banners = queryPort.findBanners(BANNER_LIMIT, member?.id, canViewAdultContent),
|
||||||
|
originalSeries = queryPort.findOriginalSeries(ORIGINAL_SERIES_LIMIT, member?.id, canViewAdultContent, now),
|
||||||
|
latestAudios = queryPort.findLatestAudios(LATEST_AUDIO_LIMIT, member?.id, canViewAdultContent, now),
|
||||||
|
newAndHotAudios = emptyList(),
|
||||||
|
freeAudios = queryPort.findFreeAudios(FREE_AUDIO_LIMIT, member?.id, canViewAdultContent, now),
|
||||||
|
pointAudios = queryPort.findPointAudios(POINT_AUDIO_LIMIT, member?.id, canViewAdultContent, now),
|
||||||
|
mostCommentedAudios = emptyList(),
|
||||||
|
recommendedAudios = emptyList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resolveVisibility(member: Member?): AudioRecommendationVisibility {
|
||||||
|
return if (canViewAdultContent(member)) AudioRecommendationVisibility.ALL else AudioRecommendationVisibility.SAFE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newAndHotSectionType(visibility: AudioRecommendationVisibility): RecommendedSectionType {
|
||||||
|
return when (visibility) {
|
||||||
|
AudioRecommendationVisibility.SAFE -> RecommendedSectionType.NEW_AND_HOT_AUDIO_SAFE
|
||||||
|
AudioRecommendationVisibility.ALL -> RecommendedSectionType.NEW_AND_HOT_AUDIO_ALL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mostCommentedSectionType(visibility: AudioRecommendationVisibility): RecommendedSectionType {
|
||||||
|
return when (visibility) {
|
||||||
|
AudioRecommendationVisibility.SAFE -> RecommendedSectionType.MOST_COMMENTED_AUDIO_SAFE
|
||||||
|
AudioRecommendationVisibility.ALL -> RecommendedSectionType.MOST_COMMENTED_AUDIO_ALL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun recommendedAudioSectionType(visibility: AudioRecommendationVisibility): RecommendedSectionType {
|
||||||
|
return when (visibility) {
|
||||||
|
AudioRecommendationVisibility.SAFE -> RecommendedSectionType.RECOMMENDED_AUDIO_SAFE
|
||||||
|
AudioRecommendationVisibility.ALL -> RecommendedSectionType.RECOMMENDED_AUDIO_ALL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun canViewAdultContent(member: Member?): Boolean {
|
||||||
|
if (member == null) return false
|
||||||
|
val preference = memberContentPreferenceService.initializeDefaultPreference(member)
|
||||||
|
return isAdultVisibleByPolicy(member, preference.isAdultContentVisible)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val BANNER_LIMIT = 20
|
||||||
|
const val ORIGINAL_SERIES_LIMIT = 12
|
||||||
|
const val LATEST_AUDIO_LIMIT = 12
|
||||||
|
const val FREE_AUDIO_LIMIT = 10
|
||||||
|
const val POINT_AUDIO_LIMIT = 10
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.audio.recommendation.port.out
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.AudioCard
|
||||||
|
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 java.time.LocalDateTime
|
||||||
|
|
||||||
|
interface AudioRecommendationQueryPort {
|
||||||
|
fun findBanners(limit: Int, memberId: Long?, canViewAdultContent: Boolean): List<RecommendationBanner>
|
||||||
|
fun findOriginalSeries(limit: Int, memberId: Long?, canViewAdultContent: Boolean, now: LocalDateTime): List<OriginalSeries>
|
||||||
|
fun findLatestAudios(limit: Int, memberId: Long?, canViewAdultContent: Boolean, now: LocalDateTime): List<AudioCard>
|
||||||
|
fun findFreeAudios(limit: Int, memberId: Long?, canViewAdultContent: Boolean, now: LocalDateTime): List<AudioCard>
|
||||||
|
fun findPointAudios(limit: Int, memberId: Long?, canViewAdultContent: Boolean, now: LocalDateTime): List<AudioCard>
|
||||||
|
fun findAudioCardsByIds(
|
||||||
|
contentIds: List<Long>,
|
||||||
|
memberId: Long?,
|
||||||
|
canViewAdultContent: Boolean,
|
||||||
|
now: LocalDateTime
|
||||||
|
): List<AudioCard>
|
||||||
|
fun findCommentedAudiosByIds(contentIds: List<Long>, memberId: Long?, canViewAdultContent: Boolean): List<CommentedAudio>
|
||||||
|
}
|
||||||
@@ -9,5 +9,11 @@ enum class RecommendedSectionType(val code: String) {
|
|||||||
AI_CHARACTER("AI_CHARACTER"),
|
AI_CHARACTER("AI_CHARACTER"),
|
||||||
GENRE_CREATOR("GENRE_CREATOR"),
|
GENRE_CREATOR("GENRE_CREATOR"),
|
||||||
CHEER_CREATOR("CHEER_CREATOR"),
|
CHEER_CREATOR("CHEER_CREATOR"),
|
||||||
POPULAR_COMMUNITY("POPULAR_COMMUNITY")
|
POPULAR_COMMUNITY("POPULAR_COMMUNITY"),
|
||||||
|
NEW_AND_HOT_AUDIO_SAFE("NEW_AND_HOT_AUDIO_SAFE"),
|
||||||
|
NEW_AND_HOT_AUDIO_ALL("NEW_AND_HOT_AUDIO_ALL"),
|
||||||
|
MOST_COMMENTED_AUDIO_SAFE("MOST_COMMENTED_AUDIO_SAFE"),
|
||||||
|
MOST_COMMENTED_AUDIO_ALL("MOST_COMMENTED_AUDIO_ALL"),
|
||||||
|
RECOMMENDED_AUDIO_SAFE("RECOMMENDED_AUDIO_SAFE"),
|
||||||
|
RECOMMENDED_AUDIO_ALL("RECOMMENDED_AUDIO_ALL")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.audio.recommendation.application
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.domain.AudioRecommendationVisibility
|
||||||
|
import kr.co.vividnext.sodalive.v2.audio.recommendation.port.out.AudioRecommendationQueryPort
|
||||||
|
import kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendedSectionType
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.Mockito
|
||||||
|
|
||||||
|
class AudioRecommendationQueryServiceTest {
|
||||||
|
private val queryPort = Mockito.mock(AudioRecommendationQueryPort::class.java)
|
||||||
|
private val preferenceService = Mockito.mock(MemberContentPreferenceService::class.java)
|
||||||
|
private val service = AudioRecommendationQueryService(queryPort, preferenceService)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("비회원은 SAFE visibility를 사용한다")
|
||||||
|
fun shouldResolveSafeVisibilityForAnonymous() {
|
||||||
|
assertEquals(AudioRecommendationVisibility.SAFE, service.resolveVisibility(null))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("visibility와 섹션 조합은 신규 RecommendedSectionType으로 매핑된다")
|
||||||
|
fun shouldMapVisibilityToAudioSectionTypes() {
|
||||||
|
assertEquals(
|
||||||
|
RecommendedSectionType.NEW_AND_HOT_AUDIO_SAFE,
|
||||||
|
service.newAndHotSectionType(AudioRecommendationVisibility.SAFE)
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
RecommendedSectionType.NEW_AND_HOT_AUDIO_ALL,
|
||||||
|
service.newAndHotSectionType(AudioRecommendationVisibility.ALL)
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
RecommendedSectionType.MOST_COMMENTED_AUDIO_SAFE,
|
||||||
|
service.mostCommentedSectionType(AudioRecommendationVisibility.SAFE)
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
RecommendedSectionType.MOST_COMMENTED_AUDIO_ALL,
|
||||||
|
service.mostCommentedSectionType(AudioRecommendationVisibility.ALL)
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
RecommendedSectionType.RECOMMENDED_AUDIO_SAFE,
|
||||||
|
service.recommendedAudioSectionType(AudioRecommendationVisibility.SAFE)
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
RecommendedSectionType.RECOMMENDED_AUDIO_ALL,
|
||||||
|
service.recommendedAudioSectionType(AudioRecommendationVisibility.ALL)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user