Files
sodalive-backend-spring-boot/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmEvent.kt
Klaus 96513eef6a 라이브룸 성별 제한 추가
라이브룸 생성/수정 요청에 genderRestriction 필드 추가
라이브룸 상세 응답에 genderRestriction 필드 추가
2026-02-02 14:44:07 +09:00

182 lines
7.4 KiB
Kotlin

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<Any> = listOf(),
val container: String = "",
val recipients: List<Long> = listOf(),
val pushTokens: List<PushTokenInfo>? = 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<PushTokenInfo>,
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<Any>): String {
if (key == null) return default
val template = messageSource.getMessage(key, lang) ?: return default
return String.format(template, *args.toTypedArray())
}
}