Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DPMBE-128] 유저 도착 이벤트를 받아 FCM Message 발송과 알림을 저장한다 #220

Merged
merged 4 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.depromeet.whatnow.api.notification.dto

import com.depromeet.whatnow.domains.notification.domain.ArrivalNotification
import com.depromeet.whatnow.domains.notification.domain.NotificationType
import java.time.LocalDateTime

class ArrivalNotificationResponse(
val promiseId: Long,
val senderUserId: Long,
override val createdAt: LocalDateTime,
) : NotificationAbstract(NotificationType.START_SHARING, createdAt) {
companion object {
fun from(notification: ArrivalNotification): ArrivalNotificationResponse {
return ArrivalNotificationResponse(
notification.promiseId,
notification.senderUserId,
notification.createdAt,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.depromeet.whatnow.api.notification.usecase

import com.depromeet.whatnow.annotation.UseCase
import com.depromeet.whatnow.api.notification.dto.ArrivalNotificationResponse
import com.depromeet.whatnow.api.notification.dto.EndSharingNotificationResponse
import com.depromeet.whatnow.api.notification.dto.ImageNotificationResponse
import com.depromeet.whatnow.api.notification.dto.InteractionAttainmentNotificationResponse
Expand All @@ -9,6 +10,7 @@ import com.depromeet.whatnow.api.notification.dto.NotificationResponse
import com.depromeet.whatnow.api.notification.dto.StartSharingNotificationResponse
import com.depromeet.whatnow.api.notification.dto.TimeOverNotificationResponse
import com.depromeet.whatnow.config.security.SecurityUtils
import com.depromeet.whatnow.domains.notification.domain.ArrivalNotification
import com.depromeet.whatnow.domains.notification.domain.EndSharingNotification
import com.depromeet.whatnow.domains.notification.domain.ImageNotification
import com.depromeet.whatnow.domains.notification.domain.InteractionAttainmentNotification
Expand Down Expand Up @@ -46,6 +48,9 @@ class NotificationReadUseCase(
is TimeOverNotification -> {
TimeOverNotificationResponse.from(notification)
}
is ArrivalNotification -> {
ArrivalNotificationResponse.from(notification)
}
else -> throw UnknownNotificationTypeException.EXCEPTION
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.depromeet.whatnow.domains.notification.domain

import javax.persistence.DiscriminatorValue
import javax.persistence.Entity

@Entity
@DiscriminatorValue("ARRIVAL")
class ArrivalNotification(
var promiseId: Long,

var senderUserId: Long,

override var targetUserId: Long,
) : Notification(targetUserId)
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.depromeet.whatnow.domains.notification.service

import com.depromeet.whatnow.domains.interaction.domain.InteractionType
import com.depromeet.whatnow.domains.notification.adapter.NotificationAdapter
import com.depromeet.whatnow.domains.notification.domain.ArrivalNotification
import com.depromeet.whatnow.domains.notification.domain.EndSharingNotification
import com.depromeet.whatnow.domains.notification.domain.ImageNotification
import com.depromeet.whatnow.domains.notification.domain.InteractionAttainmentNotification
Expand Down Expand Up @@ -49,6 +50,10 @@ class NotificationDomainService(
notificationAdapter.save(InteractionAttainmentNotification(promiseId, senderUserId, interactionType, targetUserId))
}

fun saveForArrival(promiseId: Long, senderUserId: Long, targetUserId: Long) {
notificationAdapter.save(ArrivalNotification(promiseId, senderUserId, targetUserId))
}

@Transactional(readOnly = true)
fun getMyNotifications(userId: Long, pageable: Pageable): Slice<Notification> {
return notificationAdapter.getMyNotifications(userId, pageable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import com.depromeet.whatnow.common.aop.event.DomainEvent
class PromiseUserUpdateLocationEvent(
val promiseId: Long,
val userId: Long,
val id: Long,
val promiseUserId: Long,
) : DomainEvent()
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class ImageRegisterEventHandler(

// 앱 알람 허용한 유저
val appAlarmPermitUsers = usersExcludingSelf.filter { user ->
user.fcmNotification.fcmToken != null && user.fcmNotification.appAlarm
user.fcmNotification.fcmToken != "" && user.fcmNotification.appAlarm
}

val data: MutableMap<String, String> = mutableMapOf()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class PromiseTimeEndEventHandler(

// 앱 알람 허용한 유저
val appAlarmPermitUsers = users
.filter { user -> user.fcmNotification.fcmToken != null && user.fcmNotification.appAlarm }
.filter { user -> user.fcmNotification.fcmToken != "" && user.fcmNotification.appAlarm }

val lateData: MutableMap<String, String> = mutableMapOf()
lateData["notificationType"] = NotificationType.TIMEOVER.name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class PromiseTimeStartEventHandler(

// 앱 알람 허용한 유저
val appAlarmPermitUsers = users
.filter { user -> user.fcmNotification.fcmToken != null && user.fcmNotification.appAlarm }
.filter { user -> user.fcmNotification.fcmToken != "" && user.fcmNotification.appAlarm }

val data: MutableMap<String, String> = mutableMapOf()
data["notificationType"] = NotificationType.START_SHARING.name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class PromiseTrackingTimeEndEventHandler(

// 앱 알람 허용한 유저
val appAlarmPermitUsers = users
.filter { user -> user.fcmNotification.fcmToken != null && user.fcmNotification.appAlarm }
.filter { user -> user.fcmNotification.fcmToken != "" && user.fcmNotification.appAlarm }

val data: MutableMap<String, String> = mutableMapOf()
data["notificationType"] = NotificationType.END_SHARING.name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ package com.depromeet.whatnow.events.handler

import ch.hsr.geohash.GeoHash
import com.depromeet.whatnow.common.vo.CoordinateVo
import com.depromeet.whatnow.config.fcm.FcmService
import com.depromeet.whatnow.consts.RADIUS_CONVERT_METER
import com.depromeet.whatnow.domains.district.repository.DistrictRepository
import com.depromeet.whatnow.domains.notification.domain.NotificationType
import com.depromeet.whatnow.domains.notification.service.NotificationDomainService
import com.depromeet.whatnow.domains.promise.adaptor.PromiseAdaptor
import com.depromeet.whatnow.domains.promiseuser.adaptor.PromiseUserAdaptor
import com.depromeet.whatnow.domains.promiseuser.service.PromiseUserDomainService
import com.depromeet.whatnow.domains.user.adapter.UserAdapter
import com.depromeet.whatnow.events.domainEvent.PromiseUserUpdateLocationEvent
import com.mongodb.client.model.geojson.Point
import com.mongodb.client.model.geojson.Position
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Propagation
Expand All @@ -18,6 +25,10 @@ class PromiseUserUpdateLocationEventHandler(
val promiseAdaptor: PromiseAdaptor,
val promiseUserAdaptor: PromiseUserAdaptor,
val promiseUserDomainService: PromiseUserDomainService,
val userAdapter: UserAdapter,
val fcmService: FcmService,
val notificationDomainService: NotificationDomainService,
val districtRepository: DistrictRepository,
) {
// logger

Expand All @@ -26,14 +37,59 @@ class PromiseUserUpdateLocationEventHandler(
@TransactionalEventListener(classes = [PromiseUserUpdateLocationEvent::class], phase = TransactionPhase.AFTER_COMMIT)
fun handlerPromiseUserUpdateLocationEventHandler(promiseUserUpdateLocationEvent: PromiseUserUpdateLocationEvent) {
val promiseId = promiseUserUpdateLocationEvent.promiseId
val userId = promiseUserUpdateLocationEvent.userId
val promiseUserId = promiseUserUpdateLocationEvent.promiseUserId

val promise = promiseAdaptor.queryPromise(promiseId)
val promiseUser = promiseUserAdaptor.findByPromiseIdAndUserId(promiseUserUpdateLocationEvent.promiseId, promiseUserUpdateLocationEvent.userId)
val promiseUser = promiseUserAdaptor.findByPromiseIdAndUserId(promiseId, userId)
// if (promiseUserDomainService.isArrived(promiseUser, promise.meetPlace!!.coordinate)) {
if (isArrived(promiseUser.userLocation, promise.meetPlace!!.coordinate)) {
// 활성화된 약속이 종료되기 전일 때
if (promise.isBeforePromiseEndTime() && promise.isActive()) {
// 약속유저 상태 도착 상태(WAIT)로 변경
promiseUser.updatePromiseUserTypeToWait()

val promiseUsers = promiseUserAdaptor.findByPromiseId(promiseId)

// 약속에 참여한 유저들 조회 (자기 자신 제외)
val users = promiseUsers
.map { promiseUser -> userAdapter.queryUser(promiseUser.userId) }
.filter { user -> user.id != userId }

// 도착한 유저
val arrivalUser = userAdapter.queryUser(userId)

// 앱 알람 허용한 유저
val appAlarmPermitUsers = users
.filter { user -> user.fcmNotification.fcmToken != "" && user.fcmNotification.appAlarm }

val data: MutableMap<String, String> = mutableMapOf()
data["notificationType"] = NotificationType.ARRIVAL.name
data["promiseId"] = promiseId.toString()
data["senderUserId"] = promiseUserUpdateLocationEvent.userId.toString()

// 행정동 조회
val intersects = districtRepository.findByLocationIntersects(
Point(
Position(promise.meetPlace!!.coordinate.longitude, promise.meetPlace!!.coordinate.latitude),
),
)
val district = intersects.properties.adm_nm

// 앱 알람 허용한 유저에게 알람 보내기
Comment on lines +51 to +79
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전체적으로 SRP 준수하게 변경해도 좋을 것 같아요 private 하게 data, intersects district 관련 조회들은 분리해볼 수 있지 않을까요? ㅎㅎ 동작 하기만 하면 상관 없지만 리펙토링 다음에 같이 해요 ㅎ

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fcm 발송하는 쪽 전체적으로 리팩토링이 필요할 것 같아요!!
감사합니다 🙇🏻

if (appAlarmPermitUsers.isNotEmpty()) {
fcmService.sendGroupMessageAsync(
tokenList = appAlarmPermitUsers.map { user -> user.fcmNotification.fcmToken!! },
title = "${arrivalUser.nickname} 도착완료",
content = "\uD83C\uDFC1 $district",
data = data,
)
}

// notification 저장
users.forEach { user ->
notificationDomainService.saveForArrival(promiseId, arrivalUser.id!!, user.id!!)
}
}
// 활성화된 약속이 종료되고 나서 도착 시 사전에 LATE처리를 해서 별도의 처리가 필요 없음
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ package com.depromeet.whatnow.events.handler

import com.depromeet.whatnow.common.vo.CoordinateVo
import com.depromeet.whatnow.config.DomainIntegrateSpringBootTest
import com.depromeet.whatnow.config.fcm.FcmService
import com.depromeet.whatnow.domains.district.repository.DistrictRepository
import com.depromeet.whatnow.domains.notification.service.NotificationDomainService
import com.depromeet.whatnow.domains.promise.adaptor.PromiseAdaptor
import com.depromeet.whatnow.domains.promiseuser.adaptor.PromiseUserAdaptor
import com.depromeet.whatnow.domains.promiseuser.domain.PromiseUser
import com.depromeet.whatnow.domains.promiseuser.domain.PromiseUserType
import com.depromeet.whatnow.domains.promiseuser.service.PromiseUserDomainService
import com.depromeet.whatnow.domains.user.adapter.UserAdapter
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
Expand All @@ -24,12 +28,32 @@ class PromiseUserUpdateLocationEventHandlerTest {
@Mock
lateinit var promiseUserDomainService: PromiseUserDomainService

@Mock
lateinit var userAdapter: UserAdapter

@Mock
lateinit var fcmService: FcmService

@Mock
lateinit var notificationDomainService: NotificationDomainService

@Mock
lateinit var districtRepository: DistrictRepository

@Autowired
lateinit var promiseUserUpdateLocationEventHandler: PromiseUserUpdateLocationEventHandler

@BeforeEach
fun setUp() {
promiseUserUpdateLocationEventHandler = PromiseUserUpdateLocationEventHandler(promiseAdaptor, promiseUserAdaptor, promiseUserDomainService)
promiseUserUpdateLocationEventHandler = PromiseUserUpdateLocationEventHandler(
promiseAdaptor,
promiseUserAdaptor,
promiseUserDomainService,
userAdapter,
fcmService,
notificationDomainService,
districtRepository,
)
}

@Test
Expand Down
Loading