푸시 알림 전송 언어 처리 #376

Merged
klaus merged 1 commits from test into main 2026-01-15 08:45:47 +00:00
18 changed files with 258 additions and 503 deletions
Showing only changes of commit ed2660adc6 - Show all commits

View File

@@ -7,8 +7,6 @@ import kr.co.vividnext.sodalive.aws.s3.S3Uploader
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.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
import kr.co.vividnext.sodalive.utils.generateFileName
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.ApplicationEventPublisher
@@ -24,8 +22,6 @@ class AdminAuditionService(
private val repository: AdminAuditionRepository,
private val roleRepository: AdminAuditionRoleRepository,
private val applicationEventPublisher: ApplicationEventPublisher,
private val langContext: LangContext,
private val messageSource: SodaMessageSource,
@Value("\${cloud.aws.s3.bucket}")
private val bucket: String
@@ -92,14 +88,12 @@ class AdminAuditionService(
}
if (request.status != null && request.status == AuditionStatus.IN_PROGRESS && audition.isActive) {
val title = messageSource.getMessage("admin.audition.fcm.title.new", langContext.lang).orEmpty()
val messageTemplate = messageSource.getMessage("admin.audition.fcm.message.new", langContext.lang).orEmpty()
val message = String.format(messageTemplate, audition.title)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.IN_PROGRESS_AUDITION,
title = title,
message = message,
titleKey = "admin.audition.fcm.title.new",
messageKey = "admin.audition.fcm.message.new",
args = listOf(audition.title),
isAuth = audition.isAdult,
auditionId = audition.id ?: -1
)

View File

@@ -321,20 +321,17 @@ class AdminLiveService(
}
// 예약현황 취소
val pushTokenListMap = memberRepository.getPushTokenFromReservationList(room.id!!)
val pushTokens = memberRepository.getPushTokenFromReservationList(room.id!!)
reservationRepository.cancelReservation(roomId = room.id!!)
// 라이브 취소 푸시 발송
val cancelMessageTemplate = messageSource
.getMessage("live.room.fcm.message.canceled", langContext.lang)
.orEmpty()
val cancelMessage = String.format(cancelMessageTemplate, room.title)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.CANCEL_LIVE,
title = room.member!!.nickname,
message = cancelMessage,
recipientsMap = pushTokenListMap
messageKey = "live.room.fcm.message.canceled",
args = listOf(room.title),
pushTokens = pushTokens
)
)
}

View File

@@ -83,10 +83,8 @@ class ChargeEventService(
FcmEvent(
type = FcmEventType.INDIVIDUAL,
title = chargeEvent.title,
message = formatMessage(
"can.charge.event.additional_can_paid",
additionalCan
),
messageKey = "can.charge.event.additional_can_paid",
args = listOf(additionalCan),
recipients = listOf(member.id!!),
isAuth = null
)
@@ -109,13 +107,9 @@ class ChargeEventService(
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.INDIVIDUAL,
title = messageSource
.getMessage("can.charge.event.first_title", langContext.lang)
.orEmpty(),
message = formatMessage(
"can.charge.event.additional_can_paid",
additionalCan
),
titleKey = "can.charge.event.first_title",
messageKey = "can.charge.event.additional_can_paid",
args = listOf(additionalCan),
recipients = listOf(member.id!!),
isAuth = null
)

View File

@@ -459,7 +459,7 @@ class AudioContentService(
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.INDIVIDUAL,
title = formatMessage("content.notification.upload_complete_title"),
titleKey = "content.notification.upload_complete_title",
message = audioContent.title,
recipients = listOf(audioContent.member!!.id!!),
isAuth = null,
@@ -474,23 +474,11 @@ class AudioContentService(
FcmEvent(
type = FcmEventType.UPLOAD_CONTENT,
title = audioContent.member!!.nickname,
message = formatMessage("content.notification.uploaded_message", audioContent.title),
messageKey = "content.notification.uploaded_message",
args = listOf(audioContent.title),
isAuth = audioContent.isAdult,
contentId = contentId,
creatorId = audioContent.member!!.id,
container = "ios"
)
)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.UPLOAD_CONTENT,
title = audioContent.member!!.nickname,
message = formatMessage("content.notification.uploaded_message", audioContent.title),
isAuth = audioContent.isAdult,
contentId = contentId,
creatorId = audioContent.member!!.id,
container = "aos"
creatorId = audioContent.member!!.id
)
)
}
@@ -508,23 +496,11 @@ class AudioContentService(
FcmEvent(
type = FcmEventType.UPLOAD_CONTENT,
title = audioContent.member!!.nickname,
message = "콘텐츠를 업로드 하였습니다. - ${audioContent.title}",
messageKey = "content.notification.uploaded_message",
args = listOf(audioContent.title),
isAuth = audioContent.isAdult,
contentId = audioContent.id!!,
creatorId = audioContent.member!!.id,
container = "ios"
)
)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.UPLOAD_CONTENT,
title = audioContent.member!!.nickname,
message = "콘텐츠를 업로드 하였습니다. - ${audioContent.title}",
isAuth = audioContent.isAdult,
contentId = audioContent.id!!,
creatorId = audioContent.member!!.id,
container = "aos"
creatorId = audioContent.member!!.id
)
)
}

