diff --git a/src/main/java/com/cozymate/cozymate_server/domain/fcm/dto/FcmPushContentDto.java b/src/main/java/com/cozymate/cozymate_server/domain/fcm/dto/FcmPushContentDto.java index 6a38b4c4..baa0bd8e 100644 --- a/src/main/java/com/cozymate/cozymate_server/domain/fcm/dto/FcmPushContentDto.java +++ b/src/main/java/com/cozymate/cozymate_server/domain/fcm/dto/FcmPushContentDto.java @@ -1,6 +1,7 @@ package com.cozymate.cozymate_server.domain.fcm.dto; import com.cozymate.cozymate_server.domain.member.Member; +import com.cozymate.cozymate_server.domain.room.Room; import java.util.List; import lombok.Getter; @@ -10,11 +11,20 @@ public class FcmPushContentDto { private Member member; private String roleContent; private List todoContents; + private Room room; + + private FcmPushContentDto(Member member, Room room) { + this.member = member; + this.room = room; + roleContent = null; + todoContents = null; + } private FcmPushContentDto(Member member) { this.member = member; roleContent = null; todoContents = null; + room = null; } // 투두 내용 리스트로 전부 줘야하는 경우, 00시 투드 리스트 스케줄러 @@ -22,6 +32,7 @@ private FcmPushContentDto(Member member, List todoContents) { this.member = member; this.todoContents = todoContents; roleContent = null; + room = null; } // 롤 내용 하나만 줘야하는 경우, 21시 미완료 Role 한개만 리마인더 스케줄러 @@ -29,6 +40,7 @@ private FcmPushContentDto(Member member, String roleContent) { this.member= member; this.roleContent = roleContent; todoContents = null; + room = null; } private FcmPushContentDto() { @@ -46,6 +58,10 @@ public static FcmPushContentDto create(Member member, List todoContents) return new FcmPushContentDto(member, todoContents); } + public static FcmPushContentDto create(Member member, Room room) { + return new FcmPushContentDto(member, room); + } + // 방이 열렸어요 -> 에서 사용합니다! public static FcmPushContentDto create() { return new FcmPushContentDto(); 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 afb214aa..e715294f 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 @@ -2,6 +2,7 @@ import com.cozymate.cozymate_server.domain.member.Member; import com.cozymate.cozymate_server.domain.notificationlog.enums.NotificationType; +import com.cozymate.cozymate_server.domain.room.Room; import java.util.List; import lombok.Getter; @@ -134,5 +135,25 @@ public static GroupWithOutMeTargetDto create(Member me, List memberList, } } + @Getter + public static class GroupRoomNameWithOutMeTargetDto { + private final Member me; + private final List memberList; + private final Room room; + private final NotificationType notificationType; + + private GroupRoomNameWithOutMeTargetDto(Member me, List memberList, Room room, + NotificationType notificationType) { + this.me = me; + this.memberList = memberList; + this.room = room; + this.notificationType = notificationType; + } + + public static GroupRoomNameWithOutMeTargetDto create(Member me, List memberList, + Room room, NotificationType notificationType) { + return new GroupRoomNameWithOutMeTargetDto(me, memberList, room, 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 518a6e89..91a4fb54 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.GroupRoomNameWithOutMeTargetDto; 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; @@ -11,10 +12,10 @@ import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupTargetDto; import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.OneTargetReverseDto; import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.OneTargetDto; +import com.cozymate.cozymate_server.domain.room.Room; 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; @@ -260,4 +261,78 @@ private String getContent(Member member, NotificationType notificationType, return notificationType.generateContent( FcmPushContentDto.create(member, todoContents)); } + + + + + + + + + + + + + + + + + /** + * 방 입장 시 : 방에 속한 메이트 모두에게 [방이름]에 [닉네임]님이 뛰어들어왔어요! 알림 전송 + */ + public void sendNotification(GroupRoomNameWithOutMeTargetDto target) { + List memberList = target.getMemberList(); // 알림을 받을 멤버 리스트 + Member me = target.getMe(); // 알림 내용에 들어갈 멤버 (의 이름) + Room room = target.getRoom(); // 알림 내용에 들어갈 방 (의 이름) + + memberList.forEach(member -> { + sendNotificationToMember(me, room, member, target.getNotificationType()); + }); + } + + private void sendNotificationToMember(Member contentMember, Room room, Member recipientMember, + NotificationType notificationType) { + List messages = createMessage(contentMember, room, recipientMember, notificationType); + + messages.forEach(message -> { + try { + firebaseMessaging.send(message); + } catch (FirebaseMessagingException e) { + log.error("cannot send to member push message. error info : {}", e.getMessage()); + } + }); + + NotificationLog notificationLog = NotificationLog.builder() + .member(recipientMember) + .category(notificationType.getCategory()) + .content(getContent(contentMember, room, notificationType)) + .build(); + + notificationLogRepository.save(notificationLog); + } + + private List createMessage(Member contentMember, Room room, Member recipientMember, NotificationType notificationType) { + List fcmList = fcmRepository.findByMember(recipientMember); + + List messages = fcmList.stream() + .map(fcm -> { + String token = fcm.getToken(); + String content = getContent(contentMember, room, notificationType); + + HashMap messageMap = new HashMap<>(); + messageMap.put("title", NOTIFICATION_TITLE); + messageMap.put("body", content); + + return Message.builder() + .putAllData(messageMap) + .setToken(token) + .build(); + }).toList(); + + return messages; + } + + private String getContent(Member member, Room room, NotificationType notificationType) { + return notificationType.generateContent(FcmPushContentDto.create(member, room)); + } } \ No newline at end of file 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 c824cfa9..70dc9b53 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 @@ -51,4 +51,7 @@ Optional findByMemberIdAndEntryStatusAndRoomStatusIn(Long memberId, List findByRoom(Room room); Optional findByMember(Member member); + + @Query("select m from Mate m join fetch m.member") + List findFetchAll(); } 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 ef632925..cc0b944d 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 @@ -111,6 +111,15 @@ public String generateContent(FcmPushContentDto fcmPushContentDto) { + builder.toString(); } }, + + JOIN_ROOM(NotificationCategory.COZY_HOME) { + @Override + public String generateContent(FcmPushContentDto fcmPushContentDto) { + return getNicknameShowFormat(fcmPushContentDto.getRoom().getName()) + "에 " + + getNicknameShowFormat(fcmPushContentDto.getMember().getNickname()) + +"님이 뛰어들어왔어요!"; + } + } ; private NotificationCategory category; diff --git a/src/main/java/com/cozymate/cozymate_server/domain/room/service/RoomCommandService.java b/src/main/java/com/cozymate/cozymate_server/domain/room/service/RoomCommandService.java index 11b9bed4..f7e5e83a 100644 --- a/src/main/java/com/cozymate/cozymate_server/domain/room/service/RoomCommandService.java +++ b/src/main/java/com/cozymate/cozymate_server/domain/room/service/RoomCommandService.java @@ -1,5 +1,7 @@ package com.cozymate.cozymate_server.domain.room.service; +import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupRoomNameWithOutMeTargetDto; +import com.cozymate.cozymate_server.domain.fcm.dto.FcmPushTargetDto.GroupWithOutMeTargetDto; import com.cozymate.cozymate_server.domain.feed.Feed; import com.cozymate.cozymate_server.domain.feed.converter.FeedConverter; import com.cozymate.cozymate_server.domain.feed.repository.FeedRepository; @@ -11,6 +13,7 @@ import com.cozymate.cozymate_server.domain.mate.repository.MateRepository; import com.cozymate.cozymate_server.domain.member.Member; import com.cozymate.cozymate_server.domain.member.repository.MemberRepository; +import com.cozymate.cozymate_server.domain.notificationlog.enums.NotificationType; import com.cozymate.cozymate_server.domain.post.Post; import com.cozymate.cozymate_server.domain.post.repository.PostRepository; import com.cozymate.cozymate_server.domain.postcomment.PostCommentRepository; @@ -33,6 +36,7 @@ import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; @Service @@ -57,6 +61,7 @@ public class RoomCommandService { private final FriendRepository friendRepository; private final RoomQueryService roomQueryService; private final RoomLogCommandService roomLogCommandService; + private final ApplicationEventPublisher eventPublisher; public RoomCreateResponse createRoom(RoomCreateRequest request, Member member) { Member creator = memberRepository.findById(member.getId()) @@ -107,6 +112,20 @@ public void joinRoom(Long roomId, Long memberId) { room.isRoomFull(); roomRepository.save(room); + // Room의 Mate들을 찾아온다 + List findRoomMates = mateRepository.findByRoom(room); + + List memberList = findRoomMates.stream() + .map(Mate::getMember) + .filter(findMember -> !findMember.getId().equals(member.getId())) + .toList(); + + // 알림 내용에는 현재 코드 상 member의 이름이 담겨야하고, 현재 코드 상의 room의 이름도 담긴다 + // 알림을 받는 대상은 방에 있는 메이트들이다. + // 넘겨야 할 파라미터 = member, room, memberList(알림 받을 대상 멤버 리스트), NotificationType + eventPublisher.publishEvent(GroupRoomNameWithOutMeTargetDto.create(member, memberList, room, + NotificationType.JOIN_ROOM)); + } public void deleteRoom(Long roomId, Long memberId) { 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 8de20fcf..76069c93 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 @@ -5,6 +5,8 @@ import java.time.LocalDate; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface TodoRepository extends JpaRepository { @@ -14,10 +16,13 @@ public interface TodoRepository extends JpaRepository { Integer countAllByRoomIdAndMateIdAndTimePoint(Long roomId, Long mateId, LocalDate timePoint); - List findByTimePoint(LocalDate today); + @Query("select t from Todo t join fetch t.mate m join fetch m.member where t.timePoint = :today") + List findByTimePoint(@Param("today") LocalDate today); - List findByTimePointAndRoleIsNotNull(LocalDate today); + @Query("select t from Todo t join fetch t.mate m join fetch m.member where t.timePoint = :today and t.role is not null and t.completed is false") + List findByTimePointAndRoleIsNotNullCompletedFalse(@Param("today") LocalDate today); - boolean existsByMateAndTimePointAndCompletedFalse(Mate mate, LocalDate timePoint); + ListfindByTimePointAndRoleIsNotNull(LocalDate today); + boolean existsByMateAndTimePointAndCompletedFalse(Mate mate, LocalDate timePoint); } 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 91eba496..03c2ef63 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.GroupRoomNameWithOutMeTargetDto; 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; @@ -51,4 +52,9 @@ public void sendNotification(OneTargetDto oneTargetDto) { public void sendNotification(GroupWithOutMeTargetDto groupWithOutMeTargetDto) { fcmPushService.sendNotification(groupWithOutMeTargetDto); } + + @TransactionalEventListener + public void sendNotification(GroupRoomNameWithOutMeTargetDto groupRoomNameWithOutMeTargetDto) { + fcmPushService.sendNotification(groupRoomNameWithOutMeTargetDto); + } } \ No newline at end of file diff --git a/src/main/java/com/cozymate/cozymate_server/global/scheduler/NotificationScheduler.java b/src/main/java/com/cozymate/cozymate_server/global/scheduler/NotificationScheduler.java index 9523ab3c..af882ce4 100644 --- a/src/main/java/com/cozymate/cozymate_server/global/scheduler/NotificationScheduler.java +++ b/src/main/java/com/cozymate/cozymate_server/global/scheduler/NotificationScheduler.java @@ -25,10 +25,12 @@ import java.util.function.Function; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +@Slf4j @Component @RequiredArgsConstructor @Transactional @@ -42,7 +44,8 @@ public class NotificationScheduler { private final RoomRepository roomRepository; private final RoleRepository roleRepository; - @Scheduled(cron = "0 0 0 * * *") + // 매일 자정 반복 (해당하는 날 역할을 Todo에 추가) 작업 자정에 먼저하고 나서 시작하도록 30초로 설정 + @Scheduled(cron = "30 0 0 * * *") public void sendDailyNotification() { LocalDate today = LocalDate.now(); List todoList = todoRepository.findByTimePoint(today); @@ -62,10 +65,10 @@ public void sendDailyNotification() { }); } - @Scheduled(cron = "0 0 22 * * *") + @Scheduled(cron = "0 0 21 * * *") public void sendReminderRoleNotification() { LocalDate today = LocalDate.now(); - List todoList = todoRepository.findByTimePointAndRoleIsNotNull(today); + List todoList = todoRepository.findByTimePointAndRoleIsNotNullCompletedFalse(today); Map todoMap = todoList.stream() .filter(todo -> !todo.isCompleted()) @@ -92,7 +95,7 @@ public void addReminderRoleRoomLog() { @Scheduled(cron = "0 0 12 L * ?") public void sendMonthlyNotification() { - List mates = mateRepository.findAll(); + List mates = mateRepository.findFetchAll(); List memberList = mates.stream() .map(Mate::getMember)