Merge pull request 'test' (#152) from test into main

Reviewed-on: #152
This commit is contained in:
klaus 2024-03-28 06:31:43 +00:00
commit fb5641343e
11 changed files with 147 additions and 19 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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