commit
fb5641343e
|
@ -82,6 +82,8 @@ class AdminAudioContentQueryRepositoryImpl(
|
||||||
audioContentTheme.theme,
|
audioContentTheme.theme,
|
||||||
audioContentTheme.id,
|
audioContentTheme.id,
|
||||||
audioContent.price,
|
audioContent.price,
|
||||||
|
audioContent.limited,
|
||||||
|
audioContent.remaining,
|
||||||
audioContent.isAdult,
|
audioContent.isAdult,
|
||||||
audioContent.duration,
|
audioContent.duration,
|
||||||
audioContent.content,
|
audioContent.content,
|
||||||
|
|
|
@ -18,6 +18,8 @@ data class GetAdminContentListItem @QueryProjection constructor(
|
||||||
val theme: String,
|
val theme: String,
|
||||||
val themeId: Long,
|
val themeId: Long,
|
||||||
val price: Int,
|
val price: Int,
|
||||||
|
val totalContentCount: Int?,
|
||||||
|
val remainingContentCount: Int?,
|
||||||
val isAdult: Boolean,
|
val isAdult: Boolean,
|
||||||
val remainingTime: String,
|
val remainingTime: String,
|
||||||
var contentUrl: String,
|
var contentUrl: String,
|
||||||
|
|
|
@ -34,6 +34,8 @@ data class AudioContent(
|
||||||
var detail: String,
|
var detail: String,
|
||||||
val price: Int = 0,
|
val price: Int = 0,
|
||||||
var releaseDate: LocalDateTime? = null,
|
var releaseDate: LocalDateTime? = null,
|
||||||
|
val limited: Int? = null,
|
||||||
|
var remaining: Int? = null,
|
||||||
@Enumerated(value = EnumType.STRING)
|
@Enumerated(value = EnumType.STRING)
|
||||||
val type: AudioContentType = AudioContentType.INDIVIDUAL,
|
val type: AudioContentType = AudioContentType.INDIVIDUAL,
|
||||||
val isGeneratePreview: Boolean = true,
|
val isGeneratePreview: Boolean = true,
|
||||||
|
|
|
@ -14,6 +14,7 @@ import kr.co.vividnext.sodalive.content.like.AudioContentLikeRepository
|
||||||
import kr.co.vividnext.sodalive.content.like.PutAudioContentLikeRequest
|
import kr.co.vividnext.sodalive.content.like.PutAudioContentLikeRequest
|
||||||
import kr.co.vividnext.sodalive.content.like.PutAudioContentLikeResponse
|
import kr.co.vividnext.sodalive.content.like.PutAudioContentLikeResponse
|
||||||
import kr.co.vividnext.sodalive.content.main.GetAudioContentRanking
|
import kr.co.vividnext.sodalive.content.main.GetAudioContentRanking
|
||||||
|
import kr.co.vividnext.sodalive.content.order.LimitedEditionOrderRepository
|
||||||
import kr.co.vividnext.sodalive.content.order.OrderRepository
|
import kr.co.vividnext.sodalive.content.order.OrderRepository
|
||||||
import kr.co.vividnext.sodalive.content.order.OrderType
|
import kr.co.vividnext.sodalive.content.order.OrderType
|
||||||
import kr.co.vividnext.sodalive.content.pin.PinContent
|
import kr.co.vividnext.sodalive.content.pin.PinContent
|
||||||
|
@ -47,6 +48,7 @@ class AudioContentService(
|
||||||
private val blockMemberRepository: BlockMemberRepository,
|
private val blockMemberRepository: BlockMemberRepository,
|
||||||
private val hashTagRepository: HashTagRepository,
|
private val hashTagRepository: HashTagRepository,
|
||||||
private val orderRepository: OrderRepository,
|
private val orderRepository: OrderRepository,
|
||||||
|
private val limitedEditionOrderRepository: LimitedEditionOrderRepository,
|
||||||
private val themeQueryRepository: AudioContentThemeQueryRepository,
|
private val themeQueryRepository: AudioContentThemeQueryRepository,
|
||||||
private val playbackTrackingRepository: PlaybackTrackingRepository,
|
private val playbackTrackingRepository: PlaybackTrackingRepository,
|
||||||
private val commentRepository: AudioContentCommentRepository,
|
private val commentRepository: AudioContentCommentRepository,
|
||||||
|
@ -183,13 +185,15 @@ class AudioContentService(
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
releaseDate = releaseDate,
|
releaseDate = releaseDate,
|
||||||
|
limited = request.limited,
|
||||||
|
remaining = request.limited,
|
||||||
isAdult = request.isAdult,
|
isAdult = request.isAdult,
|
||||||
isGeneratePreview = if (request.type == AudioContentType.INDIVIDUAL) {
|
isGeneratePreview = if (request.type == AudioContentType.INDIVIDUAL) {
|
||||||
request.isGeneratePreview
|
request.isGeneratePreview
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
isOnlyRental = request.isOnlyRental,
|
isOnlyRental = if (request.limited != null && request.limited > 0) false else request.isOnlyRental,
|
||||||
isCommentAvailable = request.isCommentAvailable
|
isCommentAvailable = request.isCommentAvailable
|
||||||
)
|
)
|
||||||
audioContent.theme = theme
|
audioContent.theme = theme
|
||||||
|
@ -463,11 +467,21 @@ class AudioContentService(
|
||||||
contentId = audioContent.id!!
|
contentId = audioContent.id!!
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val existOrdered = isExistsBundleAudioContent || isExistsAudioContent
|
||||||
|
val orderSequence = if (existOrdered) {
|
||||||
|
limitedEditionOrderRepository.getOrderSequence(
|
||||||
|
contentId = audioContent.id!!,
|
||||||
|
memberId = member.id!!
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isExistsAudioContent &&
|
!existOrdered &&
|
||||||
!isExistsBundleAudioContent &&
|
|
||||||
!audioContent.isActive &&
|
!audioContent.isActive &&
|
||||||
audioContent.releaseDate == null
|
audioContent.releaseDate != null &&
|
||||||
|
audioContent.releaseDate!! < LocalDateTime.now()
|
||||||
) {
|
) {
|
||||||
throw SodaException("잘못된 콘텐츠 입니다.\n다시 시도해 주세요.")
|
throw SodaException("잘못된 콘텐츠 입니다.\n다시 시도해 주세요.")
|
||||||
}
|
}
|
||||||
|
@ -591,11 +605,14 @@ class AudioContentService(
|
||||||
price = audioContent.price,
|
price = audioContent.price,
|
||||||
duration = audioContent.duration ?: "",
|
duration = audioContent.duration ?: "",
|
||||||
releaseDate = releaseDate,
|
releaseDate = releaseDate,
|
||||||
|
totalContentCount = audioContent.limited,
|
||||||
|
remainingContentCount = audioContent.remaining,
|
||||||
|
orderSequence = orderSequence,
|
||||||
isActivePreview = audioContent.isGeneratePreview,
|
isActivePreview = audioContent.isGeneratePreview,
|
||||||
isAdult = audioContent.isAdult,
|
isAdult = audioContent.isAdult,
|
||||||
isMosaic = audioContent.isAdult && member.auth == null,
|
isMosaic = audioContent.isAdult && member.auth == null,
|
||||||
isOnlyRental = audioContent.isOnlyRental,
|
isOnlyRental = audioContent.isOnlyRental,
|
||||||
existOrdered = isExistsBundleAudioContent || isExistsAudioContent,
|
existOrdered = existOrdered,
|
||||||
orderType = orderType,
|
orderType = orderType,
|
||||||
remainingTime = remainingTime,
|
remainingTime = remainingTime,
|
||||||
creatorOtherContentList = creatorOtherContentList,
|
creatorOtherContentList = creatorOtherContentList,
|
||||||
|
|
|
@ -5,6 +5,7 @@ data class CreateAudioContentRequest(
|
||||||
val detail: String,
|
val detail: String,
|
||||||
val tags: String,
|
val tags: String,
|
||||||
val price: Int,
|
val price: Int,
|
||||||
|
val limited: Int? = null,
|
||||||
val timezone: String = "Asia/Seoul",
|
val timezone: String = "Asia/Seoul",
|
||||||
val releaseDate: String? = null,
|
val releaseDate: String? = null,
|
||||||
val themeId: Long = 0,
|
val themeId: Long = 0,
|
||||||
|
|
|
@ -15,6 +15,9 @@ data class GetAudioContentDetailResponse(
|
||||||
val price: Int,
|
val price: Int,
|
||||||
val duration: String,
|
val duration: String,
|
||||||
val releaseDate: String?,
|
val releaseDate: String?,
|
||||||
|
val totalContentCount: Int?,
|
||||||
|
val remainingContentCount: Int?,
|
||||||
|
val orderSequence: Int?,
|
||||||
val isActivePreview: Boolean,
|
val isActivePreview: Boolean,
|
||||||
val isAdult: Boolean,
|
val isAdult: Boolean,
|
||||||
val isMosaic: Boolean,
|
val isMosaic: Boolean,
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package kr.co.vividnext.sodalive.content.order
|
||||||
|
|
||||||
|
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
|
||||||
|
import javax.persistence.OneToOne
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class LimitedEditionOrder(
|
||||||
|
val sequence: Int
|
||||||
|
) : BaseEntity() {
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "content_id", nullable = false)
|
||||||
|
var audioContent: AudioContent? = null
|
||||||
|
|
||||||
|
@OneToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "order_id", nullable = false)
|
||||||
|
var order: Order? = null
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package kr.co.vividnext.sodalive.content.order
|
||||||
|
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
|
||||||
|
import kr.co.vividnext.sodalive.content.order.QLimitedEditionOrder.limitedEditionOrder
|
||||||
|
import kr.co.vividnext.sodalive.content.order.QOrder.order
|
||||||
|
import kr.co.vividnext.sodalive.member.QMember.member
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
|
||||||
|
interface LimitedEditionOrderRepository : JpaRepository<LimitedEditionOrder, Long>, LimitedEditionOrderQueryRepository
|
||||||
|
|
||||||
|
interface LimitedEditionOrderQueryRepository {
|
||||||
|
fun getNextSequence(contentId: Long): Int
|
||||||
|
|
||||||
|
fun getOrderSequence(contentId: Long, memberId: Long): Int?
|
||||||
|
}
|
||||||
|
|
||||||
|
class LimitedEditionOrderQueryRepositoryImpl(
|
||||||
|
private val queryFactory: JPAQueryFactory
|
||||||
|
) : LimitedEditionOrderQueryRepository {
|
||||||
|
override fun getNextSequence(contentId: Long): Int {
|
||||||
|
val maxSequence = queryFactory.select(limitedEditionOrder.sequence)
|
||||||
|
.from(limitedEditionOrder)
|
||||||
|
.innerJoin(limitedEditionOrder.audioContent, audioContent)
|
||||||
|
.where(limitedEditionOrder.audioContent.id.eq(contentId))
|
||||||
|
.orderBy(limitedEditionOrder.sequence.desc())
|
||||||
|
.limit(1)
|
||||||
|
.fetchFirst()
|
||||||
|
|
||||||
|
return if (maxSequence == null) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
maxSequence + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOrderSequence(contentId: Long, memberId: Long): Int? {
|
||||||
|
return queryFactory.select(limitedEditionOrder.sequence)
|
||||||
|
.from(limitedEditionOrder)
|
||||||
|
.innerJoin(limitedEditionOrder.audioContent, audioContent)
|
||||||
|
.innerJoin(limitedEditionOrder.order, order)
|
||||||
|
.innerJoin(order.member, member)
|
||||||
|
.where(
|
||||||
|
limitedEditionOrder.audioContent.id.eq(contentId)
|
||||||
|
.and(limitedEditionOrder.order.member.id.eq(memberId))
|
||||||
|
)
|
||||||
|
.fetchFirst()
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.content.order
|
||||||
import kr.co.vividnext.sodalive.can.payment.CanPaymentService
|
import kr.co.vividnext.sodalive.can.payment.CanPaymentService
|
||||||
import kr.co.vividnext.sodalive.can.use.CanUsage
|
import kr.co.vividnext.sodalive.can.use.CanUsage
|
||||||
import kr.co.vividnext.sodalive.common.SodaException
|
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.AudioContentRepository
|
||||||
import kr.co.vividnext.sodalive.content.comment.AudioContentCommentRepository
|
import kr.co.vividnext.sodalive.content.comment.AudioContentCommentRepository
|
||||||
import kr.co.vividnext.sodalive.content.like.AudioContentLikeRepository
|
import kr.co.vividnext.sodalive.content.like.AudioContentLikeRepository
|
||||||
|
@ -21,6 +22,7 @@ class OrderService(
|
||||||
private val audioContentRepository: AudioContentRepository,
|
private val audioContentRepository: AudioContentRepository,
|
||||||
private val audioContentCommentQueryRepository: AudioContentCommentRepository,
|
private val audioContentCommentQueryRepository: AudioContentCommentRepository,
|
||||||
private val audioContentLikeQueryRepository: AudioContentLikeRepository,
|
private val audioContentLikeQueryRepository: AudioContentLikeRepository,
|
||||||
|
private val limitedEditionOrderRepository: LimitedEditionOrderRepository,
|
||||||
|
|
||||||
@Value("\${cloud.aws.cloud-front.host}")
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
private val audioContentCoverImageHost: String
|
private val audioContentCoverImageHost: String
|
||||||
|
@ -29,24 +31,15 @@ class OrderService(
|
||||||
fun order(contentId: Long, orderType: OrderType, container: String, member: Member) {
|
fun order(contentId: Long, orderType: OrderType, container: String, member: Member) {
|
||||||
val content = audioContentRepository.findByIdAndActive(contentId)
|
val content = audioContentRepository.findByIdAndActive(contentId)
|
||||||
?: throw SodaException("잘못된 콘텐츠 입니다\n다시 시도해 주세요.")
|
?: throw SodaException("잘못된 콘텐츠 입니다\n다시 시도해 주세요.")
|
||||||
|
validateOrder(memberId = member.id!!, content = content)
|
||||||
|
|
||||||
val order = if (content.isOnlyRental) {
|
val order = if (content.limited != null && content.remaining != null) {
|
||||||
Order(type = OrderType.RENTAL)
|
if (content.remaining!! <= 0) throw SodaException("해당 콘텐츠가 매진되었습니다.")
|
||||||
|
orderLimitedEditionContent(content, member)
|
||||||
} else {
|
} else {
|
||||||
Order(type = orderType)
|
orderContent(orderType, content, member)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (member.id!! == content.member!!.id!!) throw SodaException("자신이 올린 콘텐츠는 구매할 수 없습니다.")
|
|
||||||
if (repository.isExistOrdered(memberId = member.id!!, contentId = contentId)) {
|
|
||||||
throw SodaException("이미 구매한 콘텐츠 입니다.")
|
|
||||||
}
|
|
||||||
|
|
||||||
order.member = member
|
|
||||||
order.creator = content.member
|
|
||||||
order.audioContent = content
|
|
||||||
|
|
||||||
repository.save(order)
|
|
||||||
|
|
||||||
canPaymentService.spendCan(
|
canPaymentService.spendCan(
|
||||||
memberId = member.id!!,
|
memberId = member.id!!,
|
||||||
needCan = order.can,
|
needCan = order.can,
|
||||||
|
@ -56,6 +49,39 @@ class OrderService(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun orderContent(orderType: OrderType, content: AudioContent, member: Member): Order {
|
||||||
|
val order = if (content.isOnlyRental) {
|
||||||
|
Order(type = OrderType.RENTAL)
|
||||||
|
} else {
|
||||||
|
Order(type = orderType)
|
||||||
|
}
|
||||||
|
order.member = member
|
||||||
|
order.creator = content.member
|
||||||
|
order.audioContent = content
|
||||||
|
return repository.save(order)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun orderLimitedEditionContent(content: AudioContent, member: Member): Order {
|
||||||
|
val order = orderContent(OrderType.KEEP, content, member)
|
||||||
|
|
||||||
|
val sequence = limitedEditionOrderRepository.getNextSequence(content.id!!)
|
||||||
|
val limitedEditionOrder = LimitedEditionOrder(sequence = sequence)
|
||||||
|
limitedEditionOrder.order = order
|
||||||
|
limitedEditionOrder.audioContent = content
|
||||||
|
limitedEditionOrderRepository.save(limitedEditionOrder)
|
||||||
|
|
||||||
|
content.remaining = content.remaining!! - 1
|
||||||
|
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateOrder(memberId: Long, content: AudioContent) {
|
||||||
|
if (memberId == content.member!!.id!!) throw SodaException("자신이 올린 콘텐츠는 구매할 수 없습니다.")
|
||||||
|
if (repository.isExistOrdered(memberId = memberId, contentId = content.id!!)) {
|
||||||
|
throw SodaException("이미 구매한 콘텐츠 입니다.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getAudioContentOrderList(
|
fun getAudioContentOrderList(
|
||||||
member: Member,
|
member: Member,
|
||||||
offset: Long,
|
offset: Long,
|
||||||
|
|
|
@ -81,6 +81,8 @@ class CreatorAdminAudioContentQueryRepositoryImpl(
|
||||||
audioContent.member!!.nickname,
|
audioContent.member!!.nickname,
|
||||||
audioContentTheme.theme,
|
audioContentTheme.theme,
|
||||||
audioContent.price,
|
audioContent.price,
|
||||||
|
audioContent.limited,
|
||||||
|
audioContent.remaining,
|
||||||
audioContent.isAdult,
|
audioContent.isAdult,
|
||||||
audioContent.isCommentAvailable,
|
audioContent.isCommentAvailable,
|
||||||
audioContent.duration,
|
audioContent.duration,
|
||||||
|
|
|
@ -15,6 +15,8 @@ data class GetCreatorAdminContentListItem @QueryProjection constructor(
|
||||||
val creatorNickname: String,
|
val creatorNickname: String,
|
||||||
val theme: String,
|
val theme: String,
|
||||||
val price: Int,
|
val price: Int,
|
||||||
|
val totalContentCount: Int?,
|
||||||
|
val remainingContentCount: Int?,
|
||||||
val isAdult: Boolean,
|
val isAdult: Boolean,
|
||||||
val isCommentAvailable: Boolean,
|
val isCommentAvailable: Boolean,
|
||||||
val remainingTime: String,
|
val remainingTime: String,
|
||||||
|
|
Loading…
Reference in New Issue