Skip to content

Commit e60d5b2

Browse files
jpark0506momuzzi
andauthored
[COZY-216] TODO 완료시 FCM 알림 구현 (#84)
* [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 <[email protected]>
1 parent 55e901d commit e60d5b2

File tree

9 files changed

+120
-29
lines changed

9 files changed

+120
-29
lines changed

config

src/main/java/com/cozymate/cozymate_server/domain/fcm/dto/FcmPushTargetDto.java

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ public class FcmPushTargetDto {
99

1010
/**
1111
* 상대에게만 알림이 가는 경우 or 나에게만 알림이 가는 경우 notificationType : 알림 종류
12-
*
13-
* best, worst 투표가 완료되면 알림 -> 투표 요청이 마지막 투표자인지 체크하고 Member에 Best 멤버 넣어서 사용
14-
* 여기까지는 파라미터에 Member, NotificationType만 사용하면 됩니다!
15-
*
12+
* <p>
13+
* best, worst 투표가 완료되면 알림 -> 투표 요청이 마지막 투표자인지 체크하고 Member에 Best 멤버 넣어서 사용 여기까지는 파라미터에 Member,
14+
* NotificationType만 사용하면 됩니다!
15+
* <p>
1616
* --------------
17-
*
17+
* <p>
1818
* roleContent or todoContents 는 스케줄러에서만 사용합니다!
1919
*/
2020
@Getter
@@ -63,13 +63,11 @@ public static OneTargetDto create(Member member, NotificationType notificationTy
6363
}
6464

6565
/**
66-
* 코지메이트 신청을 한 경우 - A(나)가 B(상대)에게 코지메이트를 신청했다고 가정하면
67-
* ex) A님에게서 코지메이트 신청이 도착했어요! 알림을 B(상대)가 받고 B의 알림 로그에 저장 -> contentMember : A, recipientMember : B
68-
* ex) B님에게 코지메이트 신청을 보냈어요! 알림을 A(나)가 받고 A의 알림 로그에 저장 -> contentMember : B, recipientMember : A
69-
* contentMember: "xx님"에서 xx에 들어갈 이름의 멤버
70-
* recipientMember : 알림을 받는 member 즉, 로그가 저장될 대상 멤버
71-
* notificationType : 알림 종류
72-
*
66+
* 코지메이트 신청을 한 경우 - A(나)가 B(상대)에게 코지메이트를 신청했다고 가정하면 ex) A님에게서 코지메이트 신청이 도착했어요! 알림을 B(상대)가 받고 B의
67+
* 알림 로그에 저장 -> contentMember : A, recipientMember : B ex) B님에게 코지메이트 신청을 보냈어요! 알림을 A(나)가 받고 A의
68+
* 알림 로그에 저장 -> contentMember : B, recipientMember : A contentMember: "xx님"에서 xx에 들어갈 이름의 멤버
69+
* recipientMember : 알림을 받는 member 즉, 로그가 저장될 대상 멤버 notificationType : 알림 종류
70+
* <p>
7371
* 요약하면 알림 내용에 포함되는 멤버의 닉네임과 실제 알림을 받는 멤버가 다른 경우 OneTargetReverSeDto 사용
7472
*/
7573
@Getter
@@ -112,4 +110,29 @@ public static GroupTargetDto create(List<Member> memberList,
112110
return new GroupTargetDto(memberList, notificationType);
113111
}
114112
}
113+
114+
/**
115+
* 자신이 오늘의 모든 투두를 완료했을 때, 나를 제외한 룸메이트들에게 알림을 보낸다.
116+
*/
117+
@Getter
118+
public static class GroupWithOutMeTargetDto {
119+
120+
private final Member me;
121+
private final List<Member> memberList;
122+
private final NotificationType notificationType;
123+
124+
private GroupWithOutMeTargetDto(Member me, List<Member> memberList,
125+
NotificationType notificationType) {
126+
this.me = me;
127+
this.memberList = memberList;
128+
this.notificationType = notificationType;
129+
}
130+
131+
public static GroupWithOutMeTargetDto create(Member me, List<Member> memberList,
132+
NotificationType notificationType) {
133+
return new GroupWithOutMeTargetDto(me, memberList, notificationType);
134+
}
135+
}
136+
137+
115138
}

src/main/java/com/cozymate/cozymate_server/domain/fcm/service/FcmPushService.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.cozymate.cozymate_server.domain.fcm.Fcm;
44
import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushContentDto;
5+
import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupWithOutMeTargetDto;
56
import com.cozymate.cozymate_server.domain.fcm.repository.FcmRepository;
67
import com.cozymate.cozymate_server.domain.member.Member;
78
import com.cozymate.cozymate_server.domain.notificationlog.NotificationLog;
@@ -13,6 +14,7 @@
1314
import com.google.firebase.messaging.FirebaseMessaging;
1415
import com.google.firebase.messaging.FirebaseMessagingException;
1516
import com.google.firebase.messaging.Message;
17+
import com.google.firebase.messaging.Notification;
1618
import java.util.HashMap;
1719
import java.util.List;
1820
import lombok.RequiredArgsConstructor;
@@ -63,6 +65,16 @@ public void sendNotification(GroupTargetDto target) {
6365
});
6466
}
6567

