| @@ -35,6 +35,7 @@ data class AudioContent( | ||||
|     @Enumerated(value = EnumType.STRING) | ||||
|     val type: AudioContentType = AudioContentType.INDIVIDUAL, | ||||
|     val isGeneratePreview: Boolean = true, | ||||
|     var isOnlyRental: Boolean = false, | ||||
|     var isAdult: Boolean = false, | ||||
|     var isCommentAvailable: Boolean = true | ||||
| ) : BaseEntity() { | ||||
|   | ||||
| @@ -2,7 +2,6 @@ package kr.co.vividnext.sodalive.content | ||||
|  | ||||
| import com.querydsl.core.types.dsl.Expressions | ||||
| import com.querydsl.jpa.impl.JPAQueryFactory | ||||
| import kr.co.vividnext.sodalive.can.use.QUseCan.useCan | ||||
| import kr.co.vividnext.sodalive.content.QAudioContent.audioContent | ||||
| import kr.co.vividnext.sodalive.content.QBundleAudioContent.bundleAudioContent | ||||
| import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | ||||
| @@ -78,6 +77,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 +339,6 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | ||||
|             .fetch() | ||||
|     } | ||||
|  | ||||
|     @Cacheable( | ||||
|         value = ["getNewContentUploadCreatorList"], | ||||
|         cacheManager = "cacheManager" | ||||
|     ) | ||||
|     override fun getNewContentUploadCreatorList( | ||||
|         cloudfrontHost: String, | ||||
|         isAdult: Boolean | ||||
| @@ -410,6 +408,30 @@ 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() | ||||
|     } | ||||
|  | ||||
|     @Cacheable( | ||||
|         value = ["findAudioContentByCurationId"], | ||||
|         cacheManager = "cacheManager" | ||||
|     ) | ||||
|     override fun findAudioContentByCurationId( | ||||
|         curationId: Long, | ||||
|         cloudfrontHost: String, | ||||
| @@ -459,12 +481,13 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | ||||
|         offset: Long, | ||||
|         limit: Long | ||||
|     ): List<GetAudioContentRankingItem> { | ||||
|         var where = audioContent.isActive.isTrue | ||||
|         var where = order.createdAt.goe(startDate) | ||||
|             .and(order.createdAt.lt(endDate)) | ||||
|             .and(audioContent.isActive.isTrue) | ||||
|             .and(audioContent.member.isNotNull) | ||||
|             .and(audioContent.duration.isNotNull) | ||||
|             .and(audioContent.member.isActive.isTrue) | ||||
|             .and(useCan.createdAt.goe(startDate)) | ||||
|             .and(useCan.createdAt.lt(endDate)) | ||||
|             .and(audioContentTheme.isActive.isTrue) | ||||
|  | ||||
|         if (!isAdult) { | ||||
|             where = where.and(audioContent.isAdult.isFalse) | ||||
| @@ -483,8 +506,7 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | ||||
|                     member.nickname | ||||
|                 ) | ||||
|             ) | ||||
|             .from(useCan) | ||||
|             .innerJoin(useCan.order, order) | ||||
|             .from(order) | ||||
|             .innerJoin(order.audioContent, audioContent) | ||||
|             .innerJoin(audioContent.member, member) | ||||
|             .innerJoin(audioContent.theme, audioContentTheme) | ||||
|   | ||||
| @@ -176,6 +176,7 @@ class AudioContentService( | ||||
|             } else { | ||||
|                 false | ||||
|             }, | ||||
|             isOnlyRental = request.isOnlyRental, | ||||
|             isCommentAvailable = request.isCommentAvailable | ||||
|         ) | ||||
|         audioContent.theme = theme | ||||
| @@ -486,6 +487,7 @@ class AudioContentService( | ||||
|             duration = audioContent.duration ?: "", | ||||
|             isAdult = audioContent.isAdult, | ||||
|             isMosaic = audioContent.isAdult && member.auth == null, | ||||
|             isOnlyRental = audioContent.isOnlyRental, | ||||
|             existOrdered = isExistsBundleAudioContent || isExistsAudioContent, | ||||
|             orderType = orderType, | ||||
|             remainingTime = remainingTime, | ||||
|   | ||||
| @@ -8,6 +8,7 @@ data class CreateAudioContentRequest( | ||||
|     val themeId: Long = 0, | ||||
|     val isAdult: Boolean = false, | ||||
|     val isGeneratePreview: Boolean = true, | ||||
|     val isOnlyRental: Boolean = false, | ||||
|     val isCommentAvailable: Boolean = false, | ||||
|     val type: AudioContentType = AudioContentType.INDIVIDUAL, | ||||
|     val childIds: List<Long>? = null, | ||||
|   | ||||
| @@ -16,6 +16,7 @@ data class GetAudioContentDetailResponse( | ||||
|     val duration: String, | ||||
|     val isAdult: Boolean, | ||||
|     val isMosaic: Boolean, | ||||
|     val isOnlyRental: Boolean, | ||||
|     val existOrdered: Boolean, | ||||
|     val orderType: OrderType?, | ||||
|     val remainingTime: String?, | ||||
|   | ||||
| @@ -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)) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,13 +1,14 @@ | ||||
| package kr.co.vividnext.sodalive.content.main | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.querydsl.core.annotations.QueryProjection | ||||
|  | ||||
| data class GetAudioContentMainItem @QueryProjection constructor( | ||||
|     val contentId: Long, | ||||
|     val coverImageUrl: String, | ||||
|     val title: String, | ||||
|     val isAdult: Boolean, | ||||
|     val creatorId: Long, | ||||
|     val creatorProfileImageUrl: String, | ||||
|     val creatorNickname: String | ||||
|     @JsonProperty("contentId") val contentId: Long, | ||||
|     @JsonProperty("coverImageUrl") val coverImageUrl: String, | ||||
|     @JsonProperty("title") val title: String, | ||||
|     @JsonProperty("adult") val isAdult: Boolean, | ||||
|     @JsonProperty("creatorId") val creatorId: Long, | ||||
|     @JsonProperty("creatorProfileImageUrl") val creatorProfileImageUrl: String, | ||||
|     @JsonProperty("creatorNickname") val creatorNickname: String | ||||
| ) | ||||
|   | ||||
| @@ -41,8 +41,8 @@ data class Order( | ||||
|     @JoinColumn(name = "content_id", nullable = false) | ||||
|     var audioContent: AudioContent? = null | ||||
|         set(value) { | ||||
|             can = if (type == OrderType.RENTAL) { | ||||
|                 ceil(value?.price!! * 0.6).toInt() | ||||
|             can = if (type == OrderType.RENTAL && !value?.isOnlyRental!!) { | ||||
|                 ceil(value.price * 0.6).toInt() | ||||
|             } else { | ||||
|                 value?.price!! | ||||
|             } | ||||
| @@ -52,7 +52,7 @@ data class Order( | ||||
|     override fun prePersist() { | ||||
|         super.prePersist() | ||||
|         if (type == OrderType.RENTAL) { | ||||
|             endDate = startDate.plusDays(7) | ||||
|             endDate = startDate.plusDays(15) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user