시리즈 리스트 상세 API

- 콘텐츠 리스트 추가
This commit is contained in:
Klaus 2024-04-26 21:18:13 +09:00
parent 0350f86322
commit 3b807543b7
6 changed files with 143 additions and 18 deletions

View File

@ -46,4 +46,22 @@ class ContentSeriesController(private val service: ContentSeriesService) {
service.getSeriesDetail(seriesId = id, member = member) service.getSeriesDetail(seriesId = id, member = member)
) )
} }
@GetMapping("/{id}/content")
fun getSeriesContentList(
@PathVariable id: Long,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
pageable: Pageable
) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
ApiResponse.ok(
service.getSeriesContentList(
seriesId = id,
member = member,
offset = pageable.offset,
limit = pageable.pageSize.toLong()
)
)
}
} }

View File

@ -8,7 +8,6 @@ import kr.co.vividnext.sodalive.content.series.content.QGetSeriesContentMinMaxPr
import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series
import kr.co.vividnext.sodalive.creator.admin.content.series.QSeriesContent.seriesContent import kr.co.vividnext.sodalive.creator.admin.content.series.QSeriesContent.seriesContent
import kr.co.vividnext.sodalive.creator.admin.content.series.Series import kr.co.vividnext.sodalive.creator.admin.content.series.Series
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek
import kr.co.vividnext.sodalive.creator.admin.content.series.keyword.QSeriesKeyword.seriesKeyword import kr.co.vividnext.sodalive.creator.admin.content.series.keyword.QSeriesKeyword.seriesKeyword
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
@ -25,7 +24,6 @@ interface ContentSeriesQueryRepository {
): List<Series> ): List<Series>
fun getSeriesDetail(seriesId: Long, isAuth: Boolean): Series? fun getSeriesDetail(seriesId: Long, isAuth: Boolean): Series?
fun getPublishedDaysOfWeek(seriesId: Long): Set<SeriesPublishedDaysOfWeek>
fun getKeywordList(seriesId: Long): List<String> fun getKeywordList(seriesId: Long): List<String>
fun getSeriesContentMinMaxPrice(seriesId: Long): GetSeriesContentMinMaxPriceResponse fun getSeriesContentMinMaxPrice(seriesId: Long): GetSeriesContentMinMaxPriceResponse
} }
@ -85,14 +83,6 @@ class ContentSeriesQueryRepositoryImpl(
.fetchFirst() .fetchFirst()
} }
override fun getPublishedDaysOfWeek(seriesId: Long): Set<SeriesPublishedDaysOfWeek> {
return queryFactory
.select(series.publishedDaysOfWeek)
.from(series)
.where(series.id.eq(seriesId))
.fetchFirst()
}
override fun getKeywordList(seriesId: Long): List<String> { override fun getKeywordList(seriesId: Long): List<String> {
return queryFactory return queryFactory
.select(hashTag.tag) .select(hashTag.tag)

View File

@ -1,7 +1,10 @@
package kr.co.vividnext.sodalive.content.series package kr.co.vividnext.sodalive.content.series
import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.content.order.OrderRepository
import kr.co.vividnext.sodalive.content.order.OrderType
import kr.co.vividnext.sodalive.content.series.content.ContentSeriesContentRepository import kr.co.vividnext.sodalive.content.series.content.ContentSeriesContentRepository
import kr.co.vividnext.sodalive.content.series.content.GetSeriesContentListResponse
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesSortType import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesSortType
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesState import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesState
@ -16,6 +19,7 @@ import java.time.format.DateTimeFormatter
@Service @Service
class ContentSeriesService( class ContentSeriesService(
private val repository: ContentSeriesRepository, private val repository: ContentSeriesRepository,
private val orderRepository: OrderRepository,
private val explorerQueryRepository: ExplorerQueryRepository, private val explorerQueryRepository: ExplorerQueryRepository,
private val seriesContentRepository: ContentSeriesContentRepository, private val seriesContentRepository: ContentSeriesContentRepository,
@ -97,7 +101,9 @@ class ContentSeriesService(
val rentalMinPrice = (minMaxPrice.minPrice * 0.7).toInt() val rentalMinPrice = (minMaxPrice.minPrice * 0.7).toInt()
val rentalMaxPrice = (minMaxPrice.maxPrice * 0.7).toInt() val rentalMaxPrice = (minMaxPrice.maxPrice * 0.7).toInt()
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") val seriesContentList = getSeriesContentList(seriesId = seriesId, member = member, offset = 0, limit = 5)
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy.MM.dd")
return GetSeriesDetailResponse( return GetSeriesDetailResponse(
seriesId = seriesId, seriesId = seriesId,
title = series.title, title = series.title,
@ -124,11 +130,43 @@ class ContentSeriesService(
minPrice = minPrice, minPrice = minPrice,
maxPrice = maxPrice, maxPrice = maxPrice,
keywordList = keywordList, keywordList = keywordList,
publishedDaysOfWeek = publishedDaysOfWeekText(series.publishedDaysOfWeek) publishedDaysOfWeek = publishedDaysOfWeekText(series.publishedDaysOfWeek),
contentList = seriesContentList.items,
contentCount = seriesContentList.totalCount
) )
} }
fun getSeriesContentList(seriesId: Long, member: Member, offset: Long, limit: Long): GetSeriesContentListResponse {
val isAdult = member.auth != null
val totalCount = seriesContentRepository.getContentCount(seriesId, isAdult = isAdult)
val contentList = seriesContentRepository.getContentList(
seriesId = seriesId,
isAdult = isAdult,
imageHost = coverImageHost,
offset = offset,
limit = limit
)
.map {
val (isExistsAudioContent, orderType) = orderRepository.isExistOrderedAndOrderType(
memberId = member.id!!,
contentId = it.contentId
)
if (isExistsAudioContent) {
if (orderType == OrderType.RENTAL) {
it.isRented = true
} else {
it.isOwned = true
}
}
it
}
return GetSeriesContentListResponse(totalCount, contentList)
}
private fun publishedDaysOfWeekText(publishedDaysOfWeek: Set<SeriesPublishedDaysOfWeek>): String { private fun publishedDaysOfWeekText(publishedDaysOfWeek: Set<SeriesPublishedDaysOfWeek>): String {
val dayOfWeekText = publishedDaysOfWeek.toList().sortedBy { it.ordinal } val dayOfWeekText = publishedDaysOfWeek.toList().sortedBy { it.ordinal }
.map { .map {

View File

@ -1,5 +1,7 @@
package kr.co.vividnext.sodalive.content.series package kr.co.vividnext.sodalive.content.series
import kr.co.vividnext.sodalive.content.series.content.GetSeriesContentListItem
data class GetSeriesDetailResponse( data class GetSeriesDetailResponse(
val seriesId: Long, val seriesId: Long,
val title: String, val title: String,
@ -14,15 +16,17 @@ data class GetSeriesDetailResponse(
var rentalMinPrice: Int, var rentalMinPrice: Int,
var rentalMaxPrice: Int, var rentalMaxPrice: Int,
val rentalPeriod: Int, val rentalPeriod: Int,
var minPrice: Int, val minPrice: Int,
var maxPrice: Int, val maxPrice: Int,
var keywordList: List<String>, val keywordList: List<String>,
var publishedDaysOfWeek: String val publishedDaysOfWeek: String,
val contentList: List<GetSeriesContentListItem>,
val contentCount: Int
) { ) {
data class GetSeriesDetailCreator( data class GetSeriesDetailCreator(
val creatorId: Long, val creatorId: Long,
val nickname: String, val nickname: String,
val profileImage: String, val profileImage: String,
var isFollow: Boolean val isFollow: Boolean
) )
} }

View File

@ -1,5 +1,6 @@
package kr.co.vividnext.sodalive.content.series.content package kr.co.vividnext.sodalive.content.series.content
import com.querydsl.core.types.dsl.Expressions
import com.querydsl.jpa.impl.JPAQueryFactory import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series
@ -12,6 +13,14 @@ interface ContentSeriesContentRepository : JpaRepository<SeriesContent, Long>, C
interface ContentSeriesContentQueryRepository { interface ContentSeriesContentQueryRepository {
fun getContentCount(seriesId: Long, isAdult: Boolean): Int fun getContentCount(seriesId: Long, isAdult: Boolean): Int
fun getContentList(
seriesId: Long,
isAdult: Boolean,
imageHost: String,
offset: Long,
limit: Long
): List<GetSeriesContentListItem>
fun isNewContent(seriesId: Long, isAdult: Boolean, fromDate: LocalDateTime, nowDate: LocalDateTime): Boolean fun isNewContent(seriesId: Long, isAdult: Boolean, fromDate: LocalDateTime, nowDate: LocalDateTime): Boolean
} }
@ -36,6 +45,53 @@ class ContentSeriesContentQueryRepositoryImpl(
.size .size
} }
override fun getContentList(
seriesId: Long,
isAdult: Boolean,
imageHost: String,
offset: Long,
limit: Long
): List<GetSeriesContentListItem> {
var where = series.id.eq(seriesId)
.and(audioContent.isActive.isTrue)
.and(audioContent.duration.isNotNull)
if (!isAdult) {
where = where.and(audioContent.isAdult.isFalse)
}
val formattedDate = Expressions.stringTemplate(
"DATE_FORMAT({0}, {1})",
Expressions.dateTimeTemplate(
LocalDateTime::class.java,
"CONVERT_TZ({0},{1},{2})",
audioContent.releaseDate,
"UTC",
"Asia/Seoul"
),
"%y.%m.%d"
)
return queryFactory
.select(
QGetSeriesContentListItem(
audioContent.id,
audioContent.title,
audioContent.coverImage.prepend("/").prepend(imageHost),
formattedDate,
audioContent.duration,
audioContent.price,
Expressions.asBoolean(false),
Expressions.asBoolean(false)
)
)
.from(seriesContent)
.innerJoin(seriesContent.series, series)
.innerJoin(seriesContent.content, audioContent)
.where(where)
.fetch()
}
override fun isNewContent( override fun isNewContent(
seriesId: Long, seriesId: Long,
isAdult: Boolean, isAdult: Boolean,

View File

@ -0,0 +1,19 @@
package kr.co.vividnext.sodalive.content.series.content
import com.querydsl.core.annotations.QueryProjection
data class GetSeriesContentListResponse(
val totalCount: Int,
val items: List<GetSeriesContentListItem>
)
data class GetSeriesContentListItem @QueryProjection constructor(
val contentId: Long,
val title: String,
val coverImage: String,
val releaseDate: String,
val duration: String,
val price: Int,
var isRented: Boolean,
var isOwned: Boolean
)