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: 리뷰 작성 API 구현 #102

Merged
merged 15 commits into from
Jul 25, 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
2 changes: 2 additions & 0 deletions backend/src/main/java/reviewme/member/domain/GithubId.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Embeddable
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = "id")
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@Getter
public class GithubId {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "github_id_reviewer_group")
Copy link
Contributor

Choose a reason for hiding this comment

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

테이블 명시 👍🏻

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class GithubIdReviewerGroup {
Expand All @@ -23,6 +26,7 @@ public class GithubIdReviewerGroup {
private GithubId githubId;

@ManyToOne
@JoinColumn(name = "reviewer_group_id", nullable = false)
private ReviewerGroup reviewerGroup;

public GithubIdReviewerGroup(GithubId githubId, ReviewerGroup reviewerGroup) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package reviewme.member.domain;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Embeddable;
import jakarta.persistence.OneToMany;
import java.util.List;
Expand All @@ -14,7 +15,7 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ReviewerGroupGithubIds {

@OneToMany(mappedBy = "reviewerGroup")
@OneToMany(mappedBy = "reviewerGroup", cascade = CascadeType.PERSIST)
private Set<GithubIdReviewerGroup> reviewerGithubIds;

public ReviewerGroupGithubIds(ReviewerGroup reviewerGroup, List<GithubId> githubIds) {
Expand Down
11 changes: 11 additions & 0 deletions backend/src/main/java/reviewme/review/domain/Review.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
Expand Down Expand Up @@ -42,6 +44,9 @@ public class Review {
@JoinColumn(name = "reviewer_group_id", nullable = false)
private ReviewerGroup reviewerGroup;

@OneToMany(mappedBy = "review")
private List<ReviewContent> reviewContents;

@Embedded
private Keywords keywords;

Expand All @@ -58,9 +63,11 @@ public Review(Member reviewer, Member reviewee, ReviewerGroup reviewerGroup,
}
this.reviewer = reviewer;
this.reviewee = reviewee;
this.reviewContents = new ArrayList<>();
this.keywords = new Keywords(keywords);
this.createdAt = createdAt;
reviewerGroup.addReview(this);
this.reviewerGroup = reviewerGroup;
this.isPublic = false;
}

Expand All @@ -71,4 +78,8 @@ public boolean isSubmittedBy(Member member) {
public boolean isForReviewee(Member member) {
return reviewee.equals(member);
}

public void addReviewContents(ReviewContent reviewContent) {
reviewContents.add(reviewContent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public ReviewContent(Review review, Question question, String answer) {
this.review = review;
this.question = question;
this.answer = answer;
review.addReviewContents(this);
}

private void validateAnswerLength(String answer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,16 @@

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

@Schema(description = "리뷰 내용 등록 요청")
public record CreateReviewContentRequest(
@NotNull(message = "리뷰 항목 순서를 입력해주세요.")
@Schema(description = "리뷰 항목 순서")
Long order,

@NotBlank(message = "질문을 입력해주세요.")
@Schema(description = "리뷰 문항")
String question,
@NotBlank(message = "질문을 입력해주세요.")
Long questionId,

@NotBlank(message = "답변을 입력해주세요.")
@Schema(description = "리뷰 문항에 대한 답변")
@NotBlank(message = "답변을 입력해주세요.")
String answer
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,22 @@

@Schema(description = "리뷰 등록 요청")
public record CreateReviewRequest(
@NotNull(message = "리뷰어 아이디를 입력해주세요.")

@Schema(description = "리뷰어 ID")
@NotNull(message = "리뷰어 아이디를 입력해주세요.")
Long reviewerId,

@NotNull(message = "리뷰어 그룹 아이디를 입력해주세요.")
@Schema(description = "리뷰어 그룹 ID")
@NotNull(message = "리뷰어 그룹 아이디를 입력해주세요.")
Long reviewerGroupId,

@Schema(description = "리뷰 내용 목록")
@Valid
@NotNull(message = "리뷰 내용을 입력해주세요.")
@Schema(description = "리뷰 내용 목록")
List<CreateReviewContentRequest> contents,
List<CreateReviewContentRequest> reviewContents,

@NotNull(message = "키워드를 입력해주세요.")
@Schema(description = "선택된 키워드 ID 목록")
List<Long> selectedKeywordIds
@NotNull(message = "키워드를 입력해주세요.")
List<Long> keywords
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package reviewme.review.exception;

import reviewme.global.exception.NotFoundException;

public class QuestionNotFoundException extends NotFoundException {

public QuestionNotFoundException() {
super("질문이 존재하지 않습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package reviewme.review.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import reviewme.review.domain.Question;
import reviewme.review.exception.QuestionNotFoundException;

@Repository
public interface QuestionRepository extends JpaRepository<Question, Long> {

default Question getQuestionById(long id) {
return findById(id).orElseThrow(QuestionNotFoundException::new);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

전반적으로 쿼리가 많이 보이기는 하는데 이건 추후에 봐야겠네요 🙄

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package reviewme.review.service;

import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand All @@ -11,13 +12,15 @@
import reviewme.member.domain.ReviewerGroup;
import reviewme.member.repository.MemberRepository;
import reviewme.member.repository.ReviewerGroupRepository;
import reviewme.review.domain.Question;
import reviewme.review.domain.Review;
import reviewme.review.domain.ReviewContent;
import reviewme.review.dto.request.CreateReviewRequest;
import reviewme.review.dto.response.ReviewDetailResponse;
import reviewme.review.dto.response.ReviewDetailReviewContentResponse;
import reviewme.review.dto.response.ReviewDetailReviewerGroupResponse;
import reviewme.review.exception.ReviewUnAuthorizedException;
import reviewme.review.repository.QuestionRepository;
import reviewme.review.repository.ReviewContentRepository;
import reviewme.review.repository.ReviewRepository;

Expand All @@ -29,11 +32,33 @@ public class ReviewService {
private final MemberRepository memberRepository;
private final ReviewerGroupRepository reviewerGroupRepository;
private final ReviewContentRepository reviewContentRepository;
private final QuestionRepository questionRepository;
private final KeywordRepository keywordRepository;

@Transactional
public Long createReview(CreateReviewRequest request) {
return null;
ReviewerGroup reviewerGroup = reviewerGroupRepository.getReviewerGroupById(request.reviewerGroupId());
Member reviewer = memberRepository.getMemberById(request.reviewerId());

List<Keyword> keywordList = request.keywords()
.stream()
.map(keywordRepository::getKeywordById)
.toList();

Review review = new Review(reviewer, reviewerGroup.getReviewee(),
reviewerGroup, keywordList, LocalDateTime.now());
Review savedReview = reviewRepository.save(review);
Comment on lines +48 to +50
Copy link
Contributor

Choose a reason for hiding this comment

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

리뷰이 부분에 들어갈 인자를 reviewerGroup 에서 빼서 넣어주셨는데요! 그런데 그렇게 한다면, 리뷰를 리뷰어 그룹에 추가할 때 검증하는 함수인 addReview 에서 '추가될 리뷰의 리뷰이와 리뷰어 그룹의 리뷰이가 같은지 검증하는' 부분이 무용지물이 될 것 같아요!

그러니 MemverRepository 로부터 찾아서 넣어주는건 어떨까요?

// ReviwerGroup.class
public void addReview(Review review) {

    Member reviewer = review.getReviewer();
    if (isDeadlineExceeded(review.getCreatedAt())) {
        throw new DeadlineExpiredException();
    }
    if (hasSubmittedReviewBy(reviewer)) {
        throw new ReviewAlreadySubmittedException();
    }
    if (reviewerGithubIds.doesNotContain(reviewer)) {
        throw new GithubReviewerGroupUnAuthorizedException();
    }
    if (!review.getReviewee().equals(reviewee)) {   // ❗️이부분
        throw new RevieweeMismatchException();
    }
    reviews.add(review);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Member reviewer = memberRepository.getMemberById(request.reviewerId());
Member reviewee = memberRepository.getMemberById(reviewerGroup.getReviewee().getId());

이전에 위의 방식으로 했었는데요, 어차피 리뷰이를 따로 받는게 아니라 사실상 리뷰어 그룹에 있는 리뷰이를 사용할거라면 두 방식에 차이가 없어 보여요. 차라리 리뷰 생성자로 리뷰이를 받지 않고, 리뷰 그룹에서 꺼내 쓰는게 나을 것 같기도 하고, 어떤 방식이든 사실 순환참조 때문에 '추가될 리뷰의 리뷰이와 리뷰어 그룹의 리뷰이가 같은지 검증하는' 부분이 의미가 없긴 할 것 같아요.

Copy link
Contributor

Choose a reason for hiding this comment

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

오 동의합니다.
저도 코드를 보다보니 '이 코드가 쓰이지 않는 것 같으니 사용되도록 데이터를 세탕' 하라는 식으로 리뷰했었었는데,
다시 생각해보니 주객이 전도되었네요😓
리뷰어그룹으로부터 리뷰어를 받아오게 하는게 더 간단할 것 같아요.

Copy link
Contributor

Choose a reason for hiding this comment

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

'리뷰어 그룹으로부터 리뷰이를 받아오도록 리팩터링하는건 어떨까?' 라는 생각이 든다!


request.reviewContents()
.forEach(contentsRequest -> {
Question question = questionRepository.getQuestionById(contentsRequest.questionId());
String answer = contentsRequest.answer();

ReviewContent reviewContent = new ReviewContent(savedReview, question, answer);
reviewContentRepository.save(reviewContent);
});

return savedReview.getId();
}

@Transactional(readOnly = true)
Expand Down

This file was deleted.

Loading