From 919e9e2cd04137bc95a2f243bd79094030c3ee39 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Thu, 25 Sep 2025 20:37:32 +0900 Subject: [PATCH 01/15] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EC=B0=A8?= =?UTF-8?q?=EB=8B=A8=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 커뮤니티 로직 수정 필요 --- .../common/exception/ErrorCode.java | 4 +++ .../controller/SiteUserController.java | 12 ++++++++ .../siteuser/domain/UserBlock.java | 5 ++++ .../repository/UserBlockRepository.java | 9 ++++++ .../siteuser/service/SiteUserService.java | 29 +++++++++++++++++++ 5 files changed, 59 insertions(+) create mode 100644 src/main/java/com/example/solidconnection/siteuser/repository/UserBlockRepository.java diff --git a/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java b/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java index 4d135416e..eec10b170 100644 --- a/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java +++ b/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java @@ -126,6 +126,10 @@ public enum ErrorCode { // report ALREADY_REPORTED_BY_CURRENT_USER(HttpStatus.BAD_REQUEST.value(), "이미 신고한 상태입니다."), + // block + ALREADY_BLOCKED_BY_CURRENT_USER(HttpStatus.BAD_REQUEST.value(), "이미 차단한 상태입니다."), + CANNOT_BLOCK_YOURSELF(HttpStatus.BAD_REQUEST.value(), "자기 자신을 차단할 수 없습니다."), + // chat INVALID_CHAT_ROOM_STATE(HttpStatus.BAD_REQUEST.value(), "잘못된 채팅방 상태입니다."), diff --git a/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java b/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java index 64d926eca..3453e4b4a 100644 --- a/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java +++ b/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java @@ -1,10 +1,13 @@ package com.example.solidconnection.siteuser.controller; +import com.example.solidconnection.common.resolver.AuthorizedUser; import com.example.solidconnection.siteuser.dto.NicknameExistsResponse; import com.example.solidconnection.siteuser.service.SiteUserService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -23,4 +26,13 @@ public ResponseEntity checkNicknameExists( NicknameExistsResponse nicknameExistsResponse = siteUserService.checkNicknameExists(nickname); return ResponseEntity.ok(nicknameExistsResponse); } + + @PostMapping("/block/{blocked-id}") + public ResponseEntity blockUser( + @AuthorizedUser long siteUserId, + @PathVariable("blocked-id") Long blockedId + ) { + siteUserService.blockUser(siteUserId, blockedId); + return ResponseEntity.ok().build(); + } } diff --git a/src/main/java/com/example/solidconnection/siteuser/domain/UserBlock.java b/src/main/java/com/example/solidconnection/siteuser/domain/UserBlock.java index 2e88ff7cf..9cd78795b 100644 --- a/src/main/java/com/example/solidconnection/siteuser/domain/UserBlock.java +++ b/src/main/java/com/example/solidconnection/siteuser/domain/UserBlock.java @@ -34,4 +34,9 @@ public class UserBlock extends BaseEntity { @Column(name = "blocked_id", nullable = false) private long blockedId; + + public UserBlock(long blockerId, long blockedId) { + this.blockerId = blockerId; + this.blockedId = blockedId; + } } diff --git a/src/main/java/com/example/solidconnection/siteuser/repository/UserBlockRepository.java b/src/main/java/com/example/solidconnection/siteuser/repository/UserBlockRepository.java new file mode 100644 index 000000000..5e1fc510c --- /dev/null +++ b/src/main/java/com/example/solidconnection/siteuser/repository/UserBlockRepository.java @@ -0,0 +1,9 @@ +package com.example.solidconnection.siteuser.repository; + +import com.example.solidconnection.siteuser.domain.UserBlock; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserBlockRepository extends JpaRepository { + + boolean existsByBlockerIdAndBlockedId(long blockerId, long blockedId); +} diff --git a/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java b/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java index e67d71ab8..f935d3067 100644 --- a/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java +++ b/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java @@ -1,7 +1,16 @@ package com.example.solidconnection.siteuser.service; +import static com.example.solidconnection.common.exception.ErrorCode.ALREADY_BLOCKED_BY_CURRENT_USER; +import static com.example.solidconnection.common.exception.ErrorCode.CANNOT_BLOCK_YOURSELF; +import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND; + +import com.example.solidconnection.common.exception.CustomException; +import com.example.solidconnection.siteuser.domain.UserBlock; import com.example.solidconnection.siteuser.dto.NicknameExistsResponse; import com.example.solidconnection.siteuser.repository.SiteUserRepository; +import com.example.solidconnection.siteuser.repository.UserBlockRepository; +import jakarta.transaction.Transactional; +import java.util.Objects; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -10,9 +19,29 @@ public class SiteUserService { private final SiteUserRepository siteUserRepository; + private final UserBlockRepository userBlockRepository; public NicknameExistsResponse checkNicknameExists(String nickname) { boolean exists = siteUserRepository.existsByNickname(nickname); return NicknameExistsResponse.from(exists); } + + @Transactional + public void blockUser(long blockerId, long blockedId) { + validateBlockUser(blockerId, blockedId); + UserBlock userBlock = new UserBlock(blockerId, blockedId); + userBlockRepository.save(userBlock); + } + + private void validateBlockUser(long blockerId, long blockedId) { + if (Objects.equals(blockerId, blockedId)) { + throw new CustomException(CANNOT_BLOCK_YOURSELF); + } + if (!siteUserRepository.existsById(blockedId)) { + throw new CustomException(USER_NOT_FOUND); + } + if (userBlockRepository.existsByBlockerIdAndBlockedId(blockerId, blockedId)) { + throw new CustomException(ALREADY_BLOCKED_BY_CURRENT_USER); + } + } } From e56bd403f0cc766f52e1bdbe72af4537108d6a0d Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Thu, 25 Sep 2025 20:38:37 +0900 Subject: [PATCH 02/15] =?UTF-8?q?teat:=20=EC=9C=A0=EC=A0=80=20=EC=B0=A8?= =?UTF-8?q?=EB=8B=A8=20api=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../siteuser/service/SiteUserServiceTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java b/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java index 3a81d40e2..f004e7363 100644 --- a/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java +++ b/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java @@ -1,10 +1,15 @@ package com.example.solidconnection.siteuser.service; +import static com.example.solidconnection.common.exception.ErrorCode.ALREADY_BLOCKED_BY_CURRENT_USER; +import static com.example.solidconnection.common.exception.ErrorCode.CANNOT_BLOCK_YOURSELF; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import com.example.solidconnection.common.exception.CustomException; import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.dto.NicknameExistsResponse; import com.example.solidconnection.siteuser.fixture.SiteUserFixture; +import com.example.solidconnection.siteuser.repository.UserBlockRepository; import com.example.solidconnection.support.TestContainerSpringBootTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -19,6 +24,9 @@ class SiteUserServiceTest { @Autowired private SiteUserService siteUserService; + @Autowired + private UserBlockRepository userBlockRepository; + @Autowired private SiteUserFixture siteUserFixture; @@ -50,4 +58,43 @@ class 닉네임_중복_검사 { assertThat(response.exists()).isFalse(); } } + + @Nested + class 유저_차단 { + + private SiteUser blockedUser; + + @BeforeEach + void setUp() { + blockedUser = siteUserFixture.사용자(1, "blockedUser"); + } + + @Test + void 성공적으로_유저를_차단한다() { + // when + siteUserService.blockUser(user.getId(), blockedUser.getId()); + + // then + assertThat(userBlockRepository.existsByBlockerIdAndBlockedId(user.getId(), blockedUser.getId())).isTrue(); + } + + @Test + void 자기_자신을_차단하면_예외가_발생한다() { + // when & then + assertThatCode(() -> siteUserService.blockUser(user.getId(), user.getId())) + .isInstanceOf(CustomException.class) + .hasMessage(CANNOT_BLOCK_YOURSELF.getMessage()); + } + + @Test + void 이미_차단했으면_예외가_발생한다() { + // given + siteUserService.blockUser(user.getId(), blockedUser.getId()); + + // when & then + assertThatCode(() -> siteUserService.blockUser(user.getId(), blockedUser.getId())) + .isInstanceOf(CustomException.class) + .hasMessage(ALREADY_BLOCKED_BY_CURRENT_USER.getMessage()); + } + } } From 55ce185b01133cf42c73343ae1a75ad183fbd772 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Thu, 25 Sep 2025 22:40:00 +0900 Subject: [PATCH 03/15] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EC=B0=A8?= =?UTF-8?q?=EB=8B=A8=20=EC=B7=A8=EC=86=8C=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../solidconnection/common/exception/ErrorCode.java | 1 + .../siteuser/controller/SiteUserController.java | 10 ++++++++++ .../siteuser/repository/UserBlockRepository.java | 3 +++ .../siteuser/service/SiteUserService.java | 11 +++++++++++ 4 files changed, 25 insertions(+) diff --git a/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java b/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java index eec10b170..fbd110f9d 100644 --- a/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java +++ b/src/main/java/com/example/solidconnection/common/exception/ErrorCode.java @@ -47,6 +47,7 @@ public enum ErrorCode { REPORT_TARGET_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "존재하지 않는 신고 대상입니다."), CHAT_PARTNER_NOT_FOUND(HttpStatus.BAD_REQUEST.value(), "채팅 상대를 찾을 수 없습니다."), CHAT_PARTICIPANT_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "채팅 참여자를 찾을 수 없습니다."), + BLOCK_USER_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "차단 대상 사용자를 찾을 수 없습니다."), // auth USER_ALREADY_SIGN_OUT(HttpStatus.UNAUTHORIZED.value(), "로그아웃 되었습니다."), diff --git a/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java b/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java index 3453e4b4a..8c550be57 100644 --- a/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java +++ b/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java @@ -5,6 +5,7 @@ import com.example.solidconnection.siteuser.service.SiteUserService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -35,4 +36,13 @@ public ResponseEntity blockUser( siteUserService.blockUser(siteUserId, blockedId); return ResponseEntity.ok().build(); } + + @DeleteMapping("/block/{blocked-id}") + public ResponseEntity cancelUserBlock( + @AuthorizedUser long siteUserId, + @PathVariable("blocked-id") Long blockedId + ) { + siteUserService.cancelUserBlock(siteUserId, blockedId); + return ResponseEntity.ok().build(); + } } diff --git a/src/main/java/com/example/solidconnection/siteuser/repository/UserBlockRepository.java b/src/main/java/com/example/solidconnection/siteuser/repository/UserBlockRepository.java index 5e1fc510c..c939b3cf1 100644 --- a/src/main/java/com/example/solidconnection/siteuser/repository/UserBlockRepository.java +++ b/src/main/java/com/example/solidconnection/siteuser/repository/UserBlockRepository.java @@ -1,9 +1,12 @@ package com.example.solidconnection.siteuser.repository; import com.example.solidconnection.siteuser.domain.UserBlock; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; public interface UserBlockRepository extends JpaRepository { boolean existsByBlockerIdAndBlockedId(long blockerId, long blockedId); + + Optional findByBlockerIdAndBlockedId(long blockerId, long blockedId); } diff --git a/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java b/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java index f935d3067..8a0c6ecb7 100644 --- a/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java +++ b/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java @@ -1,6 +1,7 @@ package com.example.solidconnection.siteuser.service; import static com.example.solidconnection.common.exception.ErrorCode.ALREADY_BLOCKED_BY_CURRENT_USER; +import static com.example.solidconnection.common.exception.ErrorCode.BLOCK_USER_NOT_FOUND; import static com.example.solidconnection.common.exception.ErrorCode.CANNOT_BLOCK_YOURSELF; import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND; @@ -44,4 +45,14 @@ private void validateBlockUser(long blockerId, long blockedId) { throw new CustomException(ALREADY_BLOCKED_BY_CURRENT_USER); } } + + @Transactional + public void cancelUserBlock(long blockerId, long blockedId) { + if (!siteUserRepository.existsById(blockedId)) { + throw new CustomException(USER_NOT_FOUND); + } + UserBlock userBlock = userBlockRepository.findByBlockerIdAndBlockedId(blockerId, blockedId) + .orElseThrow(() -> new CustomException(BLOCK_USER_NOT_FOUND)); + userBlockRepository.delete(userBlock); + } } From 760ba023f86cd0c987be1d7560abbc62c34c9fad Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Thu, 25 Sep 2025 22:40:53 +0900 Subject: [PATCH 04/15] =?UTF-8?q?test:=20=EC=9C=A0=EC=A0=80=20=EC=B0=A8?= =?UTF-8?q?=EB=8B=A8=20=EA=B4=80=EB=A0=A8=20fixture=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../siteuser/fixture/UserBlockFixture.java | 20 +++++++++++ .../fixture/UserBlockFixtureBuilder.java | 35 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/test/java/com/example/solidconnection/siteuser/fixture/UserBlockFixture.java create mode 100644 src/test/java/com/example/solidconnection/siteuser/fixture/UserBlockFixtureBuilder.java diff --git a/src/test/java/com/example/solidconnection/siteuser/fixture/UserBlockFixture.java b/src/test/java/com/example/solidconnection/siteuser/fixture/UserBlockFixture.java new file mode 100644 index 000000000..fe9fca698 --- /dev/null +++ b/src/test/java/com/example/solidconnection/siteuser/fixture/UserBlockFixture.java @@ -0,0 +1,20 @@ +package com.example.solidconnection.siteuser.fixture; + +import com.example.solidconnection.siteuser.domain.UserBlock; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.test.context.TestComponent; + +@TestComponent +@RequiredArgsConstructor +public class UserBlockFixture { + + private final UserBlockFixtureBuilder userBlockFixtureBuilder; + + public UserBlock 유저_차단(long blockerId, long blockedId) { + return userBlockFixtureBuilder.userBlock() + .blockerId(blockerId) + .blockedId(blockedId) + .create(); + } + +} diff --git a/src/test/java/com/example/solidconnection/siteuser/fixture/UserBlockFixtureBuilder.java b/src/test/java/com/example/solidconnection/siteuser/fixture/UserBlockFixtureBuilder.java new file mode 100644 index 000000000..deb4e0642 --- /dev/null +++ b/src/test/java/com/example/solidconnection/siteuser/fixture/UserBlockFixtureBuilder.java @@ -0,0 +1,35 @@ +package com.example.solidconnection.siteuser.fixture; + +import com.example.solidconnection.siteuser.domain.UserBlock; +import com.example.solidconnection.siteuser.repository.UserBlockRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.test.context.TestComponent; + +@TestComponent +@RequiredArgsConstructor +public class UserBlockFixtureBuilder { + + private final UserBlockRepository userBlockRepository; + + private long blockerId; + private long blockedId; + + public UserBlockFixtureBuilder userBlock() { + return new UserBlockFixtureBuilder(userBlockRepository); + } + + public UserBlockFixtureBuilder blockerId(long blockerId) { + this.blockerId = blockerId; + return this; + } + + public UserBlockFixtureBuilder blockedId(long blockedId) { + this.blockedId = blockedId; + return this; + } + + public UserBlock create() { + UserBlock userBlock = new UserBlock(blockerId, blockedId); + return userBlockRepository.save(userBlock); + } +} From 0e7705c5d8a64c56b026e04e0ff5f96305e79571 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Thu, 25 Sep 2025 22:41:04 +0900 Subject: [PATCH 05/15] =?UTF-8?q?test:=20=EC=9C=A0=EC=A0=80=20=EC=B0=A8?= =?UTF-8?q?=EB=8B=A8=20=EC=B7=A8=EC=86=8C=20api=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../siteuser/service/SiteUserServiceTest.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java b/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java index f004e7363..f4b75c4a4 100644 --- a/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java +++ b/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java @@ -1,6 +1,7 @@ package com.example.solidconnection.siteuser.service; import static com.example.solidconnection.common.exception.ErrorCode.ALREADY_BLOCKED_BY_CURRENT_USER; +import static com.example.solidconnection.common.exception.ErrorCode.BLOCK_USER_NOT_FOUND; import static com.example.solidconnection.common.exception.ErrorCode.CANNOT_BLOCK_YOURSELF; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; @@ -9,6 +10,7 @@ import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.dto.NicknameExistsResponse; import com.example.solidconnection.siteuser.fixture.SiteUserFixture; +import com.example.solidconnection.siteuser.fixture.UserBlockFixture; import com.example.solidconnection.siteuser.repository.UserBlockRepository; import com.example.solidconnection.support.TestContainerSpringBootTest; import org.junit.jupiter.api.BeforeEach; @@ -30,6 +32,9 @@ class SiteUserServiceTest { @Autowired private SiteUserFixture siteUserFixture; + @Autowired + private UserBlockFixture userBlockFixture; + private SiteUser user; @BeforeEach @@ -96,5 +101,25 @@ void setUp() { .isInstanceOf(CustomException.class) .hasMessage(ALREADY_BLOCKED_BY_CURRENT_USER.getMessage()); } + + @Test + void 성공적으로_유저_차단을_취소한다() { + // given + userBlockFixture.유저_차단(user.getId(), blockedUser.getId()); + + // when + siteUserService.cancelUserBlock(user.getId(), blockedUser.getId()); + + // then + assertThat(userBlockRepository.existsByBlockerIdAndBlockedId(user.getId(), blockedUser.getId())).isFalse(); + } + + @Test + void 차단하지_않았으면_예외가_발생한다() { + // when & then + assertThatCode(() -> siteUserService.cancelUserBlock(user.getId(), blockedUser.getId())) + .isInstanceOf(CustomException.class) + .hasMessage(BLOCK_USER_NOT_FOUND.getMessage()); + } } } From 61d0f53cac377b1a770a31edec168d1db689aaf8 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Thu, 25 Sep 2025 23:18:59 +0900 Subject: [PATCH 06/15] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EC=B0=A8?= =?UTF-8?q?=EB=8B=A8=20=EC=A1=B0=ED=9A=8C=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../siteuser/controller/SiteUserController.java | 14 ++++++++++++++ .../siteuser/dto/UserBlockResponse.java | 12 ++++++++++++ .../siteuser/repository/UserBlockRepository.java | 15 +++++++++++++++ .../siteuser/service/SiteUserService.java | 15 ++++++++++++++- 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/example/solidconnection/siteuser/dto/UserBlockResponse.java diff --git a/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java b/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java index 8c550be57..77dd01152 100644 --- a/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java +++ b/src/main/java/com/example/solidconnection/siteuser/controller/SiteUserController.java @@ -1,9 +1,14 @@ package com.example.solidconnection.siteuser.controller; +import com.example.solidconnection.common.dto.SliceResponse; import com.example.solidconnection.common.resolver.AuthorizedUser; import com.example.solidconnection.siteuser.dto.NicknameExistsResponse; +import com.example.solidconnection.siteuser.dto.UserBlockResponse; import com.example.solidconnection.siteuser.service.SiteUserService; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -28,6 +33,15 @@ public ResponseEntity checkNicknameExists( return ResponseEntity.ok(nicknameExistsResponse); } + @GetMapping("/blocks") + public ResponseEntity> getBlockedUsers( + @AuthorizedUser long siteUserId, + @PageableDefault(size = 20, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable + ) { + SliceResponse response = siteUserService.getBlockedUsers(siteUserId, pageable); + return ResponseEntity.ok(response); + } + @PostMapping("/block/{blocked-id}") public ResponseEntity blockUser( @AuthorizedUser long siteUserId, diff --git a/src/main/java/com/example/solidconnection/siteuser/dto/UserBlockResponse.java b/src/main/java/com/example/solidconnection/siteuser/dto/UserBlockResponse.java new file mode 100644 index 000000000..2f307d984 --- /dev/null +++ b/src/main/java/com/example/solidconnection/siteuser/dto/UserBlockResponse.java @@ -0,0 +1,12 @@ +package com.example.solidconnection.siteuser.dto; + +import java.time.ZonedDateTime; + +public record UserBlockResponse( + long id, + long blockedId, + String nickname, + ZonedDateTime createdAt +) { + +} diff --git a/src/main/java/com/example/solidconnection/siteuser/repository/UserBlockRepository.java b/src/main/java/com/example/solidconnection/siteuser/repository/UserBlockRepository.java index c939b3cf1..28a88874a 100644 --- a/src/main/java/com/example/solidconnection/siteuser/repository/UserBlockRepository.java +++ b/src/main/java/com/example/solidconnection/siteuser/repository/UserBlockRepository.java @@ -1,12 +1,27 @@ package com.example.solidconnection.siteuser.repository; import com.example.solidconnection.siteuser.domain.UserBlock; +import com.example.solidconnection.siteuser.dto.UserBlockResponse; import java.util.Optional; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface UserBlockRepository extends JpaRepository { boolean existsByBlockerIdAndBlockedId(long blockerId, long blockedId); Optional findByBlockerIdAndBlockedId(long blockerId, long blockedId); + + @Query(""" + SELECT new com.example.solidconnection.siteuser.dto.UserBlockResponse( + ub.id, ub.blockedId, su.nickname, ub.createdAt + ) + FROM UserBlock ub + JOIN SiteUser su ON ub.blockedId = su.id + WHERE ub.blockerId = :blockerId + """) + Slice findBlockedUsersWithNickname(@Param("blockerId") long blockerId, Pageable pageable); } diff --git a/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java b/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java index 8a0c6ecb7..7052a735a 100644 --- a/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java +++ b/src/main/java/com/example/solidconnection/siteuser/service/SiteUserService.java @@ -5,15 +5,20 @@ import static com.example.solidconnection.common.exception.ErrorCode.CANNOT_BLOCK_YOURSELF; import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND; +import com.example.solidconnection.common.dto.SliceResponse; import com.example.solidconnection.common.exception.CustomException; import com.example.solidconnection.siteuser.domain.UserBlock; import com.example.solidconnection.siteuser.dto.NicknameExistsResponse; +import com.example.solidconnection.siteuser.dto.UserBlockResponse; import com.example.solidconnection.siteuser.repository.SiteUserRepository; import com.example.solidconnection.siteuser.repository.UserBlockRepository; -import jakarta.transaction.Transactional; +import java.util.List; import java.util.Objects; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor @Service @@ -27,6 +32,14 @@ public NicknameExistsResponse checkNicknameExists(String nickname) { return NicknameExistsResponse.from(exists); } + @Transactional(readOnly = true) + public SliceResponse getBlockedUsers(long siteUserId, Pageable pageable) { + Slice slice = userBlockRepository.findBlockedUsersWithNickname(siteUserId, pageable); + + List content = slice.getContent(); + return SliceResponse.of(content, slice); + } + @Transactional public void blockUser(long blockerId, long blockedId) { validateBlockUser(blockerId, blockedId); From fd1f33f47da8721ba7e51c745f6497657c3c927e Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Thu, 25 Sep 2025 23:20:09 +0900 Subject: [PATCH 07/15] =?UTF-8?q?test:=20=EC=9C=A0=EC=A0=80=20=EC=B0=A8?= =?UTF-8?q?=EB=8B=A8=20=EC=A1=B0=ED=9A=8C=20api=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../siteuser/service/SiteUserServiceTest.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java b/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java index f4b75c4a4..6dd35d282 100644 --- a/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java +++ b/src/test/java/com/example/solidconnection/siteuser/service/SiteUserServiceTest.java @@ -5,10 +5,14 @@ import static com.example.solidconnection.common.exception.ErrorCode.CANNOT_BLOCK_YOURSELF; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.junit.jupiter.api.Assertions.assertAll; +import com.example.solidconnection.common.dto.SliceResponse; import com.example.solidconnection.common.exception.CustomException; import com.example.solidconnection.siteuser.domain.SiteUser; +import com.example.solidconnection.siteuser.domain.UserBlock; import com.example.solidconnection.siteuser.dto.NicknameExistsResponse; +import com.example.solidconnection.siteuser.dto.UserBlockResponse; import com.example.solidconnection.siteuser.fixture.SiteUserFixture; import com.example.solidconnection.siteuser.fixture.UserBlockFixture; import com.example.solidconnection.siteuser.repository.UserBlockRepository; @@ -18,6 +22,9 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; @TestContainerSpringBootTest @DisplayName("유저 서비스 테스트") @@ -64,6 +71,54 @@ class 닉네임_중복_검사 { } } + @Nested + class 유저_차단_조회 { + + private static final int NO_NEXT_PAGE_NUMBER = -1; + + private SiteUser blockedUser1; + private SiteUser blockedUser2; + private Pageable pageable; + + @BeforeEach + void setUp() { + blockedUser1 = siteUserFixture.사용자(1, "blockedUser1"); + blockedUser2 = siteUserFixture.사용자(2, "blockedUser2"); + pageable = PageRequest.of(0, 20, Sort.by(Sort.Direction.DESC, "createdAt")); + } + + @Test + void 최신순으로_차단한_사용자를_정상적으로_조회한다() { + // given + UserBlock userBlock1 = userBlockFixture.유저_차단(user.getId(), blockedUser1.getId()); + UserBlock userBlock2 = userBlockFixture.유저_차단(user.getId(), blockedUser2.getId()); + + // when + SliceResponse response = siteUserService.getBlockedUsers(user.getId(), pageable); + + // then + assertAll( + () -> assertThat(response.content()).hasSize(2), + () -> assertThat(response.content().get(0).id()).isEqualTo(userBlock2.getId()), + () -> assertThat(response.content().get(0).blockedId()).isEqualTo(blockedUser2.getId()), + () -> assertThat(response.content().get(1).id()).isEqualTo(userBlock1.getId()), + () -> assertThat(response.content().get(1).blockedId()).isEqualTo(blockedUser1.getId()) + ); + } + + @Test + void 차단한_사용자가_없으면_빈_목록을_반환한다() { + // when + SliceResponse response = siteUserService.getBlockedUsers(user.getId(), pageable); + + // then + assertAll( + () -> assertThat(response.content()).isEmpty(), + () -> assertThat(response.nextPageNumber()).isEqualTo(NO_NEXT_PAGE_NUMBER) + ); + } + } + @Nested class 유저_차단 { From f231b3ac4257cc8d6644f55b2bc64f5070f9f278 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Thu, 25 Sep 2025 23:54:57 +0900 Subject: [PATCH 08/15] =?UTF-8?q?feat:=20=EC=B0=A8=EB=8B=A8=ED=95=9C=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=EC=9D=98=20=EA=B2=8C=EC=8B=9C=EA=B8=80?= =?UTF-8?q?=EC=9D=80=20=EB=B3=B4=EC=9D=B4=EC=A7=80=20=EC=95=8A=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 커뮤니티 목록은 로그인하지 않더라도 보이게 수정 --- .../board/controller/BoardController.java | 4 +- .../post/repository/PostRepository.java | 9 ++++ .../post/service/PostQueryService.java | 9 +++- .../post/service/PostQueryServiceTest.java | 41 +++++++++++++++++-- 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/example/solidconnection/community/board/controller/BoardController.java b/src/main/java/com/example/solidconnection/community/board/controller/BoardController.java index 196a48239..89970d334 100644 --- a/src/main/java/com/example/solidconnection/community/board/controller/BoardController.java +++ b/src/main/java/com/example/solidconnection/community/board/controller/BoardController.java @@ -33,11 +33,11 @@ public ResponseEntity findAccessibleCodes() { @GetMapping("/{code}") public ResponseEntity findPostsByCodeAndCategory( - @AuthorizedUser long siteUserId, // todo: '사용하지 않는 인자'로 인증된 유저만 접근하게 하기보다는, 다른 방식으로 접근하는것이 좋을 것 같다 + @AuthorizedUser(required = false) Long siteUserId, @PathVariable(value = "code") String code, @RequestParam(value = "category", defaultValue = "전체") String category) { List postsByCodeAndPostCategory = postQueryService - .findPostsByCodeAndPostCategory(code, category); + .findPostsByCodeAndPostCategory(code, category, siteUserId); return ResponseEntity.ok().body(postsByCodeAndPostCategory); } } diff --git a/src/main/java/com/example/solidconnection/community/post/repository/PostRepository.java b/src/main/java/com/example/solidconnection/community/post/repository/PostRepository.java index de16d8ab1..aad12fab1 100644 --- a/src/main/java/com/example/solidconnection/community/post/repository/PostRepository.java +++ b/src/main/java/com/example/solidconnection/community/post/repository/PostRepository.java @@ -16,6 +16,15 @@ public interface PostRepository extends JpaRepository { List findByBoardCode(String boardCode); + @Query(""" + SELECT p FROM Post p + WHERE p.boardCode = :boardCode + AND p.siteUserId NOT IN ( + SELECT ub.blockedId FROM UserBlock ub WHERE ub.blockerId = :siteUserId + ) + """) + List findByBoardCodeExcludingBlockedUsers(@Param("boardCode") String boardCode, @Param("siteUserId") Long siteUserId); + @EntityGraph(attributePaths = {"postImageList"}) Optional findPostById(Long id); diff --git a/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java b/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java index 85657a8a7..c2f43f532 100644 --- a/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java +++ b/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java @@ -43,13 +43,18 @@ public class PostQueryService { private final RedisUtils redisUtils; @Transactional(readOnly = true) - public List findPostsByCodeAndPostCategory(String code, String category) { + public List findPostsByCodeAndPostCategory(String code, String category, Long siteUserId) { String boardCode = validateCode(code); PostCategory postCategory = validatePostCategory(category); boardRepository.getByCode(boardCode); - List postList = postRepository.findByBoardCode(boardCode); + List postList; // todo : 추후 개선 필요(현재 최신순으로 응답나가지 않고 있음) + if (siteUserId != null) { + postList = postRepository.findByBoardCodeExcludingBlockedUsers(boardCode, siteUserId); + } else { + postList = postRepository.findByBoardCode(boardCode); + } return PostListResponse.from(getPostListByPostCategory(postList, postCategory)); } diff --git a/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java b/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java index f5e1bb45b..4816e5f13 100644 --- a/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java +++ b/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import com.example.solidconnection.community.board.domain.Board; import com.example.solidconnection.community.board.domain.BoardCode; import com.example.solidconnection.community.board.fixture.BoardFixture; import com.example.solidconnection.community.comment.domain.Comment; @@ -15,6 +16,7 @@ import com.example.solidconnection.community.post.fixture.PostImageFixture; import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.fixture.SiteUserFixture; +import com.example.solidconnection.siteuser.fixture.UserBlockFixture; import com.example.solidconnection.support.TestContainerSpringBootTest; import com.example.solidconnection.util.RedisUtils; import java.time.ZonedDateTime; @@ -52,6 +54,9 @@ class PostQueryServiceTest { @Autowired private CommentFixture commentFixture; + @Autowired + private UserBlockFixture userBlockFixture; + private SiteUser user; private Post post1; private Post post2; @@ -99,7 +104,8 @@ void setUp() { // when List actualResponses = postQueryService.findPostsByCodeAndPostCategory( BoardCode.FREE.name(), - PostCategory.자유.name() + PostCategory.자유.name(), + null ); // then @@ -121,7 +127,8 @@ void setUp() { // when List actualResponses = postQueryService.findPostsByCodeAndPostCategory( BoardCode.FREE.name(), - PostCategory.전체.name() + PostCategory.전체.name(), + null ); // then @@ -178,7 +185,8 @@ void setUp() { // when List actualResponses = postQueryService.findPostsByCodeAndPostCategory( BoardCode.FREE.name(), - PostCategory.전체.name() + PostCategory.전체.name(), + null ); // then @@ -195,7 +203,8 @@ void setUp() { // when List actualResponses = postQueryService.findPostsByCodeAndPostCategory( BoardCode.FREE.name(), - PostCategory.전체.name() + PostCategory.전체.name(), + null ); // then @@ -206,4 +215,28 @@ void setUp() { assertThat(postResponse.postThumbnailUrl()).isNull(); } + + @Test + void 차단한_사용자의_게시글은_제외된다() { + // given + SiteUser blockedUser = siteUserFixture.사용자(1, "blockedUser"); + SiteUser notBlockedUser = siteUserFixture.사용자(2, "notBlockedUser"); + userBlockFixture.유저_차단(user.getId(), blockedUser.getId()); + Board board = boardFixture.자유게시판(); + Post blockedPost = postFixture.게시글(board, blockedUser); + Post notBlockedPost = postFixture.게시글(board, notBlockedUser); + + // when + List response = postQueryService.findPostsByCodeAndPostCategory( + BoardCode.FREE.name(), + PostCategory.전체.name(), + user.getId() + ); + + // then + assertAll( + () -> assertThat(response).extracting(PostListResponse::id).contains(notBlockedPost.getId()), + () -> assertThat(response).extracting(PostListResponse::id).doesNotContain(blockedPost.getId()) + ); + } } From 159781f5e66f50fac749a1a84bc3ec8c52cf0310 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Fri, 26 Sep 2025 00:33:19 +0900 Subject: [PATCH 09/15] =?UTF-8?q?feat:=20=EC=B0=A8=EB=8B=A8=ED=95=9C=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=EC=9D=98=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EB=8C=93=EA=B8=80=EB=8F=84=20=EB=B3=B4=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 차단한 댓글이면 대댓글은 차단하지 않았더라도 보이지 않도록 함 --- .../comment/repository/CommentRepository.java | 44 +++++++++++-------- .../comment/service/CommentService.java | 2 +- .../post/service/PostQueryService.java | 9 ++++ .../comment/service/CommentServiceTest.java | 31 +++++++++++++ .../post/service/PostQueryServiceTest.java | 18 ++++++++ 5 files changed, 84 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java b/src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java index c05cf9bd6..d3e85df66 100644 --- a/src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java +++ b/src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java @@ -12,25 +12,31 @@ public interface CommentRepository extends JpaRepository { @Query(value = """ - WITH RECURSIVE CommentTree AS ( - SELECT - id, parent_id, post_id, site_user_id, content, - created_at, updated_at, is_deleted, - 0 AS level, CAST(id AS CHAR(255)) AS path - FROM comment - WHERE post_id = :postId AND parent_id IS NULL - UNION ALL - SELECT - c.id, c.parent_id, c.post_id, c.site_user_id, c.content, - c.created_at, c.updated_at, c.is_deleted, - ct.level + 1, CONCAT(ct.path, '->', c.id) - FROM comment c - INNER JOIN CommentTree ct ON c.parent_id = ct.id - ) - SELECT * FROM CommentTree - ORDER BY path - """, nativeQuery = true) - List findCommentTreeByPostId(@Param("postId") Long postId); + WITH RECURSIVE CommentTree AS ( + SELECT + id, parent_id, post_id, site_user_id, content, + created_at, updated_at, is_deleted, + 0 AS level, CAST(id AS CHAR(255)) AS path + FROM comment + WHERE post_id = :postId AND parent_id IS NULL + AND site_user_id NOT IN ( + SELECT blocked_id FROM user_block WHERE blocker_id = :siteUserId + ) + UNION ALL + SELECT + c.id, c.parent_id, c.post_id, c.site_user_id, c.content, + c.created_at, c.updated_at, c.is_deleted, + ct.level + 1, CONCAT(ct.path, '->', c.id) + FROM comment c + INNER JOIN CommentTree ct ON c.parent_id = ct.id + WHERE c.site_user_id NOT IN ( + SELECT blocked_id FROM user_block WHERE blocker_id = :siteUserId + ) + ) + SELECT * FROM CommentTree + ORDER BY path + """, nativeQuery = true) + List findCommentTreeByPostId(@Param("postId") Long postId, @Param("siteUserId") Long siteUserId); default Comment getById(Long id) { return findById(id) diff --git a/src/main/java/com/example/solidconnection/community/comment/service/CommentService.java b/src/main/java/com/example/solidconnection/community/comment/service/CommentService.java index 81b6bb49b..d6d1896d4 100644 --- a/src/main/java/com/example/solidconnection/community/comment/service/CommentService.java +++ b/src/main/java/com/example/solidconnection/community/comment/service/CommentService.java @@ -40,7 +40,7 @@ public class CommentService { public List findCommentsByPostId(long siteUserId, Long postId) { SiteUser siteUser = siteUserRepository.findById(siteUserId) .orElseThrow(() -> new CustomException(USER_NOT_FOUND)); - List allComments = commentRepository.findCommentTreeByPostId(postId); + List allComments = commentRepository.findCommentTreeByPostId(postId, siteUserId); List filteredComments = filterCommentsByDeletionRules(allComments); Set userIds = filteredComments.stream() diff --git a/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java b/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java index c2f43f532..7097d579f 100644 --- a/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java +++ b/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java @@ -1,5 +1,6 @@ package com.example.solidconnection.community.post.service; +import static com.example.solidconnection.common.exception.ErrorCode.ACCESS_DENIED; import static com.example.solidconnection.common.exception.ErrorCode.INVALID_BOARD_CODE; import static com.example.solidconnection.common.exception.ErrorCode.INVALID_POST_CATEGORY; import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND; @@ -21,6 +22,7 @@ import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.dto.PostFindSiteUserResponse; import com.example.solidconnection.siteuser.repository.SiteUserRepository; +import com.example.solidconnection.siteuser.repository.UserBlockRepository; import com.example.solidconnection.util.RedisUtils; import java.util.List; import java.util.Objects; @@ -38,6 +40,7 @@ public class PostQueryService { private final PostRepository postRepository; private final PostLikeRepository postLikeRepository; private final SiteUserRepository siteUserRepository; + private final UserBlockRepository userBlockRepository; private final CommentService commentService; private final RedisService redisService; private final RedisUtils redisUtils; @@ -63,6 +66,12 @@ public PostFindResponse findPostById(long siteUserId, Long postId) { SiteUser siteUser = siteUserRepository.findById(siteUserId) .orElseThrow(() -> new CustomException(USER_NOT_FOUND)); Post post = postRepository.getByIdUsingEntityGraph(postId); + + boolean isBlockedByMe = userBlockRepository.existsByBlockerIdAndBlockedId(siteUserId, post.getSiteUserId()); + if (isBlockedByMe) { + throw new CustomException(ACCESS_DENIED); + } + Boolean isOwner = getIsOwner(post, siteUser); Boolean isLiked = getIsLiked(post, siteUser); diff --git a/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java b/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java index c5219b036..9dbe6c199 100644 --- a/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java +++ b/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java @@ -25,6 +25,7 @@ import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.dto.PostFindSiteUserResponse; import com.example.solidconnection.siteuser.fixture.SiteUserFixture; +import com.example.solidconnection.siteuser.fixture.UserBlockFixture; import com.example.solidconnection.support.TestContainerSpringBootTest; import jakarta.transaction.Transactional; import java.util.List; @@ -56,6 +57,9 @@ class CommentServiceTest { @Autowired private CommentFixture commentFixture; + @Autowired + private UserBlockFixture userBlockFixture; + private SiteUser user1; private SiteUser user2; private Post post; @@ -187,6 +191,33 @@ class 댓글_조회_테스트 { .containsExactlyInAnyOrder(user2.getId(), user2.getId()) ); } + + @Test + void 차단한_사용자의_댓글은_제외된다() { + // given + userBlockFixture.유저_차단(user1.getId(), user2.getId()); + Comment parentComment1 = commentFixture.부모_댓글("부모 댓글1", post, user1); + Comment childComment1 = commentFixture.자식_댓글("자식 댓글1", post, user1, parentComment1); + Comment childComment2 = commentFixture.자식_댓글("자식 댓글2", post, user2, parentComment1); + Comment parentCommen2 = commentFixture.부모_댓글("부모 댓글2", post, user2); + Comment childComment3 = commentFixture.자식_댓글("자식 댓글1", post, user1, parentCommen2); + Comment childComment4 = commentFixture.자식_댓글("자식 댓글1", post, user1, parentCommen2); + + + // when + List responses = commentService.findCommentsByPostId(post.getId(), user1.getId()); + + // then + assertAll( + () -> assertThat(responses).hasSize(2), + () -> assertThat(responses) + .extracting(PostFindCommentResponse::id) + .containsExactly(parentComment1.getId(), childComment1.getId()), + () -> assertThat(responses) + .extracting(PostFindCommentResponse::id) + .doesNotContain(childComment2.getId(), parentCommen2.getId(), childComment3.getId(), childComment4.getId())) + ; + } } @Nested diff --git a/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java b/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java index 4816e5f13..d7eeb5eb4 100644 --- a/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java +++ b/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java @@ -1,8 +1,11 @@ package com.example.solidconnection.community.post.service; +import static com.example.solidconnection.common.exception.ErrorCode.ACCESS_DENIED; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; import static org.junit.jupiter.api.Assertions.assertAll; +import com.example.solidconnection.common.exception.CustomException; import com.example.solidconnection.community.board.domain.Board; import com.example.solidconnection.community.board.domain.BoardCode; import com.example.solidconnection.community.board.fixture.BoardFixture; @@ -174,6 +177,21 @@ void setUp() { ); } + @Test + void 차단한_사용자의_게시글을_조회하면_예외가_발생한다() { + // given + SiteUser blockedUser = siteUserFixture.사용자(1, "blockedUser"); + userBlockFixture.유저_차단(user.getId(), blockedUser.getId()); + Board board = boardFixture.자유게시판(); + Post post = postFixture.게시글(board, blockedUser); + + // when & then + assertThatCode(() -> postQueryService.findPostById(user.getId(), post.getId())) + .isInstanceOf(CustomException.class) + .hasMessage(ACCESS_DENIED.getMessage()); + + } + @Test void 게시글_목록_조회시_첫번째_이미지를_썸네일로_반환한다() { // given From 82b404656f91760e2848da275a8faf10818bb3ad Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Fri, 26 Sep 2025 00:54:02 +0900 Subject: [PATCH 10/15] =?UTF-8?q?test:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 인자 순서 변경 --- .../community/comment/service/CommentServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java b/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java index 9dbe6c199..b96808390 100644 --- a/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java +++ b/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java @@ -205,7 +205,7 @@ class 댓글_조회_테스트 { // when - List responses = commentService.findCommentsByPostId(post.getId(), user1.getId()); + List responses = commentService.findCommentsByPostId(user1.getId(), post.getId()); // then assertAll( From d9c7b63684b294ee3a89c716bf48fd27990ab496 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Fri, 26 Sep 2025 00:55:30 +0900 Subject: [PATCH 11/15] =?UTF-8?q?style:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EA=B0=9C=ED=96=89=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/post/service/PostQueryServiceTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java b/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java index d7eeb5eb4..beeb3fc88 100644 --- a/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java +++ b/src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java @@ -189,7 +189,6 @@ void setUp() { assertThatCode(() -> postQueryService.findPostById(user.getId(), post.getId())) .isInstanceOf(CustomException.class) .hasMessage(ACCESS_DENIED.getMessage()); - } @Test From 7fd42c4f8c5e32f6ca44f8c14b6fc5529394e37a Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Sat, 27 Sep 2025 16:37:08 +0900 Subject: [PATCH 12/15] =?UTF-8?q?style:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EA=B0=9C=ED=96=89=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../solidconnection/siteuser/fixture/UserBlockFixture.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/example/solidconnection/siteuser/fixture/UserBlockFixture.java b/src/test/java/com/example/solidconnection/siteuser/fixture/UserBlockFixture.java index fe9fca698..cfb11b6e9 100644 --- a/src/test/java/com/example/solidconnection/siteuser/fixture/UserBlockFixture.java +++ b/src/test/java/com/example/solidconnection/siteuser/fixture/UserBlockFixture.java @@ -16,5 +16,4 @@ public class UserBlockFixture { .blockedId(blockedId) .create(); } - } From a94ae52761ebebe9d6bf17557b22f1708021b709 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Sat, 27 Sep 2025 16:37:23 +0900 Subject: [PATCH 13/15] =?UTF-8?q?style:=20=ED=95=A8=EC=88=98=EB=AA=85=20?= =?UTF-8?q?=EB=AA=85=ED=99=95=ED=95=98=EA=B2=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/comment/repository/CommentRepository.java | 2 +- .../community/comment/service/CommentService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java b/src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java index d3e85df66..39b42f49a 100644 --- a/src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java +++ b/src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java @@ -36,7 +36,7 @@ WHERE c.site_user_id NOT IN ( SELECT * FROM CommentTree ORDER BY path """, nativeQuery = true) - List findCommentTreeByPostId(@Param("postId") Long postId, @Param("siteUserId") Long siteUserId); + List findCommentTreeByPostIdExcludingBlockedUsers(@Param("postId") Long postId, @Param("siteUserId") Long siteUserId); default Comment getById(Long id) { return findById(id) diff --git a/src/main/java/com/example/solidconnection/community/comment/service/CommentService.java b/src/main/java/com/example/solidconnection/community/comment/service/CommentService.java index d6d1896d4..a10be68a9 100644 --- a/src/main/java/com/example/solidconnection/community/comment/service/CommentService.java +++ b/src/main/java/com/example/solidconnection/community/comment/service/CommentService.java @@ -40,7 +40,7 @@ public class CommentService { public List findCommentsByPostId(long siteUserId, Long postId) { SiteUser siteUser = siteUserRepository.findById(siteUserId) .orElseThrow(() -> new CustomException(USER_NOT_FOUND)); - List allComments = commentRepository.findCommentTreeByPostId(postId, siteUserId); + List allComments = commentRepository.findCommentTreeByPostIdExcludingBlockedUsers(postId, siteUserId); List filteredComments = filterCommentsByDeletionRules(allComments); Set userIds = filteredComments.stream() From a4a6e5f251bb2aa77fecf6f4a33eedbdb10b6d9e Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Sat, 27 Sep 2025 22:24:29 +0900 Subject: [PATCH 14/15] =?UTF-8?q?style:=20=EA=B0=9C=ED=96=89=20=EC=BB=A8?= =?UTF-8?q?=EB=B2=A4=EC=85=98=20=EC=A4=80=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/comment/service/CommentServiceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java b/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java index b96808390..63a824e53 100644 --- a/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java +++ b/src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java @@ -215,8 +215,8 @@ class 댓글_조회_테스트 { .containsExactly(parentComment1.getId(), childComment1.getId()), () -> assertThat(responses) .extracting(PostFindCommentResponse::id) - .doesNotContain(childComment2.getId(), parentCommen2.getId(), childComment3.getId(), childComment4.getId())) - ; + .doesNotContain(childComment2.getId(), parentCommen2.getId(), childComment3.getId(), childComment4.getId()) + ); } } From d6331ba0c93de4c2744774f3f3de4ddd30e27a81 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 Date: Sat, 27 Sep 2025 22:24:55 +0900 Subject: [PATCH 15/15] =?UTF-8?q?refactor:=20validated=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 불필요한 지역변수 제거 --- .../community/post/service/PostQueryService.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java b/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java index 7097d579f..9602cd454 100644 --- a/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java +++ b/src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java @@ -67,10 +67,7 @@ public PostFindResponse findPostById(long siteUserId, Long postId) { .orElseThrow(() -> new CustomException(USER_NOT_FOUND)); Post post = postRepository.getByIdUsingEntityGraph(postId); - boolean isBlockedByMe = userBlockRepository.existsByBlockerIdAndBlockedId(siteUserId, post.getSiteUserId()); - if (isBlockedByMe) { - throw new CustomException(ACCESS_DENIED); - } + validatedIsBlockedByMe(post, siteUser); Boolean isOwner = getIsOwner(post, siteUser); Boolean isLiked = getIsLiked(post, siteUser); @@ -125,4 +122,10 @@ private List getPostListByPostCategory(List postList, PostCategory p .filter(post -> post.getCategory().equals(postCategory)) .collect(Collectors.toList()); } + + private void validatedIsBlockedByMe(Post post, SiteUser siteUser) { + if (userBlockRepository.existsByBlockerIdAndBlockedId(siteUser.getId(), post.getSiteUserId())) { + throw new CustomException(ACCESS_DENIED); + } + } }