From 2d58d36b064e3f126523adc38f9fd78fa9d75cd4 Mon Sep 17 00:00:00 2001 From: Jaehyeon Date: Wed, 22 Nov 2023 22:36:18 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20=EC=9E=AC=EB=82=9C=20=EB=B0=9C=EC=86=A1?= =?UTF-8?q?=20=EA=B8=B0=EC=A4=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/DisasterEventHandler.java | 59 +++++++++++++------ .../NotificationDisasterRepository.java | 4 ++ 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/numberone/backend/domain/disaster/service/DisasterEventHandler.java b/src/main/java/com/numberone/backend/domain/disaster/service/DisasterEventHandler.java index ce804443..8bd9aece 100644 --- a/src/main/java/com/numberone/backend/domain/disaster/service/DisasterEventHandler.java +++ b/src/main/java/com/numberone/backend/domain/disaster/service/DisasterEventHandler.java @@ -1,18 +1,22 @@ package com.numberone.backend.domain.disaster.service; import com.numberone.backend.domain.disaster.event.DisasterEvent; +import com.numberone.backend.domain.disaster.util.DisasterType; import com.numberone.backend.domain.friendship.entity.Friendship; import com.numberone.backend.domain.member.entity.Member; import com.numberone.backend.domain.member.repository.MemberRepository; import com.numberone.backend.domain.notification.entity.NotificationEntity; import com.numberone.backend.domain.notification.entity.NotificationTag; import com.numberone.backend.domain.notification.repository.NotificationRepository; +import com.numberone.backend.domain.notificationdisaster.entity.NotificationDisaster; +import com.numberone.backend.domain.notificationdisaster.repository.NotificationDisasterRepository; import com.numberone.backend.domain.notificationregion.repository.NotificationRegionRepository; import com.numberone.backend.exception.notfound.NotFoundMemberException; import com.numberone.backend.support.fcm.service.FcmMessageProvider; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.event.TransactionalEventListener; @@ -28,6 +32,8 @@ public class DisasterEventHandler { private final FcmMessageProvider fcmMessageProvider; private final NotificationRepository notificationRepository; private final NotificationRegionRepository notificationRegionRepository; + private final NotificationDisasterRepository notificationDisasterRepository; + @Transactional(jakarta.transaction.Transactional.TxType.REQUIRES_NEW) @TransactionalEventListener @@ -36,36 +42,51 @@ public void sendFcmMessagesByPresentLocation(DisasterEvent disasterEvent) { log.info("[sendFcmMessagesByPresentLocation]"); String type = disasterEvent.getType().code2kor(); - String location = disasterEvent.getLocation(); + String disasterLocation = disasterEvent.getLocation(); Long disasterNum = disasterEvent.getDisasterNum(); - String title = String.format("[긴급] %s %s 발생", location, type); + String title = String.format("[긴급] %s %s 발생", disasterLocation, type); String message = "대피로에 접속하여 행동요령을 확인하세요!"; - // 현재 재난 위치에 있는 회원에게 알림을 보낸다. - log.info("현재 재난 위치에 있는 회원에게 알림을 전송합니다."); - List memberIdListByPresentLocation = memberRepository.findAllByLocation(location); + // 현재 재난 위치에 있는 회원 아이디 리스트 + List memberIdListByOnlyPresentLocation = memberRepository.findAllByLocation(disasterLocation); + - List targetMemberFcmTokens = memberIdListByPresentLocation.stream().map(memberId -> { + // 현재 재난 위치에 있으면서, 지금 발생한 재난 유형이 온보딩 때 선택한 재난 유형에 속하는 경우 핸들링 + List memberIdListByPresentLocationAndOnboardingDisasterTypes = memberIdListByOnlyPresentLocation.stream().filter( + memberId -> { + List enableDisasterTypes = notificationDisasterRepository.findAllByMemberId(memberId) + .stream().map(NotificationDisaster::getDisasterType).toList(); + return enableDisasterTypes.contains(disasterEvent.getType()); + }).map(memberId -> { Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); NotificationEntity savedNotificationEntity = notificationRepository.save( - new NotificationEntity(member, disasterEvent.getType(), disasterEvent.getMessage(), true, location) + new NotificationEntity(member, disasterEvent.getType(), disasterEvent.getMessage(), true, disasterLocation) ); member.updateSafety(false); log.info("received member id: {} Notification id: {} ", member.getId(), savedNotificationEntity.getId()); log.info(title); log.info(message); + return member.getId(); + }).filter(Objects::nonNull).toList(); + + List fcmTokensByPresentLocationAndOnboardingDisasterType = memberIdListByPresentLocationAndOnboardingDisasterTypes.stream().map(memberId -> { + Member member = memberRepository.findById(memberId) + .orElseThrow(NotFoundMemberException::new); return member.getFcmToken(); }).filter(Objects::nonNull).toList(); // fcm 메세지 일괄 전송 - fcmMessageProvider.sendFcmToMembers(targetMemberFcmTokens, title, message, NotificationTag.DISASTER); + log.info("현재 재난 위치에 있는, {} 재난 유형을 허용한 회원들에게 알림을 전송합니다.", type); + fcmMessageProvider.sendFcmToMembers(fcmTokensByPresentLocationAndOnboardingDisasterType, title, message, NotificationTag.DISASTER); + + log.info("위험 지역에 위치한 회원의 가족에게 알림을 보냅니다."); // 해당 회원의 가족에게 알림을 보낸다. String messageToFriend = ""; String titleToFriend = ""; - memberIdListByPresentLocation.forEach(memberId -> { + memberIdListByOnlyPresentLocation.forEach(memberId -> { Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); @@ -93,28 +114,30 @@ public void sendFcmMessagesByPresentLocation(DisasterEvent disasterEvent) { ); }); - // 중복 알람 방지 - List memberIdListByOnboardingRegions = memberRepository.findAll() + // 온보딩때 선택한 지역에 대한 알림을 받고자 하는 회원 리스트를 필터링합니다. + // 이때 중복 알림 발송을 방지하기 위한 필터링 작업을 먼저 수행합니다. + List distinctMemberIdListByOnboardingRegions = memberRepository.findAll() .stream().map(Member::getId) - .filter(id -> !memberIdListByPresentLocation.contains(id)) + .filter(id -> !memberIdListByPresentLocationAndOnboardingDisasterTypes.contains(id)) .toList(); + log.info("회원이 재난문자 알림을 받고자 하는 지역에 대한 푸시알람을 중복을 제거하여 보냅니다."); - // 해당 회원의 온보딩 리스트를 기준으로 알림을 보낸다. - List targetFcmListByOnboardingRegions = memberIdListByOnboardingRegions.stream() + // 해당 회원의 온보딩 리스트 및 알림을 허용하는 재난 유형을 기준으로 알림을 보낸다. + List targetFcmsByOnboardingRegionsAndDisasterTypes = distinctMemberIdListByOnboardingRegions.stream() .flatMap(memberId -> { Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); boolean isMatched = notificationRegionRepository.findByMemberId(memberId) .stream().anyMatch( - region -> region.getLocation().contains(location) + region -> region.getLocation().contains(disasterLocation) ); notificationRepository.save( - new NotificationEntity(member, disasterEvent.getType(), disasterEvent.getMessage(), true, location) + new NotificationEntity(member, disasterEvent.getType(), disasterEvent.getMessage(), true, disasterLocation) ); - return isMatched ? Stream.of(member.getFcmToken()) : Stream.empty(); + return isMatched ? Stream.of(member.getFcmToken()) : null; }).filter(Objects::nonNull).toList(); - fcmMessageProvider.sendFcmToMembers(targetFcmListByOnboardingRegions, title, message, NotificationTag.DISASTER); + fcmMessageProvider.sendFcmToMembers(targetFcmsByOnboardingRegionsAndDisasterTypes, title, message, NotificationTag.DISASTER); } } diff --git a/src/main/java/com/numberone/backend/domain/notificationdisaster/repository/NotificationDisasterRepository.java b/src/main/java/com/numberone/backend/domain/notificationdisaster/repository/NotificationDisasterRepository.java index 1d79e31e..76babb1c 100644 --- a/src/main/java/com/numberone/backend/domain/notificationdisaster/repository/NotificationDisasterRepository.java +++ b/src/main/java/com/numberone/backend/domain/notificationdisaster/repository/NotificationDisasterRepository.java @@ -3,6 +3,10 @@ import com.numberone.backend.domain.notificationdisaster.entity.NotificationDisaster; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface NotificationDisasterRepository extends JpaRepository { void deleteAllByMemberId(Long memberId); + List findAllByMemberId(Long memberId); + }