@@ -65,6 +65,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
 | 
				
			|||||||
            .where(
 | 
					            .where(
 | 
				
			||||||
                order.createdAt.goe(startDate)
 | 
					                order.createdAt.goe(startDate)
 | 
				
			||||||
                    .and(order.createdAt.loe(endDate))
 | 
					                    .and(order.createdAt.loe(endDate))
 | 
				
			||||||
 | 
					                    .and(order.isActive.isTrue)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .groupBy(audioContent.id, order.type, orderFormattedDate, order.can)
 | 
					            .groupBy(audioContent.id, order.type, orderFormattedDate, order.can)
 | 
				
			||||||
            .orderBy(member.id.desc(), orderFormattedDate.desc(), audioContent.id.asc())
 | 
					            .orderBy(member.id.desc(), orderFormattedDate.desc(), audioContent.id.asc())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -105,6 +105,7 @@ class AudioContentController(private val service: AudioContentService) {
 | 
				
			|||||||
    fun getAudioContentList(
 | 
					    fun getAudioContentList(
 | 
				
			||||||
        @RequestParam("creator-id") creatorId: Long,
 | 
					        @RequestParam("creator-id") creatorId: Long,
 | 
				
			||||||
        @RequestParam("sort-type", required = false) sortType: SortType = SortType.NEWEST,
 | 
					        @RequestParam("sort-type", required = false) sortType: SortType = SortType.NEWEST,
 | 
				
			||||||
 | 
					        @RequestParam("category-id", required = false) categoryId: Long? = 0,
 | 
				
			||||||
        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
 | 
					        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
 | 
				
			||||||
        pageable: Pageable
 | 
					        pageable: Pageable
 | 
				
			||||||
    ) = run {
 | 
					    ) = run {
 | 
				
			||||||
@@ -114,6 +115,7 @@ class AudioContentController(private val service: AudioContentService) {
 | 
				
			|||||||
            service.getAudioContentList(
 | 
					            service.getAudioContentList(
 | 
				
			||||||
                creatorId = creatorId,
 | 
					                creatorId = creatorId,
 | 
				
			||||||
                sortType = sortType,
 | 
					                sortType = sortType,
 | 
				
			||||||
 | 
					                categoryId = categoryId ?: 0,
 | 
				
			||||||
                member = member,
 | 
					                member = member,
 | 
				
			||||||
                offset = pageable.offset,
 | 
					                offset = pageable.offset,
 | 
				
			||||||
                limit = pageable.pageSize.toLong()
 | 
					                limit = pageable.pageSize.toLong()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import com.querydsl.core.types.dsl.Expressions
 | 
				
			|||||||
import com.querydsl.jpa.impl.JPAQueryFactory
 | 
					import com.querydsl.jpa.impl.JPAQueryFactory
 | 
				
			||||||
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
 | 
					import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
 | 
				
			||||||
import kr.co.vividnext.sodalive.content.QBundleAudioContent.bundleAudioContent
 | 
					import kr.co.vividnext.sodalive.content.QBundleAudioContent.bundleAudioContent
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.content.category.QCategoryContent.categoryContent
 | 
				
			||||||
import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment
 | 
					import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment
 | 
				
			||||||
import kr.co.vividnext.sodalive.content.like.QAudioContentLike.audioContentLike
 | 
					import kr.co.vividnext.sodalive.content.like.QAudioContentLike.audioContentLike
 | 
				
			||||||
import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
 | 
					import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
 | 
				
			||||||
@@ -37,6 +38,7 @@ interface AudioContentQueryRepository {
 | 
				
			|||||||
        coverImageHost: String,
 | 
					        coverImageHost: String,
 | 
				
			||||||
        isAdult: Boolean = false,
 | 
					        isAdult: Boolean = false,
 | 
				
			||||||
        sortType: SortType = SortType.NEWEST,
 | 
					        sortType: SortType = SortType.NEWEST,
 | 
				
			||||||
 | 
					        categoryId: Long = 0,
 | 
				
			||||||
        offset: Long = 0,
 | 
					        offset: Long = 0,
 | 
				
			||||||
        limit: Long = 10
 | 
					        limit: Long = 10
 | 
				
			||||||
    ): List<GetAudioContentListItem>
 | 
					    ): List<GetAudioContentListItem>
 | 
				
			||||||
@@ -144,6 +146,7 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
 | 
				
			|||||||
        coverImageHost: String,
 | 
					        coverImageHost: String,
 | 
				
			||||||
        isAdult: Boolean,
 | 
					        isAdult: Boolean,
 | 
				
			||||||
        sortType: SortType,
 | 
					        sortType: SortType,
 | 
				
			||||||
 | 
					        categoryId: Long,
 | 
				
			||||||
        offset: Long,
 | 
					        offset: Long,
 | 
				
			||||||
        limit: Long
 | 
					        limit: Long
 | 
				
			||||||
    ): List<GetAudioContentListItem> {
 | 
					    ): List<GetAudioContentListItem> {
 | 
				
			||||||
@@ -163,6 +166,10 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
 | 
				
			|||||||
            where = where.and(audioContent.isAdult.isFalse)
 | 
					            where = where.and(audioContent.isAdult.isFalse)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (categoryId > 0) {
 | 
				
			||||||
 | 
					            where = where.and(categoryContent.category.id.eq(categoryId))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return queryFactory
 | 
					        return queryFactory
 | 
				
			||||||
            .select(
 | 
					            .select(
 | 
				
			||||||
                QGetAudioContentListItem(
 | 
					                QGetAudioContentListItem(
 | 
				
			||||||
@@ -180,6 +187,8 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
 | 
				
			|||||||
                )
 | 
					                )
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .from(audioContent)
 | 
					            .from(audioContent)
 | 
				
			||||||
 | 
					            .leftJoin(categoryContent)
 | 
				
			||||||
 | 
					            .on(audioContent.id.eq(categoryContent.content.id).and(categoryContent.isActive.ne(false)))
 | 
				
			||||||
            .leftJoin(pinContent)
 | 
					            .leftJoin(pinContent)
 | 
				
			||||||
            .on(audioContent.id.eq(pinContent.content.id).and(pinContent.isActive.ne(false)))
 | 
					            .on(audioContent.id.eq(pinContent.content.id).and(pinContent.isActive.ne(false)))
 | 
				
			||||||
            .where(where)
 | 
					            .where(where)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -624,6 +624,7 @@ class AudioContentService(
 | 
				
			|||||||
        creatorId: Long,
 | 
					        creatorId: Long,
 | 
				
			||||||
        sortType: SortType,
 | 
					        sortType: SortType,
 | 
				
			||||||
        member: Member,
 | 
					        member: Member,
 | 
				
			||||||
 | 
					        categoryId: Long = 0,
 | 
				
			||||||
        offset: Long,
 | 
					        offset: Long,
 | 
				
			||||||
        limit: Long
 | 
					        limit: Long
 | 
				
			||||||
    ): GetAudioContentListResponse {
 | 
					    ): GetAudioContentListResponse {
 | 
				
			||||||
@@ -637,6 +638,7 @@ class AudioContentService(
 | 
				
			|||||||
            coverImageHost = coverImageHost,
 | 
					            coverImageHost = coverImageHost,
 | 
				
			||||||
            isAdult = member.auth != null,
 | 
					            isAdult = member.auth != null,
 | 
				
			||||||
            sortType = sortType,
 | 
					            sortType = sortType,
 | 
				
			||||||
 | 
					            categoryId = categoryId,
 | 
				
			||||||
            offset = offset,
 | 
					            offset = offset,
 | 
				
			||||||
            limit = limit
 | 
					            limit = limit
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.content.category
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.common.BaseEntity
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.member.Member
 | 
				
			||||||
 | 
					import javax.persistence.Entity
 | 
				
			||||||
 | 
					import javax.persistence.FetchType
 | 
				
			||||||
 | 
					import javax.persistence.JoinColumn
 | 
				
			||||||
 | 
					import javax.persistence.ManyToOne
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Entity
 | 
				
			||||||
 | 
					data class Category(
 | 
				
			||||||
 | 
					    var title: String,
 | 
				
			||||||
 | 
					    var orders: Int = 1,
 | 
				
			||||||
 | 
					    var isActive: Boolean = true
 | 
				
			||||||
 | 
					) : BaseEntity() {
 | 
				
			||||||
 | 
					    @ManyToOne(fetch = FetchType.LAZY)
 | 
				
			||||||
 | 
					    @JoinColumn(name = "member_id", nullable = false)
 | 
				
			||||||
 | 
					    var member: Member? = null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.content.category
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 CategoryContent(
 | 
				
			||||||
 | 
					    var orders: Int = 1,
 | 
				
			||||||
 | 
					    var isActive: Boolean = true
 | 
				
			||||||
 | 
					) : BaseEntity() {
 | 
				
			||||||
 | 
					    @ManyToOne(fetch = FetchType.LAZY)
 | 
				
			||||||
 | 
					    @JoinColumn(name = "content_id", nullable = false)
 | 
				
			||||||
 | 
					    var content: AudioContent? = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ManyToOne(fetch = FetchType.LAZY)
 | 
				
			||||||
 | 
					    @JoinColumn(name = "category_id", nullable = false)
 | 
				
			||||||
 | 
					    var category: Category? = null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.content.category
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.querydsl.jpa.impl.JPAQueryFactory
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.content.category.QCategoryContent.categoryContent
 | 
				
			||||||
 | 
					import org.springframework.data.jpa.repository.JpaRepository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface CategoryContentRepository : JpaRepository<CategoryContent, Long>, CategoryContentQueryRepository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface CategoryContentQueryRepository {
 | 
				
			||||||
 | 
					    fun findByContentIdAndCategoryId(contentId: Long, categoryId: Long): CategoryContent?
 | 
				
			||||||
 | 
					    fun deleteByCategoryId(categoryId: Long)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CategoryContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CategoryContentQueryRepository {
 | 
				
			||||||
 | 
					    override fun findByContentIdAndCategoryId(contentId: Long, categoryId: Long): CategoryContent? {
 | 
				
			||||||
 | 
					        return queryFactory
 | 
				
			||||||
 | 
					            .selectFrom(categoryContent)
 | 
				
			||||||
 | 
					            .where(
 | 
				
			||||||
 | 
					                categoryContent.content.id.eq(contentId)
 | 
				
			||||||
 | 
					                    .and(categoryContent.category.id.eq(categoryId))
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .fetchFirst()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun deleteByCategoryId(categoryId: Long) {
 | 
				
			||||||
 | 
					        queryFactory
 | 
				
			||||||
 | 
					            .update(categoryContent)
 | 
				
			||||||
 | 
					            .set(categoryContent.isActive, false)
 | 
				
			||||||
 | 
					            .where(categoryContent.category.id.eq(categoryId))
 | 
				
			||||||
 | 
					            .execute()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.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.security.access.prepost.PreAuthorize
 | 
				
			||||||
 | 
					import org.springframework.security.core.annotation.AuthenticationPrincipal
 | 
				
			||||||
 | 
					import org.springframework.web.bind.annotation.DeleteMapping
 | 
				
			||||||
 | 
					import org.springframework.web.bind.annotation.GetMapping
 | 
				
			||||||
 | 
					import org.springframework.web.bind.annotation.PathVariable
 | 
				
			||||||
 | 
					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("/category")
 | 
				
			||||||
 | 
					class CategoryController(private val service: CategoryService) {
 | 
				
			||||||
 | 
					    @PostMapping
 | 
				
			||||||
 | 
					    @PreAuthorize("hasRole('CREATOR')")
 | 
				
			||||||
 | 
					    fun createCategory(
 | 
				
			||||||
 | 
					        @RequestBody request: CreateCategoryRequest,
 | 
				
			||||||
 | 
					        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
 | 
				
			||||||
 | 
					    ) = run {
 | 
				
			||||||
 | 
					        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ApiResponse.ok(service.createCategory(request = request, member = member))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @PutMapping
 | 
				
			||||||
 | 
					    @PreAuthorize("hasRole('CREATOR')")
 | 
				
			||||||
 | 
					    fun modifyCategory(
 | 
				
			||||||
 | 
					        @RequestBody request: ModifyCategoryRequest,
 | 
				
			||||||
 | 
					        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
 | 
				
			||||||
 | 
					    ) = run {
 | 
				
			||||||
 | 
					        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ApiResponse.ok(service.modifyCategory(request = request, member = member))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @DeleteMapping("/{id}")
 | 
				
			||||||
 | 
					    @PreAuthorize("hasRole('CREATOR')")
 | 
				
			||||||
 | 
					    fun deleteCategory(
 | 
				
			||||||
 | 
					        @PathVariable("id") categoryId: Long,
 | 
				
			||||||
 | 
					        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
 | 
				
			||||||
 | 
					    ) = run {
 | 
				
			||||||
 | 
					        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ApiResponse.ok(service.deleteCategory(categoryId = categoryId, member = member))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @GetMapping
 | 
				
			||||||
 | 
					    fun getCategoryList(
 | 
				
			||||||
 | 
					        @RequestParam creatorId: Long,
 | 
				
			||||||
 | 
					        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
 | 
				
			||||||
 | 
					    ) = run {
 | 
				
			||||||
 | 
					        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ApiResponse.ok(service.getCategoryList(creatorId = creatorId, memberId = member.id!!))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.content.category
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.querydsl.jpa.impl.JPAQueryFactory
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.content.category.QCategory.category
 | 
				
			||||||
 | 
					import org.springframework.data.jpa.repository.JpaRepository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface CategoryRepository : JpaRepository<Category, Long>, CategoryQueryRepository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface CategoryQueryRepository {
 | 
				
			||||||
 | 
					    fun findByTitleAndMemberId(title: String, memberId: Long): Category?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun findByIdAndMemberId(categoryId: Long, memberId: Long): Category?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun findByCreatorId(creatorId: Long): List<GetCategoryListResponse>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CategoryQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CategoryQueryRepository {
 | 
				
			||||||
 | 
					    override fun findByTitleAndMemberId(title: String, memberId: Long): Category? {
 | 
				
			||||||
 | 
					        return queryFactory
 | 
				
			||||||
 | 
					            .selectFrom(category)
 | 
				
			||||||
 | 
					            .where(
 | 
				
			||||||
 | 
					                category.title.eq(title)
 | 
				
			||||||
 | 
					                    .and(category.member.id.eq(memberId))
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .fetchFirst()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun findByIdAndMemberId(categoryId: Long, memberId: Long): Category? {
 | 
				
			||||||
 | 
					        return queryFactory
 | 
				
			||||||
 | 
					            .selectFrom(category)
 | 
				
			||||||
 | 
					            .where(
 | 
				
			||||||
 | 
					                category.id.eq(categoryId)
 | 
				
			||||||
 | 
					                    .and(category.member.id.eq(memberId))
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .fetchFirst()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun findByCreatorId(creatorId: Long): List<GetCategoryListResponse> {
 | 
				
			||||||
 | 
					        return queryFactory
 | 
				
			||||||
 | 
					            .select(QGetCategoryListResponse(category.id, category.title))
 | 
				
			||||||
 | 
					            .from(category)
 | 
				
			||||||
 | 
					            .where(category.member.id.eq(creatorId))
 | 
				
			||||||
 | 
					            .fetch()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,99 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.content.category
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.common.SodaException
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.content.AudioContentRepository
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.member.Member
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
 | 
				
			||||||
 | 
					import org.springframework.stereotype.Service
 | 
				
			||||||
 | 
					import org.springframework.transaction.annotation.Transactional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Service
 | 
				
			||||||
 | 
					class CategoryService(
 | 
				
			||||||
 | 
					    private val repository: CategoryRepository,
 | 
				
			||||||
 | 
					    private val contentRepository: AudioContentRepository,
 | 
				
			||||||
 | 
					    private val blockMemberRepository: BlockMemberRepository,
 | 
				
			||||||
 | 
					    private val categoryContentRepository: CategoryContentRepository
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    @Transactional
 | 
				
			||||||
 | 
					    fun createCategory(request: CreateCategoryRequest, member: Member) {
 | 
				
			||||||
 | 
					        validateTitle(title = request.title)
 | 
				
			||||||
 | 
					        val category = repository.findByTitleAndMemberId(title = request.title, memberId = member.id!!)
 | 
				
			||||||
 | 
					            ?: repository.save(
 | 
				
			||||||
 | 
					                Category(title = request.title).apply {
 | 
				
			||||||
 | 
					                    this.member = member
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        category.isActive = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (contentId in request.contentIdList) {
 | 
				
			||||||
 | 
					            val content = contentRepository.findByIdAndActive(contentId = contentId)
 | 
				
			||||||
 | 
					                ?: continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val categoryContent = categoryContentRepository.findByContentIdAndCategoryId(
 | 
				
			||||||
 | 
					                contentId = contentId,
 | 
				
			||||||
 | 
					                categoryId = category.id!!
 | 
				
			||||||
 | 
					            ) ?: categoryContentRepository.save(
 | 
				
			||||||
 | 
					                CategoryContent().apply {
 | 
				
			||||||
 | 
					                    this.content = content
 | 
				
			||||||
 | 
					                    this.category = category
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            categoryContent.isActive = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Transactional
 | 
				
			||||||
 | 
					    fun modifyCategory(request: ModifyCategoryRequest, member: Member) {
 | 
				
			||||||
 | 
					        val category = repository.findByIdAndMemberId(categoryId = request.categoryId, memberId = member.id!!)
 | 
				
			||||||
 | 
					            ?: throw SodaException("잘못된 요청입니다.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!request.title.isNullOrBlank()) {
 | 
				
			||||||
 | 
					            validateTitle(title = request.title)
 | 
				
			||||||
 | 
					            category.title = request.title
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (contentId in request.addContentIdList) {
 | 
				
			||||||
 | 
					            val content = contentRepository.findByIdAndActive(contentId = contentId)
 | 
				
			||||||
 | 
					                ?: continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val categoryContent = categoryContentRepository.findByContentIdAndCategoryId(
 | 
				
			||||||
 | 
					                contentId = contentId,
 | 
				
			||||||
 | 
					                categoryId = category.id!!
 | 
				
			||||||
 | 
					            ) ?: categoryContentRepository.save(
 | 
				
			||||||
 | 
					                CategoryContent().apply {
 | 
				
			||||||
 | 
					                    this.content = content
 | 
				
			||||||
 | 
					                    this.category = category
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            categoryContent.isActive = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (contentId in request.removeContentIdList) {
 | 
				
			||||||
 | 
					            val categoryContent = categoryContentRepository.findByContentIdAndCategoryId(
 | 
				
			||||||
 | 
					                contentId = contentId,
 | 
				
			||||||
 | 
					                categoryId = category.id!!
 | 
				
			||||||
 | 
					            ) ?: continue
 | 
				
			||||||
 | 
					            categoryContent.isActive = false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Transactional
 | 
				
			||||||
 | 
					    fun deleteCategory(categoryId: Long, member: Member) {
 | 
				
			||||||
 | 
					        val category = repository.findByIdAndMemberId(categoryId = categoryId, memberId = member.id!!)
 | 
				
			||||||
 | 
					            ?: throw SodaException("잘못된 요청입니다.")
 | 
				
			||||||
 | 
					        category.isActive = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        categoryContentRepository.deleteByCategoryId(categoryId = categoryId)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun getCategoryList(creatorId: Long, memberId: Long): List<GetCategoryListResponse> {
 | 
				
			||||||
 | 
					        val isBlocked = blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = creatorId)
 | 
				
			||||||
 | 
					        if (isBlocked) throw SodaException("잘못된 접근입니다.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return repository.findByCreatorId(creatorId = creatorId)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun validateTitle(title: String) {
 | 
				
			||||||
 | 
					        if (title.length < 2) throw SodaException("카테고리명은 2글자 이상 입력하세요")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.content.category
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data class CreateCategoryRequest(
 | 
				
			||||||
 | 
					    val title: String,
 | 
				
			||||||
 | 
					    val contentIdList: List<Long>
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.content.category
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.querydsl.core.annotations.QueryProjection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data class GetCategoryListResponse @QueryProjection constructor(
 | 
				
			||||||
 | 
					    val categoryId: Long,
 | 
				
			||||||
 | 
					    val category: String
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.content.category
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data class ModifyCategoryRequest(
 | 
				
			||||||
 | 
					    val categoryId: Long,
 | 
				
			||||||
 | 
					    val title: String?,
 | 
				
			||||||
 | 
					    val addContentIdList: List<Long>,
 | 
				
			||||||
 | 
					    val removeContentIdList: List<Long>
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -116,16 +116,14 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
 | 
				
			|||||||
                    order.can.sum()
 | 
					                    order.can.sum()
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .from(useCanCalculate)
 | 
					            .from(order)
 | 
				
			||||||
            .innerJoin(useCanCalculate.useCan, useCan)
 | 
					 | 
				
			||||||
            .innerJoin(useCan.order, order)
 | 
					 | 
				
			||||||
            .innerJoin(order.audioContent, audioContent)
 | 
					            .innerJoin(order.audioContent, audioContent)
 | 
				
			||||||
            .innerJoin(audioContent.member, member)
 | 
					            .innerJoin(audioContent.member, member)
 | 
				
			||||||
            .where(
 | 
					            .where(
 | 
				
			||||||
                useCanCalculate.status.eq(UseCanCalculateStatus.RECEIVED)
 | 
					                order.createdAt.goe(startDate)
 | 
				
			||||||
                    .and(useCanCalculate.recipientCreatorId.eq(memberId))
 | 
					 | 
				
			||||||
                    .and(order.createdAt.goe(startDate))
 | 
					 | 
				
			||||||
                    .and(order.createdAt.loe(endDate))
 | 
					                    .and(order.createdAt.loe(endDate))
 | 
				
			||||||
 | 
					                    .and(order.isActive.isTrue)
 | 
				
			||||||
 | 
					                    .and(order.creator.id.eq(memberId))
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .groupBy(audioContent.id, order.type, orderFormattedDate, order.can)
 | 
					            .groupBy(audioContent.id, order.type, orderFormattedDate, order.can)
 | 
				
			||||||
            .offset(offset)
 | 
					            .offset(offset)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user