From d45a34525b8700287cd450eea5c03b1b1ac15fe5 Mon Sep 17 00:00:00 2001
From: Klaus <klaus@vividnext.co.kr>
Date: Thu, 2 Nov 2023 02:28:23 +0900
Subject: [PATCH] =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=EB=A9=94?=
 =?UTF-8?q?=EC=9D=B8=20-=20=EC=88=9C=EC=9C=84=20=EC=A0=95=EB=A0=AC(?=
 =?UTF-8?q?=EB=A7=A4=EC=B6=9C,=20=ED=9B=84=EC=9B=90,=20=EB=8C=93=EA=B8=80,?=
 =?UTF-8?q?=20=EC=A2=8B=EC=95=84=EC=9A=94)=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../content/AudioContentController.kt         | 21 ++++-
 .../content/AudioContentRepository.kt         | 87 ++++++++++++++++---
 .../sodalive/content/AudioContentService.kt   | 41 ++++-----
 .../sodalive/content/like/AudioContentLike.kt | 13 ++-
 .../like/AudioContentLikeRepository.kt        |  4 +-
 .../content/main/AudioContentMainService.kt   | 36 +++-----
 .../main/GetAudioContentMainResponse.kt       |  1 +
 7 files changed, 137 insertions(+), 66 deletions(-)

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 286f232..bddedcd 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt
@@ -19,6 +19,9 @@ import org.springframework.web.bind.annotation.RequestParam
 import org.springframework.web.bind.annotation.RequestPart
 import org.springframework.web.bind.annotation.RestController
 import org.springframework.web.multipart.MultipartFile
+import java.time.DayOfWeek
+import java.time.LocalDateTime
+import java.time.temporal.TemporalAdjusters
 
 @RestController
 @RequestMapping("/audio-content")
@@ -152,15 +155,29 @@ class AudioContentController(private val service: AudioContentService) {
     @GetMapping("/ranking")
     fun getAudioContentRanking(
         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
+        @RequestParam("sortType") sortType: String,
         pageable: Pageable
     ) = run {
         if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
 
+        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)
+
         ApiResponse.ok(
             service.getAudioContentRanking(
-                member = member,
+                isAdult = member.auth != null,
+                startDate = startDate,
+                endDate = endDate,
                 offset = pageable.offset,
-                limit = pageable.pageSize.toLong()
+                limit = pageable.pageSize.toLong(),
+                sortType = sortType
             )
         )
     }
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 e7bfebe..ee349c7 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt
@@ -2,8 +2,11 @@ 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.comment.QAudioContentComment.audioContentComment
+import kr.co.vividnext.sodalive.content.like.QAudioContentLike.audioContentLike
 import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
 import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
 import kr.co.vividnext.sodalive.content.main.GetNewContentUploadCreator
@@ -88,7 +91,8 @@ interface AudioContentQueryRepository {
         startDate: LocalDateTime,
         endDate: LocalDateTime,
         offset: Long = 0,
-        limit: Long = 12
+        limit: Long = 12,
+        sortType: String = "매출"
     ): List<GetAudioContentRankingItem>
 }
 
@@ -443,11 +447,11 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
         startDate: LocalDateTime,
         endDate: LocalDateTime,
         offset: Long,
-        limit: Long
+        limit: Long,
+        sortType: String
     ): List<GetAudioContentRankingItem> {
-        var where = order.createdAt.goe(startDate)
-            .and(order.createdAt.lt(endDate))
-            .and(audioContent.isActive.isTrue)
+        var where = audioContent.isActive.isTrue
+            .and(audioContent.member.id.ne(648))
             .and(audioContent.member.isNotNull)
             .and(audioContent.duration.isNotNull)
             .and(audioContent.member.isActive.isTrue)
@@ -457,7 +461,7 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
             where = where.and(audioContent.isAdult.isFalse)
         }
 
-        return queryFactory
+        var select = queryFactory
             .select(
                 QGetAudioContentRankingItem(
                     audioContent.id,
@@ -470,13 +474,70 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
                     member.nickname
                 )
             )
