From cbd7fb21a0ca3333ac24be6015244fdb031fe692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=B0=94=EB=8B=A4?= Date: Fri, 10 Oct 2025 17:29:59 +0900 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20WorkspaceMember=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EB=B0=8F=20=EC=B6=94=EB=B0=A9=20=EC=8B=9C?= =?UTF-8?q?=20ChatMessage=EC=9D=98=20=EC=99=B8=EB=9E=98=20=ED=82=A4=20?= =?UTF-8?q?=EC=A0=9C=EC=95=BD=20=EC=A1=B0=EA=B1=B4=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=EC=82=AD=EC=A0=9C=20=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=EB=A5=BC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - ChatMessage.sender를 nullable로 변경하여 탈퇴한 사용자의 메시지 유지 - ChatMessageRepository에 nullifySenderByWorkspaceMemberId() 추가 - leaveWorkspace()와 kickMember()에서 멤버 삭제 전 sender null 처리 - ChatConverter에서 null sender를 "탈퇴한 사용자"로 표시 --- .../domain/chat/converter/ChatConverter.java | 8 +++++--- .../syncly/domain/chat/entity/ChatMessage.java | 2 +- .../chat/repository/ChatMessageRepository.java | 8 +++++++- .../workspace/service/WorkspaceServiceImpl.java | 14 ++++++++++---- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/project/syncly/domain/chat/converter/ChatConverter.java b/src/main/java/com/project/syncly/domain/chat/converter/ChatConverter.java index cdd1bf2..3ee98d7 100644 --- a/src/main/java/com/project/syncly/domain/chat/converter/ChatConverter.java +++ b/src/main/java/com/project/syncly/domain/chat/converter/ChatConverter.java @@ -22,12 +22,14 @@ public static ChatMessage toChatMessage(Workspace workspace, WorkspaceMember sen } public static ChatWebSocketResponseDto.ChatResponseDto toChatMessageResponse(ChatMessage chatMessage) { + WorkspaceMember sender = chatMessage.getSender(); + return ChatWebSocketResponseDto.ChatResponseDto.builder() .id(chatMessage.getId()) .workspaceId(chatMessage.getWorkspace().getId()) - .senderId(chatMessage.getSender().getId()) - .senderName(chatMessage.getSender().getName()) - .senderProfileImage(chatMessage.getSender().getProfileImage()) + .senderId(sender != null ? sender.getId() : null) + .senderName(sender != null ? sender.getName() : "탈퇴한 사용자") + .senderProfileImage(sender != null ? sender.getProfileImage() : null) .msgId(chatMessage.getMsgId()) .seq(chatMessage.getSeq()) .content(chatMessage.getContent()) diff --git a/src/main/java/com/project/syncly/domain/chat/entity/ChatMessage.java b/src/main/java/com/project/syncly/domain/chat/entity/ChatMessage.java index 53f483f..3298c13 100644 --- a/src/main/java/com/project/syncly/domain/chat/entity/ChatMessage.java +++ b/src/main/java/com/project/syncly/domain/chat/entity/ChatMessage.java @@ -31,7 +31,7 @@ public class ChatMessage extends BaseCreatedEntity { private Workspace workspace; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "sender_id", nullable = false) + @JoinColumn(name = "sender_id", nullable = true) private WorkspaceMember sender; @Column(name = "msg_id", nullable = false, length = 36) diff --git a/src/main/java/com/project/syncly/domain/chat/repository/ChatMessageRepository.java b/src/main/java/com/project/syncly/domain/chat/repository/ChatMessageRepository.java index 0f7ea96..7d6cd5f 100644 --- a/src/main/java/com/project/syncly/domain/chat/repository/ChatMessageRepository.java +++ b/src/main/java/com/project/syncly/domain/chat/repository/ChatMessageRepository.java @@ -3,6 +3,7 @@ import com.project.syncly.domain.chat.entity.ChatMessage; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @@ -45,7 +46,7 @@ public interface ChatMessageRepository extends JpaRepository Long findLatestSeq(@Param("ws") Long wsId); @Query(""" - SELECT cm + SELECT cm FROM ChatMessage cm JOIN FETCH cm.sender ws JOIN FETCH ws.member m @@ -53,6 +54,11 @@ public interface ChatMessageRepository extends JpaRepository """) Optional findByIdWithSenderAndMember(@Param("id") Long id); + // WorkspaceMember 삭제 전에 해당 멤버가 보낸 모든 메시지의 sender를 null로 설정 + @Modifying + @Query("UPDATE ChatMessage cm SET cm.sender = null WHERE cm.sender.id = :workspaceMemberId") + void nullifySenderByWorkspaceMemberId(@Param("workspaceMemberId") Long workspaceMemberId); + } diff --git a/src/main/java/com/project/syncly/domain/workspace/service/WorkspaceServiceImpl.java b/src/main/java/com/project/syncly/domain/workspace/service/WorkspaceServiceImpl.java index 69f97f9..224f96a 100644 --- a/src/main/java/com/project/syncly/domain/workspace/service/WorkspaceServiceImpl.java +++ b/src/main/java/com/project/syncly/domain/workspace/service/WorkspaceServiceImpl.java @@ -1,5 +1,6 @@ package com.project.syncly.domain.workspace.service; +import com.project.syncly.domain.chat.repository.ChatMessageRepository; import com.project.syncly.domain.folder.service.FolderCommandService; import com.project.syncly.domain.member.entity.Member; import com.project.syncly.domain.member.repository.MemberRepository; @@ -40,6 +41,7 @@ public class WorkspaceServiceImpl implements WorkspaceService { private final InvitationMailServiceImpl invitationMailService; private final SseServiceImpl sseService; private final FolderCommandService folderCommandService; + private final ChatMessageRepository chatMessageRepository; @Value("${spring.mail.invitation.link}") @@ -322,20 +324,23 @@ public WorkspaceResponseDto.LeaveWorkspaceResponseDto leaveWorkspace(Long worksp nextManager.setRole(Role.MANAGER); workspaceMemberRepository.save(nextManager); - // 멤버 삭제 + // 채팅 메시지의 sender를 null로 설정 후 멤버 삭제 + chatMessageRepository.nullifySenderByWorkspaceMemberId(member.getId()); workspaceMemberRepository.delete(member); } //나가고자 하는 사람이 매니저 일 경우 ( && 팀원이 매니저 포함 1명일 경우) else if (member.getRole() == Role.MANAGER) { - //멤버 삭제 후 워크 스페이스 삭제 + // 채팅 메시지의 sender를 null로 설정 후 멤버 삭제 및 워크 스페이스 삭제 + chatMessageRepository.nullifySenderByWorkspaceMemberId(member.getId()); workspaceMemberRepository.delete(member); workspaceRepository.delete(workspace); } //CREW 일 경우 else { - // 멤버 삭제 + // 채팅 메시지의 sender를 null로 설정 후 멤버 삭제 + chatMessageRepository.nullifySenderByWorkspaceMemberId(member.getId()); workspaceMemberRepository.delete(member); } @@ -378,7 +383,8 @@ public WorkspaceResponseDto.KickMemberResponseDto kickMember(Long workspaceId, L throw new CustomException(WorkspaceErrorCode.NOT_WORKSPACE_CREW); } - // 멤버 삭제 (추방) + // 채팅 메시지의 sender를 null로 설정 후 멤버 삭제 (추방) + chatMessageRepository.nullifySenderByWorkspaceMemberId(targetMember.getId()); workspaceMemberRepository.delete(targetMember); //반환