시리즈 리스트 상세 API
- 콘텐츠 리스트 추가
This commit is contained in:
		| @@ -46,4 +46,22 @@ class ContentSeriesController(private val service: ContentSeriesService) { | ||||
|             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() | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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.QSeriesContent.seriesContent | ||||
| 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 org.springframework.data.jpa.repository.JpaRepository | ||||
|  | ||||
| @@ -25,7 +24,6 @@ interface ContentSeriesQueryRepository { | ||||
|     ): List<Series> | ||||
|  | ||||
|     fun getSeriesDetail(seriesId: Long, isAuth: Boolean): Series? | ||||
|     fun getPublishedDaysOfWeek(seriesId: Long): Set<SeriesPublishedDaysOfWeek> | ||||
|     fun getKeywordList(seriesId: Long): List<String> | ||||
|     fun getSeriesContentMinMaxPrice(seriesId: Long): GetSeriesContentMinMaxPriceResponse | ||||
| } | ||||
| @@ -85,14 +83,6 @@ class ContentSeriesQueryRepositoryImpl( | ||||
|             .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> { | ||||
|         return queryFactory | ||||
|             .select(hashTag.tag) | ||||
|   | ||||
| @@ -1,7 +1,10 @@ | ||||
| package kr.co.vividnext.sodalive.content.series | ||||
|  | ||||
| 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.GetSeriesContentListResponse | ||||
| 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.SeriesState | ||||
| @@ -16,6 +19,7 @@ import java.time.format.DateTimeFormatter | ||||
| @Service | ||||
| class ContentSeriesService( | ||||
|     private val repository: ContentSeriesRepository, | ||||
|     private val orderRepository: OrderRepository, | ||||
|     private val explorerQueryRepository: ExplorerQueryRepository, | ||||
|     private val seriesContentRepository: ContentSeriesContentRepository, | ||||
|  | ||||
| @@ -97,7 +101,9 @@ class ContentSeriesService( | ||||
|         val rentalMinPrice = (minMaxPrice.minPrice * 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( | ||||
|             seriesId = seriesId, | ||||
|             title = series.title, | ||||
| @@ -124,11 +130,43 @@ class ContentSeriesService( | ||||
|             minPrice = minPrice, | ||||
|             maxPrice = maxPrice, | ||||
|             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 { | ||||
|         val dayOfWeekText = publishedDaysOfWeek.toList().sortedBy { it.ordinal } | ||||
|             .map { | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| package kr.co.vividnext.sodalive.content.series | ||||
|  | ||||
| import kr.co.vividnext.sodalive.content.series.content.GetSeriesContentListItem | ||||
|  | ||||
| data class GetSeriesDetailResponse( | ||||
|     val seriesId: Long, | ||||
|     val title: String, | ||||
| @@ -14,15 +16,17 @@ data class GetSeriesDetailResponse( | ||||
|     var rentalMinPrice: Int, | ||||
|     var rentalMaxPrice: Int, | ||||
|     val rentalPeriod: Int, | ||||
|     var minPrice: Int, | ||||
|     var maxPrice: Int, | ||||
|     var keywordList: List<String>, | ||||
|     var publishedDaysOfWeek: String | ||||
|     val minPrice: Int, | ||||
|     val maxPrice: Int, | ||||
|     val keywordList: List<String>, | ||||
|     val publishedDaysOfWeek: String, | ||||
|     val contentList: List<GetSeriesContentListItem>, | ||||
|     val contentCount: Int | ||||
| ) { | ||||
|     data class GetSeriesDetailCreator( | ||||
|         val creatorId: Long, | ||||
|         val nickname: String, | ||||
|         val profileImage: String, | ||||
|         var isFollow: Boolean | ||||
|         val isFollow: Boolean | ||||
|     ) | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| package kr.co.vividnext.sodalive.content.series.content | ||||
|  | ||||
| import com.querydsl.core.types.dsl.Expressions | ||||
| import com.querydsl.jpa.impl.JPAQueryFactory | ||||
| import kr.co.vividnext.sodalive.content.QAudioContent.audioContent | ||||
| import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series | ||||
| @@ -12,6 +13,14 @@ interface ContentSeriesContentRepository : JpaRepository<SeriesContent, Long>, C | ||||
|  | ||||
| interface ContentSeriesContentQueryRepository { | ||||
|     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 | ||||
| } | ||||
|  | ||||
| @@ -36,6 +45,53 @@ class ContentSeriesContentQueryRepositoryImpl( | ||||
|             .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( | ||||
|         seriesId: Long, | ||||
|         isAdult: Boolean, | ||||
|   | ||||
| @@ -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 | ||||
| ) | ||||
		Reference in New Issue
	
	Block a user