From 66c302a2b6771f63607d283bd34e12578628aa3d Mon Sep 17 00:00:00 2001 From: byeunghoon Kang Date: Wed, 5 Feb 2025 23:00:04 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=ED=83=80=EC=9E=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/manchui/domain/entity/mongodb/ChatMessageType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/manchui/domain/entity/mongodb/ChatMessageType.java b/src/main/java/com/manchui/domain/entity/mongodb/ChatMessageType.java index dc18455..b8681af 100644 --- a/src/main/java/com/manchui/domain/entity/mongodb/ChatMessageType.java +++ b/src/main/java/com/manchui/domain/entity/mongodb/ChatMessageType.java @@ -2,5 +2,5 @@ public enum ChatMessageType { - ENTER, MESSAGE + ENTER, MESSAGE, QUITE, OPEN } From cd9c060aa01c79a448414246111efa93ef16e3ad Mon Sep 17 00:00:00 2001 From: byeunghoon Kang Date: Wed, 5 Feb 2025 23:10:05 +0900 Subject: [PATCH 2/7] =?UTF-8?q?=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=EC=8B=9C=20=EC=B1=84=ED=8C=85=EB=B0=A9=EC=97=90=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=20=EA=B0=9C=EC=84=A4=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=A0=80=EC=9E=A5=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/manchui/domain/dto/chat/ChatMessageRequest.java | 2 ++ .../com/manchui/domain/service/ChatMessageService.java | 5 +++++ .../com/manchui/domain/service/GatheringServiceImpl.java | 8 +++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/manchui/domain/dto/chat/ChatMessageRequest.java b/src/main/java/com/manchui/domain/dto/chat/ChatMessageRequest.java index 4a08fc4..699dcd2 100644 --- a/src/main/java/com/manchui/domain/dto/chat/ChatMessageRequest.java +++ b/src/main/java/com/manchui/domain/dto/chat/ChatMessageRequest.java @@ -1,8 +1,10 @@ package com.manchui.domain.dto.chat; +import lombok.AllArgsConstructor; import lombok.Getter; @Getter +@AllArgsConstructor public class ChatMessageRequest { private String sender; diff --git a/src/main/java/com/manchui/domain/service/ChatMessageService.java b/src/main/java/com/manchui/domain/service/ChatMessageService.java index 929e66a..84d4753 100644 --- a/src/main/java/com/manchui/domain/service/ChatMessageService.java +++ b/src/main/java/com/manchui/domain/service/ChatMessageService.java @@ -43,6 +43,11 @@ public Mono chatMessageSave(ChatMessageRequest chatMessageRequest, chatMessageRequest.getMessage(), LocalDateTime.now())); } + public Mono chatRoomOpenMessageSave(ChatMessageRequest chatMessageRequest, String roomId){ + return chatMessageRepository.save(new ChatMessage(roomId, ChatMessageType.OPEN,chatMessageRequest.getSender(), + chatMessageRequest.getMessage(), LocalDateTime.now())); + } + //채팅방 입장 및 채팅 목록 조회 메서드 public Mono findChatList(CustomUserDetails customUserDetails, String roomId, ObjectId lastMessageId, int limit, RabbitTemplate rabbitTemplate) { diff --git a/src/main/java/com/manchui/domain/service/GatheringServiceImpl.java b/src/main/java/com/manchui/domain/service/GatheringServiceImpl.java index 5032dba..7210bd7 100644 --- a/src/main/java/com/manchui/domain/service/GatheringServiceImpl.java +++ b/src/main/java/com/manchui/domain/service/GatheringServiceImpl.java @@ -2,6 +2,7 @@ import com.manchui.domain.dto.CustomUserDetails; import com.manchui.domain.dto.UserInfo; +import com.manchui.domain.dto.chat.ChatMessageRequest; import com.manchui.domain.dto.gathering.*; import com.manchui.domain.dto.review.ReviewDetailPagingResponse; import com.manchui.domain.dto.review.ReviewInfo; @@ -57,6 +58,9 @@ public class GatheringServiceImpl implements GatheringService { private final ChatRoomUserRepository chatRoomUserRepository; + private final ChatMessageService chatMessageService; + + /** * 0. 모임 생성 * 작성자 : 오예령 @@ -117,8 +121,10 @@ public GatheringCreateResponse createGathering(String email, GatheringCreateRequ } else { // 2. 모임 및 이미지 객체 저장 ChatRoom chatRoom = new ChatRoom(UUID.randomUUID().toString()); - chatRoomRepository.save(chatRoom); + String roomId = chatRoomRepository.save(chatRoom).getRoomId(); chatRoomUserRepository.save(new ChatRoomUser(user, chatRoom)); + chatMessageService.chatRoomOpenMessageSave(new ChatMessageRequest(user.getName(), user.getName() + "님이 채팅방을 개설하였습니다."), roomId).block(); + Gathering gathering = gatheringStore.saveGathering(createRequest, user, gatheringDate, dueDate, chatRoom); imageService.uploadGatheringImage(createRequest.getGatheringImage(), gathering.getId(), false); From 8ab38a3c8429391992af446484fedd166d660fe4 Mon Sep 17 00:00:00 2001 From: byeunghoon Kang Date: Wed, 5 Feb 2025 23:14:48 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=20=EB=82=98?= =?UTF-8?q?=EA=B0=80=EA=B8=B0=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/controller/ChatController.java | 21 +++++++++++++++++++ .../repository/ChatRoomUserRepository.java | 6 ++++-- .../domain/service/ChatMessageService.java | 6 ++++++ .../domain/service/ChatRoomService.java | 15 +++++++++++++ .../manchui/global/exception/ErrorCode.java | 3 ++- 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/manchui/domain/controller/ChatController.java b/src/main/java/com/manchui/domain/controller/ChatController.java index 622b73d..9488dcb 100644 --- a/src/main/java/com/manchui/domain/controller/ChatController.java +++ b/src/main/java/com/manchui/domain/controller/ChatController.java @@ -19,7 +19,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; +import java.security.Principal; import java.time.LocalDateTime; @RestController @@ -54,6 +56,25 @@ public Mono>> chat(@DestinationVariable Str }).then(Mono.just(ResponseEntity.ok().body(SuccessResponse.successWithNoData("메시지 전송 성공")))); } + @MessageMapping("chat.leave.{roomId}") + public Mono>> chatRoomLeave(@DestinationVariable String roomId, + @RequestBody ChatMessageRequest chatMessageRequest, + Principal principal){ + + return chatMessageService.chatQuiteMessageSave(chatMessageRequest, roomId).flatMap(chatMessage -> { + // 채팅방 나가기 메시지 전송 + rabbitTemplate.convertAndSend("chat.exchange", "room." + roomId, new ChatMessageResponse( + chatMessageRequest.getSender(), chatMessageRequest.getSender() + chatMessageRequest.getMessage(), chatMessage.getChatMessageType(), LocalDateTime.now())); + + // 블로킹 JPA 메서드는 별도 스레드에서 실행 + // 채팅방 유저 목록에서 유저 softDelete + return Mono.fromCallable(() -> { + chatRoomService.chatRoomQuite(principal.getName(), roomId); + return null; + }).subscribeOn(Schedulers.boundedElastic()); + }).then(Mono.just(ResponseEntity.ok().body(SuccessResponse.successWithNoData("채팅방 나기기 성공")))); + } + @GetMapping("/api/chat/user/list/{roomId}") public ResponseEntity> chatRoomUserList(@PathVariable String roomId) { diff --git a/src/main/java/com/manchui/domain/repository/ChatRoomUserRepository.java b/src/main/java/com/manchui/domain/repository/ChatRoomUserRepository.java index fa9d16c..c6b7efb 100644 --- a/src/main/java/com/manchui/domain/repository/ChatRoomUserRepository.java +++ b/src/main/java/com/manchui/domain/repository/ChatRoomUserRepository.java @@ -11,9 +11,11 @@ public interface ChatRoomUserRepository extends JpaRepository { - List findByChatRoomEquals(ChatRoom chatRoom); + List findByChatRoomEqualsAndDeletedAtIsNull(ChatRoom chatRoom); + + Optional findByUserEqualsAndChatRoomEqualsAndDeletedAtIsNull(User user, ChatRoom chatRoom); Optional findByUserEqualsAndChatRoomEquals(User user, ChatRoom chatRoom); - List findByUser(User user); + List findByUserAndDeletedAtIsNull(User user); } diff --git a/src/main/java/com/manchui/domain/service/ChatMessageService.java b/src/main/java/com/manchui/domain/service/ChatMessageService.java index 84d4753..4bc1e72 100644 --- a/src/main/java/com/manchui/domain/service/ChatMessageService.java +++ b/src/main/java/com/manchui/domain/service/ChatMessageService.java @@ -43,6 +43,12 @@ public Mono chatMessageSave(ChatMessageRequest chatMessageRequest, chatMessageRequest.getMessage(), LocalDateTime.now())); } + public Mono chatQuiteMessageSave(ChatMessageRequest chatMessageRequest, String roomId){ + + return chatMessageRepository.save(new ChatMessage(roomId, ChatMessageType.QUITE,chatMessageRequest.getSender(), + chatMessageRequest.getSender() + chatMessageRequest.getMessage(), LocalDateTime.now())); + } + public Mono chatRoomOpenMessageSave(ChatMessageRequest chatMessageRequest, String roomId){ return chatMessageRepository.save(new ChatMessage(roomId, ChatMessageType.OPEN,chatMessageRequest.getSender(), chatMessageRequest.getMessage(), LocalDateTime.now())); diff --git a/src/main/java/com/manchui/domain/service/ChatRoomService.java b/src/main/java/com/manchui/domain/service/ChatRoomService.java index 7d83a75..5ece476 100644 --- a/src/main/java/com/manchui/domain/service/ChatRoomService.java +++ b/src/main/java/com/manchui/domain/service/ChatRoomService.java @@ -12,7 +12,9 @@ import com.manchui.global.exception.CustomException; import com.manchui.global.exception.ErrorCode; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.Comparator; @@ -21,6 +23,7 @@ @Service @RequiredArgsConstructor +@Slf4j public class ChatRoomService { private final ChatRoomUserRepository chatRoomUserRepository; @@ -68,4 +71,16 @@ public ChatRoomListResponse chatRoomList(CustomUserDetails customUserDetails) { return new ChatRoomListResponse(chatRoomListDetails); } + + // 채팅방에 속한 사용자 softDelete + @Transactional + public void chatRoomQuite(String email, String roomId){ + + User user = userRepository.findByEmail(email); + ChatRoom chatRoom = chatRoomRepository.findByRoomId(roomId); + ChatRoomUser chatRoomUser = chatRoomUserRepository.findByUserEqualsAndChatRoomEqualsAndDeletedAtIsNull(user, chatRoom).orElseThrow( + () -> new CustomException(ErrorCode.MEMBER_NOT_IN_CHATROOM) + ); + chatRoomUser.softDelete(); + } } diff --git a/src/main/java/com/manchui/global/exception/ErrorCode.java b/src/main/java/com/manchui/global/exception/ErrorCode.java index 0fe3c1b..4503daa 100644 --- a/src/main/java/com/manchui/global/exception/ErrorCode.java +++ b/src/main/java/com/manchui/global/exception/ErrorCode.java @@ -75,7 +75,8 @@ public enum ErrorCode { NOTIFICATION_CANNOT_BE_DELETED(HttpStatus.BAD_REQUEST, "삭제할 수 없는 알림입니다.?"), // chat - CHATROOM_NOT_FOUND(HttpStatus.BAD_REQUEST, "존재하지 않는 채팅방 입니다."); + CHATROOM_NOT_FOUND(HttpStatus.BAD_REQUEST, "존재하지 않는 채팅방 입니다."), + MEMBER_NOT_IN_CHATROOM(HttpStatus.NOT_FOUND, "회원이 해당 채팅방에 속하지 않습니다."); private final HttpStatus httpStatus; private final String message; From 329828ff14cf51209e6fb749911ac5ef324fd154 Mon Sep 17 00:00:00 2001 From: byeunghoon Kang Date: Wed, 5 Feb 2025 23:15:43 +0900 Subject: [PATCH 4/7] =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EC=B2=AB=20=EC=9E=85?= =?UTF-8?q?=EC=9E=A5,=20=EC=9E=AC=20=EC=9E=85=EC=9E=A5,=20=EB=8B=A8?= =?UTF-8?q?=EC=88=9C=20=EC=9E=85=EC=9E=A5=EC=8B=9C=20=EC=9E=85=EC=9E=A5=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=EC=9D=84=20=EA=B8=B0=EC=A4=80=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=B1=84=ED=8C=85=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=ED=95=98=EB=8F=84=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mongodb/ChatMessageRepository.java | 7 +- .../domain/service/ChatMessageService.java | 81 ++++++++++++------- 2 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/manchui/domain/repository/mongodb/ChatMessageRepository.java b/src/main/java/com/manchui/domain/repository/mongodb/ChatMessageRepository.java index bd1f4b3..d7d6251 100644 --- a/src/main/java/com/manchui/domain/repository/mongodb/ChatMessageRepository.java +++ b/src/main/java/com/manchui/domain/repository/mongodb/ChatMessageRepository.java @@ -7,13 +7,14 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.time.LocalDateTime; public interface ChatMessageRepository extends ReactiveMongoRepository { - Flux findByRoomIdOrderByCreatedAtDesc(String roomId); + Flux findByRoomIdAndCreatedAtGreaterThanEqualOrderByCreatedAtDesc(String roomId, LocalDateTime createdAt); - @Query(value = "{ 'roomId': ?0, '_id': { $lt: ?1 } }", sort = "{ '_id': -1 }") - Flux findByRoomIdAndIdLessThanOrderByIdDesc(String roomId, ObjectId lastMessageId); + @Query(value = "{ 'roomId': ?0, '_id': { $lt: ?1 }, 'createdAt': { $lt: ?2 } }", sort = "{ '_id': -1 }") + Flux findByRoomIdAndIdLessThanAndCreatedAtGreaterThanEqualOrderByIdDesc(String roomId, ObjectId lastMessageId, LocalDateTime createdAt); Mono findFirstByRoomIdOrderByCreatedAtDesc(String roomId); } diff --git a/src/main/java/com/manchui/domain/service/ChatMessageService.java b/src/main/java/com/manchui/domain/service/ChatMessageService.java index 4bc1e72..1ba53a4 100644 --- a/src/main/java/com/manchui/domain/service/ChatMessageService.java +++ b/src/main/java/com/manchui/domain/service/ChatMessageService.java @@ -18,8 +18,11 @@ import org.bson.types.ObjectId; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionTemplate; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; +import reactor.util.function.Tuples; import java.time.LocalDateTime; import java.util.List; @@ -35,6 +38,7 @@ public class ChatMessageService { private final UserRepository userRepository; private final ChatRoomRepository chatRoomRepository; private final ChatRoomUserRepository chatRoomUserRepository; + private final TransactionTemplate transactionTemplate; // 채팅 저장 메서드 public Mono chatMessageSave(ChatMessageRequest chatMessageRequest, String roomId) { @@ -55,45 +59,68 @@ public Mono chatRoomOpenMessageSave(ChatMessageRequest chatMessageR } //채팅방 입장 및 채팅 목록 조회 메서드 + @Transactional public Mono findChatList(CustomUserDetails customUserDetails, String roomId, ObjectId lastMessageId, int limit, RabbitTemplate rabbitTemplate) { // Blocking(JPA) 작업을 다른 스레드풀에서 수행하기 위해 fromCallable 사용 - return Mono.fromCallable(() -> { - - // 블로킹(JPA) 코드 실행 영역. - // 이벤트 루프가 아닌 별도 쓰레드에서 처리할 예정이므로, - // 여기서 JPA 호출을 수행해도 WebFlux 이벤트 루프를 방해하지 않는다. - - // 유저 정보와 채팅방 정보를 블로킹 방식으로 조회 - String userEmail = customUserDetails.getUsername(); - User user = userRepository.findByEmail(userEmail); - ChatRoom chatRoom = chatRoomRepository.findByRoomId(roomId); - - // 해당 유저가 채팅방 참여자가 맞는지 확인 후, 없으면 새로 저장 및 입장 메시지 - Optional chatRoomUser = chatRoomUserRepository.findByUserEqualsAndChatRoomEquals(user, chatRoom); - if (chatRoomUser.isEmpty()) { - chatRoomUserRepository.save(new ChatRoomUser(user, chatRoom)); - chatMessageRepository.save(new ChatMessage(roomId, ChatMessageType.ENTER, user.getName(), user.getName() + " 님이 입장 하셨습니다.", LocalDateTime.now())).block(); - rabbitTemplate.convertAndSend("chat.exchange", "room." + roomId, new ChatMessageResponse( - user.getName(), user.getName() + " 님이 입장 하셨습니다.", ChatMessageType.MESSAGE,LocalDateTime.now())); - } + return Mono.fromCallable(() -> + transactionTemplate.execute(status -> { + // 블로킹(JPA) 코드 실행 영역. + // 이벤트 루프가 아닌 별도 쓰레드에서 처리할 예정이므로, + // 여기서 JPA 호출을 수행해도 WebFlux 이벤트 루프를 방해하지 않는다. - // 블로킹 영역에서 최종적으로 roomId만 반환 - // (이후 flatMapMany로 ReactiveMongoRepository를 호출하기 위해서) - return roomId; - }) + // 유저 정보와 채팅방 정보를 블로킹 방식으로 조회 + String userEmail = customUserDetails.getUsername(); + User user = userRepository.findByEmail(userEmail); + ChatRoom chatRoom = chatRoomRepository.findByRoomId(roomId); + + // 해당 유저가 채팅방 참여자가 맞는지 확인 후, 없으면 새로 저장 및 입장 메시지 + // DeleteatAt 상관없이 조회 후에 delete 가 null 이면 그냥 새로운 채팅방 회원, deleteatat의 값이 있으면 deleatedat null로 변환후, + Optional chatRoomUser = chatRoomUserRepository.findByUserEqualsAndChatRoomEquals(user, chatRoom); + LocalDateTime chatRoomUserUpdatedAt = null; + // 채팅방에 유저가 속해있으면 채팅방 입장 시간 조회 + if (chatRoomUser.isPresent()) { + chatRoomUserUpdatedAt = chatRoomUser.get().getUpdatedAt(); + } + + if (chatRoomUser.isEmpty()) { + // 채팅방 첫입장 + chatRoomUserUpdatedAt = chatRoomUserRepository.save(new ChatRoomUser(user, chatRoom)).getUser().getUpdatedAt(); + chatMessageRepository.save(new ChatMessage(roomId, ChatMessageType.ENTER, user.getName(), user.getName() + " 님이 입장 하셨습니다.", LocalDateTime.now())).block(); + rabbitTemplate.convertAndSend("chat.exchange", "room." + roomId, new ChatMessageResponse( + user.getName(), user.getName() + " 님이 입장 하셨습니다.", ChatMessageType.ENTER, LocalDateTime.now())); + }else if(chatRoomUser.get().getDeletedAt() != null){ + // 채팅방 재입장 + chatRoomUser.get().restore(); + chatRoomUser.get().updateTime(); + chatRoomUserRepository.flush(); + chatRoomUserUpdatedAt = chatRoomUser.get().getUpdatedAt(); + + chatMessageRepository.save(new ChatMessage(roomId, ChatMessageType.ENTER, user.getName(), user.getName() + " 님이 입장 하셨습니다.", chatRoomUserUpdatedAt)).block(); + rabbitTemplate.convertAndSend("chat.exchange", "room." + roomId, new ChatMessageResponse( + user.getName(), user.getName() + " 님이 입장 하셨습니다.", ChatMessageType.ENTER, chatRoomUserUpdatedAt)); + } + + // 블로킹 영역에서 최종적으로 roomId만 반환 + // (이후 flatMapMany로 ReactiveMongoRepository를 호출하기 위해서) + return Tuples.of(roomId, chatRoomUserUpdatedAt); + }) + ) // subscribeOn: 위의 fromCallable 블록을 별도의 쓰레드 풀(boundedElastic)에서 실행 .subscribeOn(Schedulers.boundedElastic()) // flatMapMany로 넘겨 받은 roomId로 리액티브 MongoDB 쿼리 수행 - .flatMapMany(rId -> { + .flatMapMany(tuple -> { + + String rId = tuple.getT1(); + LocalDateTime updatedAt = tuple.getT2(); // lastMessageId == null이면 최신 메시지 조회 if (lastMessageId == null) { // 초기 요청 (최신 메시지 limit + 1개) - return chatMessageRepository.findByRoomIdOrderByCreatedAtDesc(rId) - .take(limit + 1); // // limit+1개 가져와서 다음 페이지 여부 확인 + return chatMessageRepository.findByRoomIdAndCreatedAtGreaterThanEqualOrderByCreatedAtDesc(rId, updatedAt) + .take(limit + 1); // limit+1개 가져와서 다음 페이지 여부 확인 } else { // 이후 요청 (lastMessageId 기준) - return chatMessageRepository.findByRoomIdAndIdLessThanOrderByIdDesc(rId, lastMessageId) + return chatMessageRepository.findByRoomIdAndIdLessThanAndCreatedAtGreaterThanEqualOrderByIdDesc(rId, lastMessageId, updatedAt) .take(limit + 1); } }) From 75c772c9e45206613749841365a30f5ce9ffa810 Mon Sep 17 00:00:00 2001 From: byeunghoon Kang Date: Wed, 5 Feb 2025 23:17:16 +0900 Subject: [PATCH 5/7] =?UTF-8?q?[Feat]=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EC=99=80=20=EC=82=AC=EC=9A=A9=EC=9E=90=EA=B0=80=20=EC=86=8D?= =?UTF-8?q?=ED=95=9C=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=EC=8B=9C=20=EC=82=AD=EC=A0=9C=EB=90=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=EC=9D=B4=EC=99=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/manchui/domain/service/ChatRoomService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/manchui/domain/service/ChatRoomService.java b/src/main/java/com/manchui/domain/service/ChatRoomService.java index 5ece476..abdf441 100644 --- a/src/main/java/com/manchui/domain/service/ChatRoomService.java +++ b/src/main/java/com/manchui/domain/service/ChatRoomService.java @@ -38,7 +38,7 @@ public ChatRoomUserListResponse chatRoomUserList(String roomId) { ChatRoom chatRoom = chatRoomRepository.findByRoomId(roomId); - List userList = chatRoomUserRepository.findByChatRoomEquals(chatRoom); + List userList = chatRoomUserRepository.findByChatRoomEqualsAndDeletedAtIsNull(chatRoom); List userInfoList = userList.stream().map(m -> new UserInfo( m.getUser().getName(), @@ -54,7 +54,7 @@ public ChatRoomListResponse chatRoomList(CustomUserDetails customUserDetails) { String userEmail = customUserDetails.getUsername(); User user = userRepository.findByEmail(userEmail); // 사용자가 속하 ChatRoomUser 조회 - List chatRoomUsers = chatRoomUserRepository.findByUser(user); + List chatRoomUsers = chatRoomUserRepository.findByUserAndDeletedAtIsNull(user); // ChatRoomUser -> DTO(ChatRoomListDetail) 변환 List chatRoomListDetails = chatRoomUsers.stream().map((m -> { ChatRoom chatRoom = m.getChatRoom(); @@ -62,7 +62,7 @@ public ChatRoomListResponse chatRoomList(CustomUserDetails customUserDetails) { () -> new CustomException(ErrorCode.GATHERING_NOT_FOUND)); Image image = imageRepository.findByGatheringId(gathering.getId()); - List chatRoomEquals = chatRoomUserRepository.findByChatRoomEquals(chatRoom); + List chatRoomEquals = chatRoomUserRepository.findByChatRoomEqualsAndDeletedAtIsNull(chatRoom); ChatMessage lastMessage = chatMessageRepository.findFirstByRoomIdOrderByCreatedAtDesc(chatRoom.getRoomId()).block(); return new ChatRoomListDetail(m.getChatRoom().getRoomId(), image.getFilePath(), gathering.getGroupName(), From c5818c906ff192fa2f680690ee10bee11b5596b4 Mon Sep 17 00:00:00 2001 From: byeunghoon Kang Date: Wed, 5 Feb 2025 23:18:13 +0900 Subject: [PATCH 6/7] =?UTF-8?q?[Feat]=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=EC=86=8D=ED=95=9C=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=EB=90=98=EB=8A=94=20DTO=EC=97=90=20=EB=A7=88=EC=A7=80?= =?UTF-8?q?=EB=A7=89=20=EC=B1=84=ED=8C=85=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=20=EC=9D=B4=EB=A6=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/manchui/domain/dto/chat/ChatRoomListDetail.java | 1 + src/main/java/com/manchui/domain/service/ChatRoomService.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/manchui/domain/dto/chat/ChatRoomListDetail.java b/src/main/java/com/manchui/domain/dto/chat/ChatRoomListDetail.java index 862c03b..27bc5dc 100644 --- a/src/main/java/com/manchui/domain/dto/chat/ChatRoomListDetail.java +++ b/src/main/java/com/manchui/domain/dto/chat/ChatRoomListDetail.java @@ -15,4 +15,5 @@ public class ChatRoomListDetail { private int userNum; private LocalDateTime lastMessageTime; private String lastMessage; + private String lastMessageUserName; } diff --git a/src/main/java/com/manchui/domain/service/ChatRoomService.java b/src/main/java/com/manchui/domain/service/ChatRoomService.java index abdf441..1fe0c96 100644 --- a/src/main/java/com/manchui/domain/service/ChatRoomService.java +++ b/src/main/java/com/manchui/domain/service/ChatRoomService.java @@ -66,7 +66,7 @@ public ChatRoomListResponse chatRoomList(CustomUserDetails customUserDetails) { ChatMessage lastMessage = chatMessageRepository.findFirstByRoomIdOrderByCreatedAtDesc(chatRoom.getRoomId()).block(); return new ChatRoomListDetail(m.getChatRoom().getRoomId(), image.getFilePath(), gathering.getGroupName(), - chatRoomEquals.size(), lastMessage.getCreatedAt(), lastMessage.getMessage()); + chatRoomEquals.size(), lastMessage.getCreatedAt(), lastMessage.getMessage(), lastMessage.getSender()); })).sorted(Comparator.comparing(ChatRoomListDetail::getLastMessageTime).reversed()).collect(Collectors.toList()); return new ChatRoomListResponse(chatRoomListDetails); From da31b11fdaf6d5ed436e0392b8afc62f7ea3a194 Mon Sep 17 00:00:00 2001 From: byeunghoon Kang Date: Wed, 5 Feb 2025 23:18:41 +0900 Subject: [PATCH 7/7] =?UTF-8?q?[Feat]=20STOMP=EC=97=90=EC=84=9C=20Authenti?= =?UTF-8?q?cation=EC=9D=84=20=EC=82=AC=EC=9A=A9=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../manchui/global/handler/StompHandler.java | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/manchui/global/handler/StompHandler.java b/src/main/java/com/manchui/global/handler/StompHandler.java index 7c4b129..309429c 100644 --- a/src/main/java/com/manchui/global/handler/StompHandler.java +++ b/src/main/java/com/manchui/global/handler/StompHandler.java @@ -1,5 +1,8 @@ package com.manchui.global.handler; +import com.manchui.domain.dto.CustomUserDetails; +import com.manchui.domain.entity.User; +import com.manchui.domain.repository.UserRepository; import com.manchui.domain.service.RedisRefreshTokenService; import com.manchui.global.exception.CustomException; import com.manchui.global.exception.ErrorCode; @@ -15,6 +18,10 @@ import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.messaging.support.ChannelInterceptor; +import org.springframework.messaging.support.MessageHeaderAccessor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; @Component @@ -25,18 +32,25 @@ public class StompHandler implements ChannelInterceptor { private final JWTUtil jwtUtil; private final RedisRefreshTokenService redisRefreshTokenService; + private final UserRepository userRepository; // STOMP 메시지 전송 전에 가로채서 처리할 로직 @Override public Message preSend(Message message, MessageChannel channel) { // STOMP 헤더를 핸들링하기 위한 액세서 생성 - StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); + StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); // 만약 CONNECT 커맨드라면, JWT 검증 수행 if(StompCommand.CONNECT.equals(accessor.getCommand())){ String authorizationHeader = String.valueOf(accessor.getFirstNativeHeader("Authorization")); validateAccessToken(authorizationHeader); + + // 인증 정보(Authentication)를 SecurityContext에서 가져와 STOMP 메시지의 사용자(User)로 설정 + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth != null) { + accessor.setUser(auth); // STOMP 메시지의 Principal에 사용자 정보를 저장 + } } // 정상 처리된 경우 메시지를 그대로 리턴 @@ -78,5 +92,14 @@ private void validateAccessToken(String authorization) { if (!redisRefreshTokenService.existsByAccessToken(userEmail)) { throw new CustomException(ErrorCode.INVALID_ACCESS_TOKEN); } + + User user = userRepository.findByEmail(userEmail); + CustomUserDetails customUserDetails = new CustomUserDetails(user); + + // Authentication 객체 생성 + Authentication authentication = new UsernamePasswordAuthenticationToken(customUserDetails, null, customUserDetails.getAuthorities()); + + // SecurityContext 에 등록 + SecurityContextHolder.getContext().setAuthentication(authentication); } }