diff --git a/src/main/java/com/fmi/domain/chatroom/converter/ChatRoomConverter.java b/src/main/java/com/fmi/domain/chatroom/converter/ChatRoomConverter.java index 093974ea..23c99ebb 100644 --- a/src/main/java/com/fmi/domain/chatroom/converter/ChatRoomConverter.java +++ b/src/main/java/com/fmi/domain/chatroom/converter/ChatRoomConverter.java @@ -17,13 +17,7 @@ public class ChatRoomConverter { public static ChatRoomResultDTO toChatRoomResultDTO(ChatRoom chatRoom, User user, Post post, Long unreadCount, String thumbnailUrl, boolean isBlocked) { - var opponentUser = opponentUserDTO.builder() - .opponentUserId(user.getId()) - .nickname(user.getNickname()) - .profileImageUrl(user.getProfile_img() == null ? null : user.getProfile_img()) - .emailVerified(user.isEmail_verified()) - .blocked(isBlocked) - .build(); + var opponentUser = buildOpponentUserDTO(user, isBlocked); var postInfo = PostInfoDTO.builder() .postId(post.getId()) @@ -46,13 +40,7 @@ public static ChatRoomResultDTO toChatRoomResultDTO(ChatRoom chatRoom, User user public static ChatRoomResultDTO toChatRoomResultDTOFromSnapshot(ChatRoom chatRoom, User opponent, Long unreadCount, boolean isBlocked) { - var opponentUser = opponentUserDTO.builder() - .opponentUserId(opponent.getId()) - .nickname(opponent.getNickname()) - .profileImageUrl(opponent.getProfile_img()) - .emailVerified(opponent.isEmail_verified()) - .blocked(isBlocked) - .build(); + var opponentUser = buildOpponentUserDTO(opponent, isBlocked); var postInfo = PostInfoDTO.builder() .postId(chatRoom.getSourcePostId()) @@ -86,11 +74,7 @@ public static List toChatRoomSummaryListDTO(List thumbnailMap) { - ContactUserDTO contactUserDTO = ContactUserDTO.builder() - .userId(contactUser.getId()) - .nickname(contactUser.getNickname()) - .profileImageUrl(contactUser.getProfile_img()) - .build(); + ContactUserDTO contactUserDTO = buildContactUserDTO(contactUser); ChatRoom room = participant.getChatRoom(); @@ -144,6 +128,49 @@ private static boolean isImageMessage(ChatMessage message) { return message.getMessageType() == MessageType.IMAGE; } + private static ContactUserDTO buildContactUserDTO(User user) { + boolean withdrawn = user.getDeletedAt() != null; + + if (withdrawn) { + return ContactUserDTO.builder() + .userId(user.getId()) + .nickname(null) + .profileImageUrl(null) + .withdrawn(true) + .build(); + } + + return ContactUserDTO.builder() + .userId(user.getId()) + .nickname(user.getNickname()) + .profileImageUrl(user.getProfile_img()) + .withdrawn(false) + .build(); + } + + private static opponentUserDTO buildOpponentUserDTO(User user, boolean isBlocked) { + boolean withdrawn = user.getDeletedAt() != null; + + if (withdrawn) { + return opponentUserDTO.builder() + .opponentUserId(user.getId()) + .nickname(null) + .profileImageUrl(null) + .emailVerified(false) + .blocked(isBlocked) + .withdrawn(true) + .build(); + } + + return opponentUserDTO.builder() + .opponentUserId(user.getId()) + .nickname(user.getNickname()) + .profileImageUrl(user.getProfile_img()) + .emailVerified(user.isEmail_verified()) + .blocked(isBlocked) + .withdrawn(false) + .build(); + } private ChatRoomConverter() { } diff --git a/src/main/java/com/fmi/domain/chatroom/web/dto/ChatRoomResponseDTO.java b/src/main/java/com/fmi/domain/chatroom/web/dto/ChatRoomResponseDTO.java index 782b0910..0a3dd60d 100644 --- a/src/main/java/com/fmi/domain/chatroom/web/dto/ChatRoomResponseDTO.java +++ b/src/main/java/com/fmi/domain/chatroom/web/dto/ChatRoomResponseDTO.java @@ -35,6 +35,7 @@ public static class opponentUserDTO { private String profileImageUrl; private boolean emailVerified; private boolean blocked; + private boolean withdrawn; } @Builder @@ -83,5 +84,6 @@ public static class ContactUserDTO { private Long userId; private String nickname; private String profileImageUrl; + private boolean withdrawn; } } diff --git a/src/main/java/com/fmi/domain/userblock/repository/BlockedUserRepository.java b/src/main/java/com/fmi/domain/userblock/repository/BlockedUserRepository.java index 549a7562..64180963 100644 --- a/src/main/java/com/fmi/domain/userblock/repository/BlockedUserRepository.java +++ b/src/main/java/com/fmi/domain/userblock/repository/BlockedUserRepository.java @@ -16,6 +16,7 @@ @Repository public interface BlockedUserRepository extends JpaRepository { boolean existsByBlockerAndBlocked(User blocker, User blocked); + boolean existsByBlocker_IdAndBlocked_Id(Long blockerId, Long blockedId); Optional findByBlockerAndBlocked(User blocker, User blocked); List findAllByBlocker(User blocker); diff --git a/src/main/java/com/fmi/domain/userblock/service/BlockService.java b/src/main/java/com/fmi/domain/userblock/service/BlockService.java index 153eac59..60cf64e4 100644 --- a/src/main/java/com/fmi/domain/userblock/service/BlockService.java +++ b/src/main/java/com/fmi/domain/userblock/service/BlockService.java @@ -115,13 +115,8 @@ private void saveBlockIfNotExists(User blocker, User blocked) { } public boolean isBlocked(Long blockerUserId, Long otherUserId) { - User blocker = userRepository.findActiveById(blockerUserId) - .orElseThrow(() -> new GeneralException(ErrorStatus._USER_NOT_FOUND)); - User other = userRepository.findActiveById(otherUserId) - .orElseThrow(() -> new GeneralException(ErrorStatus._USER_NOT_FOUND)); - // 양방향 중 하나라도 존재하면 차단된 것으로 간주 - return blockedUserRepository.existsByBlockerAndBlocked(blocker, other) - || blockedUserRepository.existsByBlockerAndBlocked(other, blocker); + return blockedUserRepository.existsByBlocker_IdAndBlocked_Id(blockerUserId, otherUserId) + || blockedUserRepository.existsByBlocker_IdAndBlocked_Id(otherUserId, blockerUserId); } } diff --git a/src/test/java/com/fmi/domain/chatroom/service/ChatRoomServiceDetailTest.java b/src/test/java/com/fmi/domain/chatroom/service/ChatRoomServiceDetailTest.java index d5f46c85..41801ce9 100644 --- a/src/test/java/com/fmi/domain/chatroom/service/ChatRoomServiceDetailTest.java +++ b/src/test/java/com/fmi/domain/chatroom/service/ChatRoomServiceDetailTest.java @@ -25,6 +25,7 @@ import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +import java.time.LocalDateTime; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -197,6 +198,87 @@ void getChatRoomDetail_postHardDeleted_returnsSnapshot() { assertThat(result.getPostInfo().isDeleted()).isTrue(); } + @Test + @DisplayName("getChatRoomDetail - 상대가 탈퇴한 경우 withdrawn=true, 개인정보는 마스킹된다") + void getChatRoomDetail_opponentWithdrawn_returnsMaskedDto() { + User withdrawnOpponent = User.builder() + .id(2L) + .email("other@test.com") + .nickname("상대") + .profile_img("https://s3.example.com/p.jpg") + .email_verified(true) + .deletedAt(LocalDateTime.now().minusDays(1)) + .build(); + + Post post = mock(Post.class); + given(post.getId()).willReturn(99L); + given(post.getTitle()).willReturn("제목"); + given(post.getPostType()).willReturn(PostType.LOST); + given(post.getCategory()).willReturn(Category.WALLET); + given(post.getAddress()).willReturn("서울"); + given(post.getPostStatus()).willReturn(PostStatus.SEARCHING); + given(post.isDeleted()).willReturn(false); + + ChatRoom chatRoom = mock(ChatRoom.class); + given(chatRoom.getId()).willReturn(10L); + given(chatRoom.isSourcePostDeleted()).willReturn(false); + given(chatRoom.getPost()).willReturn(post); + given(chatRoom.getOtherParticipant(currentUser.getId())).willReturn(withdrawnOpponent); + given(chatRoom.getParticipant(currentUser.getId())).willReturn(participant); + + given(chatRoomRepository.findById(10L)).willReturn(Optional.of(chatRoom)); + given(postImageService.findThumbnailImageUrl(post)).willReturn(null); + given(blockService.isBlocked(currentUser.getId(), withdrawnOpponent.getId())).willReturn(false); + + ChatRoomResultDTO result = chatRoomService.getChatRoomDetail(10L, currentUser); + + assertThat(result.getOpponentUser().isWithdrawn()).isTrue(); + assertThat(result.getOpponentUser().getOpponentUserId()).isEqualTo(2L); + assertThat(result.getOpponentUser().getNickname()).isNull(); + assertThat(result.getOpponentUser().getProfileImageUrl()).isNull(); + assertThat(result.getOpponentUser().isEmailVerified()).isFalse(); + assertThat(result.getOpponentUser().isBlocked()).isFalse(); + } + + @Test + @DisplayName("getChatRoomDetail - 상대가 활성 상태면 withdrawn=false, 기존 정보 그대로 반환한다") + void getChatRoomDetail_opponentActive_returnsOriginalDto() { + User activeOpponent = User.builder() + .id(2L) + .email("other@test.com") + .nickname("상대") + .profile_img("https://s3.example.com/p.jpg") + .email_verified(true) + .build(); + + Post post = mock(Post.class); + given(post.getId()).willReturn(99L); + given(post.getTitle()).willReturn("제목"); + given(post.getPostType()).willReturn(PostType.LOST); + given(post.getCategory()).willReturn(Category.WALLET); + given(post.getAddress()).willReturn("서울"); + given(post.getPostStatus()).willReturn(PostStatus.SEARCHING); + given(post.isDeleted()).willReturn(false); + + ChatRoom chatRoom = mock(ChatRoom.class); + given(chatRoom.getId()).willReturn(10L); + given(chatRoom.isSourcePostDeleted()).willReturn(false); + given(chatRoom.getPost()).willReturn(post); + given(chatRoom.getOtherParticipant(currentUser.getId())).willReturn(activeOpponent); + given(chatRoom.getParticipant(currentUser.getId())).willReturn(participant); + + given(chatRoomRepository.findById(10L)).willReturn(Optional.of(chatRoom)); + given(postImageService.findThumbnailImageUrl(post)).willReturn(null); + given(blockService.isBlocked(currentUser.getId(), activeOpponent.getId())).willReturn(false); + + ChatRoomResultDTO result = chatRoomService.getChatRoomDetail(10L, currentUser); + + assertThat(result.getOpponentUser().isWithdrawn()).isFalse(); + assertThat(result.getOpponentUser().getNickname()).isEqualTo("상대"); + assertThat(result.getOpponentUser().getProfileImageUrl()).isEqualTo("https://s3.example.com/p.jpg"); + assertThat(result.getOpponentUser().isEmailVerified()).isTrue(); + } + @Test @DisplayName("getChatRoomDetail - unreadCount가 응답에 포함된다") void getChatRoomDetail_returnsUnreadCount() {