test #123

Merged
klaus merged 9 commits from test into main 2024-02-05 02:12:10 +00:00
14 changed files with 320 additions and 6 deletions

View File

@ -65,6 +65,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
.where(
order.createdAt.goe(startDate)
.and(order.createdAt.loe(endDate))
.and(order.isActive.isTrue)
)
.groupBy(audioContent.id, order.type, orderFormattedDate, order.can)
.orderBy(member.id.desc(), orderFormattedDate.desc(), audioContent.id.asc())

View File

@ -105,6 +105,7 @@ class AudioContentController(private val service: AudioContentService) {
fun getAudioContentList(
@RequestParam("creator-id") creatorId: Long,
@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?,
pageable: Pageable
) = run {
@ -114,6 +115,7 @@ class AudioContentController(private val service: AudioContentService) {
service.getAudioContentList(
creatorId = creatorId,
sortType = sortType,
categoryId = categoryId ?: 0,
member = member,
offset = pageable.offset,
limit = pageable.pageSize.toLong()

View File

@ -4,6 +4,7 @@ import com.querydsl.core.types.dsl.Expressions
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
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.like.QAudioContentLike.audioContentLike
import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
@ -37,6 +38,7 @@ interface AudioContentQueryRepository {
coverImageHost: String,
isAdult: Boolean = false,
sortType: SortType = SortType.NEWEST,
categoryId: Long = 0,
offset: Long = 0,
limit: Long = 10
): List<GetAudioContentListItem>
@ -144,6 +146,7 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
coverImageHost: String,
isAdult: Boolean,
sortType: SortType,
categoryId: Long,
offset: Long,
limit: Long
): List<GetAudioContentListItem> {
@ -163,6 +166,10 @@ 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
.select(
QGetAudioContentListItem(
@ -180,6 +187,8 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
)
)
.from(audioContent)
.leftJoin(categoryContent)
.on(audioContent.id.eq(categoryContent.content.id).and(categoryContent.isActive.ne(false)))
.leftJoin(pinContent)
.on(audioContent.id.eq(pinContent.content.id).and(pinContent.isActive.ne(false)))
.where(where)

View File

@ -624,6 +624,7 @@ class AudioContentService(
creatorId: Long,
sortType: SortType,
member: Member,
categoryId: Long = 0,
offset: Long,
limit: Long
): GetAudioContentListResponse {
@ -637,6 +638,7 @@ class AudioContentService(
coverImageHost = coverImageHost,
isAdult = member.auth != null,
sortType = sortType,
categoryId = categoryId,
offset = offset,
limit = limit
)

View File

@ -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
}

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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!!))
}
}

View File

@ -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()
}
}

View File

@ -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글자 이상 입력하세요")
}
}

View File

@ -0,0 +1,6 @@
package kr.co.vividnext.sodalive.content.category
data class CreateCategoryRequest(
val title: String,
val contentIdList: List<Long>
)

View File

@ -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
)

View File

@ -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>
)

View File

@ -116,16 +116,14 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
order.can.sum()
)
)
.from(useCanCalculate)
.innerJoin(useCanCalculate.useCan, useCan)
.innerJoin(useCan.order, order)
.from(order)
.innerJoin(order.audioContent, audioContent)
.innerJoin(audioContent.member, member)
.where(
useCanCalculate.status.eq(UseCanCalculateStatus.RECEIVED)
.and(useCanCalculate.recipientCreatorId.eq(memberId))
.and(order.createdAt.goe(startDate))
order.createdAt.goe(startDate)
.and(order.createdAt.loe(endDate))
.and(order.isActive.isTrue)
.and(order.creator.id.eq(memberId))
)
.groupBy(audioContent.id, order.type, orderFormattedDate, order.can)
.offset(offset)