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 493346c..a8756b5 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 @@ -85,7 +85,12 @@ class ChargeService( charge.payment?.status = PaymentStatus.COMPLETE member.charge(charge.chargeCan, charge.rewardCan, "pg") - applicationEventPublisher.publishEvent(ChargeSpringEvent(chargeId = charge.id!!, member = member)) + applicationEventPublisher.publishEvent( + ChargeSpringEvent( + chargeId = charge.id!!, + memberId = member.id!! + ) + ) } else { throw SodaException("결제정보에 오류가 있습니다.") } @@ -134,7 +139,12 @@ class ChargeService( charge.payment?.status = PaymentStatus.COMPLETE member.charge(charge.chargeCan, charge.rewardCan, "ios") - applicationEventPublisher.publishEvent(ChargeSpringEvent(chargeId = charge.id!!, member = member)) + applicationEventPublisher.publishEvent( + ChargeSpringEvent( + chargeId = charge.id!!, + memberId = member.id!! + ) + ) } else { throw SodaException("결제정보에 오류가 있습니다.") } 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 index 632b623..7a8ff3e 100644 --- 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 @@ -10,6 +10,7 @@ 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 kr.co.vividnext.sodalive.member.MemberRepository import kr.co.vividnext.sodalive.member.auth.AuthRepository import org.springframework.context.ApplicationEventPublisher import org.springframework.data.repository.findByIdOrNull @@ -22,25 +23,29 @@ import kotlin.math.round class ChargeEventService( private val repository: ChargeEventRepository, private val authRepository: AuthRepository, + private val memberRepository: MemberRepository, private val chargeRepository: ChargeRepository, private val chargeEventRepository: ChargeEventRepository, private val applicationEventPublisher: ApplicationEventPublisher ) { @Transactional - fun applyChargeEvent(chargeId: Long, member: Member) { + fun applyChargeEvent(chargeId: Long, memberId: Long) { val charge = chargeRepository.findByIdOrNull(chargeId) - ?: throw SodaException("이벤트가 적용되지 않았습니다.") + ?: throw SodaException("이벤트가 적용되지 않았습니다.\n고객센터에 문의해 주세요.") + + val member = memberRepository.findByIdOrNull(memberId) + ?: throw SodaException("이벤트가 적용되지 않았습니다.\n고객센터에 문의해 주세요.") if (member.auth != null) { val authDate = authRepository.getOldestCreatedAtByDi(member.auth!!.di) val memberIds = authRepository.getMemberIdsByDi(member.auth!!.di) var chargeCount = 0 - for (memberId in memberIds) { - chargeCount += chargeRepository.getChargeCountAfterDate(memberId = memberId, authDate) + for (id in memberIds) { + chargeCount += chargeRepository.getChargeCountAfterDate(memberId = id, authDate) } - if (chargeCount > 0) { + if (chargeCount > 1) { applyOtherEvent(charge, member) } else { applyFirstChargeEvent(charge, member) 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 index 2716f51..5a6b014 100644 --- 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 @@ -1,13 +1,12 @@ 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 +import org.springframework.transaction.event.TransactionalEventListener class ChargeSpringEvent( val chargeId: Long, - val member: Member + val memberId: Long ) @Component @@ -15,8 +14,8 @@ class ChargeSpringEventListener( private val chargeEventService: ChargeEventService ) { @Async - @EventListener + @TransactionalEventListener fun applyChargeEvent(event: ChargeSpringEvent) { - chargeEventService.applyChargeEvent(event.chargeId, event.member) + chargeEventService.applyChargeEvent(event.chargeId, event.memberId) } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt index 57b4036..f436dd2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt @@ -11,11 +11,14 @@ import kr.co.vividnext.sodalive.explorer.profile.CreatorCheers import kr.co.vividnext.sodalive.explorer.profile.CreatorCheersRepository import kr.co.vividnext.sodalive.explorer.profile.PostWriteCheersRequest import kr.co.vividnext.sodalive.explorer.profile.PutWriteCheersRequest +import kr.co.vividnext.sodalive.fcm.FcmEvent +import kr.co.vividnext.sodalive.fcm.FcmEventType import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.MemberRole import kr.co.vividnext.sodalive.member.MemberService import org.springframework.beans.factory.annotation.Value +import org.springframework.context.ApplicationEventPublisher import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -28,6 +31,7 @@ class ExplorerService( private val queryRepository: ExplorerQueryRepository, private val cheersRepository: CreatorCheersRepository, private val noticeRepository: ChannelNoticeRepository, + private val applicationEventPublisher: ApplicationEventPublisher, @Value("\${cloud.aws.cloud-front.host}") private val cloudFrontHost: String @@ -318,5 +322,14 @@ class ExplorerService( } else { channelNotice.notice = notice } + + applicationEventPublisher.publishEvent( + FcmEvent( + type = FcmEventType.CHANGE_NOTICE, + title = member.nickname, + message = "공지를 등록했습니다.", + creatorId = member.id!! + ) + ) } } 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 17ace1f..3f71cf0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmEvent.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmEvent.kt @@ -6,7 +6,7 @@ import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Component enum class FcmEventType { - ALL, INDIVIDUAL, CREATE_LIVE, START_LIVE, UPLOAD_CONTENT, SEND_MESSAGE + ALL, INDIVIDUAL, CREATE_LIVE, START_LIVE, UPLOAD_CONTENT, SEND_MESSAGE, CHANGE_NOTICE } class FcmEvent( @@ -156,6 +156,39 @@ class FcmSendListener( messageId = fcmEvent.messageId ) } + + FcmEventType.CHANGE_NOTICE -> { + if (fcmEvent.creatorId != null) { + val pushTokenList = memberRepository.getChangeNoticeRecipientPushTokens(fcmEvent.creatorId) + + val iosPushTokens = pushTokenList["ios"] + val aosPushToken = pushTokenList["aos"] + + if (iosPushTokens != null) { + for (tokens in iosPushTokens) { + pushService.send( + tokens = tokens, + title = fcmEvent.title, + message = fcmEvent.message, + container = "ios", + creatorId = fcmEvent.creatorId + ) + } + } + + if (aosPushToken != null) { + for (tokens in aosPushToken) { + pushService.send( + tokens = tokens, + title = fcmEvent.title, + message = fcmEvent.message, + container = "aos", + creatorId = fcmEvent.creatorId + ) + } + } + } + } } } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmService.kt index 1383185..1b08b35 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmService.kt @@ -19,7 +19,8 @@ class FcmService { container: String, roomId: Long? = null, messageId: Long? = null, - contentId: Long? = null + contentId: Long? = null, + creatorId: Long? = null ) { if (tokens.isNotEmpty()) { logger.info("os: $container") @@ -52,6 +53,10 @@ class FcmService { multicastMessage.putData("content_id", contentId.toString()) } + if (creatorId != null) { + multicastMessage.putData("channel_id", creatorId.toString()) + } + val response = FirebaseMessaging.getInstance().sendEachForMulticast(multicastMessage.build()) logger.info("보내기 성공: ${response.successCount}") logger.info("보내기 실패: ${response.failureCount}") diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/MemberRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/MemberRepository.kt index 45fa78e..b846bac 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/member/MemberRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/MemberRepository.kt @@ -40,6 +40,8 @@ interface MemberQueryRepository { fun getIndividualRecipientPushTokens(recipients: List, isAuth: Boolean): Map>> fun getChangeNicknamePrice(memberId: Long): GetChangeNicknamePriceResponse fun getMemberByEmail(email: String): Member? + + fun getChangeNoticeRecipientPushTokens(creatorId: Long): Map>> } @Repository @@ -244,4 +246,41 @@ class MemberQueryRepositoryImpl( .where(member.email.eq(email)) .fetchOne() } + + override fun getChangeNoticeRecipientPushTokens(creatorId: Long): Map>> { + val member = QMember.member + val creator = QMember.member + + val where = creatorFollowing.isActive.isTrue + .and(creatorFollowing.creator.id.eq(creatorId)) + .and(creatorFollowing.member.email.notIn("admin@sodalive.net")) + .and( + creatorFollowing.member.id.notIn( + blockMemberRepository.getBlockedMemberList(creatorId) + ) + ) + .and(creatorFollowing.member.pushToken.isNotNull) + + val aosPushTokens = queryFactory + .select(creatorFollowing.member.pushToken) + .from(creatorFollowing) + .innerJoin(creatorFollowing.creator, creator) + .innerJoin(creatorFollowing.member, member) + .where(where.and(member.container.eq("aos"))) + .fetch() + .toSet() + .chunked(500) + + val iosPushTokens = queryFactory + .select(creatorFollowing.member.pushToken) + .from(creatorFollowing) + .innerJoin(creatorFollowing.creator, creator) + .innerJoin(creatorFollowing.member, member) + .where(where.and(member.container.eq("ios"))) + .fetch() + .toSet() + .chunked(500) + + return mapOf("aos" to aosPushTokens, "ios" to iosPushTokens) + } }