From 00e4fefc8f9e21fe06ae52fa162ee5b678afcd6b Mon Sep 17 00:00:00 2001
From: Klaus <klaus@vividnext.co.kr>
Date: Wed, 18 Dec 2024 03:22:13 +0900
Subject: [PATCH 1/3] =?UTF-8?q?=EA=B5=AC=EB=A7=A4=EB=AA=A9=EB=A1=9D=20-=20?=
 =?UTF-8?q?orderType=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=A0=9C?=
 =?UTF-8?q?=EA=B1=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../sodalive/content/order/OrderController.kt |  3 -
 .../sodalive/content/order/OrderRepository.kt | 76 +++++--------------
 .../sodalive/content/order/OrderService.kt    |  3 -
 3 files changed, 20 insertions(+), 62 deletions(-)

diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderController.kt
index 889e6ed..89c1e08 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderController.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderController.kt
@@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.GetMapping
 import org.springframework.web.bind.annotation.PostMapping
 import org.springframework.web.bind.annotation.RequestBody
 import org.springframework.web.bind.annotation.RequestMapping
-import org.springframework.web.bind.annotation.RequestParam
 import org.springframework.web.bind.annotation.RestController
 
 @RestController
@@ -34,7 +33,6 @@ class OrderController(private val service: OrderService) {
 
     @GetMapping("/audio-content")
     fun getAudioContentOrderList(
-        @RequestParam(value = "orderType", required = false) orderType: OrderType? = null,
         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
         pageable: Pageable
     ) = run {
@@ -42,7 +40,6 @@ class OrderController(private val service: OrderService) {
 
         ApiResponse.ok(
             service.getAudioContentOrderList(
-                orderType = orderType,
                 member = member,
                 offset = pageable.offset,
                 limit = pageable.pageSize.toLong()
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderRepository.kt
index 4c7887b..6b31b77 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderRepository.kt
@@ -20,7 +20,6 @@ interface OrderQueryRepository {
     fun isExistOrderedAndOrderType(memberId: Long, contentId: Long): Pair<Boolean, OrderType?>
     fun getAudioContentRemainingTime(memberId: Long, contentId: Long, timezone: String): String
     fun getAudioContentOrderList(
-        orderType: OrderType?,
         dateTime: LocalDateTime,
         coverImageHost: String,
         memberId: Long,
@@ -28,7 +27,7 @@ interface OrderQueryRepository {
         limit: Long = 10
     ): List<GetAudioContentOrderListItem>
 
-    fun totalAudioContentOrderListCount(orderType: OrderType?, memberId: Long, dateTime: LocalDateTime): Int
+    fun totalAudioContentOrderListCount(memberId: Long, dateTime: LocalDateTime): Int
     fun getAudioContentMainOrderList(
         dateTime: LocalDateTime,
         coverImageHost: String,
@@ -118,40 +117,22 @@ class OrderQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Orde
     }
 
     override fun getAudioContentOrderList(
-        orderType: OrderType?,
         dateTime: LocalDateTime,
         coverImageHost: String,
         memberId: Long,
         offset: Long,
         limit: Long
     ): List<GetAudioContentOrderListItem> {
-        var where = order.member.id.eq(memberId)
+        val where = order.member.id.eq(memberId)
             .and(order.isActive.isTrue)
-
-        when (orderType) {
-            OrderType.RENTAL -> {
-                where = where.and(
-                    order.type.eq(OrderType.RENTAL)
-                        .and(order.startDate.before(dateTime))
-                        .and(order.endDate.after(dateTime))
-                )
-            }
-
-            OrderType.KEEP -> {
-                where = where.and(order.type.eq(OrderType.KEEP))
-            }
-
-            null -> {
-                where = where.and(
-                    order.type.eq(OrderType.KEEP)
-                        .or(
-                            order.type.eq(OrderType.RENTAL)
-                                .and(order.startDate.before(dateTime))
-                                .and(order.endDate.after(dateTime))
-                        )
-                )
-            }
-        }
+            .and(
+                order.type.eq(OrderType.KEEP)
+                    .or(
+                        order.type.eq(OrderType.RENTAL)
+                            .and(order.startDate.before(dateTime))
+                            .and(order.endDate.after(dateTime))
+                    )
+            )
 
         return queryFactory
             .select(
@@ -178,34 +159,17 @@ class OrderQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Orde
             .fetch()
     }
 
-    override fun totalAudioContentOrderListCount(orderType: OrderType?, memberId: Long, dateTime: LocalDateTime): Int {
-        var where = order.member.id.eq(memberId)
+    override fun totalAudioContentOrderListCount(memberId: Long, dateTime: LocalDateTime): Int {
+        val where = order.member.id.eq(memberId)
             .and(order.isActive.isTrue)
-
-        when (orderType) {
-            OrderType.RENTAL -> {
-                where = where.and(
-                    order.type.eq(OrderType.RENTAL)
-                        .and(order.startDate.before(dateTime))
-                        .and(order.endDate.after(dateTime))
-                )
-            }
-
-            OrderType.KEEP -> {
-                where = where.and(order.type.eq(OrderType.KEEP))
-            }
-
-            null -> {
-                where = where.and(
-                    order.type.eq(OrderType.KEEP)
-                        .or(
-                            order.type.eq(OrderType.RENTAL)
-                                .and(order.startDate.before(dateTime))
-                                .and(order.endDate.after(dateTime))
-                        )
-                )
-            }
-        }
+            .and(
+                order.type.eq(OrderType.KEEP)
+                    .or(
+                        order.type.eq(OrderType.RENTAL)
+                            .and(order.startDate.before(dateTime))
+                            .and(order.endDate.after(dateTime))
+                    )
+            )
 
         return queryFactory.select(order.id)
             .from(order)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderService.kt
index 620aaaa..630b22a 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderService.kt
@@ -90,18 +90,15 @@ class OrderService(
     }
 
     fun getAudioContentOrderList(
-        orderType: OrderType? = null,
         member: Member,
         offset: Long,
         limit: Long
     ): GetAudioContentOrderListResponse {
         val totalCount = repository.totalAudioContentOrderListCount(
-            orderType = orderType,
             memberId = member.id!!,
             dateTime = LocalDateTime.now()
         )
         val orderItems = repository.getAudioContentOrderList(
-            orderType = orderType,
             dateTime = LocalDateTime.now(),
             coverImageHost = audioContentCoverImageHost,
             memberId = member.id!!,

From 36052f034ace4e8e2fc3afbf6a9a21b13c31b3a8 Mon Sep 17 00:00:00 2001
From: Klaus <klaus@vividnext.co.kr>
Date: Wed, 18 Dec 2024 06:18:36 +0900
Subject: [PATCH 2/3] =?UTF-8?q?=EC=9E=AC=EC=83=9D=EB=AA=A9=EB=A1=9D=20?=
 =?UTF-8?q?=EB=93=B1=EB=A1=9D/=EC=88=98=EC=A0=95=20-=20=EB=93=B1=EB=A1=9D/?=
 =?UTF-8?q?=EC=88=98=EC=A0=95=EC=8B=9C=20=EB=A7=8C=EB=A3=8C=20=EB=82=A0?=
 =?UTF-8?q?=EC=A7=9C=20=EC=8B=9C=EA=B0=84=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../sodalive/content/order/OrderRepository.kt | 22 +++++++++-
 .../playlist/AudioContentPlaylistService.kt   | 41 ++++++++++++++-----
 .../playlist/ContentIdAndEndDateData.kt       |  9 ++++
 .../playlist/PlaylistContentIdAndOrder.kt     |  5 ++-
 4 files changed, 64 insertions(+), 13 deletions(-)
 create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/ContentIdAndEndDateData.kt

diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderRepository.kt
index 6b31b77..e900de8 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderRepository.kt
@@ -6,6 +6,8 @@ import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
 import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
 import kr.co.vividnext.sodalive.content.main.QGetAudioContentMainItem
 import kr.co.vividnext.sodalive.content.order.QOrder.order
+import kr.co.vividnext.sodalive.content.playlist.ContentIdAndEndDateData
+import kr.co.vividnext.sodalive.content.playlist.QContentIdAndEndDateData
 import kr.co.vividnext.sodalive.member.QMember.member
 import org.springframework.data.jpa.repository.JpaRepository
 import org.springframework.stereotype.Repository
@@ -37,6 +39,7 @@ interface OrderQueryRepository {
     ): List<GetAudioContentMainItem>
 
     fun findOrderedContent(contentIdList: List<Long>, memberId: Long): List<Long>
+    fun findEndDateByContentId(contentIdList: List<Long>, memberId: Long): List<ContentIdAndEndDateData>
 }
 
 @Repository
@@ -229,9 +232,24 @@ class OrderQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Orde
             .innerJoin(order.audioContent, audioContent)
             .where(
                 order.isActive.isTrue,
-                order.endDate.isNull,
                 member.id.eq(memberId),
-                audioContent.id.`in`(contentIdList)
+                audioContent.id.`in`(contentIdList),
+                order.endDate.isNull.or(order.endDate.after(LocalDateTime.now()))
+            )
+            .fetch()
+    }
+
+    override fun findEndDateByContentId(contentIdList: List<Long>, memberId: Long): List<ContentIdAndEndDateData> {
+        return queryFactory
+            .select(QContentIdAndEndDateData(audioContent.id, order.endDate))
+            .from(order)
+            .innerJoin(order.member, member)
+            .innerJoin(order.audioContent, audioContent)
+            .where(
+                order.isActive.isTrue,
+                member.id.eq(memberId),
+                audioContent.id.`in`(contentIdList),
+                order.endDate.isNull.or(order.endDate.after(LocalDateTime.now()))
             )
             .fetch()
     }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/AudioContentPlaylistService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/AudioContentPlaylistService.kt
index 46ccf02..3b23c67 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/AudioContentPlaylistService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/AudioContentPlaylistService.kt
@@ -27,10 +27,9 @@ class AudioContentPlaylistService(
             throw SodaException("플레이 리스트는 최대 10개까지 생성할 수 있습니다.")
         }
 
-        // 콘텐츠 유효성 검사 (소장으로 구매한 콘텐츠 인가?)
-        validateContent(
-            contentIdList = request.contentIdAndOrderList.map { it.contentId },
-            memberId = member.id!!
+        val contentIdAndOrderList = validateAndGetContentIdAndOrderList(
+            contentIdAndOrderList = request.contentIdAndOrderList,
+            member
         )
 
         val playlist = AudioContentPlaylist(
@@ -38,12 +37,34 @@ class AudioContentPlaylistService(
             memberId = member.id!!,
             title = request.title,
             desc = request.desc,
-            contentIdAndOrderList = request.contentIdAndOrderList
+            contentIdAndOrderList = contentIdAndOrderList
         )
 
         redisRepository.save(playlist)
     }
 
+    private fun validateAndGetContentIdAndOrderList(
+        contentIdAndOrderList: List<PlaylistContentIdAndOrder>,
+        member: Member
+    ): List<PlaylistContentIdAndOrder> {
+        validateContent(
+            contentIdList = contentIdAndOrderList.map { it.contentId },
+            memberId = member.id!!
+        )
+
+        val contentIdAndEndDate = orderRepository.findEndDateByContentId(
+            contentIdList = contentIdAndOrderList.map { it.contentId },
+            memberId = member.id!!
+        )
+
+        val contentIdAndEndDateMap = contentIdAndEndDate.associate { it.contentId to it.endDate }
+
+        return contentIdAndOrderList.map {
+            it.endDate = contentIdAndEndDateMap[it.contentId]
+            it
+        }
+    }
+
     private fun validateContent(contentIdList: List<Long>, memberId: Long) {
         if (contentIdList.isEmpty()) {
             throw SodaException("콘텐츠를 1개 이상 추가하세요")
@@ -61,7 +82,7 @@ class AudioContentPlaylistService(
         val notOrderedContentList = orderedContentMap.filterValues { !it }.keys
 
         if (notOrderedContentList.isNotEmpty()) {
-            throw SodaException("소장하지 않은 콘텐츠는 재생목록에 추가할 수 없습니다.")
+            throw SodaException("대여/소장하지 않은 콘텐츠는 재생목록에 추가할 수 없습니다.")
         }
     }
 
@@ -77,15 +98,15 @@ class AudioContentPlaylistService(
             throw SodaException("잘못된 요청입니다.")
         }
 
-        checkOrderedContent(
-            contentIdList = request.contentIdAndOrderList.map { it.contentId },
-            memberId = member.id!!
+        val contentIdAndOrderList = validateAndGetContentIdAndOrderList(
+            contentIdAndOrderList = request.contentIdAndOrderList,
+            member
         )
 
         val updatePlaylist = playlist.copy(
             title = request.title ?: playlist.title,
             desc = request.desc ?: playlist.desc,
-            contentIdAndOrderList = request.contentIdAndOrderList
+            contentIdAndOrderList = contentIdAndOrderList
         )
 
         redisRepository.save(updatePlaylist)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/ContentIdAndEndDateData.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/ContentIdAndEndDateData.kt
new file mode 100644
index 0000000..5d89a49
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/ContentIdAndEndDateData.kt
@@ -0,0 +1,9 @@
+package kr.co.vividnext.sodalive.content.playlist
+
+import com.querydsl.core.annotations.QueryProjection
+import java.time.LocalDateTime
+
+data class ContentIdAndEndDateData @QueryProjection constructor(
+    val contentId: Long,
+    val endDate: LocalDateTime?
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/PlaylistContentIdAndOrder.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/PlaylistContentIdAndOrder.kt
index 2e0d558..dbc8196 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/PlaylistContentIdAndOrder.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/PlaylistContentIdAndOrder.kt
@@ -1,6 +1,9 @@
 package kr.co.vividnext.sodalive.content.playlist
 
+import java.time.LocalDateTime
+
 data class PlaylistContentIdAndOrder(
     val contentId: Long,
-    val order: Int
+    val order: Int,
+    var endDate: LocalDateTime? = null
 )

From 0c4dc7e5df49199990a5bba8bb77378d2d669aa2 Mon Sep 17 00:00:00 2001
From: Klaus <klaus@vividnext.co.kr>
Date: Wed, 18 Dec 2024 06:39:10 +0900
Subject: [PATCH 3/3] =?UTF-8?q?=EC=9E=AC=EC=83=9D=EB=AA=A9=EB=A1=9D=20?=
 =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8,=20=EC=83=81=EC=84=B8=20-=20?=
 =?UTF-8?q?=EB=8C=80=EC=97=AC=EA=B0=80=20=EB=A7=8C=EB=A3=8C=EB=90=9C=20?=
 =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=EB=A5=BC=20=EC=A0=9C=EC=99=B8?=
 =?UTF-8?q?=ED=95=98=EA=B3=A0=20=EC=A1=B0=ED=9A=8C=EB=90=98=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../playlist/AudioContentPlaylistService.kt   | 25 +++++++++++++------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/AudioContentPlaylistService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/AudioContentPlaylistService.kt
index 3b23c67..f224ab0 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/AudioContentPlaylistService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/playlist/AudioContentPlaylistService.kt
@@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.live.roulette.RedisIdGenerator
 import kr.co.vividnext.sodalive.member.Member
 import org.springframework.data.repository.findByIdOrNull
 import org.springframework.stereotype.Service
+import java.time.LocalDateTime
 import java.time.ZoneId
 import java.time.format.DateTimeFormatter
 
@@ -119,18 +120,22 @@ class AudioContentPlaylistService(
 
         return GetPlaylistsResponse(
             totalCount = playlists.size,
-            items = playlists.map {
-                val contentCount = it.contentIdAndOrderList.size
+            items = playlists.map { playlist ->
+                val contentIdAndOrderList = playlist.contentIdAndOrderList.filter {
+                    it.endDate == null || it.endDate!! > LocalDateTime.now()
+                }
+
+                val contentCount = contentIdAndOrderList.size
                 val coverImageUrl = if (contentCount > 0) {
-                    audioContentRepository.getCoverImageById(id = it.contentIdAndOrderList[0].contentId)
+                    audioContentRepository.getCoverImageById(id = contentIdAndOrderList[0].contentId)
                         ?: ""
                 } else {
                     ""
                 }
                 GetPlaylistsItem(
-                    id = it.id,
-                    title = it.title,
-                    desc = it.desc ?: "",
+                    id = playlist.id,
+                    title = playlist.title,
+                    desc = playlist.desc ?: "",
                     contentCount = contentCount,
                     coverImageUrl = coverImageUrl
                 )
@@ -163,11 +168,15 @@ class AudioContentPlaylistService(
             .withZoneSameInstant(ZoneId.of("Asia/Seoul"))
             .format(dateTimeFormatter)
 
+        val contentIdAndOrderList = playlist.contentIdAndOrderList.filter {
+            it.endDate == null || it.endDate!! > LocalDateTime.now()
+        }
+
         val contentList = audioContentRepository.fetchContentForPlaylist(
-            contentIdList = playlist.contentIdAndOrderList.map { it.contentId }
+            contentIdList = contentIdAndOrderList.map { it.contentId }
         )
 
-        val orderMap = playlist.contentIdAndOrderList.sortedBy { it.order }
+        val orderMap = contentIdAndOrderList.sortedBy { it.order }
             .mapIndexed { index, item -> item.contentId to index }
             .toMap()