Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE] feat: 리뷰 상세 조회 기능 구현 #182

Merged
merged 13 commits into from
Aug 1, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,23 @@
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reviewme.review.dto.request.CreateReviewRequest;
import reviewme.review.dto.response.ReceivedReviewsResponse;
import reviewme.review.dto.response.ReviewDetailResponse;
import reviewme.review.dto.response.ReviewSetupResponse;
import reviewme.review.service.ReviewService;

@RestController
@RequiredArgsConstructor
public class ReviewController implements ReviewApi {

private static final String GROUP_ACCESS_CODE_HEADER = "GroupAccessCode";

private final ReviewService reviewService;

@PostMapping("/reviews")
Expand All @@ -29,7 +33,7 @@ public ResponseEntity<Void> createReview(@Valid @RequestBody CreateReviewRequest

@GetMapping("/reviews")
public ResponseEntity<ReceivedReviewsResponse> findReceivedReviews(HttpServletRequest request) {
String groupAccessCode = request.getHeader("GroupAccessCode");
String groupAccessCode = request.getHeader(GROUP_ACCESS_CODE_HEADER);
ReceivedReviewsResponse response = reviewService.findReceivedReviews(groupAccessCode);
return ResponseEntity.ok(response);
}
Expand All @@ -39,4 +43,11 @@ public ResponseEntity<ReviewSetupResponse> findReviewCreationSetup(@RequestParam
ReviewSetupResponse response = reviewService.findReviewCreationSetup(reviewRequestCode);
return ResponseEntity.ok(response);
}

@GetMapping("/reviews/{id}")
public ResponseEntity<ReviewDetailResponse> findReceivedReviewDetail(@PathVariable long id, HttpServletRequest request) {
String groupAccessCode = request.getHeader(GROUP_ACCESS_CODE_HEADER);
ReviewDetailResponse response = reviewService.findReceivedReviewDetail(groupAccessCode, id);
return ResponseEntity.ok(response);
}
}
4 changes: 4 additions & 0 deletions backend/src/main/java/reviewme/review/domain/Review.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@ public Review(long reviewGroupId, List<ReviewContent> reviewContents, LocalDateT
this.reviewContents = reviewContents;
this.createdAt = createdAt;
}

public boolean isGroupIdEqualTo(long reviewGroupId) {
return this.reviewGroupId == reviewGroupId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package reviewme.review.domain.exception;

import reviewme.global.exception.NotFoundException;

public class ReviewGroupNotFoundException extends NotFoundException {

public ReviewGroupNotFoundException() {
super("리뷰 그룹을 찾을 수 없어요.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package reviewme.review.domain.exception;

import reviewme.global.exception.BadRequestException;

public class ReviewIsNotInReviewGroupException extends BadRequestException {

public ReviewIsNotInReviewGroupException() {
super("리뷰 그룹에 해당하는 리뷰가 아니에요.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import io.swagger.v3.oas.annotations.media.Schema;

@Schema(name = "키워드 응답")
public record ReviewSetUpKeyword(
public record KeywordResponse(

@Schema(description = "키워드 ID")
long id,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package reviewme.review.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;
import java.util.List;

@Schema(name = "리뷰 상세 조회 응답")
public record ReviewDetailResponse(

@Schema(description = "리뷰 ID")
long id,

@Schema(description = "리뷰 작성일")
LocalDate createdAt,

@Schema(description = "프로젝트명")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 진짜 사소한 것 :
revieweeName은 '리뷰이 이름'인데
projectName은 '프로젝트명'으로 되어있어요!
통일하면 좋을 것 같아요

String projectName,

@Schema(description = "리뷰이 이름")
String revieweeName,

@Schema(description = "리뷰 문항 목록")
List<ReviewContentResponse> contents,

@Schema(description = "키워드 목록")
List<KeywordResponse> keywords
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ public record ReviewSetupResponse(
List<QuestionSetupResponse> questions,

@Schema(description = "키워드 목록")
List<ReviewSetUpKeyword> keywords
List<KeywordResponse> keywords
) {
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package reviewme.review.repository;

import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import reviewme.review.domain.Review;
Expand All @@ -11,6 +12,8 @@ public interface ReviewRepository extends JpaRepository<Review, Long> {

List<Review> findAllByReviewGroupId(long id);

Optional<Review> findByIdAndReviewGroupId(long reviewId, long reviewGroupId);

default Review getReviewById(Long id) {
return findById(id).orElseThrow(ReviewNotFoundException::new);
}
Expand Down
63 changes: 52 additions & 11 deletions backend/src/main/java/reviewme/review/service/ReviewService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,26 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reviewme.keyword.repository.KeywordRepository;
import reviewme.question.domain.Question;
import reviewme.review.domain.Review;
import reviewme.review.domain.ReviewContent;
import reviewme.review.domain.ReviewKeyword;
import reviewme.review.domain.exception.ReviewGroupNotFoundException;
import reviewme.review.domain.exception.ReviewIsNotInReviewGroupException;
import reviewme.review.dto.request.CreateReviewContentRequest;
import reviewme.review.dto.request.CreateReviewRequest;
import reviewme.review.dto.response.KeywordResponse;
import reviewme.review.dto.response.QuestionSetupResponse;
import reviewme.review.dto.response.ReceivedReviewKeywordsResponse;
import reviewme.review.dto.response.ReceivedReviewResponse;
import reviewme.review.dto.response.ReceivedReviewsResponse;
import reviewme.review.dto.response.ReviewSetUpKeyword;
import reviewme.review.dto.response.ReviewContentResponse;
import reviewme.review.dto.response.ReviewDetailResponse;
import reviewme.review.dto.response.ReviewSetupResponse;
import reviewme.review.repository.QuestionRepository;
import reviewme.review.repository.ReviewContentRepository;
import reviewme.review.repository.ReviewKeywordRepository;
import reviewme.review.repository.ReviewRepository;
import reviewme.review.service.exception.InvalidGroupAccessCodeException;
import reviewme.review.service.exception.InvalidReviewRequestCodeException;
import reviewme.reviewgroup.domain.ReviewGroup;
import reviewme.reviewgroup.repository.ReviewGroupRepository;

Expand Down Expand Up @@ -51,7 +54,7 @@ public Long createReview(CreateReviewRequest request) {

private Review saveReview(CreateReviewRequest request) {
ReviewGroup reviewGroup = reviewGroupRepository.findByReviewRequestCode(request.reviewRequestCode())
.orElseThrow(InvalidReviewRequestCodeException::new);
.orElseThrow(ReviewGroupNotFoundException::new);

List<Long> questionIds = request.reviewContents()
.stream()
Expand Down Expand Up @@ -79,29 +82,67 @@ private void saveReviewKeywords(List<Long> selectedKeywordIds, long savedReviewI
@Transactional(readOnly = true)
public ReviewSetupResponse findReviewCreationSetup(String reviewRequestCode) {
ReviewGroup reviewGroup = reviewGroupRepository.findByReviewRequestCode(reviewRequestCode)
.orElseThrow(InvalidReviewRequestCodeException::new);
.orElseThrow(ReviewGroupNotFoundException::new);
return createReviewSetupResponse(reviewGroup);
}

private ReviewSetupResponse createReviewSetupResponse(ReviewGroup reviewGroup) {
List<QuestionSetupResponse> questionSetupRespons = questionRepository.findAll()
List<QuestionSetupResponse> questionSetupResponse = questionRepository.findAll()
.stream()
.map(question -> new QuestionSetupResponse(question.getId(), question.getContent()))
.toList();

List<ReviewSetUpKeyword> reviewSetUpKeywordRespons = keywordRepository.findAll()
List<KeywordResponse> keywordResponse = keywordRepository.findAll()
.stream()
.map(keyword -> new ReviewSetUpKeyword(keyword.getId(), keyword.getContent()))
.map(keyword -> new KeywordResponse(keyword.getId(), keyword.getContent()))
.toList();

return new ReviewSetupResponse(reviewGroup.getReviewee(), reviewGroup.getProjectName(),
questionSetupRespons, reviewSetUpKeywordRespons);
return new ReviewSetupResponse(
reviewGroup.getReviewee(), reviewGroup.getProjectName(), questionSetupResponse, keywordResponse
);
}

@Transactional(readOnly = true)
public ReviewDetailResponse findReceivedReviewDetail(String groupAccessCode, long reviewId) {
ReviewGroup reviewGroup = reviewGroupRepository.findByGroupAccessCode(groupAccessCode)
.orElseThrow(ReviewGroupNotFoundException::new);

Review review = reviewRepository.findByIdAndReviewGroupId(reviewId, reviewGroup.getId())
.orElseThrow(ReviewIsNotInReviewGroupException::new);

return createReviewDetailResponse(review, reviewGroup);
}

private ReviewDetailResponse createReviewDetailResponse(Review review, ReviewGroup reviewGroup) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

커테도 dto 변환을 private 메서드로 분리해서 해줬군요!
저희도 그렇게 해주었는데요!
나중에 dto 컨벤션 정할 때 'dto 변환 함수 컨벤션'도 이야기할 필요가 있을 것 같네요,
이슈 & 디스커션에 추가해둘게요!

List<ReviewContentResponse> reviewContents = review.getReviewContents()
.stream()
.map(reviewContent -> {
Question question = questionRepository.getQuestionById(reviewContent.getQuestionId());
return new ReviewContentResponse(reviewContent.getId(), question.getContent(),
reviewContent.getAnswer());
})
.toList();

List<KeywordResponse> keywords = reviewKeywordRepository.findAllByReviewId(review.getId())
.stream()
.map(reviewKeyword -> keywordRepository.getKeywordById(reviewKeyword.getKeywordId()))
.map(keyword -> new KeywordResponse(keyword.getId(), keyword.getContent()))
.toList();

return new ReviewDetailResponse(
review.getId(),
review.getCreatedAt().toLocalDate(),
reviewGroup.getProjectName(),
reviewGroup.getReviewee(),
reviewContents,
keywords
);
}

@Transactional(readOnly = true)
public ReceivedReviewsResponse findReceivedReviews(String groupAccessCode) {
ReviewGroup reviewGroup = reviewGroupRepository.findByGroupAccessCode(groupAccessCode)
.orElseThrow(InvalidGroupAccessCodeException::new);
.orElseThrow(ReviewGroupNotFoundException::new);
List<ReceivedReviewResponse> reviewResponses = reviewRepository.findAllByReviewGroupId(reviewGroup.getId())
.stream()
.map(this::extractResponse)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +0,0 @@
package reviewme.review.service.exception;

import reviewme.global.exception.BadRequestException;

public class InvalidReviewRequestCodeException extends BadRequestException {

public InvalidReviewRequestCodeException() {
super("잘못된 리뷰 요청코드입니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
import reviewme.review.domain.Review;
import reviewme.review.domain.ReviewContent;
import reviewme.review.domain.ReviewKeyword;
import reviewme.review.domain.exception.ReviewGroupNotFoundException;
import reviewme.review.domain.exception.ReviewIsNotInReviewGroupException;
import reviewme.review.dto.request.CreateReviewContentRequest;
import reviewme.review.dto.request.CreateReviewRequest;
import reviewme.review.dto.response.ReceivedReviewsResponse;
import reviewme.review.dto.response.ReviewDetailResponse;
import reviewme.review.dto.response.ReviewSetupResponse;
import reviewme.review.repository.QuestionRepository;
import reviewme.review.repository.ReviewContentRepository;
Expand Down Expand Up @@ -116,7 +119,7 @@ class ReviewServiceTest {
@Test
void 확인_코드에_해당하는_그룹이_없는_경우_예외가_발생한다() {
assertThatThrownBy(() -> reviewService.findReceivedReviews("abc"))
.isInstanceOf(InvalidGroupAccessCodeException.class);
.isInstanceOf(ReviewGroupNotFoundException.class);
}

@Test
Expand Down Expand Up @@ -147,4 +150,50 @@ class ReviewServiceTest {
// then
assertThat(response.reviews()).hasSize(2);
}

@Test
void 리뷰를_조회한다() {
// given
String groupAccessCode = "groupAccessCode";
ReviewGroup reviewGroup = reviewGroupRepository.save(
new ReviewGroup("테드", "리뷰미 프로젝트", "reviewRequestCode", groupAccessCode));
Review review = reviewRepository.save(new Review(reviewGroup.getId(), List.of(), LocalDateTime.now()));

// when
ReviewDetailResponse response = reviewService.findReceivedReviewDetail(groupAccessCode,
review.getId());

// then
assertThat(response.id()).isEqualTo(review.getId());
}

@Test
void 잘못된_그룹_액세스_코드로_리뷰를_조회할_경우_예외를_발생한다() {
// given
ReviewGroup reviewGroup = reviewGroupRepository.save(
new ReviewGroup("테드", "리뷰미 프로젝트", "reviewRequestCode", "groupAccessCode"));

Review review = reviewRepository.save(new Review(reviewGroup.getId(), List.of(), LocalDateTime.now()));

// when, then
assertThatThrownBy(() -> reviewService.findReceivedReviewDetail("wrongGroupAccessCode", review.getId()))
.isInstanceOf(ReviewGroupNotFoundException.class);
}

@Test
void 리뷰_그룹에_해당하는_않는_리뷰를_조회할_경우_예외를_발생한다() {
// given
ReviewGroup reviewGroup1 = reviewGroupRepository.save(
new ReviewGroup("테드", "리뷰미 프로젝트", "reviewRequestCode1", "groupAccessCode1"));
ReviewGroup reviewGroup2 = reviewGroupRepository.save(
new ReviewGroup("테드", "리뷰미 프로젝트", "reviewRequestCode2", "groupAccessCode2"));

Review review1 = reviewRepository.save(new Review(reviewGroup1.getId(), List.of(), LocalDateTime.now()));
Review review2 = reviewRepository.save(new Review(reviewGroup2.getId(), List.of(), LocalDateTime.now()));

// when, then
assertThatThrownBy(
() -> reviewService.findReceivedReviewDetail(reviewGroup1.getGroupAccessCode(), review2.getId()))
.isInstanceOf(ReviewIsNotInReviewGroupException.class);
}
}