Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/docs/asciidoc/posts.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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` 게시글 투표 마감

Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/chooz/common/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -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("토큰이 만료됐습니다."),
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/com/chooz/post/application/PostCommandService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -89,4 +91,28 @@ public void close(Long userId, Long postId) {
.orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND));
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()
)
);
}
}
18 changes: 13 additions & 5 deletions src/main/java/com/chooz/post/application/PostQueryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -70,18 +71,18 @@ private PostResponse createPostResponse(Long userId, Post post) {
.distinct()
.count();
boolean isAuthor = post.getUserId().equals(userId);
List<PollChoiceResponse> pollChoiceResponseList = createPollChoiceResponse(
List<PollChoiceVoteResponse> 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<PollChoiceResponse> createPollChoiceResponse(Long userId, List<PollChoice> pollChoices, List<Vote> voteList) {
private List<PollChoiceVoteResponse> createPollChoiceResponse(Long userId, List<PollChoice> pollChoices, List<Vote> voteList) {
return pollChoices.stream()
.map(pollChoice -> new PollChoiceResponse(
.map(pollChoice -> new PollChoiceVoteResponse(
pollChoice.getId(),
pollChoice.getTitle(),
pollChoice.getImageUrl(),
Expand Down Expand Up @@ -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);
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/chooz/post/application/PostService.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
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;
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;
Expand Down Expand Up @@ -33,6 +35,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);
}
Expand All @@ -52,4 +59,8 @@ public PostResponse findByShareUrl(Long userId, String shareUrl) {
public CursorBasePaginatedResponse<FeedResponse> 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);
}
}
63 changes: 63 additions & 0 deletions src/main/java/com/chooz/post/application/PostValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
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;
import java.util.Objects;

@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);
}

LocalDateTime newClosedAt = request.closeOption().closedAt();
Integer newMaxVoterCount = request.closeOption().maxVoterCount();
if (closeType == CloseType.DATE) {
validateUpdateClosedAt(post, newClosedAt, newMaxVoterCount);
} else if (closeType == CloseType.VOTER) {
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
16 changes: 4 additions & 12 deletions src/main/java/com/chooz/post/domain/PollOption.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/com/chooz/post/domain/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down Expand Up @@ -198,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;
}
}
2 changes: 2 additions & 0 deletions src/main/java/com/chooz/post/domain/PostRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,6 @@ Slice<PostWithVoteCount> findVotedPostsWithVoteCount(
@Param("postId") Long postId,
Pageable pageable
);

Optional<Post> findByIdAndUserId(Long id, Long userId);
}
14 changes: 12 additions & 2 deletions src/main/java/com/chooz/post/presentation/PostController.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -51,7 +52,6 @@ public ResponseEntity<PostResponse> findPostById(
return ResponseEntity.ok(postService.findById(userId, postId));
}


@GetMapping("/shareUrl/{shareUrl}")
public ResponseEntity<PostResponse> findPostByShareUrl(
@PathVariable("shareUrl") String shareUrl,
Expand All @@ -63,15 +63,25 @@ public ResponseEntity<PostResponse> findPostByShareUrl(
return ResponseEntity.ok(postService.findByShareUrl(userId, shareUrl));
}

@PostMapping("/{postId}/update")
@PutMapping("/{postId}")
public ResponseEntity<Void> updatePost(
@PathVariable("postId") Long postId,
@Valid @RequestBody UpdatePostRequest request,
@AuthenticationPrincipal UserInfo userInfo
) {
postService.update(userInfo.userId(), postId, request);
return ResponseEntity.ok().build();
}

@GetMapping("/{postId}/update")
public ResponseEntity<UpdatePostResponse> updatePost(
@PathVariable("postId") Long postId,
@AuthenticationPrincipal UserInfo userInfo
) {
UpdatePostResponse response = postService.findUpdatePost(userInfo.userId(), postId);
return ResponseEntity.ok(response);
}

@PostMapping("/{postId}/close")
public ResponseEntity<Void> closePost(
@PathVariable("postId") Long postId,
Expand Down
55 changes: 55 additions & 0 deletions src/main/java/com/chooz/post/presentation/UpdatePostResponse.java
Original file line number Diff line number Diff line change
@@ -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<PollChoiceResponse> 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()
);
}
}
Loading