diff --git a/backend/src/main/java/reviewme/member/domain/GithubIdReviewerGroup.java b/backend/src/main/java/reviewme/member/domain/GithubIdReviewerGroup.java index 5497a76b2..5f93d0c50 100644 --- a/backend/src/main/java/reviewme/member/domain/GithubIdReviewerGroup.java +++ b/backend/src/main/java/reviewme/member/domain/GithubIdReviewerGroup.java @@ -5,13 +5,16 @@ 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.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; @Entity +@Table(name = "github_id_reviewer_group") @NoArgsConstructor(access = AccessLevel.PROTECTED) @EqualsAndHashCode(of = "id") @Getter @@ -25,6 +28,7 @@ public class GithubIdReviewerGroup { private GithubId githubId; @ManyToOne + @JoinColumn(name = "reviewer_group_id", nullable = false) private ReviewerGroup reviewerGroup; public GithubIdReviewerGroup(GithubId githubId, ReviewerGroup reviewerGroup) { diff --git a/backend/src/main/java/reviewme/member/domain/ReviewerGroupGithubIds.java b/backend/src/main/java/reviewme/member/domain/ReviewerGroupGithubIds.java index e61919614..43b9adfc0 100644 --- a/backend/src/main/java/reviewme/member/domain/ReviewerGroupGithubIds.java +++ b/backend/src/main/java/reviewme/member/domain/ReviewerGroupGithubIds.java @@ -1,5 +1,6 @@ package reviewme.member.domain; +import jakarta.persistence.CascadeType; import jakarta.persistence.Embeddable; import jakarta.persistence.OneToMany; import java.util.List; @@ -14,7 +15,7 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ReviewerGroupGithubIds { - @OneToMany(mappedBy = "reviewerGroup") + @OneToMany(mappedBy = "reviewerGroup", cascade = CascadeType.PERSIST) private Set reviewerGithubIds; public ReviewerGroupGithubIds(ReviewerGroup reviewerGroup, List githubIds) { diff --git a/backend/src/main/java/reviewme/review/domain/Review.java b/backend/src/main/java/reviewme/review/domain/Review.java index 12e5f1f99..5f2fc4422 100644 --- a/backend/src/main/java/reviewme/review/domain/Review.java +++ b/backend/src/main/java/reviewme/review/domain/Review.java @@ -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; @@ -42,6 +44,9 @@ public class Review { @JoinColumn(name = "reviewer_group_id", nullable = false) private ReviewerGroup reviewerGroup; + @OneToMany(mappedBy = "review") + private List reviewContents; + @Embedded private Keywords keywords; @@ -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; } @@ -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); + } } diff --git a/backend/src/main/java/reviewme/review/domain/ReviewContent.java b/backend/src/main/java/reviewme/review/domain/ReviewContent.java index 7d2839f4c..db3fb01c1 100644 --- a/backend/src/main/java/reviewme/review/domain/ReviewContent.java +++ b/backend/src/main/java/reviewme/review/domain/ReviewContent.java @@ -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) { diff --git a/backend/src/main/java/reviewme/review/dto/request/CreateReviewContentRequest.java b/backend/src/main/java/reviewme/review/dto/request/CreateReviewContentRequest.java index 92a604411..6f3d62182 100644 --- a/backend/src/main/java/reviewme/review/dto/request/CreateReviewContentRequest.java +++ b/backend/src/main/java/reviewme/review/dto/request/CreateReviewContentRequest.java @@ -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 ) { } diff --git a/backend/src/main/java/reviewme/review/dto/request/CreateReviewRequest.java b/backend/src/main/java/reviewme/review/dto/request/CreateReviewRequest.java index 40de59424..dc838c899 100644 --- a/backend/src/main/java/reviewme/review/dto/request/CreateReviewRequest.java +++ b/backend/src/main/java/reviewme/review/dto/request/CreateReviewRequest.java @@ -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 contents, + List reviewContents, - @NotNull(message = "키워드를 입력해주세요.") @Schema(description = "선택된 키워드 ID 목록") - List selectedKeywordIds + @NotNull(message = "키워드를 입력해주세요.") + List keywords ) { } diff --git a/backend/src/main/java/reviewme/review/exception/QuestionNotFoundException.java b/backend/src/main/java/reviewme/review/exception/QuestionNotFoundException.java new file mode 100644 index 000000000..a01ce2e16 --- /dev/null +++ b/backend/src/main/java/reviewme/review/exception/QuestionNotFoundException.java @@ -0,0 +1,10 @@ +package reviewme.review.exception; + +import reviewme.global.exception.NotFoundException; + +public class QuestionNotFoundException extends NotFoundException { + + public QuestionNotFoundException() { + super("질문이 존재하지 않습니다."); + } +} diff --git a/backend/src/main/java/reviewme/review/repository/QuestionRepository.java b/backend/src/main/java/reviewme/review/repository/QuestionRepository.java new file mode 100644 index 000000000..27a8c18ad --- /dev/null +++ b/backend/src/main/java/reviewme/review/repository/QuestionRepository.java @@ -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 { + + default Question getQuestionById(long id) { + return findById(id).orElseThrow(QuestionNotFoundException::new); + } +} diff --git a/backend/src/main/java/reviewme/review/service/ReviewService.java b/backend/src/main/java/reviewme/review/service/ReviewService.java index 38614f6a6..1e3167ee8 100644 --- a/backend/src/main/java/reviewme/review/service/ReviewService.java +++ b/backend/src/main/java/reviewme/review/service/ReviewService.java @@ -1,5 +1,6 @@ package reviewme.review.service; +import java.time.LocalDateTime; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -11,6 +12,7 @@ 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; @@ -18,6 +20,7 @@ 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; @@ -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 keywordList = request.keywords() + .stream() + .map(keywordRepository::getKeywordById) + .toList(); + + Review review = new Review(reviewer, reviewerGroup.getReviewee(), + reviewerGroup, keywordList, LocalDateTime.now()); + Review savedReview = reviewRepository.save(review); + + 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) diff --git a/backend/src/test/java/reviewme/review/domain/ReviewContentTest.java b/backend/src/test/java/reviewme/review/domain/ReviewContentTest.java deleted file mode 100644 index 6c3ed8047..000000000 --- a/backend/src/test/java/reviewme/review/domain/ReviewContentTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package reviewme.review.domain; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import reviewme.review.domain.exception.InvalidAnswerLengthException; - -class ReviewContentTest { - - @ParameterizedTest - @ValueSource(ints = {20, 1000}) - void 리뷰_답변이_범위에_해당하는_경우_올바르게_생성된다(int length) { - // given - Question question = new Question("Question 1"); - String content = "A".repeat(length); - - // when, then - assertDoesNotThrow(() -> new ReviewContent(null, question, content)); - } - - @ParameterizedTest - @ValueSource(ints = {19, 1001}) - void 리뷰_답변이_범위를_벗어나는_경우_예외를_발생한다(int length) { - // given - Question question = new Question("Question 1"); - String content = "A".repeat(length); - - // when, then - assertThatThrownBy(() -> new ReviewContent(null, question, content)) - .isInstanceOf(InvalidAnswerLengthException.class); - } -} diff --git a/backend/src/test/java/reviewme/review/service/ReviewServiceTest.java b/backend/src/test/java/reviewme/review/service/ReviewServiceTest.java index 20ba55c12..d3e13e216 100644 --- a/backend/src/test/java/reviewme/review/service/ReviewServiceTest.java +++ b/backend/src/test/java/reviewme/review/service/ReviewServiceTest.java @@ -1,11 +1,9 @@ -/* package reviewme.review.service; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static reviewme.fixture.KeywordFixture.꼼꼼하게_기록해요; -import static reviewme.fixture.KeywordFixture.회의를_이끌어요; -import static reviewme.fixture.ReviewerGroupFixture.리뷰_그룹; +import static reviewme.fixture.KeywordFixture.추진력이_좋아요; +import static reviewme.fixture.MemberFixture.회원_산초; +import static reviewme.fixture.MemberFixture.회원_아루; import java.time.LocalDateTime; import java.util.List; @@ -13,19 +11,16 @@ import org.springframework.beans.factory.annotation.Autowired; import reviewme.keyword.domain.Keyword; import reviewme.keyword.repository.KeywordRepository; -import reviewme.member.domain.GithubReviewerGroup; +import reviewme.member.domain.GithubId; import reviewme.member.domain.Member; import reviewme.member.domain.ReviewerGroup; -import reviewme.member.repository.GithubReviewerGroupRepository; import reviewme.member.repository.MemberRepository; import reviewme.member.repository.ReviewerGroupRepository; +import reviewme.review.domain.Question; import reviewme.review.domain.Review; -import reviewme.review.domain.exception.DeadlineExpiredException; import reviewme.review.dto.request.CreateReviewContentRequest; import reviewme.review.dto.request.CreateReviewRequest; -import reviewme.review.dto.response.ReviewDetailResponse; -import reviewme.review.exception.GithubReviewerGroupUnAuthorizedException; -import reviewme.review.exception.ReviewAlreadySubmittedException; +import reviewme.review.repository.QuestionRepository; import reviewme.review.repository.ReviewContentRepository; import reviewme.review.repository.ReviewRepository; import reviewme.support.ServiceTest; @@ -45,38 +40,31 @@ class ReviewServiceTest { @Autowired ReviewerGroupRepository reviewerGroupRepository; - @Autowired - GithubReviewerGroupRepository githubReviewerGroupRepository; - @Autowired KeywordRepository keywordRepository; @Autowired ReviewContentRepository reviewContentRepository; + @Autowired + QuestionRepository questionRepository; + @Test void 리뷰를_작성한다() { // given - memberRepository.save(new Member("산초", "sancho")); - Member reviewee = memberRepository.save(new Member("아루", "aru")); + Member reviewer = memberRepository.save(회원_산초.create()); + Member reviewee = memberRepository.save(회원_아루.create()); + List reviewerGithubIds = List.of(reviewer.getGithubId()); + ReviewerGroup reviewerGroup = reviewerGroupRepository.save( - new ReviewerGroup(reviewee, "그룹A", "그룹 설명", LocalDateTime.now().minusDays(1)) + new ReviewerGroup(reviewee, reviewerGithubIds, "그룹명", "그룹설명", LocalDateTime.now().plusDays(1)) ); - githubReviewerGroupRepository.save(new GithubReviewerGroup("sancho", reviewerGroup)); - Keyword keyword1 = keywordRepository.save(꼼꼼하게_기록해요.create()); - Keyword keyword2 = keywordRepository.save(회의를_이끌어요.create()); + Question question = questionRepository.save(new Question("질문")); + Keyword keyword = keywordRepository.save(추진력이_좋아요.create()); + CreateReviewContentRequest contentRequest = new CreateReviewContentRequest(question.getId(), "답변".repeat(10)); - CreateReviewContentRequest contentRequest1 = new CreateReviewContentRequest( - 1L, "소프트스킬이 어떤가요?", "소통을 잘해요" - ); - CreateReviewContentRequest contentRequest2 = new CreateReviewContentRequest( - 2L, "기술역량이 어떤가요?", "스트림을 잘다뤄요" - ); - CreateReviewRequest createReviewRequest = new CreateReviewRequest( - 1L, - 1L, - List.of(contentRequest1, contentRequest2), - List.of(keyword1.getId(), keyword2.getId()) + CreateReviewRequest createReviewRequest = new CreateReviewRequest(reviewer.getId(), reviewerGroup.getId(), + List.of(contentRequest), List.of(keyword.getId()) ); // when @@ -86,83 +74,4 @@ class ReviewServiceTest { List actual = reviewRepository.findAll(); assertThat(actual).hasSize(1); } - - @Test - void 리뷰를_조회한다() { - // given - Member reviewer = memberRepository.save(new Member("테드", "ted")); - Member reviewee = memberRepository.save(new Member("아루", "aru")); - memberRepository.save(new Member("산초", "sancho")); - ReviewerGroup reviewerGroup = reviewerGroupRepository.save(리뷰_그룹.create(reviewee)); - Review review = reviewRepository.save(new Review(reviewer, reviewerGroup, LocalDateTime.now())); - - // when - ReviewDetailResponse response = reviewService.findReview(review.getId()); - - // then - Long id = response.id(); - assertThat(id).isEqualTo(review.getId()); - } - - @Test - void 리뷰어_그룹에_속하지_않는_리뷰어가_리뷰를_작성할_경우_예외를_발생한다() { - // given - Member reviewee = memberRepository.save(new Member("아루", "aru")); - Member reviewer = memberRepository.save(new Member("테드", "ted")); - ReviewerGroup reviewerGroup = reviewerGroupRepository.save(리뷰_그룹.create(reviewee)); - githubReviewerGroupRepository.save(new GithubReviewerGroup("kirby", reviewerGroup)); - - CreateReviewRequest createReviewRequest = new CreateReviewRequest( - reviewer.getId(), - reviewerGroup.getId(), - List.of(), - List.of() - ); - - // when, then - assertThatThrownBy(() -> reviewService.createReview(createReviewRequest)) - .isInstanceOf(GithubReviewerGroupUnAuthorizedException.class); - } - - @Test - void 이미_작성한_리뷰가_있는데_리뷰를_작성할_경우_예외를_발생한다() { - // given - Member reviewee = memberRepository.save(new Member("아루", "aru")); - Member reviewer = memberRepository.save(new Member("테드", "ted")); - ReviewerGroup reviewerGroup = reviewerGroupRepository.save(리뷰_그룹.create(reviewee)); - githubReviewerGroupRepository.save(new GithubReviewerGroup("ted", reviewerGroup)); - - CreateReviewRequest createReviewRequest = new CreateReviewRequest( - reviewer.getId(), - reviewerGroup.getId(), - List.of(), - List.of() - ); - - reviewRepository.save(new Review(reviewer, reviewerGroup, LocalDateTime.now())); - - // when, then - assertThatThrownBy(() -> reviewService.createReview(createReviewRequest)) - .isInstanceOf(ReviewAlreadySubmittedException.class); - } - - @Test - void 데드라인이_지난_리뷰그룹에_대해_리뷰를_작성하려하면_예외가_발생한다() { - // given - memberRepository.save(new Member("산초", "sancho")); - Member reviewee = memberRepository.save(new Member("아루", "aru")); - LocalDateTime createdAt = LocalDateTime.now().minusDays(7).minusMinutes(1); - ReviewerGroup reviewerGroup = reviewerGroupRepository.save( - new ReviewerGroup(reviewee, "그룹A", "그룹 설명", createdAt) - ); - githubReviewerGroupRepository.save(new GithubReviewerGroup("sancho", reviewerGroup)); - CreateReviewRequest createReviewRequest = new CreateReviewRequest( - 1L, 1L, List.of(), List.of() - ); - - // when, then - assertThatThrownBy(() -> reviewService.createReview(createReviewRequest)) - .isInstanceOf(DeadlineExpiredException.class); - } } -*/