From dd5d6b88cbde013997cfd33396d1d71eae10f17f Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 15:59:30 +0900 Subject: [PATCH 01/14] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=88=AC=ED=91=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ApplicationControllerAdvice.java | 16 ++++++++++++++++ .../swyp8team2/vote/application/VoteService.java | 14 +++++++++++--- .../vote/presentation/VoteController.java | 6 ++---- .../vote/presentation/dto/VoteRequest.java | 2 +- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/swyp8team2/common/exception/ApplicationControllerAdvice.java b/src/main/java/com/swyp8team2/common/exception/ApplicationControllerAdvice.java index 9abbe93f..2796149d 100644 --- a/src/main/java/com/swyp8team2/common/exception/ApplicationControllerAdvice.java +++ b/src/main/java/com/swyp8team2/common/exception/ApplicationControllerAdvice.java @@ -5,9 +5,11 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingRequestHeaderException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.HandlerMethodValidationException; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.servlet.resource.NoResourceFoundException; import javax.naming.AuthenticationException; @@ -68,6 +70,20 @@ public ResponseEntity handle(AccessDeniedException e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponse(ErrorCode.INVALID_TOKEN)); } + @ExceptionHandler(MissingRequestHeaderException.class) + public ResponseEntity handle(MissingRequestHeaderException e) { + log.debug("MissingRequestHeaderException {}", e.getMessage()); + return ResponseEntity.badRequest() + .body(new ErrorResponse(ErrorCode.INVALID_ARGUMENT)); + } + + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public ResponseEntity handle(MethodArgumentTypeMismatchException e) { + log.debug("MethodArgumentTypeMismatchException {}", e.getMessage()); + return ResponseEntity.badRequest() + .body(new ErrorResponse(ErrorCode.INVALID_ARGUMENT)); + } + @ExceptionHandler(Exception.class) public ResponseEntity handle(Exception e) { log.error("Exception", e); diff --git a/src/main/java/com/swyp8team2/vote/application/VoteService.java b/src/main/java/com/swyp8team2/vote/application/VoteService.java index 1c7b2bf0..328d14c9 100644 --- a/src/main/java/com/swyp8team2/vote/application/VoteService.java +++ b/src/main/java/com/swyp8team2/vote/application/VoteService.java @@ -7,6 +7,7 @@ import com.swyp8team2.user.domain.UserRepository; import com.swyp8team2.vote.domain.Vote; import com.swyp8team2.vote.domain.VoteRepository; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,12 +27,12 @@ public Long vote(Long userId, Long postId, Long imageId) { .orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND)); voteRepository.findByUserSeqAndPostId(user.getSeq(), postId) .ifPresent(vote -> deleteExistingVote(postId, vote)); - Vote vote = createVote(postId, imageId, user); + Vote vote = createVote(postId, imageId, user.getSeq()); return vote.getId(); } - private Vote createVote(Long postId, Long imageId, User user) { - Vote vote = voteRepository.save(Vote.of(postId, imageId, user.getSeq())); + private Vote createVote(Long postId, Long imageId, String userSeq) { + Vote vote = voteRepository.save(Vote.of(postId, imageId, userSeq)); postRepository.findById(postId) .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)) .vote(imageId); @@ -44,4 +45,11 @@ private void deleteExistingVote(Long postId, Vote vote) { .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)) .cancelVote(vote.getPostImageId()); } + + public Long guestVote(String guestId, Long postId, Long imageId) { + voteRepository.findByUserSeqAndPostId(guestId, postId) + .ifPresent(vote -> deleteExistingVote(postId, vote)); + Vote vote = createVote(postId, imageId, guestId); + return vote.getId(); + } } diff --git a/src/main/java/com/swyp8team2/vote/presentation/VoteController.java b/src/main/java/com/swyp8team2/vote/presentation/VoteController.java index df3e6711..25025afd 100644 --- a/src/main/java/com/swyp8team2/vote/presentation/VoteController.java +++ b/src/main/java/com/swyp8team2/vote/presentation/VoteController.java @@ -4,14 +4,11 @@ import com.swyp8team2.common.presentation.CustomHeader; import com.swyp8team2.vote.application.VoteService; import com.swyp8team2.vote.presentation.dto.ChangeVoteRequest; -import com.swyp8team2.vote.presentation.dto.GuestVoteRequest; import com.swyp8team2.vote.presentation.dto.VoteRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -33,7 +30,7 @@ public ResponseEntity vote( @Valid @RequestBody VoteRequest request, @AuthenticationPrincipal UserInfo userInfo ) { - voteService.vote(userInfo.userId(), postId, request.voteId()); + voteService.vote(userInfo.userId(), postId, request.imageId()); return ResponseEntity.ok().build(); } @@ -43,6 +40,7 @@ public ResponseEntity guestVote( @RequestHeader(CustomHeader.GUEST_ID) String guestId, @Valid @RequestBody VoteRequest request ) { + voteService.guestVote(guestId, postId, request.imageId()); return ResponseEntity.ok().build(); } diff --git a/src/main/java/com/swyp8team2/vote/presentation/dto/VoteRequest.java b/src/main/java/com/swyp8team2/vote/presentation/dto/VoteRequest.java index 202c8157..9c7b2adb 100644 --- a/src/main/java/com/swyp8team2/vote/presentation/dto/VoteRequest.java +++ b/src/main/java/com/swyp8team2/vote/presentation/dto/VoteRequest.java @@ -4,6 +4,6 @@ public record VoteRequest( @NotNull - Long voteId + Long imageId ) { } From 913ab132fcf61975c69e76715759a3958d6e6c95 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 15:59:48 +0900 Subject: [PATCH 02/14] =?UTF-8?q?fix:=20=ED=88=AC=ED=91=9C=20=EB=B9=84?= =?UTF-8?q?=EC=9C=A8=20=EA=B3=84=EC=82=B0=20=EC=9D=B8=EC=9E=90=20=EC=88=9C?= =?UTF-8?q?=EC=84=9C=20=EC=9E=98=EB=AA=BB=EB=90=9C=20=EB=B6=80=EB=B6=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/swyp8team2/post/application/PostService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/swyp8team2/post/application/PostService.java b/src/main/java/com/swyp8team2/post/application/PostService.java index 1eb11b6f..32d0b391 100644 --- a/src/main/java/com/swyp8team2/post/application/PostService.java +++ b/src/main/java/com/swyp8team2/post/application/PostService.java @@ -119,7 +119,7 @@ public List findPostStatus(Long postId) { int totalVoteCount = getTotalVoteCount(post.getImages()); return post.getImages().stream() .map(image -> { - String ratio = ratioCalculator.calculate(image.getVoteCount(), totalVoteCount); + String ratio = ratioCalculator.calculate(totalVoteCount, image.getVoteCount()); return new PostImageVoteStatusResponse(image.getName(), image.getVoteCount(), ratio); }).toList(); } From 3ab22b246ebb18fd07f84493d23d8b3fd4592e4d Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 16:03:42 +0900 Subject: [PATCH 03/14] =?UTF-8?q?refactor:=20voteId=20->=20imageId=20?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/presentation/dto/CommentResponse.java | 2 +- .../comment/presentation/CommentControllerTest.java | 4 ++-- .../post/application/RatioCalculatorTest.java | 2 +- .../vote/presentation/VoteControllerTest.java | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/swyp8team2/comment/presentation/dto/CommentResponse.java b/src/main/java/com/swyp8team2/comment/presentation/dto/CommentResponse.java index 702158f2..30af16ab 100644 --- a/src/main/java/com/swyp8team2/comment/presentation/dto/CommentResponse.java +++ b/src/main/java/com/swyp8team2/comment/presentation/dto/CommentResponse.java @@ -11,7 +11,7 @@ public record CommentResponse( Long commentId, String content, AuthorDto author, - Long voteId, + Long imageId, LocalDateTime createdAt ) implements CursorDto { diff --git a/src/test/java/com/swyp8team2/comment/presentation/CommentControllerTest.java b/src/test/java/com/swyp8team2/comment/presentation/CommentControllerTest.java index 3c55561e..46dfbf8b 100644 --- a/src/test/java/com/swyp8team2/comment/presentation/CommentControllerTest.java +++ b/src/test/java/com/swyp8team2/comment/presentation/CommentControllerTest.java @@ -119,10 +119,10 @@ void findComments() throws Exception { fieldWithPath("data[].author.profileUrl") .type(JsonFieldType.STRING) .description("작성자 프로필 이미지 url"), - fieldWithPath("data[].voteId") + fieldWithPath("data[].imageId") .type(JsonFieldType.NUMBER) .optional() - .description("작성자 투표 Id (투표 없을 시 null)"), + .description("작성자가 투표한 이미지 Id (투표 없을 시 null)"), fieldWithPath("data[].createdAt") .type(JsonFieldType.STRING) .description("댓글 작성일") diff --git a/src/test/java/com/swyp8team2/post/application/RatioCalculatorTest.java b/src/test/java/com/swyp8team2/post/application/RatioCalculatorTest.java index c9262704..27e95e3d 100644 --- a/src/test/java/com/swyp8team2/post/application/RatioCalculatorTest.java +++ b/src/test/java/com/swyp8team2/post/application/RatioCalculatorTest.java @@ -17,7 +17,7 @@ void setUp() { } @ParameterizedTest(name = "{index}: totalVoteCount={0}, voteCount={1} => result={2}") - @CsvSource({"3, 2, 66.7", "3, 1, 33.3", "4, 2, 50.0", "4, 3, 75.0", "0, 0, 0.0", "1, 0, 0.0", "1, 1, 100.0"}) + @CsvSource({"3, 2, 66.7", "3, 1, 33.3", "4, 2, 50.0", "4, 3, 75.0", "0, 0, 0.0", "1, 0, 0.0", "1, 1, 100.0", "10, 7, 70.0", "10, 3, 30.0"}) @DisplayName("비율 계산") void calculate(int totalVoteCount, int voteCount, String result) throws Exception { //given diff --git a/src/test/java/com/swyp8team2/vote/presentation/VoteControllerTest.java b/src/test/java/com/swyp8team2/vote/presentation/VoteControllerTest.java index e0e8263d..77907f64 100644 --- a/src/test/java/com/swyp8team2/vote/presentation/VoteControllerTest.java +++ b/src/test/java/com/swyp8team2/vote/presentation/VoteControllerTest.java @@ -44,7 +44,7 @@ void vote() throws Exception { parameterWithName("postId").description("게시글 Id") ), requestFields( - fieldWithPath("voteId") + fieldWithPath("imageId") .type(JsonFieldType.NUMBER) .description("투표 후보 Id") ) @@ -70,7 +70,7 @@ void guestVote() throws Exception { parameterWithName("postId").description("게시글 Id") ), requestFields( - fieldWithPath("voteId") + fieldWithPath("imageId") .type(JsonFieldType.NUMBER) .description("투표 후보 Id") ) @@ -96,9 +96,9 @@ void changeVote() throws Exception { parameterWithName("postId").description("변경할 게시글 Id") ), requestFields( - fieldWithPath("voteId") + fieldWithPath("imageId") .type(JsonFieldType.NUMBER) - .description("변경할 투표 후보 Id") + .description("변경할 투표 이미지 Id") ) )); } @@ -122,9 +122,9 @@ void guestChangeVote() throws Exception { parameterWithName("postId").description("변경활 게시글 Id") ), requestFields( - fieldWithPath("voteId") + fieldWithPath("imageId") .type(JsonFieldType.NUMBER) - .description("변경할 투표 후보 Id") + .description("변경할 투표 이미지 Id") ) )); } From 4cac8aae9a32ef02131178591d34db8e123a9a11 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 16:03:53 +0900 Subject: [PATCH 04/14] =?UTF-8?q?test:=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=88=AC=ED=91=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=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 --- .../presentation/dto/ChangeVoteRequest.java | 2 +- .../vote/application/VoteServiceTest.java | 50 +++++++++++++++++++ .../vote/presentation/VoteControllerTest.java | 7 +++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/swyp8team2/vote/presentation/dto/ChangeVoteRequest.java b/src/main/java/com/swyp8team2/vote/presentation/dto/ChangeVoteRequest.java index 816e9a7d..f9c102f8 100644 --- a/src/main/java/com/swyp8team2/vote/presentation/dto/ChangeVoteRequest.java +++ b/src/main/java/com/swyp8team2/vote/presentation/dto/ChangeVoteRequest.java @@ -4,6 +4,6 @@ public record ChangeVoteRequest( @NotNull - Long voteId + Long imageId ) { } diff --git a/src/test/java/com/swyp8team2/vote/application/VoteServiceTest.java b/src/test/java/com/swyp8team2/vote/application/VoteServiceTest.java index e17a96db..c12afc49 100644 --- a/src/test/java/com/swyp8team2/vote/application/VoteServiceTest.java +++ b/src/test/java/com/swyp8team2/vote/application/VoteServiceTest.java @@ -83,4 +83,54 @@ void vote_change() { () -> assertThat(findPost.getImages().get(1).getVoteCount()).isEqualTo(1) ); } + + @Test + @DisplayName("게스트 투표하기") + void guestVote() { + // given + String guestId = "guestId"; + User user = userRepository.save(createUser(1)); + ImageFile imageFile1 = imageFileRepository.save(createImageFile(1)); + ImageFile imageFile2 = imageFileRepository.save(createImageFile(2)); + Post post = postRepository.save(createPost(user.getId(), imageFile1, imageFile2, 1)); + + // when + Long voteId = voteService.guestVote(guestId, post.getId(), post.getImages().get(0).getId()); + + // then + Vote vote = voteRepository.findById(voteId).get(); + Post findPost = postRepository.findById(post.getId()).get(); + assertAll( + () -> assertThat(vote.getUserSeq()).isEqualTo(guestId), + () -> assertThat(vote.getPostId()).isEqualTo(post.getId()), + () -> assertThat(vote.getPostImageId()).isEqualTo(post.getImages().get(0).getId()), + () -> assertThat(findPost.getImages().get(0).getVoteCount()).isEqualTo(1) + ); + } + + @Test + @DisplayName("게스트 투표하기 - 다른 이미지로 투표 변경한 경우") + void guestVote_change() { + // given + String guestId = "guestId"; + User user = userRepository.save(createUser(1)); + ImageFile imageFile1 = imageFileRepository.save(createImageFile(1)); + ImageFile imageFile2 = imageFileRepository.save(createImageFile(2)); + Post post = postRepository.save(createPost(user.getId(), imageFile1, imageFile2, 1)); + voteService.guestVote(guestId, post.getId(), post.getImages().get(0).getId()); + + // when + Long voteId = voteService.guestVote(guestId, post.getId(), post.getImages().get(1).getId()); + + // then + Vote vote = voteRepository.findById(voteId).get(); + Post findPost = postRepository.findById(post.getId()).get(); + assertAll( + () -> assertThat(vote.getUserSeq()).isEqualTo(guestId), + () -> assertThat(vote.getPostId()).isEqualTo(post.getId()), + () -> assertThat(vote.getPostImageId()).isEqualTo(post.getImages().get(1).getId()), + () -> assertThat(findPost.getImages().get(0).getVoteCount()).isEqualTo(0), + () -> assertThat(findPost.getImages().get(1).getVoteCount()).isEqualTo(1) + ); + } } diff --git a/src/test/java/com/swyp8team2/vote/presentation/VoteControllerTest.java b/src/test/java/com/swyp8team2/vote/presentation/VoteControllerTest.java index 77907f64..c28de442 100644 --- a/src/test/java/com/swyp8team2/vote/presentation/VoteControllerTest.java +++ b/src/test/java/com/swyp8team2/vote/presentation/VoteControllerTest.java @@ -14,6 +14,11 @@ import java.util.UUID; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; @@ -49,6 +54,7 @@ void vote() throws Exception { .description("투표 후보 Id") ) )); + verify(voteService, times(1)).vote(any(), any(), any()); } @Test @@ -75,6 +81,7 @@ void guestVote() throws Exception { .description("투표 후보 Id") ) )); + verify(voteService, times(1)).guestVote(any(), any(), any()); } @Test From 7976205fd9219c82b817eef418dc7d06a6bd1002 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 16:11:32 +0900 Subject: [PATCH 05/14] =?UTF-8?q?docs:=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=88=AC=ED=91=9C=20=EA=B5=AC=ED=98=84=20=ED=98=84=ED=99=A9=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/votes.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/asciidoc/votes.adoc b/src/docs/asciidoc/votes.adoc index e89d4323..98b1cdc6 100644 --- a/src/docs/asciidoc/votes.adoc +++ b/src/docs/asciidoc/votes.adoc @@ -7,7 +7,7 @@ operation::vote-controller-test/vote[snippets='http-request,curl-request,request-headers,request-fields,http-response'] [[게스트-투표]] -=== `POST` 게스트 투표 (미구현) +=== `POST` 게스트 투표 operation::vote-controller-test/guest-vote[snippets='http-request,curl-request,request-headers,request-fields,http-response'] From 6111e65071e053c98eab37c79cf67668cb96a784 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 16:33:45 +0900 Subject: [PATCH 06/14] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EB=A7=88=EA=B0=90=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../swyp8team2/common/exception/ErrorCode.java | 2 ++ .../post/application/PostService.java | 17 +++++++++++++++++ .../java/com/swyp8team2/post/domain/Post.java | 14 ++++++++++++++ .../post/presentation/PostController.java | 15 +++++++++++++-- 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/swyp8team2/common/exception/ErrorCode.java b/src/main/java/com/swyp8team2/common/exception/ErrorCode.java index 0cbbdf1a..3a1f52d5 100644 --- a/src/main/java/com/swyp8team2/common/exception/ErrorCode.java +++ b/src/main/java/com/swyp8team2/common/exception/ErrorCode.java @@ -18,6 +18,8 @@ public enum ErrorCode { POST_NOT_FOUND("존재하지 않는 게시글"), DESCRIPTION_LENGTH_EXCEEDED("게시글 설명 길이 초과"), INVALID_POST_IMAGE_COUNT("게시글 이미지 개수 오류"), + NOT_POST_AUTHOR("게시글 작성자가 아님"), + POST_ALREADY_CLOSED("이미 마감된 게시글"), //401 EXPIRED_TOKEN("토큰 만료"), diff --git a/src/main/java/com/swyp8team2/post/application/PostService.java b/src/main/java/com/swyp8team2/post/application/PostService.java index 13c3264f..d8fdaf7b 100644 --- a/src/main/java/com/swyp8team2/post/application/PostService.java +++ b/src/main/java/com/swyp8team2/post/application/PostService.java @@ -131,4 +131,21 @@ private int getTotalVoteCount(List images) { } return totalVoteCount; } + + public void delete(Long userId, String postId) { + Post post = postRepository.findById(Long.valueOf(postId)) + .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)); + if (!post.getUserId().equals(userId)) { + throw new BadRequestException(ErrorCode.NOT_POST_AUTHOR); + } + post.validateOwner(userId); + postRepository.delete(post); + } + + @Transactional + public void close(Long userId, Long postId) { + Post post = postRepository.findById(postId) + .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)); + post.close(userId); + } } diff --git a/src/main/java/com/swyp8team2/post/domain/Post.java b/src/main/java/com/swyp8team2/post/domain/Post.java index b036b0e8..5ef39a8f 100644 --- a/src/main/java/com/swyp8team2/post/domain/Post.java +++ b/src/main/java/com/swyp8team2/post/domain/Post.java @@ -94,4 +94,18 @@ public void cancelVote(Long imageId) { .orElseThrow(() -> new InternalServerException(ErrorCode.POST_IMAGE_NOT_FOUND)); image.decreaseVoteCount(); } + + public void close(Long userId) { + validateOwner(userId); + if (state == State.CLOSED) { + throw new BadRequestException(ErrorCode.POST_ALREADY_CLOSED); + } + this.state = State.CLOSED; + } + + public void validateOwner(Long userId) { + if (!this.userId.equals(userId)) { + throw new BadRequestException(ErrorCode.NOT_POST_AUTHOR); + } + } } diff --git a/src/main/java/com/swyp8team2/post/presentation/PostController.java b/src/main/java/com/swyp8team2/post/presentation/PostController.java index 1d67011e..f76bc24b 100644 --- a/src/main/java/com/swyp8team2/post/presentation/PostController.java +++ b/src/main/java/com/swyp8team2/post/presentation/PostController.java @@ -16,6 +16,7 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -80,11 +81,21 @@ public ResponseEntity findPost(@PathVariable("shareUrl") String sh )); } - @DeleteMapping("/{shareUrl}") + @PostMapping("/{postId}/close") + public ResponseEntity closePost( + @PathVariable("postId") Long postId, + @AuthenticationPrincipal UserInfo userInfo + ) { + postService.close(userInfo.userId(), postId); + return ResponseEntity.ok().build(); + } + + @DeleteMapping("/{postId}") public ResponseEntity deletePost( - @PathVariable("shareUrl") String shareUrl, + @PathVariable("postId") String postId, @AuthenticationPrincipal UserInfo userInfo ) { + postService.delete(userInfo.userId(), postId); return ResponseEntity.ok().build(); } From 1e74a9be98b58411aae2467f12348ce5c21b6e8b Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 16:33:58 +0900 Subject: [PATCH 07/14] =?UTF-8?q?test:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EB=A7=88=EA=B0=90=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=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/PostServiceTest.java | 60 +++++++++++++++++++ .../com/swyp8team2/post/domain/PostTest.java | 53 ++++++++++++++++ .../post/presentation/PostControllerTest.java | 21 +++++++ 3 files changed, 134 insertions(+) diff --git a/src/test/java/com/swyp8team2/post/application/PostServiceTest.java b/src/test/java/com/swyp8team2/post/application/PostServiceTest.java index b86dd717..0399ab04 100644 --- a/src/test/java/com/swyp8team2/post/application/PostServiceTest.java +++ b/src/test/java/com/swyp8team2/post/application/PostServiceTest.java @@ -7,6 +7,7 @@ import com.swyp8team2.post.domain.Post; import com.swyp8team2.post.domain.PostImage; import com.swyp8team2.post.domain.PostRepository; +import com.swyp8team2.post.domain.State; import com.swyp8team2.post.presentation.dto.CreatePostRequest; import com.swyp8team2.post.presentation.dto.PostResponse; import com.swyp8team2.post.presentation.dto.PostImageRequestDto; @@ -237,4 +238,63 @@ void findPostStatus() throws Exception { () -> assertThat(response.get(1).voteRatio()).isEqualTo("0.0") ); } + + @Test + @DisplayName("투표 마감") + void close() throws Exception { + //given + User user = userRepository.save(createUser(1)); + ImageFile imageFile1 = imageFileRepository.save(createImageFile(1)); + ImageFile imageFile2 = imageFileRepository.save(createImageFile(2)); + Post post = postRepository.save(createPost(user.getId(), imageFile1, imageFile2, 1)); + + //when + post.close(user.getId()); + + //then + postRepository.findById(post.getId()).get(); + assertThat(post.getState()).isEqualTo(State.CLOSED); + } + + @Test + @DisplayName("투표 마감 - 게시글 작성자가 아닐 경우") + void close_notPostAuthor() throws Exception { + //given + User user = userRepository.save(createUser(1)); + ImageFile imageFile1 = imageFileRepository.save(createImageFile(1)); + ImageFile imageFile2 = imageFileRepository.save(createImageFile(2)); + Post post = postRepository.save(createPost(user.getId(), imageFile1, imageFile2, 1)); + + //when then + assertThatThrownBy(() -> post.close(2L)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.NOT_POST_AUTHOR.getMessage()); + } + + @Test + @DisplayName("투표 마감 - 이미 마감된 게시글인 경우") + void close_alreadyClosed() throws Exception { + //given + User user = userRepository.save(createUser(1)); + ImageFile imageFile1 = imageFileRepository.save(createImageFile(1)); + ImageFile imageFile2 = imageFileRepository.save(createImageFile(2)); + Post post = postRepository.save(createPost(user.getId(), imageFile1, imageFile2, 1)); + post.close(user.getId()); + + //when then + assertThatThrownBy(() -> post.close(user.getId())) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.POST_ALREADY_CLOSED.getMessage()); + } + + @Test + @DisplayName("투표 마감 - 존재하지 않는 게시글일 경우") + void close_notFoundPost() throws Exception { + //given + + //when then + assertThatThrownBy(() -> postService.close(1L, 1L)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.POST_NOT_FOUND.getMessage()); + } } diff --git a/src/test/java/com/swyp8team2/post/domain/PostTest.java b/src/test/java/com/swyp8team2/post/domain/PostTest.java index def54c57..ab59071e 100644 --- a/src/test/java/com/swyp8team2/post/domain/PostTest.java +++ b/src/test/java/com/swyp8team2/post/domain/PostTest.java @@ -3,6 +3,7 @@ import com.swyp8team2.common.exception.BadRequestException; import com.swyp8team2.common.exception.ErrorCode; import com.swyp8team2.common.exception.InternalServerException; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -75,4 +76,56 @@ void create_descriptionCountExceeded() throws Exception { .isInstanceOf(BadRequestException.class) .hasMessage(ErrorCode.DESCRIPTION_LENGTH_EXCEEDED.getMessage()); } + + @Test + @DisplayName("투표 마감") + void close() throws Exception { + //given + long userId = 1L; + List postImages = List.of( + PostImage.create("뽀또A", 1L), + PostImage.create("뽀또B", 2L) + ); + Post post = new Post(null, userId, "description", State.PROGRESS, postImages, "shareUrl"); + + //when + post.close(userId); + + //then + assertThat(post.getState()).isEqualTo(State.CLOSED); + } + + @Test + @DisplayName("투표 마감 - 이미 마감된 게시글인 경우") + void close_alreadyClosed() throws Exception { + //given + long userId = 1L; + List postImages = List.of( + PostImage.create("뽀또A", 1L), + PostImage.create("뽀또B", 2L) + ); + Post post = new Post(null, userId, "description", State.CLOSED, postImages, "shareUrl"); + + //when then + assertThatThrownBy(() -> post.close(userId)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.POST_ALREADY_CLOSED.getMessage()); + } + + @Test + @DisplayName("투표 마감 - 게시글 작성자가 아닌 경우") + void close_notPostAuthor() throws Exception { + //given + long userId = 1L; + List postImages = List.of( + PostImage.create("뽀또A", 1L), + PostImage.create("뽀또B", 2L) + ); + Post post = new Post(null, userId, "description", State.PROGRESS, postImages, "shareUrl"); + + //when then + assertThatThrownBy(() -> post.close(2L)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.NOT_POST_AUTHOR.getMessage()); + } } diff --git a/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java b/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java index 11c0659c..781364b8 100644 --- a/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java +++ b/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java @@ -23,6 +23,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; @@ -278,4 +280,23 @@ void findVotedPost() throws Exception { ) )); } + + @Test + @WithMockUserInfo + @DisplayName("게시글 마감") + void closePost() throws Exception { + //given + + //when then + mockMvc.perform(RestDocumentationRequestBuilders.post("/posts/{postId}/close", 1) + .header(HttpHeaders.AUTHORIZATION, "Bearer token")) + .andExpect(status().isOk()) + .andDo(restDocs.document( + requestHeaders(authorizationHeader()), + pathParameters( + parameterWithName("postId").description("게시글 Id") + ) + )); + verify(postService, times(1)).close(any(), any()); + } } From 18a0dc63991b6acfcf39f55c5024df00b13197b4 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 16:35:14 +0900 Subject: [PATCH 08/14] =?UTF-8?q?docs:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=ED=88=AC=ED=91=9C=20=EB=A7=88=EA=B0=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/posts.adoc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/docs/asciidoc/posts.adoc b/src/docs/asciidoc/posts.adoc index 6206b90b..82d9ea18 100644 --- a/src/docs/asciidoc/posts.adoc +++ b/src/docs/asciidoc/posts.adoc @@ -27,6 +27,13 @@ operation::post-controller-test/find-my-post[snippets='http-request,curl-request operation::post-controller-test/find-voted-post[snippets='http-request,curl-request,query-parameters,request-headers,http-response,response-fields'] +[[게시글-투표-마감]] +=== `POST` 게시글 투표 마감 + +operation::post-controller-test/close-post[snippets='http-request,curl-request,path-parameters,request-headers,http-response'] + +[[게시글-수정]] + [[게시글-삭제]] === 게시글 삭제 (미구현) From 28988015538461bb86992e69ed7123f2145a491f Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 17:44:46 +0900 Subject: [PATCH 09/14] =?UTF-8?q?fix:=20=ED=88=AC=ED=91=9C=20=EB=A7=88?= =?UTF-8?q?=EA=B0=90=EB=90=90=EC=9D=84=20=EB=95=8C=20=ED=88=AC=ED=91=9C?= =?UTF-8?q?=ED=95=A0=20=EA=B2=BD=EC=9A=B0=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/swyp8team2/post/domain/Post.java | 6 ++ .../vote/application/VoteService.java | 39 +++++++------ .../vote/application/VoteServiceTest.java | 57 +++++++++++++++++++ 3 files changed, 84 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/swyp8team2/post/domain/Post.java b/src/main/java/com/swyp8team2/post/domain/Post.java index 5ef39a8f..96ad72dd 100644 --- a/src/main/java/com/swyp8team2/post/domain/Post.java +++ b/src/main/java/com/swyp8team2/post/domain/Post.java @@ -108,4 +108,10 @@ public void validateOwner(Long userId) { throw new BadRequestException(ErrorCode.NOT_POST_AUTHOR); } } + + public void validateProgress() { + if (!this.state.equals(State.PROGRESS)) { + throw new BadRequestException(ErrorCode.POST_ALREADY_CLOSED); + } + } } diff --git a/src/main/java/com/swyp8team2/vote/application/VoteService.java b/src/main/java/com/swyp8team2/vote/application/VoteService.java index 328d14c9..9860e8a7 100644 --- a/src/main/java/com/swyp8team2/vote/application/VoteService.java +++ b/src/main/java/com/swyp8team2/vote/application/VoteService.java @@ -2,6 +2,7 @@ import com.swyp8team2.common.exception.BadRequestException; import com.swyp8team2.common.exception.ErrorCode; +import com.swyp8team2.post.domain.Post; import com.swyp8team2.post.domain.PostRepository; import com.swyp8team2.user.domain.User; import com.swyp8team2.user.domain.UserRepository; @@ -22,33 +23,35 @@ public class VoteService { private final PostRepository postRepository; @Transactional - public Long vote(Long userId, Long postId, Long imageId) { - User user = userRepository.findById(userId) + public Long vote(Long voterId, Long postId, Long imageId) { + User voter = userRepository.findById(voterId) .orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND)); - voteRepository.findByUserSeqAndPostId(user.getSeq(), postId) - .ifPresent(vote -> deleteExistingVote(postId, vote)); - Vote vote = createVote(postId, imageId, user.getSeq()); + deleteVoteIfExisting(postId, voter.getSeq()); + Vote vote = createVote(postId, imageId, voter.getSeq()); return vote.getId(); } - private Vote createVote(Long postId, Long imageId, String userSeq) { - Vote vote = voteRepository.save(Vote.of(postId, imageId, userSeq)); - postRepository.findById(postId) - .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)) - .vote(imageId); - return vote; + private void deleteVoteIfExisting(Long postId, String userSeq) { + voteRepository.findByUserSeqAndPostId(userSeq, postId) + .ifPresent(vote -> { + voteRepository.delete(vote); + postRepository.findById(postId) + .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)) + .cancelVote(vote.getPostImageId()); + }); } - private void deleteExistingVote(Long postId, Vote vote) { - voteRepository.delete(vote); - postRepository.findById(postId) - .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)) - .cancelVote(vote.getPostImageId()); + private Vote createVote(Long postId, Long imageId, String userSeq) { + Post post = postRepository.findById(postId) + .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)); + post.validateProgress(); + Vote vote = voteRepository.save(Vote.of(post.getId(), imageId, userSeq)); + post.vote(imageId); + return vote; } public Long guestVote(String guestId, Long postId, Long imageId) { - voteRepository.findByUserSeqAndPostId(guestId, postId) - .ifPresent(vote -> deleteExistingVote(postId, vote)); + deleteVoteIfExisting(postId, guestId); Vote vote = createVote(postId, imageId, guestId); return vote.getId(); } diff --git a/src/test/java/com/swyp8team2/vote/application/VoteServiceTest.java b/src/test/java/com/swyp8team2/vote/application/VoteServiceTest.java index c12afc49..04c545cd 100644 --- a/src/test/java/com/swyp8team2/vote/application/VoteServiceTest.java +++ b/src/test/java/com/swyp8team2/vote/application/VoteServiceTest.java @@ -1,9 +1,13 @@ package com.swyp8team2.vote.application; +import com.swyp8team2.common.exception.BadRequestException; +import com.swyp8team2.common.exception.ErrorCode; import com.swyp8team2.image.domain.ImageFile; import com.swyp8team2.image.domain.ImageFileRepository; import com.swyp8team2.post.domain.Post; +import com.swyp8team2.post.domain.PostImage; import com.swyp8team2.post.domain.PostRepository; +import com.swyp8team2.post.domain.State; import com.swyp8team2.support.IntegrationTest; import com.swyp8team2.user.domain.User; import com.swyp8team2.user.domain.UserRepository; @@ -13,10 +17,13 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.util.List; + import static com.swyp8team2.support.fixture.FixtureGenerator.createImageFile; import static com.swyp8team2.support.fixture.FixtureGenerator.createPost; import static com.swyp8team2.support.fixture.FixtureGenerator.createUser; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.*; class VoteServiceTest extends IntegrationTest { @@ -84,6 +91,31 @@ void vote_change() { ); } + @Test + @DisplayName("투표하기 - 투표 마감된 경우") + void vote_alreadyClosed() { + // given + User user = userRepository.save(createUser(1)); + ImageFile imageFile1 = imageFileRepository.save(createImageFile(1)); + ImageFile imageFile2 = imageFileRepository.save(createImageFile(2)); + Post post = postRepository.save(new Post( + null, + user.getId(), + "description", + State.CLOSED, + List.of( + PostImage.create("뽀또A", imageFile1.getId()), + PostImage.create("뽀또B", imageFile2.getId()) + ), + "shareUrl" + )); + + // when + assertThatThrownBy(() -> voteService.vote(user.getId(), post.getId(), post.getImages().get(0).getId())) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.POST_ALREADY_CLOSED.getMessage()); + } + @Test @DisplayName("게스트 투표하기") void guestVote() { @@ -133,4 +165,29 @@ void guestVote_change() { () -> assertThat(findPost.getImages().get(1).getVoteCount()).isEqualTo(1) ); } + + @Test + @DisplayName("게스트 투표하기 - 투표 마감된 경우") + void guestVote_alreadyClosed() { + // given + User user = userRepository.save(createUser(1)); + ImageFile imageFile1 = imageFileRepository.save(createImageFile(1)); + ImageFile imageFile2 = imageFileRepository.save(createImageFile(2)); + Post post = postRepository.save(new Post( + null, + user.getId(), + "description", + State.CLOSED, + List.of( + PostImage.create("뽀또A", imageFile1.getId()), + PostImage.create("뽀또B", imageFile2.getId()) + ), + "shareUrl" + )); + + // when + assertThatThrownBy(() -> voteService.guestVote("guestId", post.getId(), post.getImages().get(0).getId())) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.POST_ALREADY_CLOSED.getMessage()); + } } From 20a4fabeb502acb4352187a4fa53e9593ecc76d3 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 17:48:05 +0900 Subject: [PATCH 10/14] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/swyp8team2/post/application/PostService.java | 8 +++----- .../com/swyp8team2/post/presentation/PostController.java | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/swyp8team2/post/application/PostService.java b/src/main/java/com/swyp8team2/post/application/PostService.java index d8fdaf7b..7eea9767 100644 --- a/src/main/java/com/swyp8team2/post/application/PostService.java +++ b/src/main/java/com/swyp8team2/post/application/PostService.java @@ -132,12 +132,10 @@ private int getTotalVoteCount(List images) { return totalVoteCount; } - public void delete(Long userId, String postId) { - Post post = postRepository.findById(Long.valueOf(postId)) + @Transactional + public void delete(Long userId, Long postId) { + Post post = postRepository.findById(postId) .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)); - if (!post.getUserId().equals(userId)) { - throw new BadRequestException(ErrorCode.NOT_POST_AUTHOR); - } post.validateOwner(userId); postRepository.delete(post); } diff --git a/src/main/java/com/swyp8team2/post/presentation/PostController.java b/src/main/java/com/swyp8team2/post/presentation/PostController.java index f76bc24b..52ff749d 100644 --- a/src/main/java/com/swyp8team2/post/presentation/PostController.java +++ b/src/main/java/com/swyp8team2/post/presentation/PostController.java @@ -92,7 +92,7 @@ public ResponseEntity closePost( @DeleteMapping("/{postId}") public ResponseEntity deletePost( - @PathVariable("postId") String postId, + @PathVariable("postId") Long postId, @AuthenticationPrincipal UserInfo userInfo ) { postService.delete(userInfo.userId(), postId); From dd43ca84a3363a7367073cb362f2aab95aa677f5 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 17:48:38 +0900 Subject: [PATCH 11/14] =?UTF-8?q?test:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=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/PostServiceTest.java | 16 ++++++++++++++++ .../post/presentation/PostControllerTest.java | 5 +++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/swyp8team2/post/application/PostServiceTest.java b/src/test/java/com/swyp8team2/post/application/PostServiceTest.java index 0399ab04..53b927be 100644 --- a/src/test/java/com/swyp8team2/post/application/PostServiceTest.java +++ b/src/test/java/com/swyp8team2/post/application/PostServiceTest.java @@ -297,4 +297,20 @@ void close_notFoundPost() throws Exception { .isInstanceOf(BadRequestException.class) .hasMessage(ErrorCode.POST_NOT_FOUND.getMessage()); } + + @Test + @DisplayName("게시글 삭제") + void delete() throws Exception { + //given + User user = userRepository.save(createUser(1)); + ImageFile imageFile1 = imageFileRepository.save(createImageFile(1)); + ImageFile imageFile2 = imageFileRepository.save(createImageFile(2)); + Post post = postRepository.save(createPost(user.getId(), imageFile1, imageFile2, 1)); + + //when + postService.delete(user.getId(), post.getId()); + + //then + assertThat(postRepository.findById(post.getId())).isEmpty(); + } } diff --git a/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java b/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java index 781364b8..2615f5d9 100644 --- a/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java +++ b/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java @@ -160,15 +160,16 @@ void deletePost() throws Exception { //given //when then - mockMvc.perform(RestDocumentationRequestBuilders.delete("/posts/{shareUrl}", "shareUrl") + mockMvc.perform(RestDocumentationRequestBuilders.delete("/posts/{postId}", 1) .header(HttpHeaders.AUTHORIZATION, "Bearer token")) .andExpect(status().isOk()) .andDo(restDocs.document( requestHeaders(authorizationHeader()), pathParameters( - parameterWithName("shareUrl").description("게시글 공유 URL") + parameterWithName("postId").description("게시글 Id") ) )); + verify(postService, times(1)).delete(any(), any()); } @Test From 4361e970c29f34b4a2d707e47588346e7042add4 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 17:51:09 +0900 Subject: [PATCH 12/14] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=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 --- .../com/swyp8team2/user/application/UserService.java | 10 ++++++++++ .../swyp8team2/user/presentation/UserController.java | 5 ++++- .../user/presentation/dto/UserInfoResponse.java | 5 +++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/swyp8team2/user/application/UserService.java b/src/main/java/com/swyp8team2/user/application/UserService.java index 1d09f4bc..71e1c954 100644 --- a/src/main/java/com/swyp8team2/user/application/UserService.java +++ b/src/main/java/com/swyp8team2/user/application/UserService.java @@ -1,7 +1,10 @@ package com.swyp8team2.user.application; +import com.swyp8team2.common.exception.BadRequestException; +import com.swyp8team2.common.exception.ErrorCode; import com.swyp8team2.user.domain.User; import com.swyp8team2.user.domain.UserRepository; +import com.swyp8team2.user.presentation.dto.UserInfoResponse; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -9,6 +12,7 @@ import java.util.Optional; @Service +@Transactional(readOnly = true) @RequiredArgsConstructor public class UserService { @@ -29,4 +33,10 @@ private String getNickname(String email) { return Optional.ofNullable(email) .orElseGet(() -> "user_" + System.currentTimeMillis()); } + + public UserInfoResponse findById(Long userId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND)); + return UserInfoResponse.of(user); + } } diff --git a/src/main/java/com/swyp8team2/user/presentation/UserController.java b/src/main/java/com/swyp8team2/user/presentation/UserController.java index d232886b..dc5ac165 100644 --- a/src/main/java/com/swyp8team2/user/presentation/UserController.java +++ b/src/main/java/com/swyp8team2/user/presentation/UserController.java @@ -1,5 +1,6 @@ package com.swyp8team2.user.presentation; +import com.swyp8team2.user.application.UserService; import com.swyp8team2.user.presentation.dto.UserInfoResponse; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -13,8 +14,10 @@ @RequestMapping("/users") public class UserController { + private final UserService userService; + @GetMapping("/{userId}") public ResponseEntity findUserInfo(@PathVariable("userId") Long userId) { - return ResponseEntity.ok(new UserInfoResponse(1L, "nickname", "https://image.com/profile-image")); + return ResponseEntity.ok(userService.findById(userId)); } } diff --git a/src/main/java/com/swyp8team2/user/presentation/dto/UserInfoResponse.java b/src/main/java/com/swyp8team2/user/presentation/dto/UserInfoResponse.java index 19c204fb..13bf6e26 100644 --- a/src/main/java/com/swyp8team2/user/presentation/dto/UserInfoResponse.java +++ b/src/main/java/com/swyp8team2/user/presentation/dto/UserInfoResponse.java @@ -1,8 +1,13 @@ package com.swyp8team2.user.presentation.dto; +import com.swyp8team2.user.domain.User; + public record UserInfoResponse( Long id, String nickname, String profileUrl ) { + public static UserInfoResponse of(User user) { + return new UserInfoResponse(user.getId(), user.getNickname(), user.getProfileUrl()); + } } From 350ecc383218c54a8ac9c966ee79decdcdc4de89 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 17:51:17 +0900 Subject: [PATCH 13/14] =?UTF-8?q?test:=20=EC=9C=A0=EC=A0=80=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=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 --- src/test/java/com/swyp8team2/support/WebUnitTest.java | 4 ++++ .../com/swyp8team2/user/presentation/UserControllerTest.java | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/test/java/com/swyp8team2/support/WebUnitTest.java b/src/test/java/com/swyp8team2/support/WebUnitTest.java index fbb726db..ebd1b17c 100644 --- a/src/test/java/com/swyp8team2/support/WebUnitTest.java +++ b/src/test/java/com/swyp8team2/support/WebUnitTest.java @@ -6,6 +6,7 @@ import com.swyp8team2.comment.application.CommentService; import com.swyp8team2.image.application.ImageService; import com.swyp8team2.post.application.PostService; +import com.swyp8team2.user.application.UserService; import com.swyp8team2.vote.application.VoteService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -40,4 +41,7 @@ public abstract class WebUnitTest { @MockitoBean protected CommentService commentService; + + @MockitoBean + protected UserService userService; } diff --git a/src/test/java/com/swyp8team2/user/presentation/UserControllerTest.java b/src/test/java/com/swyp8team2/user/presentation/UserControllerTest.java index 63a47bd9..f11a7990 100644 --- a/src/test/java/com/swyp8team2/user/presentation/UserControllerTest.java +++ b/src/test/java/com/swyp8team2/user/presentation/UserControllerTest.java @@ -7,6 +7,7 @@ import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; import org.springframework.security.test.context.support.WithMockUser; +import static org.mockito.BDDMockito.given; import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; import static org.springframework.restdocs.payload.JsonFieldType.STRING; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; @@ -24,6 +25,8 @@ class UserControllerTest extends RestDocsTest { void findUserInfo() throws Exception { //given UserInfoResponse response = new UserInfoResponse(1L, "nickname", "https://image.com/profile-image"); + given(userService.findById(1L)) + .willReturn(response); //when then mockMvc.perform(RestDocumentationRequestBuilders.get("/users/{userId}", "1")) From 96d2256ce7b86a79a10caa27d11bcf362f10e810 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Tue, 25 Feb 2025 17:53:52 +0900 Subject: [PATCH 14/14] =?UTF-8?q?docs:=20=EA=B5=AC=ED=98=84=20=ED=98=84?= =?UTF-8?q?=ED=99=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/comments.adoc | 4 ++-- src/docs/asciidoc/posts.adoc | 2 +- src/docs/asciidoc/users.adoc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/docs/asciidoc/comments.adoc b/src/docs/asciidoc/comments.adoc index 2321a088..1491db53 100644 --- a/src/docs/asciidoc/comments.adoc +++ b/src/docs/asciidoc/comments.adoc @@ -2,12 +2,12 @@ == 댓글 API [[댓글-생성]] -=== 댓글 생성 +=== `POST` 댓글 생성 operation::comment-controller-test/create-comment[snippets='http-request,curl-request,path-parameters,request-headers,request-fields,http-response'] [[댓글-조회]] -=== 댓글 조회 +=== `GET` 댓글 조회 operation::comment-controller-test/find-comments[snippets='http-request,curl-request,path-parameters,http-response,response-fields'] diff --git a/src/docs/asciidoc/posts.adoc b/src/docs/asciidoc/posts.adoc index 82d9ea18..fc4dea9f 100644 --- a/src/docs/asciidoc/posts.adoc +++ b/src/docs/asciidoc/posts.adoc @@ -35,6 +35,6 @@ operation::post-controller-test/close-post[snippets='http-request,curl-request,p [[게시글-수정]] [[게시글-삭제]] -=== 게시글 삭제 (미구현) +=== `DELETE` 게시글 삭제 operation::post-controller-test/delete-post[snippets='http-request,curl-request,path-parameters,request-headers,http-response'] diff --git a/src/docs/asciidoc/users.adoc b/src/docs/asciidoc/users.adoc index a342582e..9faf6982 100644 --- a/src/docs/asciidoc/users.adoc +++ b/src/docs/asciidoc/users.adoc @@ -2,6 +2,6 @@ == 유저 API [[유저-정보-조회]] -=== 유저 정보 조회 (미구현) +=== `GET` 유저 정보 조회 operation::user-controller-test/find-user-info[snippets='http-request,curl-request,path-parameters,http-response,response-fields'] \ No newline at end of file