From 2545e0422903c1fbc6ed00117cb8ffdacf44b67d Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Mon, 28 Jul 2025 15:40:20 +0900 Subject: [PATCH 1/8] =?UTF-8?q?fix:=20self=EA=B0=80=20=EC=95=84=EB=8B=8C?= =?UTF-8?q?=20=EA=B2=BD=EC=9A=B0=EC=97=90=EB=8F=84=20=EB=A7=88=EA=B0=90?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=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 --- .../com/chooz/common/exception/ErrorCode.java | 1 + .../post/application/PostCommandService.java | 1 + src/main/java/com/chooz/post/domain/Post.java | 3 +++ .../java/com/chooz/post/domain/PostTest.java | 22 +++++++++++++++++-- 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/chooz/common/exception/ErrorCode.java b/src/main/java/com/chooz/common/exception/ErrorCode.java index ec823426..523e9138 100644 --- a/src/main/java/com/chooz/common/exception/ErrorCode.java +++ b/src/main/java/com/chooz/common/exception/ErrorCode.java @@ -45,6 +45,7 @@ public enum ErrorCode { SINGLE_POLL_ALLOWS_MAXIMUM_ONE_CHOICE("단일 투표인 경우 최대 하나의 선택지만 투표 가능"), DUPLICATE_POLL_CHOICE("복수 투표의 경우 중복된 선택지가 있으면 안 됨"), NOT_POST_POLL_CHOICE_ID("게시글의 투표 선택지가 아님"), + ONLY_SELF_CAN_CLOSE("작성자 마감의 경우, SELF 마감 방식만이 마감 가능합니다."), //401 EXPIRED_TOKEN("토큰이 만료됐습니다."), diff --git a/src/main/java/com/chooz/post/application/PostCommandService.java b/src/main/java/com/chooz/post/application/PostCommandService.java index f0da2e59..b67dda3b 100644 --- a/src/main/java/com/chooz/post/application/PostCommandService.java +++ b/src/main/java/com/chooz/post/application/PostCommandService.java @@ -89,4 +89,5 @@ public void close(Long userId, Long postId) { .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)); post.closeByAuthor(userId); } + } diff --git a/src/main/java/com/chooz/post/domain/Post.java b/src/main/java/com/chooz/post/domain/Post.java index 8b86e1dd..5b50c06e 100644 --- a/src/main/java/com/chooz/post/domain/Post.java +++ b/src/main/java/com/chooz/post/domain/Post.java @@ -140,6 +140,9 @@ public void closeByAuthor(Long userId) { if (!isAuthor(userId)) { throw new BadRequestException(ErrorCode.NOT_POST_AUTHOR); } + if (closeOption.getCloseType() != CloseType.SELF) { + throw new BadRequestException(ErrorCode.ONLY_SELF_CAN_CLOSE); + } close(); } diff --git a/src/test/java/com/chooz/post/domain/PostTest.java b/src/test/java/com/chooz/post/domain/PostTest.java index 5f967062..9235af1d 100644 --- a/src/test/java/com/chooz/post/domain/PostTest.java +++ b/src/test/java/com/chooz/post/domain/PostTest.java @@ -2,6 +2,7 @@ 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; @@ -9,6 +10,7 @@ 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.assertThat; @@ -131,7 +133,7 @@ void closeByAuthor() throws Exception { @Test @DisplayName("투표 마감 - 이미 마감된 게시글인 경우") - void close_ByAuthor_alreadyClosed() throws Exception { + void closeByAuthor_alreadyClosed() throws Exception { //given long userId = 1L; Post post = createPostBuilder() @@ -147,7 +149,7 @@ void close_ByAuthor_alreadyClosed() throws Exception { @Test @DisplayName("투표 마감 - 게시글 작성자가 아닌 경우") - void close_ByAuthor_notPostAuthor() throws Exception { + void closeByAuthor_notPostAuthor() throws Exception { //given long userId = 1L; Post post = createPostBuilder() @@ -159,4 +161,20 @@ void close_ByAuthor_notPostAuthor() throws Exception { .isInstanceOf(BadRequestException.class) .hasMessage(ErrorCode.NOT_POST_AUTHOR.getMessage()); } + + @Test + @DisplayName("투표 마감 - 마감 방식이 SELF가 아닌 경우") + void closeByAuthor_onlySelfCanClose() throws Exception { + //given + long userId = 1L; + Post post = createPostBuilder() + .closeOption(PostFixture.voterCloseOption(5)) + .userId(userId) + .build(); + + //when then + assertThatThrownBy(() -> post.closeByAuthor(userId)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.ONLY_SELF_CAN_CLOSE.getMessage()); + } } From cab70f64bf10abd76d1b08941476796e2a0b0ec6 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Wed, 30 Jul 2025 15:54:03 +0900 Subject: [PATCH 2/8] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/chooz/post/domain/PollOption.java | 16 +++--------- .../post/presentation/dto/CloseOptionDto.java | 2 ++ .../presentation/dto/CreatePostRequest.java | 25 ------------------- .../post/presentation/dto/PollOptionDto.java | 18 +++++++++++++ .../application/PostCommandServiceTest.java | 6 +++-- 5 files changed, 28 insertions(+), 39 deletions(-) create mode 100644 src/main/java/com/chooz/post/presentation/dto/PollOptionDto.java diff --git a/src/main/java/com/chooz/post/domain/PollOption.java b/src/main/java/com/chooz/post/domain/PollOption.java index 1cc4a359..73627d33 100644 --- a/src/main/java/com/chooz/post/domain/PollOption.java +++ b/src/main/java/com/chooz/post/domain/PollOption.java @@ -14,13 +14,13 @@ @Embeddable @NoArgsConstructor(access = AccessLevel.PROTECTED) public class PollOption { - + @Enumerated(EnumType.STRING) private PollType pollType; - + @Enumerated(EnumType.STRING) private Scope scope; - + @Enumerated(EnumType.STRING) private CommentActive commentActive; @@ -35,12 +35,4 @@ public static PollOption create(PollType pollType, Scope scope, CommentActive co validateNull(pollType, scope, commentActive); return new PollOption(pollType, scope, commentActive); } - - public void toggleScope() { - this.scope = scope.equals(Scope.PRIVATE) ? Scope.PUBLIC : Scope.PRIVATE; - } - - public void toggleCommentStatus() { - this.commentActive = commentActive.equals(CommentActive.CLOSED) ? CommentActive.OPEN : CommentActive.CLOSED; - } -} \ No newline at end of file +} diff --git a/src/main/java/com/chooz/post/presentation/dto/CloseOptionDto.java b/src/main/java/com/chooz/post/presentation/dto/CloseOptionDto.java index 1765d648..2e141a65 100644 --- a/src/main/java/com/chooz/post/presentation/dto/CloseOptionDto.java +++ b/src/main/java/com/chooz/post/presentation/dto/CloseOptionDto.java @@ -1,10 +1,12 @@ package com.chooz.post.presentation.dto; import com.chooz.post.domain.CloseType; +import jakarta.validation.constraints.NotNull; import java.time.LocalDateTime; public record CloseOptionDto( + @NotNull CloseType closeType, LocalDateTime closedAt, diff --git a/src/main/java/com/chooz/post/presentation/dto/CreatePostRequest.java b/src/main/java/com/chooz/post/presentation/dto/CreatePostRequest.java index ca711eba..a737425b 100644 --- a/src/main/java/com/chooz/post/presentation/dto/CreatePostRequest.java +++ b/src/main/java/com/chooz/post/presentation/dto/CreatePostRequest.java @@ -1,14 +1,9 @@ package com.chooz.post.presentation.dto; -import com.chooz.post.domain.CloseType; -import com.chooz.post.domain.CommentActive; -import com.chooz.post.domain.PollType; -import com.chooz.post.domain.Scope; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import java.time.LocalDateTime; import java.util.List; public record CreatePostRequest( @@ -31,24 +26,4 @@ public record CreatePostRequest( @NotNull CloseOptionDto closeOption ) { - - public record PollOptionDto( - @NotNull - Scope scope, - - @NotNull - PollType pollType, - - @NotNull - CommentActive commentActive - ) { } - - public record CloseOptionDto( - @NotNull - CloseType closeType, - - Integer maxVoterCount, - - LocalDateTime closedAt - ) { } } diff --git a/src/main/java/com/chooz/post/presentation/dto/PollOptionDto.java b/src/main/java/com/chooz/post/presentation/dto/PollOptionDto.java new file mode 100644 index 00000000..b5003caf --- /dev/null +++ b/src/main/java/com/chooz/post/presentation/dto/PollOptionDto.java @@ -0,0 +1,18 @@ +package com.chooz.post.presentation.dto; + +import com.chooz.post.domain.CommentActive; +import com.chooz.post.domain.PollType; +import com.chooz.post.domain.Scope; +import jakarta.validation.constraints.NotNull; + +public record PollOptionDto( + @NotNull + Scope scope, + + @NotNull + PollType pollType, + + @NotNull + CommentActive commentActive +) { } + diff --git a/src/test/java/com/chooz/post/application/PostCommandServiceTest.java b/src/test/java/com/chooz/post/application/PostCommandServiceTest.java index 28e5f2fa..50ed5aad 100644 --- a/src/test/java/com/chooz/post/application/PostCommandServiceTest.java +++ b/src/test/java/com/chooz/post/application/PostCommandServiceTest.java @@ -3,9 +3,11 @@ import com.chooz.common.exception.BadRequestException; import com.chooz.common.exception.ErrorCode; import com.chooz.post.domain.*; +import com.chooz.post.presentation.dto.CloseOptionDto; import com.chooz.post.presentation.dto.CreatePostRequest; import com.chooz.post.presentation.dto.CreatePostResponse; import com.chooz.post.presentation.dto.PollChoiceRequestDto; +import com.chooz.post.presentation.dto.PollOptionDto; import com.chooz.support.IntegrationTest; import com.chooz.support.fixture.PostFixture; import com.chooz.support.fixture.UserFixture; @@ -55,8 +57,8 @@ void create() throws Exception { new PollChoiceRequestDto("title1", "http://image1.com"), new PollChoiceRequestDto("title2", "http://image2.com") ), - new CreatePostRequest.PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), - new CreatePostRequest.CloseOptionDto(CloseType.SELF, null, null) + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), + new CloseOptionDto(CloseType.SELF, null, null) ); String shareUrl = "shareUrl"; given(shareUrlService.generateShareUrl()) From 56a20d8f12ee4f88e3370832d39c17ec8d09243c Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Wed, 30 Jul 2025 16:23:05 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=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 --- .../post/application/PostCommandService.java | 25 ++ .../chooz/post/application/PostService.java | 6 + .../chooz/post/application/PostValidator.java | 46 ++++ src/main/java/com/chooz/post/domain/Post.java | 14 ++ .../presentation/dto/UpdatePostRequest.java | 15 +- .../application/PostCommandServiceTest.java | 218 +++++++++++++++++- .../post/presentation/PostControllerTest.java | 70 +++++- 7 files changed, 375 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/chooz/post/application/PostValidator.java diff --git a/src/main/java/com/chooz/post/application/PostCommandService.java b/src/main/java/com/chooz/post/application/PostCommandService.java index b67dda3b..bcea434d 100644 --- a/src/main/java/com/chooz/post/application/PostCommandService.java +++ b/src/main/java/com/chooz/post/application/PostCommandService.java @@ -9,6 +9,7 @@ import com.chooz.post.domain.PostRepository; import com.chooz.post.presentation.dto.CreatePostRequest; import com.chooz.post.presentation.dto.CreatePostResponse; +import com.chooz.post.presentation.dto.UpdatePostRequest; import com.chooz.thumbnail.domain.Thumbnail; import com.chooz.thumbnail.domain.ThumbnailRepository; import lombok.RequiredArgsConstructor; @@ -26,6 +27,7 @@ public class PostCommandService { private final PostRepository postRepository; private final ShareUrlService shareUrlService; private final ThumbnailRepository thumbnailRepository; + private final PostValidator postValidator; public CreatePostResponse create(Long userId, CreatePostRequest request) { Post post = createPost(userId, request); @@ -90,4 +92,27 @@ public void close(Long userId, Long postId) { post.closeByAuthor(userId); } + @Transactional + public void update(Long userId, Long postId, UpdatePostRequest request) { + Post post = postRepository.findById(postId) + .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)); + + postValidator.validateUpdate(post, userId, request); + + post.update( + userId, + request.title(), + request.description(), + PollOption.create( + request.pollOption().pollType(), + request.pollOption().scope(), + request.pollOption().commentActive() + ), + new CloseOption( // 수정 예정 + request.closeOption().closeType(), + request.closeOption().closedAt(), + request.closeOption().maxVoterCount() + ) + ); + } } diff --git a/src/main/java/com/chooz/post/application/PostService.java b/src/main/java/com/chooz/post/application/PostService.java index 64afbab8..ab2b26e6 100644 --- a/src/main/java/com/chooz/post/application/PostService.java +++ b/src/main/java/com/chooz/post/application/PostService.java @@ -6,6 +6,7 @@ import com.chooz.post.presentation.dto.FeedResponse; import com.chooz.post.presentation.dto.MyPagePostResponse; import com.chooz.post.presentation.dto.PostResponse; +import com.chooz.post.presentation.dto.UpdatePostRequest; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -33,6 +34,11 @@ public void close(Long userId, Long postId) { postCommandService.close(userId, postId); } + @Transactional + 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); } diff --git a/src/main/java/com/chooz/post/application/PostValidator.java b/src/main/java/com/chooz/post/application/PostValidator.java new file mode 100644 index 00000000..1aee5f31 --- /dev/null +++ b/src/main/java/com/chooz/post/application/PostValidator.java @@ -0,0 +1,46 @@ +package com.chooz.post.application; + +import com.chooz.common.exception.BadRequestException; +import com.chooz.common.exception.ErrorCode; +import com.chooz.post.domain.CloseOption; +import com.chooz.post.domain.CloseType; +import com.chooz.post.domain.Post; +import com.chooz.post.presentation.dto.UpdatePostRequest; +import com.chooz.vote.domain.VoteRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +@Component +@RequiredArgsConstructor +public class PostValidator { + + private final VoteRepository voteRepository; + + public void validateUpdate(Post post, Long userId, UpdatePostRequest request) { + CloseOption closeOption = post.getCloseOption(); + CloseType closeType = closeOption.getCloseType(); + + if (!post.isAuthor(userId)) { + throw new BadRequestException(ErrorCode.NOT_POST_AUTHOR); + } + if (post.isClosed()) { + throw new BadRequestException(ErrorCode.POST_ALREADY_CLOSED); + } + + // 추후에 voterCount 수정 후 리팩터링 + if (closeType == CloseType.DATE) { + LocalDateTime newClosedAt = request.closeOption().closedAt(); + if (newClosedAt.isBefore(LocalDateTime.now()) || newClosedAt.isBefore(post.getCreatedAt().plusHours(1))) { + throw new BadRequestException(ErrorCode.INVALID_DATE_CLOSE_OPTION); + } + } else if (closeType == CloseType.VOTER) { + int newMaxVoterCount = request.closeOption().maxVoterCount(); + long voterCount = voteRepository.countVoterByPostId(post.getId()); + if (newMaxVoterCount < voterCount) { + throw new BadRequestException(ErrorCode.INVALID_VOTER_CLOSE_OPTION); + } + } + } +} diff --git a/src/main/java/com/chooz/post/domain/Post.java b/src/main/java/com/chooz/post/domain/Post.java index 5b50c06e..962202df 100644 --- a/src/main/java/com/chooz/post/domain/Post.java +++ b/src/main/java/com/chooz/post/domain/Post.java @@ -201,4 +201,18 @@ public boolean isClosed() { return this.status.equals(Status.CLOSED); } + public void update( + Long userId, + String title, + String description, + PollOption pollOption, + CloseOption closeOption + ) { + validateTitle(title); + validateDescription(description); + this.title = title; + this.description = description; + this.pollOption = pollOption; + this.closeOption = closeOption; + } } diff --git a/src/main/java/com/chooz/post/presentation/dto/UpdatePostRequest.java b/src/main/java/com/chooz/post/presentation/dto/UpdatePostRequest.java index 6e5e1293..f8329a78 100644 --- a/src/main/java/com/chooz/post/presentation/dto/UpdatePostRequest.java +++ b/src/main/java/com/chooz/post/presentation/dto/UpdatePostRequest.java @@ -1,9 +1,22 @@ package com.chooz.post.presentation.dto; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; public record UpdatePostRequest( + + @NotNull + String title, + + @NotNull + String description, + + @Valid + @NotNull + CloseOptionDto closeOption, + + @Valid @NotNull - String description + PollOptionDto pollOption ) { } diff --git a/src/test/java/com/chooz/post/application/PostCommandServiceTest.java b/src/test/java/com/chooz/post/application/PostCommandServiceTest.java index 50ed5aad..9c1697c0 100644 --- a/src/test/java/com/chooz/post/application/PostCommandServiceTest.java +++ b/src/test/java/com/chooz/post/application/PostCommandServiceTest.java @@ -8,18 +8,22 @@ import com.chooz.post.presentation.dto.CreatePostResponse; import com.chooz.post.presentation.dto.PollChoiceRequestDto; import com.chooz.post.presentation.dto.PollOptionDto; +import com.chooz.post.presentation.dto.UpdatePostRequest; import com.chooz.support.IntegrationTest; import com.chooz.support.fixture.PostFixture; import com.chooz.support.fixture.UserFixture; +import com.chooz.support.fixture.VoteFixture; import com.chooz.thumbnail.domain.Thumbnail; import com.chooz.thumbnail.domain.ThumbnailRepository; import com.chooz.user.domain.User; import com.chooz.user.domain.UserRepository; +import com.chooz.vote.domain.VoteRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.bean.override.mockito.MockitoBean; +import java.time.LocalDateTime; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -45,6 +49,9 @@ public class PostCommandServiceTest extends IntegrationTest { @Autowired ThumbnailRepository thumbnailRepository; + @Autowired + VoteRepository voteRepository; + @Test @DisplayName("게시글 작성") void create() throws Exception { @@ -101,8 +108,8 @@ void create_invalidPollChoiceCount() throws Exception { List.of( new PollChoiceRequestDto("title1", "http://image1.com") ), - new CreatePostRequest.PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), - new CreatePostRequest.CloseOptionDto(CloseType.SELF, null, null) + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), + new CloseOptionDto(CloseType.SELF, null, null) ); //when then assertThatThrownBy(() -> postService.create(userId, request)) @@ -122,8 +129,8 @@ void create_descriptionCountExceeded() throws Exception { new PollChoiceRequestDto("title1", "http://image1.com"), new PollChoiceRequestDto("title2", "http://image2.com") ), - new CreatePostRequest.PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), - new CreatePostRequest.CloseOptionDto(CloseType.SELF, null, null) + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), + new CloseOptionDto(CloseType.SELF, null, null) ); //when then @@ -144,8 +151,8 @@ void create_titleCountExceeded() throws Exception { new PollChoiceRequestDto("title1", "http://image1.com"), new PollChoiceRequestDto("title2", "http://image2.com") ), - new CreatePostRequest.PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), - new CreatePostRequest.CloseOptionDto(CloseType.SELF, null, null) + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), + new CloseOptionDto(CloseType.SELF, null, null) ); //when then @@ -225,4 +232,203 @@ void delete() throws Exception { //then assertThat(postRepository.findById(post.getId())).isEmpty(); } + + @Test + @DisplayName("게시글 수정") + void update() throws Exception { + //given + User user = userRepository.save(UserFixture.createDefaultUser()); + Post post = postRepository.save(PostFixture.createDefaultPost(user.getId())); + UpdatePostRequest request = new UpdatePostRequest( + "Updated Title", + "Updated Description", + new CloseOptionDto(CloseType.SELF, null, null), + new PollOptionDto(Scope.PRIVATE, PollType.MULTIPLE, CommentActive.CLOSED) + ); + + //when + postService.update(user.getId(), post.getId(), request); + + //then + Post updatedPost = postRepository.findById(post.getId()).orElseThrow(); + assertAll( + () -> assertThat(updatedPost.getTitle()).isEqualTo(request.title()), + () -> assertThat(updatedPost.getDescription()).isEqualTo(request.description()), + () -> assertThat(updatedPost.getPollOption().getPollType()).isEqualTo(request.pollOption().pollType()), + () -> assertThat(updatedPost.getPollOption().getScope()).isEqualTo(request.pollOption().scope()), + () -> assertThat(updatedPost.getPollOption().getCommentActive()).isEqualTo(request.pollOption().commentActive()), + () -> assertThat(updatedPost.getCloseOption().getCloseType()).isEqualTo(CloseType.SELF) + ); + } + + @Test + @DisplayName("게시글 수정 - 게시글 작성자가 아닐 경우") + void update_notPostAuthor() throws Exception { + //given + User user = userRepository.save(UserFixture.createDefaultUser()); + User anotherUser = userRepository.save(UserFixture.createDefaultUser()); + Post post = postRepository.save(PostFixture.createDefaultPost(user.getId())); + UpdatePostRequest request = new UpdatePostRequest( + "Updated Title", + "Updated Description", + new CloseOptionDto(CloseType.SELF, null, null), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + ); + + //when then + assertThatThrownBy(() -> postService.update(anotherUser.getId(), post.getId(), request)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.NOT_POST_AUTHOR.getMessage()); + } + + @Test + @DisplayName("게시글 수정 - 이미 마감된 게시글인 경우") + void update_alreadyClosed() throws Exception { + //given + User user = userRepository.save(UserFixture.createDefaultUser()); + Post post = postRepository.save( + PostFixture.createPostBuilder() + .userId(user.getId()) + .status(Status.CLOSED) + .build() + ); + UpdatePostRequest request = new UpdatePostRequest( + "Updated Title", + "Updated Description", + new CloseOptionDto(CloseType.SELF, null, null), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + ); + + //when then + assertThatThrownBy(() -> postService.update(user.getId(), post.getId(), request)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.POST_ALREADY_CLOSED.getMessage()); + } + + @Test + @DisplayName("게시글 수정 - 제목이 50자를 초과하는 경우") + void update_titleLengthExceeded() throws Exception { + //given + User user = userRepository.save(UserFixture.createDefaultUser()); + Post post = postRepository.save(PostFixture.createDefaultPost(user.getId())); + UpdatePostRequest request = new UpdatePostRequest( + "a".repeat(51), + "Updated Description", + new CloseOptionDto(CloseType.SELF, null, null), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + ); + + //when then + assertThatThrownBy(() -> postService.update(user.getId(), post.getId(), request)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.TITLE_LENGTH_EXCEEDED.getMessage()); + } + + @Test + @DisplayName("게시글 수정 - 설명이 100자를 초과하는 경우") + void update_descriptionLengthExceeded() throws Exception { + //given + User user = userRepository.save(UserFixture.createDefaultUser()); + Post post = postRepository.save(PostFixture.createDefaultPost(user.getId())); + UpdatePostRequest request = new UpdatePostRequest( + "Updated Title", + "a".repeat(101), + new CloseOptionDto(CloseType.SELF, null, null), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + ); + + //when then + assertThatThrownBy(() -> postService.update(user.getId(), post.getId(), request)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.DESCRIPTION_LENGTH_EXCEEDED.getMessage()); + } + + @Test + @DisplayName("게시글 수정 - DATE 타입 마감 옵션에서 과거 날짜로 설정하는 경우") + void update_invalidPastDateCloseOption() throws Exception { + //given + User user = userRepository.save(UserFixture.createDefaultUser()); + Post post = postRepository.save( + PostFixture.createPostBuilder() + .userId(user.getId()) + .closeOption( + CloseOption.create(CloseType.DATE, LocalDateTime.now().plusDays(1), null) + ) + .build() + ); + + UpdatePostRequest request = new UpdatePostRequest( + "Updated Title", + "Updated Description", + new CloseOptionDto(CloseType.DATE, LocalDateTime.now().minusDays(1), null), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + ); + + //when then + assertThatThrownBy(() -> postService.update(user.getId(), post.getId(), request)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.INVALID_DATE_CLOSE_OPTION.getMessage()); + } + + @Test + @DisplayName("게시글 수정 - DATE 타입 마감 옵션에서 생성 시간 기준 1시간 이내로 설정하는 경우") + void update_invalidDateCloseOptionWithinOneHour() throws Exception { + //given + User user = userRepository.save(UserFixture.createDefaultUser()); + Post post = postRepository.save( + PostFixture.createPostBuilder() + .userId(user.getId()) + .closeOption( + CloseOption.create(CloseType.DATE, LocalDateTime.now().plusDays(1), null) + ) + .build() + ); + + UpdatePostRequest request = new UpdatePostRequest( + "Updated Title", + "Updated Description", + new CloseOptionDto(CloseType.DATE, LocalDateTime.now().plusMinutes(30), null), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + ); + + //when then + assertThatThrownBy(() -> postService.update(user.getId(), post.getId(), request)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.INVALID_DATE_CLOSE_OPTION.getMessage()); + } + + @Test + @DisplayName("게시글 수정 - VOTER 타입 마감 옵션에서 현재 투표자 수보다 적은 값으로 설정하는 경우") + void update_invalidVoterCloseOption() throws Exception { + //given + User user = userRepository.save(UserFixture.createDefaultUser()); + User voter1 = userRepository.save(UserFixture.createDefaultUser()); + User voter2 = userRepository.save(UserFixture.createDefaultUser()); + + Post post = postRepository.save( + PostFixture.createPostBuilder() + .userId(user.getId()) + .closeOption( + CloseOption.create(CloseType.VOTER, null, 10) + ) + .build() + ); + + // 투표 데이터 생성 (2명의 투표자) + voteRepository.save(VoteFixture.createDefaultVote(voter1.getId(), post.getId(), post.getPollChoices().get(0).getId())); + voteRepository.save(VoteFixture.createDefaultVote(voter2.getId(), post.getId(), post.getPollChoices().get(1).getId())); + + UpdatePostRequest request = new UpdatePostRequest( + "Updated Title", + "Updated Description", + new CloseOptionDto(CloseType.VOTER, null, 1), // 1명으로 설정 (현재 2명 투표함) + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + ); + + //when then + assertThatThrownBy(() -> postService.update(user.getId(), post.getId(), request)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.INVALID_VOTER_CLOSE_OPTION.getMessage()); + } + } diff --git a/src/test/java/com/chooz/post/presentation/PostControllerTest.java b/src/test/java/com/chooz/post/presentation/PostControllerTest.java index 96347f72..ba34a2b3 100644 --- a/src/test/java/com/chooz/post/presentation/PostControllerTest.java +++ b/src/test/java/com/chooz/post/presentation/PostControllerTest.java @@ -6,7 +6,18 @@ import com.chooz.post.domain.PollType; import com.chooz.post.domain.Scope; import com.chooz.post.domain.Status; -import com.chooz.post.presentation.dto.*; +import com.chooz.post.presentation.dto.AuthorDto; +import com.chooz.post.presentation.dto.CloseOptionDto; +import com.chooz.post.presentation.dto.CreatePostRequest; +import com.chooz.post.presentation.dto.CreatePostResponse; +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.PollChoiceRequestDto; +import com.chooz.post.presentation.dto.PollChoiceResponse; +import com.chooz.post.presentation.dto.PollOptionDto; +import com.chooz.post.presentation.dto.PostResponse; +import com.chooz.post.presentation.dto.UpdatePostRequest; import com.chooz.support.RestDocsTest; import com.chooz.support.WithMockUserInfo; import org.junit.jupiter.api.DisplayName; @@ -21,9 +32,9 @@ import java.util.List; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; +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.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; @@ -49,8 +60,8 @@ void createPost() throws Exception { new PollChoiceRequestDto("title1", "http://image1.com"), new PollChoiceRequestDto("title2", "http://image2.com") ), - new CreatePostRequest.PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), - new CreatePostRequest.CloseOptionDto(CloseType.SELF, null, null) + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), + new CloseOptionDto(CloseType.SELF, null, null) ); CreatePostResponse response = new CreatePostResponse(1L, "shareUrl"); given(postService.create(any(), any())) @@ -105,11 +116,11 @@ void createPost() throws Exception { fieldWithPath("closeOption.closedAt") .type(JsonFieldType.STRING) .optional() - .description("투표 마감 시간"), + .description("투표 마감 시간 (now + 1h < closedAt)"), fieldWithPath("closeOption.maxVoterCount") .type(JsonFieldType.NUMBER) .optional() - .description("투표 최대 참여자 수") + .description("투표 최대 참여자 수 (1 < maxVoterCount < 999)") ), responseFields( fieldWithPath("postId") @@ -121,7 +132,7 @@ void createPost() throws Exception { ) )); } - + @Test @WithAnonymousUser @DisplayName("게시글 공유 url 상세 조회") @@ -488,7 +499,12 @@ void findVotedPost() throws Exception { @DisplayName("게시글 수정") void updatePost() throws Exception { //given - UpdatePostRequest request = new UpdatePostRequest("설명"); + UpdatePostRequest request = new UpdatePostRequest( + "title", + "description", + new CloseOptionDto(CloseType.SELF, null, null), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + ); //when then mockMvc.perform(post("/posts/{postId}/update", 1) @@ -502,11 +518,41 @@ void updatePost() throws Exception { parameterWithName("postId").description("게시글 Id") ), requestFields( + fieldWithPath("title") + .type(JsonFieldType.STRING) + .description("게시글 제목") + .attributes(constraints("1~50자 사이")), fieldWithPath("description") .type(JsonFieldType.STRING) .description("설명") - .attributes(constraints("0~100자 사이")) - ) + .attributes(constraints("0~100자 사이")), + fieldWithPath("pollOption") + .type(JsonFieldType.OBJECT) + .description("투표 옵션"), + fieldWithPath("pollOption.scope") + .type(JsonFieldType.STRING) + .description(enumDescription("투표 공개 범위", Scope.class)), + fieldWithPath("pollOption.pollType") + .type(JsonFieldType.STRING) + .description(enumDescription("투표 방식", PollType.class)), + fieldWithPath("pollOption.commentActive") + .type(JsonFieldType.STRING) + .description(enumDescription("게시글 댓글 활성화 여부", CommentActive.class)), + fieldWithPath("closeOption") + .type(JsonFieldType.OBJECT) + .description("투표 마감 옵션"), + fieldWithPath("closeOption.closeType") + .type(JsonFieldType.STRING) + .description(enumDescription("투표 마감 방식", CloseType.class)), + fieldWithPath("closeOption.closedAt") + .type(JsonFieldType.STRING) + .optional() + .description("투표 마감 시간 (now or createdAt + 1h < closedAt)"), + fieldWithPath("closeOption.maxVoterCount") + .type(JsonFieldType.NUMBER) + .optional() + .description("투표 최대 참여자 수 (1 or 현재 투표 참여자 수 < maxVoterCount < 999)") + ) )); } @@ -534,7 +580,7 @@ void closeByAuthorPost() throws Exception { @DisplayName("피드 조회") void findFeed() throws Exception { //given - var response = new CursorBasePaginatedResponse<> ( + var response = new CursorBasePaginatedResponse<>( 1L, false, List.of( From 0f72fd68d9b52dc2a7869c0b00cb252b2d28e0ea Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Mon, 4 Aug 2025 11:50:31 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/presentation/PostController.java | 4 +- .../presentation/dto/UpdatePostRequest.java | 13 +++- .../application/PostCommandServiceTest.java | 63 +++++++++++++------ .../post/presentation/PostControllerTest.java | 8 ++- 4 files changed, 62 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/chooz/post/presentation/PostController.java b/src/main/java/com/chooz/post/presentation/PostController.java index 12616fd6..0c15fe77 100644 --- a/src/main/java/com/chooz/post/presentation/PostController.java +++ b/src/main/java/com/chooz/post/presentation/PostController.java @@ -18,6 +18,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -51,7 +52,6 @@ public ResponseEntity findPostById( return ResponseEntity.ok(postService.findById(userId, postId)); } - @GetMapping("/shareUrl/{shareUrl}") public ResponseEntity findPostByShareUrl( @PathVariable("shareUrl") String shareUrl, @@ -63,7 +63,7 @@ public ResponseEntity findPostByShareUrl( return ResponseEntity.ok(postService.findByShareUrl(userId, shareUrl)); } - @PostMapping("/{postId}/update") + @PutMapping("/{postId}") public ResponseEntity updatePost( @PathVariable("postId") Long postId, @Valid @RequestBody UpdatePostRequest request, diff --git a/src/main/java/com/chooz/post/presentation/dto/UpdatePostRequest.java b/src/main/java/com/chooz/post/presentation/dto/UpdatePostRequest.java index f8329a78..ed5a1af9 100644 --- a/src/main/java/com/chooz/post/presentation/dto/UpdatePostRequest.java +++ b/src/main/java/com/chooz/post/presentation/dto/UpdatePostRequest.java @@ -1,11 +1,14 @@ package com.chooz.post.presentation.dto; import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import java.util.List; + public record UpdatePostRequest( - @NotNull + @NotBlank String title, @NotNull @@ -13,10 +16,14 @@ public record UpdatePostRequest( @Valid @NotNull - CloseOptionDto closeOption, + List pollChoices, + + @Valid + @NotNull + PollOptionDto pollOption, @Valid @NotNull - PollOptionDto pollOption + CloseOptionDto closeOption ) { } diff --git a/src/test/java/com/chooz/post/application/PostCommandServiceTest.java b/src/test/java/com/chooz/post/application/PostCommandServiceTest.java index 9c1697c0..357fc364 100644 --- a/src/test/java/com/chooz/post/application/PostCommandServiceTest.java +++ b/src/test/java/com/chooz/post/application/PostCommandServiceTest.java @@ -242,8 +242,12 @@ void update() throws Exception { UpdatePostRequest request = new UpdatePostRequest( "Updated Title", "Updated Description", - new CloseOptionDto(CloseType.SELF, null, null), - new PollOptionDto(Scope.PRIVATE, PollType.MULTIPLE, CommentActive.CLOSED) + List.of( + new PollChoiceRequestDto("title1", "http://image1.com"), + new PollChoiceRequestDto("title2", "http://image2.com") + ), + new PollOptionDto(Scope.PRIVATE, PollType.MULTIPLE, CommentActive.CLOSED), + new CloseOptionDto(CloseType.SELF, null, null) ); //when @@ -271,10 +275,15 @@ void update_notPostAuthor() throws Exception { UpdatePostRequest request = new UpdatePostRequest( "Updated Title", "Updated Description", - new CloseOptionDto(CloseType.SELF, null, null), - new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + List.of( + new PollChoiceRequestDto("title1", "http://image1.com"), + new PollChoiceRequestDto("title2", "http://image2.com") + ), + new PollOptionDto(Scope.PRIVATE, PollType.MULTIPLE, CommentActive.CLOSED), + new CloseOptionDto(CloseType.SELF, null, null) ); + //when then assertThatThrownBy(() -> postService.update(anotherUser.getId(), post.getId(), request)) .isInstanceOf(BadRequestException.class) @@ -295,10 +304,15 @@ void update_alreadyClosed() throws Exception { UpdatePostRequest request = new UpdatePostRequest( "Updated Title", "Updated Description", - new CloseOptionDto(CloseType.SELF, null, null), - new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + List.of( + new PollChoiceRequestDto("title1", "http://image1.com"), + new PollChoiceRequestDto("title2", "http://image2.com") + ), + new PollOptionDto(Scope.PRIVATE, PollType.MULTIPLE, CommentActive.CLOSED), + new CloseOptionDto(CloseType.SELF, null, null) ); + //when then assertThatThrownBy(() -> postService.update(user.getId(), post.getId(), request)) .isInstanceOf(BadRequestException.class) @@ -312,12 +326,17 @@ void update_titleLengthExceeded() throws Exception { User user = userRepository.save(UserFixture.createDefaultUser()); Post post = postRepository.save(PostFixture.createDefaultPost(user.getId())); UpdatePostRequest request = new UpdatePostRequest( - "a".repeat(51), + "Updated Title", "Updated Description", - new CloseOptionDto(CloseType.SELF, null, null), - new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + List.of( + new PollChoiceRequestDto("title1", "http://image1.com"), + new PollChoiceRequestDto("title2", "http://image2.com") + ), + new PollOptionDto(Scope.PRIVATE, PollType.MULTIPLE, CommentActive.CLOSED), + new CloseOptionDto(CloseType.SELF, null, null) ); + //when then assertThatThrownBy(() -> postService.update(user.getId(), post.getId(), request)) .isInstanceOf(BadRequestException.class) @@ -332,11 +351,16 @@ void update_descriptionLengthExceeded() throws Exception { Post post = postRepository.save(PostFixture.createDefaultPost(user.getId())); UpdatePostRequest request = new UpdatePostRequest( "Updated Title", - "a".repeat(101), - new CloseOptionDto(CloseType.SELF, null, null), - new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + "Updated Description", + List.of( + new PollChoiceRequestDto("title1", "http://image1.com"), + new PollChoiceRequestDto("title2", "http://image2.com") + ), + new PollOptionDto(Scope.PRIVATE, PollType.MULTIPLE, CommentActive.CLOSED), + new CloseOptionDto(CloseType.SELF, null, null) ); + //when then assertThatThrownBy(() -> postService.update(user.getId(), post.getId(), request)) .isInstanceOf(BadRequestException.class) @@ -360,8 +384,9 @@ void update_invalidPastDateCloseOption() throws Exception { UpdatePostRequest request = new UpdatePostRequest( "Updated Title", "Updated Description", - new CloseOptionDto(CloseType.DATE, LocalDateTime.now().minusDays(1), null), - new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + List.of(), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), + new CloseOptionDto(CloseType.DATE, LocalDateTime.now().minusDays(1), null) ); //when then @@ -387,8 +412,9 @@ void update_invalidDateCloseOptionWithinOneHour() throws Exception { UpdatePostRequest request = new UpdatePostRequest( "Updated Title", "Updated Description", - new CloseOptionDto(CloseType.DATE, LocalDateTime.now().plusMinutes(30), null), - new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + List.of(), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), + new CloseOptionDto(CloseType.DATE, LocalDateTime.now().plusMinutes(30), null) ); //when then @@ -421,8 +447,9 @@ void update_invalidVoterCloseOption() throws Exception { UpdatePostRequest request = new UpdatePostRequest( "Updated Title", "Updated Description", - new CloseOptionDto(CloseType.VOTER, null, 1), // 1명으로 설정 (현재 2명 투표함) - new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + List.of(), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), + new CloseOptionDto(CloseType.VOTER, null, 1) // 1명으로 설정 (현재 2명 투표함) ); //when then diff --git a/src/test/java/com/chooz/post/presentation/PostControllerTest.java b/src/test/java/com/chooz/post/presentation/PostControllerTest.java index ba34a2b3..cc6da110 100644 --- a/src/test/java/com/chooz/post/presentation/PostControllerTest.java +++ b/src/test/java/com/chooz/post/presentation/PostControllerTest.java @@ -43,6 +43,7 @@ import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -502,12 +503,13 @@ void updatePost() throws Exception { UpdatePostRequest request = new UpdatePostRequest( "title", "description", - new CloseOptionDto(CloseType.SELF, null, null), - new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN) + List.of(), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), + new CloseOptionDto(CloseType.SELF, null, null) ); //when then - mockMvc.perform(post("/posts/{postId}/update", 1) + mockMvc.perform(put("/posts/{postId}", 1) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) .header(HttpHeaders.AUTHORIZATION, "Bearer token")) From 69a387d58d41f9687cdf74d89ab005d7ab33a43a Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Mon, 4 Aug 2025 12:06:58 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/application/PostCommandService.java | 2 +- .../chooz/post/application/PostValidator.java | 37 ++++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/chooz/post/application/PostCommandService.java b/src/main/java/com/chooz/post/application/PostCommandService.java index bcea434d..072141a0 100644 --- a/src/main/java/com/chooz/post/application/PostCommandService.java +++ b/src/main/java/com/chooz/post/application/PostCommandService.java @@ -108,7 +108,7 @@ public void update(Long userId, Long postId, UpdatePostRequest request) { request.pollOption().scope(), request.pollOption().commentActive() ), - new CloseOption( // 수정 예정 + new CloseOption( request.closeOption().closeType(), request.closeOption().closedAt(), request.closeOption().maxVoterCount() diff --git a/src/main/java/com/chooz/post/application/PostValidator.java b/src/main/java/com/chooz/post/application/PostValidator.java index 1aee5f31..5aab268f 100644 --- a/src/main/java/com/chooz/post/application/PostValidator.java +++ b/src/main/java/com/chooz/post/application/PostValidator.java @@ -11,6 +11,7 @@ import org.springframework.stereotype.Component; import java.time.LocalDateTime; +import java.util.Objects; @Component @RequiredArgsConstructor @@ -29,18 +30,34 @@ public void validateUpdate(Post post, Long userId, UpdatePostRequest request) { throw new BadRequestException(ErrorCode.POST_ALREADY_CLOSED); } - // 추후에 voterCount 수정 후 리팩터링 + LocalDateTime newClosedAt = request.closeOption().closedAt(); + Integer newMaxVoterCount = request.closeOption().maxVoterCount(); if (closeType == CloseType.DATE) { - LocalDateTime newClosedAt = request.closeOption().closedAt(); - if (newClosedAt.isBefore(LocalDateTime.now()) || newClosedAt.isBefore(post.getCreatedAt().plusHours(1))) { - throw new BadRequestException(ErrorCode.INVALID_DATE_CLOSE_OPTION); - } + validateUpdateClosedAt(post, newClosedAt, newMaxVoterCount); } else if (closeType == CloseType.VOTER) { - int newMaxVoterCount = request.closeOption().maxVoterCount(); - long voterCount = voteRepository.countVoterByPostId(post.getId()); - if (newMaxVoterCount < voterCount) { - throw new BadRequestException(ErrorCode.INVALID_VOTER_CLOSE_OPTION); - } + validateUpdateMaxVoter(post, newClosedAt, newMaxVoterCount); + } + } + + private void validateUpdateMaxVoter(Post post, LocalDateTime newClosedAt, Integer newMaxVoterCount) { + if (Objects.nonNull(newClosedAt) || Objects.isNull(newMaxVoterCount)) { + throw new BadRequestException(ErrorCode.INVALID_VOTER_CLOSE_OPTION); + } + long voterCount = voteRepository.countVoterByPostId(post.getId()); + if (newMaxVoterCount < 1 || newMaxVoterCount > 999) { + throw new BadRequestException(ErrorCode.INVALID_VOTER_CLOSE_OPTION); + } + if (newMaxVoterCount < voterCount) { + throw new BadRequestException(ErrorCode.INVALID_VOTER_CLOSE_OPTION); + } + } + + private static void validateUpdateClosedAt(Post post, LocalDateTime newClosedAt, Integer newMaxVoterCount) { + if (Objects.isNull(newClosedAt) || Objects.nonNull(newMaxVoterCount)) { + throw new BadRequestException(ErrorCode.INVALID_DATE_CLOSE_OPTION); + } + if (newClosedAt.isBefore(LocalDateTime.now()) || newClosedAt.isBefore(post.getCreatedAt().plusHours(1))) { + throw new BadRequestException(ErrorCode.INVALID_DATE_CLOSE_OPTION); } } } From 515aff8edf6b52b55821531ad2c00b909a522d98 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Mon, 4 Aug 2025 12:07:49 +0900 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/chooz/post/presentation/PostController.java | 1 + .../java/com/chooz/post/presentation/PostControllerTest.java | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/main/java/com/chooz/post/presentation/PostController.java b/src/main/java/com/chooz/post/presentation/PostController.java index 0c15fe77..b39aed0f 100644 --- a/src/main/java/com/chooz/post/presentation/PostController.java +++ b/src/main/java/com/chooz/post/presentation/PostController.java @@ -69,6 +69,7 @@ public ResponseEntity updatePost( @Valid @RequestBody UpdatePostRequest request, @AuthenticationPrincipal UserInfo userInfo ) { + postService.update(userInfo.userId(), postId, request); return ResponseEntity.ok().build(); } diff --git a/src/test/java/com/chooz/post/presentation/PostControllerTest.java b/src/test/java/com/chooz/post/presentation/PostControllerTest.java index cc6da110..790478a1 100644 --- a/src/test/java/com/chooz/post/presentation/PostControllerTest.java +++ b/src/test/java/com/chooz/post/presentation/PostControllerTest.java @@ -528,6 +528,10 @@ void updatePost() throws Exception { .type(JsonFieldType.STRING) .description("설명") .attributes(constraints("0~100자 사이")), + fieldWithPath("pollChoices") + .type(JsonFieldType.ARRAY) + .description("투표 선택지") + .attributes(constraints("최소 2개 최대 10개")), fieldWithPath("pollOption") .type(JsonFieldType.OBJECT) .description("투표 옵션"), From 051905140920e44eb3462e7cedc592844c2806b2 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Mon, 4 Aug 2025 12:31:03 +0900 Subject: [PATCH 7/8] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=A1=B0=ED=9A=8C=20api=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 --- .../post/application/PostQueryService.java | 18 +++-- .../chooz/post/application/PostService.java | 5 ++ .../post/domain/PollChoiceRepository.java | 1 - .../com/chooz/post/domain/PostRepository.java | 2 + .../post/presentation/PostController.java | 9 +++ .../post/presentation/UpdatePostResponse.java | 55 +++++++++++++++ .../presentation/dto/PollChoiceResponse.java | 7 +- .../dto/PollChoiceVoteResponse.java | 9 +++ .../post/presentation/dto/PostResponse.java | 17 +---- .../application/PostCommandServiceTest.java | 4 +- .../application/PostQueryServiceTest.java | 4 +- .../post/presentation/PostControllerTest.java | 67 +++++++++++++++++-- 12 files changed, 162 insertions(+), 36 deletions(-) create mode 100644 src/main/java/com/chooz/post/presentation/UpdatePostResponse.java create mode 100644 src/main/java/com/chooz/post/presentation/dto/PollChoiceVoteResponse.java diff --git a/src/main/java/com/chooz/post/application/PostQueryService.java b/src/main/java/com/chooz/post/application/PostQueryService.java index fb3ca07a..caf380e7 100644 --- a/src/main/java/com/chooz/post/application/PostQueryService.java +++ b/src/main/java/com/chooz/post/application/PostQueryService.java @@ -10,12 +10,13 @@ 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.presentation.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.PollChoiceResponse; +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; @@ -70,18 +71,18 @@ private PostResponse createPostResponse(Long userId, Post post) { .distinct() .count(); boolean isAuthor = post.getUserId().equals(userId); - List pollChoiceResponseList = createPollChoiceResponse( + List pollChoiceVoteResponseList = createPollChoiceResponse( userId, post.getPollChoices(), voteList ); - return PostResponse.of(post, author, pollChoiceResponseList, isAuthor, commentCount, voterCount); + return PostResponse.of(post, author, pollChoiceVoteResponseList, isAuthor, commentCount, voterCount); } - private List createPollChoiceResponse(Long userId, List pollChoices, List voteList) { + private List createPollChoiceResponse(Long userId, List pollChoices, List voteList) { return pollChoices.stream() - .map(pollChoice -> new PollChoiceResponse( + .map(pollChoice -> new PollChoiceVoteResponse( pollChoice.getId(), pollChoice.getTitle(), pollChoice.getImageUrl(), @@ -177,4 +178,11 @@ private FeedResponse createFeedResponse(Long userId, FeedDto feedDto) { boolean isAuthor = feedDto.postUserId().equals(userId); return FeedResponse.of(feedDto, author, isAuthor); } + + public UpdatePostResponse findUpdatePost(Long userId, Long postId) { + Post post = postRepository.findByIdAndUserId(postId, userId) + .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)); + + return UpdatePostResponse.of(post); + } } diff --git a/src/main/java/com/chooz/post/application/PostService.java b/src/main/java/com/chooz/post/application/PostService.java index ab2b26e6..cd3ccf81 100644 --- a/src/main/java/com/chooz/post/application/PostService.java +++ b/src/main/java/com/chooz/post/application/PostService.java @@ -1,6 +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.CreatePostRequest; import com.chooz.post.presentation.dto.CreatePostResponse; import com.chooz.post.presentation.dto.FeedResponse; @@ -58,4 +59,8 @@ public PostResponse findByShareUrl(Long userId, String shareUrl) { public CursorBasePaginatedResponse findFeed(Long userId, Long cursor, int size) { return postQueryService.findFeed(userId, cursor, size); } + + public UpdatePostResponse findUpdatePost(Long userId, Long postId) { + return postQueryService.findUpdatePost(userId, postId); + } } \ No newline at end of file diff --git a/src/main/java/com/chooz/post/domain/PollChoiceRepository.java b/src/main/java/com/chooz/post/domain/PollChoiceRepository.java index baf8dd5b..86e4c22d 100644 --- a/src/main/java/com/chooz/post/domain/PollChoiceRepository.java +++ b/src/main/java/com/chooz/post/domain/PollChoiceRepository.java @@ -1,7 +1,6 @@ package com.chooz.post.domain; import com.chooz.post.application.dto.PollChoiceVoteInfo; -import com.chooz.post.presentation.dto.PollChoiceResponse; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; diff --git a/src/main/java/com/chooz/post/domain/PostRepository.java b/src/main/java/com/chooz/post/domain/PostRepository.java index 7ad10ef5..c79fb021 100644 --- a/src/main/java/com/chooz/post/domain/PostRepository.java +++ b/src/main/java/com/chooz/post/domain/PostRepository.java @@ -144,4 +144,6 @@ Slice findVotedPostsWithVoteCount( @Param("postId") Long postId, Pageable pageable ); + + Optional findByIdAndUserId(Long id, Long userId); } diff --git a/src/main/java/com/chooz/post/presentation/PostController.java b/src/main/java/com/chooz/post/presentation/PostController.java index b39aed0f..565d3218 100644 --- a/src/main/java/com/chooz/post/presentation/PostController.java +++ b/src/main/java/com/chooz/post/presentation/PostController.java @@ -73,6 +73,15 @@ public ResponseEntity updatePost( return ResponseEntity.ok().build(); } + @GetMapping("/{postId}/update") + public ResponseEntity updatePost( + @PathVariable("postId") Long postId, + @AuthenticationPrincipal UserInfo userInfo + ) { + UpdatePostResponse response = postService.findUpdatePost(userInfo.userId(), postId); + return ResponseEntity.ok(response); + } + @PostMapping("/{postId}/close") public ResponseEntity closePost( @PathVariable("postId") Long postId, diff --git a/src/main/java/com/chooz/post/presentation/UpdatePostResponse.java b/src/main/java/com/chooz/post/presentation/UpdatePostResponse.java new file mode 100644 index 00000000..95811165 --- /dev/null +++ b/src/main/java/com/chooz/post/presentation/UpdatePostResponse.java @@ -0,0 +1,55 @@ +package com.chooz.post.presentation; + +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; + +public record UpdatePostResponse( + Long id, + String title, + String description, + List pollChoices, + String shareUrl, + Status status, + PollOptionDto pollOption, + CloseOptionDto closeOption, + LocalDateTime createdAt +) { + + public static UpdatePostResponse of(Post post) { + PollOption pollOption = post.getPollOption(); + CloseOption closeOption = post.getCloseOption(); + return new UpdatePostResponse( + post.getId(), + post.getTitle(), + post.getDescription(), + post.getPollChoices() + .stream() + .map(pollChoice -> new PollChoiceResponse( + pollChoice.getId(), + pollChoice.getTitle(), + pollChoice.getImageUrl() + )).toList(), + post.getShareUrl(), + post.getStatus(), + new PollOptionDto( + pollOption.getScope(), + pollOption.getPollType(), + pollOption.getCommentActive() + ), + new CloseOptionDto( + closeOption.getCloseType(), + closeOption.getClosedAt(), + closeOption.getMaxVoterCount() + ), + post.getCreatedAt() + ); + } +} diff --git a/src/main/java/com/chooz/post/presentation/dto/PollChoiceResponse.java b/src/main/java/com/chooz/post/presentation/dto/PollChoiceResponse.java index f3c2c99d..eb0485c3 100644 --- a/src/main/java/com/chooz/post/presentation/dto/PollChoiceResponse.java +++ b/src/main/java/com/chooz/post/presentation/dto/PollChoiceResponse.java @@ -1,9 +1,4 @@ package com.chooz.post.presentation.dto; -public record PollChoiceResponse( - Long id, - String title, - String imageUrl, - Long voteId -) { +public record PollChoiceResponse(Long id, String title, String imageUrl) { } diff --git a/src/main/java/com/chooz/post/presentation/dto/PollChoiceVoteResponse.java b/src/main/java/com/chooz/post/presentation/dto/PollChoiceVoteResponse.java new file mode 100644 index 00000000..c99c25c4 --- /dev/null +++ b/src/main/java/com/chooz/post/presentation/dto/PollChoiceVoteResponse.java @@ -0,0 +1,9 @@ +package com.chooz.post.presentation.dto; + +public record PollChoiceVoteResponse( + Long id, + String title, + String imageUrl, + Long voteId +) { +} diff --git a/src/main/java/com/chooz/post/presentation/dto/PostResponse.java b/src/main/java/com/chooz/post/presentation/dto/PostResponse.java index 4f621b38..2a1b49d6 100644 --- a/src/main/java/com/chooz/post/presentation/dto/PostResponse.java +++ b/src/main/java/com/chooz/post/presentation/dto/PostResponse.java @@ -1,11 +1,8 @@ package com.chooz.post.presentation.dto; import com.chooz.post.domain.CloseOption; -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.Scope; import com.chooz.post.domain.Status; import com.chooz.user.domain.User; @@ -17,7 +14,7 @@ public record PostResponse( String title, String description, AuthorDto author, - List pollChoices, + List pollChoices, String shareUrl, boolean isAuthor, Status status, @@ -28,18 +25,10 @@ public record PostResponse( LocalDateTime createdAt ) { - public record PollOptionDto( - PollType pollType, - - Scope scope, - - CommentActive commentActive - ) { } - public static PostResponse of( Post post, User user, - List pollChoices, + List pollChoices, boolean isAuthor, long commentCount, long voterCount @@ -56,8 +45,8 @@ public static PostResponse of( isAuthor, post.getStatus(), new PollOptionDto( - pollOption.getPollType(), pollOption.getScope(), + pollOption.getPollType(), pollOption.getCommentActive() ), new CloseOptionDto( diff --git a/src/test/java/com/chooz/post/application/PostCommandServiceTest.java b/src/test/java/com/chooz/post/application/PostCommandServiceTest.java index 357fc364..620c259b 100644 --- a/src/test/java/com/chooz/post/application/PostCommandServiceTest.java +++ b/src/test/java/com/chooz/post/application/PostCommandServiceTest.java @@ -326,7 +326,7 @@ void update_titleLengthExceeded() throws Exception { User user = userRepository.save(UserFixture.createDefaultUser()); Post post = postRepository.save(PostFixture.createDefaultPost(user.getId())); UpdatePostRequest request = new UpdatePostRequest( - "Updated Title", + "a".repeat(51), "Updated Description", List.of( new PollChoiceRequestDto("title1", "http://image1.com"), @@ -351,7 +351,7 @@ void update_descriptionLengthExceeded() throws Exception { Post post = postRepository.save(PostFixture.createDefaultPost(user.getId())); UpdatePostRequest request = new UpdatePostRequest( "Updated Title", - "Updated Description", + "a".repeat(101), List.of( new PollChoiceRequestDto("title1", "http://image1.com"), new PollChoiceRequestDto("title2", "http://image2.com") diff --git a/src/test/java/com/chooz/post/application/PostQueryServiceTest.java b/src/test/java/com/chooz/post/application/PostQueryServiceTest.java index 5dbeb3b9..e9c1d005 100644 --- a/src/test/java/com/chooz/post/application/PostQueryServiceTest.java +++ b/src/test/java/com/chooz/post/application/PostQueryServiceTest.java @@ -5,7 +5,7 @@ import com.chooz.common.dto.CursorBasePaginatedResponse; import com.chooz.post.domain.*; import com.chooz.post.presentation.dto.FeedResponse; -import com.chooz.post.presentation.dto.PollChoiceResponse; +import com.chooz.post.presentation.dto.PollChoiceVoteResponse; import com.chooz.post.presentation.dto.PostResponse; import com.chooz.support.IntegrationTest; import com.chooz.support.fixture.VoteFixture; @@ -64,7 +64,7 @@ void findById() throws Exception { PostResponse response = postService.findById(user1.getId(), post.getId()); //then - List pollChoices = response.pollChoices(); + List pollChoices = response.pollChoices(); assertAll( () -> assertThat(response.id()).isEqualTo(post.getId()), () -> assertThat(response.description()).isEqualTo(post.getDescription()), diff --git a/src/test/java/com/chooz/post/presentation/PostControllerTest.java b/src/test/java/com/chooz/post/presentation/PostControllerTest.java index 790478a1..907238a4 100644 --- a/src/test/java/com/chooz/post/presentation/PostControllerTest.java +++ b/src/test/java/com/chooz/post/presentation/PostControllerTest.java @@ -15,6 +15,7 @@ import com.chooz.post.presentation.dto.MyPagePostResponse; import com.chooz.post.presentation.dto.PollChoiceRequestDto; import com.chooz.post.presentation.dto.PollChoiceResponse; +import com.chooz.post.presentation.dto.PollChoiceVoteResponse; import com.chooz.post.presentation.dto.PollOptionDto; import com.chooz.post.presentation.dto.PostResponse; import com.chooz.post.presentation.dto.UpdatePostRequest; @@ -148,13 +149,13 @@ void findPost_shareUrl() throws Exception { "https://image.chooz.site/profile-image" ), List.of( - new PollChoiceResponse(1L, "title1", "https://image.chooz.site/image/1", 1L), - new PollChoiceResponse(2L, "title2", "https://image.chooz.site/image/2", null) + new PollChoiceVoteResponse(1L, "title1", "https://image.chooz.site/image/1", 1L), + new PollChoiceVoteResponse(2L, "title2", "https://image.chooz.site/image/2", null) ), "https://chooz.site/shareurl", true, Status.PROGRESS, - new PostResponse.PollOptionDto(PollType.SINGLE, Scope.PUBLIC, CommentActive.OPEN), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), new CloseOptionDto(CloseType.SELF, null, null), 0L, 1L, @@ -217,13 +218,13 @@ void findPost() throws Exception { "https://image.chooz.site/profile-image" ), List.of( - new PollChoiceResponse(1L, "title1", "https://image.chooz.site/image/1", 1L), - new PollChoiceResponse(2L, "title2", "https://image.chooz.site/image/2", null) + new PollChoiceVoteResponse(1L, "title1", "https://image.chooz.site/image/1", 1L), + new PollChoiceVoteResponse(2L, "title2", "https://image.chooz.site/image/2", null) ), "https://chooz.site/shareurl", true, Status.PROGRESS, - new PostResponse.PollOptionDto(PollType.SINGLE, Scope.PUBLIC, CommentActive.OPEN), + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), new CloseOptionDto(CloseType.SELF, null, null), 0L, 1L, @@ -562,6 +563,60 @@ void updatePost() throws Exception { )); } + @Test + @WithMockUserInfo + @DisplayName("게시글 수정 조회") + void findPost_update() throws Exception { + UpdatePostResponse response = new UpdatePostResponse( + 1L, + "title", + "description", + List.of( + new PollChoiceResponse(1L, "title1", "https://image.chooz.site/image/1"), + new PollChoiceResponse(2L, "title2", "https://image.chooz.site/image/2") + ), + "https://chooz.site/shareurl", + Status.PROGRESS, + new PollOptionDto(Scope.PUBLIC, PollType.SINGLE, CommentActive.OPEN), + new CloseOptionDto(CloseType.SELF, null, null), + LocalDateTime.of(2025, 2, 13, 12, 0) + ); + //given + given(postService.findUpdatePost(any(), any())) + .willReturn(response); + + //when then + mockMvc.perform(RestDocumentationRequestBuilders.get("/posts/{postId}/update", "1") + .header(HttpHeaders.AUTHORIZATION, "Bearer token")) + .andExpect(status().isOk()) + .andExpect(content().json(objectMapper.writeValueAsString(response))) + .andDo(restDocs.document( + pathParameters( + parameterWithName("postId").description("게시글 Id") + ), + responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("게시글 Id"), + fieldWithPath("title").type(JsonFieldType.STRING).description("게시글 제목"), + fieldWithPath("description").type(JsonFieldType.STRING).description("게시글 설명"), + fieldWithPath("pollChoices[]").type(JsonFieldType.ARRAY).description("투표 선택지 목록"), + fieldWithPath("pollChoices[].id").type(JsonFieldType.NUMBER).description("투표 선택지 Id"), + fieldWithPath("pollChoices[].title").type(JsonFieldType.STRING).description("사진 이름"), + fieldWithPath("pollChoices[].imageUrl").type(JsonFieldType.STRING).description("사진 이미지"), + fieldWithPath("shareUrl").type(JsonFieldType.STRING).description("게시글 공유 URL"), + fieldWithPath("pollOption").type(JsonFieldType.OBJECT).description("투표 설정"), + fieldWithPath("pollOption.pollType").type(JsonFieldType.STRING).description(enumDescription("단일/복수 투표", PollType.class)), + fieldWithPath("pollOption.scope").type(JsonFieldType.STRING).description(enumDescription("공개 여부", Scope.class)), + fieldWithPath("pollOption.commentActive").type(JsonFieldType.STRING).description(enumDescription("댓글 활성화 여부", CommentActive.class)), + fieldWithPath("closeOption").type(JsonFieldType.OBJECT).description("마감 설정"), + fieldWithPath("closeOption.closeType").type(JsonFieldType.STRING).description(enumDescription("마감 방식", CloseType.class)), + fieldWithPath("closeOption.closedAt").type(JsonFieldType.STRING).optional().description("마감 시간, (closeType이 DATE일 경우 NN)"), + fieldWithPath("closeOption.maxVoterCount").type(JsonFieldType.NUMBER).optional().description("남은 투표 참여자 수 (closeType이 VOTER_COUNT일 경우 NN)"), + fieldWithPath("status").type(JsonFieldType.STRING).description("게시글 마감 여부 (PROGRESS, CLOSED)"), + fieldWithPath("createdAt").type(JsonFieldType.STRING).description("게시글 작성 시간") + ) + )); + } + @Test @WithMockUserInfo @DisplayName("게시글 마감") From e4410f9df9bae1f5862c5e0cb0639c7aef64da15 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Mon, 4 Aug 2025 12:33:12 +0900 Subject: [PATCH 8/8] =?UTF-8?q?docs:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=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 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/docs/asciidoc/posts.adoc b/src/docs/asciidoc/posts.adoc index a5d6dd8d..76f50fe4 100644 --- a/src/docs/asciidoc/posts.adoc +++ b/src/docs/asciidoc/posts.adoc @@ -27,10 +27,15 @@ 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` 게시글 투표 수정 (미구현) +=== `PUT` 게시글 투표 수정 operation::post-controller-test/update-post[snippets='http-request,curl-request,path-parameters,request-headers,http-response'] +[[게시글-투표-수정]] +=== `GET` 게시글 투표 수정 조회 + +operation::post-controller-test/find-post_update[snippets='path-parameters,http-response,response-fields'] + [[게시글-투표-마감]] === `POST` 게시글 투표 마감