라이브 - 시작, 취소, 입장, 수정, 예약 API 추가

This commit is contained in:
2023-07-31 17:09:45 +09:00
parent 197cca1f1b
commit f393c7630e
29 changed files with 1044 additions and 19 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,6 +30,8 @@ data class UseCanCalculate(
value?.useCanCalculates?.add(this)
field = value
}
var recipientCreatorId: Long? = null
}
enum class UseCanCalculateStatus {

View File

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

View File

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