| @@ -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<Charge, Long>, ChargeQueryRepository | ||||
| @@ -16,6 +17,7 @@ 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? | ||||
|     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" -> { | ||||
|   | ||||
| @@ -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("결제정보에 오류가 있습니다.") | ||||
|             } | ||||
|   | ||||
| @@ -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() | ||||
|     } | ||||
| } | ||||
| @@ -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") | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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) | ||||
|     } | ||||
| } | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user