diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContent.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContent.kt index 9d121eb..2b6c768 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContent.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContent.kt @@ -18,8 +18,8 @@ import javax.persistence.OneToMany import javax.persistence.OneToOne import javax.persistence.Table -enum class AudioContentType { - INDIVIDUAL, BUNDLE +enum class PurchaseOption { + BOTH, BUY_ONLY, RENT_ONLY } enum class SortType { @@ -37,7 +37,7 @@ data class AudioContent( val limited: Int? = null, var remaining: Int? = null, @Enumerated(value = EnumType.STRING) - val type: AudioContentType = AudioContentType.INDIVIDUAL, + val purchaseOption: PurchaseOption = PurchaseOption.BOTH, val isGeneratePreview: Boolean = true, var isOnlyRental: Boolean = false, var isAdult: Boolean = false, @@ -63,7 +63,4 @@ data class AudioContent( @OneToMany(mappedBy = "audioContent", cascade = [CascadeType.ALL]) val audioContentHashTags: MutableList = mutableListOf() - - @OneToMany(mappedBy = "child", cascade = [CascadeType.ALL]) - var children: MutableList = mutableListOf() } 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 e417a61..acc85e7 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt @@ -3,7 +3,6 @@ package kr.co.vividnext.sodalive.content 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 @@ -32,7 +31,6 @@ interface AudioContentRepository : JpaRepository, AudioConte interface AudioContentQueryRepository { fun findByIdAndActive(contentId: Long): AudioContent? fun findByIdAndCreatorId(contentId: Long, creatorId: Long): AudioContent? - fun findBundleByContentId(contentId: Long): List fun findByCreatorId( creatorId: Long, coverImageHost: String, @@ -147,18 +145,6 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) .fetchOne() } - // 해당 컨텐츠가 속한 묶음(번들) 상품 리스트 검색 - override fun findBundleByContentId(contentId: Long): List { - return queryFactory - .select(bundleAudioContent.parent) - .from(bundleAudioContent) - .where( - bundleAudioContent.child.id.eq(contentId) - .and(bundleAudioContent.child.isActive.isTrue) - ) - .fetch() - } - override fun findByCreatorId( creatorId: Long, coverImageHost: String, 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 a8d1519..4610561 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt @@ -160,25 +160,28 @@ class AudioContentService( } // contentFile 체크 - if (contentFile == null && request.type == AudioContentType.INDIVIDUAL) { + if (contentFile == null) { throw SodaException("콘텐츠를 선택해 주세요.") } - if (request.type == AudioContentType.BUNDLE && request.childIds == null) { - throw SodaException("묶음상품의 하위상품을 선택해 주세요.") - } - // 테마 체크 val theme = themeQueryRepository.findThemeByIdAndActive(id = request.themeId) ?: throw SodaException("잘못된 테마입니다. 다시 선택해 주세요.") if (request.price in 1..4) throw SodaException("콘텐츠의 최소금액은 5캔 입니다.") + val isOnlyRental = if (request.limited != null && request.limited > 0) { + false + } else if (request.purchaseOption == PurchaseOption.RENT_ONLY) { + true + } else { + request.isOnlyRental + } + // DB에 값 추가 val audioContent = AudioContent( title = request.title, detail = request.detail, - type = request.type, price = if (request.price > 0) { request.price } else { @@ -188,18 +191,13 @@ class AudioContentService( limited = request.limited, remaining = request.limited, isAdult = request.isAdult, - isGeneratePreview = if (request.type == AudioContentType.INDIVIDUAL) { - request.isGeneratePreview - } else { - false - }, - isOnlyRental = if (request.limited != null && request.limited > 0) false else request.isOnlyRental, + purchaseOption = request.purchaseOption, + isGeneratePreview = request.isGeneratePreview, + isOnlyRental = isOnlyRental, isCommentAvailable = request.isCommentAvailable ) audioContent.theme = theme audioContent.member = member - audioContent.isActive = request.type == AudioContentType.BUNDLE - repository.save(audioContent) // 태그 분리, #추가, 등록 @@ -246,48 +244,34 @@ class AudioContentService( audioContent.coverImage = coverImagePath - if (contentFile != null && request.type == AudioContentType.INDIVIDUAL) { - // 콘텐츠 파일명 생성 - val contentFileName = generateFileName(prefix = "${audioContent.id}-content") + // 콘텐츠 파일명 생성 + val contentFileName = generateFileName(prefix = "${audioContent.id}-content") - // 콘텐츠 파일 업로드 - metadata = ObjectMetadata() - metadata.contentLength = contentFile.size - metadata.addUserMetadata( - "generate_preview", - if (request.price > 0) { - request.isGeneratePreview.toString() - } else { - "false" - } - ) - - if (request.previewStartTime != null && request.previewEndTime != null) { - metadata.addUserMetadata("preview_start_time", request.previewStartTime) - metadata.addUserMetadata("preview_end_time", request.previewEndTime) + // 콘텐츠 파일 업로드 + metadata = ObjectMetadata() + metadata.contentLength = contentFile.size + metadata.addUserMetadata( + "generate_preview", + if (request.price > 0) { + request.isGeneratePreview.toString() + } else { + "false" } + ) - val contentPath = s3Uploader.upload( - inputStream = contentFile.inputStream, - bucket = audioContentBucket, - filePath = "input/${audioContent.id}/$contentFileName", - metadata = metadata - ) - - audioContent.content = contentPath + if (request.previewStartTime != null && request.previewEndTime != null) { + metadata.addUserMetadata("preview_start_time", request.previewStartTime) + metadata.addUserMetadata("preview_end_time", request.previewEndTime) } - if (request.childIds != null && request.type == AudioContentType.BUNDLE) { - for (childId in request.childIds) { - val childContent = repository.findByIdAndActive(childId) - ?: continue + val contentPath = s3Uploader.upload( + inputStream = contentFile.inputStream, + bucket = audioContentBucket, + filePath = "input/${audioContent.id}/$contentFileName", + metadata = metadata + ) - val bundleAudioContent = BundleAudioContent() - bundleAudioContent.parent = audioContent - bundleAudioContent.child = childContent - audioContent.children.add(bundleAudioContent) - } - } + audioContent.content = contentPath return CreateAudioContentResponse(contentId = audioContent.id!!) } @@ -438,9 +422,6 @@ class AudioContentService( } fun getDetail(id: Long, member: Member, timezone: String): GetAudioContentDetailResponse { - // 묶음 콘텐츠 조회 - val bundleAudioContentList = repository.findBundleByContentId(contentId = id) - // 오디오 콘텐츠 조회 (content_id, 제목, 내용, 테마, 태그, 19여부, 이미지, 콘텐츠 PATH) val audioContent = repository.findByIdOrNull(id) ?: throw SodaException("잘못된 콘텐츠 입니다.\n다시 시도해 주세요.") @@ -455,23 +436,16 @@ class AudioContentService( memberId = member.id!! ) - // 구매 여부 확인 - val isExistsBundleAudioContent = bundleAudioContentList - .map { orderRepository.isExistOrdered(memberId = member.id!!, contentId = it.id!!) } - .contains(true) - val (isExistsAudioContent, orderType) = orderRepository.isExistOrderedAndOrderType( memberId = member.id!!, contentId = audioContent.id!! ) - val existOrdered = isExistsBundleAudioContent || isExistsAudioContent - // 차단된 사용자 체크 val isBlocked = blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = creatorId) - if (isBlocked && !existOrdered) throw SodaException("${creator.nickname}님의 요청으로 콘텐츠 접근이 제한됩니다.") + if (isBlocked && !isExistsAudioContent) throw SodaException("${creator.nickname}님의 요청으로 콘텐츠 접근이 제한됩니다.") - val orderSequence = if (existOrdered) { + val orderSequence = if (isExistsAudioContent) { limitedEditionOrderRepository.getOrderSequence( contentId = audioContent.id!!, memberId = member.id!! @@ -481,7 +455,7 @@ class AudioContentService( } if ( - !existOrdered && + !isExistsAudioContent && !audioContent.isActive && audioContent.releaseDate != null && audioContent.releaseDate!! < LocalDateTime.now() @@ -536,7 +510,6 @@ class AudioContentService( audioContentCloudFront.generateSignedURL( resourcePath = if ( isExistsAudioContent || - isExistsBundleAudioContent || audioContent.member!!.id!! == member.id!! || audioContent.price <= 0 ) { @@ -604,6 +577,18 @@ class AudioContentService( false } + val isOnlyRental = if (audioContent.purchaseOption == PurchaseOption.RENT_ONLY) { + true + } else { + audioContent.isOnlyRental + } + + val purchaseOption = if (audioContent.isOnlyRental) { + PurchaseOption.RENT_ONLY + } else { + audioContent.purchaseOption + } + return GetAudioContentDetailResponse( contentId = audioContent.id!!, title = audioContent.title, @@ -621,8 +606,9 @@ class AudioContentService( isActivePreview = audioContent.isGeneratePreview, isAdult = audioContent.isAdult, isMosaic = audioContent.isAdult && member.auth == null, - isOnlyRental = audioContent.isOnlyRental, - existOrdered = existOrdered, + isOnlyRental = isOnlyRental, + existOrdered = isExistsAudioContent, + purchaseOption = purchaseOption, orderType = orderType, remainingTime = remainingTime, creatorOtherContentList = creatorOtherContentList, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/BundleAudioContent.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/BundleAudioContent.kt deleted file mode 100644 index 375e819..0000000 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/BundleAudioContent.kt +++ /dev/null @@ -1,22 +0,0 @@ -package kr.co.vividnext.sodalive.content - -import kr.co.vividnext.sodalive.common.BaseEntity -import javax.persistence.Entity -import javax.persistence.FetchType -import javax.persistence.JoinColumn -import javax.persistence.ManyToOne -import javax.persistence.Table - -@Entity -@Table(name = "bundle_content") -data class BundleAudioContent( - var isActive: Boolean = true -) : BaseEntity() { - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "parent_content_id", nullable = false) - var parent: AudioContent? = null - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "child_content_id", nullable = false) - var child: AudioContent? = null -} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/CreateAudioContentRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/CreateAudioContentRequest.kt index 7a155c1..3c4e6bf 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/CreateAudioContentRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/CreateAudioContentRequest.kt @@ -5,6 +5,7 @@ data class CreateAudioContentRequest( val detail: String, val tags: String, val price: Int, + val purchaseOption: PurchaseOption = PurchaseOption.BOTH, val limited: Int? = null, val timezone: String = "Asia/Seoul", val releaseDate: String? = null, @@ -13,8 +14,6 @@ data class CreateAudioContentRequest( val isGeneratePreview: Boolean = false, val isOnlyRental: Boolean = false, val isCommentAvailable: Boolean = false, - val type: AudioContentType = AudioContentType.INDIVIDUAL, - val childIds: List? = null, val previewStartTime: String? = null, val previewEndTime: String? = null ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/GetAudioContentDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/GetAudioContentDetailResponse.kt index baf2cbc..d7346ef 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/GetAudioContentDetailResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/GetAudioContentDetailResponse.kt @@ -23,6 +23,7 @@ data class GetAudioContentDetailResponse( val isMosaic: Boolean, val isOnlyRental: Boolean, val existOrdered: Boolean, + val purchaseOption: PurchaseOption, val orderType: OrderType?, val remainingTime: String?, val creatorOtherContentList: List, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderService.kt index 4826816..630b22a 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderService.kt @@ -5,6 +5,7 @@ import kr.co.vividnext.sodalive.can.use.CanUsage import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.content.AudioContent import kr.co.vividnext.sodalive.content.AudioContentRepository +import kr.co.vividnext.sodalive.content.PurchaseOption import kr.co.vividnext.sodalive.content.comment.AudioContentCommentRepository import kr.co.vividnext.sodalive.content.like.AudioContentLikeRepository import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem @@ -31,7 +32,7 @@ class OrderService( fun order(contentId: Long, orderType: OrderType, container: String, member: Member) { val content = audioContentRepository.findByIdAndActive(contentId) ?: throw SodaException("잘못된 콘텐츠 입니다\n다시 시도해 주세요.") - validateOrder(memberId = member.id!!, content = content) + validateOrder(memberId = member.id!!, content = content, orderType = orderType) val order = if (content.limited != null && content.remaining != null) { if (content.remaining!! <= 0) throw SodaException("해당 콘텐츠가 매진되었습니다.") @@ -75,11 +76,17 @@ class OrderService( return order } - private fun validateOrder(memberId: Long, content: AudioContent) { + private fun validateOrder(memberId: Long, content: AudioContent, orderType: OrderType) { if (memberId == content.member!!.id!!) throw SodaException("자신이 올린 콘텐츠는 구매할 수 없습니다.") - if (repository.isExistOrdered(memberId = memberId, contentId = content.id!!)) { - throw SodaException("이미 구매한 콘텐츠 입니다.") - } + + val existOrdered = repository.isExistOrdered(memberId = memberId, contentId = content.id!!) + if (existOrdered) throw SodaException("이미 구매한 콘텐츠 입니다.") + + val isOnlyRental = content.purchaseOption == PurchaseOption.RENT_ONLY || content.isOnlyRental + if (isOnlyRental && orderType == OrderType.KEEP) throw SodaException("대여만 가능한 콘텐츠 입니다.") + + val isOnlyBuy = content.purchaseOption == PurchaseOption.BUY_ONLY && orderType == OrderType.RENTAL + if (isOnlyBuy) throw SodaException("소장만 가능한 콘텐츠 입니다.\n앱 업데이트 후 구매해 주세요.") } fun getAudioContentOrderList(