Skip to content

Commit 38d4233

Browse files
committed
✨Feat: 쪽지 알람 기능 구현
1 parent e36f591 commit 38d4233

5 files changed

Lines changed: 64 additions & 4 deletions

File tree

src/main/java/com/be/sportizebe/domain/chat/websocket/handler/ChatStompController.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@
55
import com.be.sportizebe.domain.chat.dto.request.ChatSendRequest;
66
import com.be.sportizebe.domain.chat.entity.ChatMessage;
77
import com.be.sportizebe.domain.chat.entity.ChatRoom;
8+
import com.be.sportizebe.domain.chat.entity.ChatRoomMember;
9+
import com.be.sportizebe.domain.chat.repository.ChatRoomMemberRepository;
810
import com.be.sportizebe.domain.chat.service.ChatMessageService;
911
import com.be.sportizebe.domain.chat.service.ChatRoomService;
12+
import com.be.sportizebe.domain.notification.service.NotificationService;
13+
import com.be.sportizebe.domain.user.entity.User;
14+
import com.be.sportizebe.domain.user.repository.UserRepository;
1015
import lombok.RequiredArgsConstructor;
1116
import org.springframework.messaging.handler.annotation.MessageMapping;
1217
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
@@ -17,9 +22,12 @@
1722
@RequiredArgsConstructor
1823
public class ChatStompController {
1924
private final ChatMessageService chatMessageService;
20-
private final ChatRoomService chatRoomService; // ✅ 추가
25+
private final ChatRoomService chatRoomService;
2126
private final SimpMessagingTemplate messagingTemplate;
2227
private final ChatSessionRegistry registry;
28+
private final NotificationService notificationService;
29+
private final ChatRoomMemberRepository chatRoomMemberRepository;
30+
private final UserRepository userRepository;
2331

2432
/**
2533
* 클라이언트가 SEND로 보낸 메세지를 받는 주소가 여기
@@ -45,6 +53,24 @@ public void send(ChatSendRequest req){
4553
"/sub/chat/rooms/" + req.getRoomId(), // 채팅방 단위 브로드캐스트
4654
ChatMessageResponse.from(saved)
4755
);
56+
57+
// 1:1 채팅방(쪽지)인 경우 상대방에게 알림 전송
58+
if (room.getChatRoomType() == ChatRoom.ChatRoomType.NOTE) {
59+
sendNoteNotification(saved, req.getSenderUserId());
60+
}
61+
}
62+
63+
/**
64+
* 쪽지 알림 전송 (발신자 제외한 상대방에게)
65+
*/
66+
private void sendNoteNotification(ChatMessage chatMessage, Long senderUserId) {
67+
chatRoomMemberRepository.findAllByRoom_IdAndLeftAtIsNull(chatMessage.getRoom().getId())
68+
.stream()
69+
.map(ChatRoomMember::getUserId)
70+
.filter(userId -> !userId.equals(senderUserId))
71+
.findFirst() // 첫번째 사람만 선택
72+
.flatMap(userRepository::findById)
73+
.ifPresent(receiver -> notificationService.createNoteNotification(chatMessage, receiver));
4874
}
4975

5076
@MessageMapping("/chat.join")

src/main/java/com/be/sportizebe/domain/notification/dto/response/NotificationResponse.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ public record NotificationResponse(
1818
@Schema(description = "게시글 ID") Long postId,
1919
@Schema(description = "게시글 제목") String postTitle,
2020
@Schema(description = "댓글 작성자 닉네임") String commenterNickname,
21-
@Schema(description = "관련 대상 ID (채팅 등)") Long targetId,
21+
@Schema(description = "채팅방 ID") Long chatRoomId,
22+
@Schema(description = "쪽지 발신자 닉네임") String senderNickname,
23+
@Schema(description = "관련 대상 ID") Long targetId,
2224
@Schema(description = "알림 생성 일시") LocalDateTime createdAt
2325
) {
2426
public static NotificationResponse from(Notification notification) {
2527
var joinRequest = notification.getJoinClubRequest();
2628
var comment = notification.getComment();
29+
var chatMessage = notification.getChatMessage();
2730

2831
return NotificationResponse.builder()
2932
.id(notification.getId())
@@ -35,6 +38,8 @@ public static NotificationResponse from(Notification notification) {
3538
.postId(comment != null ? comment.getPost().getId() : null)
3639
.postTitle(comment != null ? comment.getPost().getTitle() : null)
3740
.commenterNickname(comment != null ? comment.getUser().getNickname() : null)
41+
.chatRoomId(chatMessage != null ? chatMessage.getRoom().getId() : null)
42+
.senderNickname(chatMessage != null ? chatMessage.getSenderNickname() : null)
3843
.targetId(notification.getTargetId())
3944
.createdAt(notification.getCreatedAt())
4045
.build();

src/main/java/com/be/sportizebe/domain/notification/entity/Notification.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.be.sportizebe.domain.notification.entity;
22

3+
import com.be.sportizebe.domain.chat.entity.ChatMessage;
34
import com.be.sportizebe.domain.comment.entity.Comment;
45
import com.be.sportizebe.domain.user.entity.User;
56
import com.be.sportizebe.global.common.BaseTimeEntity;
@@ -26,7 +27,8 @@ public enum NotificationType {
2627
JOIN_REJECTED, // 가입 거절 (신청자에게)
2728
CHAT, // 새 채팅 메시지
2829
COMMENT, // 새 댓글 (게시글 작성자에게)
29-
REPLY // 대댓글 (부모 댓글 작성자에게)
30+
REPLY, // 대댓글 (부모 댓글 작성자에게)
31+
NOTE // 쪽지 (수신자에게)
3032
}
3133

3234
@Id
@@ -55,8 +57,12 @@ public enum NotificationType {
5557
@JoinColumn(name = "comment_id")
5658
private Comment comment;
5759

60+
// 쪽지 알림용
61+
@ManyToOne(fetch = FetchType.LAZY)
62+
@JoinColumn(name = "chat_message_id")
63+
private ChatMessage chatMessage;
64+
5865
// 기타 알림용 - targetId로 다형성 처리
59-
// CHAT: chatRoomId 등
6066
private Long targetId;
6167

6268
public void markAsRead() {

src/main/java/com/be/sportizebe/domain/notification/service/NotificationService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.be.sportizebe.domain.notification.service;
22

3+
import com.be.sportizebe.domain.chat.entity.ChatMessage;
34
import com.be.sportizebe.domain.comment.entity.Comment;
45
import com.be.sportizebe.domain.notification.dto.response.NotificationResponse;
56
import com.be.sportizebe.domain.notification.entity.JoinClubRequest;
@@ -25,6 +26,9 @@ public interface NotificationService {
2526
// 대댓글 알림 생성 및 웹소켓 전송 (부모 댓글 작성자에게)
2627
void createReplyNotification(Comment reply);
2728

29+
// 쪽지 알림 생성 및 웹소켓 전송 (수신자에게)
30+
void createNoteNotification(ChatMessage chatMessage, User receiver);
31+
2832
// 사용자의 모든 알림 조회
2933
List<NotificationResponse> getNotifications(User user);
3034

src/main/java/com/be/sportizebe/domain/notification/service/NotificationServiceImpl.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.be.sportizebe.domain.notification.service;
22

3+
import com.be.sportizebe.domain.chat.entity.ChatMessage;
34
import com.be.sportizebe.domain.comment.entity.Comment;
45
import com.be.sportizebe.domain.notification.dto.response.NotificationResponse;
56
import com.be.sportizebe.domain.notification.entity.JoinClubRequest;
@@ -145,6 +146,24 @@ public void createReplyNotification(Comment reply) {
145146
sendNotificationToUser(parentAuthor.getId(), notification);
146147
}
147148

149+
@Override
150+
@Transactional
151+
public void createNoteNotification(ChatMessage chatMessage, User receiver) {
152+
// 자신에게 보낸 경우 알림 생성하지 않음
153+
if (receiver.getId() == chatMessage.getSenderUserId()) {
154+
return;
155+
}
156+
157+
Notification notification = Notification.builder()
158+
.receiver(receiver)
159+
.type(Notification.NotificationType.NOTE)
160+
.chatMessage(chatMessage)
161+
.build();
162+
163+
notificationRepository.save(notification);
164+
sendNotificationToUser(receiver.getId(), notification);
165+
}
166+
148167
/**
149168
* 웹소켓으로 알림 전송
150169
*/

0 commit comments

Comments
 (0)