diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt index 026e9ec..47c243b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt @@ -41,8 +41,8 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { .on(member.id.eq(creatorSettlementRatio.member.id)) .where( useCan.isRefund.isFalse - .and(liveRoom.beginDateTime.goe(startDate)) - .and(liveRoom.beginDateTime.loe(endDate)) + .and(useCan.createdAt.goe(startDate)) + .and(useCan.createdAt.loe(endDate)) ) .groupBy(liveRoom.id, useCan.canUsage, creatorSettlementRatio.liveSettlementRatio) .orderBy(member.nickname.desc(), liveRoom.id.desc(), useCan.canUsage.desc(), formattedDate.desc()) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentController.kt index fa875fb..65ebb64 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentController.kt @@ -42,6 +42,9 @@ class AdminContentController(private val service: AdminContentService) { fun modifyAudioContent( @RequestBody request: UpdateAdminContentRequest ) = ApiResponse.ok(service.updateAudioContent(request)) + + @GetMapping("/main/tab") + fun getContentMainTabList() = ApiResponse.ok(service.getContentMainTabList()) } enum class ContentReleaseStatus { diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentRepository.kt index d01218d..8365b2b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentRepository.kt @@ -32,6 +32,7 @@ interface AdminAudioContentQueryRepository { ): List fun getHashTagList(audioContentId: Long): List + fun findByIdAndActiveTrue(audioContentId: Long): AudioContent? } class AdminAudioContentQueryRepositoryImpl( @@ -143,6 +144,16 @@ class AdminAudioContentQueryRepositoryImpl( .fetch() } + override fun findByIdAndActiveTrue(audioContentId: Long): AudioContent? { + return queryFactory + .selectFrom(audioContent) + .where( + audioContent.id.eq(audioContentId), + audioContent.isActive.isTrue + ) + .fetchFirst() + } + private fun formattedDateExpression( dateTime: DateTimePath, format: String = "%Y-%m-%d" diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentService.kt index ebf0445..9733e46 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentService.kt @@ -1,9 +1,11 @@ package kr.co.vividnext.sodalive.admin.content import kr.co.vividnext.sodalive.admin.content.curation.AdminContentCurationRepository +import kr.co.vividnext.sodalive.admin.content.tab.AdminContentMainTabRepository import kr.co.vividnext.sodalive.admin.content.theme.AdminContentThemeRepository import kr.co.vividnext.sodalive.aws.cloudfront.AudioContentCloudFront import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.content.main.tab.GetContentMainTabItem import org.springframework.data.domain.Pageable import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service @@ -14,7 +16,8 @@ class AdminContentService( private val repository: AdminContentRepository, private val themeRepository: AdminContentThemeRepository, private val audioContentCloudFront: AudioContentCloudFront, - private val curationRepository: AdminContentCurationRepository + private val curationRepository: AdminContentCurationRepository, + private val contentMainTabRepository: AdminContentMainTabRepository ) { fun getAudioContentList(status: ContentReleaseStatus, pageable: Pageable): GetAdminContentListResponse { val totalCount = repository.getAudioContentTotalCount(status = status) @@ -118,4 +121,8 @@ class AdminContentService( audioContent.theme = theme } } + + fun getContentMainTabList(): List { + return contentMainTabRepository.findAllByActiveIsTrue() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerController.kt index 1324585..249a8b2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerController.kt @@ -7,6 +7,7 @@ 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.RequestPart import org.springframework.web.bind.annotation.RestController import org.springframework.web.multipart.MultipartFile @@ -33,5 +34,7 @@ class AdminContentBannerController(private val service: AdminContentBannerServic ) = ApiResponse.ok(service.updateBannerOrders(request.ids), "수정되었습니다.") @GetMapping - fun getAudioContentMainBannerList() = ApiResponse.ok(service.getAudioContentMainBannerList()) + fun getAudioContentMainBannerList( + @RequestParam(value = "tabId", required = false) tabId: Long? = null + ) = ApiResponse.ok(service.getAudioContentMainBannerList(tabId = tabId)) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerRepository.kt index 14c1f25..00933fc 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerRepository.kt @@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.admin.content.banner import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.content.main.banner.AudioContentBanner import kr.co.vividnext.sodalive.content.main.banner.QAudioContentBanner.audioContentBanner +import kr.co.vividnext.sodalive.content.main.tab.QAudioContentMainTab.audioContentMainTab import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series import kr.co.vividnext.sodalive.event.QEvent.event import kr.co.vividnext.sodalive.member.QMember.member @@ -14,7 +15,7 @@ import org.springframework.stereotype.Repository interface AdminContentBannerRepository : JpaRepository, AdminContentBannerQueryRepository interface AdminContentBannerQueryRepository { - fun getAudioContentMainBannerList(): List + fun getAudioContentMainBannerList(tabId: Long = 1): List } class AdminContentBannerQueryRepositoryImpl( @@ -22,11 +23,20 @@ class AdminContentBannerQueryRepositoryImpl( @Value("\${cloud.aws.cloud-front.host}") private val cloudFrontHost: String ) : AdminContentBannerQueryRepository { - override fun getAudioContentMainBannerList(): List { + override fun getAudioContentMainBannerList(tabId: Long): List { + var where = audioContentBanner.isActive.isTrue + + where = if (tabId <= 1L) { + where.and(audioContentMainTab.id.isNull) + } else { + where.and(audioContentMainTab.id.eq(tabId)) + } + return queryFactory .select( QGetAdminContentBannerResponse( audioContentBanner.id, + audioContentBanner.tab.id.coalesce(1), audioContentBanner.type, audioContentBanner.thumbnailImage.prepend("/").prepend(cloudFrontHost), audioContentBanner.event.id, @@ -43,7 +53,8 @@ class AdminContentBannerQueryRepositoryImpl( .leftJoin(audioContentBanner.event, event) .leftJoin(audioContentBanner.creator, member) .leftJoin(audioContentBanner.series, series) - .where(audioContentBanner.isActive.isTrue) + .leftJoin(audioContentBanner.tab, audioContentMainTab) + .where(where) .orderBy(audioContentBanner.orders.asc()) .fetch() } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt index 4fdddae..3811632 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt @@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.admin.content.banner import com.fasterxml.jackson.databind.ObjectMapper import kr.co.vividnext.sodalive.admin.content.series.AdminContentSeriesRepository +import kr.co.vividnext.sodalive.admin.content.tab.AdminContentMainTabRepository import kr.co.vividnext.sodalive.aws.s3.S3Uploader import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.content.main.banner.AudioContentBanner @@ -22,6 +23,7 @@ class AdminContentBannerService( private val memberRepository: MemberRepository, private val seriesRepository: AdminContentSeriesRepository, private val eventRepository: EventRepository, + private val contentMainTabRepository: AdminContentMainTabRepository, private val objectMapper: ObjectMapper, @Value("\${cloud.aws.s3.bucket}") @@ -64,12 +66,19 @@ class AdminContentBannerService( null } + val tab = if (request.tabId !== null) { + contentMainTabRepository.findByIdOrNull(request.tabId) + } else { + null + } + val audioContentBanner = AudioContentBanner(type = request.type) audioContentBanner.link = request.link audioContentBanner.isAdult = request.isAdult audioContentBanner.event = event audioContentBanner.creator = creator audioContentBanner.series = series + audioContentBanner.tab = tab repository.save(audioContentBanner) val fileName = generateFileName() @@ -156,6 +165,10 @@ class AdminContentBannerService( audioContentBanner.type = request.type } + + if (request.tabId !== null) { + audioContentBanner.tab = contentMainTabRepository.findByIdOrNull(request.tabId) + } } @Transactional @@ -169,7 +182,7 @@ class AdminContentBannerService( } } - fun getAudioContentMainBannerList(): List { - return repository.getAudioContentMainBannerList() + fun getAudioContentMainBannerList(tabId: Long?): List { + return repository.getAudioContentMainBannerList(tabId = tabId ?: 1) } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/CreateContentBannerRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/CreateContentBannerRequest.kt index ca9cb5e..e564206 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/CreateContentBannerRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/CreateContentBannerRequest.kt @@ -4,6 +4,7 @@ import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType data class CreateContentBannerRequest( val type: AudioContentBannerType, + val tabId: Long?, val eventId: Long?, val creatorId: Long?, val seriesId: Long?, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/GetAdminContentBannerResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/GetAdminContentBannerResponse.kt index bfd21d1..2ba9348 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/GetAdminContentBannerResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/GetAdminContentBannerResponse.kt @@ -5,6 +5,7 @@ import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType data class GetAdminContentBannerResponse @QueryProjection constructor( val id: Long, + val tabId: Long?, val type: AudioContentBannerType, val thumbnailImageUrl: String, val eventId: Long?, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/UpdateContentBannerRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/UpdateContentBannerRequest.kt index fcbad72..4390788 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/UpdateContentBannerRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/UpdateContentBannerRequest.kt @@ -5,6 +5,7 @@ import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType data class UpdateContentBannerRequest( val id: Long, val type: AudioContentBannerType?, + val tabId: Long?, val eventId: Long?, val creatorId: Long?, val seriesId: Long?, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AddItemToCurationRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AddItemToCurationRequest.kt new file mode 100644 index 0000000..938ad44 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AddItemToCurationRequest.kt @@ -0,0 +1,6 @@ +package kr.co.vividnext.sodalive.admin.content.curation + +data class AddItemToCurationRequest( + val curationId: Long, + val itemIdList: List +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationController.kt index 849697a..e065e62 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationController.kt @@ -7,6 +7,7 @@ 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 @@ -29,5 +30,34 @@ class AdminContentCurationController(private val service: AdminContentCurationSe ) = ApiResponse.ok(service.updateContentCurationOrders(request.ids), "수정되었습니다.") @GetMapping - fun getContentCurationList() = ApiResponse.ok(service.getContentCurationList()) + fun getContentCurationList( + @RequestParam tabId: Long + ) = ApiResponse.ok(service.getContentCurationList(tabId = tabId)) + + @GetMapping("/items") + fun getCurationItems( + @RequestParam curationId: Long + ) = ApiResponse.ok(service.getCurationItem(curationId = curationId)) + + @GetMapping("/search/content") + fun searchCurationContentItem( + @RequestParam curationId: Long, + @RequestParam searchWord: String + ) = ApiResponse.ok(service.searchCurationContentItem(curationId, searchWord)) + + @GetMapping("/search/series") + fun searchCurationSeriesItem( + @RequestParam curationId: Long, + @RequestParam searchWord: String + ) = ApiResponse.ok(service.searchCurationSeriesItem(curationId, searchWord)) + + @PostMapping("/add/item") + fun addItemToCuration( + @RequestBody request: AddItemToCurationRequest + ) = ApiResponse.ok(service.addItemToCuration(request), "큐레이션 아이템을 등록했습니다.") + + @PostMapping("/remove/item") + fun removeItemInCuration( + @RequestBody request: RemoveItemInCurationRequest + ) = ApiResponse.ok(service.removeItemInCuration(request), "큐레이션 아이템을 제거했습니다.") } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationItemRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationItemRepository.kt new file mode 100644 index 0000000..bcfe675 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationItemRepository.kt @@ -0,0 +1,46 @@ +package kr.co.vividnext.sodalive.admin.content.curation + +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.content.QAudioContent.audioContent +import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationItem +import kr.co.vividnext.sodalive.content.main.curation.QAudioContentCuration.audioContentCuration +import kr.co.vividnext.sodalive.content.main.curation.QAudioContentCurationItem.audioContentCurationItem +import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series +import org.springframework.data.jpa.repository.JpaRepository + +interface AdminContentCurationItemRepository : + JpaRepository, + AdminContentCurationItemQueryRepository + +interface AdminContentCurationItemQueryRepository { + fun findByCurationIdAndSeriesId(curationId: Long, seriesId: Long?): AudioContentCurationItem? + fun findByCurationIdAndContentId(curationId: Long, contentId: Long?): AudioContentCurationItem? +} + +class AdminContentCurationItemQueryRepositoryImpl( + val queryFactory: JPAQueryFactory +) : AdminContentCurationItemQueryRepository { + override fun findByCurationIdAndSeriesId(curationId: Long, seriesId: Long?): AudioContentCurationItem? { + return queryFactory + .selectFrom(audioContentCurationItem) + .innerJoin(audioContentCurationItem.curation, audioContentCuration) + .innerJoin(audioContentCurationItem.series, series) + .where( + audioContentCurationItem.curation.id.eq(curationId), + audioContentCurationItem.series.id.eq(seriesId) + ) + .fetchFirst() + } + + override fun findByCurationIdAndContentId(curationId: Long, contentId: Long?): AudioContentCurationItem? { + return queryFactory + .selectFrom(audioContentCurationItem) + .innerJoin(audioContentCurationItem.curation, audioContentCuration) + .innerJoin(audioContentCurationItem.content, audioContent) + .where( + audioContentCurationItem.curation.id.eq(curationId), + audioContentCurationItem.content.id.eq(contentId) + ) + .fetchFirst() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationRepository.kt index a24fa77..d0277eb 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationRepository.kt @@ -1,8 +1,14 @@ package kr.co.vividnext.sodalive.admin.content.curation import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.content.QAudioContent.audioContent import kr.co.vividnext.sodalive.content.main.curation.AudioContentCuration +import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationItem import kr.co.vividnext.sodalive.content.main.curation.QAudioContentCuration.audioContentCuration +import kr.co.vividnext.sodalive.content.main.curation.QAudioContentCurationItem.audioContentCurationItem +import kr.co.vividnext.sodalive.content.main.tab.QAudioContentMainTab.audioContentMainTab +import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series +import org.springframework.beans.factory.annotation.Value import org.springframework.data.jpa.repository.JpaRepository import org.springframework.stereotype.Repository @@ -12,26 +18,39 @@ interface AdminContentCurationRepository : AdminContentCurationQueryRepository interface AdminContentCurationQueryRepository { - fun getAudioContentCurationList(): List + fun getAudioContentCurationList(tabId: Long): List fun findByIdAndActive(id: Long): AudioContentCuration? + fun findByCurationIdAndItemId(curationId: Long, itemId: Long): AudioContentCurationItem? + fun getAudioContentCurationItemList(curationId: Long): List + fun searchCurationContentItem(curationId: Long, searchWord: String): List + fun searchCurationSeriesItem(curationId: Long, searchWord: String): List } @Repository class AdminContentCurationQueryRepositoryImpl( - private val queryFactory: JPAQueryFactory + private val queryFactory: JPAQueryFactory, + + @Value("\${cloud.aws.cloud-front.host}") + private val imageHost: String ) : AdminContentCurationQueryRepository { - override fun getAudioContentCurationList(): List { + override fun getAudioContentCurationList(tabId: Long): List { return queryFactory .select( QGetAdminContentCurationResponse( audioContentCuration.id, + audioContentMainTab.id, audioContentCuration.title, audioContentCuration.description, - audioContentCuration.isAdult + audioContentCuration.isAdult, + audioContentCuration.isSeries ) ) .from(audioContentCuration) - .where(audioContentCuration.isActive.isTrue) + .innerJoin(audioContentCuration.tab, audioContentMainTab) + .where( + audioContentCuration.isActive.isTrue, + audioContentMainTab.id.eq(tabId) + ) .orderBy(audioContentCuration.orders.asc()) .fetch() } @@ -45,4 +64,89 @@ class AdminContentCurationQueryRepositoryImpl( ) .fetchFirst() } + + override fun findByCurationIdAndItemId(curationId: Long, itemId: Long): AudioContentCurationItem? { + return queryFactory.selectFrom(audioContentCurationItem) + .innerJoin(audioContentCurationItem.curation, audioContentCuration) + .where(audioContentCuration.id.eq(curationId), audioContentCurationItem.id.eq(itemId)) + .fetchFirst() + } + + override fun getAudioContentCurationItemList(curationId: Long): List { + return queryFactory + .select( + QGetCurationItemResponse( + audioContentCurationItem.id, + audioContent.title.coalesce(series.title), + audioContent.detail.coalesce(series.introduction), + audioContent.coverImage.coalesce(series.coverImage).prepend("/").prepend(imageHost), + audioContent.member.nickname.coalesce(series.member.nickname).coalesce(""), + audioContent.isAdult.coalesce(series.isAdult) + ) + ) + .from(audioContentCurationItem) + .innerJoin(audioContentCurationItem.curation, audioContentCuration) + .leftJoin(audioContentCurationItem.series, series) + .leftJoin(audioContentCurationItem.content, audioContent) + .where( + audioContentCuration.id.eq(curationId), + audioContentCurationItem.isActive.isTrue + ) + .fetch() + } + + override fun searchCurationContentItem( + curationId: Long, + searchWord: String + ): List { + return queryFactory + .select( + QSearchCurationItemResponse( + audioContent.id, + audioContent.title, + audioContent.coverImage.prepend("/").prepend(imageHost) + ) + ) + .from(audioContent) + .leftJoin(audioContentCurationItem) + .on( + audioContent.id.eq(audioContentCurationItem.content.id) + .and(audioContentCurationItem.curation.id.eq(curationId)) + ) + .where( + audioContent.duration.isNotNull + .and(audioContent.member.isNotNull) + .and(audioContent.isActive.isTrue) + .and(audioContent.title.contains(searchWord)) + .and(audioContentCurationItem.id.isNull) + ) + .fetch() + } + + override fun searchCurationSeriesItem( + curationId: Long, + searchWord: String + ): List { + return queryFactory + .select( + QSearchCurationItemResponse( + series.id, + series.title, + series.coverImage.prepend("/").prepend(imageHost) + ) + ) + .from(series) + .leftJoin(audioContentCurationItem) + .on( + series.id.eq(audioContentCurationItem.series.id) + .and(audioContentCurationItem.curation.id.eq(curationId)) + ) + .where( + series.isActive.isTrue + .and(series.member.isNotNull) + .and(series.title.contains(searchWord)) + .and(audioContentCurationItem.id.isNull) + ) + .fetch() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationService.kt index b6d0c14..7135e27 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationService.kt @@ -1,24 +1,37 @@ package kr.co.vividnext.sodalive.admin.content.curation +import kr.co.vividnext.sodalive.admin.content.AdminContentRepository +import kr.co.vividnext.sodalive.admin.content.series.AdminContentSeriesRepository +import kr.co.vividnext.sodalive.admin.content.tab.AdminContentMainTabRepository import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.content.main.curation.AudioContentCuration +import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationItem import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @Service class AdminContentCurationService( - private val repository: AdminContentCurationRepository + private val repository: AdminContentCurationRepository, + private val contentMainTabRepository: AdminContentMainTabRepository, + private val seriesRepository: AdminContentSeriesRepository, + private val contentRepository: AdminContentRepository, + private val contentCurationItemRepository: AdminContentCurationItemRepository ) { @Transactional fun createContentCuration(request: CreateContentCurationRequest) { - repository.save( - AudioContentCuration( - title = request.title, - description = request.description, - isAdult = request.isAdult - ) + val tab = contentMainTabRepository.findByIdOrNull(request.tabId) + ?: throw SodaException("잘못된 요청입니다.") + + val curation = AudioContentCuration( + title = request.title, + description = request.description, + isAdult = request.isAdult, + isSeries = request.isSeries ) + curation.tab = tab + + repository.save(curation) } @Transactional @@ -41,6 +54,18 @@ class AdminContentCurationService( if (request.isActive != null) { audioContentCuration.isActive = request.isActive } + + if (request.isSeries != null) { + audioContentCuration.isSeries = request.isSeries + } + + if (request.tabId != null) { + val tab = contentMainTabRepository.findByIdOrNull(request.tabId) + + if (tab != null) { + audioContentCuration.tab = tab + } + } } @Transactional @@ -54,7 +79,68 @@ class AdminContentCurationService( } } - fun getContentCurationList(): List { - return repository.getAudioContentCurationList() + fun getContentCurationList(tabId: Long): List { + return repository.getAudioContentCurationList(tabId = tabId) + } + + fun getCurationItem(curationId: Long): List { + return repository.getAudioContentCurationItemList(curationId) + } + + fun searchCurationContentItem(curationId: Long, searchWord: String): List { + return repository.searchCurationContentItem(curationId, searchWord) + } + + fun searchCurationSeriesItem(curationId: Long, searchWord: String): List { + return repository.searchCurationSeriesItem(curationId, searchWord) + } + + @Transactional + fun addItemToCuration(request: AddItemToCurationRequest) { + // 큐레이션 조회 + val audioContentCuration = repository.findByIdOrNull(id = request.curationId) + ?: throw SodaException("잘못된 요청입니다.") + + if (audioContentCuration.isSeries) { + request.itemIdList.forEach { seriesId -> + val series = seriesRepository.findByIdAndActiveTrue(seriesId) + + if (series != null) { + val item = contentCurationItemRepository.findByCurationIdAndSeriesId( + curationId = request.curationId, + seriesId = series.id + ) ?: AudioContentCurationItem() + item.curation = audioContentCuration + item.series = series + item.isActive = true + contentCurationItemRepository.save(item) + } + } + } else { + request.itemIdList.forEach { contentId -> + val audioContent = contentRepository.findByIdAndActiveTrue(contentId) + + if (audioContent != null) { + val item = contentCurationItemRepository.findByCurationIdAndContentId( + curationId = request.curationId, + contentId = audioContent.id + ) ?: AudioContentCurationItem() + item.curation = audioContentCuration + item.content = audioContent + item.isActive = true + contentCurationItemRepository.save(item) + } + } + } + } + + @Transactional + fun removeItemInCuration(request: RemoveItemInCurationRequest) { + val audioContentCurationItem = repository.findByCurationIdAndItemId( + curationId = request.curationId, + itemId = request.itemId + ) + + audioContentCurationItem?.isActive = false } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AudioContentCurationRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AudioContentCurationRequest.kt index 49b46d7..b2ae35d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AudioContentCurationRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AudioContentCurationRequest.kt @@ -1,16 +1,20 @@ package kr.co.vividnext.sodalive.admin.content.curation data class CreateContentCurationRequest( + val tabId: Long, val title: String, val description: String, - val isAdult: Boolean + val isAdult: Boolean, + val isSeries: Boolean ) data class UpdateContentCurationRequest( val id: Long, + val tabId: Long?, val title: String?, val description: String?, val isAdult: Boolean?, + val isSeries: Boolean?, val isActive: Boolean? ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/GetAdminContentCurationResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/GetAdminContentCurationResponse.kt index 2938cfd..a43364d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/GetAdminContentCurationResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/GetAdminContentCurationResponse.kt @@ -4,7 +4,9 @@ import com.querydsl.core.annotations.QueryProjection data class GetAdminContentCurationResponse @QueryProjection constructor( val id: Long, + val tabId: Long, val title: String, val description: String, - val isAdult: Boolean + val isAdult: Boolean, + val isSeries: Boolean ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/GetCurationItemResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/GetCurationItemResponse.kt new file mode 100644 index 0000000..b293643 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/GetCurationItemResponse.kt @@ -0,0 +1,12 @@ +package kr.co.vividnext.sodalive.admin.content.curation + +import com.querydsl.core.annotations.QueryProjection + +data class GetCurationItemResponse @QueryProjection constructor( + val id: Long, + val title: String, + val desc: String, + val coverImageUrl: String, + val creatorNickname: String, + val isAdult: Boolean +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/RemoveItemInCurationRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/RemoveItemInCurationRequest.kt new file mode 100644 index 0000000..c9b108f --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/RemoveItemInCurationRequest.kt @@ -0,0 +1,6 @@ +package kr.co.vividnext.sodalive.admin.content.curation + +data class RemoveItemInCurationRequest( + val curationId: Long, + val itemId: Long +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/SearchCurationItemResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/SearchCurationItemResponse.kt new file mode 100644 index 0000000..f373893 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/SearchCurationItemResponse.kt @@ -0,0 +1,9 @@ +package kr.co.vividnext.sodalive.admin.content.curation + +import com.querydsl.core.annotations.QueryProjection + +data class SearchCurationItemResponse @QueryProjection constructor( + val id: Long, + val title: String, + val coverImageUrl: String +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesRepository.kt index c251e5b..3e4b7ea 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesRepository.kt @@ -22,6 +22,7 @@ interface AdminContentSeriesQueryRepository { ): List fun searchSeriesList(searchWord: String): List + fun findByIdAndActiveTrue(seriesId: Long): Series? } class AdminContentSeriesQueryRepositoryImpl( @@ -97,4 +98,14 @@ class AdminContentSeriesQueryRepositoryImpl( .orderBy(series.id.desc()) .fetch() } + + override fun findByIdAndActiveTrue(seriesId: Long): Series? { + return queryFactory + .selectFrom(series) + .where( + series.id.eq(seriesId), + series.isActive.isTrue + ) + .fetchFirst() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/tab/AdminContentMainTabRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/tab/AdminContentMainTabRepository.kt new file mode 100644 index 0000000..38f690b --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/tab/AdminContentMainTabRepository.kt @@ -0,0 +1,31 @@ +package kr.co.vividnext.sodalive.admin.content.tab + +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTab +import kr.co.vividnext.sodalive.content.main.tab.GetContentMainTabItem +import kr.co.vividnext.sodalive.content.main.tab.QAudioContentMainTab.audioContentMainTab +import kr.co.vividnext.sodalive.content.main.tab.QGetContentMainTabItem +import org.springframework.data.jpa.repository.JpaRepository + +interface AdminContentMainTabRepository : JpaRepository, AdminContentMainTabQueryRepository + +interface AdminContentMainTabQueryRepository { + fun findAllByActiveIsTrue(): List +} + +class AdminContentMainTabQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory +) : AdminContentMainTabQueryRepository { + override fun findAllByActiveIsTrue(): List { + return queryFactory + .select( + QGetContentMainTabItem( + audioContentMainTab.id, + audioContentMainTab.title + ) + ) + .from(audioContentMainTab) + .where(audioContentMainTab.isActive.isTrue) + .fetch() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt index 09063db..f1ac4e8 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt @@ -15,6 +15,7 @@ import kr.co.vividnext.sodalive.content.main.banner.AudioContentBanner import kr.co.vividnext.sodalive.content.main.banner.QAudioContentBanner.audioContentBanner import kr.co.vividnext.sodalive.content.main.curation.AudioContentCuration import kr.co.vividnext.sodalive.content.main.curation.QAudioContentCuration.audioContentCuration +import kr.co.vividnext.sodalive.content.main.tab.QAudioContentMainTab.audioContentMainTab import kr.co.vividnext.sodalive.content.order.QOrder.order import kr.co.vividnext.sodalive.content.pin.QPinContent.pinContent import kr.co.vividnext.sodalive.content.playlist.AudioContentPlaylistContent @@ -755,7 +756,8 @@ class AudioContentQueryRepositoryImpl( return queryFactory .selectFrom(audioContentCuration) - .where(where) + .leftJoin(audioContentCuration.tab, audioContentMainTab) + .where(where, audioContentMainTab.id.isNull) .offset(offset) .limit(limit) .orderBy(audioContentCuration.orders.asc()) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCuration.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCuration.kt index d5955cf..eeebc15 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCuration.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCuration.kt @@ -1,8 +1,12 @@ package kr.co.vividnext.sodalive.content.main.curation import kr.co.vividnext.sodalive.common.BaseEntity +import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTab import javax.persistence.Column import javax.persistence.Entity +import javax.persistence.FetchType +import javax.persistence.JoinColumn +import javax.persistence.OneToOne import javax.persistence.Table @Entity @@ -17,5 +21,11 @@ data class AudioContentCuration( @Column(nullable = false) var isActive: Boolean = true, @Column(nullable = false) - var orders: Int = 1 -) : BaseEntity() + var orders: Int = 1, + @Column(nullable = false) + var isSeries: Boolean = false +) : BaseEntity() { + @OneToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "tab_id", nullable = true) + var tab: AudioContentMainTab? = null +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationItem.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationItem.kt new file mode 100644 index 0000000..7eddaa4 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationItem.kt @@ -0,0 +1,33 @@ +package kr.co.vividnext.sodalive.content.main.curation + +import kr.co.vividnext.sodalive.common.BaseEntity +import kr.co.vividnext.sodalive.content.AudioContent +import kr.co.vividnext.sodalive.creator.admin.content.series.Series +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.FetchType +import javax.persistence.JoinColumn +import javax.persistence.ManyToOne +import javax.persistence.Table + +@Entity +@Table(name = "content_curation_item") +data class AudioContentCurationItem( + @Column(nullable = false) + var orders: Int = 1, + + @Column(nullable = false) + var isActive: Boolean = true +) : BaseEntity() { + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "curation_id", nullable = false) + var curation: AudioContentCuration? = null + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "content_id", nullable = true) + var content: AudioContent? = null + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "series_id", nullable = true) + var series: Series? = null +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetContentMainTabItem.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetContentMainTabItem.kt new file mode 100644 index 0000000..72b2b81 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetContentMainTabItem.kt @@ -0,0 +1,8 @@ +package kr.co.vividnext.sodalive.content.main.tab + +import com.querydsl.core.annotations.QueryProjection + +data class GetContentMainTabItem @QueryProjection constructor( + val tabId: Long, + val title: String +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/calculate/CreatorAdminCalculateQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/calculate/CreatorAdminCalculateQueryRepository.kt index 590e27a..bae92bc 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/calculate/CreatorAdminCalculateQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/calculate/CreatorAdminCalculateQueryRepository.kt @@ -55,8 +55,8 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac .on(member.id.eq(creatorSettlementRatio.member.id)) .where( useCan.isRefund.isFalse - .and(liveRoom.beginDateTime.goe(startDate)) - .and(liveRoom.beginDateTime.loe(endDate)) + .and(useCan.createdAt.goe(startDate)) + .and(useCan.createdAt.loe(endDate)) .and(liveRoom.member.id.eq(memberId)) ) .groupBy(liveRoom.id, useCan.canUsage, creatorSettlementRatio.liveSettlementRatio) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt index 9e2d1d6..1bd336d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt @@ -43,6 +43,7 @@ data class Series( @CollectionTable(name = "series_published_days_of_week", joinColumns = [JoinColumn(name = "series_id")]) val publishedDaysOfWeek: MutableSet = mutableSetOf(), var isAdult: Boolean = false, + var isOriginal: Boolean = false, var isActive: Boolean = true, var orders: Int = 1 ) : BaseEntity() {