-            .from(order)
-            .innerJoin(order.audioContent, audioContent)
-            .innerJoin(audioContent.member, member)
-            .innerJoin(audioContent.theme, audioContentTheme)
-            .where(where)
-            .groupBy(audioContent.id)
-            .orderBy(order.can.sum().desc(), audioContent.createdAt.asc())
+
+        select = when (sortType) {
+            "후원" -> {
+                select
+                    .from(useCan)
+                    .innerJoin(useCan.audioContent, audioContent)
+                    .innerJoin(audioContent.member, member)
+                    .innerJoin(audioContent.theme, audioContentTheme)
+                    .where(
+                        where
+                            .and(audioContentComment.createdAt.goe(startDate))
+                            .and(audioContentComment.createdAt.lt(endDate))
+                    )
+                    .groupBy(audioContent.id)
+                    .orderBy(useCan.can.add(useCan.rewardCan).sum().desc(), audioContent.createdAt.asc())
+            }
+
+            "댓글" -> {
+                select
+                    .from(audioContentComment)
+                    .innerJoin(audioContentComment.audioContent, audioContent)
+                    .innerJoin(audioContentComment.audioContent.member, member)
+                    .innerJoin(audioContentComment.audioContent.theme, audioContentTheme)
+                    .where(
+                        where
+                            .and(audioContentComment.createdAt.goe(startDate))
+                            .and(audioContentComment.createdAt.lt(endDate))
+                    )
+                    .groupBy(audioContentComment.audioContent.id)
+                    .orderBy(audioContentComment.id.count().desc(), audioContent.createdAt.asc())
+            }
+
+            "좋아요" -> {
+                select
+                    .from(audioContentLike)
+                    .innerJoin(audioContentLike.audioContent, audioContent)
+                    .innerJoin(audioContentLike.audioContent.member, member)
+                    .innerJoin(audioContentLike.audioContent.theme, audioContentTheme)
+                    .where(
+                        where
+                            .and(audioContentLike.createdAt.goe(startDate))
+                            .and(audioContentLike.createdAt.lt(endDate))
+                    )
+                    .groupBy(audioContentLike.audioContent.id)
+                    .orderBy(audioContentLike.id.count().desc(), audioContent.createdAt.asc())
+            }
+
+            else -> {
+                select
+                    .from(order)
+                    .innerJoin(order.audioContent, audioContent)
+                    .innerJoin(audioContent.member, member)
+                    .innerJoin(audioContent.theme, audioContentTheme)
+                    .where(
+                        where
+                            .and(order.createdAt.goe(startDate))
+                            .and(order.createdAt.lt(endDate))
+                    )
+                    .groupBy(audioContent.id)
+                    .orderBy(order.can.sum().desc(), audioContent.createdAt.asc())
+            }
+        }
+
+        return select
             .offset(offset)
             .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 ea50196..6d12fe6 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt
@@ -24,17 +24,16 @@ import kr.co.vividnext.sodalive.member.Member
 import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
 import kr.co.vividnext.sodalive.utils.generateFileName
 import org.springframework.beans.factory.annotation.Value
+import org.springframework.cache.annotation.Cacheable
 import org.springframework.context.ApplicationEventPublisher
 import org.springframework.data.repository.findByIdOrNull
 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
@@ -72,11 +71,10 @@ class AudioContentService(
         )
 
         if (audioContentLike == null) {
-            audioContentLike = AudioContentLike(
-                memberId = member.id!!,
-                contentId = request.contentId
-            )
+            audioContentLike = AudioContentLike(memberId = member.id!!)
 
+            val audioContent = repository.findByIdAndActive(request.contentId)
+            audioContentLike.audioContent = audioContent
             audioContentLikeRepository.save(audioContentLike)
         } else {
             audioContentLike.isActive = !audioContentLike.isActive
@@ -579,21 +577,19 @@ class AudioContentService(
         }
     }
 
+    @Cacheable(
+        cacheNames = ["cache_ttl_3_days"],
+        key = "'contentRanking:' + ':' +" +
+            "#isAdult + ':' + #startDate + ':' + #endDate + ':' + #sortType + ':' + #offset + ':' + #limit"
+    )
     fun getAudioContentRanking(
-        member: Member,
+        isAdult: Boolean,
+        startDate: LocalDateTime,
+        endDate: LocalDateTime,
         offset: Long,
-        limit: Long
+        limit: Long,
+        sortType: String = "매출"
     ): 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일")
 
@@ -602,9 +598,10 @@ class AudioContentService(
                 cloudfrontHost = coverImageHost,
                 startDate = startDate.minusDays(1),
                 endDate = endDate.minusDays(1),
-                isAdult = member.auth != null,
+                isAdult = isAdult,
                 offset = offset,
-                limit = limit
+                limit = limit,
+                sortType = sortType
             )
 
         return GetAudioContentRanking(
@@ -613,4 +610,8 @@ class AudioContentService(
             items = contentRankingItemList
         )
     }
+
+    fun getContentRankingSortTypeList(): List<String> {
+        return listOf("매출", "후원", "댓글", "좋아요")
+    }
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/like/AudioContentLike.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/like/AudioContentLike.kt
index 5b9b16a..d54c5dd 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/like/AudioContentLike.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/like/AudioContentLike.kt
@@ -1,20 +1,21 @@
 package kr.co.vividnext.sodalive.content.like
 
+import kr.co.vividnext.sodalive.content.AudioContent
 import java.time.LocalDateTime
 import javax.persistence.Entity
