라이브 - 시작, 취소, 입장, 수정, 예약 API 추가
This commit is contained in:
@@ -27,6 +27,7 @@ interface CanQueryRepository {
|
||||
fun getCanUseStatus(member: Member, pageable: Pageable): List<UseCan>
|
||||
fun getCanChargeStatus(member: Member, pageable: Pageable, container: String): List<Charge>
|
||||
fun isExistPaidLiveRoom(memberId: Long, roomId: Long): UseCan?
|
||||
fun getCanUsedForLiveRoomNotRefund(memberId: Long, roomId: Long): UseCan?
|
||||
}
|
||||
|
||||
@Repository
|
||||
@@ -111,4 +112,19 @@ class CanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanQue
|
||||
.orderBy(useCan.id.desc())
|
||||
.fetchFirst()
|
||||
}
|
||||
|
||||
override fun getCanUsedForLiveRoomNotRefund(memberId: Long, roomId: Long): UseCan? {
|
||||
return queryFactory
|
||||
.selectFrom(useCan)
|
||||
.innerJoin(useCan.member, member)
|
||||
.innerJoin(useCan.room, liveRoom)
|
||||
.where(
|
||||
member.id.eq(memberId)
|
||||
.and(liveRoom.id.eq(roomId))
|
||||
.and(useCan.canUsage.eq(CanUsage.LIVE))
|
||||
.and(useCan.isRefund.isFalse)
|
||||
)
|
||||
.orderBy(useCan.id.desc())
|
||||
.fetchFirst()
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,78 @@
|
||||
package kr.co.vividnext.sodalive.can.charge
|
||||
|
||||
import com.querydsl.core.types.dsl.BooleanExpression
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||
import kr.co.vividnext.sodalive.can.charge.QCharge.charge
|
||||
import kr.co.vividnext.sodalive.can.payment.PaymentGateway
|
||||
import kr.co.vividnext.sodalive.can.payment.PaymentStatus
|
||||
import kr.co.vividnext.sodalive.can.payment.QPayment.payment
|
||||
import kr.co.vividnext.sodalive.member.QMember.member
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
interface ChargeRepository : JpaRepository<Charge, Long>
|
||||
interface ChargeRepository : JpaRepository<Charge, Long>, ChargeQueryRepository
|
||||
|
||||
interface ChargeQueryRepository {
|
||||
fun getOldestChargeWhereRewardCanGreaterThan0(chargeId: Long, memberId: Long, container: String): Charge?
|
||||
fun getOldestChargeWhereChargeCanGreaterThan0(chargeId: Long, memberId: Long, container: String): Charge?
|
||||
}
|
||||
|
||||
class ChargeQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : ChargeQueryRepository {
|
||||
override fun getOldestChargeWhereRewardCanGreaterThan0(
|
||||
chargeId: Long,
|
||||
memberId: Long,
|
||||
container: String
|
||||
): Charge? {
|
||||
return queryFactory
|
||||
.selectFrom(charge)
|
||||
.innerJoin(charge.member, member)
|
||||
.leftJoin(charge.payment, payment)
|
||||
.where(
|
||||
member.id.eq(memberId)
|
||||
.and(charge.rewardCan.gt(0))
|
||||
.and(charge.id.gt(chargeId))
|
||||
.and(payment.status.eq(PaymentStatus.COMPLETE))
|
||||
.and(getPaymentGatewayCondition(container))
|
||||
)
|
||||
.orderBy(charge.id.asc())
|
||||
.fetchFirst()
|
||||
}
|
||||
|
||||
override fun getOldestChargeWhereChargeCanGreaterThan0(
|
||||
chargeId: Long,
|
||||
memberId: Long,
|
||||
container: String
|
||||
): Charge? {
|
||||
return queryFactory
|
||||
.selectFrom(charge)
|
||||
.innerJoin(charge.member, member)
|
||||
.leftJoin(charge.payment, payment)
|
||||
.where(
|
||||
member.id.eq(memberId)
|
||||
.and(charge.chargeCan.gt(0))
|
||||
.and(charge.id.gt(chargeId))
|
||||
.and(payment.status.eq(PaymentStatus.COMPLETE))
|
||||
.and(getPaymentGatewayCondition(container))
|
||||
)
|
||||
.orderBy(charge.id.asc())
|
||||
.fetchFirst()
|
||||
}
|
||||
|
||||
private fun getPaymentGatewayCondition(container: String): BooleanExpression? {
|
||||
val paymentGatewayCondition = when (container) {
|
||||
"aos" -> {
|
||||
payment.paymentGateway.eq(PaymentGateway.PG)
|
||||
.or(payment.paymentGateway.eq(PaymentGateway.GOOGLE_IAP))
|
||||
}
|
||||
|
||||
"ios" -> {
|
||||
payment.paymentGateway.eq(PaymentGateway.PG)
|
||||
.or(payment.paymentGateway.eq(PaymentGateway.APPLE_IAP))
|
||||
}
|
||||
|
||||
else -> payment.paymentGateway.eq(PaymentGateway.PG)
|
||||
}
|
||||
return paymentGatewayCondition
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,245 @@
|
||||
package kr.co.vividnext.sodalive.can.payment
|
||||
|
||||
import kr.co.vividnext.sodalive.can.charge.ChargeRepository
|
||||
import kr.co.vividnext.sodalive.can.use.CanUsage
|
||||
import kr.co.vividnext.sodalive.can.use.SpentCan
|
||||
import kr.co.vividnext.sodalive.can.use.TotalSpentCan
|
||||
import kr.co.vividnext.sodalive.can.use.UseCan
|
||||
import kr.co.vividnext.sodalive.can.use.UseCanCalculate
|
||||
import kr.co.vividnext.sodalive.can.use.UseCanCalculateRepository
|
||||
import kr.co.vividnext.sodalive.can.use.UseCanCalculateStatus
|
||||
import kr.co.vividnext.sodalive.can.use.UseCanRepository
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoom
|
||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
|
||||
@Service
|
||||
class CanPaymentService(
|
||||
private val memberRepository: MemberRepository,
|
||||
private val chargeRepository: ChargeRepository,
|
||||
private val useCanRepository: UseCanRepository,
|
||||
private val useCanCalculateRepository: UseCanCalculateRepository
|
||||
) {
|
||||
@Transactional
|
||||
fun spendCan(
|
||||
memberId: Long,
|
||||
needCan: Int,
|
||||
canUsage: CanUsage,
|
||||
liveRoom: LiveRoom? = null,
|
||||
container: String
|
||||
) {
|
||||
val member = memberRepository.findByIdOrNull(id = memberId)
|
||||
?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.")
|
||||
val useRewardCan = spendRewardCan(memberId, needCan, container)
|
||||
val useChargeCan = if (needCan - useRewardCan.total > 0) {
|
||||
spendChargeCan(memberId, needCan = needCan - useRewardCan.total, container = container)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
if (needCan - useRewardCan.total - (useChargeCan?.total ?: 0) > 0) {
|
||||
throw SodaException(
|
||||
"${needCan - useRewardCan.total - (useChargeCan?.total ?: 0)} " +
|
||||
"캔이 부족합니다. 충전 후 이용해 주세요."
|
||||
)
|
||||
}
|
||||
|
||||
if (!useRewardCan.verify() || useChargeCan?.verify() == false) {
|
||||
throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.")
|
||||
}
|
||||
|
||||
val useCan = UseCan(
|
||||
canUsage = canUsage,
|
||||
can = useChargeCan?.total ?: 0,
|
||||
rewardCan = useRewardCan.total
|
||||
)
|
||||
|
||||
var recipientId: Long? = null
|
||||
if (canUsage == CanUsage.LIVE && liveRoom != null) {
|
||||
recipientId = liveRoom.member!!.id!!
|
||||
useCan.room = liveRoom
|
||||
useCan.member = member
|
||||
} else if (canUsage == CanUsage.CHANGE_NICKNAME) {
|
||||
useCan.member = member
|
||||
} else if (canUsage == CanUsage.DONATION && liveRoom != null) {
|
||||
recipientId = liveRoom.member!!.id!!
|
||||
useCan.room = liveRoom
|
||||
useCan.member = member
|
||||
} else {
|
||||
throw SodaException("잘못된 요청입니다.")
|
||||
}
|
||||
|
||||
useCanRepository.save(useCan)
|
||||
|
||||
setUseCoinCalculate(recipientId, useRewardCan, useChargeCan, useCan, paymentGateway = PaymentGateway.PG)
|
||||
setUseCoinCalculate(
|
||||
recipientId,
|
||||
useRewardCan,
|
||||
useChargeCan,
|
||||
useCan,
|
||||
paymentGateway = PaymentGateway.GOOGLE_IAP
|
||||
)
|
||||
setUseCoinCalculate(
|
||||
recipientId,
|
||||
useRewardCan,
|
||||
useChargeCan,
|
||||
useCan,
|
||||
paymentGateway = PaymentGateway.APPLE_IAP
|
||||
)
|
||||
}
|
||||
|
||||
private fun setUseCoinCalculate(
|
||||
recipientId: Long?,
|
||||
useRewardCan: TotalSpentCan,
|
||||
useChargeCan: TotalSpentCan?,
|
||||
useCan: UseCan,
|
||||
paymentGateway: PaymentGateway
|
||||
) {
|
||||
val totalSpentRewardCan = useRewardCan.spentCans
|
||||
.filter { it.paymentGateway == paymentGateway }
|
||||
.fold(0) { sum, spentCans -> sum + spentCans.can }
|
||||
|
||||
val useCanCalculate = if (useChargeCan != null) {
|
||||
val totalSpentChargeCan = useChargeCan.spentCans
|
||||
.filter { it.paymentGateway == paymentGateway }
|
||||
.fold(0) { sum, spentCans -> sum + spentCans.can }
|
||||
|
||||
UseCanCalculate(
|
||||
can = totalSpentChargeCan + totalSpentRewardCan,
|
||||
paymentGateway = paymentGateway,
|
||||
status = UseCanCalculateStatus.RECEIVED
|
||||
)
|
||||
} else {
|
||||
UseCanCalculate(
|
||||
can = totalSpentRewardCan,
|
||||
paymentGateway = paymentGateway,
|
||||
status = UseCanCalculateStatus.RECEIVED
|
||||
)
|
||||
}
|
||||
|
||||
if (useCanCalculate.can > 0) {
|
||||
useCanCalculate.useCan = useCan
|
||||
useCanCalculate.recipientCreatorId = recipientId
|
||||
useCanCalculateRepository.save(useCanCalculate)
|
||||
}
|
||||
}
|
||||
|
||||
private fun spendRewardCan(memberId: Long, needCan: Int, container: String): TotalSpentCan {
|
||||
return if (needCan > 0) {
|
||||
val member = memberRepository.findByIdOrNull(id = memberId)
|
||||
?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.")
|
||||
|
||||
val spentCans = mutableListOf<SpentCan>()
|
||||
var chargeId = 0L
|
||||
var total = 0
|
||||
|
||||
while (needCan - total > 0) {
|
||||
val remainingNeedCan = needCan - total
|
||||
val charge = chargeRepository.getOldestChargeWhereRewardCanGreaterThan0(chargeId, memberId, container)
|
||||
?: break
|
||||
|
||||
if (charge.rewardCan >= remainingNeedCan) {
|
||||
charge.rewardCan -= remainingNeedCan
|
||||
|
||||
when (charge.payment!!.paymentGateway) {
|
||||
PaymentGateway.PG -> member.pgRewardCan -= remainingNeedCan
|
||||
PaymentGateway.APPLE_IAP -> member.appleRewardCan -= remainingNeedCan
|
||||
PaymentGateway.GOOGLE_IAP -> member.pgRewardCan -= remainingNeedCan
|
||||
}
|
||||
|
||||
total += remainingNeedCan
|
||||
|
||||
spentCans.add(
|
||||
SpentCan(
|
||||
paymentGateway = charge.payment!!.paymentGateway,
|
||||
can = remainingNeedCan
|
||||
)
|
||||
)
|
||||
} else {
|
||||
total += charge.rewardCan
|
||||
spentCans.add(
|
||||
SpentCan(
|
||||
paymentGateway = charge.payment!!.paymentGateway,
|
||||
can = charge.rewardCan
|
||||
)
|
||||
)
|
||||
|
||||
when (charge.payment!!.paymentGateway) {
|
||||
PaymentGateway.PG -> member.pgRewardCan -= remainingNeedCan
|
||||
PaymentGateway.APPLE_IAP -> member.appleRewardCan -= remainingNeedCan
|
||||
PaymentGateway.GOOGLE_IAP -> member.pgRewardCan -= remainingNeedCan
|
||||
}
|
||||
|
||||
charge.rewardCan = 0
|
||||
}
|
||||
|
||||
chargeId = charge.id!!
|
||||
}
|
||||
|
||||
TotalSpentCan(spentCans, total)
|
||||
} else {
|
||||
TotalSpentCan(total = 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun spendChargeCan(memberId: Long, needCan: Int, container: String): TotalSpentCan {
|
||||
return if (needCan > 0) {
|
||||
val member = memberRepository.findByIdOrNull(id = memberId)
|
||||
?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.")
|
||||
|
||||
val spentCans = mutableListOf<SpentCan>()
|
||||
var chargeId = 0L
|
||||
var total = 0
|
||||
|
||||
while (needCan - total > 0) {
|
||||
val remainingNeedCan = needCan - total
|
||||
val charge = chargeRepository.getOldestChargeWhereChargeCanGreaterThan0(chargeId, memberId, container)
|
||||
?: break
|
||||
|
||||
if (charge.rewardCan >= remainingNeedCan) {
|
||||
charge.rewardCan -= remainingNeedCan
|
||||
|
||||
when (charge.payment!!.paymentGateway) {
|
||||
PaymentGateway.PG -> member.pgRewardCan -= remainingNeedCan
|
||||
PaymentGateway.APPLE_IAP -> member.appleRewardCan -= remainingNeedCan
|
||||
PaymentGateway.GOOGLE_IAP -> member.pgRewardCan -= remainingNeedCan
|
||||
}
|
||||
|
||||
total += remainingNeedCan
|
||||
|
||||
spentCans.add(
|
||||
SpentCan(
|
||||
paymentGateway = charge.payment!!.paymentGateway,
|
||||
can = remainingNeedCan
|
||||
)
|
||||
)
|
||||
} else {
|
||||
total += charge.rewardCan
|
||||
spentCans.add(
|
||||
SpentCan(
|
||||
paymentGateway = charge.payment!!.paymentGateway,
|
||||
can = charge.rewardCan
|
||||
)
|
||||
)
|
||||
|
||||
when (charge.payment!!.paymentGateway) {
|
||||
PaymentGateway.PG -> member.pgRewardCan -= remainingNeedCan
|
||||
PaymentGateway.APPLE_IAP -> member.appleRewardCan -= remainingNeedCan
|
||||
PaymentGateway.GOOGLE_IAP -> member.pgRewardCan -= remainingNeedCan
|
||||
}
|
||||
|
||||
charge.rewardCan = 0
|
||||
}
|
||||
|
||||
chargeId = charge.id!!
|
||||
}
|
||||
|
||||
TotalSpentCan(spentCans, total)
|
||||
} else {
|
||||
TotalSpentCan(total = 0)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package kr.co.vividnext.sodalive.can.use
|
||||
|
||||
import kr.co.vividnext.sodalive.can.payment.PaymentGateway
|
||||
|
||||
data class TotalSpentCan(
|
||||
val spentCans: MutableList<SpentCan> = mutableListOf(),
|
||||
val total: Int
|
||||
) {
|
||||
fun verify(): Boolean {
|
||||
val sumSpentCans = spentCans.fold(0) { sum, spentCan -> sum + spentCan.can }
|
||||
return total == sumSpentCans
|
||||
}
|
||||
}
|
||||
|
||||
data class SpentCan(
|
||||
val paymentGateway: PaymentGateway,
|
||||
val can: Int
|
||||
)
|
@@ -30,6 +30,8 @@ data class UseCanCalculate(
|
||||
value?.useCanCalculates?.add(this)
|
||||
field = value
|
||||
}
|
||||
|
||||
var recipientCreatorId: Long? = null
|
||||
}
|
||||
|
||||
enum class UseCanCalculateStatus {
|
||||
|
@@ -0,0 +1,12 @@
|
||||
package kr.co.vividnext.sodalive.can.use
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
interface UseCanCalculateRepository : JpaRepository<UseCanCalculate, Long> {
|
||||
fun findByUseCanIdAndStatus(
|
||||
useCanId: Long,
|
||||
status: UseCanCalculateStatus = UseCanCalculateStatus.RECEIVED
|
||||
): List<UseCanCalculate>
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package kr.co.vividnext.sodalive.can.use
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
interface UseCanRepository : JpaRepository<UseCan, Long>
|
Reference in New Issue
Block a user