Skip to content

Commit

Permalink
[COZY-216] TODO 완료시 FCM 알림 구현 (#84)
Browse files Browse the repository at this point in the history
* [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]>
  • Loading branch information
jpark0506 and veronees authored Aug 29, 2024
1 parent 55e901d commit e60d5b2
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 29 deletions.
2 changes: 1 addition & 1 deletion config
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ public class FcmPushTargetDto {

/**
* 상대에게만 알림이 가는 경우 or 나에게만 알림이 가는 경우 notificationType : 알림 종류
*
* best, worst 투표가 완료되면 알림 -> 투표 요청이 마지막 투표자인지 체크하고 Member에 Best 멤버 넣어서 사용
* 여기까지는 파라미터에 Member, NotificationType만 사용하면 됩니다!
*
* <p>
* best, worst 투표가 완료되면 알림 -> 투표 요청이 마지막 투표자인지 체크하고 Member에 Best 멤버 넣어서 사용 여기까지는 파라미터에 Member,
* NotificationType만 사용하면 됩니다!
* <p>
* --------------
*
* <p>
* roleContent or todoContents 는 스케줄러에서만 사용합니다!
*/
@Getter
Expand Down Expand Up @@ -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 : 알림 종류
* <p>
* 요약하면 알림 내용에 포함되는 멤버의 닉네임과 실제 알림을 받는 멤버가 다른 경우 OneTargetReverSeDto 사용
*/
@Getter
Expand Down Expand Up @@ -112,4 +110,29 @@ public static GroupTargetDto create(List<Member> memberList,
return new GroupTargetDto(memberList, notificationType);
}
}

/**
* 자신이 오늘의 모든 투두를 완료했을 때, 나를 제외한 룸메이트들에게 알림을 보낸다.
*/
@Getter
public static class GroupWithOutMeTargetDto {

private final Member me;
private final List<Member> memberList;
private final NotificationType notificationType;

private GroupWithOutMeTargetDto(Member me, List<Member> memberList,
NotificationType notificationType) {
this.me = me;
this.memberList = memberList;
this.notificationType = notificationType;
}

public static GroupWithOutMeTargetDto create(Member me, List<Member> memberList,
NotificationType notificationType) {
return new GroupWithOutMeTargetDto(me, memberList, notificationType);
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -63,6 +65,16 @@ public void sendNotification(GroupTargetDto target) {
});
}

@Async
public void sendNotification(GroupWithOutMeTargetDto target) {
List<Member> memberList = target.getMemberList();
Member me = target.getMe();

memberList.forEach(member -> {
sendNotificationToMember(me, member, target.getNotificationType());
});
}

private void sendNotificationToMember(Member member,
NotificationType notificationType) {
List<Message> messages = createMessage(member, notificationType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -45,4 +47,8 @@ Optional<Mate> 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<Mate> findAllByMemberBirthDayMonthAndDayAndEntryStatus(@Param("month") int month, @Param("day") int day, @Param("entryStatus") EntryStatus entryStatus);

List<Mate> findByRoom(Room room);

Optional<Mate> findByMember(Member member);
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -44,6 +47,7 @@ public ResponseEntity<ApiResponse<List<NotificationLogResponseDto>>> getNotifica
*/

private final FcmPushService fcmPushService;
private final MateRepository mateRepository;

@Deprecated
@GetMapping("/test")
Expand All @@ -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<String> 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> mate = mateRepository.findByMember(member);
//
// List<String> todoList = new ArrayList<>();
// todoList.add("설거지하기");
// todoList.add("콩 밥주기");
// todoList.add("밥 먹기");
// todoList.add("버스 표 끊기");
// fcmPushService.sendNotification(
// OneTargetDto.create(member, NotificationType.TODO_LIST, todoList));
// log.info("투두 리스트 알림 전송 완료");
// return "알림 전송 완료";
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,4 +17,7 @@ public interface TodoRepository extends JpaRepository<Todo, Long> {
List<Todo> findByTimePoint(LocalDate today);

List<Todo> findByTimePointAndRoleIsNotNull(LocalDate today);

boolean existsByMateAndTimePointAndCompletedFalse(Mate mate, LocalDate timePoint);

}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

Expand All @@ -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,
Expand Down Expand Up @@ -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<Mate> findRoomMates = mateRepository.findByRoom(todo.getRoom());

List<Member> 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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
}
}

0 comments on commit e60d5b2

Please sign in to comment.