diff --git a/src/main/java/inu/codin/codin/common/config/WebSocketConfig.java b/src/main/java/inu/codin/codin/common/config/WebSocketConfig.java index 006bf393..ed6205a6 100644 --- a/src/main/java/inu/codin/codin/common/config/WebSocketConfig.java +++ b/src/main/java/inu/codin/codin/common/config/WebSocketConfig.java @@ -39,6 +39,8 @@ public void configureMessageBroker(MessageBrokerRegistry registry) { //메세지를 브로커로 라우팅 registry.setApplicationDestinationPrefixes("/pub"); //클라이언트에서 보낸 메세지를 받을 prefix, controller의 @MessageMapping과 이어짐 + registry.setUserDestinationPrefix("/user"); + //convertAndSendToUser 사용할 prefix } @Override diff --git a/src/main/java/inu/codin/codin/common/stomp/StompMessageProcessor.java b/src/main/java/inu/codin/codin/common/stomp/StompMessageProcessor.java index 1b462ebf..7432f34d 100644 --- a/src/main/java/inu/codin/codin/common/stomp/StompMessageProcessor.java +++ b/src/main/java/inu/codin/codin/common/stomp/StompMessageProcessor.java @@ -30,19 +30,24 @@ public void handleMessage(StompHeaderAccessor headerAccessor){ throw new MessageDeliveryException(HttpStatus.BAD_REQUEST.toString()); } - switch (headerAccessor.getCommand()){ - case CONNECT -> { - stompMessageService.connectSession(headerAccessor); - } - case SUBSCRIBE -> { - stompMessageService.enterToChatRoom(headerAccessor); - } - case UNSUBSCRIBE -> { - stompMessageService.exitToChatRoom(headerAccessor); - } - case DISCONNECT -> { - stompMessageService.exitToChatRoom(headerAccessor); - stompMessageService.disconnectSession(headerAccessor); + /* + 채팅방 구독, 구독취소에 대한 destination만 처리 + */ + if (headerAccessor.getDestination()!=null && headerAccessor.getDestination().matches("/queue(/unread)?/[^/]+")) { + switch (headerAccessor.getCommand()) { + case CONNECT -> { + stompMessageService.connectSession(headerAccessor); + } + case SUBSCRIBE -> { + stompMessageService.enterToChatRoom(headerAccessor); + } + case UNSUBSCRIBE -> { + stompMessageService.exitToChatRoom(headerAccessor); + } + case DISCONNECT -> { + stompMessageService.exitToChatRoom(headerAccessor); + stompMessageService.disconnectSession(headerAccessor); + } } } } diff --git a/src/main/java/inu/codin/codin/common/stomp/StompMessageService.java b/src/main/java/inu/codin/codin/common/stomp/StompMessageService.java index 79d78d03..2111d537 100644 --- a/src/main/java/inu/codin/codin/common/stomp/StompMessageService.java +++ b/src/main/java/inu/codin/codin/common/stomp/StompMessageService.java @@ -77,7 +77,7 @@ private Result getResult(StompHeaderAccessor headerAccessor) { log.info(headerAccessor.toString()); String chatroomId; if (Objects.equals(headerAccessor.getCommand(), StompCommand.DISCONNECT)){ //UNSCRIBE 하지 않은 상태에서 DISCONNECT라면 UNSCRIBE도 같이 - if (!sessionStore.get(headerAccessor.getSessionId()).isEmpty()) { + if (sessionStore.containsKey(headerAccessor.getSessionId())) { chatroomId = sessionStore.get(headerAccessor.getSessionId()); sessionStore.remove(headerAccessor.getSessionId()); } else return null; diff --git a/src/main/java/inu/codin/codin/domain/chat/chatroom/dto/ChatRoomListResponseDto.java b/src/main/java/inu/codin/codin/domain/chat/chatroom/dto/ChatRoomListResponseDto.java index ae731165..ebdcb985 100644 --- a/src/main/java/inu/codin/codin/domain/chat/chatroom/dto/ChatRoomListResponseDto.java +++ b/src/main/java/inu/codin/codin/domain/chat/chatroom/dto/ChatRoomListResponseDto.java @@ -47,7 +47,7 @@ public static ChatRoomListResponseDto of(ChatRoom chatRoom, ObjectId userId) { .chatRoomId(chatRoom.get_id().toString()) .roomName(chatRoom.getRoomName()) .lastMessage(chatRoom.getLastMessage()==null ? null : chatRoom.getLastMessage()) - .currentMessageDate(chatRoom.getUpdatedAt()==null ? null : chatRoom.getUpdatedAt()) + .currentMessageDate(chatRoom.getCurrentMessageDate()==null ? null : chatRoom.getCurrentMessageDate()) .unread(chatRoom.getParticipants().getInfo().get(userId).getUnreadMessage()) .build(); } diff --git a/src/main/java/inu/codin/codin/domain/chat/chatroom/entity/ChatRoom.java b/src/main/java/inu/codin/codin/domain/chat/chatroom/entity/ChatRoom.java index 8eb503a6..e7767a05 100644 --- a/src/main/java/inu/codin/codin/domain/chat/chatroom/entity/ChatRoom.java +++ b/src/main/java/inu/codin/codin/domain/chat/chatroom/entity/ChatRoom.java @@ -11,6 +11,8 @@ import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; +import java.time.LocalDateTime; + @Document(collection = "chatroom") @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @@ -30,13 +32,16 @@ public class ChatRoom extends BaseTimeEntity { private String lastMessage; + private LocalDateTime currentMessageDate; + @Builder - public ChatRoom(String roomName, ObjectId referenceId, Participants participants, String lastMessage) { + public ChatRoom(String roomName, ObjectId referenceId, Participants participants, String lastMessage, LocalDateTime currentMessageDate) { this.roomName = roomName; this.referenceId = referenceId; this.participants = participants; this.lastMessage = lastMessage; + this.currentMessageDate = currentMessageDate; } public static ChatRoom of(ChatRoomCreateRequestDto chatRoomCreateRequestDto, ObjectId senderId){ @@ -47,10 +52,12 @@ public static ChatRoom of(ChatRoomCreateRequestDto chatRoomCreateRequestDto, Obj .roomName(chatRoomCreateRequestDto.getRoomName()) .referenceId(new ObjectId(chatRoomCreateRequestDto.getReferenceId())) .participants(participants) + .currentMessageDate(LocalDateTime.now()) .build(); } public void updateLastMessage(String message){ this.lastMessage = message; + this.currentMessageDate = LocalDateTime.now(); } } diff --git a/src/main/java/inu/codin/codin/domain/chat/chatroom/service/ChatRoomService.java b/src/main/java/inu/codin/codin/domain/chat/chatroom/service/ChatRoomService.java index 75528c7b..0c8dc3af 100644 --- a/src/main/java/inu/codin/codin/domain/chat/chatroom/service/ChatRoomService.java +++ b/src/main/java/inu/codin/codin/domain/chat/chatroom/service/ChatRoomService.java @@ -1,6 +1,5 @@ package inu.codin.codin.domain.chat.chatroom.service; -import inu.codin.codin.common.dto.BaseTimeEntity; import inu.codin.codin.common.exception.NotFoundException; import inu.codin.codin.common.security.util.SecurityUtils; import inu.codin.codin.domain.block.service.BlockService; @@ -84,7 +83,7 @@ public List getAllChatRoomByUser() { return chatRooms.stream() .filter(chatRoom -> chatRoom.getParticipants().getInfo().keySet().stream() .noneMatch(blockedUsersId::contains)) - .sorted(Comparator.comparing(BaseTimeEntity::getUpdatedAt,Comparator.reverseOrder())) + .sorted(Comparator.comparing(ChatRoom::getCurrentMessageDate,Comparator.nullsLast(Comparator.reverseOrder()))) .map(chatRoom -> ChatRoomListResponseDto.of(chatRoom, userId)).toList(); } diff --git a/src/main/java/inu/codin/codin/domain/chat/chatting/controller/ChattingController.java b/src/main/java/inu/codin/codin/domain/chat/chatting/controller/ChattingController.java index 89ffd05d..53f45c62 100644 --- a/src/main/java/inu/codin/codin/domain/chat/chatting/controller/ChattingController.java +++ b/src/main/java/inu/codin/codin/domain/chat/chatting/controller/ChattingController.java @@ -67,4 +67,9 @@ public String chatImageHtml(){ return "chatImage"; } + @GetMapping("/chat/room") + public String chatroomHtml(){ + return "chatroom"; + } + } diff --git a/src/main/java/inu/codin/codin/domain/chat/chatting/dto/event/ChattingNotificationEvent.java b/src/main/java/inu/codin/codin/domain/chat/chatting/dto/event/ChattingNotificationEvent.java new file mode 100644 index 00000000..ddbff691 --- /dev/null +++ b/src/main/java/inu/codin/codin/domain/chat/chatting/dto/event/ChattingNotificationEvent.java @@ -0,0 +1,19 @@ +package inu.codin.codin.domain.chat.chatting.dto.event; + +import inu.codin.codin.domain.chat.chatroom.entity.ChatRoom; +import lombok.Getter; +import org.bson.types.ObjectId; +import org.springframework.context.ApplicationEvent; +@Getter +public class ChattingNotificationEvent extends ApplicationEvent { + + private final ChatRoom chatRoom; + private final ObjectId userId; + + + public ChattingNotificationEvent(Object source, ObjectId userId, ChatRoom chatRoom) { + super(source); + this.userId = userId; + this.chatRoom = chatRoom; + } +} diff --git a/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingEventListener.java b/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingEventListener.java index 7cc0b0eb..d83bcf17 100644 --- a/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingEventListener.java +++ b/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingEventListener.java @@ -2,20 +2,24 @@ import inu.codin.codin.common.exception.NotFoundException; import inu.codin.codin.domain.chat.chatroom.entity.ChatRoom; +import inu.codin.codin.domain.chat.chatroom.entity.ParticipantInfo; import inu.codin.codin.domain.chat.chatroom.repository.ChatRoomRepository; import inu.codin.codin.domain.chat.chatting.dto.event.ChattingArrivedEvent; +import inu.codin.codin.domain.chat.chatting.dto.event.ChattingNotificationEvent; import inu.codin.codin.domain.chat.chatting.dto.event.UpdateUnreadCountEvent; import inu.codin.codin.domain.chat.chatting.entity.Chatting; import inu.codin.codin.domain.chat.chatting.repository.ChattingRepository; +import inu.codin.codin.domain.notification.service.NotificationService; +import inu.codin.codin.domain.user.entity.UserEntity; +import inu.codin.codin.domain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; +import org.bson.types.ObjectId; import org.springframework.context.event.EventListener; import org.springframework.messaging.simp.SimpMessageSendingOperations; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; @Service @RequiredArgsConstructor @@ -23,28 +27,71 @@ public class ChattingEventListener { private final ChatRoomRepository chatRoomRepository; private final ChattingRepository chattingRepository; + private final UserRepository userRepository; private final SimpMessageSendingOperations template; + private final NotificationService notificationService; + /* + 채팅을 발신했을 경우, + 1. 상대방이 접속한 상태가 아니라면 상대방의 unread 값 +1 + 2. 채팅방의 마지막 메세지 업데이트 + 3. /queue/chatroom/unread 를 통해 상대방의 채팅방 목록 실시간 업데이트 + */ @Async @EventListener public void handleChattingArrivedEvent(ChattingArrivedEvent event){ Chatting chatting = event.getChatting(); ChatRoom chatRoom = chatRoomRepository.findById(chatting.getChatRoomId()) .orElseThrow(() -> new NotFoundException("채팅방을 찾을 수 없습니다. ID: "+ chatting.getChatRoomId())); - chatRoom.getParticipants().getInfo().forEach( - (id, participantInfo) -> { - if (!participantInfo.isConnected()) { - participantInfo.plusUnread(); - } - } - ); - chatRoom.updateLastMessage(chatting.getContent()); + + updateUnread(event, chatRoom); chatRoomRepository.save(chatRoom); chattingRepository.save(chatting); } + private void updateUnread(ChattingArrivedEvent event, ChatRoom chatRoom) { + Map result = new HashMap<>(); + ObjectId receiverId = null; + for (Map.Entry entry : chatRoom.getParticipants().getInfo().entrySet()) { + ParticipantInfo participantInfo = entry.getValue(); + + if (!participantInfo.getUserId().equals(event.getChatting().getSenderId())) { + if (!participantInfo.isConnected()) { + receiverId = participantInfo.getUserId(); + participantInfo.plusUnread(); + result = getLastMessageAndUnread(event, participantInfo); + } + } + } + chatRoom.updateLastMessage(event.getChatting().getContent()); + if (receiverId!=null) { //받는 사람이 없다는 것은 채팅에 연결 중인 상태, 채팅방 업데이트할 필요 X + Optional user = userRepository.findByUserId(receiverId); + if (user.isPresent()) + template.convertAndSendToUser(user.get().getEmail(), "/queue/chatroom/unread", result); + } + } + + private static Map getLastMessageAndUnread(ChattingArrivedEvent event, ParticipantInfo participantInfo) { + return Map.of( + "chatRoomId", event.getChatting().get_id().toString(), + "lastMessage", event.getChatting().getContent(), + "unread", String.valueOf(participantInfo.getUnreadMessage()) + ); + } + + @Async + @EventListener + public void handleChattingNotificationEvent(ChattingNotificationEvent event){ + event.getChatRoom().getParticipants().getInfo().values().stream() + .filter(participantInfo -> !participantInfo.getUserId().equals(event.getUserId()) && participantInfo.isNotificationsEnabled() & !participantInfo.isConnected()) + .peek(participantInfo -> notificationService.sendNotificationMessageByChat(participantInfo.getUserId(), event.getChatRoom().get_id())); + } + /* + 유저가 채팅방 입장 시, 읽지 않은 채팅에 대하여 새로운 unread 값 송신 + 클라이언트 : chat_id 와 일치하는 채팅값의 unread 값 업데이트 + */ @EventListener public void updateUnreadCountEvent(UpdateUnreadCountEvent updateUnreadCountEvent){ List> result = new ArrayList<>(); diff --git a/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingService.java b/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingService.java index 3272d5c5..5c3af31a 100644 --- a/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingService.java +++ b/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingService.java @@ -7,6 +7,7 @@ import inu.codin.codin.domain.chat.chatroom.repository.ChatRoomRepository; import inu.codin.codin.domain.chat.chatroom.service.ChatRoomService; import inu.codin.codin.domain.chat.chatting.dto.event.ChattingArrivedEvent; +import inu.codin.codin.domain.chat.chatting.dto.event.ChattingNotificationEvent; import inu.codin.codin.domain.chat.chatting.dto.request.ChattingRequestDto; import inu.codin.codin.domain.chat.chatting.dto.response.ChattingAndUserIdResponseDto; import inu.codin.codin.domain.chat.chatting.dto.response.ChattingResponseDto; @@ -62,16 +63,10 @@ public ChattingResponseDto sendMessage(String id, ChattingRequestDto chattingReq .forEach(ParticipantInfo::remain); chatRoomRepository.save(chatRoom); - eventPublisher.publishEvent(new ChattingArrivedEvent(this, chatting)); //상대 유저가 접속하지 않은 상태라면 unread 개수 업데이트 및 마지막 대화 내용 업데이트 - -// //Receiver의 알림 체크 후, 메세지 전송 -// for (Participants participant : chatRoom.getParticipants()){ -// if (participant.getUserId() != userId && participant.isNotificationsEnabled()){ -// notificationService.sendNotificationMessageByChat(participant.getUserId(), chattingRequestDto, chatRoom); -// } -// } - + eventPublisher.publishEvent(new ChattingArrivedEvent(this, chatting)); + //알림 보내기 + eventPublisher.publishEvent(new ChattingNotificationEvent(this, userId, chatRoom)); return ChattingResponseDto.of(chatting); } diff --git a/src/main/java/inu/codin/codin/domain/notification/entity/NotificationEntity.java b/src/main/java/inu/codin/codin/domain/notification/entity/NotificationEntity.java index 39edcb64..66d4d8aa 100644 --- a/src/main/java/inu/codin/codin/domain/notification/entity/NotificationEntity.java +++ b/src/main/java/inu/codin/codin/domain/notification/entity/NotificationEntity.java @@ -23,8 +23,7 @@ public class NotificationEntity extends BaseTimeEntity { @Id @NotBlank private ObjectId id; - @DBRef(lazy = true) - private UserEntity user; + private ObjectId userId; private ObjectId targetId; @@ -43,8 +42,8 @@ public class NotificationEntity extends BaseTimeEntity { private String priority; @Builder - public NotificationEntity(UserEntity user, ObjectId targetId, String title, String message, String type, String priority) { - this.user = user; + public NotificationEntity(ObjectId userId, ObjectId targetId, String title, String message, String type, String priority) { + this.userId = userId; this.targetId = targetId; this.title = title; this.message = message; diff --git a/src/main/java/inu/codin/codin/domain/notification/repository/NotificationRepository.java b/src/main/java/inu/codin/codin/domain/notification/repository/NotificationRepository.java index 8256a6d6..075b06cb 100644 --- a/src/main/java/inu/codin/codin/domain/notification/repository/NotificationRepository.java +++ b/src/main/java/inu/codin/codin/domain/notification/repository/NotificationRepository.java @@ -1,7 +1,6 @@ package inu.codin.codin.domain.notification.repository; import inu.codin.codin.domain.notification.entity.NotificationEntity; -import inu.codin.codin.domain.user.entity.UserEntity; import org.bson.types.ObjectId; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.Query; @@ -11,8 +10,8 @@ @Repository public interface NotificationRepository extends MongoRepository { - @Query("{ 'user': ?0, 'isRead': false, deletedAt: null }") - long countUnreadNotificationsByUser(UserEntity user); + @Query("{ 'userId': ?0, 'isRead': false, deletedAt: null }") + long countUnreadNotificationsByUserId(ObjectId userId); - List findAllByUser(UserEntity user); + List findAllByUserId(ObjectId userId); } \ No newline at end of file diff --git a/src/main/java/inu/codin/codin/domain/notification/service/NotificationService.java b/src/main/java/inu/codin/codin/domain/notification/service/NotificationService.java index 396385bd..07e05bd1 100644 --- a/src/main/java/inu/codin/codin/domain/notification/service/NotificationService.java +++ b/src/main/java/inu/codin/codin/domain/notification/service/NotificationService.java @@ -2,7 +2,6 @@ import inu.codin.codin.common.exception.NotFoundException; import inu.codin.codin.common.security.util.SecurityUtils; -import inu.codin.codin.domain.chat.chatroom.entity.ChatRoom; import inu.codin.codin.domain.like.entity.LikeType; import inu.codin.codin.domain.notification.dto.NotificationListResponseDto; import inu.codin.codin.domain.notification.entity.NotificationEntity; @@ -14,7 +13,6 @@ import inu.codin.codin.domain.post.entity.PostCategory; import inu.codin.codin.domain.post.entity.PostEntity; import inu.codin.codin.domain.post.repository.PostRepository; -import inu.codin.codin.domain.user.entity.UserEntity; import inu.codin.codin.domain.user.repository.UserRepository; import inu.codin.codin.infra.fcm.dto.FcmMessageTopicDto; import inu.codin.codin.infra.fcm.dto.FcmMessageUserDto; @@ -48,11 +46,11 @@ public class NotificationService { /** * 특정 유저의 읽지 않은 알림 개수를 반환 - * @param user 알림 수신자 + * @param userId 알림 수신자의 _id * @return 읽지 않은 알림 개수 */ - public long getUnreadNotificationCount(UserEntity user) { - return notificationRepository.countUnreadNotificationsByUser(user); + public long getUnreadNotificationCount(ObjectId userId) { + return notificationRepository.countUnreadNotificationsByUserId(userId); } /** @@ -60,11 +58,11 @@ public long getUnreadNotificationCount(UserEntity user) { * @param title 메시지 제목 * @param body 메시지 내용 * @param data 알림 대상의 _id - * @param user 메시지를 받을 사용자 + * @param userId 메시지를 받을 사용자의 _id */ - public void sendFcmMessageToUser(String title, String body, Map data, UserEntity user) { + public void sendFcmMessageToUser(String title, String body, Map data, ObjectId userId) { FcmMessageUserDto msgDto = FcmMessageUserDto.builder() - .user(user) + .userId(userId) .title(title) .body(body) .data(data) @@ -109,7 +107,7 @@ public void sendFcmMessageToTopic(String title, String body, Map // 알림 로그를 저장하는 로직 (특정 사용자 대상) private void saveNotificationLog(FcmMessageUserDto msgDto, Map data) { NotificationEntity notificationEntity = NotificationEntity.builder() - .user(msgDto.getUser()) + .userId(msgDto.getUserId()) .title(msgDto.getTitle()) .message(msgDto.getBody()) .targetId(new ObjectId(data.get("id"))) @@ -131,21 +129,21 @@ private void saveNotificationLog(FcmMessageTopicDto msgDto) { } public void sendNotificationMessageByComment(PostCategory postCategory, ObjectId userId, String postId, String content) { - UserEntity user = userRepository.findById(userId) + userRepository.findById(userId) .orElseThrow(() -> new NotFoundException("유저를 찾을 수 없습니다.")); Map post = new HashMap<>(); post.put("id", postId); String title = postCategory.getDescription().split("_")[0]; - sendFcmMessageToUser(title, NOTI_COMMENT+content, post, user); + sendFcmMessageToUser(title, NOTI_COMMENT+content, post, userId); } public void sendNotificationMessageByReply(PostCategory postCategory, ObjectId userId, String postId, String content) { - UserEntity user = userRepository.findById(userId) + userRepository.findById(userId) .orElseThrow(() -> new NotFoundException("유저를 찾을 수 없습니다.")); Map post = new HashMap<>(); post.put("id", postId); String title = postCategory.getDescription().split("_")[0]; - sendFcmMessageToUser(title, NOTI_REPLY+content, post, user); + sendFcmMessageToUser(title, NOTI_REPLY+content, post, userId); } public void sendNotificationMessageByLike(LikeType likeType, ObjectId id) { @@ -153,11 +151,11 @@ public void sendNotificationMessageByLike(LikeType likeType, ObjectId id) { case POST -> { PostEntity postEntity = postRepository.findByIdAndNotDeleted(id) .orElseThrow(() -> new NotFoundException("게시글을 찾을 수 없습니다.")); - UserEntity user = userRepository.findById(postEntity.getUserId()) + userRepository.findById(postEntity.getUserId()) .orElseThrow(() -> new NotFoundException("유저 정보를 찾을 수 없습니다.")); Map post = new HashMap<>(); post.put("id", postEntity.get_id().toString()); - sendFcmMessageToUser(NOTI_LIKE, "내 게시글 보러 가기", post, user); + sendFcmMessageToUser(NOTI_LIKE, "내 게시글 보러 가기", post, postEntity.getUserId()); } case REPLY -> { ReplyCommentEntity replyCommentEntity = replyCommentRepository.findByIdAndNotDeleted(id) @@ -166,32 +164,32 @@ public void sendNotificationMessageByLike(LikeType likeType, ObjectId id) { .orElseThrow(() -> new NotFoundException("댓글을 찾을 수 없습니다.")); PostEntity postEntity = postRepository.findByIdAndNotDeleted(commentEntity.getPostId()) .orElseThrow(() -> new NotFoundException("게시글을 찾을 수 없습니다.")); - UserEntity user = userRepository.findById(replyCommentEntity.getUserId()) + userRepository.findById(replyCommentEntity.getUserId()) .orElseThrow(() -> new NotFoundException("유저 정보를 찾을 수 없습니다.")); Map post = new HashMap<>(); post.put("id", postEntity.get_id().toString()); - sendFcmMessageToUser(NOTI_LIKE, "내 답글 보러 가기", post, user); + sendFcmMessageToUser(NOTI_LIKE, "내 답글 보러 가기", post, replyCommentEntity.getUserId()); } case COMMENT -> { CommentEntity commentEntity = commentRepository.findByIdAndNotDeleted(id) .orElseThrow(() -> new NotFoundException("댓글을 찾을 수 없습니다.")); PostEntity postEntity = postRepository.findByIdAndNotDeleted(commentEntity.getPostId()) .orElseThrow(() -> new NotFoundException("게시글을 찾을 수 없습니다.")); - UserEntity user = userRepository.findById(commentEntity.getUserId()) + userRepository.findById(commentEntity.getUserId()) .orElseThrow(() -> new NotFoundException("유저 정보를 찾을 수 없습니다.")); Map post = new HashMap<>(); post.put("id", postEntity.get_id().toString()); - sendFcmMessageToUser(NOTI_LIKE, "내 댓글 보러 가기", post, user); + sendFcmMessageToUser(NOTI_LIKE, "내 댓글 보러 가기", post, commentEntity.getUserId()); } } } public void sendNotificationMessageByChat(ObjectId userId, ObjectId chatRoomId) { - UserEntity user = userRepository.findById(userId) + userRepository.findById(userId) .orElseThrow(() -> new NotFoundException("유저 정보를 찾을 수 없습니다.")); Map chat = new HashMap<>(); chat.put("id", chatRoomId.toString()); - sendFcmMessageToUser("익명 채팅방", NOTI_CHAT, chat, user); + sendFcmMessageToUser("익명 채팅방", NOTI_CHAT, chat, userId); } public void readNotification(String notificationId){ @@ -204,9 +202,9 @@ public void readNotification(String notificationId){ public List getNotification() { //todo 유저에게 맞는 토픽 알림들도 반환 ObjectId userId = SecurityUtils.getCurrentUserId(); - UserEntity user = userRepository.findById(userId) + userRepository.findById(userId) .orElseThrow(() -> new NotFoundException("유저를 찾을 수 없습니다")); - List notifications = notificationRepository.findAllByUser(user); + List notifications = notificationRepository.findAllByUserId(userId); return notifications.stream() .map(NotificationListResponseDto::of) .toList(); diff --git a/src/main/java/inu/codin/codin/infra/fcm/dto/FcmMessageUserDto.java b/src/main/java/inu/codin/codin/infra/fcm/dto/FcmMessageUserDto.java index 49bab9c8..f6f7a106 100644 --- a/src/main/java/inu/codin/codin/infra/fcm/dto/FcmMessageUserDto.java +++ b/src/main/java/inu/codin/codin/infra/fcm/dto/FcmMessageUserDto.java @@ -3,6 +3,7 @@ import inu.codin.codin.domain.user.entity.UserEntity; import lombok.Builder; import lombok.Data; +import org.bson.types.ObjectId; import java.util.Map; @@ -13,15 +14,15 @@ @Data public class FcmMessageUserDto { - private UserEntity user; + private ObjectId userId; private String title; private String body; private String imageUrl; private Map data; @Builder - public FcmMessageUserDto(UserEntity user, String title, String body, String imageUrl, Map data) { - this.user = user; + public FcmMessageUserDto(ObjectId userId, String title, String body, String imageUrl, Map data) { + this.userId = userId; this.title = title; this.body = body; this.imageUrl = imageUrl; diff --git a/src/main/java/inu/codin/codin/infra/fcm/entity/FcmTokenEntity.java b/src/main/java/inu/codin/codin/infra/fcm/entity/FcmTokenEntity.java index f9d1ba7a..733765b0 100644 --- a/src/main/java/inu/codin/codin/infra/fcm/entity/FcmTokenEntity.java +++ b/src/main/java/inu/codin/codin/infra/fcm/entity/FcmTokenEntity.java @@ -18,18 +18,17 @@ public class FcmTokenEntity extends BaseTimeEntity { @Id - private ObjectId id; + private ObjectId _id; - @DBRef - private UserEntity user; + private ObjectId userId; private List fcmTokenList; private String deviceType; @Builder - public FcmTokenEntity(UserEntity user, List fcmTokenList, String deviceType) { - this.user = user; + public FcmTokenEntity(ObjectId userId, List fcmTokenList, String deviceType) { + this.userId = userId; this.fcmTokenList = fcmTokenList; this.deviceType = deviceType; } diff --git a/src/main/java/inu/codin/codin/infra/fcm/repository/FcmTokenRepository.java b/src/main/java/inu/codin/codin/infra/fcm/repository/FcmTokenRepository.java index ec11c7b2..ee3b015b 100644 --- a/src/main/java/inu/codin/codin/infra/fcm/repository/FcmTokenRepository.java +++ b/src/main/java/inu/codin/codin/infra/fcm/repository/FcmTokenRepository.java @@ -1,6 +1,5 @@ package inu.codin.codin.infra.fcm.repository; -import inu.codin.codin.domain.user.entity.UserEntity; import inu.codin.codin.infra.fcm.entity.FcmTokenEntity; import org.bson.types.ObjectId; import org.springframework.data.mongodb.repository.MongoRepository; @@ -11,6 +10,6 @@ @Repository public interface FcmTokenRepository extends MongoRepository { - @Query("{ 'user': ?0, deletedAt: null }") - Optional findByUser(UserEntity user); + @Query("{ 'userId': ?0, deletedAt: null }") + Optional findByUserId(ObjectId user); } diff --git a/src/main/java/inu/codin/codin/infra/fcm/service/FcmService.java b/src/main/java/inu/codin/codin/infra/fcm/service/FcmService.java index 8a139575..2ed53662 100644 --- a/src/main/java/inu/codin/codin/infra/fcm/service/FcmService.java +++ b/src/main/java/inu/codin/codin/infra/fcm/service/FcmService.java @@ -36,8 +36,7 @@ public class FcmService { public void saveFcmToken(@Valid FcmTokenRequest fcmTokenRequest) { // 유저의 FCM 토큰이 존재하는지 확인 ObjectId userId = SecurityUtils.getCurrentUserId(); - UserEntity user = getUserEntityFromUserId(userId); - Optional fcmToken = fcmTokenRepository.findByUser(user); + Optional fcmToken = fcmTokenRepository.findByUserId(userId); if (fcmToken.isPresent()) { // 이미 존재하는 유저라면 토큰 추가 FcmTokenEntity fcmTokenEntity = fcmToken.get(); @@ -46,7 +45,7 @@ public void saveFcmToken(@Valid FcmTokenRequest fcmTokenRequest) { } else { // 존재하지 않는 FCM 토큰이라면 저장 FcmTokenEntity newFcmTokenEntity = FcmTokenEntity.builder() - .user(user) + .userId(userId) .fcmTokenList(List.of(fcmTokenRequest.getFcmToken())) .deviceType(fcmTokenRequest.getDeviceType()) .build(); @@ -60,11 +59,12 @@ public void saveFcmToken(@Valid FcmTokenRequest fcmTokenRequest) { */ public void sendFcmMessage(FcmMessageUserDto fcmMessageUserDto) { // 유저의 알림 설정 조회 - UserEntity user = fcmMessageUserDto.getUser(); - NotificationPreference userPreference = fcmMessageUserDto.getUser().getNotificationPreference(); + ObjectId userId = fcmMessageUserDto.getUserId(); + UserEntity user = getUserEntityFromUserId(userId); + NotificationPreference userPreference = user.getNotificationPreference(); // 유저의 FCM 토큰 조회 - FcmTokenEntity fcmTokenEntity = fcmTokenRepository.findByUser(user).orElseThrow(() + FcmTokenEntity fcmTokenEntity = fcmTokenRepository.findByUserId(userId).orElseThrow(() -> new FcmTokenNotFoundException("유저에게 FCM 토큰이 존재하지 않습니다.")); // 알림 설정에 따라 알림 전송 @@ -130,26 +130,21 @@ public void sendFcmMessageByTopic(FcmMessageTopicDto fcmMessageTopicDto) { private void handleFirebaseMessagingException(FirebaseMessagingException e, FcmTokenEntity fcmTokenEntity, String fcmToken) { MessagingErrorCode errorCode = e.getMessagingErrorCode(); switch (errorCode) { - case INVALID_ARGUMENT: // FCM 토큰이 유효하지 않을 때 - log.error("Invalid argument error for token: {}", fcmToken); - break; - case UNREGISTERED: // FCM 토큰이 등록되지 않았을 때 + case INVALID_ARGUMENT -> // FCM 토큰이 유효하지 않을 때 + log.error("Invalid argument error for token: {}", fcmToken); + case UNREGISTERED -> { // FCM 토큰이 등록되지 않았을 때 log.warn("Unregistered token: {}. Removing from database.", fcmToken); removeFcmToken(fcmTokenEntity, fcmToken); - break; - case QUOTA_EXCEEDED: // FCM 토큰의 전송량이 초과되었을 때 - log.error("Quota exceeded for token: {}", fcmToken); - // 에러관리 및 리포팅 기능 추가 - break; - case SENDER_ID_MISMATCH: // FCM 토큰의 발신자 ID가 일치하지 않을 때 - log.error("Sender ID mismatch for token: {}", fcmToken); - break; - case THIRD_PARTY_AUTH_ERROR: // FCM 토큰의 인증이 실패했을 때 - log.error("Third-party authentication error for token: {}", fcmToken); - break; - default: // 그 외의 에러 - log.error("Unknown error for token: {}", fcmToken); - break; + } + case QUOTA_EXCEEDED -> // FCM 토큰의 전송량이 초과되었을 때 + log.error("Quota exceeded for token: {}", fcmToken); + // 에러관리 및 리포팅 기능 추가 + case SENDER_ID_MISMATCH -> // FCM 토큰의 발신자 ID가 일치하지 않을 때 + log.error("Sender ID mismatch for token: {}", fcmToken); + case THIRD_PARTY_AUTH_ERROR -> // FCM 토큰의 인증이 실패했을 때 + log.error("Third-party authentication error for token: {}", fcmToken); + default -> // 그 외의 에러 + log.error("Unknown error for token: {}", fcmToken); } } @@ -165,8 +160,7 @@ private void removeFcmToken(FcmTokenEntity fcmTokenEntity, String fcmToken) { */ public void subscribeTopic(String topic) { ObjectId userId = SecurityUtils.getCurrentUserId(); - UserEntity user = getUserEntityFromUserId(userId); - FcmTokenEntity fcmTokenEntity = fcmTokenRepository.findByUser(user) + FcmTokenEntity fcmTokenEntity = fcmTokenRepository.findByUserId(userId) .orElseThrow(() -> new FcmTokenNotFoundException("유저의 FCM 토큰이 존재하지 않습니다.")); for (String token : fcmTokenEntity.getFcmTokenList()) { @@ -185,8 +179,7 @@ public void subscribeTopic(String topic) { */ public void unsubscribeTopic(String topic) { ObjectId userId = SecurityUtils.getCurrentUserId(); - UserEntity user = getUserEntityFromUserId(userId); - FcmTokenEntity fcmTokenEntity = fcmTokenRepository.findByUser(user) + FcmTokenEntity fcmTokenEntity = fcmTokenRepository.findByUserId(userId) .orElseThrow(() -> new FcmTokenNotFoundException("유저의 FCM 토큰이 존재하지 않습니다.")); for (String token : fcmTokenEntity.getFcmTokenList()) { diff --git a/src/main/resources b/src/main/resources index b8f8bd64..7c46ef20 160000 --- a/src/main/resources +++ b/src/main/resources @@ -1 +1 @@ -Subproject commit b8f8bd648f38a735948c41459547ec667dedad8f +Subproject commit 7c46ef207fa09405e7728b058642b151c0aa1261