diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/domain/CreatorChannelLiveReplayQueryPolicy.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/domain/CreatorChannelLiveReplayQueryPolicy.kt new file mode 100644 index 00000000..4c25f42a --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/domain/CreatorChannelLiveReplayQueryPolicy.kt @@ -0,0 +1,28 @@ +package kr.co.vividnext.sodalive.v2.creator.channel.live.domain + +import kr.co.vividnext.sodalive.common.SodaException +import org.springframework.stereotype.Component + +@Component +class CreatorChannelLiveReplayQueryPolicy { + fun createPage(page: Int, size: Int): CreatorChannelPage { + if (page < MIN_PAGE || size < MIN_PAGE_SIZE || size > MAX_PAGE_SIZE) { + throw SodaException(messageKey = "common.error.invalid_request") + } + return CreatorChannelPage(page = page, size = size) + } + + fun limitItems(fetched: List, page: CreatorChannelPage): List { + return fetched.take(page.size) + } + + fun hasNext(fetched: List<*>, page: CreatorChannelPage): Boolean { + return fetched.size > page.size + } + + companion object { + private const val MIN_PAGE = 0 + private const val MIN_PAGE_SIZE = 20 + private const val MAX_PAGE_SIZE = 50 + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/domain/CreatorChannelLiveTab.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/domain/CreatorChannelLiveTab.kt new file mode 100644 index 00000000..bedfa2e0 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/domain/CreatorChannelLiveTab.kt @@ -0,0 +1,38 @@ +package kr.co.vividnext.sodalive.v2.creator.channel.live.domain + +import kr.co.vividnext.sodalive.v2.common.domain.ContentSort +import java.time.LocalDateTime + +data class CreatorChannelLiveTab( + val liveReplayContentCount: Int, + val currentLive: CreatorChannelLive?, + val liveReplayContents: List, + val sort: ContentSort, + val page: CreatorChannelPage, + val hasNext: Boolean +) + +data class CreatorChannelLive( + val liveId: Long, + val title: String, + val coverImageUrl: String?, + val beginDateTime: LocalDateTime, + val price: Int, + val isAdult: Boolean +) + +data class CreatorChannelAudioContent( + val audioContentId: Long, + val title: String, + val duration: String?, + val imageUrl: String?, + val price: Int, + val isAdult: Boolean, + val isPointAvailable: Boolean, + val isFirstContent: Boolean, + val publishedAt: LocalDateTime, + val seriesName: String?, + val isOriginalSeries: Boolean?, + val isOwned: Boolean, + val isRented: Boolean +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/domain/CreatorChannelPage.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/domain/CreatorChannelPage.kt new file mode 100644 index 00000000..284a92db --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/domain/CreatorChannelPage.kt @@ -0,0 +1,9 @@ +package kr.co.vividnext.sodalive.v2.creator.channel.live.domain + +data class CreatorChannelPage( + val page: Int, + val size: Int +) { + val offset: Long = page.toLong() * size + val fetchLimit: Int = size + 1 +} diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/domain/CreatorChannelLiveReplayQueryPolicyTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/domain/CreatorChannelLiveReplayQueryPolicyTest.kt new file mode 100644 index 00000000..f26581c9 --- /dev/null +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/live/domain/CreatorChannelLiveReplayQueryPolicyTest.kt @@ -0,0 +1,76 @@ +package kr.co.vividnext.sodalive.v2.creator.channel.live.domain + +import kr.co.vividnext.sodalive.common.SodaException +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class CreatorChannelLiveReplayQueryPolicyTest { + private val policy = CreatorChannelLiveReplayQueryPolicy() + + @Test + @DisplayName("라이브 다시듣기 page 정책은 offset, fetch limit, items limit, hasNext를 계산한다") + fun shouldCalculatePagePolicyForLiveReplayContents() { + val page = policy.createPage(page = 0, size = 20) + val fetched = (1..21).toList() + + val items = policy.limitItems(fetched, page) + + assertEquals(0L, page.offset) + assertEquals(21, page.fetchLimit) + assertEquals(20, items.size) + assertEquals((1..20).toList(), items) + assertTrue(policy.hasNext(fetched, page)) + } + + @Test + @DisplayName("size가 50이면 fetch limit을 51로 계산한다") + fun shouldCalculateFetchLimitWhenSizeIsMaximum() { + val page = policy.createPage(page = 1, size = 50) + + assertEquals(50L, page.offset) + assertEquals(51, page.fetchLimit) + } + + @Test + @DisplayName("page가 0보다 작으면 invalid request 예외를 던진다") + fun shouldThrowInvalidRequestWhenPageIsNegative() { + val exception = assertThrows(SodaException::class.java) { + policy.createPage(page = -1, size = 20) + } + + assertEquals("common.error.invalid_request", exception.messageKey) + } + + @Test + @DisplayName("size가 20보다 작으면 invalid request 예외를 던진다") + fun shouldThrowInvalidRequestWhenSizeIsLessThanMinimum() { + val exception = assertThrows(SodaException::class.java) { + policy.createPage(page = 0, size = 19) + } + + assertEquals("common.error.invalid_request", exception.messageKey) + } + + @Test + @DisplayName("size가 50보다 크면 invalid request 예외를 던진다") + fun shouldThrowInvalidRequestWhenSizeIsGreaterThanMaximum() { + val exception = assertThrows(SodaException::class.java) { + policy.createPage(page = 0, size = 51) + } + + assertEquals("common.error.invalid_request", exception.messageKey) + } + + @Test + @DisplayName("size가 Int 최대값이면 fetch limit overflow 전에 invalid request 예외를 던진다") + fun shouldThrowInvalidRequestWhenSizeWouldOverflowFetchLimit() { + val exception = assertThrows(SodaException::class.java) { + policy.createPage(page = 0, size = Int.MAX_VALUE) + } + + assertEquals("common.error.invalid_request", exception.messageKey) + } +}