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 ff06f60..1a02e7f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt @@ -43,7 +43,7 @@ interface AudioContentQueryRepository { limit: Long = 10 ): List - fun findTotalCountByCreatorId(creatorId: Long, isAdult: Boolean = false): Int + fun findTotalCountByCreatorId(creatorId: Long, isAdult: Boolean = false, categoryId: Long = 0): Int fun getCreatorOtherContentList( cloudfrontHost: String, contentId: Long, @@ -194,13 +194,20 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) .where(where) .offset(offset) .limit(limit) - .orderBy(pinContent.isActive.desc(), pinContent.updatedAt.desc(), orderBy, audioContent.id.desc()) + .orderBy( + pinContent.isActive.desc(), + pinContent.updatedAt.desc(), + orderBy, + audioContent.updatedAt.desc(), + audioContent.id.desc() + ) .fetch() } override fun findTotalCountByCreatorId( creatorId: Long, - isAdult: Boolean + isAdult: Boolean, + categoryId: Long ): Int { var where = audioContent.member.id.eq(creatorId) .and( @@ -212,8 +219,15 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) where = where.and(audioContent.isAdult.isFalse) } + if (categoryId > 0) { + where = where.and(categoryContent.category.id.eq(categoryId)) + } + return queryFactory - .selectFrom(audioContent) + .select(audioContent.id) + .from(audioContent) + .leftJoin(categoryContent) + .on(audioContent.id.eq(categoryContent.content.id).and(categoryContent.isActive.ne(false))) .where(where) .fetch() .size diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt index 832a9f9..de0e173 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt @@ -630,7 +630,8 @@ class AudioContentService( ): GetAudioContentListResponse { val totalCount = repository.findTotalCountByCreatorId( creatorId = creatorId, - isAdult = member.auth != null + isAdult = member.auth != null, + categoryId = categoryId ) val audioContentList = repository.findByCreatorId( diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryController.kt index b3db249..0906e5f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryController.kt @@ -40,6 +40,17 @@ class CategoryController(private val service: CategoryService) { ApiResponse.ok(service.modifyCategory(request = request, member = member)) } + @PutMapping("/orders") + @PreAuthorize("hasRole('CREATOR')") + fun updateCategoryOrders( + @RequestBody request: UpdateCategoryOrdersRequest, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok(service.updateCategoryOrders(request = request, member = member)) + } + @DeleteMapping("/{id}") @PreAuthorize("hasRole('CREATOR')") fun deleteCategory( diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryRepository.kt index eacc9bd..b9fbf0b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryRepository.kt @@ -39,7 +39,11 @@ class CategoryQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : C return queryFactory .select(QGetCategoryListResponse(category.id, category.title)) .from(category) - .where(category.member.id.eq(creatorId)) + .where( + category.member.id.eq(creatorId) + .and(category.isActive.isTrue) + ) + .orderBy(category.orders.asc()) .fetch() } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryService.kt index 3afe603..00c6c9a 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryService.kt @@ -86,6 +86,17 @@ class CategoryService( categoryContentRepository.deleteByCategoryId(categoryId = categoryId) } + @Transactional + fun updateCategoryOrders(request: UpdateCategoryOrdersRequest, member: Member) { + for (index in request.ids.indices) { + val category = repository.findByIdAndMemberId(categoryId = request.ids[index], memberId = member.id!!) + + if (category != null) { + category.orders = index + 1 + } + } + } + fun getCategoryList(creatorId: Long, memberId: Long): List { val isBlocked = blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = creatorId) if (isBlocked) throw SodaException("잘못된 접근입니다.") diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/UpdateCategoryOrdersRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/UpdateCategoryOrdersRequest.kt new file mode 100644 index 0000000..93ef105 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/UpdateCategoryOrdersRequest.kt @@ -0,0 +1,3 @@ +package kr.co.vividnext.sodalive.content.category + +data class UpdateCategoryOrdersRequest(val ids: List) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/CreatorAdminCategoryController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/CreatorAdminCategoryController.kt new file mode 100644 index 0000000..37aaeca --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/CreatorAdminCategoryController.kt @@ -0,0 +1,52 @@ +package kr.co.vividnext.sodalive.creator.admin.content.category + +import kr.co.vividnext.sodalive.common.ApiResponse +import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.member.Member +import org.springframework.data.domain.Pageable +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@RestController +@PreAuthorize("hasRole('CREATOR')") +@RequestMapping("/creator-admin/content-category") +class CreatorAdminCategoryController(private val service: CreatorAdminCategoryService) { + @GetMapping("/search") + fun searchContentNotInCategory( + @RequestParam(value = "category_id") categoryId: Long, + @RequestParam(value = "search_word") searchWord: String, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok( + service.searchContentNotInCategory( + categoryId = categoryId, + searchWord = searchWord, + memberId = member.id!! + ) + ) + } + + @GetMapping + fun getContentInCategory( + @RequestParam(value = "category_id") categoryId: Long, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, + pageable: Pageable + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok( + service.getContentInCategory( + categoryId = categoryId, + memberId = member.id!!, + offset = pageable.offset, + limit = pageable.pageSize.toLong() + ) + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/CreatorAdminCategoryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/CreatorAdminCategoryRepository.kt new file mode 100644 index 0000000..35b8196 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/CreatorAdminCategoryRepository.kt @@ -0,0 +1,82 @@ +package kr.co.vividnext.sodalive.creator.admin.content.category + +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.content.QAudioContent.audioContent +import kr.co.vividnext.sodalive.content.category.QCategoryContent.categoryContent +import org.springframework.stereotype.Repository + +@Repository +class CreatorAdminCategoryRepository(private val queryFactory: JPAQueryFactory) { + fun searchContentNotInCategory( + categoryId: Long, + searchWord: String, + memberId: Long + ): List { + return queryFactory + .select(QSearchContentNotInCategoryResponse(audioContent.id, audioContent.title)) + .from(audioContent) + .leftJoin(categoryContent) + .on( + audioContent.id.eq(categoryContent.content.id) + .and(categoryContent.category.id.eq(categoryId)) + ) + .where( + audioContent.duration.isNotNull + .and(audioContent.member.isNotNull) + .and(audioContent.member.id.eq(memberId)) + .and(audioContent.isActive.isTrue.or(audioContent.releaseDate.isNotNull)) + .and(audioContent.title.contains(searchWord)) + .and(categoryContent.id.isNull.or(categoryContent.isActive.isFalse)) + ) + .fetch() + } + + fun getContentInCategoryTotalCount(categoryId: Long, memberId: Long): Int { + return queryFactory + .select(audioContent.id) + .from(audioContent) + .leftJoin(categoryContent) + .on(audioContent.id.eq(categoryContent.content.id).and(categoryContent.isActive.isTrue)) + .where( + categoryContent.category.id.eq(categoryId) + .and(audioContent.duration.isNotNull) + .and(audioContent.member.isNotNull) + .and(audioContent.member.id.eq(memberId)) + .and(audioContent.isActive.isTrue.or(audioContent.releaseDate.isNotNull)) + ) + .fetch() + .size + } + + fun getContentInCategory( + imageHost: String, + categoryId: Long, + memberId: Long, + offset: Long, + limit: Long + ): List { + return queryFactory + .select( + QGetContentInCategoryItem( + audioContent.id, + audioContent.title, + audioContent.isAdult, + audioContent.coverImage.prepend("/").prepend(imageHost) + ) + ) + .from(audioContent) + .leftJoin(categoryContent) + .on(audioContent.id.eq(categoryContent.content.id).and(categoryContent.isActive.isTrue)) + .where( + categoryContent.category.id.eq(categoryId) + .and(audioContent.duration.isNotNull) + .and(audioContent.member.isNotNull) + .and(audioContent.member.id.eq(memberId)) + .and(audioContent.isActive.isTrue.or(audioContent.releaseDate.isNotNull)) + ) + .orderBy(categoryContent.updatedAt.desc()) + .offset(offset) + .limit(limit) + .fetch() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/CreatorAdminCategoryService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/CreatorAdminCategoryService.kt new file mode 100644 index 0000000..ba1a2f7 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/CreatorAdminCategoryService.kt @@ -0,0 +1,42 @@ +package kr.co.vividnext.sodalive.creator.admin.content.category + +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Service + +@Service +class CreatorAdminCategoryService( + private val repository: CreatorAdminCategoryRepository, + + @Value("\${cloud.aws.cloud-front.host}") + private val imageHost: String +) { + fun searchContentNotInCategory( + categoryId: Long, + searchWord: String, + memberId: Long + ): List { + return repository.searchContentNotInCategory( + categoryId, + searchWord, + memberId + ) + } + + fun getContentInCategory( + categoryId: Long, + memberId: Long, + offset: Long, + limit: Long + ): GetContentInCategoryResponse { + val totalCount = repository.getContentInCategoryTotalCount(categoryId, memberId) + val items = repository.getContentInCategory( + imageHost, + categoryId, + memberId, + offset, + limit + ) + + return GetContentInCategoryResponse(totalCount, items) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/GetContentInCategoryResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/GetContentInCategoryResponse.kt new file mode 100644 index 0000000..2004597 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/GetContentInCategoryResponse.kt @@ -0,0 +1,15 @@ +package kr.co.vividnext.sodalive.creator.admin.content.category + +import com.querydsl.core.annotations.QueryProjection + +data class GetContentInCategoryResponse( + val totalCount: Int, + val items: List +) + +data class GetContentInCategoryItem @QueryProjection constructor( + val contentId: Long, + val title: String, + val isAdult: Boolean, + val image: String +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/SearchContentNotInCategoryResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/SearchContentNotInCategoryResponse.kt new file mode 100644 index 0000000..cf103c9 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/category/SearchContentNotInCategoryResponse.kt @@ -0,0 +1,8 @@ +package kr.co.vividnext.sodalive.creator.admin.content.category + +import com.querydsl.core.annotations.QueryProjection + +data class SearchContentNotInCategoryResponse @QueryProjection constructor( + val contentId: Long, + val title: String +)