View File

@@ -4,7 +4,9 @@ import com.querydsl.core.types.dsl.Expressions
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment
import kr.co.vividnext.sodalive.fcm.PushTokenInfo
import kr.co.vividnext.sodalive.fcm.QPushToken.pushToken
import kr.co.vividnext.sodalive.fcm.QPushTokenInfo
import kr.co.vividnext.sodalive.member.QMember.member
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@@ -38,7 +40,7 @@ interface AudioContentCommentQueryRepository {
contentId: Long,
commentParentId: Long?,
myMemberId: Long
): List<FindPushTokenByContentIdAndCommentParentIdMyMemberIdResponse>
): List<PushTokenInfo>
}
@Repository
@@ -191,7 +193,7 @@ class AudioContentCommentQueryRepositoryImpl(
contentId: Long,
commentParentId: Long?,
myMemberId: Long
): List<FindPushTokenByContentIdAndCommentParentIdMyMemberIdResponse> {
): List<PushTokenInfo> {
var where = audioContent.id.eq(contentId)
.and(member.id.ne(myMemberId))
@@ -206,9 +208,10 @@ class AudioContentCommentQueryRepositoryImpl(
val response = if (commentParentId != null) {
queryFactory
.select(
QFindPushTokenByContentIdAndCommentParentIdMyMemberIdResponse(
QPushTokenInfo(
pushToken.token,
pushToken.deviceType
pushToken.deviceType,
pushToken.languageCode.coalesce("ko")
)
)
.from(audioContentComment)
@@ -220,9 +223,10 @@ class AudioContentCommentQueryRepositoryImpl(
} else {
queryFactory
.select(
QFindPushTokenByContentIdAndCommentParentIdMyMemberIdResponse(
QPushTokenInfo(
pushToken.token,
pushToken.deviceType
pushToken.deviceType,
pushToken.languageCode.coalesce("ko")
)
)
.from(audioContent)

View File

@@ -83,11 +83,12 @@ class AudioContentCommentService(
} else {
member.nickname
},
message = if (parent != null) {
formatMessage("content.comment.notification.reply", audioContent.title)
messageKey = if (parent != null) {
"content.comment.notification.reply"
} else {
formatMessage("content.comment.notification.new", audioContent.title)
"content.comment.notification.new"
},
args = listOf(audioContent.title),
contentId = audioContentId,
commentParentId = parentId,
myMemberId = member.id

View File

@@ -1,8 +0,0 @@
package kr.co.vividnext.sodalive.content.comment
import com.querydsl.core.annotations.QueryProjection
data class FindPushTokenByContentIdAndCommentParentIdMyMemberIdResponse @QueryProjection constructor(
val pushToken: String,
val container: String
)

View File

@@ -578,7 +578,7 @@ class ExplorerService(
FcmEvent(
type = FcmEventType.CHANGE_NOTICE,
title = member.nickname,
message = messageSource.getMessage("explorer.notice.fcm.message", langContext.lang).orEmpty(),
messageKey = "explorer.notice.fcm.message",
creatorId = member.id!!
)
)

View File

@@ -123,7 +123,7 @@ class CreatorCommunityService(
FcmEvent(
type = FcmEventType.CHANGE_NOTICE,
title = member.nickname,
message = messageSource.getMessage("creator.community.fcm.new_post", langContext.lang).orEmpty(),
messageKey = "creator.community.fcm.new_post",
creatorId = member.id!!
)
)

View File

@@ -33,17 +33,6 @@ class FcmController(private val applicationEventPublisher: ApplicationEventPubli
type = FcmEventType.ALL,
title = request.title,
message = request.message,
container = "ios",
isAuth = request.isAuth
)
)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.ALL,
title = request.title,
message = request.message,
container = "aos",
isAuth = request.isAuth
)
)

View File

@@ -1,6 +1,8 @@
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.member.MemberRepository
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Component
@@ -15,11 +17,14 @@ enum class FcmEventType {
class FcmEvent(
val type: FcmEventType,
val title: String,
val message: String,
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 recipientsMap: Map<String, List<List<String>>>? = null,
val pushTokens: List<PushTokenInfo>? = null,
val isAuth: Boolean? = null,
val roomId: Long? = null,
val contentId: Long? = null,
@@ -35,7 +40,8 @@ class FcmEvent(
class FcmSendListener(
private val pushService: FcmService,
private val memberRepository: MemberRepository,
private val contentCommentRepository: AudioContentCommentRepository
private val contentCommentRepository: AudioContentCommentRepository,
private val messageSource: SodaMessageSource
) {
@Async
@TransactionalEventListener
@@ -43,21 +49,10 @@ class FcmSendListener(
fun send(fcmEvent: FcmEvent) {
when (fcmEvent.type) {
FcmEventType.ALL -> {
if (fcmEvent.container.isNotBlank()) {
val pushTokens = memberRepository.getAllRecipientPushTokens(
fcmEvent.isAuth,
fcmEvent.container
fcmEvent.isAuth
)
for (tokens in pushTokens) {
pushService.send(
tokens = tokens,
title = fcmEvent.title,
message = fcmEvent.message,
container = fcmEvent.container
)
}
}
sendPush(pushTokens, fcmEvent)
}
FcmEventType.INDIVIDUAL -> {
@@ -66,254 +61,117 @@ class FcmSendListener(
recipients = fcmEvent.recipients,
isAuth = fcmEvent.isAuth
)
val iosPushTokens = pushTokens["ios"]
val aosPushToken = pushTokens["aos"]
if (iosPushTokens != null) {
for (tokens in iosPushTokens) {
pushService.send(
tokens = tokens,
title = fcmEvent.title,
message = fcmEvent.message,
container = "ios",
contentId = fcmEvent.contentId
)
}
}
if (aosPushToken != null) {
for (tokens in aosPushToken) {
pushService.send(
tokens = tokens,
title = fcmEvent.title,
message = fcmEvent.message,
container = "aos",
contentId = fcmEvent.contentId
)
}
}
sendPush(pushTokens, fcmEvent)
}
}
FcmEventType.CREATE_LIVE -> {
if (fcmEvent.container.isNotBlank()) {
val pushTokens = memberRepository.getCreateLiveRoomNotificationRecipientPushTokens(
creatorId = fcmEvent.creatorId!!,
isAuth = fcmEvent.isAuth ?: false,
isAvailableJoinCreator = fcmEvent.isAvailableJoinCreator ?: false,
container = fcmEvent.container
isAvailableJoinCreator = fcmEvent.isAvailableJoinCreator ?: false
)
for (tokens in pushTokens) {
pushService.send(
tokens = tokens,
title = fcmEvent.title,
message = fcmEvent.message,
container = fcmEvent.container,
roomId = fcmEvent.roomId
)
}
}
sendPush(pushTokens, fcmEvent, roomId = fcmEvent.roomId)
}
FcmEventType.START_LIVE -> {
if (fcmEvent.container.isNotBlank()) {
val pushTokens = memberRepository.getStartLiveRoomNotificationRecipientPushTokens(
creatorId = fcmEvent.creatorId!!,
roomId = fcmEvent.roomId!!,
isAuth = fcmEvent.isAuth ?: false,
isAvailableJoinCreator = fcmEvent.isAvailableJoinCreator ?: false,
container = fcmEvent.container
isAvailableJoinCreator = fcmEvent.isAvailableJoinCreator ?: false
)
for (tokens in pushTokens) {
pushService.send(
tokens = tokens,
title = fcmEvent.title,
message = fcmEvent.message,
container = fcmEvent.container,
roomId = fcmEvent.roomId
)
}
}
sendPush(pushTokens, fcmEvent, roomId = fcmEvent.roomId)
}
FcmEventType.CANCEL_LIVE -> {
if (fcmEvent.recipientsMap != null) {
val iosPushTokens = fcmEvent.recipientsMap["ios"]
val aosPushToken = fcmEvent.recipientsMap["aos"]
if (iosPushTokens != null) {
for (tokens in iosPushTokens) {
pushService.send(
tokens = tokens,
title = fcmEvent.title,
message = fcmEvent.message,
container = "ios"
)
}
}
if (aosPushToken != null) {
for (tokens in aosPushToken) {
pushService.send(
tokens = tokens,
title = fcmEvent.title,
message = fcmEvent.message,
container = "aos"
)
}
}
if (fcmEvent.pushTokens != null) {
sendPush(fcmEvent.pushTokens, fcmEvent)
}
}
FcmEventType.UPLOAD_CONTENT -> {
if (fcmEvent.container.isNotBlank()) {
val pushTokens = memberRepository.getUploadContentNotificationRecipientPushTokens(
creatorId = fcmEvent.creatorId!!,
isAuth = fcmEvent.isAuth ?: false,
container = fcmEvent.container
isAuth = fcmEvent.isAuth ?: false
)
for (tokens in pushTokens) {
pushService.send(
tokens = tokens,
title = fcmEvent.title,
message = fcmEvent.message,
container = fcmEvent.container,
contentId = fcmEvent.contentId
)
}
}
sendPush(pushTokens, fcmEvent, contentId = fcmEvent.contentId)
}
FcmEventType.SEND_MESSAGE -> {
val response = memberRepository.getMessageRecipientPushToken(messageId = fcmEvent.messageId!!)
if (response != null) {
pushService.send(
tokens = listOf(response.pushToken),
title = fcmEvent.title,
message = fcmEvent.message,
container = response.container,
messageId = fcmEvent.messageId
)
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 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
)
}
}
val pushTokens = memberRepository.getChangeNoticeRecipientPushTokens(fcmEvent.creatorId)
sendPush(pushTokens, fcmEvent, creatorId = fcmEvent.creatorId)
}
}
FcmEventType.CREATE_CONTENT_COMMENT -> {
if (fcmEvent.myMemberId != null && fcmEvent.contentId != null) {
val response = contentCommentRepository.findPushTokenByContentIdAndCommentParentIdMyMemberId(
val pushTokens = contentCommentRepository.findPushTokenByContentIdAndCommentParentIdMyMemberId(
contentId = fcmEvent.contentId,
commentParentId = fcmEvent.commentParentId,
myMemberId = fcmEvent.myMemberId
)
val iosPushTokens = response
.asSequence()
.distinct()
.filter { it.pushToken.isNotBlank() }
.filter { it.container == "ios" }
.map { it.pushToken }
.toList()
val aosPushTokens = response
.asSequence()
.distinct()
.filter { it.pushToken.isNotBlank() }
.filter { it.container == "aos" }
.map { it.pushToken }
.toList()
if (iosPushTokens.isNotEmpty()) {
pushService.send(
tokens = iosPushTokens,
title = fcmEvent.title,
message = fcmEvent.message,
container = "ios",
contentId = fcmEvent.contentId
)
}
if (aosPushTokens.isNotEmpty()) {
pushService.send(
tokens = aosPushTokens,
title = fcmEvent.title,
message = fcmEvent.message,
container = "aos",
contentId = fcmEvent.contentId
)
}
sendPush(pushTokens, fcmEvent, contentId = fcmEvent.contentId)
}
}
FcmEventType.IN_PROGRESS_AUDITION -> {
if (fcmEvent.auditionId != null && fcmEvent.auditionId > 0) {
val pushTokenList = memberRepository.getAuditionNoticeRecipientPushTokens(
val pushTokens = memberRepository.getAuditionNoticeRecipientPushTokens(
isAuth = fcmEvent.isAuth ?: false
)
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",
auditionId = fcmEvent.auditionId
)
sendPush(pushTokens, fcmEvent, auditionId = fcmEvent.auditionId)
}
}
}
}
if (aosPushToken != null) {
for (tokens in aosPushToken) {
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 = tokens,
title = fcmEvent.title,
message = fcmEvent.message,
container = "aos",
auditionId = fcmEvent.auditionId
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())
}
}

View File

@@ -1,8 +0,0 @@
package kr.co.vividnext.sodalive.fcm
import com.querydsl.core.annotations.QueryProjection
data class GetMessageRecipientPushTokenResponse @QueryProjection constructor(
val pushToken: String,
val container: String
)

View File

@@ -10,7 +10,8 @@ import javax.persistence.ManyToOne
@Entity
data class PushToken(
var token: String,
var deviceType: String
var deviceType: String,
var languageCode: String? = null
) : BaseEntity() {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = true)

View File

@@ -0,0 +1,9 @@
package kr.co.vividnext.sodalive.fcm
import com.querydsl.core.annotations.QueryProjection
data class PushTokenInfo @QueryProjection constructor(
val token: String,
val deviceType: String,
val languageCode: String
)

View File

@@ -1,5 +1,6 @@
package kr.co.vividnext.sodalive.fcm
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.member.MemberRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
@@ -8,7 +9,8 @@ import org.springframework.transaction.annotation.Transactional
@Service
class PushTokenService(
private val repository: PushTokenRepository,
private val memberRepository: MemberRepository
private val memberRepository: MemberRepository,
private val langContext: LangContext
) {
@Transactional
fun registerToken(memberId: Long, token: String, deviceType: String) {
@@ -20,8 +22,9 @@ class PushTokenService(
existing.member = member
existing.token = token
existing.deviceType = deviceType
existing.languageCode = langContext.lang.code
} else {
val newToken = PushToken(token, deviceType)
val newToken = PushToken(token, deviceType, langContext.lang.code)
newToken.member = member
repository.save(newToken)
}

View File

@@ -405,35 +405,20 @@ class LiveRoomService(
}
}
val createdMessage = if (createdRoom.channelName != null) {
formatMessage("live.room.fcm.message.started", createdRoom.title)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.CREATE_LIVE,
title = createdRoom.member!!.nickname,
messageKey = if (createdRoom.channelName != null) {
"live.room.fcm.message.started"
} else {
formatMessage("live.room.fcm.message.reserved", createdRoom.title)
}
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.CREATE_LIVE,
title = createdRoom.member!!.nickname,
message = createdMessage,
"live.room.fcm.message.reserved"
},
args = listOf(createdRoom.title),
isAuth = createdRoom.isAdult,
isAvailableJoinCreator = createdRoom.isAvailableJoinCreator,
roomId = createdRoom.id,
creatorId = createdRoom.member!!.id,
container = "ios"
)
)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.CREATE_LIVE,
title = createdRoom.member!!.nickname,
message = createdMessage,
isAuth = createdRoom.isAdult,
isAvailableJoinCreator = createdRoom.isAvailableJoinCreator,
roomId = createdRoom.id,
creatorId = createdRoom.member!!.id,
container = "aos"
creatorId = createdRoom.member!!.id
)
)
@@ -580,30 +565,16 @@ class LiveRoomService(
room.beginDateTime = nowDateTime
val startedMessage = formatMessage("live.room.fcm.message.started_now", room.title)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.START_LIVE,
title = room.member!!.nickname,
message = startedMessage,
messageKey = "live.room.fcm.message.started",
args = listOf(room.title),
isAuth = room.isAdult,
isAvailableJoinCreator = room.isAvailableJoinCreator,
roomId = room.id,
creatorId = room.member!!.id,
container = "ios"
)
)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.START_LIVE,
title = room.member!!.nickname,
message = startedMessage,
isAuth = room.isAdult,
isAvailableJoinCreator = room.isAvailableJoinCreator,
roomId = room.id,
creatorId = room.member!!.id,
container = "aos"
creatorId = room.member!!.id
)
)
}
@@ -660,16 +631,17 @@ class LiveRoomService(
}
}
val pushTokenListMap = memberRepository.getPushTokenFromReservationList(request.roomId)
val pushTokens = memberRepository.getPushTokenFromReservationList(request.roomId)
reservationRepository.cancelReservation(roomId = room.id!!)
val cancelMessage = formatMessage("live.room.fcm.message.canceled", room.title)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.CANCEL_LIVE,
title = room.member!!.nickname,
message = cancelMessage,
recipientsMap = pushTokenListMap
messageKey = "live.room.fcm.message.canceled",
args = listOf(room.title),
pushTokens = pushTokens
)
)
}

