콘텐츠 메인
- 시리즈 탭 API
This commit is contained in:
		| @@ -6,6 +6,8 @@ import kr.co.vividnext.sodalive.content.QAudioContent.audioContent | ||||
| import kr.co.vividnext.sodalive.content.SortType | ||||
| import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | ||||
| import kr.co.vividnext.sodalive.content.main.QGetAudioContentMainItem | ||||
| import kr.co.vividnext.sodalive.content.main.curation.QAudioContentCuration.audioContentCuration | ||||
| import kr.co.vividnext.sodalive.content.main.tab.QAudioContentMainTab.audioContentMainTab | ||||
| import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme | ||||
| import kr.co.vividnext.sodalive.member.QMember.member | ||||
| import org.springframework.stereotype.Repository | ||||
| @@ -87,4 +89,20 @@ class AudioContentCurationQueryRepository(private val queryFactory: JPAQueryFact | ||||
|             .orderBy(orderBy) | ||||
|             .fetch() | ||||
|     } | ||||
|  | ||||
|     fun findByContentMainTabId(tabId: Long, isAdult: Boolean): List<AudioContentCuration> { | ||||
|         var where = audioContentCuration.isActive.isTrue | ||||
|             .and(audioContentMainTab.id.eq(tabId)) | ||||
|  | ||||
|         if (!isAdult) { | ||||
|             where = where.and(audioContentCuration.isAdult.isFalse) | ||||
|         } | ||||
|  | ||||
|         return queryFactory | ||||
|             .selectFrom(audioContentCuration) | ||||
|             .innerJoin(audioContentCuration.tab, audioContentMainTab) | ||||
|             .where(where) | ||||
|             .orderBy(audioContentCuration.orders.asc()) | ||||
|             .fetch() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,11 @@ | ||||
| package kr.co.vividnext.sodalive.content.main.tab | ||||
|  | ||||
| import com.querydsl.core.annotations.QueryProjection | ||||
|  | ||||
| data class GetRecommendSeriesListResponse @QueryProjection constructor( | ||||
|     val seriesId: Long, | ||||
|     val title: String, | ||||
|     val imageUrl: String, | ||||
|     val creatorId: Long, | ||||
|     val creatorNickname: String | ||||
| ) | ||||
| @@ -0,0 +1,43 @@ | ||||
| package kr.co.vividnext.sodalive.content.main.tab | ||||
|  | ||||
| import com.querydsl.jpa.impl.JPAQueryFactory | ||||
| import kr.co.vividnext.sodalive.content.main.tab.QRecommendSeries.recommendSeries | ||||
| import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series | ||||
| import kr.co.vividnext.sodalive.member.QMember.member | ||||
| import org.springframework.beans.factory.annotation.Value | ||||
| import org.springframework.stereotype.Repository | ||||
|  | ||||
| @Repository | ||||
| class RecommendSeriesRepository( | ||||
|     private val queryFactory: JPAQueryFactory, | ||||
|  | ||||
|     @Value("\${cloud.aws.cloud-front.host}") | ||||
|     private val imageHost: String | ||||
| ) { | ||||
|     fun getNewSeriesList(isAdult: Boolean): List<GetRecommendSeriesListResponse> { | ||||
|         var where = recommendSeries.isActive.isTrue | ||||
|             .and(recommendSeries.isFree.isFalse) | ||||
|             .and(series.isActive.isTrue) | ||||
|  | ||||
|         if (!isAdult) { | ||||
|             where = where.and(series.isAdult.isFalse) | ||||
|         } | ||||
|  | ||||
|         return queryFactory | ||||
|             .select( | ||||
|                 QGetRecommendSeriesListResponse( | ||||
|                     series.id, | ||||
|                     series.title, | ||||
|                     recommendSeries.imagePath.prepend("/").prepend(imageHost), | ||||
|                     member.id, | ||||
|                     member.nickname | ||||
|                 ) | ||||
|             ) | ||||
|             .from(recommendSeries) | ||||
|             .innerJoin(recommendSeries.series, series) | ||||
|             .innerJoin(series.member, member) | ||||
|             .where(where) | ||||
|             .orderBy(recommendSeries.orders.asc()) | ||||
|             .fetch() | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,22 @@ | ||||
| package kr.co.vividnext.sodalive.content.main.tab.series | ||||
|  | ||||
| import kr.co.vividnext.sodalive.common.ApiResponse | ||||
| import kr.co.vividnext.sodalive.common.SodaException | ||||
| 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.RestController | ||||
|  | ||||
| @RestController | ||||
| @RequestMapping("/v2/audio-content/main/series") | ||||
| class AudioContentMainTabSeriesController(private val service: AudioContentMainTabSeriesService) { | ||||
|     @GetMapping | ||||
|     fun fetchContentMainSeries( | ||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|  | ||||
|         ApiResponse.ok(service.fetchData(member)) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,149 @@ | ||||
| package kr.co.vividnext.sodalive.content.main.tab.series | ||||
|  | ||||
| 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.RecommendSeriesRepository | ||||
| import kr.co.vividnext.sodalive.content.series.ContentSeriesService | ||||
| import kr.co.vividnext.sodalive.event.EventService | ||||
| import kr.co.vividnext.sodalive.member.Member | ||||
| import kr.co.vividnext.sodalive.rank.RankingService | ||||
| import org.springframework.stereotype.Service | ||||
| import java.time.DayOfWeek | ||||
| import java.time.LocalDateTime | ||||
| import java.time.temporal.TemporalAdjusters | ||||
|  | ||||
| @Service | ||||
| class AudioContentMainTabSeriesService( | ||||
|     private val bannerService: AudioContentBannerService, | ||||
|     private val seriesService: ContentSeriesService, | ||||
|     private val rankingService: RankingService, | ||||
|     private val recommendSeriesRepository: RecommendSeriesRepository, | ||||
|     private val eventService: EventService, | ||||
|     private val curationRepository: AudioContentCurationQueryRepository | ||||
| ) { | ||||
|     fun fetchData(member: Member): GetContentMainTabSeriesResponse { | ||||
|         val isAdult = member.auth != null | ||||
|  | ||||
|         // 메인 배너 (시리즈) | ||||
|         val contentBannerList = bannerService.getBannerList( | ||||
|             tabId = 2, | ||||
|             memberId = member.id!!, | ||||
|             isAdult = isAdult | ||||
|         ) | ||||
|  | ||||
|         val originalAudioDrama = seriesService.getOriginalAudioDramaList( | ||||
|             isAdult = isAdult, | ||||
|             offset = 0, | ||||
|             limit = 20 | ||||
|         ) | ||||
|  | ||||
|         // 일간 랭킹 | ||||
|         val currentDateTime = LocalDateTime.now() | ||||
|         val dailyRankingStartDate = currentDateTime | ||||
|             .withHour(15) | ||||
|             .withMinute(0) | ||||
|             .withSecond(0) | ||||
|             .minusDays(2) | ||||
|         val dailyRankingEndDate = dailyRankingStartDate | ||||
|             .plusDays(1) | ||||
|  | ||||
|         val rankSeriesList = rankingService.getSeriesRanking( | ||||
|             memberId = member.id!!, | ||||
|             isAdult = isAdult, | ||||
|             startDate = dailyRankingStartDate, | ||||
|             endDate = dailyRankingEndDate | ||||
|         ) | ||||
|  | ||||
|         // 시리즈 장르 | ||||
|         val genreList = seriesService.getGenreList(isAdult = isAdult) | ||||
|  | ||||
|         // 장르별 추천 시리즈 | ||||
|         val recommendSeriesList = if (genreList.isNotEmpty()) { | ||||
|             rankingService.getSeriesAllRankingByGenre( | ||||
|                 memberId = member.id!!, | ||||
|                 isAdult = isAdult, | ||||
|                 genreId = genreList[0].id | ||||
|             ) | ||||
|         } else { | ||||
|             emptyList() | ||||
|         } | ||||
|  | ||||
|         // 새로운 시리즈 | ||||
|         val newSeriesList = recommendSeriesRepository.getNewSeriesList(isAdult = isAdult) | ||||
|  | ||||
|         // 완결 시리즈 월간 랭킹 | ||||
|         val monthlyRankingStartDate = currentDateTime | ||||
|             .withDayOfMonth(1) | ||||
|             .withHour(15) | ||||
|             .withMinute(0) | ||||
|             .withSecond(0) | ||||
|             .minusDays(1) | ||||
|         val monthlyRankingEndDate = monthlyRankingStartDate | ||||
|             .plusMonths(1) | ||||
|  | ||||
|         val rankCompleteSeriesList = rankingService.getCompleteSeriesRanking( | ||||
|             memberId = member.id!!, | ||||
|             isAdult = isAdult, | ||||
|             startDate = monthlyRankingStartDate, | ||||
|             endDate = monthlyRankingEndDate | ||||
|         ) | ||||
|  | ||||
|         val startDate = currentDateTime | ||||
|             .withHour(15) | ||||
|             .withMinute(0) | ||||
|             .withSecond(0) | ||||
|             .minusWeeks(1) | ||||
|             .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) | ||||
|         val endDate = startDate | ||||
|             .plusDays(6) | ||||
|  | ||||
|         val seriesRankCreatorList = rankingService.fetchCreatorBySeriesRevenueRankTop20( | ||||
|             memberId = member.id!!, | ||||
|             startDate = startDate.minusDays(1), | ||||
|             endDate = endDate | ||||
|         ) | ||||
|  | ||||
|         val salesRankContentList = if (seriesRankCreatorList.isNotEmpty()) { | ||||
|             rankingService.fetchCreatorSeriesBySales( | ||||
|                 creatorId = seriesRankCreatorList[0].creatorId, | ||||
|                 isAdult = isAdult | ||||
|             ) | ||||
|         } else { | ||||
|             emptyList() | ||||
|         } | ||||
|  | ||||
|         // 이벤트 배너 | ||||
|         val eventBannerList = eventService.getEventList(isAdult = isAdult) | ||||
|  | ||||
|         // 큐레이션 | ||||
|         val curationList = curationRepository.findByContentMainTabId(tabId = 2, isAdult = isAdult) | ||||
|             .map { | ||||
|                 GetSeriesCurationResponse( | ||||
|                     title = it.title, | ||||
|                     items = seriesService.fetchSeriesByCurationId( | ||||
|                         curationId = it.id!!, | ||||
|                         memberId = member.id!!, | ||||
|                         isAdult = isAdult | ||||
|                     ) | ||||
|                 ) | ||||
|             } | ||||
|  | ||||
|         return GetContentMainTabSeriesResponse( | ||||
|             contentBannerList = contentBannerList, | ||||
|             originalAudioDrama = if (originalAudioDrama.size >= 3) { | ||||
|                 originalAudioDrama | ||||
|             } else { | ||||
|                 emptyList() | ||||
|             }, | ||||
|             rankSeriesList = rankSeriesList, | ||||
|             genreList = genreList, | ||||
|             recommendSeriesList = recommendSeriesList, | ||||
|             newSeriesList = newSeriesList, | ||||
|             rankCompleteSeriesList = rankCompleteSeriesList, | ||||
|             seriesRankCreatorList = seriesRankCreatorList, | ||||
|             salesRankContentList = salesRankContentList, | ||||
|             eventBannerList = eventBannerList, | ||||
|             curationList = curationList | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,23 @@ | ||||
| package kr.co.vividnext.sodalive.content.main.tab.series | ||||
|  | ||||
| import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse | ||||
| import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse | ||||
| import kr.co.vividnext.sodalive.content.main.tab.GetRecommendSeriesListResponse | ||||
| import kr.co.vividnext.sodalive.content.series.GetSeriesGenreListResponse | ||||
| import kr.co.vividnext.sodalive.content.series.GetSeriesListResponse | ||||
| import kr.co.vividnext.sodalive.event.GetEventResponse | ||||
|  | ||||
| data class GetContentMainTabSeriesResponse( | ||||
|     val tabId: Long = 2, | ||||
|     val contentBannerList: List<GetAudioContentBannerResponse>, | ||||
|     val originalAudioDrama: List<GetSeriesListResponse.SeriesListItem>, | ||||
|     val rankSeriesList: List<GetSeriesListResponse.SeriesListItem>, | ||||
|     val genreList: List<GetSeriesGenreListResponse>, | ||||
|     val recommendSeriesList: List<GetSeriesListResponse.SeriesListItem>, | ||||
|     val newSeriesList: List<GetRecommendSeriesListResponse>, | ||||
|     val rankCompleteSeriesList: List<GetSeriesListResponse.SeriesListItem>, | ||||
|     val seriesRankCreatorList: List<ContentCreatorResponse>, | ||||
|     val salesRankContentList: List<GetSeriesListResponse.SeriesListItem>, | ||||
|     val eventBannerList: GetEventResponse, | ||||
|     val curationList: List<GetSeriesCurationResponse> | ||||
| ) | ||||
| @@ -0,0 +1,8 @@ | ||||
| package kr.co.vividnext.sodalive.content.main.tab.series | ||||
|  | ||||
| import kr.co.vividnext.sodalive.content.series.GetSeriesListResponse | ||||
|  | ||||
| data class GetSeriesCurationResponse( | ||||
|     val title: String, | ||||
|     val items: List<GetSeriesListResponse.SeriesListItem> | ||||
| ) | ||||
| @@ -2,15 +2,21 @@ package kr.co.vividnext.sodalive.content.series | ||||
|  | ||||
| import com.querydsl.core.types.dsl.Expressions | ||||
| import com.querydsl.jpa.impl.JPAQueryFactory | ||||
| import kr.co.vividnext.sodalive.admin.content.series.genre.QSeriesGenre.seriesGenre | ||||
| import kr.co.vividnext.sodalive.content.ContentType | ||||
| import kr.co.vividnext.sodalive.content.QAudioContent.audioContent | ||||
| import kr.co.vividnext.sodalive.content.hashtag.QHashTag.hashTag | ||||
| import kr.co.vividnext.sodalive.content.main.curation.QAudioContentCuration.audioContentCuration | ||||
| import kr.co.vividnext.sodalive.content.main.curation.QAudioContentCurationItem.audioContentCurationItem | ||||
| import kr.co.vividnext.sodalive.content.series.content.GetSeriesContentMinMaxPriceResponse | ||||
| import kr.co.vividnext.sodalive.content.series.content.QGetSeriesContentMinMaxPriceResponse | ||||
| import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series | ||||
| import kr.co.vividnext.sodalive.creator.admin.content.series.QSeriesContent.seriesContent | ||||
| import kr.co.vividnext.sodalive.creator.admin.content.series.Series | ||||
| import kr.co.vividnext.sodalive.creator.admin.content.series.keyword.QSeriesKeyword.seriesKeyword | ||||
| 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.data.jpa.repository.JpaRepository | ||||
|  | ||||
| interface ContentSeriesRepository : JpaRepository<Series, Long>, ContentSeriesQueryRepository | ||||
| @@ -29,6 +35,9 @@ interface ContentSeriesQueryRepository { | ||||
|     fun getKeywordList(seriesId: Long): List<String> | ||||
|     fun getSeriesContentMinMaxPrice(seriesId: Long): GetSeriesContentMinMaxPriceResponse | ||||
|     fun getRecommendSeriesList(isAuth: Boolean, contentType: ContentType, limit: Long): List<Series> | ||||
|     fun getOriginalAudioDramaList(isAdult: Boolean, offset: Long = 0, limit: Long = 20): List<Series> | ||||
|     fun getGenreList(isAdult: Boolean): List<GetSeriesGenreListResponse> | ||||
|     fun findByCurationId(curationId: Long, memberId: Long, isAdult: Boolean): List<Series> | ||||
| } | ||||
|  | ||||
| class ContentSeriesQueryRepositoryImpl( | ||||
| @@ -133,4 +142,61 @@ class ContentSeriesQueryRepositoryImpl( | ||||
|             .limit(limit) | ||||
|             .fetch() | ||||
|     } | ||||
|  | ||||
|     override fun getOriginalAudioDramaList(isAdult: Boolean, offset: Long, limit: Long): List<Series> { | ||||
|         var where = series.isOriginal.isTrue | ||||
|             .and(series.isActive.isTrue) | ||||
|  | ||||
|         if (!isAdult) { | ||||
|             where = where.and(series.isAdult.isFalse) | ||||
|         } | ||||
|  | ||||
|         return queryFactory | ||||
|             .selectFrom(series) | ||||
|             .where(where) | ||||
|             .orderBy(Expressions.numberTemplate(Double::class.java, "function('rand')").asc()) | ||||
|             .offset(offset) | ||||
|             .limit(limit) | ||||
|             .fetch() | ||||
|     } | ||||
|  | ||||
|     override fun getGenreList(isAdult: Boolean): List<GetSeriesGenreListResponse> { | ||||
|         var where = seriesGenre.isActive.isTrue | ||||
|  | ||||
|         if (!isAdult) { | ||||
|             where = where.and(seriesGenre.isAdult.isFalse) | ||||
|         } | ||||
|  | ||||
|         return queryFactory | ||||
|             .select(QGetSeriesGenreListResponse(seriesGenre.id, seriesGenre.genre)) | ||||
|             .from(seriesGenre) | ||||
|             .where(where) | ||||
|             .orderBy(seriesGenre.orders.asc()) | ||||
|             .fetch() | ||||
|     } | ||||
|  | ||||
|     override fun findByCurationId(curationId: Long, memberId: Long, isAdult: Boolean): List<Series> { | ||||
|         val blockMemberCondition = blockMember.member.id.eq(member.id) | ||||
|             .and(blockMember.isActive.isTrue) | ||||
|             .and(blockMember.blockedMember.id.eq(memberId)) | ||||
|  | ||||
|         var where = series.isActive.isTrue | ||||
|             .and(member.isActive.isTrue) | ||||
|             .and(member.role.eq(MemberRole.CREATOR)) | ||||
|             .and(blockMember.id.isNull) | ||||
|  | ||||
|         if (!isAdult) { | ||||
|             where = where.and(series.isAdult.isFalse) | ||||
|         } | ||||
|  | ||||
|         return queryFactory | ||||
|             .select(series) | ||||
|             .from(audioContentCurationItem) | ||||
|             .innerJoin(audioContentCurationItem.curation, audioContentCuration) | ||||
|             .innerJoin(audioContentCurationItem.series, series) | ||||
|             .innerJoin(series.member, member) | ||||
|             .leftJoin(blockMember).on(blockMemberCondition) | ||||
|             .where(where) | ||||
|             .fetch() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,19 @@ class ContentSeriesService( | ||||
|     @Value("\${cloud.aws.cloud-front.host}") | ||||
|     private val coverImageHost: String | ||||
| ) { | ||||
|     fun getOriginalAudioDramaList( | ||||
|         isAdult: Boolean, | ||||
|         offset: Long = 0, | ||||
|         limit: Long = 20 | ||||
|     ): List<GetSeriesListResponse.SeriesListItem> { | ||||
|         val originalAudioDramaList = repository.getOriginalAudioDramaList(isAdult, offset, limit) | ||||
|         return seriesToSeriesListItem(originalAudioDramaList, isAdult) | ||||
|     } | ||||
|  | ||||
|     fun getGenreList(isAdult: Boolean): List<GetSeriesGenreListResponse> { | ||||
|         return repository.getGenreList(isAdult = isAdult) | ||||
|     } | ||||
|  | ||||
|     fun getSeriesList( | ||||
|         creatorId: Long, | ||||
|         member: Member, | ||||
| @@ -170,6 +183,15 @@ class ContentSeriesService( | ||||
|         return seriesToSeriesListItem(seriesList = seriesList, isAdult = member.auth != null) | ||||
|     } | ||||
|  | ||||
|     fun fetchSeriesByCurationId( | ||||
|         curationId: Long, | ||||
|         memberId: Long, | ||||
|         isAdult: Boolean | ||||
|     ): List<GetSeriesListResponse.SeriesListItem> { | ||||
|         val seriesList = repository.findByCurationId(curationId = curationId, memberId = memberId, isAdult = isAdult) | ||||
|         return seriesToSeriesListItem(seriesList, isAdult) | ||||
|     } | ||||
|  | ||||
|     private fun seriesToSeriesListItem( | ||||
|         seriesList: List<Series>, | ||||
|         isAdult: Boolean | ||||
|   | ||||
| @@ -0,0 +1,8 @@ | ||||
| package kr.co.vividnext.sodalive.content.series | ||||
|  | ||||
| import com.querydsl.core.annotations.QueryProjection | ||||
|  | ||||
| data class GetSeriesGenreListResponse @QueryProjection constructor( | ||||
|     val id: Long, | ||||
|     val genre: String | ||||
| ) | ||||
| @@ -1,6 +1,8 @@ | ||||
| package kr.co.vividnext.sodalive.rank | ||||
|  | ||||
| import com.querydsl.core.types.dsl.Expressions | ||||
| import com.querydsl.jpa.impl.JPAQueryFactory | ||||
| import kr.co.vividnext.sodalive.admin.content.series.genre.QSeriesGenre.seriesGenre | ||||
| import kr.co.vividnext.sodalive.content.QAudioContent.audioContent | ||||
| import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment | ||||
| import kr.co.vividnext.sodalive.content.like.QAudioContentLike.audioContentLike | ||||
| @@ -13,6 +15,7 @@ import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentThe | ||||
| import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series | ||||
| import kr.co.vividnext.sodalive.creator.admin.content.series.QSeriesContent.seriesContent | ||||
| import kr.co.vividnext.sodalive.creator.admin.content.series.Series | ||||
| import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesState | ||||
| import kr.co.vividnext.sodalive.explorer.QCreatorRanking.creatorRanking | ||||
| import kr.co.vividnext.sodalive.member.Member | ||||
| import kr.co.vividnext.sodalive.member.MemberRole | ||||
| @@ -187,16 +190,107 @@ class RankingRepository( | ||||
|             .innerJoin(seriesContent.series, series) | ||||
|             .innerJoin(seriesContent.content, audioContent) | ||||
|             .innerJoin(series.member, member) | ||||
|             .innerJoin(order).on(audioContent.id.eq(order.audioContent.id)) | ||||
|             .leftJoin(order).on(audioContent.id.eq(order.audioContent.id)) | ||||
|             .leftJoin(blockMember).on(blockMemberCondition) | ||||
|             .where(where) | ||||
|             .groupBy(series.id) | ||||
|             .orderBy(order.can.sum().desc(), series.createdAt.asc()) | ||||
|             .orderBy( | ||||
|                 order.can.sum().desc(), | ||||
|                 Expressions.numberTemplate(Double::class.java, "function('rand')").asc() | ||||
|             ) | ||||
|             .offset(0) | ||||
|             .limit(10) | ||||
|             .fetch() | ||||
|     } | ||||
|  | ||||
|     fun getCompleteSeriesRanking( | ||||
|         memberId: Long, | ||||
|         isAdult: Boolean, | ||||
|         startDate: LocalDateTime, | ||||
|         endDate: LocalDateTime, | ||||
|         offset: Long, | ||||
|         limit: Long | ||||
|     ): List<Series> { | ||||
|         val blockMemberCondition = blockMember.member.id.eq(member.id) | ||||
|             .and(blockMember.isActive.isTrue) | ||||
|             .and(blockMember.blockedMember.id.eq(memberId)) | ||||
|  | ||||
|         var where = series.isActive.isTrue | ||||
|             .and(series.state.eq(SeriesState.COMPLETE)) | ||||
|             .and(audioContent.isActive.isTrue) | ||||
|             .and(member.isActive.isTrue) | ||||
|             .and(member.isNotNull) | ||||
|             .and(member.role.eq(MemberRole.CREATOR)) | ||||
|             .and(audioContent.duration.isNotNull) | ||||
|             .and(audioContent.limited.isNull) | ||||
|             .and(blockMember.id.isNull) | ||||
|             .and(order.isActive.isTrue) | ||||
|             .and(order.createdAt.goe(startDate)) | ||||
|             .and(order.createdAt.lt(endDate)) | ||||
|  | ||||
|         if (!isAdult) { | ||||
|             where = where.and(series.isAdult.isFalse) | ||||
|         } | ||||
|  | ||||
|         return queryFactory | ||||
|             .select(series) | ||||
|             .from(seriesContent) | ||||
|             .innerJoin(seriesContent.series, series) | ||||
|             .innerJoin(seriesContent.content, audioContent) | ||||
|             .innerJoin(series.member, member) | ||||
|             .leftJoin(order).on(audioContent.id.eq(order.audioContent.id)) | ||||
|             .leftJoin(blockMember).on(blockMemberCondition) | ||||
|             .where(where) | ||||
|             .groupBy(series.id) | ||||
|             .orderBy( | ||||
|                 order.can.sum().desc(), | ||||
|                 Expressions.numberTemplate(Double::class.java, "function('rand')").asc() | ||||
|             ) | ||||
|             .offset(offset) | ||||
|             .limit(limit) | ||||
|             .fetch() | ||||
|     } | ||||
|  | ||||
|     fun getSeriesAllRankingByGenre(memberId: Long, isAdult: Boolean, genreId: Long): List<Series> { | ||||
|         val blockMemberCondition = blockMember.member.id.eq(member.id) | ||||
|             .and(blockMember.isActive.isTrue) | ||||
|             .and(blockMember.blockedMember.id.eq(memberId)) | ||||
|  | ||||
|         var where = series.isActive.isTrue | ||||
|             .and(seriesGenre.id.eq(genreId)) | ||||
|             .and(audioContent.isActive.isTrue) | ||||
|             .and(member.isActive.isTrue) | ||||
|             .and(member.isNotNull) | ||||
|             .and(member.role.eq(MemberRole.CREATOR)) | ||||
|             .and(audioContent.duration.isNotNull) | ||||
|             .and(audioContent.limited.isNull) | ||||
|             .and(blockMember.id.isNull) | ||||
|             .and(order.isActive.isTrue) | ||||
|  | ||||
|         if (!isAdult) { | ||||
|             where = where.and(series.isAdult.isFalse) | ||||
|         } | ||||
|  | ||||
|         return queryFactory | ||||
|             .select(series) | ||||
|             .from(seriesContent) | ||||
|             .innerJoin(seriesContent.series, series) | ||||
|             .innerJoin(series.genre, seriesGenre) | ||||
|             .innerJoin(seriesContent.content, audioContent) | ||||
|             .innerJoin(series.member, member) | ||||
|             .leftJoin(order).on(audioContent.id.eq(order.audioContent.id)) | ||||
|             .leftJoin(blockMember).on(blockMemberCondition) | ||||
|             .where(where) | ||||
|             .groupBy(series.id) | ||||
|             .orderBy( | ||||
|                 order.can.sum().desc(), | ||||
|                 Expressions.numberTemplate(Double::class.java, "function('rand')").asc() | ||||
|             ) | ||||
|             .offset(0) | ||||
|             .limit(20) | ||||
|             .fetch() | ||||
|     } | ||||
|  | ||||
|     fun fetchCreatorByContentRevenueRankTop20( | ||||
|         memberId: Long, | ||||
|         startDate: LocalDateTime, | ||||
| @@ -232,11 +326,11 @@ class RankingRepository( | ||||
|             .leftJoin(blockMember).on(blockMemberCondition) | ||||
|             .where(where) | ||||
|             .groupBy(member.id) | ||||
|             .having( | ||||
|                 audioContent.id.count().goe(4) | ||||
|                     .and(order.can.sum().gt(0)) | ||||
|             .having(audioContent.id.count().goe(4)) | ||||
|             .orderBy( | ||||
|                 order.can.sum().desc(), | ||||
|                 Expressions.numberTemplate(Double::class.java, "function('rand')").asc() | ||||
|             ) | ||||
|             .orderBy(order.can.sum().desc()) | ||||
|             .offset(0) | ||||
|             .limit(20) | ||||
|             .fetch() | ||||
| @@ -315,4 +409,84 @@ class RankingRepository( | ||||
|             .limit(2) | ||||
|             .fetch() | ||||
|     } | ||||
|  | ||||
|     fun fetchCreatorBySeriesRevenueRankTop20( | ||||
|         memberId: Long, | ||||
|         startDate: LocalDateTime, | ||||
|         endDate: LocalDateTime | ||||
|     ): List<ContentCreatorResponse> { | ||||
|         val blockMemberCondition = blockMember.member.id.eq(member.id) | ||||
|             .and(blockMember.isActive.isTrue) | ||||
|             .and(blockMember.blockedMember.id.eq(memberId)) | ||||
|  | ||||
|         val ordersCondition = order.audioContent.id.eq(audioContent.id) | ||||
|             .and(order.isActive.isTrue) | ||||
|             .and(order.createdAt.goe(startDate)) | ||||
|             .and(order.createdAt.lt(startDate)) | ||||
|  | ||||
|         val where = member.isActive.isTrue | ||||
|             .and(member.role.eq(MemberRole.CREATOR)) | ||||
|             .and(series.isActive.isTrue) | ||||
|             .and(audioContent.isActive.isTrue) | ||||
|             .and(audioContent.duration.isNotNull) | ||||
|             .and(audioContent.limited.isNull) | ||||
|             .and(blockMember.id.isNull) | ||||
|  | ||||
|         return queryFactory | ||||
|             .select( | ||||
|                 QContentCreatorResponse( | ||||
|                     member.id, | ||||
|                     member.nickname, | ||||
|                     member.profileImage.prepend("/").prepend(imageHost) | ||||
|                 ) | ||||
|             ) | ||||
|             .from(seriesContent) | ||||
|             .innerJoin(seriesContent.series, series) | ||||
|             .innerJoin(seriesContent.content, audioContent) | ||||
|             .innerJoin(series.member, member) | ||||
|             .leftJoin(order).on(ordersCondition) | ||||
|             .leftJoin(blockMember).on(blockMemberCondition) | ||||
|             .where(where) | ||||
|             .groupBy(member.id) | ||||
|             .having(series.id.countDistinct().goe(3)) | ||||
|             .orderBy( | ||||
|                 order.can.sum().desc(), | ||||
|                 Expressions.numberTemplate(Double::class.java, "function('rand')").asc() | ||||
|             ) | ||||
|             .offset(0) | ||||
|             .limit(20) | ||||
|             .fetch() | ||||
|     } | ||||
|  | ||||
|     fun fetchCreatorSeriesBySales(creatorId: Long, isAdult: Boolean): List<Series> { | ||||
|         var where = member.isActive.isTrue | ||||
|             .and(member.role.eq(MemberRole.CREATOR)) | ||||
|             .and(series.isActive.isTrue) | ||||
|             .and(audioContent.isActive.isTrue) | ||||
|             .and(audioContent.duration.isNotNull) | ||||
|             .and(audioContent.limited.isNull) | ||||
|             .and(order.isActive.isTrue) | ||||
|             .and(member.id.eq(creatorId)) | ||||
|  | ||||
|         if (!isAdult) { | ||||
|             where = where.and(series.isAdult.isFalse) | ||||
|         } | ||||
|  | ||||
|         return queryFactory | ||||
|             .select(series) | ||||
|             .from(seriesContent) | ||||
|             .innerJoin(seriesContent.series, series) | ||||
|             .innerJoin(seriesContent.content, audioContent) | ||||
|             .innerJoin(series.member, member) | ||||
|             .leftJoin(order).on(audioContent.id.eq(order.audioContent.id)) | ||||
|             .where(where) | ||||
|             .groupBy(series.id) | ||||
|             .orderBy( | ||||
|                 order.can.sum().desc(), | ||||
|                 Expressions.numberTemplate(Double::class.java, "function('rand')").asc() | ||||
|             ) | ||||
|             .offset(0) | ||||
|             .limit(10) | ||||
|             .fetch() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -67,6 +67,38 @@ class RankingService( | ||||
|         return seriesToSeriesListItem(seriesList = seriesList, isAdult = isAdult) | ||||
|     } | ||||
|  | ||||
|     fun getSeriesAllRankingByGenre( | ||||
|         memberId: Long, | ||||
|         isAdult: Boolean, | ||||
|         genreId: Long | ||||
|     ): List<GetSeriesListResponse.SeriesListItem> { | ||||
|         val seriesList = repository.getSeriesAllRankingByGenre( | ||||
|             memberId = memberId, | ||||
|             isAdult = isAdult, | ||||
|             genreId = genreId | ||||
|         ) | ||||
|         return seriesToSeriesListItem(seriesList = seriesList, isAdult = isAdult) | ||||
|     } | ||||
|  | ||||
|     fun getCompleteSeriesRanking( | ||||
|         memberId: Long, | ||||
|         isAdult: Boolean, | ||||
|         startDate: LocalDateTime, | ||||
|         endDate: LocalDateTime, | ||||
|         offset: Long = 0, | ||||
|         limit: Long = 10 | ||||
|     ): List<GetSeriesListResponse.SeriesListItem> { | ||||
|         val seriesList = repository.getCompleteSeriesRanking( | ||||
|             memberId = memberId, | ||||
|             isAdult = isAdult, | ||||
|             startDate = startDate, | ||||
|             endDate = endDate, | ||||
|             offset = offset, | ||||
|             limit = limit | ||||
|         ) | ||||
|         return seriesToSeriesListItem(seriesList = seriesList, isAdult = isAdult) | ||||
|     } | ||||
|  | ||||
|     private fun seriesToSeriesListItem( | ||||
|         seriesList: List<Series>, | ||||
|         isAdult: Boolean | ||||
| @@ -148,4 +180,17 @@ class RankingService( | ||||
|     fun fetchCreatorContentBySalesCountTop2(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> { | ||||
|         return repository.fetchCreatorContentBySalesCountTop2(creatorId, isAdult) | ||||
|     } | ||||
|  | ||||
|     fun fetchCreatorBySeriesRevenueRankTop20( | ||||
|         memberId: Long, | ||||
|         startDate: LocalDateTime, | ||||
|         endDate: LocalDateTime | ||||
|     ): List<ContentCreatorResponse> { | ||||
|         return repository.fetchCreatorBySeriesRevenueRankTop20(memberId, startDate, endDate) | ||||
|     } | ||||
|  | ||||
|     fun fetchCreatorSeriesBySales(creatorId: Long, isAdult: Boolean): List<GetSeriesListResponse.SeriesListItem> { | ||||
|         val seriesList = repository.fetchCreatorSeriesBySales(creatorId = creatorId, isAdult = isAdult) | ||||
|         return seriesToSeriesListItem(seriesList, isAdult) | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user