package kr.co.vividnext.sodalive.fcm import kr.co.vividnext.sodalive.content.comment.AudioContentCommentRepository import kr.co.vividnext.sodalive.i18n.Lang import kr.co.vividnext.sodalive.i18n.SodaMessageSource import kr.co.vividnext.sodalive.live.room.GenderRestriction import kr.co.vividnext.sodalive.member.MemberRepository import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Propagation import org.springframework.transaction.annotation.Transactional import org.springframework.transaction.event.TransactionalEventListener enum class FcmEventType { ALL, INDIVIDUAL, CREATE_LIVE, START_LIVE, CANCEL_LIVE, UPLOAD_CONTENT, SEND_MESSAGE, CHANGE_NOTICE, CREATE_CONTENT_COMMENT, IN_PROGRESS_AUDITION } class FcmEvent( val type: FcmEventType, val title: String = "", val message: String = "", val titleKey: String? = null, val messageKey: String? = null, val args: List = listOf(), val container: String = "", val recipients: List = listOf(), val pushTokens: List? = null, val isAuth: Boolean? = null, val roomId: Long? = null, val contentId: Long? = null, val messageId: Long? = null, val creatorId: Long? = null, val auditionId: Long? = null, val commentParentId: Long? = null, val myMemberId: Long? = null, val isAvailableJoinCreator: Boolean? = null, val genderRestriction: GenderRestriction? = null ) @Component class FcmSendListener( private val pushService: FcmService, private val memberRepository: MemberRepository, private val contentCommentRepository: AudioContentCommentRepository, private val messageSource: SodaMessageSource ) { @Async @TransactionalEventListener @Transactional(propagation = Propagation.REQUIRES_NEW) fun send(fcmEvent: FcmEvent) { when (fcmEvent.type) { FcmEventType.ALL -> { val pushTokens = memberRepository.getAllRecipientPushTokens( fcmEvent.isAuth ) sendPush(pushTokens, fcmEvent) } FcmEventType.INDIVIDUAL -> { if (fcmEvent.recipients.isNotEmpty()) { val pushTokens = memberRepository.getIndividualRecipientPushTokens( recipients = fcmEvent.recipients, isAuth = fcmEvent.isAuth ) sendPush(pushTokens, fcmEvent) } } FcmEventType.CREATE_LIVE -> { val pushTokens = memberRepository.getCreateLiveRoomNotificationRecipientPushTokens( creatorId = fcmEvent.creatorId!!, isAuth = fcmEvent.isAuth ?: false, isAvailableJoinCreator = fcmEvent.isAvailableJoinCreator ?: false, genderRestriction = fcmEvent.genderRestriction ) sendPush(pushTokens, fcmEvent, roomId = fcmEvent.roomId) } FcmEventType.START_LIVE -> { val pushTokens = memberRepository.getStartLiveRoomNotificationRecipientPushTokens( creatorId = fcmEvent.creatorId!!, roomId = fcmEvent.roomId!!, isAuth = fcmEvent.isAuth ?: false, isAvailableJoinCreator = fcmEvent.isAvailableJoinCreator ?: false, genderRestriction = fcmEvent.genderRestriction ) sendPush(pushTokens, fcmEvent, roomId = fcmEvent.roomId) } FcmEventType.CANCEL_LIVE -> { if (fcmEvent.pushTokens != null) { sendPush(fcmEvent.pushTokens, fcmEvent) } } FcmEventType.UPLOAD_CONTENT -> { val pushTokens = memberRepository.getUploadContentNotificationRecipientPushTokens( creatorId = fcmEvent.creatorId!!, isAuth = fcmEvent.isAuth ?: false ) sendPush(pushTokens, fcmEvent, contentId = fcmEvent.contentId) } FcmEventType.SEND_MESSAGE -> { val pushToken = memberRepository.getMessageRecipientPushToken(messageId = fcmEvent.messageId!!) if (pushToken != null) { sendPush(listOf(pushToken), fcmEvent, messageId = fcmEvent.messageId) } } FcmEventType.CHANGE_NOTICE -> { if (fcmEvent.creatorId != null) { val pushTokens = memberRepository.getChangeNoticeRecipientPushTokens(fcmEvent.creatorId) sendPush(pushTokens, fcmEvent, creatorId = fcmEvent.creatorId) } } FcmEventType.CREATE_CONTENT_COMMENT -> { if (fcmEvent.myMemberId != null && fcmEvent.contentId != null) { val pushTokens = contentCommentRepository.findPushTokenByContentIdAndCommentParentIdMyMemberId( contentId = fcmEvent.contentId, commentParentId = fcmEvent.commentParentId, myMemberId = fcmEvent.myMemberId ) sendPush(pushTokens, fcmEvent, contentId = fcmEvent.contentId) } } FcmEventType.IN_PROGRESS_AUDITION -> { if (fcmEvent.auditionId != null && fcmEvent.auditionId > 0) { val pushTokens = memberRepository.getAuditionNoticeRecipientPushTokens( isAuth = fcmEvent.isAuth ?: false ) sendPush(pushTokens, fcmEvent, auditionId = fcmEvent.auditionId) } } } } private fun sendPush( pushTokens: List, fcmEvent: FcmEvent, roomId: Long? = null, contentId: Long? = null, messageId: Long? = null, creatorId: Long? = null, auditionId: Long? = null ) { val tokensByLang = pushTokens.groupBy { it.languageCode } for ((langCode, tokens) in tokensByLang) { val lang = Lang.fromAcceptLanguage(langCode) val title = translate(fcmEvent.titleKey, fcmEvent.title, lang, fcmEvent.args) val message = translate(fcmEvent.messageKey, fcmEvent.message, lang, fcmEvent.args) val tokensByOS = tokens.groupBy { it.deviceType } for ((os, osTokens) in tokensByOS) { osTokens.map { it.token }.distinct().chunked(500).forEach { batch -> pushService.send( tokens = batch, title = title, message = message, container = os, roomId = roomId ?: fcmEvent.roomId, contentId = contentId ?: fcmEvent.contentId, messageId = messageId ?: fcmEvent.messageId, creatorId = creatorId ?: fcmEvent.creatorId, auditionId = auditionId ?: fcmEvent.auditionId ) } } } } private fun translate(key: String?, default: String, lang: Lang, args: List): String { if (key == null) return default val template = messageSource.getMessage(key, lang) ?: return default return String.format(template, *args.toTypedArray()) } }