푸시메시지 기능 추가 - 전체, 개별, 라이브 생성, 라이브 시작, 메시지 전송, 콘텐츠 업로드
This commit is contained in:
		| @@ -55,6 +55,9 @@ dependencies { | |||||||
|     implementation("org.json:json:20230227") |     implementation("org.json:json:20230227") | ||||||
|     implementation("com.google.code.findbugs:jsr305:3.0.2") |     implementation("com.google.code.findbugs:jsr305:3.0.2") | ||||||
|  |  | ||||||
|  |     // firebase admin sdk | ||||||
|  |     implementation("com.google.firebase:firebase-admin:9.2.0") | ||||||
|  |  | ||||||
|     developmentOnly("org.springframework.boot:spring-boot-devtools") |     developmentOnly("org.springframework.boot:spring-boot-devtools") | ||||||
|     runtimeOnly("com.h2database:h2") |     runtimeOnly("com.h2database:h2") | ||||||
|     runtimeOnly("com.mysql:mysql-connector-j") |     runtimeOnly("com.mysql:mysql-connector-j") | ||||||
|   | |||||||
| @@ -0,0 +1,25 @@ | |||||||
|  | package kr.co.vividnext.sodalive.configs | ||||||
|  |  | ||||||
|  | import com.google.auth.oauth2.GoogleCredentials | ||||||
|  | import com.google.firebase.FirebaseApp | ||||||
|  | import com.google.firebase.FirebaseOptions | ||||||
|  | import org.springframework.beans.factory.annotation.Value | ||||||
|  | import org.springframework.context.annotation.Configuration | ||||||
|  | import java.io.FileInputStream | ||||||
|  | import javax.annotation.PostConstruct | ||||||
|  |  | ||||||
|  | @Configuration | ||||||
|  | class FirebaseConfig( | ||||||
|  |     @Value("\${firebase.secret-key-path}") | ||||||
|  |     private val secretKeyPath: String | ||||||
|  | ) { | ||||||
|  |  | ||||||
|  |     @PostConstruct | ||||||
|  |     fun initialize() { | ||||||
|  |         FirebaseOptions.builder() | ||||||
|  |             .setCredentials(GoogleCredentials.fromStream(FileInputStream(secretKeyPath))) | ||||||
|  |             .build() | ||||||
|  |  | ||||||
|  |         FirebaseApp.initializeApp() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -17,10 +17,13 @@ import kr.co.vividnext.sodalive.content.order.OrderRepository | |||||||
| import kr.co.vividnext.sodalive.content.order.OrderType | import kr.co.vividnext.sodalive.content.order.OrderType | ||||||
| import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository | import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository | ||||||
| import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository | import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository | ||||||
|  | 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.Member | ||||||
| import kr.co.vividnext.sodalive.member.block.BlockMemberRepository | import kr.co.vividnext.sodalive.member.block.BlockMemberRepository | ||||||
| import kr.co.vividnext.sodalive.utils.generateFileName | import kr.co.vividnext.sodalive.utils.generateFileName | ||||||
| import org.springframework.beans.factory.annotation.Value | import org.springframework.beans.factory.annotation.Value | ||||||
|  | import org.springframework.context.ApplicationEventPublisher | ||||||
| import org.springframework.data.repository.findByIdOrNull | import org.springframework.data.repository.findByIdOrNull | ||||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||||
| import org.springframework.transaction.annotation.Transactional | import org.springframework.transaction.annotation.Transactional | ||||||
| @@ -45,6 +48,7 @@ class AudioContentService( | |||||||
|     private val s3Uploader: S3Uploader, |     private val s3Uploader: S3Uploader, | ||||||
|     private val objectMapper: ObjectMapper, |     private val objectMapper: ObjectMapper, | ||||||
|     private val audioContentCloudFront: AudioContentCloudFront, |     private val audioContentCloudFront: AudioContentCloudFront, | ||||||
|  |     private val applicationEventPublisher: ApplicationEventPublisher, | ||||||
|  |  | ||||||
|     @Value("\${cloud.aws.s3.content-bucket}") |     @Value("\${cloud.aws.s3.content-bucket}") | ||||||
|     private val audioContentBucket: String, |     private val audioContentBucket: String, | ||||||
| @@ -260,6 +264,17 @@ class AudioContentService( | |||||||
|         audioContent.isActive = true |         audioContent.isActive = true | ||||||
|         audioContent.content = content |         audioContent.content = content | ||||||
|         audioContent.duration = duration |         audioContent.duration = duration | ||||||
|  |  | ||||||
|  |         applicationEventPublisher.publishEvent( | ||||||
|  |             FcmEvent( | ||||||
|  |                 type = FcmEventType.UPLOAD_CONTENT, | ||||||
|  |                 title = audioContent.member!!.nickname, | ||||||
|  |                 message = "콘텐츠를 업로드 하였습니다. - ${audioContent.title}", | ||||||
|  |                 isAuth = audioContent.isAdult, | ||||||
|  |                 contentId = contentId, | ||||||
|  |                 creatorId = audioContent.member!!.id | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun getDetail(id: Long, member: Member, timezone: String): GetAudioContentDetailResponse { |     fun getDetail(id: Long, member: Member, timezone: String): GetAudioContentDetailResponse { | ||||||
|   | |||||||
| @@ -0,0 +1,47 @@ | |||||||
|  | package kr.co.vividnext.sodalive.fcm | ||||||
|  |  | ||||||
|  | import org.springframework.context.ApplicationEventPublisher | ||||||
|  | import org.springframework.security.access.prepost.PreAuthorize | ||||||
|  | import org.springframework.web.bind.annotation.PostMapping | ||||||
|  | import org.springframework.web.bind.annotation.RequestBody | ||||||
|  | import org.springframework.web.bind.annotation.RequestMapping | ||||||
|  | import org.springframework.web.bind.annotation.RestController | ||||||
|  |  | ||||||
|  | @RestController | ||||||
|  | @RequestMapping("/push") | ||||||
|  | @PreAuthorize("hasRole('ADMIN')") | ||||||
|  | class FcmController(private val applicationEventPublisher: ApplicationEventPublisher) { | ||||||
|  |     @PostMapping | ||||||
|  |     fun send( | ||||||
|  |         @RequestBody request: PushRequest | ||||||
|  |     ) = run { | ||||||
|  |         if (request.memberIds.isNotEmpty()) { | ||||||
|  |             applicationEventPublisher.publishEvent( | ||||||
|  |                 FcmEvent( | ||||||
|  |                     type = FcmEventType.INDIVIDUAL, | ||||||
|  |                     title = request.title, | ||||||
|  |                     message = request.message, | ||||||
|  |                     recipients = request.memberIds | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } else { | ||||||
|  |             applicationEventPublisher.publishEvent( | ||||||
|  |                 FcmEvent( | ||||||
|  |                     type = FcmEventType.ALL, | ||||||
|  |                     title = request.title, | ||||||
|  |                     message = request.message, | ||||||
|  |                     container = "ios" | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |             applicationEventPublisher.publishEvent( | ||||||
|  |                 FcmEvent( | ||||||
|  |                     type = FcmEventType.ALL, | ||||||
|  |                     title = request.title, | ||||||
|  |                     message = request.message, | ||||||
|  |                     container = "aos" | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										137
									
								
								src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmEvent.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmEvent.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | |||||||
|  | package kr.co.vividnext.sodalive.fcm | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.member.MemberRepository | ||||||
|  | import org.springframework.context.event.EventListener | ||||||
|  | import org.springframework.stereotype.Component | ||||||
|  |  | ||||||
|  | enum class FcmEventType { | ||||||
|  |     ALL, INDIVIDUAL, CREATE_LIVE, START_LIVE, UPLOAD_CONTENT, SEND_MESSAGE | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class FcmEvent( | ||||||
|  |     val type: FcmEventType, | ||||||
|  |     val title: String, | ||||||
|  |     val message: String, | ||||||
|  |     val container: String = "", | ||||||
|  |     val recipients: List<Long> = listOf(), | ||||||
|  |     val isAuth: Boolean = false, | ||||||
|  |     val roomId: Long? = null, | ||||||
|  |     val contentId: Long? = null, | ||||||
|  |     val messageId: Long? = null, | ||||||
|  |     val creatorId: Long? = null | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | @Component | ||||||
|  | class FcmSendListener( | ||||||
|  |     private val pushService: FcmService, | ||||||
|  |     private val memberRepository: MemberRepository | ||||||
|  | ) { | ||||||
|  |     @EventListener | ||||||
|  |     fun send(fcmEvent: FcmEvent) { | ||||||
|  |         when (fcmEvent.type) { | ||||||
|  |             FcmEventType.ALL -> { | ||||||
|  |                 if (fcmEvent.container.isNotBlank()) { | ||||||
|  |                     val pushTokens = memberRepository.getAllRecipientPushTokens( | ||||||
|  |                         fcmEvent.isAuth, | ||||||
|  |                         fcmEvent.container | ||||||
|  |                     ) | ||||||
|  |  | ||||||
|  |                     for (tokens in pushTokens) { | ||||||
|  |                         pushService.send( | ||||||
|  |                             tokens = tokens, | ||||||
|  |                             title = fcmEvent.title, | ||||||
|  |                             message = fcmEvent.message, | ||||||
|  |                             container = fcmEvent.container | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             FcmEventType.INDIVIDUAL -> { | ||||||
|  |                 if (fcmEvent.recipients.isNotEmpty()) { | ||||||
|  |                     val pushTokens = memberRepository.getIndividualRecipientPushTokens( | ||||||
|  |                         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 = fcmEvent.container | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (aosPushToken != null) { | ||||||
|  |                         for (tokens in aosPushToken) { | ||||||
|  |                             pushService.send( | ||||||
|  |                                 tokens = tokens, | ||||||
|  |                                 title = fcmEvent.title, | ||||||
|  |                                 message = fcmEvent.message, | ||||||
|  |                                 container = fcmEvent.container | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             FcmEventType.CREATE_LIVE, FcmEventType.START_LIVE -> { | ||||||
|  |                 if (fcmEvent.container.isNotBlank()) { | ||||||
|  |                     val pushTokens = memberRepository.getCreateLiveRoomNotificationRecipientPushTokens( | ||||||
|  |                         creatorId = fcmEvent.creatorId!!, | ||||||
|  |                         isAuth = fcmEvent.isAuth, | ||||||
|  |                         container = fcmEvent.container | ||||||
|  |                     ) | ||||||
|  |  | ||||||
|  |                     for (tokens in pushTokens) { | ||||||
|  |                         pushService.send( | ||||||
|  |                             tokens = tokens, | ||||||
|  |                             title = fcmEvent.title, | ||||||
|  |                             message = fcmEvent.message, | ||||||
|  |                             container = fcmEvent.container, | ||||||
|  |                             roomId = fcmEvent.roomId | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             FcmEventType.UPLOAD_CONTENT -> { | ||||||
|  |                 if (fcmEvent.container.isNotBlank()) { | ||||||
|  |                     val pushTokens = memberRepository.getUploadContentNotificationRecipientPushTokens( | ||||||
|  |                         creatorId = fcmEvent.creatorId!!, | ||||||
|  |                         isAuth = fcmEvent.isAuth, | ||||||
|  |                         container = fcmEvent.container | ||||||
|  |                     ) | ||||||
|  |  | ||||||
|  |                     for (tokens in pushTokens) { | ||||||
|  |                         pushService.send( | ||||||
|  |                             tokens = tokens, | ||||||
|  |                             title = fcmEvent.title, | ||||||
|  |                             message = fcmEvent.message, | ||||||
|  |                             container = fcmEvent.container, | ||||||
|  |                             contentId = fcmEvent.contentId | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             FcmEventType.SEND_MESSAGE -> { | ||||||
|  |                 val response = memberRepository.getMessageRecipientPushToken(messageId = fcmEvent.messageId!!) | ||||||
|  |  | ||||||
|  |                 pushService.send( | ||||||
|  |                     tokens = listOf(response.pushToken), | ||||||
|  |                     title = fcmEvent.title, | ||||||
|  |                     message = fcmEvent.message, | ||||||
|  |                     container = response.container, | ||||||
|  |                     messageId = fcmEvent.messageId | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmService.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmService.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | package kr.co.vividnext.sodalive.fcm | ||||||
|  |  | ||||||
|  | import com.google.firebase.messaging.FirebaseMessaging | ||||||
|  | import com.google.firebase.messaging.MulticastMessage | ||||||
|  | import org.springframework.scheduling.annotation.Async | ||||||
|  | import org.springframework.stereotype.Service | ||||||
|  |  | ||||||
|  | @Service | ||||||
|  | class FcmService { | ||||||
|  |     @Async | ||||||
|  |     fun send( | ||||||
|  |         tokens: List<String>, | ||||||
|  |         title: String, | ||||||
|  |         message: String, | ||||||
|  |         container: String, | ||||||
|  |         roomId: Long? = null, | ||||||
|  |         messageId: Long? = null, | ||||||
|  |         contentId: Long? = null | ||||||
|  |     ) { | ||||||
|  |         val multicastMessage = MulticastMessage.builder() | ||||||
|  |             .putData("title", title) | ||||||
|  |             .putData("message", message) | ||||||
|  |             .addAllTokens(tokens) | ||||||
|  |  | ||||||
|  |         if (roomId != null) { | ||||||
|  |             multicastMessage.putData("room_id", roomId.toString()) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (messageId != null) { | ||||||
|  |             multicastMessage.putData("message_id", messageId.toString()) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (contentId != null) { | ||||||
|  |             multicastMessage.putData("content_id", contentId.toString()) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         FirebaseMessaging.getInstance().sendEachForMulticast(multicastMessage.build()) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | package kr.co.vividnext.sodalive.fcm | ||||||
|  |  | ||||||
|  | import com.querydsl.core.annotations.QueryProjection | ||||||
|  |  | ||||||
|  | data class GetMessageRecipientPushTokenResponse @QueryProjection constructor( | ||||||
|  |     val pushToken: String, | ||||||
|  |     val container: String | ||||||
|  | ) | ||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | package kr.co.vividnext.sodalive.fcm | ||||||
|  |  | ||||||
|  | data class PushRequest( | ||||||
|  |     val memberIds: List<Long>, | ||||||
|  |     val title: String, | ||||||
|  |     val message: String | ||||||
|  | ) | ||||||
| @@ -19,6 +19,8 @@ import kr.co.vividnext.sodalive.can.use.UseCanCalculateStatus | |||||||
| import kr.co.vividnext.sodalive.common.SodaException | import kr.co.vividnext.sodalive.common.SodaException | ||||||
| import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository | import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository | ||||||
| import kr.co.vividnext.sodalive.extensions.convertLocalDateTime | import kr.co.vividnext.sodalive.extensions.convertLocalDateTime | ||||||
|  | import kr.co.vividnext.sodalive.fcm.FcmEvent | ||||||
|  | import kr.co.vividnext.sodalive.fcm.FcmEventType | ||||||
| import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository | import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository | ||||||
| import kr.co.vividnext.sodalive.live.room.cancel.CancelLiveRequest | import kr.co.vividnext.sodalive.live.room.cancel.CancelLiveRequest | ||||||
| import kr.co.vividnext.sodalive.live.room.cancel.LiveRoomCancel | import kr.co.vividnext.sodalive.live.room.cancel.LiveRoomCancel | ||||||
| @@ -45,6 +47,7 @@ import kr.co.vividnext.sodalive.member.MemberRole | |||||||
| import kr.co.vividnext.sodalive.member.block.BlockMemberRepository | import kr.co.vividnext.sodalive.member.block.BlockMemberRepository | ||||||
| import kr.co.vividnext.sodalive.utils.generateFileName | import kr.co.vividnext.sodalive.utils.generateFileName | ||||||
| import org.springframework.beans.factory.annotation.Value | import org.springframework.beans.factory.annotation.Value | ||||||
|  | import org.springframework.context.ApplicationEventPublisher | ||||||
| import org.springframework.data.domain.Pageable | import org.springframework.data.domain.Pageable | ||||||
| import org.springframework.data.repository.findByIdOrNull | import org.springframework.data.repository.findByIdOrNull | ||||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||||
| @@ -66,6 +69,7 @@ class LiveRoomService( | |||||||
|     private val kickOutService: LiveRoomKickOutService, |     private val kickOutService: LiveRoomKickOutService, | ||||||
|     private val blockMemberRepository: BlockMemberRepository, |     private val blockMemberRepository: BlockMemberRepository, | ||||||
|  |  | ||||||
|  |     private val applicationEventPublisher: ApplicationEventPublisher, | ||||||
|     private val useCanCalculateRepository: UseCanCalculateRepository, |     private val useCanCalculateRepository: UseCanCalculateRepository, | ||||||
|     private val reservationRepository: LiveReservationRepository, |     private val reservationRepository: LiveReservationRepository, | ||||||
|     private val explorerQueryRepository: ExplorerQueryRepository, |     private val explorerQueryRepository: ExplorerQueryRepository, | ||||||
| @@ -231,6 +235,21 @@ class LiveRoomService( | |||||||
|             room.bgImage = request.coverImageUrl |             room.bgImage = request.coverImageUrl | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         applicationEventPublisher.publishEvent( | ||||||
|  |             FcmEvent( | ||||||
|  |                 type = FcmEventType.CREATE_LIVE, | ||||||
|  |                 title = createdRoom.member!!.nickname, | ||||||
|  |                 message = if (createdRoom.channelName != null) { | ||||||
|  |                     "라이브를 시작했습니다. - ${createdRoom.title}" | ||||||
|  |                 } else { | ||||||
|  |                     "라이브를 개설했습니다. - ${createdRoom.title}" | ||||||
|  |                 }, | ||||||
|  |                 isAuth = createdRoom.isAdult, | ||||||
|  |                 roomId = createdRoom.id, | ||||||
|  |                 creatorId = createdRoom.member!!.id | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|         return CreateLiveRoomResponse(createdRoom.id, createdRoom.channelName) |         return CreateLiveRoomResponse(createdRoom.id, createdRoom.channelName) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -351,6 +370,17 @@ class LiveRoomService( | |||||||
|         room.channelName = "SODA_LIVE_CHANNEL_" + |         room.channelName = "SODA_LIVE_CHANNEL_" + | ||||||
|             "${member.id}_${dateTime.year}_${dateTime.month}_${dateTime.dayOfMonth}_" + |             "${member.id}_${dateTime.year}_${dateTime.month}_${dateTime.dayOfMonth}_" + | ||||||
|             "${dateTime.hour}_${dateTime.minute}" |             "${dateTime.hour}_${dateTime.minute}" | ||||||
|  |  | ||||||
|  |         applicationEventPublisher.publishEvent( | ||||||
|  |             FcmEvent( | ||||||
|  |                 type = FcmEventType.START_LIVE, | ||||||
|  |                 title = room.member!!.nickname, | ||||||
|  |                 message = "라이브를 시작했습니다 - ${room.title}", | ||||||
|  |                 isAuth = room.isAdult, | ||||||
|  |                 roomId = room.id, | ||||||
|  |                 creatorId = room.member!!.id | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Transactional |     @Transactional | ||||||
|   | |||||||
| @@ -1,7 +1,14 @@ | |||||||
| package kr.co.vividnext.sodalive.member | package kr.co.vividnext.sodalive.member | ||||||
|  |  | ||||||
| import com.querydsl.jpa.impl.JPAQueryFactory | 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.member.QMember.member | import kr.co.vividnext.sodalive.member.QMember.member | ||||||
|  | import kr.co.vividnext.sodalive.member.auth.QAuth.auth | ||||||
|  | import kr.co.vividnext.sodalive.member.block.BlockMemberRepository | ||||||
|  | import kr.co.vividnext.sodalive.member.following.QCreatorFollowing.creatorFollowing | ||||||
|  | import kr.co.vividnext.sodalive.member.notification.QMemberNotification.memberNotification | ||||||
|  | import kr.co.vividnext.sodalive.message.QMessage.message | ||||||
| import org.springframework.data.jpa.repository.JpaRepository | import org.springframework.data.jpa.repository.JpaRepository | ||||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||||
|  |  | ||||||
| @@ -15,10 +22,28 @@ interface MemberQueryRepository { | |||||||
|     fun findByPushToken(pushToken: String): List<Member> |     fun findByPushToken(pushToken: String): List<Member> | ||||||
|     fun findByNicknameAndOtherCondition(nickname: String, memberId: Long): List<Member> |     fun findByNicknameAndOtherCondition(nickname: String, memberId: Long): List<Member> | ||||||
|     fun findCreatorByIdOrNull(memberId: Long): Member? |     fun findCreatorByIdOrNull(memberId: Long): Member? | ||||||
|  |     fun getAllRecipientPushTokens(isAuth: Boolean, container: String): List<List<String>> | ||||||
|  |     fun getCreateLiveRoomNotificationRecipientPushTokens( | ||||||
|  |         creatorId: Long, | ||||||
|  |         isAuth: Boolean, | ||||||
|  |         container: String | ||||||
|  |     ): List<List<String>> | ||||||
|  |  | ||||||
|  |     fun getUploadContentNotificationRecipientPushTokens( | ||||||
|  |         creatorId: Long, | ||||||
|  |         isAuth: Boolean, | ||||||
|  |         container: String | ||||||
|  |     ): List<List<String>> | ||||||
|  |  | ||||||
|  |     fun getMessageRecipientPushToken(messageId: Long): GetMessageRecipientPushTokenResponse | ||||||
|  |     fun getIndividualRecipientPushTokens(recipients: List<Long>, isAuth: Boolean): Map<String, List<List<String>>> | ||||||
| } | } | ||||||
|  |  | ||||||
| @Repository | @Repository | ||||||
| class MemberQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : MemberQueryRepository { | class MemberQueryRepositoryImpl( | ||||||
|  |     private val queryFactory: JPAQueryFactory, | ||||||
|  |     private val blockMemberRepository: BlockMemberRepository | ||||||
|  | ) : MemberQueryRepository { | ||||||
|     override fun findByPushToken(pushToken: String): List<Member> { |     override fun findByPushToken(pushToken: String): List<Member> { | ||||||
|         return queryFactory |         return queryFactory | ||||||
|             .selectFrom(member) |             .selectFrom(member) | ||||||
| @@ -47,4 +72,141 @@ class MemberQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Mem | |||||||
|             ) |             ) | ||||||
|             .fetchFirst() |             .fetchFirst() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     override fun getAllRecipientPushTokens(isAuth: Boolean, container: String): List<List<String>> { | ||||||
|  |         var where = member.isActive.isTrue | ||||||
|  |             .and(member.email.notIn("admin@sodalive.net")) | ||||||
|  |             .and(member.container.eq(container)) | ||||||
|  |  | ||||||
|  |         if (isAuth) { | ||||||
|  |             where = where.and(member.auth.isNotNull) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return queryFactory | ||||||
|  |             .select(member.pushToken) | ||||||
|  |             .from(member) | ||||||
|  |             .leftJoin(member.auth, auth) | ||||||
|  |             .where(where) | ||||||
|  |             .fetch() | ||||||
|  |             .toSet() | ||||||
|  |             .chunked(500) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun getCreateLiveRoomNotificationRecipientPushTokens( | ||||||
|  |         creatorId: Long, | ||||||
|  |         isAuth: Boolean, | ||||||
|  |         container: String | ||||||
|  |     ): List<List<String>> { | ||||||
|  |         val member = QMember.member | ||||||
|  |         val creator = QMember.member | ||||||
|  |  | ||||||
|  |         var where = creatorFollowing.isActive.isTrue | ||||||
|  |             .and(creator.id.eq(creatorId)) | ||||||
|  |             .and(member.email.notIn("admin@sodalive.net")) | ||||||
|  |             .and(member.container.eq(container)) | ||||||
|  |             .and(memberNotification.live.isTrue) | ||||||
|  |             .and( | ||||||
|  |                 member.id.notIn( | ||||||
|  |                     blockMemberRepository.getBlockedMemberList(creatorId) | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |         if (isAuth) { | ||||||
|  |             where = where.and(member.auth.isNotNull) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return queryFactory | ||||||
|  |             .select(member.pushToken) | ||||||
|  |             .from(creatorFollowing) | ||||||
|  |             .innerJoin(creatorFollowing.creator, creator) | ||||||
|  |             .innerJoin(creatorFollowing.member, member) | ||||||
|  |             .innerJoin(member.notification, memberNotification) | ||||||
|  |             .leftJoin(member.auth, auth) | ||||||
|  |             .where(where) | ||||||
|  |             .fetch() | ||||||
|  |             .toSet() | ||||||
|  |             .chunked(500) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun getUploadContentNotificationRecipientPushTokens( | ||||||
|  |         creatorId: Long, | ||||||
|  |         isAuth: Boolean, | ||||||
|  |         container: String | ||||||
|  |     ): List<List<String>> { | ||||||
|  |         val member = QMember.member | ||||||
|  |         val creator = QMember.member | ||||||
|  |  | ||||||
|  |         var where = creatorFollowing.isActive.isTrue | ||||||
|  |             .and(creator.id.eq(creatorId)) | ||||||
|  |             .and(member.email.notIn("admin@sodalive.net")) | ||||||
|  |             .and(member.container.eq(container)) | ||||||
|  |             .and(memberNotification.uploadContent.isTrue) | ||||||
|  |             .and( | ||||||
|  |                 member.id.notIn( | ||||||
|  |                     blockMemberRepository.getBlockedMemberList(creatorId) | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |         if (isAuth) { | ||||||
|  |             where = where.and(member.auth.isNotNull) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return queryFactory | ||||||
|  |             .select(member.pushToken) | ||||||
|  |             .from(creatorFollowing) | ||||||
|  |             .innerJoin(creatorFollowing.creator, creator) | ||||||
|  |             .innerJoin(creatorFollowing.member, member) | ||||||
|  |             .innerJoin(member.notification, memberNotification) | ||||||
|  |             .leftJoin(member.auth, auth) | ||||||
|  |             .where(where) | ||||||
|  |             .fetch() | ||||||
|  |             .toSet() | ||||||
|  |             .chunked(500) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun getMessageRecipientPushToken(messageId: Long): GetMessageRecipientPushTokenResponse { | ||||||
|  |         return queryFactory | ||||||
|  |             .select( | ||||||
|  |                 QGetMessageRecipientPushTokenResponse( | ||||||
|  |                     member.pushToken, | ||||||
|  |                     member.container | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |             .from(message) | ||||||
|  |             .innerJoin(message.recipient, member) | ||||||
|  |             .where(message.id.eq(messageId)) | ||||||
|  |             .fetchFirst() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun getIndividualRecipientPushTokens( | ||||||
|  |         recipients: List<Long>, | ||||||
|  |         isAuth: Boolean | ||||||
|  |     ): Map<String, List<List<String>>> { | ||||||
|  |         var where = member.isActive.isTrue | ||||||
|  |             .and(member.email.notIn("admin@sodalive.net")) | ||||||
|  |  | ||||||
|  |         if (isAuth) { | ||||||
|  |             where = where.and(member.auth.isNotNull) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         val aosPushTokens = queryFactory | ||||||
|  |             .select(member.pushToken) | ||||||
|  |             .from(member) | ||||||
|  |             .leftJoin(member.auth, auth) | ||||||
|  |             .where(where.and(member.container.eq("aos"))) | ||||||
|  |             .fetch() | ||||||
|  |             .toSet() | ||||||
|  |             .chunked(500) | ||||||
|  |  | ||||||
|  |         val iosPushTokens = queryFactory | ||||||
|  |             .select(member.pushToken) | ||||||
|  |             .from(member) | ||||||
|  |             .leftJoin(member.auth, auth) | ||||||
|  |             .where(where.and(member.container.eq("ios"))) | ||||||
|  |             .fetch() | ||||||
|  |             .toSet() | ||||||
|  |             .chunked(500) | ||||||
|  |  | ||||||
|  |         return mapOf("aos" to aosPushTokens, "ios" to iosPushTokens) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ interface BlockMemberRepository : JpaRepository<BlockMember, Long>, BlockMemberQ | |||||||
| interface BlockMemberQueryRepository { | interface BlockMemberQueryRepository { | ||||||
|     fun getBlockAccount(blockedMemberId: Long, memberId: Long): BlockMember? |     fun getBlockAccount(blockedMemberId: Long, memberId: Long): BlockMember? | ||||||
|     fun isBlocked(blockedMemberId: Long, memberId: Long): Boolean |     fun isBlocked(blockedMemberId: Long, memberId: Long): Boolean | ||||||
|  |     fun getBlockedMemberList(creatorId: Long): List<Long> | ||||||
| } | } | ||||||
|  |  | ||||||
| @Repository | @Repository | ||||||
| @@ -39,4 +40,15 @@ class BlockMemberQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | |||||||
|  |  | ||||||
|         return blockedAccount != null |         return blockedAccount != null | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     override fun getBlockedMemberList(creatorId: Long): List<Long> { | ||||||
|  |         return queryFactory | ||||||
|  |             .select(blockMember.blockedMemberId) | ||||||
|  |             .from(blockMember) | ||||||
|  |             .where( | ||||||
|  |                 blockMember.memberId.eq(creatorId) | ||||||
|  |                     .and(blockMember.isActive.isTrue) | ||||||
|  |             ) | ||||||
|  |             .fetch() | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,11 +4,14 @@ import com.amazonaws.services.s3.model.ObjectMetadata | |||||||
| import com.fasterxml.jackson.databind.ObjectMapper | import com.fasterxml.jackson.databind.ObjectMapper | ||||||
| import kr.co.vividnext.sodalive.aws.s3.S3Uploader | import kr.co.vividnext.sodalive.aws.s3.S3Uploader | ||||||
| import kr.co.vividnext.sodalive.common.SodaException | 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.Member | ||||||
| import kr.co.vividnext.sodalive.member.MemberRepository | import kr.co.vividnext.sodalive.member.MemberRepository | ||||||
| import kr.co.vividnext.sodalive.member.block.BlockMemberRepository | import kr.co.vividnext.sodalive.member.block.BlockMemberRepository | ||||||
| import kr.co.vividnext.sodalive.utils.generateFileName | import kr.co.vividnext.sodalive.utils.generateFileName | ||||||
| import org.springframework.beans.factory.annotation.Value | import org.springframework.beans.factory.annotation.Value | ||||||
|  | import org.springframework.context.ApplicationEventPublisher | ||||||
| import org.springframework.data.domain.Pageable | import org.springframework.data.domain.Pageable | ||||||
| import org.springframework.data.repository.findByIdOrNull | import org.springframework.data.repository.findByIdOrNull | ||||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||||
| @@ -24,6 +27,7 @@ class MessageService( | |||||||
|     private val memberRepository: MemberRepository, |     private val memberRepository: MemberRepository, | ||||||
|     private val blockMemberRepository: BlockMemberRepository, |     private val blockMemberRepository: BlockMemberRepository, | ||||||
|  |  | ||||||
|  |     private val applicationEventPublisher: ApplicationEventPublisher, | ||||||
|     private val objectMapper: ObjectMapper, |     private val objectMapper: ObjectMapper, | ||||||
|     private val s3Uploader: S3Uploader, |     private val s3Uploader: S3Uploader, | ||||||
|  |  | ||||||
| @@ -56,6 +60,15 @@ class MessageService( | |||||||
|         message.recipient = recipient |         message.recipient = recipient | ||||||
|  |  | ||||||
|         repository.save(message) |         repository.save(message) | ||||||
|  |  | ||||||
|  |         applicationEventPublisher.publishEvent( | ||||||
|  |             FcmEvent( | ||||||
|  |                 type = FcmEventType.SEND_MESSAGE, | ||||||
|  |                 title = "메시지", | ||||||
|  |                 message = "${sender.nickname}님으로 부터 문자메시지가 도착했습니다.", | ||||||
|  |                 messageId = message.id | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun getSentTextMessages(member: Member, pageable: Pageable, timezone: String): GetTextMessageResponse { |     fun getSentTextMessages(member: Member, pageable: Pageable, timezone: String): GetTextMessageResponse { | ||||||
| @@ -115,6 +128,15 @@ class MessageService( | |||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         message.voiceMessage = messagePath |         message.voiceMessage = messagePath | ||||||
|  |  | ||||||
|  |         applicationEventPublisher.publishEvent( | ||||||
|  |             FcmEvent( | ||||||
|  |                 type = FcmEventType.SEND_MESSAGE, | ||||||
|  |                 title = "메시지", | ||||||
|  |                 message = "${sender.nickname}님으로 부터 음성메시지가 도착했습니다.", | ||||||
|  |                 messageId = message.id | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun getSentVoiceMessages(member: Member, pageable: Pageable, timezone: String): GetVoiceMessageResponse { |     fun getSentVoiceMessages(member: Member, pageable: Pageable, timezone: String): GetVoiceMessageResponse { | ||||||
|   | |||||||
| @@ -20,6 +20,9 @@ agora: | |||||||
|     appId: ${AGORA_APP_ID} |     appId: ${AGORA_APP_ID} | ||||||
|     appCertificate: ${AGORA_APP_CERTIFICATE} |     appCertificate: ${AGORA_APP_CERTIFICATE} | ||||||
|  |  | ||||||
|  | firebase: | ||||||
|  |     secretKeyPath: ${GOOGLE_APPLICATION_CREDENTIALS} | ||||||
|  |  | ||||||
| cloud: | cloud: | ||||||
|     aws: |     aws: | ||||||
|         credentials: |         credentials: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user