From 9a7e76ea7a3518bd7ca45e231c5f3b31235eadb5 Mon Sep 17 00:00:00 2001
From: Klaus <klaus@vividnext.co.kr>
Date: Sun, 22 Oct 2023 19:50:59 +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-=20API=20=EB=B6=84=EB=A6=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../content/AudioContentRepository.kt         |  27 ++-
 .../main/AudioContentMainController.kt        |  52 ++++++
 .../content/main/AudioContentMainService.kt   | 155 ++++++++++++++++++
 3 files changed, 230 insertions(+), 4 deletions(-)

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 c5df5c2..42371c7 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt
@@ -78,6 +78,9 @@ interface AudioContentQueryRepository {
 
     fun getAudioContentMainBannerList(isAdult: Boolean): List<AudioContentBanner>
     fun getAudioContentCurations(isAdult: Boolean): List<AudioContentCuration>
+
+    fun getAudioContentCurationList(isAdult: Boolean, offset: Long = 0, limit: Long = 10): List<AudioContentCuration>
+
     fun findAudioContentByCurationId(
         curationId: Long,
         cloudfrontHost: String,
@@ -337,10 +340,6 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
             .fetch()
     }
 
-    @Cacheable(
-        value = ["getNewContentUploadCreatorList"],
-        cacheManager = "cacheManager"
-    )
     override fun getNewContentUploadCreatorList(
         cloudfrontHost: String,
         isAdult: Boolean
@@ -410,6 +409,26 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
             .fetch()
     }
 
+    override fun getAudioContentCurationList(
+        isAdult: Boolean,
+        offset: Long,
+        limit: Long
+    ): List<AudioContentCuration> {
+        var where = audioContentCuration.isActive.isTrue
+
+        if (!isAdult) {
+            where = where.and(audioContentCuration.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .selectFrom(audioContentCuration)
+            .where(where)
+            .orderBy(audioContentCuration.orders.asc())
+            .offset(offset)
+            .limit(limit)
+            .fetch()
+    }
+
     override fun findAudioContentByCurationId(
         curationId: Long,
         cloudfrontHost: String,
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainController.kt
index 41cee69..df51a73 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainController.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainController.kt
@@ -23,6 +23,33 @@ class AudioContentMainController(private val service: AudioContentMainService) {
         ApiResponse.ok(service.getMain(member = member))
     }
 
+    @GetMapping("/new-content-upload-creator")
+    fun getNewContentUploadCreatorList(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.getNewContentUploadCreatorList(member = member))
+    }
+
+    @GetMapping("/banner-list")
+    fun getAudioContentMainBannerList(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.getAudioContentMainBannerList(member = member))
+    }
+
+    @GetMapping("/order-list")
+    fun getAudioContentMainOrderList(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.getAudioContentMainOrderList(member = member))
+    }
+
     @GetMapping("/new")
     fun getNewContentByTheme(
         @RequestParam("theme") theme: String,
@@ -53,4 +80,29 @@ class AudioContentMainController(private val service: AudioContentMainService) {
 
         ApiResponse.ok(service.getNewContentFor2WeeksByTheme(theme, member, pageable))
     }
+
+    @GetMapping("/curation-list")
+    fun getAudioContentMainCurationList(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
+        pageable: Pageable
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(
+            service.getAudioContentMainCurationList(
+                member,
+                offset = pageable.offset,
+                limit = pageable.pageSize.toLong()
+            )
+        )
+    }
+
+    @GetMapping("/content-ranking")
+    fun getAudioContentMainContentRanking(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.getAudioContentMainContentRanking(member))
+    }
 }
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 5b6b050..159b661 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
@@ -10,6 +10,7 @@ import kr.co.vividnext.sodalive.event.EventItem
 import kr.co.vividnext.sodalive.member.Member
 import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
 import org.springframework.beans.factory.annotation.Value
+import org.springframework.cache.annotation.Cacheable
 import org.springframework.data.domain.Pageable
 import org.springframework.stereotype.Service
 import java.time.DayOfWeek
@@ -168,6 +169,87 @@ class AudioContentMainService(
         )
     }
 
