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

Reviewed-on: #14
This commit is contained in:
klaus 2023-08-24 14:54:37 +00:00
commit 5feafe1b48
8 changed files with 224 additions and 3 deletions

View File

@ -9,6 +9,7 @@ import kr.co.vividnext.sodalive.can.payment.QPayment.payment
import kr.co.vividnext.sodalive.member.QMember.member import kr.co.vividnext.sodalive.member.QMember.member
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
import java.time.LocalDateTime
@Repository @Repository
interface ChargeRepository : JpaRepository<Charge, Long>, ChargeQueryRepository interface ChargeRepository : JpaRepository<Charge, Long>, ChargeQueryRepository
@ -16,6 +17,7 @@ interface ChargeRepository : JpaRepository<Charge, Long>, ChargeQueryRepository
interface ChargeQueryRepository { interface ChargeQueryRepository {
fun getOldestChargeWhereRewardCanGreaterThan0(chargeId: Long, memberId: Long, container: String): Charge? fun getOldestChargeWhereRewardCanGreaterThan0(chargeId: Long, memberId: Long, container: String): Charge?
fun getOldestChargeWhereChargeCanGreaterThan0(chargeId: Long, memberId: Long, container: String): Charge? fun getOldestChargeWhereChargeCanGreaterThan0(chargeId: Long, memberId: Long, container: String): Charge?
fun getChargeCountAfterDate(date: LocalDateTime): Int
} }
class ChargeQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : ChargeQueryRepository { class ChargeQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : ChargeQueryRepository {
@ -59,6 +61,10 @@ class ChargeQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Cha
.fetchFirst() .fetchFirst()
} }
override fun getChargeCountAfterDate(date: LocalDateTime): Int {
return 0
}
private fun getPaymentGatewayCondition(container: String): BooleanExpression? { private fun getPaymentGatewayCondition(container: String): BooleanExpression? {
val paymentGatewayCondition = when (container) { val paymentGatewayCondition = when (container) {
"aos" -> { "aos" -> {

View File

@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.can.charge
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import kr.co.bootpay.Bootpay import kr.co.bootpay.Bootpay
import kr.co.vividnext.sodalive.can.CanRepository import kr.co.vividnext.sodalive.can.CanRepository
import kr.co.vividnext.sodalive.can.charge.event.ChargeSpringEvent
import kr.co.vividnext.sodalive.can.payment.Payment import kr.co.vividnext.sodalive.can.payment.Payment
import kr.co.vividnext.sodalive.can.payment.PaymentGateway import kr.co.vividnext.sodalive.can.payment.PaymentGateway
import kr.co.vividnext.sodalive.can.payment.PaymentStatus import kr.co.vividnext.sodalive.can.payment.PaymentStatus
@ -15,6 +16,7 @@ import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject import org.json.JSONObject
import org.springframework.beans.factory.annotation.Value import org.springframework.beans.factory.annotation.Value
import org.springframework.context.ApplicationEventPublisher
import org.springframework.data.repository.findByIdOrNull import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpHeaders import org.springframework.http.HttpHeaders
import org.springframework.security.core.userdetails.User import org.springframework.security.core.userdetails.User
@ -29,6 +31,8 @@ class ChargeService(
private val memberRepository: MemberRepository, private val memberRepository: MemberRepository,
private val objectMapper: ObjectMapper, private val objectMapper: ObjectMapper,
private val okHttpClient: OkHttpClient, private val okHttpClient: OkHttpClient,
private val applicationEventPublisher: ApplicationEventPublisher,
@Value("\${bootpay.application-id}") @Value("\${bootpay.application-id}")
private val bootpayApplicationId: String, private val bootpayApplicationId: String,
@Value("\${bootpay.private-key}") @Value("\${bootpay.private-key}")
@ -80,6 +84,8 @@ class ChargeService(
charge.payment?.method = verifyResult.method charge.payment?.method = verifyResult.method
charge.payment?.status = PaymentStatus.COMPLETE charge.payment?.status = PaymentStatus.COMPLETE
member.charge(charge.chargeCan, charge.rewardCan, "pg") member.charge(charge.chargeCan, charge.rewardCan, "pg")
applicationEventPublisher.publishEvent(ChargeSpringEvent(chargeId = charge.id!!, member = member))
} else { } else {
throw SodaException("결제정보에 오류가 있습니다.") throw SodaException("결제정보에 오류가 있습니다.")
} }
@ -127,6 +133,8 @@ class ChargeService(
charge.payment?.method = "애플(인 앱 결제)" charge.payment?.method = "애플(인 앱 결제)"
charge.payment?.status = PaymentStatus.COMPLETE charge.payment?.status = PaymentStatus.COMPLETE
member.charge(charge.chargeCan, charge.rewardCan, "ios") member.charge(charge.chargeCan, charge.rewardCan, "ios")
applicationEventPublisher.publishEvent(ChargeSpringEvent(chargeId = charge.id!!, member = member))
} else { } else {
throw SodaException("결제정보에 오류가 있습니다.") throw SodaException("결제정보에 오류가 있습니다.")
} }

View File

@ -0,0 +1,54 @@
package kr.co.vividnext.sodalive.can.charge.event
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.admin.event.ChargeEvent
import kr.co.vividnext.sodalive.admin.event.QChargeEvent.chargeEvent
import kr.co.vividnext.sodalive.can.charge.QCharge.charge
import kr.co.vividnext.sodalive.can.payment.PaymentStatus
import kr.co.vividnext.sodalive.can.payment.QPayment.payment
import kr.co.vividnext.sodalive.member.Member
import org.springframework.data.jpa.repository.JpaRepository
import java.time.LocalDateTime
interface ChargeEventRepository : JpaRepository<ChargeEvent, Long>, ChargeEventQueryRepository
interface ChargeEventQueryRepository {
fun getChargeEvent(): ChargeEvent?
fun getPaymentCount(member: Member, method: String, startDate: LocalDateTime, endDate: LocalDateTime): Int
}
class ChargeEventQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : ChargeEventQueryRepository {
override fun getChargeEvent(): ChargeEvent? {
val now = LocalDateTime.now()
return queryFactory
.selectFrom(chargeEvent)
.where(
chargeEvent.isActive.isTrue
.and(chargeEvent.startDate.loe(now))
.and(chargeEvent.endDate.goe(now))
)
.orderBy(chargeEvent.id.asc())
.fetchFirst()
}
override fun getPaymentCount(
member: Member,
method: String,
startDate: LocalDateTime,
endDate: LocalDateTime
): Int {
val where = charge.member.eq(member)
.and(charge.payment.method.eq(method))
.and(
charge.payment.status.eq(PaymentStatus.COMPLETE)
.or(charge.payment.status.eq(PaymentStatus.RETURN))
)
return queryFactory
.selectFrom(charge)
.innerJoin(charge.payment, payment)
.where(where)
.fetch()
.count()
}
}

View File

@ -0,0 +1,118 @@
package kr.co.vividnext.sodalive.can.charge.event
import kr.co.vividnext.sodalive.can.charge.Charge
import kr.co.vividnext.sodalive.can.charge.ChargeRepository
import kr.co.vividnext.sodalive.can.charge.ChargeStatus
import kr.co.vividnext.sodalive.can.payment.Payment
import kr.co.vividnext.sodalive.can.payment.PaymentGateway
import kr.co.vividnext.sodalive.can.payment.PaymentStatus
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.fcm.FcmEvent
import kr.co.vividnext.sodalive.fcm.FcmEventType
import kr.co.vividnext.sodalive.member.Member
import org.springframework.context.ApplicationEventPublisher
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import kotlin.math.ceil
import kotlin.math.round
@Service
class ChargeEventService(
private val repository: ChargeEventRepository,
private val chargeRepository: ChargeRepository,
private val chargeEventRepository: ChargeEventRepository,
private val applicationEventPublisher: ApplicationEventPublisher
) {
@Transactional
fun applyChargeEvent(chargeId: Long, member: Member) {
val charge = chargeRepository.findByIdOrNull(chargeId)
?: throw SodaException("이벤트가 적용되지 않았습니다.")
if (member.auth != null) {
val authDate = member.auth!!.createdAt!!
val chargeCount = chargeRepository.getChargeCountAfterDate(authDate)
if (chargeCount > 0) {
applyOtherEvent(charge, member)
} else {
applyFirstChargeEvent(charge, member)
}
} else {
applyOtherEvent(charge, member)
}
}
private fun applyOtherEvent(charge: Charge, member: Member) {
val chargeEvent = repository.getChargeEvent()
if (chargeEvent != null) {
val eventChargeCount = chargeEventRepository.getPaymentCount(
member = member,
method = chargeEvent.title,
startDate = chargeEvent.startDate,
endDate = chargeEvent.endDate
)
if (eventChargeCount < chargeEvent.availableCount) {
val additionalCan = round(charge.chargeCan * chargeEvent.addPercent).toInt()
applyEvent(
additionalCan = additionalCan,
member = member,
paymentGateway = charge.payment?.paymentGateway!!,
method = chargeEvent.title
)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.INDIVIDUAL,
title = chargeEvent.title,
message = "$additionalCan 캔이 추가 지급되었습니다.",
recipients = listOf(member.id!!),
isAuth = false
)
)
}
}
}
private fun applyFirstChargeEvent(charge: Charge, member: Member) {
val additionalCan = ceil(charge.chargeCan * 0.2).toInt()
applyEvent(
additionalCan = additionalCan,
member = member,
paymentGateway = charge.payment?.paymentGateway!!,
method = "첫 충전 이벤트"
)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.INDIVIDUAL,
title = "첫 충전 이벤트",
message = "$additionalCan 캔이 추가 지급되었습니다.",
recipients = listOf(member.id!!),
isAuth = false
)
)
}
private fun applyEvent(additionalCan: Int, member: Member, paymentGateway: PaymentGateway, method: String) {
val eventCharge = Charge(0, additionalCan, status = ChargeStatus.EVENT)
eventCharge.title = "$additionalCan"
eventCharge.member = member
val payment = Payment(
status = PaymentStatus.COMPLETE,
paymentGateway = paymentGateway
)
payment.method = method
eventCharge.payment = payment
chargeRepository.save(eventCharge)
when (paymentGateway) {
PaymentGateway.PG -> member.charge(0, additionalCan, "pg")
PaymentGateway.GOOGLE_IAP -> member.charge(0, additionalCan, "aos")
PaymentGateway.APPLE_IAP -> member.charge(0, additionalCan, "ios")
}
}
}

View File

@ -0,0 +1,22 @@
package kr.co.vividnext.sodalive.can.charge.event
import kr.co.vividnext.sodalive.member.Member
import org.springframework.context.event.EventListener
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Component
class ChargeSpringEvent(
val chargeId: Long,
val member: Member
)
@Component
class ChargeSpringEventListener(
private val chargeEventService: ChargeEventService
) {
@Async
@EventListener
fun applyChargeEvent(event: ChargeSpringEvent) {
chargeEventService.applyChargeEvent(event.chargeId, event.member)
}
}

View File

@ -265,6 +265,17 @@ class AudioContentService(
audioContent.content = content audioContent.content = content
audioContent.duration = duration audioContent.duration = duration
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.INDIVIDUAL,
title = "콘텐츠 등록완료",
message = audioContent.title,
recipients = listOf(audioContent.member!!.id!!),
isAuth = audioContent.isAdult,
contentId = contentId
)
)
applicationEventPublisher.publishEvent( applicationEventPublisher.publishEvent(
FcmEvent( FcmEvent(
type = FcmEventType.UPLOAD_CONTENT, type = FcmEventType.UPLOAD_CONTENT,

View File

@ -187,7 +187,7 @@ class OrderQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Orde
audioContent.title, audioContent.title,
audioContent.isAdult, audioContent.isAdult,
member.id, member.id,
member.profileImage.nullif("profile/default-profile.png") member.profileImage
.prepend("/") .prepend("/")
.prepend(coverImageHost), .prepend(coverImageHost),
member.nickname member.nickname

View File

@ -65,7 +65,8 @@ class FcmSendListener(
tokens = tokens, tokens = tokens,
title = fcmEvent.title, title = fcmEvent.title,
message = fcmEvent.message, message = fcmEvent.message,
container = "ios" container = "ios",
contentId = fcmEvent.contentId
) )
} }
} }
@ -76,7 +77,8 @@ class FcmSendListener(
tokens = tokens, tokens = tokens,
title = fcmEvent.title, title = fcmEvent.title,
message = fcmEvent.message, message = fcmEvent.message,
container = "aos" container = "aos",
contentId = fcmEvent.contentId
) )
} }
} }