68+
@Async
69+
public void sendNotification(GroupWithOutMeTargetDto target) {
70+
List<Member> memberList = target.getMemberList();
71+
Member me = target.getMe();
72+
73+
memberList.forEach(member -> {
74+
sendNotificationToMember(me, member, target.getNotificationType());
75+
});
76+
}
77+
6678
private void sendNotificationToMember(Member member,
6779
NotificationType notificationType) {
6880
List<Message> messages = createMessage(member, notificationType);

src/main/java/com/cozymate/cozymate_server/domain/mate/repository/MateRepository.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.cozymate.cozymate_server.domain.mate.Mate;
44
import com.cozymate.cozymate_server.domain.mate.enums.EntryStatus;
5+
import com.cozymate.cozymate_server.domain.member.Member;
6+
import com.cozymate.cozymate_server.domain.room.Room;
57
import com.cozymate.cozymate_server.domain.room.enums.RoomStatus;
68
import java.time.LocalDate;
79
import java.util.List;
@@ -45,4 +47,8 @@ Optional<Mate> findByMemberIdAndEntryStatusAndRoomStatusIn(Long memberId,
4547
// MemberBirthDay의 Localdate 값에서 Month와 Day가 같은 Member들을 찾는다.
4648
@Query("SELECT m FROM Mate m WHERE MONTH(m.member.birthDay) = :month AND DAY(m.member.birthDay) = :day AND m.entryStatus = :entryStatus")
4749
List<Mate> findAllByMemberBirthDayMonthAndDayAndEntryStatus(@Param("month") int month, @Param("day") int day, @Param("entryStatus") EntryStatus entryStatus);
50+
51+
List<Mate> findByRoom(Room room);
52+
53+
Optional<Mate> findByMember(Member member);
4854
}

src/main/java/com/cozymate/cozymate_server/domain/notificationlog/controller/NotificationLogController.java

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.cozymate.cozymate_server.domain.notificationlog.controller;
22

33
import com.cozymate.cozymate_server.domain.auth.userDetails.MemberDetails;
4+
import com.cozymate.cozymate_server.domain.mate.Mate;
5+
import com.cozymate.cozymate_server.domain.mate.repository.MateRepository;
46
import com.cozymate.cozymate_server.domain.member.Member;
57
import com.cozymate.cozymate_server.domain.notificationlog.dto.NotificationLogResponseDto;
68
import com.cozymate.cozymate_server.domain.notificationlog.enums.NotificationType;
@@ -12,6 +14,7 @@
1214
import io.swagger.v3.oas.annotations.Operation;
1315
import java.util.ArrayList;
1416
import java.util.List;
17+
import java.util.Optional;
1518
import lombok.RequiredArgsConstructor;
1619
import lombok.extern.slf4j.Slf4j;
1720
import org.springframework.http.ResponseEntity;
@@ -44,6 +47,7 @@ public ResponseEntity<ApiResponse<List<NotificationLogResponseDto>>> getNotifica
4447
*/
4548

4649
private final FcmPushService fcmPushService;
50+
private final MateRepository mateRepository;
4751

4852
@Deprecated
4953
@GetMapping("/test")
@@ -56,19 +60,22 @@ public String sendNotificationTest(@AuthenticationPrincipal MemberDetails member
5660
return "알림 전송 완료";
5761
}
5862

59-
@Deprecated
60-
@GetMapping("/test/todo")
61-
@Operation(summary = "알림 테스트용")
62-
public String sendTodoNotification(@AuthenticationPrincipal MemberDetails memberDetails) {
63-
Member member = memberDetails.getMember();
64-
List<String> todoList = new ArrayList<>();
65-
todoList.add("설거지하기");
66-
todoList.add("콩 밥주기");
67-
todoList.add("밥 먹기");
68-
todoList.add("버스 표 끊기");
69-
fcmPushService.sendNotification(
70-
OneTargetDto.create(member, NotificationType.TODO_LIST, todoList));
71-
log.info("투두 리스트 알림 전송 완료");
72-
return "알림 전송 완료";
73-
}
63+
// @Deprecated
64+
// @GetMapping("/test/todo")
65+
// @Operation(summary = "알림 테스트용")
66+
// public String sendTodoNotification(@AuthenticationPrincipal MemberDetails memberDetails) {
67+
// Member member = memberDetails.getMember();
68+
//
69+
// //Optional<Mate> mate = mateRepository.findByMember(member);
70+
//
71+
// List<String> todoList = new ArrayList<>();
72+
// todoList.add("설거지하기");
73+
// todoList.add("콩 밥주기");
74+
// todoList.add("밥 먹기");
75+
// todoList.add("버스 표 끊기");
76+
// fcmPushService.sendNotification(
77+
// OneTargetDto.create(member, NotificationType.TODO_LIST, todoList));
78+
// log.info("투두 리스트 알림 전송 완료");
79+
// return "알림 전송 완료";
80+
// }
7481
}

