Skip to content

Commit

Permalink
내가 투표 한 글 조회 기능 구현 (#134)
Browse files Browse the repository at this point in the history
* feat: (#80) 리파지터리 메서드 추가

* feat: (#80) 내가 투표한 게시글 목록 조회 API 기능 추가

* feat: (#80) Swagger 어노테이션 추가

* test: (#80) 테스트 추가

* style: (#80) 개행 추가

* style: (#80) swaager 명세서 보충

* refactor: (#80) JPA 메서드 이름 수정

* feat: (#80) 회원본인이 투표한 게시글 조회 api 페이징 기능 추가

* feat: (#80) 페이징 정렬 기준 추가

* test: (#80) 회원본인이 투표한 게시글 목록 조회 테스트 추가

* style: (#80) import문 정리

* feat: (#80) swagger 문서 추가

* fix: (#80) 시간 관련검증 분까지 지정으로 수정
  • Loading branch information
aiaiaiai1 authored and tjdtls690 committed Sep 12, 2023
1 parent 11667f4 commit cb877d4
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 9 deletions.
Binary file removed backend/images/testImage1.PNG
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.net.URI;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand Down Expand Up @@ -126,6 +127,22 @@ public ResponseEntity<VoteOptionStatisticsResponse> getVoteOptionStatistics(
return ResponseEntity.ok(response);
}

@Operation(summary = "투표한 게시글 조회", description = "회원본인이 투표한 게시글 목록을 조회한다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "회원본인이 투표한 게시글 조회 성공"),
@ApiResponse(responseCode = "400", description = "잘못된 입력으로 실패.")
})
@GetMapping("/votes/me")
public ResponseEntity<List<PostResponse>> getPostsVotedByMe(
final int page,
final PostClosingType postClosingType,
final PostSortType postSortType,
@Auth final Member member
) {
final List<PostResponse> posts = postService.getPostsVotedByMember(page, postClosingType, postSortType, member);
return ResponseEntity.status(HttpStatus.OK).body(posts);
}

@Operation(summary = "게시글 조기 마감", description = "게시글을 조기 마감한다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "게시물이 조기 마감 되었습니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,22 @@
@Getter
public enum PostSortType {

LATEST(Sort.by(Sort.Direction.DESC, "createdAt")),
HOT(Sort.by(Direction.DESC, "totalVoteCount"));
LATEST(
Sort.by(Direction.DESC, "createdAt"),
Sort.by(Direction.DESC, "postOption.post.createdAt")
),

private final Sort sort;
HOT(
Sort.by(Direction.DESC, "totalVoteCount"),
Sort.by(Direction.DESC, "postOption.post.totalVoteCount")
);

PostSortType(final Sort sort) {
this.sort = sort;
private final Sort postBaseSort;
private final Sort voteBaseSort;

PostSortType(final Sort postBaseSort, final Sort voteBaseSort) {
this.postBaseSort = postBaseSort;
this.voteBaseSort = voteBaseSort;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface PostRepository extends JpaRepository<Post, Long> {

Expand All @@ -15,4 +17,15 @@ public interface PostRepository extends JpaRepository<Post, Long> {

int countByWriter(final Member member);

@Query("SELECT v.postOption.post FROM Vote v WHERE v.member = :member "
+ "AND v.postOption.post.deadline < CURRENT_TIMESTAMP")
Slice<Post> findClosedPostsVotedByMember(@Param("member") final Member member, final Pageable pageable);

@Query("SELECT v.postOption.post FROM Vote v WHERE v.member = :member "
+ "AND v.postOption.post.deadline > CURRENT_TIMESTAMP")
Slice<Post> findOpenPostsVotedByMember(@Param("member") final Member member, final Pageable pageable);

@Query("SELECT v.postOption.post FROM Vote v WHERE v.member = :member ")
Slice<Post> findPostsVotedByMember(@Param("member") final Member member, final Pageable pageable);

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.data.domain.PageRequest;
Expand All @@ -44,6 +45,7 @@ public class PostService {
private static final int MAXIMUM_DEADLINE = 3;

private final Map<PostClosingType, Function<Pageable, Slice<Post>>> postClosingTypeMapper;
private final Map<PostClosingType, BiFunction<Member, Pageable, Slice<Post>>> postsVotedByMemberMapper;
private final PostRepository postRepository;
private final PostOptionRepository postOptionRepository;
private final CategoryRepository categoryRepository;
Expand All @@ -61,7 +63,9 @@ public PostService(
this.voteRepository = voteRepository;

this.postClosingTypeMapper = new EnumMap<>(PostClosingType.class);
this.postsVotedByMemberMapper = new EnumMap<>(PostClosingType.class);
initPostClosingTypeMapper();
initPostsVotedByMemberMapper();
}

private void initPostClosingTypeMapper() {
Expand All @@ -72,6 +76,12 @@ private void initPostClosingTypeMapper() {
postRepository.findByDeadlineBefore(LocalDateTime.now(), pageable));
}

private void initPostsVotedByMemberMapper() {
postsVotedByMemberMapper.put(PostClosingType.ALL, postRepository::findPostsVotedByMember);
postsVotedByMemberMapper.put(PostClosingType.PROGRESS, postRepository::findOpenPostsVotedByMember);
postsVotedByMemberMapper.put(PostClosingType.CLOSED, postRepository::findClosedPostsVotedByMember);
}

@Transactional
public Long save(
final PostCreateRequest postCreateRequest,
Expand Down Expand Up @@ -152,7 +162,7 @@ public List<PostResponse> getAllPostBySortTypeAndClosingType(
final PostClosingType postClosingType,
final PostSortType postSortType
) {
final Pageable pageable = PageRequest.of(page, BASIC_PAGING_SIZE, postSortType.getSort());
final Pageable pageable = PageRequest.of(page, BASIC_PAGING_SIZE, postSortType.getPostBaseSort());
final List<Post> contents = findContentsBySortTypeAndClosingType(postClosingType, pageable);

return contents.stream()
Expand Down Expand Up @@ -235,7 +245,23 @@ private String groupAgeRange(final String ageRange) {
}
return ageRange;
}

@Transactional(readOnly = true)
public List<PostResponse> getPostsVotedByMember(
final int page,
final PostClosingType postClosingType,
final PostSortType postSortType,
final Member member
) {
final Pageable pageable = PageRequest.of(page, BASIC_PAGING_SIZE, postSortType.getVoteBaseSort());

Slice<Post> posts = postsVotedByMemberMapper.get(postClosingType).apply(member, pageable);

return posts.stream()
.map(post -> PostResponse.of(post, member))
.toList();
}

@Transactional
public void closePostEarlyById(final Long id, final Member loginMember) {
final Post post = postRepository.findById(id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ List<VoteStatus> findVoteCountByPostOptionIdGroupByAgeRangeAndGender(

Optional<Vote> findByMemberAndPostOption(final Member member, final PostOption postOption);

List<Vote> findByMemberAndPostOptionIn(final Member member, final List<PostOption> postOptions);
List<Vote> findAllByMemberAndPostOptionIn(final Member member, final List<PostOption> postOptions);

List<Vote> findAllByMember(final Member member);

int countByMember(final Member member);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void vote(
private void validateAlreadyVoted(final Member member, final Post post) {
final PostOptions postOptions = post.getPostOptions();
final List<Vote> alreadyVoted =
voteRepository.findByMemberAndPostOptionIn(member, postOptions.getPostOptions());
voteRepository.findAllByMemberAndPostOptionIn(member, postOptions.getPostOptions());
if (!alreadyVoted.isEmpty()) {
throw new IllegalStateException("해당 게시물에는 이미 투표하였습니다.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
Expand Down Expand Up @@ -47,6 +48,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

Expand Down Expand Up @@ -346,6 +348,42 @@ void getVoteOptionStatistics() {
assertThat(result).usingRecursiveComparison().isEqualTo(response);
}

@Test
@DisplayName("회원본인이 투표한 게시글 목록을 조회한다.")
void getPostsVotedByMember() {
// given
PostBody postBody = PostBody.builder()
.title("title")
.content("content")
.build();

Post post = Post.builder()
.writer(MALE_30.get())
.postBody(postBody)
.deadline(LocalDateTime.now().plusDays(3L).truncatedTo(ChronoUnit.MINUTES))
.build();

PostResponse postResponse = PostResponse.of(post, MALE_30.get());

given(postService.getPostsVotedByMember(anyInt(), any(), any(), any(Member.class)))
.willReturn(List.of(postResponse));

// when
List<PostResponse> result = RestAssuredMockMvc.given().log().all()
.param("page", 0)
.param("postClosingType", PostClosingType.PROGRESS)
.param("postSortType", PostSortType.LATEST)
.when().get("/posts/votes/me")
.then().log().all()
.status(HttpStatus.OK)
.extract()
.as(new ParameterizedTypeReference<List<PostResponse>>() {
}.getType());

// then
assertThat(result.get(0)).usingRecursiveComparison().isEqualTo(postResponse);
}

@Test
@DisplayName("게시글을 조기 마감 합니다")
void postClosedEarly() {
Expand Down
Loading

0 comments on commit cb877d4

Please sign in to comment.