diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt index 5a9deda..286f232 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt @@ -148,4 +148,20 @@ class AudioContentController(private val service: AudioContentService) { ApiResponse.ok(service.audioContentLike(request, member)) } + + @GetMapping("/ranking") + fun getAudioContentRanking( + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, + pageable: Pageable + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok( + service.getAudioContentRanking( + member = member, + offset = pageable.offset, + limit = pageable.pageSize.toLong() + ) + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt index 6ed2e41..3bf9362 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt @@ -2,15 +2,19 @@ package kr.co.vividnext.sodalive.content import com.querydsl.core.types.dsl.Expressions import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.can.use.QUseCan.useCan import kr.co.vividnext.sodalive.content.QAudioContent.audioContent import kr.co.vividnext.sodalive.content.QBundleAudioContent.bundleAudioContent import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem +import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem import kr.co.vividnext.sodalive.content.main.GetNewContentUploadCreator import kr.co.vividnext.sodalive.content.main.QGetAudioContentMainItem +import kr.co.vividnext.sodalive.content.main.QGetAudioContentRankingItem import kr.co.vividnext.sodalive.content.main.banner.AudioContentBanner import kr.co.vividnext.sodalive.content.main.banner.QAudioContentBanner.audioContentBanner import kr.co.vividnext.sodalive.content.main.curation.AudioContentCuration import kr.co.vividnext.sodalive.content.main.curation.QAudioContentCuration.audioContentCuration +import kr.co.vividnext.sodalive.content.order.QOrder.order import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme import kr.co.vividnext.sodalive.event.QEvent.event import kr.co.vividnext.sodalive.member.QMember.member @@ -78,6 +82,15 @@ interface AudioContentQueryRepository { cloudfrontHost: String, isAdult: Boolean ): List + + fun getAudioContentRanking( + cloudfrontHost: String, + isAdult: Boolean, + startDate: LocalDateTime, + endDate: LocalDateTime, + offset: Long = 0, + limit: Long = 12 + ): List } @Repository @@ -424,4 +437,48 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) .orderBy(audioContent.id.desc()) .fetch() } + + override fun getAudioContentRanking( + cloudfrontHost: String, + isAdult: Boolean, + startDate: LocalDateTime, + endDate: LocalDateTime, + offset: Long, + limit: Long + ): List { + var where = audioContent.isActive.isTrue + .and(audioContent.member.isNotNull) + .and(audioContent.duration.isNotNull) + .and(audioContent.member.isActive.isTrue) + .and(useCan.createdAt.goe(startDate)) + .and(useCan.createdAt.lt(endDate)) + + if (!isAdult) { + where = where.and(audioContent.isAdult.isFalse) + } + + return queryFactory + .select( + QGetAudioContentRankingItem( + audioContent.id, + audioContent.title, + audioContent.coverImage.prepend("/").prepend(cloudfrontHost), + audioContentTheme.theme, + audioContent.price, + audioContent.duration, + member.id, + member.nickname + ) + ) + .from(useCan) + .innerJoin(useCan.order, order) + .innerJoin(order.audioContent, audioContent) + .innerJoin(audioContent.member, member) + .innerJoin(audioContent.theme, audioContentTheme) + .where(where) + .groupBy(audioContent.id) + .orderBy(order.can.sum().desc()) + .limit(limit) + .fetch() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt index fbeb3ee..40c2adb 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt @@ -13,6 +13,7 @@ import kr.co.vividnext.sodalive.content.like.AudioContentLike import kr.co.vividnext.sodalive.content.like.AudioContentLikeRepository import kr.co.vividnext.sodalive.content.like.PutAudioContentLikeRequest import kr.co.vividnext.sodalive.content.like.PutAudioContentLikeResponse +import kr.co.vividnext.sodalive.content.main.GetAudioContentRanking import kr.co.vividnext.sodalive.content.order.OrderRepository import kr.co.vividnext.sodalive.content.order.OrderType import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository @@ -29,9 +30,11 @@ import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import org.springframework.web.multipart.MultipartFile import java.text.SimpleDateFormat +import java.time.DayOfWeek import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter +import java.time.temporal.TemporalAdjusters import java.util.Locale @Service @@ -573,4 +576,39 @@ class AudioContentService( ) } } + + fun getAudioContentRanking( + member: Member, + offset: Long, + limit: Long + ): GetAudioContentRanking { + val currentDateTime = LocalDateTime.now() + val startDate = currentDateTime + .withHour(15) + .withMinute(0) + .withSecond(0) + .minusWeeks(1) + .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) + val endDate = startDate + .plusDays(7) + + val startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일") + val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일") + + val contentRankingItemList = repository + .getAudioContentRanking( + cloudfrontHost = coverImageHost, + startDate = startDate, + endDate = endDate, + isAdult = member.auth != null, + offset = offset, + limit = limit + ) + + return GetAudioContentRanking( + startDate = startDate.format(startDateFormatter), + endDate = endDate.minusDays(1).format(endDateFormatter), + items = contentRankingItemList + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt index 6e3a544..d54007c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt @@ -12,6 +12,10 @@ import kr.co.vividnext.sodalive.member.block.BlockMemberRepository import org.springframework.beans.factory.annotation.Value import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service +import java.time.DayOfWeek +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.time.temporal.TemporalAdjusters @Service class AudioContentMainService( @@ -126,13 +130,41 @@ class AudioContentMainService( .filter { it.contents.isNotEmpty() } .toList() + val currentDateTime = LocalDateTime.now() + val startDate = currentDateTime + .withHour(15) + .withMinute(0) + .withSecond(0) + .minusWeeks(1) + .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) + val endDate = startDate + .plusDays(7) + + val startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일") + val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일") + + val contentRankingItemList = repository + .getAudioContentRanking( + cloudfrontHost = imageHost, + startDate = startDate, + endDate = endDate, + isAdult = isAdult + ) + + val contentRanking = GetAudioContentRanking( + startDate = startDate.format(startDateFormatter), + endDate = endDate.minusDays(1).format(endDateFormatter), + contentRankingItemList + ) + return GetAudioContentMainResponse( newContentUploadCreatorList = newContentUploadCreatorList, bannerList = bannerList, orderList = orderList, themeList = themeList, newContentList = newContentList, - curationList = curationList + curationList = curationList, + contentRanking = contentRanking ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentMainResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentMainResponse.kt index 60b708a..9734f21 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentMainResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentMainResponse.kt @@ -9,5 +9,6 @@ data class GetAudioContentMainResponse( val orderList: List, val themeList: List, val newContentList: List, - val curationList: List + val curationList: List, + val contentRanking: GetAudioContentRanking ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentRanking.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentRanking.kt new file mode 100644 index 0000000..1d9dbdf --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentRanking.kt @@ -0,0 +1,20 @@ +package kr.co.vividnext.sodalive.content.main + +import com.querydsl.core.annotations.QueryProjection + +data class GetAudioContentRanking( + val startDate: String, + val endDate: String, + val items: List +) + +data class GetAudioContentRankingItem @QueryProjection constructor( + val contentId: Long, + val title: String, + val coverImageUrl: String, + val themeStr: String, + val price: Int, + val duration: String, + val creatorId: Long, + val creatorNickname: String +)