+    @Cacheable(
+        value = ["getNewContentUploadCreatorList"],
+        cacheManager = "cacheManager"
+    )
+    fun getNewContentUploadCreatorList(member: Member): List<GetNewContentUploadCreator> {
+        val isAdult = member.auth != null
+
+        // 2주일 이내에 콘텐츠를 올린 크리에이터 20명 조회
+        return repository.getNewContentUploadCreatorList(
+            cloudfrontHost = imageHost,
+            isAdult = isAdult
+        )
+            .asSequence()
+            .filter { !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.creatorId) }
+            .toList()
+    }
+
+    @Cacheable(
+        value = ["getAudioContentMainBannerList"],
+        cacheManager = "cacheManager"
+    )
+    fun getAudioContentMainBannerList(member: Member): List<GetAudioContentBannerResponse> {
+        val isAdult = member.auth != null
+
+        return repository
+            .getAudioContentMainBannerList(isAdult = isAdult)
+            .asSequence()
+            .filter {
+                if (it.type == AudioContentBannerType.CREATOR && it.creator != null) {
+                    !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.creator!!.id!!)
+                } else {
+                    true
+                }
+            }
+            .map {
+                GetAudioContentBannerResponse(
+                    type = it.type,
+                    thumbnailImageUrl = "$imageHost/${it.thumbnailImage}",
+                    eventItem = if (it.type == AudioContentBannerType.EVENT && it.event != null) {
+                        EventItem(
+                            id = it.event!!.id!!,
+                            thumbnailImageUrl = if (!it.event!!.thumbnailImage.startsWith("https://")) {
+                                "$imageHost/${it.event!!.thumbnailImage}"
+                            } else {
+                                it.event!!.thumbnailImage
+                            },
+                            detailImageUrl = if (
+                                it.event!!.detailImage != null &&
+                                !it.event!!.detailImage!!.startsWith("https://")
+                            ) {
+                                "$imageHost/${it.event!!.detailImage}"
+                            } else {
+                                it.event!!.detailImage
+                            },
+                            popupImageUrl = null,
+                            link = it.event!!.link,
+                            title = it.event!!.title,
+                            isPopup = false
+                        )
+                    } else {
+                        null
+                    },
+                    creatorId = if (it.type == AudioContentBannerType.CREATOR && it.creator != null) {
+                        it.creator!!.id
+                    } else {
+                        null
+                    },
+                    link = it.link
+                )
+            }
+            .toList()
+    }
+
+    fun getAudioContentMainOrderList(member: Member): List<GetAudioContentMainItem> {
+        return orderService
+            .getAudioContentMainOrderList(
+                member = member,
+                limit = 20
+            )
+    }
+
     fun getThemeList(member: Member): List<String> {
         return audioContentThemeRepository.getActiveThemeOfContent(isAdult = member.auth != null)
     }
@@ -197,4 +279,77 @@ class AudioContentMainService(
 
         return GetNewContentAllResponse(totalCount, items)
     }
+
+    @Cacheable(
+        value = ["getAudioContentMainCurationList"],
+        cacheManager = "cacheManager"
+    )
+    fun getAudioContentMainCurationList(
+        member: Member,
+        offset: Long,
+        limit: Long
+    ): List<GetAudioContentCurationResponse> {
+        val isAdult = member.auth != null
+
+        return repository
+            .getAudioContentCurationList(
+                isAdult = isAdult,
+                offset = offset,
+                limit = limit
+            )
+            .asSequence()
+            .map {
+                GetAudioContentCurationResponse(
+                    curationId = it.id!!,
+                    title = it.title,
+                    description = it.description,
+                    contents = repository.findAudioContentByCurationId(
+                        curationId = it.id!!,
+                        cloudfrontHost = imageHost,
+                        isAdult = isAdult
+                    )
+                        .asSequence()
+                        .filter { content ->
+                            !blockMemberRepository.isBlocked(
+                                blockedMemberId = member.id!!,
+                                memberId = content.creatorId
+                            )
+                        }
+                        .toList()
+                )
+            }
+            .filter { it.contents.isNotEmpty() }
+            .toList()
+    }
+
+    fun getAudioContentMainContentRanking(member: Member): GetAudioContentRanking {
+        val isAdult = member.auth != null
+
+        val currentDateTime = LocalDateTime.now()
+        val startDate = currentDateTime
+            .minusWeeks(1)
+            .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
+            .withHour(15)
+            .withMinute(0)
+            .withSecond(0)
+        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.minusDays(1),
+                endDate = endDate.minusDays(1),
+                isAdult = isAdult
+            )
+
+        return GetAudioContentRanking(
+            startDate = startDate.format(startDateFormatter),
+            endDate = endDate.minusDays(1).format(endDateFormatter),
+            contentRankingItemList
+        )
+    }
 }