diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/AudioContentMainTabRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/AudioContentMainTabRepository.kt new file mode 100644 index 0000000..a5ef40a --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/AudioContentMainTabRepository.kt @@ -0,0 +1,62 @@ +package kr.co.vividnext.sodalive.content.main.tab + +import com.querydsl.core.types.dsl.Expressions +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.content.QAudioContent.audioContent +import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse +import kr.co.vividnext.sodalive.content.main.QContentCreatorResponse +import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme +import kr.co.vividnext.sodalive.member.MemberRole +import kr.co.vividnext.sodalive.member.QMember.member +import kr.co.vividnext.sodalive.member.block.QBlockMember.blockMember +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Repository + +@Repository +class AudioContentMainTabRepository( + private val queryFactory: JPAQueryFactory, + + @Value("\${cloud.aws.cloud-front.host}") + private val imageHost: String +) { + fun findCreatorByThemeContent( + memberId: Long, + theme: String, + minCount: Int + ): List { + val blockMemberCondition = blockMember.member.id.eq(member.id) + .and(blockMember.isActive.isTrue) + .and(blockMember.blockedMember.id.eq(memberId)) + + val where = member.isActive.isTrue + .and(member.role.eq(MemberRole.CREATOR)) + .and(audioContent.isActive.isTrue) + .and(audioContent.duration.isNotNull) + .and(audioContent.limited.isNull) + .and(audioContentTheme.isActive.isTrue) + .and(audioContentTheme.theme.eq(theme)) + .and(blockMember.id.isNull) + + return queryFactory + .select( + QContentCreatorResponse( + member.id, + member.nickname, + member.profileImage.prepend("/").prepend(imageHost) + ) + ) + .from(member) + .innerJoin(audioContent).on(member.id.eq(audioContent.member.id)) + .innerJoin(audioContent.theme, audioContentTheme) + .leftJoin(blockMember).on(blockMemberCondition) + .where(where) + .groupBy(member.id) + .having(audioContent.id.count().goe(minCount)) + .orderBy( + Expressions.numberTemplate(Double::class.java, "function('rand')").asc() + ) + .offset(0) + .limit(20) + .fetch() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrController.kt index ad35a6d..7714934 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrController.kt @@ -6,6 +6,7 @@ import kr.co.vividnext.sodalive.member.Member import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @RestController @@ -19,4 +20,19 @@ class AudioContentMainTabAsmrController(private val service: AudioContentMainTab ApiResponse.ok(service.fetchData(member)) } + + @GetMapping("/popular-content-by-creator") + fun getPopularContentByCreator( + @RequestParam creatorId: Long, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok( + service.getPopularContentByCreator( + creatorId = creatorId, + isAdult = member.auth != null + ) + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrService.kt index a8bcde8..4ac40c4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrService.kt @@ -4,7 +4,9 @@ import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.ContentType import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository +import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTabRepository import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse +import kr.co.vividnext.sodalive.content.main.tab.GetPopularContentByCreatorResponse import kr.co.vividnext.sodalive.event.EventService import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.rank.RankingService @@ -15,6 +17,7 @@ import java.time.temporal.TemporalAdjusters @Service class AudioContentMainTabAsmrService( + private val repository: AudioContentMainTabRepository, private val bannerService: AudioContentBannerService, private val contentRepository: AudioContentRepository, private val rankingService: RankingService, @@ -59,6 +62,32 @@ class AudioContentMainTabAsmrService( theme = theme ) + val creatorList = repository.findCreatorByThemeContent( + memberId = memberId, + theme = theme, + minCount = 4 + ) + + val salesRankContentList = if (creatorList.isNotEmpty()) { + rankingService.fetchCreatorContentBySalesTop2( + creatorId = creatorList[0].creatorId, + isAdult = isAdult, + theme = theme + ) + } else { + emptyList() + } + + val salesCountRankContentList = if (creatorList.isNotEmpty()) { + rankingService.fetchCreatorContentBySalesCountTop2( + creatorId = creatorList[0].creatorId, + isAdult = isAdult, + theme = theme + ) + } else { + emptyList() + } + val eventBannerList = eventService.getEventList(isAdult = isAdult) val curationList = curationRepository.findByContentMainTabId(tabId = tabId, isAdult = isAdult) @@ -77,8 +106,31 @@ class AudioContentMainTabAsmrService( contentBannerList = contentBannerList, newAsmrContentList = newAsmrContentList, rankAsmrContentList = rankAsmrContentList, + creatorList = creatorList, + salesRankContentList = salesRankContentList, + salesCountRankContentList = salesCountRankContentList, eventBannerList = eventBannerList, curationList = curationList ) } + + fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): GetPopularContentByCreatorResponse { + val theme = "ASMR" + val salesRankContentList = rankingService.fetchCreatorContentBySalesTop2( + creatorId = creatorId, + isAdult = isAdult, + theme = theme + ) + + val salesCountRankContentList = rankingService.fetchCreatorContentBySalesCountTop2( + creatorId = creatorId, + isAdult = isAdult, + theme = theme + ) + + return GetPopularContentByCreatorResponse( + salesRankContentList = salesRankContentList, + salesCountRankContentList = salesCountRankContentList + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/GetContentMainTabAsmrResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/GetContentMainTabAsmrResponse.kt index 5b406e1..9162e40 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/GetContentMainTabAsmrResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/GetContentMainTabAsmrResponse.kt @@ -1,5 +1,6 @@ package kr.co.vividnext.sodalive.content.main.tab.asmr +import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse @@ -11,6 +12,9 @@ data class GetContentMainTabAsmrResponse( val contentBannerList: List, val newAsmrContentList: List, val rankAsmrContentList: List, + val creatorList: List, + val salesRankContentList: List, + val salesCountRankContentList: List, val eventBannerList: GetEventResponse, val curationList: List ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayController.kt index c9b3739..17b7e38 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayController.kt @@ -6,6 +6,7 @@ import kr.co.vividnext.sodalive.member.Member import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @RestController @@ -19,4 +20,19 @@ class AudioContentMainTabLiveReplayController(private val service: AudioContentM ApiResponse.ok(service.fetchData(member)) } + + @GetMapping("/popular-content-by-creator") + fun getPopularContentByCreator( + @RequestParam creatorId: Long, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok( + service.getPopularContentByCreator( + creatorId = creatorId, + isAdult = member.auth != null + ) + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayService.kt index 767a8c9..d6d1164 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayService.kt @@ -4,7 +4,9 @@ import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.ContentType import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository +import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTabRepository import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse +import kr.co.vividnext.sodalive.content.main.tab.GetPopularContentByCreatorResponse import kr.co.vividnext.sodalive.event.EventService import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.rank.RankingService @@ -15,6 +17,7 @@ import java.time.temporal.TemporalAdjusters @Service class AudioContentMainTabLiveReplayService( + private val repository: AudioContentMainTabRepository, private val bannerService: AudioContentBannerService, private val contentRepository: AudioContentRepository, private val rankingService: RankingService, @@ -59,6 +62,32 @@ class AudioContentMainTabLiveReplayService( theme = theme ) + val creatorList = repository.findCreatorByThemeContent( + memberId = memberId, + theme = theme, + minCount = 4 + ) + + val salesRankContentList = if (creatorList.isNotEmpty()) { + rankingService.fetchCreatorContentBySalesTop2( + creatorId = creatorList[0].creatorId, + isAdult = isAdult, + theme = theme + ) + } else { + emptyList() + } + + val salesCountRankContentList = if (creatorList.isNotEmpty()) { + rankingService.fetchCreatorContentBySalesCountTop2( + creatorId = creatorList[0].creatorId, + isAdult = isAdult, + theme = theme + ) + } else { + emptyList() + } + val eventBannerList = eventService.getEventList(isAdult = isAdult) val curationList = curationRepository.findByContentMainTabId(tabId = tabId, isAdult = isAdult) @@ -77,8 +106,31 @@ class AudioContentMainTabLiveReplayService( contentBannerList = contentBannerList, newLiveReplayContentList = newLiveReplayContentList, rankLiveReplayContentList = rankLiveReplayContentList, + creatorList = creatorList, + salesRankContentList = salesRankContentList, + salesCountRankContentList = salesCountRankContentList, eventBannerList = eventBannerList, curationList = curationList ) } + + fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): GetPopularContentByCreatorResponse { + val theme = "다시듣기" + val salesRankContentList = rankingService.fetchCreatorContentBySalesTop2( + creatorId = creatorId, + isAdult = isAdult, + theme = theme + ) + + val salesCountRankContentList = rankingService.fetchCreatorContentBySalesCountTop2( + creatorId = creatorId, + isAdult = isAdult, + theme = theme + ) + + return GetPopularContentByCreatorResponse( + salesRankContentList = salesRankContentList, + salesCountRankContentList = salesCountRankContentList + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/GetContentMainTabLiveReplayResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/GetContentMainTabLiveReplayResponse.kt index eea8309..05860b2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/GetContentMainTabLiveReplayResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/GetContentMainTabLiveReplayResponse.kt @@ -1,5 +1,6 @@ package kr.co.vividnext.sodalive.content.main.tab.replay +import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse @@ -11,6 +12,9 @@ data class GetContentMainTabLiveReplayResponse( val contentBannerList: List, val newLiveReplayContentList: List, val rankLiveReplayContentList: List, + val creatorList: List, + val salesRankContentList: List, + val salesCountRankContentList: List, val eventBannerList: GetEventResponse, val curationList: List ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingRepository.kt index cf804a5..c0e9c65 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingRepository.kt @@ -381,7 +381,11 @@ class RankingRepository( .fetch() } - fun fetchCreatorContentBySalesTop2(creatorId: Long, isAdult: Boolean): List { + fun fetchCreatorContentBySalesTop2( + creatorId: Long, + isAdult: Boolean, + theme: String + ): List { var where = member.isActive.isTrue .and(member.role.eq(MemberRole.CREATOR)) .and(audioContent.isActive.isTrue) @@ -395,6 +399,10 @@ class RankingRepository( where = where.and(series.isAdult.isFalse) } + if (theme.isNotBlank()) { + where = where.and(audioContentTheme.theme.eq(theme)) + } + return queryFactory .select( QGetAudioContentRankingItem( @@ -421,7 +429,11 @@ class RankingRepository( .fetch() } - fun fetchCreatorContentBySalesCountTop2(creatorId: Long, isAdult: Boolean): List { + fun fetchCreatorContentBySalesCountTop2( + creatorId: Long, + isAdult: Boolean, + theme: String + ): List { var where = member.isActive.isTrue .and(member.role.eq(MemberRole.CREATOR)) .and(audioContent.isActive.isTrue) @@ -435,6 +447,10 @@ class RankingRepository( where = where.and(series.isAdult.isFalse) } + if (theme.isNotBlank()) { + where = where.and(audioContentTheme.theme.eq(theme)) + } + return queryFactory .select( QGetAudioContentRankingItem( diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingService.kt index f9aac15..5f657d0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingService.kt @@ -189,12 +189,20 @@ class RankingService( return repository.fetchCreatorByContentRevenueRankTop20(memberId, startDate, endDate) } - fun fetchCreatorContentBySalesTop2(creatorId: Long, isAdult: Boolean): List { - return repository.fetchCreatorContentBySalesTop2(creatorId, isAdult) + fun fetchCreatorContentBySalesTop2( + creatorId: Long, + isAdult: Boolean, + theme: String = "" + ): List { + return repository.fetchCreatorContentBySalesTop2(creatorId, isAdult, theme) } - fun fetchCreatorContentBySalesCountTop2(creatorId: Long, isAdult: Boolean): List { - return repository.fetchCreatorContentBySalesCountTop2(creatorId, isAdult) + fun fetchCreatorContentBySalesCountTop2( + creatorId: Long, + isAdult: Boolean, + theme: String = "" + ): List { + return repository.fetchCreatorContentBySalesCountTop2(creatorId, isAdult, theme) } fun fetchCreatorBySeriesRevenueRankTop20(