View File

@@ -1,9 +1,9 @@
package kr.co.vividnext.sodalive.member
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.fcm.GetMessageRecipientPushTokenResponse
import kr.co.vividnext.sodalive.fcm.QGetMessageRecipientPushTokenResponse
import kr.co.vividnext.sodalive.fcm.PushTokenInfo
import kr.co.vividnext.sodalive.fcm.QPushToken.pushToken
import kr.co.vividnext.sodalive.fcm.QPushTokenInfo
import kr.co.vividnext.sodalive.live.reservation.QLiveReservation.liveReservation
import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
import kr.co.vividnext.sodalive.member.QMember.member
@@ -29,36 +29,33 @@ interface MemberRepository : JpaRepository<Member, Long>, MemberQueryRepository
interface MemberQueryRepository {
fun findByNicknameAndOtherCondition(nickname: String, member: Member): List<Member>
fun findCreatorByIdOrNull(memberId: Long): Member?
fun getAllRecipientPushTokens(isAuth: Boolean?, container: String): List<List<String>>
fun getAllRecipientPushTokens(isAuth: Boolean?): List<PushTokenInfo>
fun getCreateLiveRoomNotificationRecipientPushTokens(
creatorId: Long,
isAuth: Boolean,
isAvailableJoinCreator: Boolean,
container: String
): List<List<String>>
isAvailableJoinCreator: Boolean
): List<PushTokenInfo>
fun getStartLiveRoomNotificationRecipientPushTokens(
creatorId: Long,
roomId: Long,
isAuth: Boolean,
isAvailableJoinCreator: Boolean,
container: String
): List<List<String>>
isAvailableJoinCreator: Boolean
): List<PushTokenInfo>
fun getUploadContentNotificationRecipientPushTokens(
creatorId: Long,
isAuth: Boolean,
container: String
): List<List<String>>
isAuth: Boolean
): List<PushTokenInfo>
fun getMessageRecipientPushToken(messageId: Long): GetMessageRecipientPushTokenResponse?
fun getIndividualRecipientPushTokens(recipients: List<Long>, isAuth: Boolean?): Map<String, List<List<String>>>
fun getMessageRecipientPushToken(messageId: Long): PushTokenInfo?
fun getIndividualRecipientPushTokens(recipients: List<Long>, isAuth: Boolean?): List<PushTokenInfo>
fun getChangeNicknamePrice(memberId: Long): GetChangeNicknamePriceResponse
fun getMemberByEmail(email: String): Member?
fun getChangeNoticeRecipientPushTokens(creatorId: Long): Map<String, List<List<String>>>
fun getPushTokenFromReservationList(roomId: Long): Map<String, List<List<String>>>
fun getAuditionNoticeRecipientPushTokens(isAuth: Boolean): Map<String, List<List<String>>>
fun getChangeNoticeRecipientPushTokens(creatorId: Long): List<PushTokenInfo>
fun getPushTokenFromReservationList(roomId: Long): List<PushTokenInfo>
fun getAuditionNoticeRecipientPushTokens(isAuth: Boolean): List<PushTokenInfo>
fun getMemberProfile(memberId: Long, myMemberId: Long): GetMemberProfileResponse
@@ -103,10 +100,9 @@ class MemberQueryRepositoryImpl(
.fetchFirst()
}
override fun getAllRecipientPushTokens(isAuth: Boolean?, container: String): List<List<String>> {
override fun getAllRecipientPushTokens(isAuth: Boolean?): List<PushTokenInfo> {
var where = member.isActive.isTrue
.and(member.email.notIn("admin@sodalive.net"))
.and(pushToken.deviceType.eq(container))
if (isAuth != null) {
where = if (isAuth) {
@@ -117,22 +113,25 @@ class MemberQueryRepositoryImpl(
}
return queryFactory
.select(pushToken.token)
.select(
QPushTokenInfo(
pushToken.token,
pushToken.deviceType,
pushToken.languageCode.coalesce("ko")
)
)
.from(member)
.innerJoin(pushToken).on(member.id.eq(pushToken.member.id))
.leftJoin(member.auth, auth)
.where(where)
.fetch()
.toSet()
.chunked(500)
}
override fun getCreateLiveRoomNotificationRecipientPushTokens(
creatorId: Long,
isAuth: Boolean,
isAvailableJoinCreator: Boolean,
container: String
): List<List<String>> {
isAvailableJoinCreator: Boolean
): List<PushTokenInfo> {
val member = QMember.member
val creator = QMember.member
@@ -147,7 +146,6 @@ class MemberQueryRepositoryImpl(
)
)
.and(creatorFollowing.isNotify.isTrue)
.and(pushToken.deviceType.eq(container))
.or(member.id.eq(4))
if (isAuth) {
@@ -159,7 +157,13 @@ class MemberQueryRepositoryImpl(
}
return queryFactory
.select(pushToken.token)
.select(
QPushTokenInfo(
pushToken.token,
pushToken.deviceType,
pushToken.languageCode.coalesce("ko")
)
)
.from(creatorFollowing)
.innerJoin(creatorFollowing.creator, creator)
.innerJoin(creatorFollowing.member, member)
@@ -168,17 +172,14 @@ class MemberQueryRepositoryImpl(
.leftJoin(creatorFollowing.member.auth, auth)
.where(where)
.fetch()
.toSet()
.chunked(500)
}
override fun getStartLiveRoomNotificationRecipientPushTokens(
creatorId: Long,
roomId: Long,
isAuth: Boolean,
isAvailableJoinCreator: Boolean,
container: String
): List<List<String>> {
isAvailableJoinCreator: Boolean
): List<PushTokenInfo> {
val member = QMember.member
val creator = QMember.member
@@ -193,7 +194,6 @@ class MemberQueryRepositoryImpl(
)
)
.and(creatorFollowing.isNotify.isTrue)
.and(pushToken.deviceType.eq(container))
.or(creatorFollowing.member.id.eq(4))
if (isAuth) {
@@ -205,7 +205,13 @@ class MemberQueryRepositoryImpl(
}
val followingMemberPushToken = queryFactory
.select(pushToken.token)
.select(
QPushTokenInfo(
pushToken.token,
pushToken.deviceType,
pushToken.languageCode.coalesce("ko")
)
)
.from(creatorFollowing)
.innerJoin(creatorFollowing.creator, creator)
.innerJoin(creatorFollowing.member, member)
@@ -224,14 +230,19 @@ class MemberQueryRepositoryImpl(
blockMemberRepository.getBlockedMemberIdList(creatorId)
)
)
.and(pushToken.deviceType.eq(container))
if (isAuth) {
where = where.and(auth.isNotNull)
}
val reservationMemberPushToken = queryFactory
.select(pushToken.token)
.select(
QPushTokenInfo(
pushToken.token,
pushToken.deviceType,
pushToken.languageCode.coalesce("ko")
)
)
.from(liveReservation)
.innerJoin(liveReservation.member, member)
.innerJoin(liveReservation.member.notification, memberNotification)
@@ -240,16 +251,13 @@ class MemberQueryRepositoryImpl(
.where(where)
.fetch()
return (followingMemberPushToken + reservationMemberPushToken)
.toSet()
.chunked(500)
return (followingMemberPushToken + reservationMemberPushToken).distinctBy { it.token }
}
override fun getUploadContentNotificationRecipientPushTokens(
creatorId: Long,
isAuth: Boolean,
container: String
): List<List<String>> {
isAuth: Boolean
): List<PushTokenInfo> {
val member = QMember.member
val creator = QMember.member
@@ -263,7 +271,6 @@ class MemberQueryRepositoryImpl(
)
)
.and(creatorFollowing.isNotify.isTrue)
.and(pushToken.deviceType.eq(container))
.or(member.id.eq(4))
if (isAuth) {
@@ -271,7 +278,13 @@ class MemberQueryRepositoryImpl(
}
return queryFactory
.select(pushToken.token)
.select(
QPushTokenInfo(
pushToken.token,
pushToken.deviceType,
pushToken.languageCode.coalesce("ko")
)
)
.from(creatorFollowing)
.innerJoin(creatorFollowing.creator, creator)
.innerJoin(creatorFollowing.member, member)
@@ -280,16 +293,15 @@ class MemberQueryRepositoryImpl(
.leftJoin(member.auth, auth)
.where(where)
.fetch()
.toSet()
.chunked(500)
}
override fun getMessageRecipientPushToken(messageId: Long): GetMessageRecipientPushTokenResponse? {
override fun getMessageRecipientPushToken(messageId: Long): PushTokenInfo? {
return queryFactory
.select(
QGetMessageRecipientPushTokenResponse(
QPushTokenInfo(
pushToken.token,
pushToken.deviceType
pushToken.deviceType,
pushToken.languageCode.coalesce("ko")
)
)
.from(message)
@@ -306,7 +318,7 @@ class MemberQueryRepositoryImpl(
override fun getIndividualRecipientPushTokens(
recipients: List<Long>,
isAuth: Boolean?
): Map<String, List<List<String>>> {
): List<PushTokenInfo> {
var where = member.isActive.isTrue
.and(member.email.notIn("admin@sodalive.net"))
.and(member.id.`in`(*recipients.toTypedArray()))
@@ -319,27 +331,19 @@ class MemberQueryRepositoryImpl(
}
}
val aosPushTokens = queryFactory
.select(pushToken.token)
return queryFactory
.select(
QPushTokenInfo(
pushToken.token,
pushToken.deviceType,
pushToken.languageCode.coalesce("ko")
)
)
.from(member)
.innerJoin(pushToken).on(member.id.eq(pushToken.member.id))
.leftJoin(member.auth, auth)
.where(where.and(pushToken.deviceType.eq("aos")))
.where(where)
.fetch()
.toSet()
.chunked(500)
val iosPushTokens = queryFactory
.select(pushToken.token)
.from(member)
.innerJoin(pushToken).on(member.id.eq(pushToken.member.id))
.leftJoin(member.auth, auth)
.where(where.and(pushToken.deviceType.eq("ios")))
.fetch()
.toSet()
.chunked(500)
return mapOf("aos" to aosPushTokens, "ios" to iosPushTokens)
}
override fun getChangeNicknamePrice(memberId: Long): GetChangeNicknamePriceResponse {
@@ -366,7 +370,7 @@ class MemberQueryRepositoryImpl(
.fetchOne()
}
override fun getChangeNoticeRecipientPushTokens(creatorId: Long): Map<String, List<List<String>>> {
override fun getChangeNoticeRecipientPushTokens(creatorId: Long): List<PushTokenInfo> {
val member = QMember.member
val creator = QMember.member
@@ -381,90 +385,63 @@ class MemberQueryRepositoryImpl(
)
.and(creatorFollowing.isNotify.isTrue)
val aosPushTokens = queryFactory
.select(pushToken.token)
return queryFactory
.select(
QPushTokenInfo(
pushToken.token,
pushToken.deviceType,
pushToken.languageCode.coalesce("ko")
)
)
.from(creatorFollowing)
.innerJoin(creatorFollowing.creator, creator)
.innerJoin(creatorFollowing.member, member)
.innerJoin(pushToken).on(creatorFollowing.member.id.eq(pushToken.member.id))
.where(where.and(pushToken.deviceType.eq("aos")))
.where(where)
.fetch()
.toSet()
.chunked(500)
val iosPushTokens = queryFactory
.select(pushToken.token)
.from(creatorFollowing)
.innerJoin(creatorFollowing.creator, creator)
.innerJoin(creatorFollowing.member, member)
.innerJoin(pushToken).on(creatorFollowing.member.id.eq(pushToken.member.id))
.where(where.and(pushToken.deviceType.eq("ios")))
.fetch()
.toSet()
.chunked(500)
return mapOf("aos" to aosPushTokens, "ios" to iosPushTokens)
}
override fun getPushTokenFromReservationList(roomId: Long): Map<String, List<List<String>>> {
override fun getPushTokenFromReservationList(roomId: Long): List<PushTokenInfo> {
val where = liveRoom.id.eq(roomId)
.and(liveReservation.isActive.isTrue)
val aosPushTokens = queryFactory
.select(pushToken.token)
return queryFactory
.select(
QPushTokenInfo(
pushToken.token,
pushToken.deviceType,
pushToken.languageCode.coalesce("ko")
)
)
.from(liveReservation)
.innerJoin(liveReservation.room, liveRoom)
.innerJoin(liveReservation.member, member)
.innerJoin(pushToken).on(member.id.eq(pushToken.member.id))
.where(where.and(pushToken.deviceType.eq("aos")))
.where(where)
.fetch()
.toSet()
.chunked(500)
val iosPushTokens = queryFactory
.select(pushToken.token)
.from(liveReservation)
.innerJoin(liveReservation.room, liveRoom)
.innerJoin(liveReservation.member, member)
.innerJoin(pushToken).on(member.id.eq(pushToken.member.id))
.where(where.and(pushToken.deviceType.eq("ios")))
.fetch()
.toSet()
.chunked(500)
return mapOf("aos" to aosPushTokens, "ios" to iosPushTokens)
}
override fun getAuditionNoticeRecipientPushTokens(isAuth: Boolean): Map<String, List<List<String>>> {
override fun getAuditionNoticeRecipientPushTokens(isAuth: Boolean): List<PushTokenInfo> {
var where = memberNotification.audition.isTrue
if (isAuth) {
where = where.and(auth.isNotNull)
}
val aosPushTokens = queryFactory
.select(pushToken.token)
return queryFactory
.select(
QPushTokenInfo(
pushToken.token,
pushToken.deviceType,
pushToken.languageCode.coalesce("ko")
)
)
.from(member)
.innerJoin(pushToken).on(member.id.eq(pushToken.member.id))
.leftJoin(member.auth, auth)
.leftJoin(memberNotification).on(memberNotification.member.id.eq(member.id))
.where(where.and(pushToken.deviceType.eq("aos")))
.where(where)
.fetch()
.toSet()
.chunked(500)
val iosPushTokens = queryFactory
.select(pushToken.token)
.from(member)
.innerJoin(pushToken).on(member.id.eq(pushToken.member.id))
.leftJoin(member.auth, auth)
.leftJoin(memberNotification).on(memberNotification.member.id.eq(member.id))
.where(where.and(pushToken.deviceType.eq("ios")))
.fetch()
.toSet()
.chunked(500)
return mapOf("aos" to aosPushTokens, "ios" to iosPushTokens)
}
override fun getMemberProfile(memberId: Long, myMemberId: Long): GetMemberProfileResponse {

View File

@@ -72,11 +72,9 @@ class MessageService(
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.SEND_MESSAGE,
title = messageSource.getMessage("message.fcm.title", langContext.lang).orEmpty(),
message = run {
val messageTemplate = messageSource.getMessage("message.fcm.text_received", langContext.lang).orEmpty()
String.format(messageTemplate, sender.nickname)
},
titleKey = "message.fcm.title",
messageKey = "message.fcm.text_received",
args = listOf(sender.nickname),
messageId = message.id
)
)
@@ -147,11 +145,9 @@ class MessageService(
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.SEND_MESSAGE,
title = messageSource.getMessage("message.fcm.title", langContext.lang).orEmpty(),
message = run {
val messageTemplate = messageSource.getMessage("message.fcm.voice_received", langContext.lang).orEmpty()
String.format(messageTemplate, sender.nickname)
},
titleKey = "message.fcm.title",
messageKey = "message.fcm.voice_received",
args = listOf(sender.nickname),
messageId = message.id
)
)