From e60d5b26fe931a521dd5c375324be05a73de6123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JunhyeokPark=28=EB=B0=95=EC=A4=80=ED=98=81=29?= Date: Thu, 29 Aug 2024 16:34:17 +0900 Subject: [PATCH] =?UTF-8?q?[COZY-216]=20TODO=20=EC=99=84=EB=A3=8C=EC=8B=9C?= =?UTF-8?q?=20FCM=20=EC=95=8C=EB=A6=BC=20=EA=B5=AC=ED=98=84=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [COZY-216] feat : 나의 오늘의 todo 모두 완료 시 룸메이트에게 완료 푸시 알림 전송 * [COZY-216] fix : 오류 수정 * [COZY-216] feat: Firebase 이사 반영 submodule * [COZY-216] feat : Message에 Notification 포함(for iOS) * [COZY-216] feat: TODO 완료시 FCM 알림 구현 --------- Co-authored-by: veronees --- config | 2 +- .../domain/fcm/dto/FcmPushTargetDto.java | 47 ++++++++++++++----- .../domain/fcm/service/FcmPushService.java | 12 +++++ .../mate/repository/MateRepository.java | 6 +++ .../controller/NotificationLogController.java | 37 +++++++++------ .../enums/NotificationType.java | 9 ++++ .../todo/repository/TodoRepository.java | 4 ++ .../todo/service/TodoCommandService.java | 26 +++++++++- .../global/event/EventListener.java | 6 +++ 9 files changed, 120 insertions(+), 29 deletions(-) diff --git a/config b/config index a437118e..811ebcaa 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit a437118e53224b1b304a2618e3a372fa42c48bea +Subproject commit 811ebcaa18381d3817514d70feaf3d4b1f689321 diff --git a/src/main/java/com/cozymate/cozymate_server/domain/fcm/dto/FcmPushTargetDto.java b/src/main/java/com/cozymate/cozymate_server/domain/fcm/dto/FcmPushTargetDto.java index 7c79de26..afb214aa 100644 --- a/src/main/java/com/cozymate/cozymate_server/domain/fcm/dto/FcmPushTargetDto.java +++ b/src/main/java/com/cozymate/cozymate_server/domain/fcm/dto/FcmPushTargetDto.java @@ -9,12 +9,12 @@ public class FcmPushTargetDto { /** * 상대에게만 알림이 가는 경우 or 나에게만 알림이 가는 경우 notificationType : 알림 종류 - * - * best, worst 투표가 완료되면 알림 -> 투표 요청이 마지막 투표자인지 체크하고 Member에 Best 멤버 넣어서 사용 - * 여기까지는 파라미터에 Member, NotificationType만 사용하면 됩니다! - * + *

+ * best, worst 투표가 완료되면 알림 -> 투표 요청이 마지막 투표자인지 체크하고 Member에 Best 멤버 넣어서 사용 여기까지는 파라미터에 Member, + * NotificationType만 사용하면 됩니다! + *

* -------------- - * + *

* roleContent or todoContents 는 스케줄러에서만 사용합니다! */ @Getter @@ -63,13 +63,11 @@ public static OneTargetDto create(Member member, NotificationType notificationTy } /** - * 코지메이트 신청을 한 경우 - A(나)가 B(상대)에게 코지메이트를 신청했다고 가정하면 - * ex) A님에게서 코지메이트 신청이 도착했어요! 알림을 B(상대)가 받고 B의 알림 로그에 저장 -> contentMember : A, recipientMember : B - * ex) B님에게 코지메이트 신청을 보냈어요! 알림을 A(나)가 받고 A의 알림 로그에 저장 -> contentMember : B, recipientMember : A - * contentMember: "xx님"에서 xx에 들어갈 이름의 멤버 - * recipientMember : 알림을 받는 member 즉, 로그가 저장될 대상 멤버 - * notificationType : 알림 종류 - * + * 코지메이트 신청을 한 경우 - A(나)가 B(상대)에게 코지메이트를 신청했다고 가정하면 ex) A님에게서 코지메이트 신청이 도착했어요! 알림을 B(상대)가 받고 B의 + * 알림 로그에 저장 -> contentMember : A, recipientMember : B ex) B님에게 코지메이트 신청을 보냈어요! 알림을 A(나)가 받고 A의 + * 알림 로그에 저장 -> contentMember : B, recipientMember : A contentMember: "xx님"에서 xx에 들어갈 이름의 멤버 + * recipientMember : 알림을 받는 member 즉, 로그가 저장될 대상 멤버 notificationType : 알림 종류 + *

* 요약하면 알림 내용에 포함되는 멤버의 닉네임과 실제 알림을 받는 멤버가 다른 경우 OneTargetReverSeDto 사용 */ @Getter @@ -112,4 +110,29 @@ public static GroupTargetDto create(List memberList, return new GroupTargetDto(memberList, notificationType); } } + + /** + * 자신이 오늘의 모든 투두를 완료했을 때, 나를 제외한 룸메이트들에게 알림을 보낸다. + */ + @Getter + public static class GroupWithOutMeTargetDto { + + private final Member me; + private final List memberList; + private final NotificationType notificationType; + + private GroupWithOutMeTargetDto(Member me, List memberList, + NotificationType notificationType) { + this.me = me; + this.memberList = memberList; + this.notificationType = notificationType; + } + + public static GroupWithOutMeTargetDto create(Member me, List memberList, + NotificationType notificationType) { + return new GroupWithOutMeTargetDto(me, memberList, notificationType); + } + } + + } \ No newline at end of file diff --git a/src/main/java/com/cozymate/cozymate_server/domain/fcm/service/FcmPushService.java b/src/main/java/com/cozymate/cozymate_server/domain/fcm/service/FcmPushService.java index 9356979d..518a6e89 100644 --- a/src/main/java/com/cozymate/cozymate_server/domain/fcm/service/FcmPushService.java +++ b/src/main/java/com/cozymate/cozymate_server/domain/fcm/service/FcmPushService.java @@ -2,6 +2,7 @@ import com.cozymate.cozymate_server.domain.fcm.Fcm; import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushContentDto; +import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupWithOutMeTargetDto; import com.cozymate.cozymate_server.domain.fcm.repository.FcmRepository; import com.cozymate.cozymate_server.domain.member.Member; import com.cozymate.cozymate_server.domain.notificationlog.NotificationLog; @@ -13,6 +14,7 @@ import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.messaging.FirebaseMessagingException; import com.google.firebase.messaging.Message; +import com.google.firebase.messaging.Notification; import java.util.HashMap; import java.util.List; import lombok.RequiredArgsConstructor; @@ -63,6 +65,16 @@ public void sendNotification(GroupTargetDto target) { }); } + @Async + public void sendNotification(GroupWithOutMeTargetDto target) { + List memberList = target.getMemberList(); + Member me = target.getMe(); + + memberList.forEach(member -> { + sendNotificationToMember(me, member, target.getNotificationType()); + }); + } + private void sendNotificationToMember(Member member, NotificationType notificationType) { List messages = createMessage(member, notificationType); diff --git a/src/main/java/com/cozymate/cozymate_server/domain/mate/repository/MateRepository.java b/src/main/java/com/cozymate/cozymate_server/domain/mate/repository/MateRepository.java index cef04b54..c824cfa9 100644 --- a/src/main/java/com/cozymate/cozymate_server/domain/mate/repository/MateRepository.java +++ b/src/main/java/com/cozymate/cozymate_server/domain/mate/repository/MateRepository.java @@ -2,6 +2,8 @@ import com.cozymate.cozymate_server.domain.mate.Mate; import com.cozymate.cozymate_server.domain.mate.enums.EntryStatus; +import com.cozymate.cozymate_server.domain.member.Member; +import com.cozymate.cozymate_server.domain.room.Room; import com.cozymate.cozymate_server.domain.room.enums.RoomStatus; import java.time.LocalDate; import java.util.List; @@ -45,4 +47,8 @@ Optional findByMemberIdAndEntryStatusAndRoomStatusIn(Long memberId, // MemberBirthDay의 Localdate 값에서 Month와 Day가 같은 Member들을 찾는다. @Query("SELECT m FROM Mate m WHERE MONTH(m.member.birthDay) = :month AND DAY(m.member.birthDay) = :day AND m.entryStatus = :entryStatus") List findAllByMemberBirthDayMonthAndDayAndEntryStatus(@Param("month") int month, @Param("day") int day, @Param("entryStatus") EntryStatus entryStatus); + + List findByRoom(Room room); + + Optional findByMember(Member member); } diff --git a/src/main/java/com/cozymate/cozymate_server/domain/notificationlog/controller/NotificationLogController.java b/src/main/java/com/cozymate/cozymate_server/domain/notificationlog/controller/NotificationLogController.java index eb2baf71..361addf7 100644 --- a/src/main/java/com/cozymate/cozymate_server/domain/notificationlog/controller/NotificationLogController.java +++ b/src/main/java/com/cozymate/cozymate_server/domain/notificationlog/controller/NotificationLogController.java @@ -1,6 +1,8 @@ package com.cozymate.cozymate_server.domain.notificationlog.controller; import com.cozymate.cozymate_server.domain.auth.userDetails.MemberDetails; +import com.cozymate.cozymate_server.domain.mate.Mate; +import com.cozymate.cozymate_server.domain.mate.repository.MateRepository; import com.cozymate.cozymate_server.domain.member.Member; import com.cozymate.cozymate_server.domain.notificationlog.dto.NotificationLogResponseDto; import com.cozymate.cozymate_server.domain.notificationlog.enums.NotificationType; @@ -12,6 +14,7 @@ import io.swagger.v3.oas.annotations.Operation; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; @@ -44,6 +47,7 @@ public ResponseEntity>> getNotifica */ private final FcmPushService fcmPushService; + private final MateRepository mateRepository; @Deprecated @GetMapping("/test") @@ -56,19 +60,22 @@ public String sendNotificationTest(@AuthenticationPrincipal MemberDetails member return "알림 전송 완료"; } - @Deprecated - @GetMapping("/test/todo") - @Operation(summary = "알림 테스트용") - public String sendTodoNotification(@AuthenticationPrincipal MemberDetails memberDetails) { - Member member = memberDetails.getMember(); - List todoList = new ArrayList<>(); - todoList.add("설거지하기"); - todoList.add("콩 밥주기"); - todoList.add("밥 먹기"); - todoList.add("버스 표 끊기"); - fcmPushService.sendNotification( - OneTargetDto.create(member, NotificationType.TODO_LIST, todoList)); - log.info("투두 리스트 알림 전송 완료"); - return "알림 전송 완료"; - } +// @Deprecated +// @GetMapping("/test/todo") +// @Operation(summary = "알림 테스트용") +// public String sendTodoNotification(@AuthenticationPrincipal MemberDetails memberDetails) { +// Member member = memberDetails.getMember(); +// +// //Optional mate = mateRepository.findByMember(member); +// +// List todoList = new ArrayList<>(); +// todoList.add("설거지하기"); +// todoList.add("콩 밥주기"); +// todoList.add("밥 먹기"); +// todoList.add("버스 표 끊기"); +// fcmPushService.sendNotification( +// OneTargetDto.create(member, NotificationType.TODO_LIST, todoList)); +// log.info("투두 리스트 알림 전송 완료"); +// return "알림 전송 완료"; +// } } \ No newline at end of file diff --git a/src/main/java/com/cozymate/cozymate_server/domain/notificationlog/enums/NotificationType.java b/src/main/java/com/cozymate/cozymate_server/domain/notificationlog/enums/NotificationType.java index 5485c06e..d3efcda2 100644 --- a/src/main/java/com/cozymate/cozymate_server/domain/notificationlog/enums/NotificationType.java +++ b/src/main/java/com/cozymate/cozymate_server/domain/notificationlog/enums/NotificationType.java @@ -11,6 +11,15 @@ @Getter public enum NotificationType { + // GroupWithOutMeTarget + // TODO : + COMPLETE_ALL_TODAY_TODO(NotificationCategory.COZY_HOME) { + @Override + public String generateContent(FcmPushContentDto fcmPushContentDto) { + return fcmPushContentDto.getMember().getNickname() + "님이 오늘 해야 할 일을 전부 완료했어요!"; + } + }, + // OneTargetReverse COZY_MATE_REQUEST_FROM(NotificationCategory.COZY_MATE_ARRIVE) { @Override diff --git a/src/main/java/com/cozymate/cozymate_server/domain/todo/repository/TodoRepository.java b/src/main/java/com/cozymate/cozymate_server/domain/todo/repository/TodoRepository.java index b2caddcb..8de20fcf 100644 --- a/src/main/java/com/cozymate/cozymate_server/domain/todo/repository/TodoRepository.java +++ b/src/main/java/com/cozymate/cozymate_server/domain/todo/repository/TodoRepository.java @@ -1,5 +1,6 @@ package com.cozymate.cozymate_server.domain.todo.repository; +import com.cozymate.cozymate_server.domain.mate.Mate; import com.cozymate.cozymate_server.domain.todo.Todo; import java.time.LocalDate; import java.util.List; @@ -16,4 +17,7 @@ public interface TodoRepository extends JpaRepository { List findByTimePoint(LocalDate today); List findByTimePointAndRoleIsNotNull(LocalDate today); + + boolean existsByMateAndTimePointAndCompletedFalse(Mate mate, LocalDate timePoint); + } diff --git a/src/main/java/com/cozymate/cozymate_server/domain/todo/service/TodoCommandService.java b/src/main/java/com/cozymate/cozymate_server/domain/todo/service/TodoCommandService.java index 07f26f0c..a8d3053c 100644 --- a/src/main/java/com/cozymate/cozymate_server/domain/todo/service/TodoCommandService.java +++ b/src/main/java/com/cozymate/cozymate_server/domain/todo/service/TodoCommandService.java @@ -1,8 +1,11 @@ package com.cozymate.cozymate_server.domain.todo.service; +import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupTargetDto; +import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupWithOutMeTargetDto; import com.cozymate.cozymate_server.domain.mate.Mate; import com.cozymate.cozymate_server.domain.mate.repository.MateRepository; import com.cozymate.cozymate_server.domain.member.Member; +import com.cozymate.cozymate_server.domain.notificationlog.enums.NotificationType; import com.cozymate.cozymate_server.domain.roomlog.service.RoomLogCommandService; import com.cozymate.cozymate_server.domain.todo.Todo; import com.cozymate.cozymate_server.domain.todo.dto.TodoRequestDto.CreateTodoRequestDto; @@ -11,7 +14,11 @@ import com.cozymate.cozymate_server.domain.todo.converter.TodoConverter; import com.cozymate.cozymate_server.global.response.code.status.ErrorStatus; import com.cozymate.cozymate_server.global.response.exception.GeneralException; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -25,6 +32,7 @@ public class TodoCommandService { private final MateRepository mateRepository; private final TodoRepository todoRepository; private final RoomLogCommandService roomLogCommandService; + private final ApplicationEventPublisher eventPublisher; public void createTodo( Member member, @@ -59,10 +67,26 @@ public void updateTodoCompleteState( throw new GeneralException(ErrorStatus._TODO_NOT_VALID); } todo.updateCompleteState(requestDto.getCompleted()); - // 투두 완료시 변한 값을 기준으로 로그 추가 + roomLogCommandService.addRoomLogFromTodo(todo); todoRepository.save(todo); + + + boolean existsFalseTodo = todoRepository.existsByMateAndTimePointAndCompletedFalse( + todo.getMate(), LocalDate.now()); + + if (!existsFalseTodo) { + List findRoomMates = mateRepository.findByRoom(todo.getRoom()); + + List memberList = findRoomMates.stream() + .map(Mate::getMember) + .filter(findMember -> !findMember.getId().equals(member.getId())) + .toList(); + + eventPublisher.publishEvent(GroupWithOutMeTargetDto.create(member, memberList, + NotificationType.COMPLETE_ALL_TODAY_TODO)); + } } public void deleteTodo( diff --git a/src/main/java/com/cozymate/cozymate_server/global/event/EventListener.java b/src/main/java/com/cozymate/cozymate_server/global/event/EventListener.java index 05ebd073..91eba496 100644 --- a/src/main/java/com/cozymate/cozymate_server/global/event/EventListener.java +++ b/src/main/java/com/cozymate/cozymate_server/global/event/EventListener.java @@ -1,5 +1,6 @@ package com.cozymate.cozymate_server.global.event; +import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupWithOutMeTargetDto; import com.cozymate.cozymate_server.domain.fcm.service.FcmPushService; import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupTargetDto; import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.OneTargetDto; @@ -45,4 +46,9 @@ public void sendNotification(GroupTargetDto groupTargetDto) { public void sendNotification(OneTargetDto oneTargetDto) { fcmPushService.sendNotification(oneTargetDto); } + + @TransactionalEventListener + public void sendNotification(GroupWithOutMeTargetDto groupWithOutMeTargetDto) { + fcmPushService.sendNotification(groupWithOutMeTargetDto); + } } \ No newline at end of file