+import javax.persistence.FetchType
 import javax.persistence.GeneratedValue
 import javax.persistence.GenerationType
 import javax.persistence.Id
+import javax.persistence.JoinColumn
+import javax.persistence.ManyToOne
 import javax.persistence.PrePersist
 import javax.persistence.PreUpdate
 import javax.persistence.Table
 
 @Entity
 @Table(name = "content_like")
-data class AudioContentLike(
-    val memberId: Long,
-    val contentId: Long
-) {
+data class AudioContentLike(val memberId: Long) {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     var id: Long? = null
@@ -34,4 +35,8 @@ data class AudioContentLike(
     }
 
     var isActive = true
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "content_id", nullable = false)
+    var audioContent: AudioContent? = null
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/like/AudioContentLikeRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/like/AudioContentLikeRepository.kt
index db8b1ad..72e9310 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/like/AudioContentLikeRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/like/AudioContentLikeRepository.kt
@@ -20,7 +20,7 @@ class AudioContentLikeQueryRepositoryImpl(private val queryFactory: JPAQueryFact
             .selectFrom(audioContentLike)
             .where(
                 audioContentLike.memberId.eq(memberId)
-                    .and(audioContentLike.contentId.eq(contentId))
+                    .and(audioContentLike.audioContent.id.eq(contentId))
             )
             .fetchFirst()
     }
@@ -30,7 +30,7 @@ class AudioContentLikeQueryRepositoryImpl(private val queryFactory: JPAQueryFact
             .select(audioContentLike.id)
             .from(audioContentLike)
             .where(
-                audioContentLike.contentId.eq(contentId)
+                audioContentLike.audioContent.id.eq(contentId)
                     .and(audioContentLike.isActive.isTrue)
             )
             .fetch()
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 56f35f6..4b3fa20 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
@@ -1,6 +1,7 @@
 package kr.co.vividnext.sodalive.content.main
 
 import kr.co.vividnext.sodalive.content.AudioContentRepository
+import kr.co.vividnext.sodalive.content.AudioContentService
 import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType
 import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse
 import kr.co.vividnext.sodalive.content.main.curation.GetAudioContentCurationResponse
@@ -15,12 +16,12 @@ 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(
     private val repository: AudioContentRepository,
+    private val audioContentService: AudioContentService,
     private val blockMemberRepository: BlockMemberRepository,
     private val orderService: OrderService,
     private val audioContentThemeRepository: AudioContentThemeQueryRepository,
@@ -66,7 +67,14 @@ class AudioContentMainService(
             .withSecond(0)
         val endDate = startDate.plusDays(7)
 
-        val contentRanking = getContentRanking(isAdult = isAdult, startDate = startDate, endDate = endDate)
+        val contentRankingSortTypeList = audioContentService.getContentRankingSortTypeList()
+        val contentRanking = audioContentService.getAudioContentRanking(
+            isAdult = isAdult,
+            startDate = startDate,
+            endDate = endDate,
+            offset = 0,
+            limit = 12
+        )
 
         return GetAudioContentMainResponse(
             newContentUploadCreatorList = newContentUploadCreatorList,
@@ -75,6 +83,7 @@ class AudioContentMainService(
             themeList = themeList,
             newContentList = newContentList,
             curationList = curationList,
+            contentRankingSortTypeList = contentRankingSortTypeList,
             contentRanking = contentRanking
         )
     }
@@ -192,27 +201,4 @@ class AudioContentMainService(
             }
             .filter { it.contents.isNotEmpty() }
             .toList()
-
-    @Cacheable(
-        cacheNames = ["cache_ttl_3_days"],
-        key = "'contentRanking:' + ':' + #isAdult + ':' + #startDate + ':' + #endDate"
-    )
-    fun getContentRanking(isAdult: Boolean, startDate: LocalDateTime, endDate: LocalDateTime): GetAudioContentRanking {
-        val startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일")
-        val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일")
-
-        val contentRankingItemList = repository
-            .getAudioContentRanking(
-                cloudfrontHost = imageHost,
-                startDate = startDate.minusDays(1),
-                endDate = endDate.minusDays(1),
-                isAdult = isAdult
-            )
-
-        return GetAudioContentRanking(
-            startDate = startDate.format(startDateFormatter),
-            endDate = endDate.minusDays(1).format(endDateFormatter),
-            contentRankingItemList
-        )
-    }
 }
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 9734f21..1cd184c 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
@@ -10,5 +10,6 @@ data class GetAudioContentMainResponse(
     val themeList: List<String>,
     val newContentList: List<GetAudioContentMainItem>,
     val curationList: List<GetAudioContentCurationResponse>,
+    val contentRankingSortTypeList: List<String>,
     val contentRanking: GetAudioContentRanking
 )