diff --git a/Whatnow-Api/src/main/kotlin/com/depromeet/whatnow/api/notification/dto/ArrivalNotificationResponse.kt b/Whatnow-Api/src/main/kotlin/com/depromeet/whatnow/api/notification/dto/ArrivalNotificationResponse.kt new file mode 100644 index 00000000..a1e22894 --- /dev/null +++ b/Whatnow-Api/src/main/kotlin/com/depromeet/whatnow/api/notification/dto/ArrivalNotificationResponse.kt @@ -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, + ) + } + } +} diff --git a/Whatnow-Api/src/main/kotlin/com/depromeet/whatnow/api/notification/usecase/NotificationReadUseCase.kt b/Whatnow-Api/src/main/kotlin/com/depromeet/whatnow/api/notification/usecase/NotificationReadUseCase.kt index 5610344c..1eb737b8 100644 --- a/Whatnow-Api/src/main/kotlin/com/depromeet/whatnow/api/notification/usecase/NotificationReadUseCase.kt +++ b/Whatnow-Api/src/main/kotlin/com/depromeet/whatnow/api/notification/usecase/NotificationReadUseCase.kt @@ -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 @@ -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 @@ -46,6 +48,9 @@ class NotificationReadUseCase( is TimeOverNotification -> { TimeOverNotificationResponse.from(notification) } + is ArrivalNotification -> { + ArrivalNotificationResponse.from(notification) + } else -> throw UnknownNotificationTypeException.EXCEPTION } } diff --git a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/notification/domain/ArrivalNotification.kt b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/notification/domain/ArrivalNotification.kt new file mode 100644 index 00000000..22a3042c --- /dev/null +++ b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/notification/domain/ArrivalNotification.kt @@ -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) diff --git a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/notification/service/NotificationDomainService.kt b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/notification/service/NotificationDomainService.kt index 4fa22df8..c6ad87b4 100644 --- a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/notification/service/NotificationDomainService.kt +++ b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/notification/service/NotificationDomainService.kt @@ -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 @@ -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 { return notificationAdapter.getMyNotifications(userId, pageable) diff --git a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/domainEvent/PromiseUserUpdateLocationEvent.kt b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/domainEvent/PromiseUserUpdateLocationEvent.kt index 622dd7cb..28a260b0 100644 --- a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/domainEvent/PromiseUserUpdateLocationEvent.kt +++ b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/domainEvent/PromiseUserUpdateLocationEvent.kt @@ -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() diff --git a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/ImageRegisterEventHandler.kt b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/ImageRegisterEventHandler.kt index d3fa9d45..30591a5c 100644 --- a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/ImageRegisterEventHandler.kt +++ b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/ImageRegisterEventHandler.kt @@ -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 = mutableMapOf() diff --git a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseTimeEndEventHandler.kt b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseTimeEndEventHandler.kt index d1c0fe9c..603de340 100644 --- a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseTimeEndEventHandler.kt +++ b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseTimeEndEventHandler.kt @@ -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 = mutableMapOf() lateData["notificationType"] = NotificationType.TIMEOVER.name diff --git a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseTimeStartEventHandler.kt b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseTimeStartEventHandler.kt index 0663a709..3735f884 100644 --- a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseTimeStartEventHandler.kt +++ b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseTimeStartEventHandler.kt @@ -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 = mutableMapOf() data["notificationType"] = NotificationType.START_SHARING.name diff --git a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseTrackingTimeEndEventHandler.kt b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseTrackingTimeEndEventHandler.kt index 6a55af43..f37c8759 100644 --- a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseTrackingTimeEndEventHandler.kt +++ b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseTrackingTimeEndEventHandler.kt @@ -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 = mutableMapOf() data["notificationType"] = NotificationType.END_SHARING.name diff --git a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseUserUpdateLocationEventHandler.kt b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseUserUpdateLocationEventHandler.kt index 35a54d00..32c3feda 100644 --- a/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseUserUpdateLocationEventHandler.kt +++ b/Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/handler/PromiseUserUpdateLocationEventHandler.kt @@ -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 @@ -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 @@ -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 = 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 + + // 앱 알람 허용한 유저에게 알람 보내기 + 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처리를 해서 별도의 처리가 필요 없음 } diff --git a/Whatnow-Domain/src/test/kotlin/com/depromeet/whatnow/events/handler/PromiseUserUpdateLocationEventHandlerTest.kt b/Whatnow-Domain/src/test/kotlin/com/depromeet/whatnow/events/handler/PromiseUserUpdateLocationEventHandlerTest.kt index b4796f84..eb3d349e 100644 --- a/Whatnow-Domain/src/test/kotlin/com/depromeet/whatnow/events/handler/PromiseUserUpdateLocationEventHandlerTest.kt +++ b/Whatnow-Domain/src/test/kotlin/com/depromeet/whatnow/events/handler/PromiseUserUpdateLocationEventHandlerTest.kt @@ -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 @@ -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