src/main/java/com/cozymate/cozymate_server/domain/notificationlog/enums/NotificationType.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@
1111
@Getter
1212
public enum NotificationType {
1313

14+
// GroupWithOutMeTarget
15+
// TODO :
16+
COMPLETE_ALL_TODAY_TODO(NotificationCategory.COZY_HOME) {
17+
@Override
18+
public String generateContent(FcmPushContentDto fcmPushContentDto) {
19+
return fcmPushContentDto.getMember().getNickname() + "님이 오늘 해야 할 일을 전부 완료했어요!";
20+
}
21+
},
22+
1423
// OneTargetReverse
1524
COZY_MATE_REQUEST_FROM(NotificationCategory.COZY_MATE_ARRIVE) {
1625
@Override

src/main/java/com/cozymate/cozymate_server/domain/todo/repository/TodoRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.cozymate.cozymate_server.domain.todo.repository;
22

3+
import com.cozymate.cozymate_server.domain.mate.Mate;
34
import com.cozymate.cozymate_server.domain.todo.Todo;
45
import java.time.LocalDate;
56
import java.util.List;
@@ -16,4 +17,7 @@ public interface TodoRepository extends JpaRepository<Todo, Long> {
1617
List<Todo> findByTimePoint(LocalDate today);
1718

1819
List<Todo> findByTimePointAndRoleIsNotNull(LocalDate today);
20+
21+
boolean existsByMateAndTimePointAndCompletedFalse(Mate mate, LocalDate timePoint);
22+
1923
}

src/main/java/com/cozymate/cozymate_server/domain/todo/service/TodoCommandService.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.cozymate.cozymate_server.domain.todo.service;
22

3+
import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupTargetDto;
4+
import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupWithOutMeTargetDto;
35
import com.cozymate.cozymate_server.domain.mate.Mate;
46
import com.cozymate.cozymate_server.domain.mate.repository.MateRepository;
57
import com.cozymate.cozymate_server.domain.member.Member;
8+
import com.cozymate.cozymate_server.domain.notificationlog.enums.NotificationType;
69
import com.cozymate.cozymate_server.domain.roomlog.service.RoomLogCommandService;
710
import com.cozymate.cozymate_server.domain.todo.Todo;
811
import com.cozymate.cozymate_server.domain.todo.dto.TodoRequestDto.CreateTodoRequestDto;
@@ -11,7 +14,11 @@
1114
import com.cozymate.cozymate_server.domain.todo.converter.TodoConverter;
1215
import com.cozymate.cozymate_server.global.response.code.status.ErrorStatus;
1316
import com.cozymate.cozymate_server.global.response.exception.GeneralException;
17+
import java.time.LocalDate;
18+
import java.util.List;
19+
import java.util.Optional;
1420
import lombok.RequiredArgsConstructor;
21+
import org.springframework.context.ApplicationEventPublisher;
1522
import org.springframework.stereotype.Service;
1623
import org.springframework.transaction.annotation.Transactional;
1724

@@ -25,6 +32,7 @@ public class TodoCommandService {
2532
private final MateRepository mateRepository;
2633
private final TodoRepository todoRepository;
2734
private final RoomLogCommandService roomLogCommandService;
35+
private final ApplicationEventPublisher eventPublisher;
2836

2937
public void createTodo(
3038
Member member,
@@ -59,10 +67,26 @@ public void updateTodoCompleteState(
5967
throw new GeneralException(ErrorStatus._TODO_NOT_VALID);
6068
}
6169
todo.updateCompleteState(requestDto.getCompleted());
62-
// 투두 완료시 변한 값을 기준으로 로그 추가
70+
6371
roomLogCommandService.addRoomLogFromTodo(todo);
6472

6573
todoRepository.save(todo);
74+
75+
76+
boolean existsFalseTodo = todoRepository.existsByMateAndTimePointAndCompletedFalse(
77+
todo.getMate(), LocalDate.now());
78+
79+
if (!existsFalseTodo) {
80+
List<Mate> findRoomMates = mateRepository.findByRoom(todo.getRoom());
81+
82+
List<Member> memberList = findRoomMates.stream()
83+
.map(Mate::getMember)
84+
.filter(findMember -> !findMember.getId().equals(member.getId()))
85+
.toList();
86+
87+
eventPublisher.publishEvent(GroupWithOutMeTargetDto.create(member, memberList,
88+
NotificationType.COMPLETE_ALL_TODAY_TODO));
89+
}
6690
}
6791

6892
public void deleteTodo(

src/main/java/com/cozymate/cozymate_server/global/event/EventListener.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.cozymate.cozymate_server.global.event;
22

3+
import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupWithOutMeTargetDto;
34
import com.cozymate.cozymate_server.domain.fcm.service.FcmPushService;
45
import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupTargetDto;
56
import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.OneTargetDto;
@@ -45,4 +46,9 @@ public void sendNotification(GroupTargetDto groupTargetDto) {
4546
public void sendNotification(OneTargetDto oneTargetDto) {
4647
fcmPushService.sendNotification(oneTargetDto);
4748
}
49+
50+
@TransactionalEventListener
51+
public void sendNotification(GroupWithOutMeTargetDto groupWithOutMeTargetDto) {
52+
fcmPushService.sendNotification(groupWithOutMeTargetDto);
53+
}
4854
}

0 commit comments

Comments
 (0)