Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@
import com.assu.server.domain.chat.repository.MessageRepository;
import com.assu.server.domain.chat.service.BlockService;
import com.assu.server.domain.chat.service.ChatService;
import com.assu.server.domain.common.enums.UserRole;
import com.assu.server.domain.member.entity.Member;
import com.assu.server.domain.member.repository.MemberRepository;
import com.assu.server.domain.notification.service.NotificationCommandService;
import com.assu.server.global.apiPayload.code.status.SuccessStatus;
import com.assu.server.global.util.PresenceTracker;
import com.assu.server.global.util.PrincipalDetails;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.handler.annotation.MessageMapping;
Expand All @@ -31,11 +28,11 @@
public class ChatController {
private final ChatService chatService;
private final SimpMessagingTemplate simpMessagingTemplate;
private final PresenceTracker presenceTracker;
private final MessageRepository messageRepository;
private final MemberRepository memberRepository;
// private final PresenceTracker presenceTracker;
// private final MessageRepository messageRepository;
// private final MemberRepository memberRepository;
private final BlockService blockService;
private final NotificationCommandService notificationCommandService;
// private final NotificationCommandService notificationCommandService;

@Operation(
summary = "채팅방을 생성하는 API",
Expand Down Expand Up @@ -71,49 +68,68 @@ public BaseResponse<List<com.assu.server.domain.chat.dto.ChatRoomListResultDTO>>
"- receiverId: Request Body, Long\n" +
"- message: Request Body, String\n"
)
@Transactional
@MessageMapping("/send")
public void handleMessage(@Payload ChatRequestDTO.ChatMessageRequestDTO request) {
// 먼저 접속 여부 확인 후 unreadCount 계산
boolean receiverInRoom = presenceTracker.isInRoom(request.getReceiverId(), request.getRoomId());
int unreadForSender = receiverInRoom ? 0 : 1;
request.setUnreadCountForSender(unreadForSender);

ChatResponseDTO.SendMessageResponseDTO saved = chatService.handleMessage(request);
simpMessagingTemplate.convertAndSend("/sub/chat/" + request.getRoomId(), saved);

if (!receiverInRoom) {
Long totalUnreadCount = messageRepository.countUnreadMessagesByRoomAndReceiver(
request.getRoomId(),
request.getReceiverId()
);

ChatRoomUpdateDTO updateDTO = ChatRoomUpdateDTO.builder()
.roomId(request.getRoomId())
.lastMessage(saved.message())
.lastMessageTime(saved.sentAt())
.unreadCount(totalUnreadCount)
.build();
// 1. 서비스 호출 (모든 비즈니스 로직 위임)
MessageHandlingResult result = chatService.handleMessage(request);

// 2. [항상 전송] 채팅방 메시지 전송
simpMessagingTemplate.convertAndSend("/sub/chat/" + request.getRoomId(), result.sendMessageResponseDTO());

// 3. [조건부 전송] 채팅방 목록 업데이트 전송
if (result.hasRoomUpdates()) {
simpMessagingTemplate.convertAndSendToUser(
request.getReceiverId().toString(),
result.receiverId().toString(),
"/queue/updates",
updateDTO
result.chatRoomUpdateDTO()
);
Member sender = memberRepository.findById(request.getSenderId()).orElse(null);
String senderName;
if (sender.getRole()== UserRole.ADMIN) {
senderName = sender.getAdminProfile().getName();
} else {
senderName = sender.getPartnerProfile().getName();
}

log.info(">>>>>>>>메시지 전송은 될걸");
notificationCommandService.sendChat(request.getReceiverId(), request.getRoomId(), senderName, request.getMessage());
log.info(">>>>>>>>알림이 가나");
}
}

// @Transactional
// @MessageMapping("/send")
// public void handleMessage(@Payload ChatRequestDTO.ChatMessageRequestDTO request) {
// // 먼저 접속 여부 확인 후 unreadCount 계산
// boolean receiverInRoom = presenceTracker.isInRoom(request.getReceiverId(), request.getRoomId());
// int unreadForSender = receiverInRoom ? 0 : 1;
// request.setUnreadCountForSender(unreadForSender);
//
// ChatResponseDTO.SendMessageResponseDTO saved = chatService.handleMessage(request);
// simpMessagingTemplate.convertAndSend("/sub/chat/" + request.getRoomId(), saved);
//
// if (!receiverInRoom) {
// Long totalUnreadCount = messageRepository.countUnreadMessagesByRoomAndReceiver(
// request.getRoomId(),
// request.getReceiverId()
// );
//
// ChatRoomUpdateDTO updateDTO = ChatRoomUpdateDTO.builder()
// .roomId(request.getRoomId())
// .lastMessage(saved.message())
// .lastMessageTime(saved.sentAt())
// .unreadCount(totalUnreadCount)
// .build();
//
// simpMessagingTemplate.convertAndSendToUser(
// request.getReceiverId().toString(),
// "/queue/updates",
// updateDTO
// );
// Member sender = memberRepository.findById(request.getSenderId()).orElse(null);
// String senderName;
// if (sender.getRole()== UserRole.ADMIN) {
// senderName = sender.getAdminProfile().getName();
// } else {
// senderName = sender.getPartnerProfile().getName();
// }
//
// log.info(">>>>>>>>메시지 전송은 될걸");
// notificationCommandService.sendChat(request.getReceiverId(), request.getRoomId(), senderName, request.getMessage());
// log.info(">>>>>>>>알림이 가나");
// }
// }

@Operation(
summary = "메시지 읽음 처리 API",
description = "# [v1.0 (2025-08-05)](https://clumsy-seeder-416.notion.site/2241197c19ed800eab45c35073761c97?v=2241197c19ed8134b64f000cc26c5d31&p=2241197c19ed81ffa771cb18ab157b54&pm=s) 메시지를 읽음처리합니다.\n"+
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.assu.server.domain.chat.dto;

public record MessageHandlingResult(
ChatResponseDTO.SendMessageResponseDTO sendMessageResponseDTO,
ChatRoomUpdateDTO chatRoomUpdateDTO,
Long receiverId
) {

// 정적 팩토리 메소드 1
public static MessageHandlingResult of(ChatResponseDTO.SendMessageResponseDTO sendMessageDTO) {
// record의 기본 생성자를 호출합니다.
return new MessageHandlingResult(sendMessageDTO, null, null);
}

// 정적 팩토리 메소드 2
public static MessageHandlingResult withUpdates(ChatResponseDTO.SendMessageResponseDTO sendMessageDTO, ChatRoomUpdateDTO updateDTO, Long receiverId) {
// record의 기본 생성자를 호출합니다.
return new MessageHandlingResult(sendMessageDTO, updateDTO, receiverId);
}

// 헬퍼(Helper) 메소드
public boolean hasRoomUpdates() {
// record는 'get' 접두사 없는 접근자(chatRoomUpdateDTO())를 사용합니다.
return chatRoomUpdateDTO != null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
import com.assu.server.domain.chat.dto.ChatRequestDTO;
import com.assu.server.domain.chat.dto.ChatResponseDTO;
import com.assu.server.domain.chat.dto.ChatRoomListResultDTO;
import com.assu.server.domain.chat.dto.MessageHandlingResult;

import java.util.List;

public interface ChatService {
List<ChatRoomListResultDTO> getChatRoomList(Long memberId);
ChatResponseDTO.CreateChatRoomResponseDTO createChatRoom(ChatRequestDTO.CreateChatRoomRequestDTO request, Long memberId);
ChatResponseDTO.SendMessageResponseDTO handleMessage(ChatRequestDTO.ChatMessageRequestDTO request);
// ChatResponseDTO.SendMessageResponseDTO handleMessage(ChatRequestDTO.ChatMessageRequestDTO request);
MessageHandlingResult handleMessage(ChatRequestDTO.ChatMessageRequestDTO request);
ChatResponseDTO.ReadMessageResponseDTO readMessage(Long roomId, Long memberId);
ChatResponseDTO.ChatHistoryResponseDTO readHistory(Long roomId, Long memberId);
ChatResponseDTO.LeaveChattingRoomResponseDTO leaveChattingRoom(Long roomId, Long memberId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
import com.assu.server.domain.admin.entity.Admin;
import com.assu.server.domain.admin.repository.AdminRepository;
import com.assu.server.domain.chat.converter.ChatConverter;
import com.assu.server.domain.chat.dto.ChatMessageDTO;
import com.assu.server.domain.chat.dto.ChatRequestDTO;
import com.assu.server.domain.chat.dto.ChatResponseDTO;
import com.assu.server.domain.chat.dto.ChatRoomListResultDTO;
import com.assu.server.domain.chat.dto.*;
import com.assu.server.domain.chat.entity.ChattingRoom;
import com.assu.server.domain.chat.entity.Message;
import com.assu.server.domain.chat.repository.ChatRepository;
import com.assu.server.domain.chat.repository.MessageRepository;
import com.assu.server.domain.common.enums.UserRole;
import com.assu.server.domain.member.entity.Member;
import com.assu.server.domain.common.enums.ActivationStatus;
import com.assu.server.domain.member.repository.MemberRepository;
Expand All @@ -21,6 +19,7 @@
import com.assu.server.domain.store.repository.StoreRepository;
import com.assu.server.global.apiPayload.code.status.ErrorStatus;
import com.assu.server.global.exception.DatabaseException;
import com.assu.server.global.util.PresenceTracker;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -42,6 +41,7 @@ public class ChatServiceImpl implements ChatService {
private final StoreRepository storeRepository;
private final SimpMessagingTemplate simpMessagingTemplate;
private final NotificationCommandService notificationCommandService;
private final PresenceTracker presenceTracker;


@Override
Expand Down Expand Up @@ -90,25 +90,84 @@ public ChatResponseDTO.CreateChatRoomResponseDTO createChatRoom(ChatRequestDTO.C
}
}

// @Override
// @Transactional
// public ChatResponseDTO.SendMessageResponseDTO handleMessage(ChatRequestDTO.ChatMessageRequestDTO request) {
// // 유효성 검사
// ChattingRoom room = chatRepository.findById(request.getRoomId())
// .orElseThrow(() -> new DatabaseException(ErrorStatus.NO_SUCH_ROOM));
// Member sender = memberRepository.findById(request.getSenderId())
// .orElseThrow(() -> new DatabaseException(ErrorStatus.NO_SUCH_MEMBER));
// Member receiver = memberRepository.findById(request.getReceiverId())
// .orElseThrow(() -> new DatabaseException(ErrorStatus.NO_SUCH_MEMBER));
//
// Message message = ChatConverter.toMessageEntity(request, room, sender, receiver);
// Message saved = messageRepository.saveAndFlush(message);
// log.info("saved message id={}, roomId={}, senderId={}, receiverId={}",
// saved.getId(), room.getId(), sender.getId(), receiver.getId());
//
// return ChatConverter.toSendMessageDTO(saved);
// }

// ChatService의 handleMessage 메서드 (수정)

@Override
@Transactional
public ChatResponseDTO.SendMessageResponseDTO handleMessage(ChatRequestDTO.ChatMessageRequestDTO request) {
// 유효성 검사
public MessageHandlingResult handleMessage(ChatRequestDTO.ChatMessageRequestDTO request) {
// 1. 유효성 검사 (기존 로직)
ChattingRoom room = chatRepository.findById(request.getRoomId())
.orElseThrow(() -> new DatabaseException(ErrorStatus.NO_SUCH_ROOM));
Member sender = memberRepository.findById(request.getSenderId())
.orElseThrow(() -> new DatabaseException(ErrorStatus.NO_SUCH_MEMBER));
Member receiver = memberRepository.findById(request.getReceiverId())
.orElseThrow(() -> new DatabaseException(ErrorStatus.NO_SUCH_MEMBER));

// 2. 컨트롤러에서 가져온 비즈니스 로직 (접속 확인)
boolean receiverInRoom = presenceTracker.isInRoom(request.getReceiverId(), request.getRoomId());
int unreadForSender = receiverInRoom ? 0 : 1;
request.setUnreadCountForSender(unreadForSender);

// 3. 메시지 저장 (기존 로직)
Message message = ChatConverter.toMessageEntity(request, room, sender, receiver);
Message saved = messageRepository.saveAndFlush(message);
log.info("saved message id={}, roomId={}, senderId={}, receiverId={}",
saved.getId(), room.getId(), sender.getId(), receiver.getId());

// boolean exists = messageRepository.existsById(saved.getId());
// log.info("Saved? {}", exists); // true 아니면 트랜잭션/DB 문제
return ChatConverter.toSendMessageDTO(saved);
ChatResponseDTO.SendMessageResponseDTO savedDTO = ChatConverter.toSendMessageDTO(saved);

// 4. 컨트롤러에서 가져온 비즈니스 로직 (수신자 부재 시)
if (!receiverInRoom) {
// 4-1. 안 읽은 수 계산
Long totalUnreadCount = messageRepository.countUnreadMessagesByRoomAndReceiver(
request.getRoomId(),
request.getReceiverId()
);

// 4-2. 채팅방 목록 업데이트 DTO 생성
ChatRoomUpdateDTO updateDTO = ChatRoomUpdateDTO.builder()
.roomId(request.getRoomId())
.lastMessage(savedDTO.message())
.lastMessageTime(savedDTO.sentAt())
.unreadCount(totalUnreadCount)
.build();

// 4-3. 발신자 이름 찾기 (기존 컨트롤러 로직)
String senderName;
if (sender.getRole() == UserRole.ADMIN) { // 이미 sender 객체가 있으므로 재활용
senderName = sender.getAdminProfile().getName();
} else {
senderName = sender.getPartnerProfile().getName();
}

// 4-4. 알림 전송
notificationCommandService.sendChat(request.getReceiverId(), request.getRoomId(), senderName, request.getMessage());

// 5. [업데이트 포함] 결과 반환
return MessageHandlingResult.withUpdates(savedDTO, updateDTO, request.getReceiverId());
}

// 5. [일반 메시지] 결과 반환
return MessageHandlingResult.of(savedDTO);
}


Expand Down