| @@ -60,4 +60,9 @@ class AdminContentCurationController(private val service: AdminContentCurationSe | |||||||
|     fun removeItemInCuration( |     fun removeItemInCuration( | ||||||
|         @RequestBody request: RemoveItemInCurationRequest |         @RequestBody request: RemoveItemInCurationRequest | ||||||
|     ) = ApiResponse.ok(service.removeItemInCuration(request), "큐레이션 아이템을 제거했습니다.") |     ) = ApiResponse.ok(service.removeItemInCuration(request), "큐레이션 아이템을 제거했습니다.") | ||||||
|  |  | ||||||
|  |     @PutMapping("/orders/item") | ||||||
|  |     fun updateItemInCurationOrders( | ||||||
|  |         @RequestBody request: UpdateCurationItemOrdersRequest | ||||||
|  |     ) = ApiResponse.ok(service.updateItemInCurationOrders(request), "수정되었습니다.") | ||||||
| } | } | ||||||
|   | |||||||
| @@ -77,6 +77,7 @@ class AdminContentCurationItemQueryRepositoryImpl( | |||||||
|                 audioContentCuration.id.eq(curationId), |                 audioContentCuration.id.eq(curationId), | ||||||
|                 audioContentCurationItem.isActive.isTrue |                 audioContentCurationItem.isActive.isTrue | ||||||
|             ) |             ) | ||||||
|  |             .orderBy(audioContentCurationItem.orders.asc()) | ||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -99,6 +100,7 @@ class AdminContentCurationItemQueryRepositoryImpl( | |||||||
|                 audioContentCuration.id.eq(curationId), |                 audioContentCuration.id.eq(curationId), | ||||||
|                 audioContentCurationItem.isActive.isTrue |                 audioContentCurationItem.isActive.isTrue | ||||||
|             ) |             ) | ||||||
|  |             .orderBy(audioContentCurationItem.orders.asc()) | ||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -150,4 +150,19 @@ class AdminContentCurationService( | |||||||
|  |  | ||||||
|         audioContentCurationItem?.isActive = false |         audioContentCurationItem?.isActive = false | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Transactional | ||||||
|  |     fun updateItemInCurationOrders(request: UpdateCurationItemOrdersRequest) { | ||||||
|  |         val ids = request.itemIds | ||||||
|  |         for (index in ids.indices) { | ||||||
|  |             val item = contentCurationItemRepository.findByCurationIdAndItemId( | ||||||
|  |                 curationId = request.curationId, | ||||||
|  |                 itemId = ids[index] | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |             if (item != null) { | ||||||
|  |                 item.orders = index + 1 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,6 @@ | |||||||
|  | package kr.co.vividnext.sodalive.admin.content.curation | ||||||
|  |  | ||||||
|  | data class UpdateCurationItemOrdersRequest( | ||||||
|  |     val curationId: Long, | ||||||
|  |     val itemIds: List<Long> | ||||||
|  | ) | ||||||
| @@ -0,0 +1,72 @@ | |||||||
|  | package kr.co.vividnext.sodalive.admin.content.curation.tag | ||||||
|  |  | ||||||
|  | import com.querydsl.jpa.impl.JPAQueryFactory | ||||||
|  | import kr.co.vividnext.sodalive.content.QAudioContent.audioContent | ||||||
|  | import kr.co.vividnext.sodalive.content.main.curation.tag.ContentHashTagCurationItem | ||||||
|  | import kr.co.vividnext.sodalive.content.main.curation.tag.QContentHashTagCuration.contentHashTagCuration | ||||||
|  | import kr.co.vividnext.sodalive.content.main.curation.tag.QContentHashTagCurationItem.contentHashTagCurationItem | ||||||
|  | import org.springframework.beans.factory.annotation.Value | ||||||
|  | import org.springframework.data.jpa.repository.JpaRepository | ||||||
|  |  | ||||||
|  | interface AdminContentHashTagCurationItemRepository : | ||||||
|  |     JpaRepository<ContentHashTagCurationItem, Long>, | ||||||
|  |     AdminContentHashTagCurationItemQueryRepository | ||||||
|  |  | ||||||
|  | interface AdminContentHashTagCurationItemQueryRepository { | ||||||
|  |     fun getContentHashTagCurationItemList(curationId: Long): List<GetAdminHashTagCurationItemResponse> | ||||||
|  |     fun findByCurationIdAndContentId(curationId: Long, contentId: Long?): ContentHashTagCurationItem? | ||||||
|  |     fun findByCurationIdAndItemId(curationId: Long, itemId: Long): ContentHashTagCurationItem? | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class AdminContentHashTagCurationItemQueryRepositoryImpl( | ||||||
|  |     val queryFactory: JPAQueryFactory, | ||||||
|  |  | ||||||
|  |     @Value("\${cloud.aws.cloud-front.host}") | ||||||
|  |     private val imageHost: String | ||||||
|  | ) : AdminContentHashTagCurationItemQueryRepository { | ||||||
|  |     override fun getContentHashTagCurationItemList(curationId: Long): List<GetAdminHashTagCurationItemResponse> { | ||||||
|  |         return queryFactory | ||||||
|  |             .select( | ||||||
|  |                 QGetAdminHashTagCurationItemResponse( | ||||||
|  |                     contentHashTagCurationItem.id, | ||||||
|  |                     audioContent.title, | ||||||
|  |                     audioContent.detail, | ||||||
|  |                     audioContent.coverImage.prepend("/").prepend(imageHost), | ||||||
|  |                     audioContent.member.nickname.coalesce(""), | ||||||
|  |                     audioContent.isAdult | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |             .from(contentHashTagCurationItem) | ||||||
|  |             .innerJoin(contentHashTagCurationItem.curation, contentHashTagCuration) | ||||||
|  |             .innerJoin(contentHashTagCurationItem.content, audioContent) | ||||||
|  |             .where( | ||||||
|  |                 contentHashTagCuration.id.eq(curationId), | ||||||
|  |                 contentHashTagCurationItem.isActive.isTrue, | ||||||
|  |                 audioContent.isActive.isTrue | ||||||
|  |             ) | ||||||
|  |             .orderBy(contentHashTagCurationItem.orders.asc()) | ||||||
|  |             .fetch() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun findByCurationIdAndContentId(curationId: Long, contentId: Long?): ContentHashTagCurationItem? { | ||||||
|  |         return queryFactory | ||||||
|  |             .selectFrom(contentHashTagCurationItem) | ||||||
|  |             .innerJoin(contentHashTagCurationItem.curation, contentHashTagCuration) | ||||||
|  |             .innerJoin(contentHashTagCurationItem.content, audioContent) | ||||||
|  |             .where( | ||||||
|  |                 contentHashTagCuration.id.eq(curationId), | ||||||
|  |                 audioContent.id.eq(contentId) | ||||||
|  |             ) | ||||||
|  |             .fetchFirst() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun findByCurationIdAndItemId(curationId: Long, itemId: Long): ContentHashTagCurationItem? { | ||||||
|  |         return queryFactory.selectFrom(contentHashTagCurationItem) | ||||||
|  |             .innerJoin(contentHashTagCurationItem.curation, contentHashTagCuration) | ||||||
|  |             .where( | ||||||
|  |                 contentHashTagCuration.id.eq(curationId), | ||||||
|  |                 contentHashTagCurationItem.id.eq(itemId) | ||||||
|  |             ) | ||||||
|  |             .fetchFirst() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,63 @@ | |||||||
|  | package kr.co.vividnext.sodalive.admin.content.curation.tag | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.admin.content.curation.AddItemToCurationRequest | ||||||
|  | import kr.co.vividnext.sodalive.admin.content.curation.RemoveItemInCurationRequest | ||||||
|  | import kr.co.vividnext.sodalive.admin.content.curation.UpdateCurationItemOrdersRequest | ||||||
|  | import kr.co.vividnext.sodalive.common.ApiResponse | ||||||
|  | import org.springframework.security.access.prepost.PreAuthorize | ||||||
|  | import org.springframework.web.bind.annotation.GetMapping | ||||||
|  | import org.springframework.web.bind.annotation.PostMapping | ||||||
|  | import org.springframework.web.bind.annotation.PutMapping | ||||||
|  | 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 | ||||||
|  | @RequestMapping("/admin/audio-content/tag/curation") | ||||||
|  | @PreAuthorize("hasRole('ADMIN')") | ||||||
|  | class AdminHashTagCurationController(private val service: AdminHashTagCurationService) { | ||||||
|  |     @GetMapping | ||||||
|  |     fun getContentHashTagCurationList() = ApiResponse.ok(service.getContentHashTagCurationList()) | ||||||
|  |  | ||||||
|  |     @PostMapping | ||||||
|  |     fun createContentHashTagCuration( | ||||||
|  |         @RequestBody request: CreateContentHashTagCurationRequest | ||||||
|  |     ) = ApiResponse.ok(service.createContentHashTagCuration(request)) | ||||||
|  |  | ||||||
|  |     @PutMapping | ||||||
|  |     fun updateContentHashTagCuration( | ||||||
|  |         @RequestBody request: UpdateContentHashTagCurationRequest | ||||||
|  |     ) = ApiResponse.ok(service.updateContentHashTagCuration(request)) | ||||||
|  |  | ||||||
|  |     @PutMapping("/orders") | ||||||
|  |     fun updateContentHashTagCurationOrders( | ||||||
|  |         @RequestBody request: UpdateContentHashTagCurationOrderRequest | ||||||
|  |     ) = ApiResponse.ok(service.updateContentHashTagCurationOrders(request.ids), "수정되었습니다.") | ||||||
|  |  | ||||||
|  |     @GetMapping("/items") | ||||||
|  |     fun getHashTagCurationItemList( | ||||||
|  |         @RequestParam curationId: Long | ||||||
|  |     ) = ApiResponse.ok(service.getHashTagCurationItemList(curationId = curationId)) | ||||||
|  |  | ||||||
|  |     @GetMapping("/search/content") | ||||||
|  |     fun searchHashTagCurationContentItem( | ||||||
|  |         @RequestParam curationId: Long, | ||||||
|  |         @RequestParam searchWord: String | ||||||
|  |     ) = ApiResponse.ok(service.searchHashTagCurationContentItem(curationId, searchWord)) | ||||||
|  |  | ||||||
|  |     @PostMapping("/add/item") | ||||||
|  |     fun addItemToHashTagCuration( | ||||||
|  |         @RequestBody request: AddItemToCurationRequest | ||||||
|  |     ) = ApiResponse.ok(service.addItemToHashTagCuration(request), "큐레이션 아이템을 등록했습니다.") | ||||||
|  |  | ||||||
|  |     @PutMapping("/remove/item") | ||||||
|  |     fun removeItemInHashTagCuration( | ||||||
|  |         @RequestBody request: RemoveItemInCurationRequest | ||||||
|  |     ) = ApiResponse.ok(service.removeItemInHashTagCuration(request), "큐레이션 아이템을 제거했습니다.") | ||||||
|  |  | ||||||
|  |     @PutMapping("/orders/item") | ||||||
|  |     fun updateItemInHashTagCurationOrders( | ||||||
|  |         @RequestBody request: UpdateCurationItemOrdersRequest | ||||||
|  |     ) = ApiResponse.ok(service.updateItemInHashTagCurationOrders(request), "수정되었습니다.") | ||||||
|  | } | ||||||
| @@ -0,0 +1,84 @@ | |||||||
|  | package kr.co.vividnext.sodalive.admin.content.curation.tag | ||||||
|  |  | ||||||
|  | import com.querydsl.jpa.impl.JPAQueryFactory | ||||||
|  | import kr.co.vividnext.sodalive.admin.content.curation.QSearchCurationItemResponse | ||||||
|  | import kr.co.vividnext.sodalive.admin.content.curation.SearchCurationItemResponse | ||||||
|  | import kr.co.vividnext.sodalive.content.QAudioContent.audioContent | ||||||
|  | import kr.co.vividnext.sodalive.content.main.curation.tag.ContentHashTagCuration | ||||||
|  | import kr.co.vividnext.sodalive.content.main.curation.tag.QContentHashTagCuration.contentHashTagCuration | ||||||
|  | import kr.co.vividnext.sodalive.content.main.curation.tag.QContentHashTagCurationItem.contentHashTagCurationItem | ||||||
|  | import org.springframework.beans.factory.annotation.Value | ||||||
|  | import org.springframework.data.jpa.repository.JpaRepository | ||||||
|  | import org.springframework.stereotype.Repository | ||||||
|  |  | ||||||
|  | interface AdminHashTagCurationRepository : | ||||||
|  |     JpaRepository<ContentHashTagCuration, Long>, | ||||||
|  |     AdminHashTagCurationQueryRepository | ||||||
|  |  | ||||||
|  | interface AdminHashTagCurationQueryRepository { | ||||||
|  |     fun getContentHashTagCurationList(): List<GetAdminContentHashTagCurationResponse> | ||||||
|  |     fun searchHashTagCurationContentItem(curationId: Long, searchWord: String): List<SearchCurationItemResponse> | ||||||
|  |     fun isExistsTag(tag: String): Boolean | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Repository | ||||||
|  | class AdminHashTagCurationQueryRepositoryImpl( | ||||||
|  |     private val queryFactory: JPAQueryFactory, | ||||||
|  |  | ||||||
|  |     @Value("\${cloud.aws.cloud-front.host}") | ||||||
|  |     private val imageHost: String | ||||||
|  | ) : AdminHashTagCurationQueryRepository { | ||||||
|  |     override fun getContentHashTagCurationList(): List<GetAdminContentHashTagCurationResponse> { | ||||||
|  |         return queryFactory | ||||||
|  |             .select( | ||||||
|  |                 QGetAdminContentHashTagCurationResponse( | ||||||
|  |                     contentHashTagCuration.id, | ||||||
|  |                     contentHashTagCuration.tag, | ||||||
|  |                     contentHashTagCuration.isAdult | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |             .from(contentHashTagCuration) | ||||||
|  |             .where(contentHashTagCuration.isActive.isTrue) | ||||||
|  |             .orderBy(contentHashTagCuration.orders.asc()) | ||||||
|  |             .fetch() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun searchHashTagCurationContentItem( | ||||||
|  |         curationId: Long, | ||||||
|  |         searchWord: String | ||||||
|  |     ): List<SearchCurationItemResponse> { | ||||||
|  |         return queryFactory | ||||||
|  |             .select( | ||||||
|  |                 QSearchCurationItemResponse( | ||||||
|  |                     audioContent.id, | ||||||
|  |                     audioContent.title, | ||||||
|  |                     audioContent.coverImage.prepend("/").prepend(imageHost) | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |             .from(audioContent) | ||||||
|  |             .leftJoin(contentHashTagCurationItem) | ||||||
|  |             .on( | ||||||
|  |                 audioContent.id.eq(contentHashTagCurationItem.content.id) | ||||||
|  |                     .and(contentHashTagCurationItem.curation.id.eq(curationId)) | ||||||
|  |             ) | ||||||
|  |             .where( | ||||||
|  |                 audioContent.duration.isNotNull | ||||||
|  |                     .and(audioContent.member.isNotNull) | ||||||
|  |                     .and(audioContent.isActive.isTrue) | ||||||
|  |                     .and(audioContent.title.contains(searchWord)) | ||||||
|  |                     .and(contentHashTagCurationItem.id.isNull) | ||||||
|  |             ) | ||||||
|  |             .fetch() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun isExistsTag(tag: String): Boolean { | ||||||
|  |         return queryFactory | ||||||
|  |             .select(contentHashTagCuration.id) | ||||||
|  |             .from(contentHashTagCuration) | ||||||
|  |             .where( | ||||||
|  |                 contentHashTagCuration.tag.eq(tag), | ||||||
|  |                 contentHashTagCuration.isActive.isTrue | ||||||
|  |             ) | ||||||
|  |             .fetch().isNotEmpty() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,133 @@ | |||||||
|  | package kr.co.vividnext.sodalive.admin.content.curation.tag | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.admin.content.curation.AddItemToCurationRequest | ||||||
|  | import kr.co.vividnext.sodalive.admin.content.curation.RemoveItemInCurationRequest | ||||||
|  | import kr.co.vividnext.sodalive.admin.content.curation.SearchCurationItemResponse | ||||||
|  | import kr.co.vividnext.sodalive.admin.content.curation.UpdateCurationItemOrdersRequest | ||||||
|  | import kr.co.vividnext.sodalive.common.SodaException | ||||||
|  | import kr.co.vividnext.sodalive.content.AudioContentRepository | ||||||
|  | import kr.co.vividnext.sodalive.content.main.curation.tag.ContentHashTagCuration | ||||||
|  | import kr.co.vividnext.sodalive.content.main.curation.tag.ContentHashTagCurationItem | ||||||
|  | import org.springframework.data.repository.findByIdOrNull | ||||||
|  | import org.springframework.stereotype.Service | ||||||
|  | import org.springframework.transaction.annotation.Transactional | ||||||
|  |  | ||||||
|  | @Service | ||||||
|  | class AdminHashTagCurationService( | ||||||
|  |     private val repository: AdminHashTagCurationRepository, | ||||||
|  |     private val itemRepository: AdminContentHashTagCurationItemRepository, | ||||||
|  |     private val audioContentRepository: AudioContentRepository | ||||||
|  | ) { | ||||||
|  |     @Transactional | ||||||
|  |     fun createContentHashTagCuration(request: CreateContentHashTagCurationRequest) { | ||||||
|  |         var tag = request.tag.trim() | ||||||
|  |         if (!tag.startsWith("#")) { | ||||||
|  |             tag = "#$tag" | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         val isExists = repository.isExistsTag(tag = tag) | ||||||
|  |  | ||||||
|  |         if (isExists) { | ||||||
|  |             throw SodaException("이미 등록된 태그 입니다.") | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         repository.save( | ||||||
|  |             ContentHashTagCuration( | ||||||
|  |                 tag = tag, | ||||||
|  |                 isAdult = request.isAdult | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Transactional | ||||||
|  |     fun updateContentHashTagCuration(request: UpdateContentHashTagCurationRequest) { | ||||||
|  |         val hashTagCuration = repository.findByIdOrNull(id = request.id) | ||||||
|  |             ?: throw SodaException("잘못된 요청입니다.") | ||||||
|  |  | ||||||
|  |         if (request.tag != null) { | ||||||
|  |             var tag = request.tag.trim() | ||||||
|  |             if (!tag.startsWith("#")) { | ||||||
|  |                 tag = "#$tag" | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             hashTagCuration.tag = tag | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (request.isAdult != null) { | ||||||
|  |             hashTagCuration.isAdult = request.isAdult | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (request.isActive != null) { | ||||||
|  |             hashTagCuration.isActive = request.isActive | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Transactional | ||||||
|  |     fun updateContentHashTagCurationOrders(ids: List<Long>) { | ||||||
|  |         for (index in ids.indices) { | ||||||
|  |             val contentHashTagCuration = repository.findByIdOrNull(ids[index]) | ||||||
|  |  | ||||||
|  |             if (contentHashTagCuration != null) { | ||||||
|  |                 contentHashTagCuration.orders = index + 1 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun getContentHashTagCurationList(): List<GetAdminContentHashTagCurationResponse> { | ||||||
|  |         return repository.getContentHashTagCurationList() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun getHashTagCurationItemList(curationId: Long): List<GetAdminHashTagCurationItemResponse> { | ||||||
|  |         return itemRepository.getContentHashTagCurationItemList(curationId) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun searchHashTagCurationContentItem(curationId: Long, searchWord: String): List<SearchCurationItemResponse> { | ||||||
|  |         return repository.searchHashTagCurationContentItem(curationId, searchWord) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Transactional | ||||||
|  |     fun addItemToHashTagCuration(request: AddItemToCurationRequest) { | ||||||
|  |         val curation = repository.findByIdOrNull(id = request.curationId) | ||||||
|  |             ?: throw SodaException("잘못된 요청입니다.") | ||||||
|  |  | ||||||
|  |         request.itemIdList.forEach { contentId -> | ||||||
|  |             val audioContent = audioContentRepository.findByIdAndActive(contentId) | ||||||
|  |  | ||||||
|  |             if (audioContent != null) { | ||||||
|  |                 val item = itemRepository.findByCurationIdAndContentId( | ||||||
|  |                     curationId = request.curationId, | ||||||
|  |                     contentId = audioContent.id | ||||||
|  |                 ) ?: ContentHashTagCurationItem() | ||||||
|  |                 item.curation = curation | ||||||
|  |                 item.content = audioContent | ||||||
|  |                 item.isActive = true | ||||||
|  |                 itemRepository.save(item) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Transactional | ||||||
|  |     fun removeItemInHashTagCuration(request: RemoveItemInCurationRequest) { | ||||||
|  |         val item = itemRepository.findByCurationIdAndItemId( | ||||||
|  |             curationId = request.curationId, | ||||||
|  |             itemId = request.itemId | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         item?.isActive = false | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Transactional | ||||||
|  |     fun updateItemInHashTagCurationOrders(request: UpdateCurationItemOrdersRequest) { | ||||||
|  |         val ids = request.itemIds | ||||||
|  |         for (index in ids.indices) { | ||||||
|  |             val item = itemRepository.findByCurationIdAndItemId( | ||||||
|  |                 curationId = request.curationId, | ||||||
|  |                 itemId = ids[index] | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |             if (item != null) { | ||||||
|  |                 item.orders = index + 1 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,17 @@ | |||||||
|  | package kr.co.vividnext.sodalive.admin.content.curation.tag | ||||||
|  |  | ||||||
|  | data class CreateContentHashTagCurationRequest( | ||||||
|  |     val tag: String, | ||||||
|  |     val isAdult: Boolean | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | data class UpdateContentHashTagCurationRequest( | ||||||
|  |     val id: Long, | ||||||
|  |     val tag: String?, | ||||||
|  |     val isAdult: Boolean?, | ||||||
|  |     val isActive: Boolean? | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | data class UpdateContentHashTagCurationOrderRequest( | ||||||
|  |     val ids: List<Long> | ||||||
|  | ) | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | package kr.co.vividnext.sodalive.admin.content.curation.tag | ||||||
|  |  | ||||||
|  | import com.querydsl.core.annotations.QueryProjection | ||||||
|  |  | ||||||
|  | data class GetAdminContentHashTagCurationResponse @QueryProjection constructor( | ||||||
|  |     val id: Long, | ||||||
|  |     val tag: String, | ||||||
|  |     val isAdult: Boolean | ||||||
|  | ) | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | package kr.co.vividnext.sodalive.admin.content.curation.tag | ||||||
|  |  | ||||||
|  | import com.querydsl.core.annotations.QueryProjection | ||||||
|  |  | ||||||
|  | data class GetAdminHashTagCurationItemResponse @QueryProjection constructor( | ||||||
|  |     val id: Long, | ||||||
|  |     val title: String, | ||||||
|  |     val desc: String, | ||||||
|  |     val coverImageUrl: String, | ||||||
|  |     val creatorNickname: String, | ||||||
|  |     val isAdult: Boolean | ||||||
|  | ) | ||||||
| @@ -32,6 +32,7 @@ data class AudioContent( | |||||||
|     var title: String, |     var title: String, | ||||||
|     @Column(columnDefinition = "TEXT", nullable = false) |     @Column(columnDefinition = "TEXT", nullable = false) | ||||||
|     var detail: String, |     var detail: String, | ||||||
|  |     var playCount: Long = 0, | ||||||
|     var price: Int = 0, |     var price: Int = 0, | ||||||
|     var releaseDate: LocalDateTime? = null, |     var releaseDate: LocalDateTime? = null, | ||||||
|     val limited: Int? = null, |     val limited: Int? = null, | ||||||
|   | |||||||
| @@ -81,6 +81,12 @@ interface AudioContentQueryRepository { | |||||||
|         limit: Long = 20 |         limit: Long = 20 | ||||||
|     ): List<GetAudioContentMainItem> |     ): List<GetAudioContentMainItem> | ||||||
|  |  | ||||||
|  |     fun totalAlarmCountByTheme( | ||||||
|  |         memberId: Long, | ||||||
|  |         theme: List<String>, | ||||||
|  |         isAdult: Boolean = false | ||||||
|  |     ): Int | ||||||
|  |  | ||||||
|     fun totalCountByTheme( |     fun totalCountByTheme( | ||||||
|         memberId: Long, |         memberId: Long, | ||||||
|         theme: String = "", |         theme: String = "", | ||||||
| @@ -486,6 +492,39 @@ class AudioContentQueryRepositoryImpl( | |||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     override fun totalAlarmCountByTheme(memberId: Long, theme: List<String>, isAdult: Boolean): Int { | ||||||
|  |         val blockMemberCondition = blockMember.member.id.eq(member.id) | ||||||
|  |             .and(blockMember.isActive.isTrue) | ||||||
|  |             .and(blockMember.blockedMember.id.eq(memberId)) | ||||||
|  |  | ||||||
|  |         var where = audioContent.isActive.isTrue | ||||||
|  |             .and(audioContent.duration.isNotNull) | ||||||
|  |             .and( | ||||||
|  |                 audioContent.releaseDate.isNull | ||||||
|  |                     .or(audioContent.releaseDate.loe(LocalDateTime.now())) | ||||||
|  |                     .or(audioContent.member.id.eq(memberId)) | ||||||
|  |             ) | ||||||
|  |             .and(blockMember.id.isNull) | ||||||
|  |  | ||||||
|  |         if (!isAdult) { | ||||||
|  |             where = where.and(audioContent.isAdult.isFalse) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (theme.isNotEmpty()) { | ||||||
|  |             where = where.and(audioContentTheme.theme.`in`(theme)) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return queryFactory | ||||||
|  |             .select(audioContent.id) | ||||||
|  |             .from(audioContent) | ||||||
|  |             .innerJoin(audioContent.member, member) | ||||||
|  |             .innerJoin(audioContent.theme, audioContentTheme) | ||||||
|  |             .leftJoin(blockMember).on(blockMemberCondition) | ||||||
|  |             .where(where) | ||||||
|  |             .fetch() | ||||||
|  |             .size | ||||||
|  |     } | ||||||
|  |  | ||||||
|     override fun totalCountByTheme(memberId: Long, theme: String, isAdult: Boolean, contentType: ContentType): Int { |     override fun totalCountByTheme(memberId: Long, theme: String, isAdult: Boolean, contentType: ContentType): Int { | ||||||
|         var where = audioContent.isActive.isTrue |         var where = audioContent.isActive.isTrue | ||||||
|             .and(audioContent.duration.isNotNull) |             .and(audioContent.duration.isNotNull) | ||||||
| @@ -659,6 +698,7 @@ class AudioContentQueryRepositoryImpl( | |||||||
|  |  | ||||||
|     override fun getAudioContentMainBannerList(isAdult: Boolean): List<AudioContentBanner> { |     override fun getAudioContentMainBannerList(isAdult: Boolean): List<AudioContentBanner> { | ||||||
|         var where = audioContentBanner.isActive.isTrue |         var where = audioContentBanner.isActive.isTrue | ||||||
|  |             .and(audioContentBanner.tab.isNull) | ||||||
|  |  | ||||||
|         if (!isAdult) { |         if (!isAdult) { | ||||||
|             where = where.and(audioContentBanner.isAdult.isFalse) |             where = where.and(audioContentBanner.isAdult.isFalse) | ||||||
| @@ -666,6 +706,7 @@ class AudioContentQueryRepositoryImpl( | |||||||
|  |  | ||||||
|         return queryFactory |         return queryFactory | ||||||
|             .selectFrom(audioContentBanner) |             .selectFrom(audioContentBanner) | ||||||
|  |             .leftJoin(audioContentBanner.tab, audioContentMainTab) | ||||||
|             .leftJoin(audioContentBanner.event, event) |             .leftJoin(audioContentBanner.event, event) | ||||||
|             .leftJoin(audioContentBanner.creator, member) |             .leftJoin(audioContentBanner.creator, member) | ||||||
|             .where(where) |             .where(where) | ||||||
|   | |||||||
| @@ -0,0 +1,30 @@ | |||||||
|  | package kr.co.vividnext.sodalive.content.main.curation.tag | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.common.BaseEntity | ||||||
|  | import kr.co.vividnext.sodalive.content.AudioContent | ||||||
|  | import javax.persistence.Entity | ||||||
|  | import javax.persistence.FetchType | ||||||
|  | import javax.persistence.JoinColumn | ||||||
|  | import javax.persistence.ManyToOne | ||||||
|  |  | ||||||
|  | @Entity | ||||||
|  | data class ContentHashTagCuration( | ||||||
|  |     var tag: String, | ||||||
|  |     var orders: Int = 1, | ||||||
|  |     var isAdult: Boolean = false, | ||||||
|  |     var isActive: Boolean = true | ||||||
|  | ) : BaseEntity() | ||||||
|  |  | ||||||
|  | @Entity | ||||||
|  | data class ContentHashTagCurationItem( | ||||||
|  |     var orders: Int = 1, | ||||||
|  |     var isActive: Boolean = true | ||||||
|  | ) : BaseEntity() { | ||||||
|  |     @ManyToOne(fetch = FetchType.LAZY) | ||||||
|  |     @JoinColumn(name = "curation_id", nullable = false) | ||||||
|  |     var curation: ContentHashTagCuration? = null | ||||||
|  |  | ||||||
|  |     @ManyToOne(fetch = FetchType.LAZY) | ||||||
|  |     @JoinColumn(name = "content_id", nullable = false) | ||||||
|  |     var content: AudioContent? = null | ||||||
|  | } | ||||||
| @@ -31,6 +31,7 @@ class AudioContentMainTabRepository( | |||||||
|         val where = member.isActive.isTrue |         val where = member.isActive.isTrue | ||||||
|             .and(member.role.eq(MemberRole.CREATOR)) |             .and(member.role.eq(MemberRole.CREATOR)) | ||||||
|             .and(audioContent.isActive.isTrue) |             .and(audioContent.isActive.isTrue) | ||||||
|  |             .and(audioContent.price.gt(0)) | ||||||
|             .and(audioContent.duration.isNotNull) |             .and(audioContent.duration.isNotNull) | ||||||
|             .and(audioContent.limited.isNull) |             .and(audioContent.limited.isNull) | ||||||
|             .and(audioContentTheme.isActive.isTrue) |             .and(audioContentTheme.isActive.isTrue) | ||||||
| @@ -59,4 +60,39 @@ class AudioContentMainTabRepository( | |||||||
|             .limit(20) |             .limit(20) | ||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fun findCreatorWithHasFreeContent(memberId: Long, minCount: Int): List<ContentCreatorResponse> { | ||||||
|  |         val blockMemberCondition = blockMember.member.id.eq(member.id) | ||||||
|  |             .and(blockMember.isActive.isTrue) | ||||||
|  |             .and(blockMember.blockedMember.id.eq(memberId)) | ||||||
|  |  | ||||||
|  |         val where = member.isActive.isTrue | ||||||
|  |             .and(member.role.eq(MemberRole.CREATOR)) | ||||||
|  |             .and(audioContent.isActive.isTrue) | ||||||
|  |             .and(audioContent.price.loe(0)) | ||||||
|  |             .and(audioContent.duration.isNotNull) | ||||||
|  |             .and(audioContent.limited.isNull) | ||||||
|  |             .and(blockMember.id.isNull) | ||||||
|  |  | ||||||
|  |         return queryFactory | ||||||
|  |             .select( | ||||||
|  |                 QContentCreatorResponse( | ||||||
|  |                     member.id, | ||||||
|  |                     member.nickname, | ||||||
|  |                     member.profileImage.prepend("/").prepend(imageHost) | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |             .from(member) | ||||||
|  |             .innerJoin(audioContent).on(member.id.eq(audioContent.member.id)) | ||||||
|  |             .leftJoin(blockMember).on(blockMemberCondition) | ||||||
|  |             .where(where) | ||||||
|  |             .groupBy(member.id) | ||||||
|  |             .having(audioContent.id.count().goe(minCount)) | ||||||
|  |             .orderBy( | ||||||
|  |                 Expressions.numberTemplate(Double::class.java, "function('rand')").asc() | ||||||
|  |             ) | ||||||
|  |             .offset(0) | ||||||
|  |             .limit(20) | ||||||
|  |             .fetch() | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,8 +0,0 @@ | |||||||
| package kr.co.vividnext.sodalive.content.main.tab |  | ||||||
|  |  | ||||||
| import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem |  | ||||||
|  |  | ||||||
| data class GetPopularContentByCreatorResponse( |  | ||||||
|     val salesRankContentList: List<GetAudioContentRankingItem>, |  | ||||||
|     val salesCountRankContentList: List<GetAudioContentRankingItem> |  | ||||||
| ) |  | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| package kr.co.vividnext.sodalive.content.main.tab.alarm | package kr.co.vividnext.sodalive.content.main.tab.alarm | ||||||
|  |  | ||||||
| import kr.co.vividnext.sodalive.content.AudioContentRepository | import kr.co.vividnext.sodalive.content.AudioContentRepository | ||||||
| import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | import kr.co.vividnext.sodalive.content.main.GetNewContentAllResponse | ||||||
| import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService | import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService | ||||||
| import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository | import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse | import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse | ||||||
| @@ -86,7 +86,7 @@ class AudioContentMainTabAlarmService( | |||||||
|         member: Member, |         member: Member, | ||||||
|         offset: Long, |         offset: Long, | ||||||
|         limit: Long |         limit: Long | ||||||
|     ): List<GetAudioContentMainItem> { |     ): GetNewContentAllResponse { | ||||||
|         val alarmThemeList = if (theme.isNotBlank()) { |         val alarmThemeList = if (theme.isNotBlank()) { | ||||||
|             listOf(theme) |             listOf(theme) | ||||||
|         } else { |         } else { | ||||||
| @@ -96,12 +96,20 @@ class AudioContentMainTabAlarmService( | |||||||
|         val memberId = member.id!! |         val memberId = member.id!! | ||||||
|         val isAdult = member.auth != null |         val isAdult = member.auth != null | ||||||
|  |  | ||||||
|         return contentRepository.findAlarmContentByTheme( |         val totalCount = contentRepository.totalAlarmCountByTheme( | ||||||
|  |             memberId = memberId, | ||||||
|  |             theme = alarmThemeList, | ||||||
|  |             isAdult = isAdult | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         val items = contentRepository.findAlarmContentByTheme( | ||||||
|             memberId = memberId, |             memberId = memberId, | ||||||
|             theme = alarmThemeList, |             theme = alarmThemeList, | ||||||
|             isAdult = isAdult, |             isAdult = isAdult, | ||||||
|             offset = offset, |             offset = offset, | ||||||
|             limit = limit |             limit = limit | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |         return GetNewContentAllResponse(totalCount, items) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,11 +2,11 @@ package kr.co.vividnext.sodalive.content.main.tab.asmr | |||||||
|  |  | ||||||
| import kr.co.vividnext.sodalive.content.AudioContentRepository | import kr.co.vividnext.sodalive.content.AudioContentRepository | ||||||
| import kr.co.vividnext.sodalive.content.ContentType | import kr.co.vividnext.sodalive.content.ContentType | ||||||
|  | import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem | ||||||
| import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService | import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService | ||||||
| import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository | import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTabRepository | import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTabRepository | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse | import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.GetPopularContentByCreatorResponse |  | ||||||
| import kr.co.vividnext.sodalive.event.EventService | import kr.co.vividnext.sodalive.event.EventService | ||||||
| import kr.co.vividnext.sodalive.member.Member | import kr.co.vividnext.sodalive.member.Member | ||||||
| import kr.co.vividnext.sodalive.rank.RankingService | import kr.co.vividnext.sodalive.rank.RankingService | ||||||
| @@ -68,18 +68,8 @@ class AudioContentMainTabAsmrService( | |||||||
|             minCount = 4 |             minCount = 4 | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         val salesRankContentList = if (creatorList.isNotEmpty()) { |  | ||||||
|             rankingService.fetchCreatorContentBySalesTop2( |  | ||||||
|                 creatorId = creatorList[0].creatorId, |  | ||||||
|                 isAdult = isAdult, |  | ||||||
|                 theme = theme |  | ||||||
|             ) |  | ||||||
|         } else { |  | ||||||
|             emptyList() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         val salesCountRankContentList = if (creatorList.isNotEmpty()) { |         val salesCountRankContentList = if (creatorList.isNotEmpty()) { | ||||||
|             rankingService.fetchCreatorContentBySalesCountTop2( |             rankingService.fetchCreatorContentBySalesCountTop4( | ||||||
|                 creatorId = creatorList[0].creatorId, |                 creatorId = creatorList[0].creatorId, | ||||||
|                 isAdult = isAdult, |                 isAdult = isAdult, | ||||||
|                 theme = theme |                 theme = theme | ||||||
| @@ -107,30 +97,17 @@ class AudioContentMainTabAsmrService( | |||||||
|             newAsmrContentList = newAsmrContentList, |             newAsmrContentList = newAsmrContentList, | ||||||
|             rankAsmrContentList = rankAsmrContentList, |             rankAsmrContentList = rankAsmrContentList, | ||||||
|             creatorList = creatorList, |             creatorList = creatorList, | ||||||
|             salesRankContentList = salesRankContentList, |  | ||||||
|             salesCountRankContentList = salesCountRankContentList, |             salesCountRankContentList = salesCountRankContentList, | ||||||
|             eventBannerList = eventBannerList, |             eventBannerList = eventBannerList, | ||||||
|             curationList = curationList |             curationList = curationList | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): GetPopularContentByCreatorResponse { |     fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> { | ||||||
|         val theme = "ASMR" |         return rankingService.fetchCreatorContentBySalesCountTop4( | ||||||
|         val salesRankContentList = rankingService.fetchCreatorContentBySalesTop2( |  | ||||||
|             creatorId = creatorId, |             creatorId = creatorId, | ||||||
|             isAdult = isAdult, |             isAdult = isAdult, | ||||||
|             theme = theme |             theme = "ASMR" | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         val salesCountRankContentList = rankingService.fetchCreatorContentBySalesCountTop2( |  | ||||||
|             creatorId = creatorId, |  | ||||||
|             isAdult = isAdult, |  | ||||||
|             theme = theme |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         return GetPopularContentByCreatorResponse( |  | ||||||
|             salesRankContentList = salesRankContentList, |  | ||||||
|             salesCountRankContentList = salesCountRankContentList |  | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,7 +13,6 @@ data class GetContentMainTabAsmrResponse( | |||||||
|     val newAsmrContentList: List<GetAudioContentMainItem>, |     val newAsmrContentList: List<GetAudioContentMainItem>, | ||||||
|     val rankAsmrContentList: List<GetAudioContentRankingItem>, |     val rankAsmrContentList: List<GetAudioContentRankingItem>, | ||||||
|     val creatorList: List<ContentCreatorResponse>, |     val creatorList: List<ContentCreatorResponse>, | ||||||
|     val salesRankContentList: List<GetAudioContentRankingItem>, |  | ||||||
|     val salesCountRankContentList: List<GetAudioContentRankingItem>, |     val salesCountRankContentList: List<GetAudioContentRankingItem>, | ||||||
|     val eventBannerList: GetEventResponse, |     val eventBannerList: GetEventResponse, | ||||||
|     val curationList: List<GetContentCurationResponse> |     val curationList: List<GetContentCurationResponse> | ||||||
|   | |||||||
| @@ -82,4 +82,15 @@ class AudioContentMainTabContentController(private val service: AudioContentMain | |||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @GetMapping("/recommend-content-by-tag") | ||||||
|  |     fun getRecommendedContentByTag( | ||||||
|  |         @RequestParam tag: String, | ||||||
|  |         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? | ||||||
|  |     ) = run { | ||||||
|  |         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||||
|  |         ApiResponse.ok( | ||||||
|  |             service.getRecommendedContentByTag(memberId = member.id!!, tag = tag) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,7 +5,8 @@ import kr.co.vividnext.sodalive.content.ContentType | |||||||
| import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | ||||||
| import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem | import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem | ||||||
| import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService | import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.GetPopularContentByCreatorResponse | import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository | ||||||
|  | import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse | ||||||
| import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository | import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository | ||||||
| import kr.co.vividnext.sodalive.event.EventService | import kr.co.vividnext.sodalive.event.EventService | ||||||
| import kr.co.vividnext.sodalive.member.Member | import kr.co.vividnext.sodalive.member.Member | ||||||
| @@ -19,7 +20,9 @@ class AudioContentMainTabContentService( | |||||||
|     private val audioContentRepository: AudioContentRepository, |     private val audioContentRepository: AudioContentRepository, | ||||||
|     private val audioContentThemeRepository: AudioContentThemeQueryRepository, |     private val audioContentThemeRepository: AudioContentThemeQueryRepository, | ||||||
|     private val rankingService: RankingService, |     private val rankingService: RankingService, | ||||||
|     private val eventService: EventService |     private val eventService: EventService, | ||||||
|  |     private val tagCurationService: ContentMainTabTagCurationService, | ||||||
|  |     private val curationRepository: AudioContentCurationQueryRepository | ||||||
| ) { | ) { | ||||||
|     fun fetchData( |     fun fetchData( | ||||||
|         isAdultContentVisible: Boolean, |         isAdultContentVisible: Boolean, | ||||||
| @@ -28,10 +31,11 @@ class AudioContentMainTabContentService( | |||||||
|     ): GetContentMainTabContentResponse { |     ): GetContentMainTabContentResponse { | ||||||
|         val memberId = member.id!! |         val memberId = member.id!! | ||||||
|         val isAdult = member.auth != null |         val isAdult = member.auth != null | ||||||
|  |         val tabId = 3L | ||||||
|  |  | ||||||
|         // 단편 배너 |         // 단편 배너 | ||||||
|         val contentBannerList = bannerService.getBannerList( |         val contentBannerList = bannerService.getBannerList( | ||||||
|             tabId = 3, |             tabId = tabId, | ||||||
|             memberId = memberId, |             memberId = memberId, | ||||||
|             isAdult = isAdult |             isAdult = isAdult | ||||||
|         ) |         ) | ||||||
| @@ -69,14 +73,14 @@ class AudioContentMainTabContentService( | |||||||
|         // 이벤트 배너 |         // 이벤트 배너 | ||||||
|         val eventBannerList = eventService.getEventList(isAdult = isAdult) |         val eventBannerList = eventService.getEventList(isAdult = isAdult) | ||||||
|  |  | ||||||
|         val contentRankCreatorList = rankingService.fetchCreatorByContentRevenueRankTop20( |         val contentRankCreatorList = rankingService.fetchCreatorBySellContentCountRankTop20( | ||||||
|             memberId = member.id!!, |             memberId = member.id!!, | ||||||
|             startDate = dailyRankingStartDate.minusDays(1), |             startDate = dailyRankingStartDate.minusDays(1), | ||||||
|             endDate = dailyRankingEndDate |             endDate = dailyRankingEndDate | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         val salesRankContentList = if (contentRankCreatorList.isNotEmpty()) { |         val salesCountRankContentList = if (contentRankCreatorList.isNotEmpty()) { | ||||||
|             rankingService.fetchCreatorContentBySalesTop2( |             rankingService.fetchCreatorContentBySalesCountTop4( | ||||||
|                 creatorId = contentRankCreatorList[0].creatorId, |                 creatorId = contentRankCreatorList[0].creatorId, | ||||||
|                 isAdult = member.auth != null |                 isAdult = member.auth != null | ||||||
|             ) |             ) | ||||||
| @@ -84,15 +88,30 @@ class AudioContentMainTabContentService( | |||||||
|             emptyList() |             emptyList() | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         val salesCountRankContentList = if (contentRankCreatorList.isNotEmpty()) { |         val tagList = if (isAdult) { | ||||||
|             rankingService.fetchCreatorContentBySalesCountTop2( |             tagCurationService.getTagList(isAdult = isAdult) | ||||||
|                 creatorId = contentRankCreatorList[0].creatorId, |  | ||||||
|                 isAdult = member.auth != null |  | ||||||
|             ) |  | ||||||
|         } else { |         } else { | ||||||
|             emptyList() |             emptyList() | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         val tagCurationContentList = if (tagList.isNotEmpty()) { | ||||||
|  |             tagCurationService.getTagCurationContentList(memberId = memberId, tag = tagList[0]) | ||||||
|  |         } else { | ||||||
|  |             emptyList() | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         val curationList = curationRepository.findByContentMainTabId(tabId = tabId, isAdult = isAdult) | ||||||
|  |             .map { | ||||||
|  |                 GetContentCurationResponse( | ||||||
|  |                     title = it.title, | ||||||
|  |                     items = audioContentRepository.findAudioContentByCurationIdV2( | ||||||
|  |                         curationId = it.id!!, | ||||||
|  |                         memberId = memberId, | ||||||
|  |                         isAdult = isAdult | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |  | ||||||
|         return GetContentMainTabContentResponse( |         return GetContentMainTabContentResponse( | ||||||
|             bannerList = contentBannerList, |             bannerList = contentBannerList, | ||||||
|             contentThemeList = themeOfContentList, |             contentThemeList = themeOfContentList, | ||||||
| @@ -100,9 +119,11 @@ class AudioContentMainTabContentService( | |||||||
|             rankSortTypeList = listOf("매출", "댓글", "좋아요"), |             rankSortTypeList = listOf("매출", "댓글", "좋아요"), | ||||||
|             rankContentList = rankContentList, |             rankContentList = rankContentList, | ||||||
|             contentRankCreatorList = contentRankCreatorList, |             contentRankCreatorList = contentRankCreatorList, | ||||||
|             salesRankContentList = salesRankContentList, |  | ||||||
|             salesCountRankContentList = salesCountRankContentList, |             salesCountRankContentList = salesCountRankContentList, | ||||||
|             eventBannerList = eventBannerList |             eventBannerList = eventBannerList, | ||||||
|  |             tagList = tagList, | ||||||
|  |             tagCurationContentList = tagCurationContentList, | ||||||
|  |             curationList = curationList | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -144,20 +165,14 @@ class AudioContentMainTabContentService( | |||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): GetPopularContentByCreatorResponse { |     fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> { | ||||||
|         val salesRankContentList = rankingService.fetchCreatorContentBySalesTop2( |         return rankingService.fetchCreatorContentBySalesCountTop4( | ||||||
|             creatorId = creatorId, |             creatorId = creatorId, | ||||||
|             isAdult = isAdult |             isAdult = isAdult | ||||||
|         ) |         ) | ||||||
|  |     } | ||||||
|         val salesCountRankContentList = rankingService.fetchCreatorContentBySalesCountTop2( |  | ||||||
|             creatorId = creatorId, |     fun getRecommendedContentByTag(memberId: Long, tag: String): List<GetAudioContentMainItem> { | ||||||
|             isAdult = isAdult |         return tagCurationService.getTagCurationContentList(memberId = memberId, tag = tag) | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         return GetPopularContentByCreatorResponse( |  | ||||||
|             salesRankContentList = salesRankContentList, |  | ||||||
|             salesCountRankContentList = salesCountRankContentList |  | ||||||
|         ) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,79 @@ | |||||||
|  | package kr.co.vividnext.sodalive.content.main.tab.content | ||||||
|  |  | ||||||
|  | import com.querydsl.jpa.impl.JPAQueryFactory | ||||||
|  | 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.main.curation.tag.QContentHashTagCuration.contentHashTagCuration | ||||||
|  | import kr.co.vividnext.sodalive.content.main.curation.tag.QContentHashTagCurationItem.contentHashTagCurationItem | ||||||
|  | import kr.co.vividnext.sodalive.member.QMember.member | ||||||
|  | import kr.co.vividnext.sodalive.member.block.QBlockMember.blockMember | ||||||
|  | import org.springframework.beans.factory.annotation.Value | ||||||
|  | import org.springframework.stereotype.Repository | ||||||
|  |  | ||||||
|  | @Repository | ||||||
|  | class ContentMainTabTagCurationRepository( | ||||||
|  |     private val queryFactory: JPAQueryFactory, | ||||||
|  |  | ||||||
|  |     @Value("\${cloud.aws.cloud-front.host}") | ||||||
|  |     private val imageHost: String | ||||||
|  | ) { | ||||||
|  |     fun getTagList(isAdult: Boolean): List<String> { | ||||||
|  |         var where = contentHashTagCuration.isActive.isTrue | ||||||
|  |             .and(audioContent.isActive.isTrue) | ||||||
|  |             .and(audioContent.duration.isNotNull) | ||||||
|  |             .and(audioContent.limited.isNull) | ||||||
|  |             .and(contentHashTagCurationItem.isActive.isTrue) | ||||||
|  |  | ||||||
|  |         if (!isAdult) { | ||||||
|  |             where = where.and(contentHashTagCuration.isAdult.isFalse) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return queryFactory | ||||||
|  |             .select(contentHashTagCuration.tag) | ||||||
|  |             .from(contentHashTagCuration) | ||||||
|  |             .innerJoin(contentHashTagCurationItem) | ||||||
|  |             .on(contentHashTagCurationItem.curation.id.eq(contentHashTagCuration.id)) | ||||||
|  |             .innerJoin(contentHashTagCurationItem.content, audioContent) | ||||||
|  |             .where(where) | ||||||
|  |             .groupBy(contentHashTagCuration.id) | ||||||
|  |             .having(contentHashTagCurationItem.id.countDistinct().gt(0)) | ||||||
|  |             .orderBy(contentHashTagCuration.orders.asc()) | ||||||
|  |             .fetch() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun getTagCurationContentList(memberId: Long, tag: String): List<GetAudioContentMainItem> { | ||||||
|  |         val blockMemberCondition = blockMember.member.id.eq(member.id) | ||||||
|  |             .and(blockMember.isActive.isTrue) | ||||||
|  |             .and(blockMember.blockedMember.id.eq(memberId)) | ||||||
|  |  | ||||||
|  |         val where = audioContent.isActive.isTrue | ||||||
|  |             .and(audioContent.duration.isNotNull) | ||||||
|  |             .and(audioContent.limited.isNull) | ||||||
|  |             .and(blockMember.id.isNull) | ||||||
|  |             .and(contentHashTagCurationItem.isActive.isTrue) | ||||||
|  |             .and(contentHashTagCuration.tag.eq(tag)) | ||||||
|  |  | ||||||
|  |         return queryFactory | ||||||
|  |             .select( | ||||||
|  |                 QGetAudioContentMainItem( | ||||||
|  |                     audioContent.id, | ||||||
|  |                     audioContent.coverImage.prepend("/").prepend(imageHost), | ||||||
|  |                     audioContent.title, | ||||||
|  |                     member.id, | ||||||
|  |                     member.profileImage.prepend("/").prepend(imageHost), | ||||||
|  |                     member.nickname, | ||||||
|  |                     audioContent.price, | ||||||
|  |                     audioContent.duration | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |             .from(contentHashTagCurationItem) | ||||||
|  |             .innerJoin(contentHashTagCurationItem.curation, contentHashTagCuration) | ||||||
|  |             .innerJoin(contentHashTagCurationItem.content, audioContent) | ||||||
|  |             .innerJoin(audioContent.member, member) | ||||||
|  |             .leftJoin(blockMember).on(blockMemberCondition) | ||||||
|  |             .where(where) | ||||||
|  |             .orderBy(contentHashTagCurationItem.orders.asc()) | ||||||
|  |             .fetch() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,15 @@ | |||||||
|  | package kr.co.vividnext.sodalive.content.main.tab.content | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | ||||||
|  | import org.springframework.stereotype.Service | ||||||
|  |  | ||||||
|  | @Service | ||||||
|  | class ContentMainTabTagCurationService(private val repository: ContentMainTabTagCurationRepository) { | ||||||
|  |     fun getTagList(isAdult: Boolean): List<String> { | ||||||
|  |         return repository.getTagList(isAdult = isAdult) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun getTagCurationContentList(memberId: Long, tag: String): List<GetAudioContentMainItem> { | ||||||
|  |         return repository.getTagCurationContentList(memberId = memberId, tag = tag) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -4,6 +4,7 @@ import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse | |||||||
| import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | ||||||
| import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem | import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem | ||||||
| import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse | import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse | ||||||
|  | import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse | ||||||
| import kr.co.vividnext.sodalive.event.GetEventResponse | import kr.co.vividnext.sodalive.event.GetEventResponse | ||||||
|  |  | ||||||
| data class GetContentMainTabContentResponse( | data class GetContentMainTabContentResponse( | ||||||
| @@ -14,7 +15,9 @@ data class GetContentMainTabContentResponse( | |||||||
|     val rankSortTypeList: List<String>, |     val rankSortTypeList: List<String>, | ||||||
|     val rankContentList: List<GetAudioContentRankingItem>, |     val rankContentList: List<GetAudioContentRankingItem>, | ||||||
|     val contentRankCreatorList: List<ContentCreatorResponse>, |     val contentRankCreatorList: List<ContentCreatorResponse>, | ||||||
|     val salesRankContentList: List<GetAudioContentRankingItem>, |  | ||||||
|     val salesCountRankContentList: List<GetAudioContentRankingItem>, |     val salesCountRankContentList: List<GetAudioContentRankingItem>, | ||||||
|     val eventBannerList: GetEventResponse |     val eventBannerList: GetEventResponse, | ||||||
|  |     val tagList: List<String>, | ||||||
|  |     val tagCurationContentList: List<GetAudioContentMainItem>, | ||||||
|  |     val curationList: List<GetContentCurationResponse> | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -60,4 +60,19 @@ class AudioContentMainTabFreeController(private val service: AudioContentMainTab | |||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @GetMapping("/popular-content-by-creator") | ||||||
|  |     fun getPopularContentByCreator( | ||||||
|  |         @RequestParam creatorId: Long, | ||||||
|  |         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? | ||||||
|  |     ) = run { | ||||||
|  |         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||||
|  |  | ||||||
|  |         ApiResponse.ok( | ||||||
|  |             service.getPopularContentByCreator( | ||||||
|  |                 creatorId = creatorId, | ||||||
|  |                 isAdult = member.auth != null | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,20 +3,25 @@ package kr.co.vividnext.sodalive.content.main.tab.free | |||||||
| import kr.co.vividnext.sodalive.content.AudioContentRepository | import kr.co.vividnext.sodalive.content.AudioContentRepository | ||||||
| import kr.co.vividnext.sodalive.content.ContentType | import kr.co.vividnext.sodalive.content.ContentType | ||||||
| import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | ||||||
|  | import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem | ||||||
| import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService | import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService | ||||||
| import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository | import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository | ||||||
|  | import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTabRepository | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse | import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.RecommendSeriesRepository | import kr.co.vividnext.sodalive.content.main.tab.RecommendSeriesRepository | ||||||
| import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository | import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository | ||||||
| import kr.co.vividnext.sodalive.member.Member | import kr.co.vividnext.sodalive.member.Member | ||||||
|  | import kr.co.vividnext.sodalive.rank.RankingService | ||||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||||
|  |  | ||||||
| @Service | @Service | ||||||
| class AudioContentMainTabFreeService( | class AudioContentMainTabFreeService( | ||||||
|  |     private val repository: AudioContentMainTabRepository, | ||||||
|     private val bannerService: AudioContentBannerService, |     private val bannerService: AudioContentBannerService, | ||||||
|     private val recommendSeriesRepository: RecommendSeriesRepository, |     private val recommendSeriesRepository: RecommendSeriesRepository, | ||||||
|     private val curationRepository: AudioContentCurationQueryRepository, |     private val curationRepository: AudioContentCurationQueryRepository, | ||||||
|     private val contentRepository: AudioContentRepository, |     private val contentRepository: AudioContentRepository, | ||||||
|  |     private val rankingService: RankingService, | ||||||
|     private val audioContentRepository: AudioContentRepository, |     private val audioContentRepository: AudioContentRepository, | ||||||
|     private val audioContentThemeRepository: AudioContentThemeQueryRepository |     private val audioContentThemeRepository: AudioContentThemeQueryRepository | ||||||
| ) { | ) { | ||||||
| @@ -53,7 +58,6 @@ class AudioContentMainTabFreeService( | |||||||
|         val newFreeContentList = if (themeList.isNotEmpty()) { |         val newFreeContentList = if (themeList.isNotEmpty()) { | ||||||
|             audioContentRepository.findByTheme( |             audioContentRepository.findByTheme( | ||||||
|                 memberId = member.id!!, |                 memberId = member.id!!, | ||||||
|                 theme = themeList[0], |  | ||||||
|                 isAdult = member.auth != null, |                 isAdult = member.auth != null, | ||||||
|                 contentType = ContentType.ALL, |                 contentType = ContentType.ALL, | ||||||
|                 offset = 0, |                 offset = 0, | ||||||
| @@ -64,6 +68,13 @@ class AudioContentMainTabFreeService( | |||||||
|             emptyList() |             emptyList() | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         val creatorList = repository.findCreatorWithHasFreeContent(memberId, 4) | ||||||
|  |         val playCountRankContentList = if (creatorList.isNotEmpty()) { | ||||||
|  |             rankingService.fetchFreeContentByCreatorIdTop4(creatorList[0].creatorId, isAdult) | ||||||
|  |         } else { | ||||||
|  |             emptyList() | ||||||
|  |         } | ||||||
|  |  | ||||||
|         val curationList = curationRepository.findByContentMainTabId(tabId = tabId, isAdult = isAdult) |         val curationList = curationRepository.findByContentMainTabId(tabId = tabId, isAdult = isAdult) | ||||||
|             .filter { it.title != "크리에이터 소개" } |             .filter { it.title != "크리에이터 소개" } | ||||||
|             .map { |             .map { | ||||||
| @@ -87,6 +98,8 @@ class AudioContentMainTabFreeService( | |||||||
|             recommendSeriesList = recommendSeriesList, |             recommendSeriesList = recommendSeriesList, | ||||||
|             themeList = themeList, |             themeList = themeList, | ||||||
|             newFreeContentList = newFreeContentList, |             newFreeContentList = newFreeContentList, | ||||||
|  |             creatorList = creatorList, | ||||||
|  |             playCountRankContentList = playCountRankContentList, | ||||||
|             curationList = curationList |             curationList = curationList | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| @@ -132,4 +145,8 @@ class AudioContentMainTabFreeService( | |||||||
|             isFree = true |             isFree = true | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> { | ||||||
|  |         return rankingService.fetchFreeContentByCreatorIdTop4(creatorId, isAdult) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| package kr.co.vividnext.sodalive.content.main.tab.free | package kr.co.vividnext.sodalive.content.main.tab.free | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse | ||||||
| import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem | ||||||
|  | import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem | ||||||
| import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse | import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse | import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.GetRecommendSeriesListResponse | import kr.co.vividnext.sodalive.content.main.tab.GetRecommendSeriesListResponse | ||||||
| @@ -12,5 +14,7 @@ data class GetContentMainTabFreeResponse( | |||||||
|     val recommendSeriesList: List<GetRecommendSeriesListResponse>, |     val recommendSeriesList: List<GetRecommendSeriesListResponse>, | ||||||
|     val themeList: List<String>, |     val themeList: List<String>, | ||||||
|     val newFreeContentList: List<GetAudioContentMainItem>, |     val newFreeContentList: List<GetAudioContentMainItem>, | ||||||
|  |     val creatorList: List<ContentCreatorResponse>, | ||||||
|  |     val playCountRankContentList: List<GetAudioContentRankingItem>, | ||||||
|     val curationList: List<GetContentCurationResponse> |     val curationList: List<GetContentCurationResponse> | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| package kr.co.vividnext.sodalive.content.main.tab.home | package kr.co.vividnext.sodalive.content.main.tab.home | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem | ||||||
| import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService | import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.GetPopularContentByCreatorResponse |  | ||||||
| import kr.co.vividnext.sodalive.event.EventService | import kr.co.vividnext.sodalive.event.EventService | ||||||
| import kr.co.vividnext.sodalive.member.Member | import kr.co.vividnext.sodalive.member.Member | ||||||
| import kr.co.vividnext.sodalive.notice.ServiceNoticeService | import kr.co.vividnext.sodalive.notice.ServiceNoticeService | ||||||
| @@ -74,27 +74,17 @@ class AudioContentMainTabHomeService( | |||||||
|  |  | ||||||
|         /* 채널별 인기 콘텐츠 |         /* 채널별 인기 콘텐츠 | ||||||
|          * - 콘텐츠를 4개 이상 등록한 채널 |          * - 콘텐츠를 4개 이상 등록한 채널 | ||||||
|          * - 주간 콘텐츠 매출 Top 20 채널 |          * - 주간 콘텐츠 판매 개수 Top 20 채널 | ||||||
|          * - 해당 채널의 누적 매출 Top 2 |          * - 해당 채널의 누적 판매 개수 Top 4 | ||||||
|          * - 해당 채널의 누적 판매 개수 Top 2 |  | ||||||
|          */ |          */ | ||||||
|         val contentRankCreatorList = rankingService.fetchCreatorByContentRevenueRankTop20( |         val contentRankCreatorList = rankingService.fetchCreatorBySellContentCountRankTop20( | ||||||
|             memberId = member.id!!, |             memberId = member.id!!, | ||||||
|             startDate = startDate.minusDays(1), |             startDate = startDate.minusDays(1), | ||||||
|             endDate = endDate |             endDate = endDate | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         val salesRankContentList = if (contentRankCreatorList.isNotEmpty()) { |  | ||||||
|             rankingService.fetchCreatorContentBySalesTop2( |  | ||||||
|                 creatorId = contentRankCreatorList[0].creatorId, |  | ||||||
|                 isAdult = member.auth != null |  | ||||||
|             ) |  | ||||||
|         } else { |  | ||||||
|             emptyList() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         val salesCountRankContentList = if (contentRankCreatorList.isNotEmpty()) { |         val salesCountRankContentList = if (contentRankCreatorList.isNotEmpty()) { | ||||||
|             rankingService.fetchCreatorContentBySalesCountTop2( |             rankingService.fetchCreatorContentBySalesCountTop4( | ||||||
|                 creatorId = contentRankCreatorList[0].creatorId, |                 creatorId = contentRankCreatorList[0].creatorId, | ||||||
|                 isAdult = member.auth != null |                 isAdult = member.auth != null | ||||||
|             ) |             ) | ||||||
| @@ -111,25 +101,14 @@ class AudioContentMainTabHomeService( | |||||||
|             rankContentList = rankContentList, |             rankContentList = rankContentList, | ||||||
|             eventBannerList = eventBannerList, |             eventBannerList = eventBannerList, | ||||||
|             contentRankCreatorList = contentRankCreatorList, |             contentRankCreatorList = contentRankCreatorList, | ||||||
|             salesRankContentList = salesRankContentList, |  | ||||||
|             salesCountRankContentList = salesCountRankContentList |             salesCountRankContentList = salesCountRankContentList | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): GetPopularContentByCreatorResponse { |     fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> { | ||||||
|         val salesRankContentList = rankingService.fetchCreatorContentBySalesTop2( |         return rankingService.fetchCreatorContentBySalesCountTop4( | ||||||
|             creatorId = creatorId, |             creatorId = creatorId, | ||||||
|             isAdult = isAdult |             isAdult = isAdult | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         val salesCountRankContentList = rankingService.fetchCreatorContentBySalesCountTop2( |  | ||||||
|             creatorId = creatorId, |  | ||||||
|             isAdult = isAdult |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         return GetPopularContentByCreatorResponse( |  | ||||||
|             salesRankContentList = salesRankContentList, |  | ||||||
|             salesCountRankContentList = salesCountRankContentList |  | ||||||
|         ) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -18,6 +18,5 @@ data class GetContentMainTabHomeResponse( | |||||||
|     val rankContentList: List<GetAudioContentRankingItem>, |     val rankContentList: List<GetAudioContentRankingItem>, | ||||||
|     val eventBannerList: GetEventResponse, |     val eventBannerList: GetEventResponse, | ||||||
|     val contentRankCreatorList: List<ContentCreatorResponse>, |     val contentRankCreatorList: List<ContentCreatorResponse>, | ||||||
|     val salesRankContentList: List<GetAudioContentRankingItem>, |  | ||||||
|     val salesCountRankContentList: List<GetAudioContentRankingItem> |     val salesCountRankContentList: List<GetAudioContentRankingItem> | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -2,11 +2,11 @@ package kr.co.vividnext.sodalive.content.main.tab.replay | |||||||
|  |  | ||||||
| import kr.co.vividnext.sodalive.content.AudioContentRepository | import kr.co.vividnext.sodalive.content.AudioContentRepository | ||||||
| import kr.co.vividnext.sodalive.content.ContentType | import kr.co.vividnext.sodalive.content.ContentType | ||||||
|  | import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem | ||||||
| import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService | import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService | ||||||
| import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository | import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTabRepository | import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTabRepository | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse | import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse | ||||||
| import kr.co.vividnext.sodalive.content.main.tab.GetPopularContentByCreatorResponse |  | ||||||
| import kr.co.vividnext.sodalive.event.EventService | import kr.co.vividnext.sodalive.event.EventService | ||||||
| import kr.co.vividnext.sodalive.member.Member | import kr.co.vividnext.sodalive.member.Member | ||||||
| import kr.co.vividnext.sodalive.rank.RankingService | import kr.co.vividnext.sodalive.rank.RankingService | ||||||
| @@ -68,18 +68,8 @@ class AudioContentMainTabLiveReplayService( | |||||||
|             minCount = 4 |             minCount = 4 | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         val salesRankContentList = if (creatorList.isNotEmpty()) { |  | ||||||
|             rankingService.fetchCreatorContentBySalesTop2( |  | ||||||
|                 creatorId = creatorList[0].creatorId, |  | ||||||
|                 isAdult = isAdult, |  | ||||||
|                 theme = theme |  | ||||||
|             ) |  | ||||||
|         } else { |  | ||||||
|             emptyList() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         val salesCountRankContentList = if (creatorList.isNotEmpty()) { |         val salesCountRankContentList = if (creatorList.isNotEmpty()) { | ||||||
|             rankingService.fetchCreatorContentBySalesCountTop2( |             rankingService.fetchCreatorContentBySalesCountTop4( | ||||||
|                 creatorId = creatorList[0].creatorId, |                 creatorId = creatorList[0].creatorId, | ||||||
|                 isAdult = isAdult, |                 isAdult = isAdult, | ||||||
|                 theme = theme |                 theme = theme | ||||||
| @@ -107,30 +97,17 @@ class AudioContentMainTabLiveReplayService( | |||||||
|             newLiveReplayContentList = newLiveReplayContentList, |             newLiveReplayContentList = newLiveReplayContentList, | ||||||
|             rankLiveReplayContentList = rankLiveReplayContentList, |             rankLiveReplayContentList = rankLiveReplayContentList, | ||||||
|             creatorList = creatorList, |             creatorList = creatorList, | ||||||
|             salesRankContentList = salesRankContentList, |  | ||||||
|             salesCountRankContentList = salesCountRankContentList, |             salesCountRankContentList = salesCountRankContentList, | ||||||
|             eventBannerList = eventBannerList, |             eventBannerList = eventBannerList, | ||||||
|             curationList = curationList |             curationList = curationList | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): GetPopularContentByCreatorResponse { |     fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> { | ||||||
|         val theme = "다시듣기" |         return rankingService.fetchCreatorContentBySalesCountTop4( | ||||||
|         val salesRankContentList = rankingService.fetchCreatorContentBySalesTop2( |  | ||||||
|             creatorId = creatorId, |             creatorId = creatorId, | ||||||
|             isAdult = isAdult, |             isAdult = isAdult, | ||||||
|             theme = theme |             theme = "다시듣기" | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         val salesCountRankContentList = rankingService.fetchCreatorContentBySalesCountTop2( |  | ||||||
|             creatorId = creatorId, |  | ||||||
|             isAdult = isAdult, |  | ||||||
|             theme = theme |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         return GetPopularContentByCreatorResponse( |  | ||||||
|             salesRankContentList = salesRankContentList, |  | ||||||
|             salesCountRankContentList = salesCountRankContentList |  | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,7 +13,6 @@ data class GetContentMainTabLiveReplayResponse( | |||||||
|     val newLiveReplayContentList: List<GetAudioContentMainItem>, |     val newLiveReplayContentList: List<GetAudioContentMainItem>, | ||||||
|     val rankLiveReplayContentList: List<GetAudioContentRankingItem>, |     val rankLiveReplayContentList: List<GetAudioContentRankingItem>, | ||||||
|     val creatorList: List<ContentCreatorResponse>, |     val creatorList: List<ContentCreatorResponse>, | ||||||
|     val salesRankContentList: List<GetAudioContentRankingItem>, |  | ||||||
|     val salesCountRankContentList: List<GetAudioContentRankingItem>, |     val salesCountRankContentList: List<GetAudioContentRankingItem>, | ||||||
|     val eventBannerList: GetEventResponse, |     val eventBannerList: GetEventResponse, | ||||||
|     val curationList: List<GetContentCurationResponse> |     val curationList: List<GetContentCurationResponse> | ||||||
|   | |||||||
| @@ -336,7 +336,7 @@ class RankingRepository( | |||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun fetchCreatorByContentRevenueRankTop20( |     fun fetchCreatorBySellContentCountRankTop20( | ||||||
|         memberId: Long, |         memberId: Long, | ||||||
|         startDate: LocalDateTime, |         startDate: LocalDateTime, | ||||||
|         endDate: LocalDateTime |         endDate: LocalDateTime | ||||||
| @@ -350,9 +350,12 @@ class RankingRepository( | |||||||
|             .and(order.createdAt.goe(startDate)) |             .and(order.createdAt.goe(startDate)) | ||||||
|             .and(order.createdAt.lt(startDate)) |             .and(order.createdAt.lt(startDate)) | ||||||
|  |  | ||||||
|         val where = member.isActive.isTrue |         val memberCondition = member.isActive.isTrue | ||||||
|             .and(member.role.eq(MemberRole.CREATOR)) |             .and(member.role.eq(MemberRole.CREATOR)) | ||||||
|             .and(audioContent.isActive.isTrue) |             .and(member.id.eq(audioContent.member.id)) | ||||||
|  |  | ||||||
|  |         val where = audioContent.isActive.isTrue | ||||||
|  |             .and(audioContent.price.gt(0)) | ||||||
|             .and(audioContent.duration.isNotNull) |             .and(audioContent.duration.isNotNull) | ||||||
|             .and(audioContent.limited.isNull) |             .and(audioContent.limited.isNull) | ||||||
|             .and(blockMember.id.isNull) |             .and(blockMember.id.isNull) | ||||||
| @@ -365,71 +368,20 @@ class RankingRepository( | |||||||
|                     member.profileImage.prepend("/").prepend(imageHost) |                     member.profileImage.prepend("/").prepend(imageHost) | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .from(member) |             .from(audioContent) | ||||||
|             .innerJoin(audioContent).on(member.id.eq(audioContent.member.id)) |             .innerJoin(member).on(memberCondition) | ||||||
|             .leftJoin(order).on(ordersCondition) |             .leftJoin(order).on(ordersCondition) | ||||||
|             .leftJoin(blockMember).on(blockMemberCondition) |             .leftJoin(blockMember).on(blockMemberCondition) | ||||||
|             .where(where) |             .where(where) | ||||||
|             .groupBy(member.id) |             .groupBy(member.id) | ||||||
|             .having(audioContent.id.count().goe(4)) |             .having(audioContent.id.count().goe(4)) | ||||||
|             .orderBy( |             .orderBy(order.id.count().desc(), member.id.desc()) | ||||||
|                 order.can.sum().desc(), |  | ||||||
|                 Expressions.numberTemplate(Double::class.java, "function('rand')").asc() |  | ||||||
|             ) |  | ||||||
|             .offset(0) |             .offset(0) | ||||||
|             .limit(20) |             .limit(20) | ||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun fetchCreatorContentBySalesTop2( |     fun fetchCreatorContentBySalesCountTop4( | ||||||
|         creatorId: Long, |  | ||||||
|         isAdult: Boolean, |  | ||||||
|         theme: String |  | ||||||
|     ): List<GetAudioContentRankingItem> { |  | ||||||
|         var where = member.isActive.isTrue |  | ||||||
|             .and(member.role.eq(MemberRole.CREATOR)) |  | ||||||
|             .and(audioContent.isActive.isTrue) |  | ||||||
|             .and(audioContent.duration.isNotNull) |  | ||||||
|             .and(audioContent.limited.isNull) |  | ||||||
|             .and(audioContentTheme.isActive.isTrue) |  | ||||||
|             .and(order.isActive.isTrue) |  | ||||||
|             .and(member.id.eq(creatorId)) |  | ||||||
|  |  | ||||||
|         if (!isAdult) { |  | ||||||
|             where = where.and(series.isAdult.isFalse) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (theme.isNotBlank()) { |  | ||||||
|             where = where.and(audioContentTheme.theme.eq(theme)) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return queryFactory |  | ||||||
|             .select( |  | ||||||
|                 QGetAudioContentRankingItem( |  | ||||||
|                     audioContent.id, |  | ||||||
|                     audioContent.title, |  | ||||||
|                     audioContent.coverImage.prepend("/").prepend(imageHost), |  | ||||||
|                     audioContentTheme.theme, |  | ||||||
|                     audioContent.price, |  | ||||||
|                     audioContent.duration, |  | ||||||
|                     member.id, |  | ||||||
|                     member.nickname, |  | ||||||
|                     member.profileImage.prepend("/").prepend(imageHost) |  | ||||||
|                 ) |  | ||||||
|             ) |  | ||||||
|             .from(order) |  | ||||||
|             .innerJoin(order.audioContent, audioContent) |  | ||||||
|             .innerJoin(audioContent.theme, audioContentTheme) |  | ||||||
|             .innerJoin(audioContent.member, member) |  | ||||||
|             .where(where) |  | ||||||
|             .groupBy(audioContent.id) |  | ||||||
|             .orderBy(order.can.sum().desc()) |  | ||||||
|             .offset(0) |  | ||||||
|             .limit(2) |  | ||||||
|             .fetch() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun fetchCreatorContentBySalesCountTop2( |  | ||||||
|         creatorId: Long, |         creatorId: Long, | ||||||
|         isAdult: Boolean, |         isAdult: Boolean, | ||||||
|         theme: String |         theme: String | ||||||
| @@ -473,7 +425,7 @@ class RankingRepository( | |||||||
|             .groupBy(audioContent.id) |             .groupBy(audioContent.id) | ||||||
|             .orderBy(order.id.count().desc()) |             .orderBy(order.id.count().desc()) | ||||||
|             .offset(0) |             .offset(0) | ||||||
|             .limit(2) |             .limit(4) | ||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -556,4 +508,41 @@ class RankingRepository( | |||||||
|             .limit(10) |             .limit(10) | ||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fun fetchFreeContentByCreatorIdTop4(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> { | ||||||
|  |         var where = member.isActive.isTrue | ||||||
|  |             .and(member.id.eq(creatorId)) | ||||||
|  |             .and(member.role.eq(MemberRole.CREATOR)) | ||||||
|  |             .and(audioContent.isActive.isTrue) | ||||||
|  |             .and(audioContent.duration.isNotNull) | ||||||
|  |             .and(audioContent.limited.isNull) | ||||||
|  |             .and(audioContent.price.loe(0)) | ||||||
|  |  | ||||||
|  |         if (!isAdult) { | ||||||
|  |             where = where.and(series.isAdult.isFalse) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return queryFactory | ||||||
|  |             .select( | ||||||
|  |                 QGetAudioContentRankingItem( | ||||||
|  |                     audioContent.id, | ||||||
|  |                     audioContent.title, | ||||||
|  |                     audioContent.coverImage.prepend("/").prepend(imageHost), | ||||||
|  |                     audioContentTheme.theme, | ||||||
|  |                     audioContent.price, | ||||||
|  |                     audioContent.duration, | ||||||
|  |                     member.id, | ||||||
|  |                     member.nickname, | ||||||
|  |                     member.profileImage.prepend("/").prepend(imageHost) | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |             .from(audioContent) | ||||||
|  |             .innerJoin(audioContent.theme, audioContentTheme) | ||||||
|  |             .innerJoin(audioContent.member, member) | ||||||
|  |             .where(where) | ||||||
|  |             .orderBy(audioContent.playCount.desc()) | ||||||
|  |             .offset(0) | ||||||
|  |             .limit(4) | ||||||
|  |             .fetch() | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -181,28 +181,20 @@ class RankingService( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun fetchCreatorByContentRevenueRankTop20( |     fun fetchCreatorBySellContentCountRankTop20( | ||||||
|         memberId: Long, |         memberId: Long, | ||||||
|         startDate: LocalDateTime, |         startDate: LocalDateTime, | ||||||
|         endDate: LocalDateTime |         endDate: LocalDateTime | ||||||
|     ): List<ContentCreatorResponse> { |     ): List<ContentCreatorResponse> { | ||||||
|         return repository.fetchCreatorByContentRevenueRankTop20(memberId, startDate, endDate) |         return repository.fetchCreatorBySellContentCountRankTop20(memberId, startDate, endDate) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun fetchCreatorContentBySalesTop2( |     fun fetchCreatorContentBySalesCountTop4( | ||||||
|         creatorId: Long, |         creatorId: Long, | ||||||
|         isAdult: Boolean, |         isAdult: Boolean, | ||||||
|         theme: String = "" |         theme: String = "" | ||||||
|     ): List<GetAudioContentRankingItem> { |     ): List<GetAudioContentRankingItem> { | ||||||
|         return repository.fetchCreatorContentBySalesTop2(creatorId, isAdult, theme) |         return repository.fetchCreatorContentBySalesCountTop4(creatorId, isAdult, theme) | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun fetchCreatorContentBySalesCountTop2( |  | ||||||
|         creatorId: Long, |  | ||||||
|         isAdult: Boolean, |  | ||||||
|         theme: String = "" |  | ||||||
|     ): List<GetAudioContentRankingItem> { |  | ||||||
|         return repository.fetchCreatorContentBySalesCountTop2(creatorId, isAdult, theme) |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun fetchCreatorBySeriesRevenueRankTop20( |     fun fetchCreatorBySeriesRevenueRankTop20( | ||||||
| @@ -217,4 +209,8 @@ class RankingService( | |||||||
|         val seriesList = repository.fetchCreatorSeriesBySales(creatorId = creatorId, isAdult = isAdult) |         val seriesList = repository.fetchCreatorSeriesBySales(creatorId = creatorId, isAdult = isAdult) | ||||||
|         return seriesToSeriesListItem(seriesList, isAdult) |         return seriesToSeriesListItem(seriesList, isAdult) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fun fetchFreeContentByCreatorIdTop4(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> { | ||||||
|  |         return repository.fetchFreeContentByCreatorIdTop4(creatorId, isAdult) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user