From 912667158163ef174d3cd4486718d1f241c606de Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 24 Aug 2023 18:03:06 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=EC=99=84=EB=A3=8C=20-=20=EC=BD=98=ED=85=90=EC=B8=A0?= =?UTF-8?q?=20=EB=93=B1=EB=A1=9D=EC=99=84=EB=A3=8C=20=EC=8B=9C=20=EC=BD=98?= =?UTF-8?q?=ED=85=90=EC=B8=A0=EB=A5=BC=20=EC=98=AC=EB=A6=B0=20=ED=81=AC?= =?UTF-8?q?=EB=A6=AC=EC=97=90=EC=9D=B4=ED=84=B0=EC=97=90=EA=B2=8C=20?= =?UTF-8?q?=ED=91=B8=EC=8B=9C=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=B0=9C?= =?UTF-8?q?=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vividnext/sodalive/content/AudioContentService.kt | 11 +++++++++++ .../kotlin/kr/co/vividnext/sodalive/fcm/FcmEvent.kt | 6 ++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt index 48dbe84..9e020c2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt @@ -265,6 +265,17 @@ class AudioContentService( audioContent.content = content 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( FcmEvent( type = FcmEventType.UPLOAD_CONTENT, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmEvent.kt b/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmEvent.kt index 1e9c196..17ace1f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmEvent.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmEvent.kt @@ -65,7 +65,8 @@ class FcmSendListener( tokens = tokens, title = fcmEvent.title, message = fcmEvent.message, - container = "ios" + container = "ios", + contentId = fcmEvent.contentId ) } } @@ -76,7 +77,8 @@ class FcmSendListener( tokens = tokens, title = fcmEvent.title, message = fcmEvent.message, - container = "aos" + container = "aos", + contentId = fcmEvent.contentId ) } } -- 2.40.1 From a47b40d9227e5b90413b2da594fa6d1dcf483198 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 24 Aug 2023 18:30:44 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=EC=A3=BC=EB=AC=B8=20=EB=A6=AC=ED=8F=AC?= =?UTF-8?q?=EC=A7=80=ED=86=A0=EB=A6=AC=20-=20=EC=BD=98=ED=85=90=EC=B8=A0?= =?UTF-8?q?=20=EB=A9=94=EC=9D=B8=20=EA=B5=AC=EB=A7=A4=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BF=BC=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/content/order/OrderRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderRepository.kt index b76ef6f..e0ee93f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/order/OrderRepository.kt @@ -187,7 +187,7 @@ class OrderQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Orde audioContent.title, audioContent.isAdult, member.id, - member.profileImage.nullif("profile/default-profile.png") + member.profileImage .prepend("/") .prepend(coverImageHost), member.nickname -- 2.40.1 From d414e3b300cc98e84634e3d1dc952ff60f026011 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 24 Aug 2023 22:13:11 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=EC=B6=A9=EC=A0=84=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/can/charge/ChargeRepository.kt | 6 + .../sodalive/can/charge/ChargeService.kt | 8 ++ .../can/charge/event/ChargeEventRepository.kt | 54 ++++++++ .../can/charge/event/ChargeEventService.kt | 118 ++++++++++++++++++ .../can/charge/event/ChargeSpringEvent.kt | 22 ++++ 5 files changed, 208 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeSpringEvent.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeRepository.kt index d8d5bf4..0b434d1 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeRepository.kt @@ -9,6 +9,7 @@ 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 +import java.time.LocalDateTime @Repository interface ChargeRepository : JpaRepository, ChargeQueryRepository @@ -16,6 +17,7 @@ interface ChargeRepository : JpaRepository, ChargeQueryRepository interface ChargeQueryRepository { fun getOldestChargeWhereRewardCanGreaterThan0(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 { @@ -59,6 +61,10 @@ class ChargeQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Cha .fetchFirst() } + override fun getChargeCountAfterDate(date: LocalDateTime): Int { + return 0 + } + private fun getPaymentGatewayCondition(container: String): BooleanExpression? { val paymentGatewayCondition = when (container) { "aos" -> { diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt index a9c7e21..493346c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt @@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.can.charge import com.fasterxml.jackson.databind.ObjectMapper import kr.co.bootpay.Bootpay 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.PaymentGateway import kr.co.vividnext.sodalive.can.payment.PaymentStatus @@ -15,6 +16,7 @@ import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import org.json.JSONObject import org.springframework.beans.factory.annotation.Value +import org.springframework.context.ApplicationEventPublisher import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpHeaders import org.springframework.security.core.userdetails.User @@ -29,6 +31,8 @@ class ChargeService( private val memberRepository: MemberRepository, private val objectMapper: ObjectMapper, private val okHttpClient: OkHttpClient, + private val applicationEventPublisher: ApplicationEventPublisher, + @Value("\${bootpay.application-id}") private val bootpayApplicationId: String, @Value("\${bootpay.private-key}") @@ -80,6 +84,8 @@ class ChargeService( charge.payment?.method = verifyResult.method charge.payment?.status = PaymentStatus.COMPLETE member.charge(charge.chargeCan, charge.rewardCan, "pg") + + applicationEventPublisher.publishEvent(ChargeSpringEvent(chargeId = charge.id!!, member = member)) } else { throw SodaException("결제정보에 오류가 있습니다.") } @@ -127,6 +133,8 @@ class ChargeService( charge.payment?.method = "애플(인 앱 결제)" charge.payment?.status = PaymentStatus.COMPLETE member.charge(charge.chargeCan, charge.rewardCan, "ios") + + applicationEventPublisher.publishEvent(ChargeSpringEvent(chargeId = charge.id!!, member = member)) } else { throw SodaException("결제정보에 오류가 있습니다.") } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventRepository.kt new file mode 100644 index 0000000..88a047b --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventRepository.kt @@ -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, 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() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventService.kt new file mode 100644 index 0000000..bdcda79 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventService.kt @@ -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") + } + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeSpringEvent.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeSpringEvent.kt new file mode 100644 index 0000000..2716f51 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeSpringEvent.kt @@ -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) + } +} -- 2.40.1