From d633b80d0238118cbddbcc95e93f3a8277bef8e4 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 2 Oct 2025 10:35:55 +0900 Subject: [PATCH 1/8] =?UTF-8?q?refactor:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/application/MyPagePostManager.java | 98 +++++++++++++++++++ .../post/application/PostQueryService.java | 76 +------------- 2 files changed, 101 insertions(+), 73 deletions(-) create mode 100644 src/main/java/com/chooz/post/application/MyPagePostManager.java diff --git a/src/main/java/com/chooz/post/application/MyPagePostManager.java b/src/main/java/com/chooz/post/application/MyPagePostManager.java new file mode 100644 index 0000000..47bd914 --- /dev/null +++ b/src/main/java/com/chooz/post/application/MyPagePostManager.java @@ -0,0 +1,98 @@ +package com.chooz.post.application; + +import com.chooz.common.dto.CursorBasePaginatedResponse; +import com.chooz.post.application.dto.PollChoiceVoteInfo; +import com.chooz.post.application.dto.PostWithVoteCount; +import com.chooz.post.domain.PollChoiceRepository; +import com.chooz.post.domain.PostRepository; +import com.chooz.post.presentation.dto.MostVotedPollChoiceDto; +import com.chooz.post.presentation.dto.MyPagePostResponse; +import com.chooz.vote.application.RatioCalculator; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.SliceImpl; +import org.springframework.stereotype.Component; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +public class MyPagePostManager { + + private final PostRepository postRepository; + private final PollChoiceRepository pollChoiceRepository; + private final RatioCalculator ratioCalculator; + + public CursorBasePaginatedResponse getUserPosts(Long userId, Long cursor, Pageable pageable) { + Slice postSlice = postRepository.findPostsWithVoteCountByUserId(userId, cursor, pageable); + + return getMyPageCursoredResponse(postSlice); + } + + public CursorBasePaginatedResponse getVotedPosts(Long userId, Long cursor, Pageable pageable) { + Slice postSlice = postRepository.findVotedPostsWithVoteCount(userId, cursor, pageable); + + return getMyPageCursoredResponse(postSlice); + } + + private CursorBasePaginatedResponse getMyPageCursoredResponse(Slice postSlice) { + if (postSlice.isEmpty()) { + return CursorBasePaginatedResponse.of(new SliceImpl<>( + List.of(), + postSlice.getPageable(), + false + )); + } + + Map mostVotedPollChoiceByPostId = getMostVotedPollChoiceByPostId(getPostIds(postSlice)); + + List response = getMyPagePostResponses(postSlice, mostVotedPollChoiceByPostId); + + return CursorBasePaginatedResponse.of(new SliceImpl<>( + response, + postSlice.getPageable(), + postSlice.hasNext() + )); + } + + private List getMyPagePostResponses( + Slice postSlice, + Map mostVotedPollChoiceByPostId + ) { + return postSlice.getContent().stream() + .map(postWithVoteCount -> { + var pollChoiceVoteInfo = mostVotedPollChoiceByPostId.get(postWithVoteCount.post().getId()); + var mostVotedPollChoiceInfo = MostVotedPollChoiceDto.of( + pollChoiceVoteInfo, + ratioCalculator.calculate(postWithVoteCount.voteCount(), pollChoiceVoteInfo.voteCounts()) + ); + return MyPagePostResponse.of(postWithVoteCount, mostVotedPollChoiceInfo); + }) + .toList(); + } + + private Map getMostVotedPollChoiceByPostId(List postIds) { + List pollChoiceWithVoteInfo = pollChoiceRepository.findPollChoiceWithVoteInfo(postIds); + return pollChoiceWithVoteInfo.stream() + .collect(Collectors.groupingBy( + PollChoiceVoteInfo::postId, + Collectors.collectingAndThen( + Collectors.toList(), + choices -> choices.stream() + .max(Comparator.comparing(PollChoiceVoteInfo::voteCounts)) + .orElse(null) + ) + )); + } + + private List getPostIds(Slice postSlice) { + return postSlice.getContent() + .stream() + .map(postWithVoteCount -> postWithVoteCount.post().getId()) + .toList(); + } +} diff --git a/src/main/java/com/chooz/post/application/PostQueryService.java b/src/main/java/com/chooz/post/application/PostQueryService.java index 41d3974..15c4e89 100644 --- a/src/main/java/com/chooz/post/application/PostQueryService.java +++ b/src/main/java/com/chooz/post/application/PostQueryService.java @@ -4,23 +4,19 @@ import com.chooz.common.dto.CursorBasePaginatedResponse; import com.chooz.common.exception.BadRequestException; import com.chooz.common.exception.ErrorCode; -import com.chooz.post.application.dto.PollChoiceVoteInfo; import com.chooz.post.application.dto.PostWithVoteCount; import com.chooz.post.domain.PollChoice; -import com.chooz.post.domain.PollChoiceRepository; import com.chooz.post.domain.Post; import com.chooz.post.domain.PostRepository; import com.chooz.post.presentation.UpdatePostResponse; import com.chooz.post.presentation.dto.AuthorDto; import com.chooz.post.application.dto.FeedDto; import com.chooz.post.presentation.dto.FeedResponse; -import com.chooz.post.presentation.dto.MostVotedPollChoiceDto; import com.chooz.post.presentation.dto.MyPagePostResponse; import com.chooz.post.presentation.dto.PollChoiceVoteResponse; import com.chooz.post.presentation.dto.PostResponse; import com.chooz.user.domain.User; import com.chooz.user.domain.UserRepository; -import com.chooz.vote.application.RatioCalculator; import com.chooz.vote.domain.Vote; import com.chooz.vote.domain.VoteRepository; import lombok.RequiredArgsConstructor; @@ -31,11 +27,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.Comparator; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; @Service @Transactional(readOnly = true) @@ -43,11 +35,10 @@ public class PostQueryService { private final PostRepository postRepository; - private final PollChoiceRepository pollChoiceRepository; private final UserRepository userRepository; private final VoteRepository voteRepository; private final CommentRepository commentRepository; - private final RatioCalculator ratioCalculator; + private final MyPagePostManager myPagePostManager; public PostResponse findByShareUrl(Long userId, String shareUrl) { Post post = postRepository.findByShareUrlFetchPollChoices(shareUrl) @@ -101,72 +92,11 @@ private Long getVoteId(List voteList, Long pollChoiceId, Long userId) { } public CursorBasePaginatedResponse findUserPosts(Long userId, Long cursor, int size) { - Slice postSlice = postRepository.findPostsWithVoteCountByUserId(userId, cursor, Pageable.ofSize(size)); - - return getCursorPaginatedResponse(postSlice); + return myPagePostManager.getUserPosts(userId, cursor, Pageable.ofSize(size)); } public CursorBasePaginatedResponse findVotedPosts(Long userId, Long cursor, int size) { - Slice postSlice = postRepository.findVotedPostsWithVoteCount(userId, cursor, Pageable.ofSize(size)); - - return getCursorPaginatedResponse(postSlice); - } - - private CursorBasePaginatedResponse getCursorPaginatedResponse(Slice postSlice) { - if (postSlice.isEmpty()) { - return CursorBasePaginatedResponse.of(new SliceImpl<>( - List.of(), - postSlice.getPageable(), - false - )); - } - - Map mostVotedPollChoiceByPostId = getMostVotedPollChoiceByPostId(getPostIds(postSlice)); - - List response = getMyPagePostResponses(postSlice, mostVotedPollChoiceByPostId); - - return CursorBasePaginatedResponse.of(new SliceImpl<>( - response, - postSlice.getPageable(), - postSlice.hasNext() - )); - } - - private List getMyPagePostResponses( - Slice postSlice, - Map mostVotedPollChoiceByPostId - ) { - return postSlice.getContent().stream() - .map(postWithVoteCount -> { - var pollChoiceVoteInfo = mostVotedPollChoiceByPostId.get(postWithVoteCount.post().getId()); - var mostVotedPollChoiceInfo = MostVotedPollChoiceDto.of( - pollChoiceVoteInfo, - ratioCalculator.calculate(postWithVoteCount.voteCount(), pollChoiceVoteInfo.voteCounts()) - ); - return MyPagePostResponse.of(postWithVoteCount, mostVotedPollChoiceInfo); - }) - .toList(); - } - - private Map getMostVotedPollChoiceByPostId(List postIds) { - List pollChoiceWithVoteInfo = pollChoiceRepository.findPollChoiceWithVoteInfo(postIds); - return pollChoiceWithVoteInfo.stream() - .collect(Collectors.groupingBy( - PollChoiceVoteInfo::postId, - Collectors.collectingAndThen( - Collectors.toList(), - choices -> choices.stream() - .max(Comparator.comparing(PollChoiceVoteInfo::voteCounts)) - .orElse(null) - ) - )); - } - - private List getPostIds(Slice postSlice) { - return postSlice.getContent() - .stream() - .map(postWithVoteCount -> postWithVoteCount.post().getId()) - .toList(); + return myPagePostManager.getVotedPosts(userId, cursor, Pageable.ofSize(size)); } public CursorBasePaginatedResponse findFeed(Long userId, Long cursor, int size) { From 73663f032a01c65a86f6642260f932fd00bcdf88 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 2 Oct 2025 11:58:13 +0900 Subject: [PATCH 2/8] =?UTF-8?q?fix:=20=EB=B9=84=EA=B3=B5=EA=B0=9C=20?= =?UTF-8?q?=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EC=97=90=20=EB=B3=B4=EC=9D=B4=EB=8A=94=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/application/MyPagePostManager.java | 52 +++++++------ .../post/application/PostQueryService.java | 18 ++++- .../chooz/post/application/PostService.java | 18 ++++- src/main/java/com/chooz/post/domain/Post.java | 5 ++ .../persistence/PostQueryDslRepository.java | 1 - .../post/presentation/PostController.java | 10 ++- .../application/CommentQueryServiceTest.java | 3 +- .../application/PostQueryServiceTest.java | 73 +++++++++++++++++-- .../post/presentation/PostControllerTest.java | 4 +- .../chooz/support/fixture/PostFixture.java | 4 +- .../vote/application/VoteServiceTest.java | 16 ++-- .../vote/application/VoteValidatorTest.java | 4 +- .../chooz/vote/domain/VoteRepositoryTest.java | 2 +- 13 files changed, 149 insertions(+), 61 deletions(-) diff --git a/src/main/java/com/chooz/post/application/MyPagePostManager.java b/src/main/java/com/chooz/post/application/MyPagePostManager.java index 47bd914..3bbc826 100644 --- a/src/main/java/com/chooz/post/application/MyPagePostManager.java +++ b/src/main/java/com/chooz/post/application/MyPagePostManager.java @@ -27,19 +27,19 @@ public class MyPagePostManager { private final PollChoiceRepository pollChoiceRepository; private final RatioCalculator ratioCalculator; - public CursorBasePaginatedResponse getUserPosts(Long userId, Long cursor, Pageable pageable) { - Slice postSlice = postRepository.findPostsWithVoteCountByUserId(userId, cursor, pageable); + public CursorBasePaginatedResponse getUserPosts(Long userId, Long myPageUserId, Long cursor, Pageable pageable) { + Slice postSlice = postRepository.findPostsWithVoteCountByUserId(myPageUserId, cursor, pageable); - return getMyPageCursoredResponse(postSlice); + return getMyPageCursoredResponse(userId, postSlice); } - public CursorBasePaginatedResponse getVotedPosts(Long userId, Long cursor, Pageable pageable) { - Slice postSlice = postRepository.findVotedPostsWithVoteCount(userId, cursor, pageable); + public CursorBasePaginatedResponse getVotedPosts(Long userId, Long myPageUserId, Long cursor, Pageable pageable) { + Slice postSlice = postRepository.findVotedPostsWithVoteCount(myPageUserId, cursor, pageable); - return getMyPageCursoredResponse(postSlice); + return getMyPageCursoredResponse(userId, postSlice); } - private CursorBasePaginatedResponse getMyPageCursoredResponse(Slice postSlice) { + private CursorBasePaginatedResponse getMyPageCursoredResponse(Long userId, Slice postSlice) { if (postSlice.isEmpty()) { return CursorBasePaginatedResponse.of(new SliceImpl<>( List.of(), @@ -48,9 +48,10 @@ private CursorBasePaginatedResponse getMyPageCursoredRespons )); } - Map mostVotedPollChoiceByPostId = getMostVotedPollChoiceByPostId(getPostIds(postSlice)); + List postIds = getPostIds(postSlice); + Map mostVotedPollChoiceByPostId = getMostVotedPollChoiceByPostId(postIds); - List response = getMyPagePostResponses(postSlice, mostVotedPollChoiceByPostId); + List response = getMyPagePostResponses(userId, postSlice, mostVotedPollChoiceByPostId); return CursorBasePaginatedResponse.of(new SliceImpl<>( response, @@ -59,11 +60,28 @@ private CursorBasePaginatedResponse getMyPageCursoredRespons )); } + private Map getMostVotedPollChoiceByPostId(List postIds) { + List pollChoiceWithVoteInfo = pollChoiceRepository.findPollChoiceWithVoteInfo(postIds); + return pollChoiceWithVoteInfo.stream() + .collect(Collectors.groupingBy( + PollChoiceVoteInfo::postId, + Collectors.collectingAndThen( + Collectors.toList(), + choices -> choices.stream() + .max(Comparator.comparing(PollChoiceVoteInfo::voteCounts)) + .orElse(null) + ) + )); + } + private List getMyPagePostResponses( + Long userId, Slice postSlice, Map mostVotedPollChoiceByPostId ) { - return postSlice.getContent().stream() + return postSlice.getContent() + .stream() + .filter(postWithVoteCount -> postWithVoteCount.post().isRevealable(userId)) .map(postWithVoteCount -> { var pollChoiceVoteInfo = mostVotedPollChoiceByPostId.get(postWithVoteCount.post().getId()); var mostVotedPollChoiceInfo = MostVotedPollChoiceDto.of( @@ -75,20 +93,6 @@ private List getMyPagePostResponses( .toList(); } - private Map getMostVotedPollChoiceByPostId(List postIds) { - List pollChoiceWithVoteInfo = pollChoiceRepository.findPollChoiceWithVoteInfo(postIds); - return pollChoiceWithVoteInfo.stream() - .collect(Collectors.groupingBy( - PollChoiceVoteInfo::postId, - Collectors.collectingAndThen( - Collectors.toList(), - choices -> choices.stream() - .max(Comparator.comparing(PollChoiceVoteInfo::voteCounts)) - .orElse(null) - ) - )); - } - private List getPostIds(Slice postSlice) { return postSlice.getContent() .stream() diff --git a/src/main/java/com/chooz/post/application/PostQueryService.java b/src/main/java/com/chooz/post/application/PostQueryService.java index 15c4e89..25943ae 100644 --- a/src/main/java/com/chooz/post/application/PostQueryService.java +++ b/src/main/java/com/chooz/post/application/PostQueryService.java @@ -91,12 +91,22 @@ private Long getVoteId(List voteList, Long pollChoiceId, Long userId) { .orElse(null); } - public CursorBasePaginatedResponse findUserPosts(Long userId, Long cursor, int size) { - return myPagePostManager.getUserPosts(userId, cursor, Pageable.ofSize(size)); + public CursorBasePaginatedResponse findUserPosts( + Long userId, + Long myPageUserId, + Long cursor, + int size + ) { + return myPagePostManager.getUserPosts(userId, myPageUserId, cursor, Pageable.ofSize(size)); } - public CursorBasePaginatedResponse findVotedPosts(Long userId, Long cursor, int size) { - return myPagePostManager.getVotedPosts(userId, cursor, Pageable.ofSize(size)); + public CursorBasePaginatedResponse findVotedPosts( + Long userId, + Long myPageUserId, + Long cursor, + int size + ) { + return myPagePostManager.getVotedPosts(userId, myPageUserId, cursor, Pageable.ofSize(size)); } public CursorBasePaginatedResponse findFeed(Long userId, Long cursor, int size) { diff --git a/src/main/java/com/chooz/post/application/PostService.java b/src/main/java/com/chooz/post/application/PostService.java index cd3ccf8..c7cb2ae 100644 --- a/src/main/java/com/chooz/post/application/PostService.java +++ b/src/main/java/com/chooz/post/application/PostService.java @@ -44,12 +44,22 @@ public PostResponse findById(Long userId, Long postId) { return postQueryService.findById(userId, postId); } - public CursorBasePaginatedResponse findUserPosts(Long userId, Long cursor, int size) { - return postQueryService.findUserPosts(userId, cursor, size); + public CursorBasePaginatedResponse findUserPosts( + Long userId, + Long myPageUserId, + Long cursor, + int size + ) { + return postQueryService.findUserPosts(userId, myPageUserId, cursor, size); } - public CursorBasePaginatedResponse findVotedPosts(Long userId, Long cursor, int size) { - return postQueryService.findVotedPosts(userId, cursor, size); + public CursorBasePaginatedResponse findVotedPosts( + Long userId, + Long myPageUserId, + Long cursor, + int size + ) { + return postQueryService.findVotedPosts(userId, myPageUserId, cursor, size); } public PostResponse findByShareUrl(Long userId, String shareUrl) { diff --git a/src/main/java/com/chooz/post/domain/Post.java b/src/main/java/com/chooz/post/domain/Post.java index 97440a3..595774b 100644 --- a/src/main/java/com/chooz/post/domain/Post.java +++ b/src/main/java/com/chooz/post/domain/Post.java @@ -222,4 +222,9 @@ public void delete(Long userId) { } this.delete(); } + + public boolean isRevealable(Long userId) { + return this.pollOption.getScope().equals(Scope.PUBLIC) || + this.userId.equals(userId); + } } diff --git a/src/main/java/com/chooz/post/persistence/PostQueryDslRepository.java b/src/main/java/com/chooz/post/persistence/PostQueryDslRepository.java index dc3d2d1..6e97d9a 100644 --- a/src/main/java/com/chooz/post/persistence/PostQueryDslRepository.java +++ b/src/main/java/com/chooz/post/persistence/PostQueryDslRepository.java @@ -93,7 +93,6 @@ public Slice findFeed(Long postId, Pageable pageable) { .from(post) .innerJoin(user).on(post.userId.eq(user.id)) .where( - post.deleted.isFalse(), post.pollOption.scope.eq(Scope.PUBLIC), cursor(postId, post.id), post.deleted.isFalse() diff --git a/src/main/java/com/chooz/post/presentation/PostController.java b/src/main/java/com/chooz/post/presentation/PostController.java index 565d321..23a2f02 100644 --- a/src/main/java/com/chooz/post/presentation/PostController.java +++ b/src/main/java/com/chooz/post/presentation/PostController.java @@ -102,20 +102,22 @@ public ResponseEntity deletePost( @GetMapping("/users/{userId}") public ResponseEntity> findMyPosts( - @PathVariable("userId") Long userId, + @PathVariable("userId") Long myPageUserId, + @AuthenticationPrincipal UserInfo userInfo, @RequestParam(name = "cursor", required = false) @Min(0) Long cursor, @RequestParam(name = "size", required = false, defaultValue = "10") @Min(1) int size ) { - return ResponseEntity.ok(postService.findUserPosts(userId, cursor, size)); + return ResponseEntity.ok(postService.findUserPosts(userInfo.userId(), myPageUserId, cursor, size)); } @GetMapping("/users/{userId}/voted") public ResponseEntity> findVotedPosts( - @PathVariable("userId") Long userId, + @PathVariable("userId") Long myPageUserId, + @AuthenticationPrincipal UserInfo userInfo, @RequestParam(name = "cursor", required = false) @Min(0) Long cursor, @RequestParam(name = "size", required = false, defaultValue = "10") @Min(1) int size ) { - return ResponseEntity.ok(postService.findVotedPosts(userId, cursor, size)); + return ResponseEntity.ok(postService.findVotedPosts(userInfo.userId(), myPageUserId, cursor, size)); } @GetMapping("/feed") diff --git a/src/test/java/com/chooz/comment/application/CommentQueryServiceTest.java b/src/test/java/com/chooz/comment/application/CommentQueryServiceTest.java index 65b4388..6960b37 100644 --- a/src/test/java/com/chooz/comment/application/CommentQueryServiceTest.java +++ b/src/test/java/com/chooz/comment/application/CommentQueryServiceTest.java @@ -20,7 +20,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.annotation.Transactional; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -122,7 +121,7 @@ void findComments20Cursor11() { void findCommentsCloseCommentActive() { // given User user = userRepository.save(UserFixture.createDefaultUser()); - PollOption pollOption = PostFixture.createPollOptionBuilder() + PollOption pollOption = PostFixture.pollOptionBuilder() .commentActive(CommentActive.CLOSED).build(); Post post = postRepository.save(PostFixture.createPostBuilder() .userId(user.getId()) diff --git a/src/test/java/com/chooz/post/application/PostQueryServiceTest.java b/src/test/java/com/chooz/post/application/PostQueryServiceTest.java index 9a0973e..8b0b55a 100644 --- a/src/test/java/com/chooz/post/application/PostQueryServiceTest.java +++ b/src/test/java/com/chooz/post/application/PostQueryServiceTest.java @@ -100,7 +100,7 @@ void findUserPosts() throws Exception { int size = 10; //when - var response = postService.findUserPosts(user.getId(), null, size); + var response = postService.findUserPosts(user.getId(), user.getId(), null, size); //then assertAll( @@ -119,7 +119,7 @@ void findUserPosts2() throws Exception { int size = 10; //when - var response = postService.findUserPosts(user.getId(), posts.get(3).getId(), size); + var response = postService.findUserPosts(user.getId(), user.getId(), posts.get(3).getId(), size); //then assertAll( @@ -150,7 +150,7 @@ void findUserPosts_multiple() { voteRepository.save(VoteFixture.createDefaultVote(user.getId(), post2.getId(), post2.getPollChoices().get(0).getId())); //when - var response = postService.findUserPosts(user.getId(), null, 10); + var response = postService.findUserPosts(user.getId(), user.getId(), null, 10); //then List data = response.data(); @@ -194,7 +194,7 @@ void findUserPosts_multiple2() { voteRepository.save(VoteFixture.createDefaultVote(user.getId(), post.getId(), post.getPollChoices().get(0).getId())); //when - var response = postService.findUserPosts(user.getId(), null, 10); + var response = postService.findUserPosts(user.getId(), user.getId(), null, 10); //then List data = response.data(); @@ -225,7 +225,7 @@ void findVotedPosts() throws Exception { int size = 10; //when - var response = postService.findVotedPosts(user.getId(), null, size); + var response = postService.findVotedPosts(user.getId(), user.getId(), null, size); //then int 전체_15개에서_맨_마지막_데이터_인덱스 = posts.size() - size; @@ -258,7 +258,7 @@ void findVotedPosts_multiple() { voteService.vote(user.getId(), post2.getId(), List.of()); //when - var response = postService.findVotedPosts(user.getId(), null, 10); + var response = postService.findVotedPosts(user.getId(), user.getId(), null, 10); //then List data = response.data(); @@ -294,7 +294,7 @@ void findVotedPosts_multiple2() { voteRepository.save(VoteFixture.createDefaultVote(user.getId(), post.getId(), post.getPollChoices().get(0).getId())); //when - var response = postService.findVotedPosts(user.getId(), null, 10); + var response = postService.findVotedPosts(user.getId(), user.getId(), null, 10); //then List data = response.data(); @@ -312,6 +312,65 @@ void findVotedPosts_multiple2() { ); } + @Test + @DisplayName("마이페이지 게시글 공개 범위 - 본인인 경우") + void scope_author() { + //given + User user = userRepository.save(UserFixture.createDefaultUser()); + Post publicPost = postRepository.save(PostFixture.createPostBuilder() + .userId(user.getId()) + .pollOption(PostFixture.pollOptionBuilder() + .scope(Scope.PUBLIC) + .build()) + .build()); + Post privatePost = postRepository.save(PostFixture.createPostBuilder() + .userId(user.getId()) + .pollOption(PostFixture.pollOptionBuilder() + .scope(Scope.PRIVATE) + .build()) + .build()); + //유저1 본인 게시글 1 2 투표 + voteRepository.save(VoteFixture.createDefaultVote(user.getId(), publicPost.getId(), publicPost.getPollChoices().get(0).getId())); + + //when + var response1 = postService.findVotedPosts(user.getId(), user.getId(), null, 10); + var response2 = postService.findUserPosts(user.getId(), user.getId(), null, 10); + + //then + assertThat(response1.data()).hasSize(1); + assertThat(response2.data()).hasSize(2); + } + + @Test + @DisplayName("마이페이지 게시글 공개 범위 - 다른 사람인 경우") + void scope_otherUser() { + //given + User author = userRepository.save(UserFixture.createDefaultUser()); + User user = userRepository.save(UserFixture.createDefaultUser()); + Post publicPost = postRepository.save(PostFixture.createPostBuilder() + .userId(author.getId()) + .pollOption(PostFixture.pollOptionBuilder() + .scope(Scope.PUBLIC) + .build()) + .build()); + Post privatePost = postRepository.save(PostFixture.createPostBuilder() + .userId(author.getId()) + .pollOption(PostFixture.pollOptionBuilder() + .scope(Scope.PRIVATE) + .build()) + .build()); + //유저1 본인 게시글 1 2 투표 + voteRepository.save(VoteFixture.createDefaultVote(author.getId(), privatePost.getId(), privatePost.getPollChoices().get(0).getId())); + + //when + var response1 = postService.findVotedPosts(user.getId(), author.getId(), null, 10); + var response2 = postService.findUserPosts(user.getId(), author.getId(), null, 10); + + //then + assertThat(response1.data()).hasSize(0); + assertThat(response2.data()).hasSize(1); + } + @Test @DisplayName("피드 조회 - 내 게시글 1개, 공개 게시글 15개, 투표 10개, 댓글 20개") void findFeed() throws Exception { diff --git a/src/test/java/com/chooz/post/presentation/PostControllerTest.java b/src/test/java/com/chooz/post/presentation/PostControllerTest.java index 907238a..237bcef 100644 --- a/src/test/java/com/chooz/post/presentation/PostControllerTest.java +++ b/src/test/java/com/chooz/post/presentation/PostControllerTest.java @@ -312,7 +312,7 @@ void findMyPost() throws Exception { ) ) ); - given(postService.findUserPosts(1L, null, 10)) + given(postService.findUserPosts(1L, 1L, null, 10)) .willReturn(response); //when then @@ -414,7 +414,7 @@ void findVotedPost() throws Exception { ) ) ); - given(postService.findVotedPosts(1L, null, 10)) + given(postService.findVotedPosts(1L, 1L, null, 10)) .willReturn(response); //when then diff --git a/src/test/java/com/chooz/support/fixture/PostFixture.java b/src/test/java/com/chooz/support/fixture/PostFixture.java index 25958fd..391adb7 100644 --- a/src/test/java/com/chooz/support/fixture/PostFixture.java +++ b/src/test/java/com/chooz/support/fixture/PostFixture.java @@ -67,7 +67,7 @@ public static CloseOption voterCloseOption(int maxVoterCount) { public static final CloseOption SELF_CREATE_OPTION = new CloseOption(CloseType.SELF, null, null); - public static PollOption.PollOptionBuilder createPollOptionBuilder() { + public static PollOption.PollOptionBuilder pollOptionBuilder() { return PollOption.builder() .pollType(PollType.SINGLE) .scope(Scope.PUBLIC) @@ -75,7 +75,7 @@ public static PollOption.PollOptionBuilder createPollOptionBuilder() { } public static PollOption multiplePollOption() { - return createPollOptionBuilder() + return pollOptionBuilder() .pollType(PollType.MULTIPLE) .build(); } diff --git a/src/test/java/com/chooz/vote/application/VoteServiceTest.java b/src/test/java/com/chooz/vote/application/VoteServiceTest.java index b541ae3..ce7e408 100644 --- a/src/test/java/com/chooz/vote/application/VoteServiceTest.java +++ b/src/test/java/com/chooz/vote/application/VoteServiceTest.java @@ -51,7 +51,7 @@ void singleVote() { Post post = postRepository.save( PostFixture.createPostBuilder() .pollOption( - PostFixture.createPollOptionBuilder() + PostFixture.pollOptionBuilder() .pollType(PollType.SINGLE) .build()) .build() @@ -79,7 +79,7 @@ void singleVote_change() { Post post = postRepository.save( PostFixture.createPostBuilder() .pollOption( - PostFixture.createPollOptionBuilder() + PostFixture.pollOptionBuilder() .pollType(PollType.SINGLE) .build()) .build()); @@ -103,7 +103,7 @@ void singleVote_cancelByEmptyChoice() { User user = userRepository.save(UserFixture.createDefaultUser()); Post post = postRepository.save( PostFixture.createPostBuilder() - .pollOption(PostFixture.createPollOptionBuilder().pollType(PollType.SINGLE).build()) + .pollOption(PostFixture.pollOptionBuilder().pollType(PollType.SINGLE).build()) .build() ); Long pollChoiceId = post.getPollChoices().getFirst().getId(); @@ -125,7 +125,7 @@ void multipleVote_atOnce() { Post post = postRepository.save( PostFixture.createPostBuilder() .pollOption( - PostFixture.createPollOptionBuilder() + PostFixture.pollOptionBuilder() .pollType(PollType.MULTIPLE) .build()) .build() @@ -156,7 +156,7 @@ void multipleVote_changeChoices() { PostFixture.createPollChoice(), PostFixture.createPollChoice()) ) - .pollOption(PostFixture.createPollOptionBuilder() + .pollOption(PostFixture.pollOptionBuilder() .pollType(PollType.MULTIPLE) .build()) .build() @@ -227,7 +227,7 @@ void singleVote_multipleChoicesException() { Post post = postRepository.save( PostFixture.createPostBuilder() .pollOption( - PostFixture.createPollOptionBuilder() + PostFixture.pollOptionBuilder() .pollType(PollType.SINGLE) .build()) .build() @@ -248,7 +248,7 @@ void multipleVote_duplicateChoicesException() { Post post = postRepository.save( PostFixture.createPostBuilder() .pollOption( - PostFixture.createPollOptionBuilder() + PostFixture.pollOptionBuilder() .pollType(PollType.MULTIPLE) .build()) .build() @@ -269,7 +269,7 @@ void multipleVote_cancelAllChoices() { User user = userRepository.save(UserFixture.createDefaultUser()); Post post = postRepository.save( PostFixture.createPostBuilder() - .pollOption(PostFixture.createPollOptionBuilder().pollType(PollType.MULTIPLE).build()) + .pollOption(PostFixture.pollOptionBuilder().pollType(PollType.MULTIPLE).build()) .build() ); List pollChoices = post.getPollChoices(); diff --git a/src/test/java/com/chooz/vote/application/VoteValidatorTest.java b/src/test/java/com/chooz/vote/application/VoteValidatorTest.java index b558a43..b3c2566 100644 --- a/src/test/java/com/chooz/vote/application/VoteValidatorTest.java +++ b/src/test/java/com/chooz/vote/application/VoteValidatorTest.java @@ -105,7 +105,7 @@ void validateSingleVote_multipleChoices() { // given Post post = PostFixture.createPostBuilder() .pollOption( - PostFixture.createPollOptionBuilder() + PostFixture.pollOptionBuilder() .pollType(PollType.SINGLE) .build() ) @@ -124,7 +124,7 @@ void validateMultipleVotes_duplicateChoices() { // given Post post = PostFixture.createPostBuilder() .pollOption( - PostFixture.createPollOptionBuilder() + PostFixture.pollOptionBuilder() .pollType(PollType.MULTIPLE) .build() ) diff --git a/src/test/java/com/chooz/vote/domain/VoteRepositoryTest.java b/src/test/java/com/chooz/vote/domain/VoteRepositoryTest.java index 6b5a016..a6587c2 100644 --- a/src/test/java/com/chooz/vote/domain/VoteRepositoryTest.java +++ b/src/test/java/com/chooz/vote/domain/VoteRepositoryTest.java @@ -53,7 +53,7 @@ void countVoterByPostId_multiple() throws Exception { Post post = postRepository.save(PostFixture.createPostBuilder() .userId(user1.getId()) .pollOption( - PostFixture.createPollOptionBuilder() + PostFixture.pollOptionBuilder() .pollType(PollType.MULTIPLE) .build()) .build()); From fa2519ad276b3aaf3ce3fcbfd1be4f31c4bc8518 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 2 Oct 2025 11:58:46 +0900 Subject: [PATCH 3/8] =?UTF-8?q?refactor:=20dto=20=EC=98=AC=EB=B0=94?= =?UTF-8?q?=EB=A5=B8=20=ED=8C=A8=ED=82=A4=EC=A7=80=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/chooz/post/application/PostQueryService.java | 4 +--- src/main/java/com/chooz/post/application/PostService.java | 2 +- .../java/com/chooz/post/presentation/PostController.java | 1 + .../post/presentation/{ => dto}/UpdatePostResponse.java | 5 +---- .../java/com/chooz/post/presentation/PostControllerTest.java | 1 + 5 files changed, 5 insertions(+), 8 deletions(-) rename src/main/java/com/chooz/post/presentation/{ => dto}/UpdatePostResponse.java (89%) diff --git a/src/main/java/com/chooz/post/application/PostQueryService.java b/src/main/java/com/chooz/post/application/PostQueryService.java index 25943ae..ed481fd 100644 --- a/src/main/java/com/chooz/post/application/PostQueryService.java +++ b/src/main/java/com/chooz/post/application/PostQueryService.java @@ -4,11 +4,10 @@ import com.chooz.common.dto.CursorBasePaginatedResponse; import com.chooz.common.exception.BadRequestException; import com.chooz.common.exception.ErrorCode; -import com.chooz.post.application.dto.PostWithVoteCount; import com.chooz.post.domain.PollChoice; import com.chooz.post.domain.Post; import com.chooz.post.domain.PostRepository; -import com.chooz.post.presentation.UpdatePostResponse; +import com.chooz.post.presentation.dto.UpdatePostResponse; import com.chooz.post.presentation.dto.AuthorDto; import com.chooz.post.application.dto.FeedDto; import com.chooz.post.presentation.dto.FeedResponse; @@ -23,7 +22,6 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; -import org.springframework.data.domain.SliceImpl; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/com/chooz/post/application/PostService.java b/src/main/java/com/chooz/post/application/PostService.java index c7cb2ae..9ff3c7c 100644 --- a/src/main/java/com/chooz/post/application/PostService.java +++ b/src/main/java/com/chooz/post/application/PostService.java @@ -1,7 +1,7 @@ package com.chooz.post.application; import com.chooz.common.dto.CursorBasePaginatedResponse; -import com.chooz.post.presentation.UpdatePostResponse; +import com.chooz.post.presentation.dto.UpdatePostResponse; import com.chooz.post.presentation.dto.CreatePostRequest; import com.chooz.post.presentation.dto.CreatePostResponse; import com.chooz.post.presentation.dto.FeedResponse; diff --git a/src/main/java/com/chooz/post/presentation/PostController.java b/src/main/java/com/chooz/post/presentation/PostController.java index 23a2f02..4ceab6a 100644 --- a/src/main/java/com/chooz/post/presentation/PostController.java +++ b/src/main/java/com/chooz/post/presentation/PostController.java @@ -9,6 +9,7 @@ import com.chooz.post.presentation.dto.UpdatePostRequest; import com.chooz.post.presentation.dto.MyPagePostResponse; import com.chooz.post.presentation.dto.FeedResponse; +import com.chooz.post.presentation.dto.UpdatePostResponse; import jakarta.validation.Valid; import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/chooz/post/presentation/UpdatePostResponse.java b/src/main/java/com/chooz/post/presentation/dto/UpdatePostResponse.java similarity index 89% rename from src/main/java/com/chooz/post/presentation/UpdatePostResponse.java rename to src/main/java/com/chooz/post/presentation/dto/UpdatePostResponse.java index 9581116..e3dd4b1 100644 --- a/src/main/java/com/chooz/post/presentation/UpdatePostResponse.java +++ b/src/main/java/com/chooz/post/presentation/dto/UpdatePostResponse.java @@ -1,12 +1,9 @@ -package com.chooz.post.presentation; +package com.chooz.post.presentation.dto; import com.chooz.post.domain.CloseOption; import com.chooz.post.domain.PollOption; import com.chooz.post.domain.Post; import com.chooz.post.domain.Status; -import com.chooz.post.presentation.dto.CloseOptionDto; -import com.chooz.post.presentation.dto.PollChoiceResponse; -import com.chooz.post.presentation.dto.PollOptionDto; import java.time.LocalDateTime; import java.util.List; diff --git a/src/test/java/com/chooz/post/presentation/PostControllerTest.java b/src/test/java/com/chooz/post/presentation/PostControllerTest.java index 237bcef..e0ce7f3 100644 --- a/src/test/java/com/chooz/post/presentation/PostControllerTest.java +++ b/src/test/java/com/chooz/post/presentation/PostControllerTest.java @@ -19,6 +19,7 @@ import com.chooz.post.presentation.dto.PollOptionDto; import com.chooz.post.presentation.dto.PostResponse; import com.chooz.post.presentation.dto.UpdatePostRequest; +import com.chooz.post.presentation.dto.UpdatePostResponse; import com.chooz.support.RestDocsTest; import com.chooz.support.WithMockUserInfo; import org.junit.jupiter.api.DisplayName; From 3ed8df8350afe6663d74dfdbe4ff236b56730e48 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 2 Oct 2025 14:22:48 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EA=B3=B5=EC=9C=A0=20=EA=B4=80=EB=A0=A8=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/chooz/common/exception/ErrorCode.java | 1 + .../post/application/PostQueryService.java | 7 +- .../chooz/post/application/PostService.java | 4 +- src/main/java/com/chooz/post/domain/Post.java | 4 + .../post/presentation/PostController.java | 3 +- .../application/PostQueryServiceTest.java | 79 +++++++++++++++++-- .../java/com/chooz/post/domain/PostTest.java | 53 +++++++++++++ .../post/presentation/PostControllerTest.java | 8 +- 8 files changed, 148 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/chooz/common/exception/ErrorCode.java b/src/main/java/com/chooz/common/exception/ErrorCode.java index 72dad14..9d83b48 100644 --- a/src/main/java/com/chooz/common/exception/ErrorCode.java +++ b/src/main/java/com/chooz/common/exception/ErrorCode.java @@ -49,6 +49,7 @@ public enum ErrorCode { INVALID_ONBOARDING_STEP("유효하지 않은 온보딩 단계."), NICKNAME_LENGTH_EXCEEDED("닉네임 길이 초과"), NOTIFICATION_NOT_FOUND("존재하지 않는 알림 입니다."), + POST_NOT_REVEALABLE("공개 불가능한 게시글입니다."), //401 EXPIRED_TOKEN("토큰이 만료됐습니다."), diff --git a/src/main/java/com/chooz/post/application/PostQueryService.java b/src/main/java/com/chooz/post/application/PostQueryService.java index ed481fd..06f3d04 100644 --- a/src/main/java/com/chooz/post/application/PostQueryService.java +++ b/src/main/java/com/chooz/post/application/PostQueryService.java @@ -18,6 +18,7 @@ import com.chooz.user.domain.UserRepository; import com.chooz.vote.domain.Vote; import com.chooz.vote.domain.VoteRepository; +import jakarta.annotation.Nullable; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -26,6 +27,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Optional; @Service @Transactional(readOnly = true) @@ -44,9 +46,12 @@ public PostResponse findByShareUrl(Long userId, String shareUrl) { return createPostResponse(userId, post); } - public PostResponse findById(Long userId, Long postId) { + public PostResponse findById(Long userId, Long postId, @Nullable String shareKey) { Post post = postRepository.findByIdFetchPollChoices(postId) .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)); + if (!post.isRevealable(userId, shareKey)) { + throw new BadRequestException(ErrorCode.POST_NOT_REVEALABLE); + } return createPostResponse(userId, post); } diff --git a/src/main/java/com/chooz/post/application/PostService.java b/src/main/java/com/chooz/post/application/PostService.java index 9ff3c7c..af33302 100644 --- a/src/main/java/com/chooz/post/application/PostService.java +++ b/src/main/java/com/chooz/post/application/PostService.java @@ -40,8 +40,8 @@ public void update(Long userId, Long postId, UpdatePostRequest request) { postCommandService.update(userId, postId, request); } - public PostResponse findById(Long userId, Long postId) { - return postQueryService.findById(userId, postId); + public PostResponse findById(Long userId, Long postId, String shareKey) { + return postQueryService.findById(userId, postId, shareKey); } public CursorBasePaginatedResponse findUserPosts( diff --git a/src/main/java/com/chooz/post/domain/Post.java b/src/main/java/com/chooz/post/domain/Post.java index 595774b..4296bd7 100644 --- a/src/main/java/com/chooz/post/domain/Post.java +++ b/src/main/java/com/chooz/post/domain/Post.java @@ -227,4 +227,8 @@ public boolean isRevealable(Long userId) { return this.pollOption.getScope().equals(Scope.PUBLIC) || this.userId.equals(userId); } + + public boolean isRevealable(Long userId, String shareUrl) { + return isRevealable(userId) || this.shareUrl.equals(shareUrl); + } } diff --git a/src/main/java/com/chooz/post/presentation/PostController.java b/src/main/java/com/chooz/post/presentation/PostController.java index 4ceab6a..1c7b2e7 100644 --- a/src/main/java/com/chooz/post/presentation/PostController.java +++ b/src/main/java/com/chooz/post/presentation/PostController.java @@ -45,12 +45,13 @@ public ResponseEntity createPost( @GetMapping("/{postId}") public ResponseEntity findPostById( @PathVariable("postId") Long postId, + @RequestParam(value = "shareKey", required = false ) String shareKey, @AuthenticationPrincipal UserInfo userInfo ) { Long userId = Optional.ofNullable(userInfo) .map(UserInfo::userId) .orElse(null); - return ResponseEntity.ok(postService.findById(userId, postId)); + return ResponseEntity.ok(postService.findById(userId, postId, shareKey)); } @GetMapping("/shareUrl/{shareUrl}") diff --git a/src/test/java/com/chooz/post/application/PostQueryServiceTest.java b/src/test/java/com/chooz/post/application/PostQueryServiceTest.java index 8b0b55a..f6791a5 100644 --- a/src/test/java/com/chooz/post/application/PostQueryServiceTest.java +++ b/src/test/java/com/chooz/post/application/PostQueryServiceTest.java @@ -3,7 +3,14 @@ import com.chooz.comment.domain.Comment; import com.chooz.comment.domain.CommentRepository; import com.chooz.common.dto.CursorBasePaginatedResponse; -import com.chooz.post.domain.*; +import com.chooz.common.exception.BadRequestException; +import com.chooz.common.exception.ErrorCode; +import com.chooz.post.domain.CommentActive; +import com.chooz.post.domain.PollOption; +import com.chooz.post.domain.PollType; +import com.chooz.post.domain.Post; +import com.chooz.post.domain.PostRepository; +import com.chooz.post.domain.Scope; import com.chooz.post.presentation.dto.FeedResponse; import com.chooz.post.presentation.dto.MyPagePostResponse; import com.chooz.post.presentation.dto.PollChoiceVoteResponse; @@ -33,6 +40,8 @@ import static com.chooz.support.fixture.UserFixture.createUserBuilder; import static com.chooz.support.fixture.VoteFixture.createDefaultVote; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; class PostQueryServiceTest extends IntegrationTest { @@ -67,7 +76,7 @@ void findById() throws Exception { Vote vote = voteRepository.save(VoteFixture.createDefaultVote(user1.getId(), post.getId(), post.getPollChoices().get(0).getId())); //when - PostResponse response = postService.findById(user1.getId(), post.getId()); + PostResponse response = postService.findById(user1.getId(), post.getId(), "shareKey"); //then List pollChoices = response.pollChoices(); @@ -91,6 +100,66 @@ void findById() throws Exception { ); } + @Test + @DisplayName("게시글 조회 - 공개 범위 PUBLIC") + void findById_public() throws Exception { + //given + User author = userRepository.save(createDefaultUser()); + User otherUser = userRepository.save(createDefaultUser()); + String shareKey = "shareKey"; + String otherKey = "otherKey"; + Post post = postRepository.save(PostFixture.createPostBuilder() + .shareUrl(shareKey) + .userId(author.getId()) + .pollOption(PostFixture.pollOptionBuilder().scope(Scope.PUBLIC).build()) + .build()); + + //when then + assertThatNoException() + .isThrownBy(() -> postService.findById(author.getId(), post.getId(), shareKey)); + assertThatNoException() + .isThrownBy(() -> postService.findById(author.getId(), post.getId(), otherKey)); + assertThatNoException() + .isThrownBy(() -> postService.findById(author.getId(), post.getId(), null)); + assertThatNoException() + .isThrownBy(() -> postService.findById(otherUser.getId(), post.getId(), shareKey)); + assertThatNoException() + .isThrownBy(() -> postService.findById(otherUser.getId(), post.getId(), otherKey)); + assertThatNoException() + .isThrownBy(() -> postService.findById(otherUser.getId(), post.getId(), null)); + } + + @Test + @DisplayName("게시글 조회 - 공개 범위 PRIVATE") + void findById_private() throws Exception { + //given + User author = userRepository.save(createDefaultUser()); + User otherUser = userRepository.save(createDefaultUser()); + String shareKey = "shareKey"; + String otherKey = "otherKey"; + Post post = postRepository.save(PostFixture.createPostBuilder() + .shareUrl(shareKey) + .userId(author.getId()) + .pollOption(PostFixture.pollOptionBuilder().scope(Scope.PRIVATE).build()) + .build()); + + //when then + assertThatNoException() + .isThrownBy(() -> postService.findById(author.getId(), post.getId(), shareKey)); + assertThatNoException() + .isThrownBy(() -> postService.findById(author.getId(), post.getId(), otherKey)); + assertThatNoException() + .isThrownBy(() -> postService.findById(author.getId(), post.getId(), null)); + assertThatNoException() + .isThrownBy(() -> postService.findById(otherUser.getId(), post.getId(), shareKey)); + assertThatThrownBy(() -> postService.findById(otherUser.getId(), post.getId(), otherKey)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.POST_NOT_REVEALABLE.getMessage()); + assertThatThrownBy(() -> postService.findById(otherUser.getId(), post.getId(), null)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.POST_NOT_REVEALABLE.getMessage()); + } + @Test @DisplayName("유저가 작성한 게시글 조회 - 커서 null인 경우") void findUserPosts() throws Exception { @@ -256,7 +325,7 @@ void findVotedPosts_multiple() { //유저1 게시글2 투표 후 취소 voteRepository.save(VoteFixture.createDefaultVote(user.getId(), post2.getId(), post2.getPollChoices().get(1).getId())); voteService.vote(user.getId(), post2.getId(), List.of()); - + //when var response = postService.findVotedPosts(user.getId(), user.getId(), null, 10); @@ -404,7 +473,7 @@ void findFeed() throws Exception { private List createPosts(User user, int size) { List posts = new ArrayList<>(); - for (int i = 0; i < size; i ++) { + for (int i = 0; i < size; i++) { Post post = postRepository.save(createDefaultPost(user.getId())); posts.add(post); thumbnailRepository.save(createDefaultThumbnail(post.getId(), post.getPollChoices().get(0).getId())); @@ -414,7 +483,7 @@ private List createPosts(User user, int size) { private List createPostsWithScope(User user, Scope scope, int size) { List posts = new ArrayList<>(); - for (int i = 0; i < size; i ++) { + for (int i = 0; i < size; i++) { Post post = createPostBuilder() .userId(user.getId()) .pollOption(PollOption.create(PollType.SINGLE, scope, CommentActive.OPEN)) diff --git a/src/test/java/com/chooz/post/domain/PostTest.java b/src/test/java/com/chooz/post/domain/PostTest.java index 9235af1..d3ad3f9 100644 --- a/src/test/java/com/chooz/post/domain/PostTest.java +++ b/src/test/java/com/chooz/post/domain/PostTest.java @@ -13,6 +13,7 @@ import static com.chooz.support.fixture.PostFixture.SELF_CREATE_OPTION; import static com.chooz.support.fixture.PostFixture.createDefaultPost; import static com.chooz.support.fixture.PostFixture.createPostBuilder; +import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.*; @@ -177,4 +178,56 @@ void closeByAuthor_onlySelfCanClose() throws Exception { .isInstanceOf(BadRequestException.class) .hasMessage(ErrorCode.ONLY_SELF_CAN_CLOSE.getMessage()); } + + @Test + @DisplayName("공개 범위 - public") + void isRevealable_public() throws Exception { + // given + long author = 1L; + long otherUser = 2L; + String shareKey = "key"; + Post post = createPostBuilder() + .userId(author) + .shareUrl(shareKey) + .pollOption(PostFixture.pollOptionBuilder().scope(Scope.PUBLIC).build()) + .build(); + + // when + boolean res1 = post.isRevealable(author, shareKey); //본인, 키 같음 + boolean res2 = post.isRevealable(author, shareKey + 1); //본인, 키 다름 + boolean res3 = post.isRevealable(otherUser, shareKey); //다른 유저, 키 같음 + boolean res4 = post.isRevealable(otherUser, shareKey + 1); //다른 유저, 키 다름 + + // then + assertThat(res1).isTrue(); + assertThat(res2).isTrue(); + assertThat(res3).isTrue(); + assertThat(res4).isTrue(); + } + + @Test + @DisplayName("공개 범위 - private") + void isRevealable_private() throws Exception { + // given + long author = 1L; + long otherUser = 2L; + String shareKey = "key"; + Post post = createPostBuilder() + .userId(author) + .shareUrl(shareKey) + .pollOption(PostFixture.pollOptionBuilder().scope(Scope.PRIVATE).build()) + .build(); + + // when + boolean res1 = post.isRevealable(author, shareKey); //본인, 키 같음 + boolean res2 = post.isRevealable(author, shareKey + 1); //본인, 키 다름 + boolean res3 = post.isRevealable(otherUser, shareKey); //다른 유저, 키 같음 + boolean res4 = post.isRevealable(otherUser, shareKey + 1); //다른 유저, 키 다름 + + // then + assertThat(res1).isTrue(); + assertThat(res2).isTrue(); + assertThat(res3).isTrue(); + assertThat(res4).isFalse(); + } } diff --git a/src/test/java/com/chooz/post/presentation/PostControllerTest.java b/src/test/java/com/chooz/post/presentation/PostControllerTest.java index e0ce7f3..7f89077 100644 --- a/src/test/java/com/chooz/post/presentation/PostControllerTest.java +++ b/src/test/java/com/chooz/post/presentation/PostControllerTest.java @@ -232,17 +232,21 @@ void findPost() throws Exception { LocalDateTime.of(2025, 2, 13, 12, 0) ); //given - given(postService.findById(any(), any())) + given(postService.findById(any(), any(), any())) .willReturn(response); //when then - mockMvc.perform(RestDocumentationRequestBuilders.get("/posts/{postId}", "1")) + mockMvc.perform(RestDocumentationRequestBuilders.get("/posts/{postId}", "1") + .param("shareKey", "shareKey")) .andExpect(status().isOk()) .andExpect(content().json(objectMapper.writeValueAsString(response))) .andDo(restDocs.document( pathParameters( parameterWithName("postId").description("게시글 Id") ), + queryParameters( + parameterWithName("shareKey").description("공유 키").optional() + ), responseFields( fieldWithPath("id").type(JsonFieldType.NUMBER).description("게시글 Id"), fieldWithPath("title").type(JsonFieldType.STRING).description("게시글 제목"), From 5bfe2411485354094cff9efccee30d93d44a560d Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 2 Oct 2025 14:23:09 +0900 Subject: [PATCH 5/8] =?UTF-8?q?docs:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EA=B3=B5=EC=9C=A0=20=EA=B4=80=EB=A0=A8=20docs=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 --- src/docs/asciidoc/posts.adoc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/docs/asciidoc/posts.adoc b/src/docs/asciidoc/posts.adoc index 76f50fe..a704caf 100644 --- a/src/docs/asciidoc/posts.adoc +++ b/src/docs/asciidoc/posts.adoc @@ -9,6 +9,20 @@ operation::post-controller-test/create-post[snippets='http-request,curl-request, [[게시글-상세-조회]] === `GET` 게시글 상세 조회 +공유 키 관련 +|=== +|공개범위(scope)|유저|shareKey 일치 여부|응답 여부 + +|PUBLIC|작성자|O|O +|PUBLIC|작성자|X|O +|PUBLIC|다른 사용자|O|O +|PUBLIC|다른 사용자|X|O +|PRIVATE|작성자|O|O +|PRIVATE|작성자|X|O +|PRIVATE|다른 사용자|O|O +|PRIVATE|다른 사용자|X|400에러 +|=== + operation::post-controller-test/find-post[snippets='http-request,curl-request,path-parameters,http-response,response-fields'] [[개사굴-공유-url-조회]] From c702d4a79e26402fb71ba3240960b3af80ec7d7f Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 2 Oct 2025 16:07:59 +0900 Subject: [PATCH 6/8] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=8F=85=EB=A6=BD=EC=84=B1=20=EA=B9=A8=EC=A7=80=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CommentLikeNotificationListenerTest.java | 20 ++++++++++++++++++- .../MyPostClosedNotificationListenerTest.java | 19 ++++++++++++++++-- .../PostClosedNotificationListenerTest.java | 19 ++++++++++++++++-- .../VotedNotificationListenerTest.java | 20 ++++++++++++++++++- 4 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/chooz/notification/application/CommentLikeNotificationListenerTest.java b/src/test/java/com/chooz/notification/application/CommentLikeNotificationListenerTest.java index 0294748..3545ae5 100644 --- a/src/test/java/com/chooz/notification/application/CommentLikeNotificationListenerTest.java +++ b/src/test/java/com/chooz/notification/application/CommentLikeNotificationListenerTest.java @@ -6,14 +6,18 @@ import com.chooz.notification.application.web.dto.NotificationDto; import com.chooz.notification.domain.NotificationQueryRepository; import com.chooz.notification.domain.TargetType; +import com.chooz.post.domain.PollChoiceRepository; import com.chooz.post.domain.Post; import com.chooz.post.domain.PostRepository; +import com.chooz.post.persistence.PostJpaRepository; import com.chooz.support.IntegrationTest; import com.chooz.support.fixture.CommentFixture; import com.chooz.support.fixture.PostFixture; import com.chooz.support.fixture.UserFixture; import com.chooz.user.domain.User; import com.chooz.user.domain.UserRepository; +import com.chooz.vote.persistence.VoteJpaRepository; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -29,7 +33,13 @@ class CommentLikeNotificationListenerTest extends IntegrationTest { UserRepository userRepository; @Autowired - PostRepository postRepository; + PostJpaRepository postRepository; + + @Autowired + VoteJpaRepository voteRepository; + + @Autowired + PollChoiceRepository pollChoiceRepository; @Autowired CommentRepository commentRepository; @@ -40,6 +50,14 @@ class CommentLikeNotificationListenerTest extends IntegrationTest { @Autowired CommentLikeService commentLikeService; + @AfterEach + void tearDown() { + voteRepository.deleteAllInBatch(); + pollChoiceRepository.deleteAllInBatch(); + postRepository.deleteAllInBatch(); + userRepository.deleteAllInBatch(); + } + @Test @DisplayName("댓글좋아요 알림") void onCommentLiked() throws Exception { diff --git a/src/test/java/com/chooz/notification/application/MyPostClosedNotificationListenerTest.java b/src/test/java/com/chooz/notification/application/MyPostClosedNotificationListenerTest.java index 561516d..a4ed8d5 100644 --- a/src/test/java/com/chooz/notification/application/MyPostClosedNotificationListenerTest.java +++ b/src/test/java/com/chooz/notification/application/MyPostClosedNotificationListenerTest.java @@ -8,8 +8,10 @@ import com.chooz.post.application.PostCommandService; import com.chooz.post.application.PostVotedEventListener; import com.chooz.post.domain.CloseType; +import com.chooz.post.domain.PollChoiceRepository; import com.chooz.post.domain.Post; import com.chooz.post.domain.PostRepository; +import com.chooz.post.persistence.PostJpaRepository; import com.chooz.support.IntegrationTest; import com.chooz.support.fixture.PostFixture; import com.chooz.support.fixture.UserFixture; @@ -18,6 +20,8 @@ import com.chooz.user.domain.UserRepository; import com.chooz.vote.application.VotedEvent; import com.chooz.vote.domain.VoteRepository; +import com.chooz.vote.persistence.VoteJpaRepository; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -35,10 +39,13 @@ class MyPostClosedNotificationListenerTest extends IntegrationTest { UserRepository userRepository; @Autowired - PostRepository postRepository; + PostJpaRepository postRepository; @Autowired - VoteRepository voteRepository; + VoteJpaRepository voteRepository; + + @Autowired + PollChoiceRepository pollChoiceRepository; @Autowired NotificationQueryRepository notificationQueryRepository; @@ -52,6 +59,14 @@ class MyPostClosedNotificationListenerTest extends IntegrationTest { @Autowired PostCommandService postCommandService; + @AfterEach + void tearDown() { + voteRepository.deleteAllInBatch(); + pollChoiceRepository.deleteAllInBatch(); + postRepository.deleteAllInBatch(); + userRepository.deleteAllInBatch(); + } + @Test @DisplayName("내 투표 마감 알림(참여자 수 마감)") void onMyPostClosedByVoter() throws Exception { diff --git a/src/test/java/com/chooz/notification/application/PostClosedNotificationListenerTest.java b/src/test/java/com/chooz/notification/application/PostClosedNotificationListenerTest.java index ece5368..36215bb 100644 --- a/src/test/java/com/chooz/notification/application/PostClosedNotificationListenerTest.java +++ b/src/test/java/com/chooz/notification/application/PostClosedNotificationListenerTest.java @@ -7,8 +7,10 @@ import com.chooz.post.application.PostCommandService; import com.chooz.post.application.PostVotedEventListener; import com.chooz.post.domain.CloseType; +import com.chooz.post.domain.PollChoiceRepository; import com.chooz.post.domain.Post; import com.chooz.post.domain.PostRepository; +import com.chooz.post.persistence.PostJpaRepository; import com.chooz.support.IntegrationTest; import com.chooz.support.fixture.PostFixture; import com.chooz.support.fixture.UserFixture; @@ -17,6 +19,8 @@ import com.chooz.user.domain.UserRepository; import com.chooz.vote.application.VotedEvent; import com.chooz.vote.domain.VoteRepository; +import com.chooz.vote.persistence.VoteJpaRepository; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -35,10 +39,13 @@ class PostClosedNotificationListenerTest extends IntegrationTest { UserRepository userRepository; @Autowired - PostRepository postRepository; + PostJpaRepository postRepository; @Autowired - VoteRepository voteRepository; + VoteJpaRepository voteRepository; + + @Autowired + PollChoiceRepository pollChoiceRepository; @Autowired NotificationQueryRepository notificationQueryRepository; @@ -52,6 +59,14 @@ class PostClosedNotificationListenerTest extends IntegrationTest { @Autowired PostCommandService postCommandService; + @AfterEach + void tearDown() { + voteRepository.deleteAllInBatch(); + pollChoiceRepository.deleteAllInBatch(); + postRepository.deleteAllInBatch(); + userRepository.deleteAllInBatch(); + } + @Test @DisplayName("투표 마감 알림(참여자 수 마감)") void onPostClosedByVoter() throws Exception { diff --git a/src/test/java/com/chooz/notification/application/VotedNotificationListenerTest.java b/src/test/java/com/chooz/notification/application/VotedNotificationListenerTest.java index b55fa3d..bdc913d 100644 --- a/src/test/java/com/chooz/notification/application/VotedNotificationListenerTest.java +++ b/src/test/java/com/chooz/notification/application/VotedNotificationListenerTest.java @@ -4,14 +4,18 @@ import com.chooz.notification.domain.NotificationQueryRepository; import com.chooz.notification.domain.TargetType; import com.chooz.post.domain.PollChoice; +import com.chooz.post.domain.PollChoiceRepository; import com.chooz.post.domain.Post; import com.chooz.post.domain.PostRepository; +import com.chooz.post.persistence.PostJpaRepository; import com.chooz.support.IntegrationTest; import com.chooz.support.fixture.PostFixture; import com.chooz.support.fixture.UserFixture; import com.chooz.user.domain.User; import com.chooz.user.domain.UserRepository; import com.chooz.vote.application.VoteService; +import com.chooz.vote.persistence.VoteJpaRepository; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -29,7 +33,10 @@ class VotedNotificationListenerTest extends IntegrationTest { UserRepository userRepository; @Autowired - PostRepository postRepository; + PostJpaRepository postRepository; + + @Autowired + VoteJpaRepository voteRepository; @Autowired VoteService voteService; @@ -37,6 +44,17 @@ class VotedNotificationListenerTest extends IntegrationTest { @Autowired NotificationQueryRepository notificationQueryRepository; + @Autowired + PollChoiceRepository pollChoiceRepository; + + @AfterEach + void tearDown() { + voteRepository.deleteAllInBatch(); + pollChoiceRepository.deleteAllInBatch(); + postRepository.deleteAllInBatch(); + userRepository.deleteAllInBatch(); + } + @Test @DisplayName("투표참여 알림") void onVoted() throws Exception { From 3a42519bd3d0aeba44da32764b30d504f1effabc Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Mon, 13 Oct 2025 10:07:39 +0900 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20hasNext=20=EC=98=AC=EB=B0=94?= =?UTF-8?q?=EB=A5=B4=EA=B2=8C=20=EC=A1=B0=ED=9A=8C=20=EC=95=88=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/application/MyPagePostManager.java | 38 ++++++--- .../post/application/PostQueryService.java | 5 +- .../com/chooz/post/domain/PostRepository.java | 4 +- .../persistence/PostQueryDslRepository.java | 85 +++++++++---------- .../post/persistence/PostRepositoryImpl.java | 8 +- 5 files changed, 74 insertions(+), 66 deletions(-) diff --git a/src/main/java/com/chooz/post/application/MyPagePostManager.java b/src/main/java/com/chooz/post/application/MyPagePostManager.java index 3bbc826..458b415 100644 --- a/src/main/java/com/chooz/post/application/MyPagePostManager.java +++ b/src/main/java/com/chooz/post/application/MyPagePostManager.java @@ -27,19 +27,39 @@ public class MyPagePostManager { private final PollChoiceRepository pollChoiceRepository; private final RatioCalculator ratioCalculator; - public CursorBasePaginatedResponse getUserPosts(Long userId, Long myPageUserId, Long cursor, Pageable pageable) { - Slice postSlice = postRepository.findPostsWithVoteCountByUserId(myPageUserId, cursor, pageable); + public CursorBasePaginatedResponse getUserPosts( + Long userId, + Long myPageUserId, + Long cursor, + Pageable pageable + ) { + Slice postSlice = postRepository.findPostsWithVoteCountByUserId( + userId, + myPageUserId, + cursor, + pageable + ); - return getMyPageCursoredResponse(userId, postSlice); + return getMyPageCursoredResponse(postSlice); } - public CursorBasePaginatedResponse getVotedPosts(Long userId, Long myPageUserId, Long cursor, Pageable pageable) { - Slice postSlice = postRepository.findVotedPostsWithVoteCount(myPageUserId, cursor, pageable); + public CursorBasePaginatedResponse getVotedPosts( + Long userId, + Long myPageUserId, + Long cursor, + Pageable pageable + ) { + Slice postSlice = postRepository.findVotedPostsWithVoteCount( + userId, + myPageUserId, + cursor, + pageable + ); - return getMyPageCursoredResponse(userId, postSlice); + return getMyPageCursoredResponse(postSlice); } - private CursorBasePaginatedResponse getMyPageCursoredResponse(Long userId, Slice postSlice) { + private CursorBasePaginatedResponse getMyPageCursoredResponse(Slice postSlice) { if (postSlice.isEmpty()) { return CursorBasePaginatedResponse.of(new SliceImpl<>( List.of(), @@ -51,7 +71,7 @@ private CursorBasePaginatedResponse getMyPageCursoredRespons List postIds = getPostIds(postSlice); Map mostVotedPollChoiceByPostId = getMostVotedPollChoiceByPostId(postIds); - List response = getMyPagePostResponses(userId, postSlice, mostVotedPollChoiceByPostId); + List response = getMyPagePostResponses(postSlice, mostVotedPollChoiceByPostId); return CursorBasePaginatedResponse.of(new SliceImpl<>( response, @@ -75,13 +95,11 @@ private Map getMostVotedPollChoiceByPostId(List } private List getMyPagePostResponses( - Long userId, Slice postSlice, Map mostVotedPollChoiceByPostId ) { return postSlice.getContent() .stream() - .filter(postWithVoteCount -> postWithVoteCount.post().isRevealable(userId)) .map(postWithVoteCount -> { var pollChoiceVoteInfo = mostVotedPollChoiceByPostId.get(postWithVoteCount.post().getId()); var mostVotedPollChoiceInfo = MostVotedPollChoiceDto.of( diff --git a/src/main/java/com/chooz/post/application/PostQueryService.java b/src/main/java/com/chooz/post/application/PostQueryService.java index 06f3d04..b68a19d 100644 --- a/src/main/java/com/chooz/post/application/PostQueryService.java +++ b/src/main/java/com/chooz/post/application/PostQueryService.java @@ -4,16 +4,16 @@ import com.chooz.common.dto.CursorBasePaginatedResponse; import com.chooz.common.exception.BadRequestException; import com.chooz.common.exception.ErrorCode; +import com.chooz.post.application.dto.FeedDto; import com.chooz.post.domain.PollChoice; import com.chooz.post.domain.Post; import com.chooz.post.domain.PostRepository; -import com.chooz.post.presentation.dto.UpdatePostResponse; import com.chooz.post.presentation.dto.AuthorDto; -import com.chooz.post.application.dto.FeedDto; import com.chooz.post.presentation.dto.FeedResponse; import com.chooz.post.presentation.dto.MyPagePostResponse; import com.chooz.post.presentation.dto.PollChoiceVoteResponse; import com.chooz.post.presentation.dto.PostResponse; +import com.chooz.post.presentation.dto.UpdatePostResponse; import com.chooz.user.domain.User; import com.chooz.user.domain.UserRepository; import com.chooz.vote.domain.Vote; @@ -27,7 +27,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Optional; @Service @Transactional(readOnly = true) diff --git a/src/main/java/com/chooz/post/domain/PostRepository.java b/src/main/java/com/chooz/post/domain/PostRepository.java index 50f473d..7b37028 100644 --- a/src/main/java/com/chooz/post/domain/PostRepository.java +++ b/src/main/java/com/chooz/post/domain/PostRepository.java @@ -30,9 +30,9 @@ public interface PostRepository { Optional findCommentActiveByPostId(Long postId); - Slice findPostsWithVoteCountByUserId(Long userId, Long postId, Pageable pageable); + Slice findPostsWithVoteCountByUserId(Long userId, Long authorId, Long postId, Pageable pageable); - Slice findVotedPostsWithVoteCount(Long userId, Long postId, Pageable pageable); + Slice findVotedPostsWithVoteCount(Long userId, Long authorId, Long postId, Pageable pageable); Optional findByIdAndUserId(Long postId, Long userId); } diff --git a/src/main/java/com/chooz/post/persistence/PostQueryDslRepository.java b/src/main/java/com/chooz/post/persistence/PostQueryDslRepository.java index 6e97d9a..2295b96 100644 --- a/src/main/java/com/chooz/post/persistence/PostQueryDslRepository.java +++ b/src/main/java/com/chooz/post/persistence/PostQueryDslRepository.java @@ -1,16 +1,15 @@ package com.chooz.post.persistence; +import com.chooz.post.application.dto.FeedDto; import com.chooz.post.application.dto.PostWithVoteCount; import com.chooz.post.application.dto.QFeedDto; import com.chooz.post.application.dto.QPostWithVoteCount; import com.chooz.post.domain.Post; -import com.chooz.post.application.dto.FeedDto; import com.chooz.post.domain.Scope; -import com.chooz.user.domain.QUser; +import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Predicate; import com.querydsl.core.types.dsl.NumberPath; import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; @@ -21,8 +20,8 @@ import java.util.List; import static com.chooz.comment.domain.QComment.comment; -import static com.chooz.post.domain.QPost.*; -import static com.chooz.user.domain.QUser.*; +import static com.chooz.post.domain.QPost.post; +import static com.chooz.user.domain.QUser.user; import static com.chooz.vote.domain.QVote.vote; @Repository @@ -60,6 +59,7 @@ private boolean isHasNext(Pageable pageable, List results) { /** * 피드 관련 데이터 조회 + * * @param postId * @param pageable * @return @@ -111,49 +111,52 @@ public Slice findFeed(Long postId, Pageable pageable) { /** * 유저가 작성한 게시글 리스트 조회 + * * @param userId * @param postId * @param pageable * @return */ - public Slice findPostsWithVoteCountByUserId(Long userId, Long postId, Pageable pageable) { - List results = queryFactory - .select(new QPostWithVoteCount( - post, - JPAExpressions - .select(vote.userId.count()) - .from(vote) - .where( - vote.postId.eq(post.id), - vote.deleted.isFalse() - ) - )) - .from(post) - .where( - post.userId.eq(userId), - cursor(postId, post.id), - post.deleted.isFalse() - ) - .orderBy(post.id.desc()) - .limit(pageable.getPageSize() + 1) - .fetch(); - - boolean hasNext = isHasNext(pageable, results); - - if (hasNext) { - results.removeLast(); - } - return new SliceImpl<>(results, pageable, hasNext); + public Slice findPostsWithVoteCountByUserId(Long userId, Long authorId, Long postId, Pageable pageable) { + BooleanBuilder builder = new BooleanBuilder( + post.userId.eq(authorId) + .and(cursor(postId, post.id)) + .and(post.deleted.isFalse()) + ); + return getPostWithVoteCounts(userId, authorId, pageable, builder); } /** * 유저가 투표한 게시글 리스트 조회 + * * @param userId + * @param authorId * @param postId * @param pageable * @return */ - public Slice findVotedPostsWithVoteCount(Long userId, Long postId, Pageable pageable) { + public Slice findVotedPostsWithVoteCount(Long userId, Long authorId, Long postId, Pageable pageable) { + BooleanBuilder builder = new BooleanBuilder( + post.id.in( + JPAExpressions + .select(vote.postId) + .from(vote) + .where( + vote.userId.eq(authorId), + vote.deleted.isFalse() + ) + ) + .and(cursor(postId, post.id)) + .and(post.deleted.isFalse()) + ); + return getPostWithVoteCounts(userId, authorId, pageable, builder); + } + + private Slice getPostWithVoteCounts(Long userId, Long authorId, Pageable pageable, BooleanBuilder builder) { + if (!userId.equals(authorId)) { + builder.and(post.pollOption.scope.eq(Scope.PUBLIC)); + } + List results = queryFactory .select(new QPostWithVoteCount( post, @@ -166,19 +169,7 @@ public Slice findVotedPostsWithVoteCount(Long userId, Long po ) )) .from(post) - .where( - post.id.in( - JPAExpressions - .select(vote.postId) - .from(vote) - .where( - vote.userId.eq(userId), - vote.deleted.isFalse() - ) - ), - cursor(postId, post.id), - post.deleted.isFalse() - ) + .where(builder) .orderBy(post.id.desc()) .limit(pageable.getPageSize() + 1) .fetch(); diff --git a/src/main/java/com/chooz/post/persistence/PostRepositoryImpl.java b/src/main/java/com/chooz/post/persistence/PostRepositoryImpl.java index ab9af2f..6432f97 100644 --- a/src/main/java/com/chooz/post/persistence/PostRepositoryImpl.java +++ b/src/main/java/com/chooz/post/persistence/PostRepositoryImpl.java @@ -66,13 +66,13 @@ public Optional findCommentActiveByPostId(Long postId) { } @Override - public Slice findPostsWithVoteCountByUserId(Long userId, Long postId, Pageable pageable) { - return postQueryDslRepository.findPostsWithVoteCountByUserId(userId, postId, pageable); + public Slice findPostsWithVoteCountByUserId(Long userId, Long authorId, Long postId, Pageable pageable) { + return postQueryDslRepository.findPostsWithVoteCountByUserId(userId, authorId, postId, pageable); } @Override - public Slice findVotedPostsWithVoteCount(Long userId, Long postId, Pageable pageable) { - return postQueryDslRepository.findVotedPostsWithVoteCount(userId, postId, pageable); + public Slice findVotedPostsWithVoteCount(Long userId, Long authorId, Long postId, Pageable pageable) { + return postQueryDslRepository.findVotedPostsWithVoteCount(userId, authorId, postId, pageable); } @Override From f6153accdc567c3cc38768ec5228e1ecdd516199 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Mon, 13 Oct 2025 10:07:48 +0900 Subject: [PATCH 8/8] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/application/MyPagePostManager.java | 4 +- .../chooz/post/domain/PostRepositoryTest.java | 52 ++++++++++++++++++- .../java/com/chooz/post/domain/PostTest.java | 5 +- .../vote/application/VoteConcurrentTest.java | 4 +- 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/chooz/post/application/MyPagePostManager.java b/src/main/java/com/chooz/post/application/MyPagePostManager.java index 458b415..d32b994 100644 --- a/src/main/java/com/chooz/post/application/MyPagePostManager.java +++ b/src/main/java/com/chooz/post/application/MyPagePostManager.java @@ -71,10 +71,10 @@ private CursorBasePaginatedResponse getMyPageCursoredRespons List postIds = getPostIds(postSlice); Map mostVotedPollChoiceByPostId = getMostVotedPollChoiceByPostId(postIds); - List response = getMyPagePostResponses(postSlice, mostVotedPollChoiceByPostId); + List responses = getMyPagePostResponses(postSlice, mostVotedPollChoiceByPostId); return CursorBasePaginatedResponse.of(new SliceImpl<>( - response, + responses, postSlice.getPageable(), postSlice.hasNext() )); diff --git a/src/test/java/com/chooz/post/domain/PostRepositoryTest.java b/src/test/java/com/chooz/post/domain/PostRepositoryTest.java index c50523b..580b17f 100644 --- a/src/test/java/com/chooz/post/domain/PostRepositoryTest.java +++ b/src/test/java/com/chooz/post/domain/PostRepositoryTest.java @@ -162,6 +162,7 @@ void findPostsWithVoteCountByUserId() throws Exception { // when Slice result = postRepository.findPostsWithVoteCountByUserId( + user1.getId(), user1.getId(), null, PageRequest.of(0, 10) @@ -188,7 +189,7 @@ void findVotedPostsWithVoteCount() throws Exception { Post post1 = postRepository.save(PostFixture.createDefaultPost(user2.getId())); Post post2 = postRepository.save(PostFixture.createDefaultPost(user2.getId())); Post post3 = postRepository.save(PostFixture.createDefaultPost(user2.getId())); - Post post4 = postRepository.save(PostFixture.createDefaultPost(user3.getId())); + Post post4 = postRepository.save(PostFixture.createDefaultPost(user2.getId())); List post1Choices = post1.getPollChoices(); List post2Choices = post2.getPollChoices(); @@ -214,6 +215,7 @@ void findVotedPostsWithVoteCount() throws Exception { // when Slice result = postRepository.findVotedPostsWithVoteCount( + user1.getId(), user1.getId(), null, PageRequest.of(0, 10) @@ -229,6 +231,54 @@ void findVotedPostsWithVoteCount() throws Exception { ); } + @Test + @DisplayName("본인의 마이페이지를 보는 경우, hasNext가 true여야 함") + void findPostsWithVoteCountByUserId_hasNext_true() throws Exception { + // given + User user1 = userRepository.save(UserFixture.createDefaultUser()); + + // 비공개 3개, 공개 9개 + createPostsWithScope(user1, Scope.PRIVATE, 3); + List publicPosts = createPostsWithScope(user1, Scope.PUBLIC, 9); + + voteRepository.save(VoteFixture.createDefaultVote(user1.getId(), publicPosts.getFirst().getId(), publicPosts.getFirst().getPollChoices().getFirst().getId())); + + // when + Slice result = postRepository.findPostsWithVoteCountByUserId( + user1.getId(), + user1.getId(), + null, + PageRequest.of(0, 10) + ); + + // then + assertThat(result.getContent()).hasSize(10); + assertThat(result.hasNext()).isTrue(); + } + + @Test + @DisplayName("다른 사람의 마이페이지를 보는 경우, hasNext가 false여야 함") + void findPostsWithVoteCountByUserId_hasNext_false() throws Exception { + // given + User user1 = userRepository.save(UserFixture.createDefaultUser()); + User user2 = userRepository.save(UserFixture.createDefaultUser()); + + // 비공개 3개, 공개 9개 + createPostsWithScope(user1, Scope.PRIVATE, 3); + createPostsWithScope(user1, Scope.PUBLIC, 9); + + // when + Slice result = postRepository.findPostsWithVoteCountByUserId( + user2.getId(), + user1.getId(), + null, + PageRequest.of(0, 10) + ); + + // then + assertThat(result.getContent()).hasSize(9); + assertThat(result.hasNext()).isFalse(); + } private List createPosts(long userId, int size) { List posts = new ArrayList<>(); diff --git a/src/test/java/com/chooz/post/domain/PostTest.java b/src/test/java/com/chooz/post/domain/PostTest.java index d3ad3f9..692aef8 100644 --- a/src/test/java/com/chooz/post/domain/PostTest.java +++ b/src/test/java/com/chooz/post/domain/PostTest.java @@ -3,20 +3,17 @@ import com.chooz.common.exception.BadRequestException; import com.chooz.common.exception.ErrorCode; import com.chooz.support.fixture.PostFixture; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; -import static com.chooz.support.fixture.PostFixture.SELF_CREATE_OPTION; import static com.chooz.support.fixture.PostFixture.createDefaultPost; import static com.chooz.support.fixture.PostFixture.createPostBuilder; -import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertAll; class PostTest { diff --git a/src/test/java/com/chooz/vote/application/VoteConcurrentTest.java b/src/test/java/com/chooz/vote/application/VoteConcurrentTest.java index 5cf1b35..d4b924e 100644 --- a/src/test/java/com/chooz/vote/application/VoteConcurrentTest.java +++ b/src/test/java/com/chooz/vote/application/VoteConcurrentTest.java @@ -24,8 +24,8 @@ import static org.assertj.core.api.Assertions.assertThat; -@ActiveProfiles("mysql") -@SpringBootTest +//@ActiveProfiles("mysql") +//@SpringBootTest class VoteConcurrentTest { @Autowired