-
Notifications
You must be signed in to change notification settings - Fork 2
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 구현 #806
Merged
Merged
Changes from all commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
cdb2784
refactor: dto 이름 변경
nayonsoso 683cd59
feat: 질문ID에 해당하는 Answer 반환 함수 추가
nayonsoso 847b565
feat: 질문ID에 해당하는 OptionItem들 반환 함수 추가
nayonsoso 4d4cf04
feat: 리뷰 요청 코드와 섹션ID에 해당하는 질문 반환 함수 추가
nayonsoso 800345f
feat: 리뷰 모이보기 API 구현
nayonsoso fb00e55
refactor: 테스트에서 검증하고자 하는 것을 분명히
nayonsoso a786bf0
refactor: 패키지 의존 방향 통일을 위한 레포지토리 함수 이동
nayonsoso 6a173fc
refactor: 테스트 목적에 해당하는 것만 남기기
nayonsoso 98dd521
refactor: 함수 이름 변경
nayonsoso a9b265e
refactor: 가동성을 위한 함수 분리
nayonsoso b4d951e
refactor: 다른 서비스 클래스들과 네이밍 통일
nayonsoso 219dc1a
feat: 리뷰 요청 코드 검증, 섹션 아이디 검증 추가
nayonsoso 97e91ef
refactor: 섹션에 해당하는 질문 가져오는 함수 수정
nayonsoso a97fe47
refactor: 답변 가져오는 함수 수정
nayonsoso c7c9c7b
feat: 답변하지 않은 내용은 빈 배열로 받아오도록 하는 기능 추가
nayonsoso 2088cf1
fix: 깨지는 테스트 코드 봉합
nayonsoso e8f91be
refactor: Mapper 분리
nayonsoso 3da45ef
refactor: 섹션 검증 방법 변경
nayonsoso be44806
refactor: 필요없는 JOIN 제거
nayonsoso 4fb46d4
test: 테스트 코드 목적별로 분리
nayonsoso e4e3bdc
feat: 질문 목록을 position 순서대로 정렬
nayonsoso 2bbdc85
refactor: 테스트 코드 extracting 사용 변경
nayonsoso 91a0a82
chore: 오타 수정
nayonsoso 0f3aec6
test: 다른 리뷰 그룹이 있는 상황에 대한 테스트
nayonsoso a484ec9
refactor: 접근제어자 수정
nayonsoso bb0c4ea
feat: 하이라이트 응답 추가
nayonsoso 064f71e
test: 추가된 속성 반환 테스트
nayonsoso 970e59c
refactor: 유연한 레포지토리 함수로 변경
nayonsoso 985959d
refactor: 변수명 변경
nayonsoso c80f961
refactor: 가독성 개선
nayonsoso c490a20
refactor: Answer -> 구체Answer 캐스팅 예외 추가
nayonsoso 50c2126
refactor: 서술형 답변 가져오는 함수 수정
nayonsoso 3725edf
refactor: 선택지 목록 가져오는 함수 수정
nayonsoso e37bda3
test: reviewGatherMapper에 대한 테스트 작성
nayonsoso 3eea7eb
style: 개행
nayonsoso 196f309
chore: 로그 메세지 수정
nayonsoso 1f9aec2
refactor: 컨트롤러 인자 타입 변경
nayonsoso 8905a13
feat: 최대로 내려주는 응답 수 제한 기능 구현
nayonsoso e8f0a40
refactor: 테스트 코드 필드 접근제한자 추가
nayonsoso d202f58
refactor: 변수명 변경
nayonsoso 8a8ce13
chore: 사용하지 않는 메서드 제거
nayonsoso File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 0 additions & 14 deletions
14
backend/src/main/java/reviewme/review/service/GatheredReviewLookupService.java
This file was deleted.
Oops, something went wrong.
73 changes: 73 additions & 0 deletions
73
backend/src/main/java/reviewme/review/service/ReviewGatheredLookupService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package reviewme.review.service; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import reviewme.question.domain.Question; | ||
import reviewme.question.repository.QuestionRepository; | ||
import reviewme.review.domain.Answer; | ||
import reviewme.review.repository.AnswerRepository; | ||
import reviewme.review.service.dto.response.gathered.ReviewsGatheredBySectionResponse; | ||
import reviewme.review.service.exception.ReviewGroupNotFoundByReviewRequestCodeException; | ||
import reviewme.review.service.exception.SectionNotFoundInTemplateException; | ||
import reviewme.review.service.mapper.ReviewGatherMapper; | ||
import reviewme.reviewgroup.domain.ReviewGroup; | ||
import reviewme.reviewgroup.repository.ReviewGroupRepository; | ||
import reviewme.template.domain.Section; | ||
import reviewme.template.repository.SectionRepository; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class ReviewGatheredLookupService { | ||
|
||
private static final int ANSWER_RESPONSE_LIMIT = 100; | ||
|
||
private final QuestionRepository questionRepository; | ||
private final AnswerRepository answerRepository; | ||
private final ReviewGroupRepository reviewGroupRepository; | ||
private final SectionRepository sectionRepository; | ||
|
||
private final ReviewGatherMapper reviewGatherMapper; | ||
|
||
@Transactional(readOnly = true) | ||
public ReviewsGatheredBySectionResponse getReceivedReviewsBySectionId(String reviewRequestCode, long sectionId) { | ||
ReviewGroup reviewGroup = getReviewGroupOrThrow(reviewRequestCode); | ||
Section section = getSectionOrThrow(sectionId, reviewGroup); | ||
Map<Question, List<Answer>> questionAnswers = getQuestionAnswers(section, reviewGroup); | ||
|
||
return reviewGatherMapper.mapToReviewsGatheredBySection(questionAnswers); | ||
} | ||
|
||
private ReviewGroup getReviewGroupOrThrow(String reviewRequestCode) { | ||
return reviewGroupRepository.findByReviewRequestCode(reviewRequestCode) | ||
.orElseThrow(() -> new ReviewGroupNotFoundByReviewRequestCodeException(reviewRequestCode)); | ||
} | ||
|
||
private Section getSectionOrThrow(long sectionId, ReviewGroup reviewGroup) { | ||
return sectionRepository.findByIdAndTemplateId(sectionId, reviewGroup.getTemplateId()) | ||
.orElseThrow(() -> new SectionNotFoundInTemplateException(sectionId, reviewGroup.getTemplateId())); | ||
} | ||
|
||
private Map<Question, List<Answer>> getQuestionAnswers(Section section, ReviewGroup reviewGroup) { | ||
Map<Long, Question> questionIdQuestion = questionRepository | ||
.findAllBySectionIdOrderByPosition(section.getId()) | ||
.stream() | ||
.collect(Collectors.toMap(Question::getId, Function.identity())); | ||
|
||
Map<Long, List<Answer>> questionIdAnswers = answerRepository | ||
.findReceivedAnswersByQuestionIds(reviewGroup.getId(), questionIdQuestion.keySet(), | ||
ANSWER_RESPONSE_LIMIT) | ||
.stream() | ||
.collect(Collectors.groupingBy(Answer::getQuestionId)); | ||
|
||
return questionIdQuestion.values().stream() | ||
.collect(Collectors.toMap( | ||
Function.identity(), | ||
question -> questionIdAnswers.getOrDefault(question.getId(), List.of()) | ||
)); | ||
} | ||
} |
6 changes: 0 additions & 6 deletions
6
...nd/src/main/java/reviewme/review/service/dto/response/gathered/AnswerContentResponse.java
This file was deleted.
Oops, something went wrong.
9 changes: 9 additions & 0 deletions
9
backend/src/main/java/reviewme/review/service/dto/response/gathered/HighlightResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package reviewme.review.service.dto.response.gathered; | ||
|
||
import java.util.List; | ||
|
||
public record HighlightResponse( | ||
long lineIndex, | ||
List<RangeResponse> ranges | ||
) { | ||
} |
7 changes: 7 additions & 0 deletions
7
backend/src/main/java/reviewme/review/service/dto/response/gathered/RangeResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package reviewme.review.service.dto.response.gathered; | ||
|
||
public record RangeResponse( | ||
long startIndex, | ||
long endIndex | ||
) { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
backend/src/main/java/reviewme/review/service/dto/response/gathered/TextResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package reviewme.review.service.dto.response.gathered; | ||
|
||
import java.util.List; | ||
|
||
public record TextResponse( | ||
long id, | ||
String content, | ||
List<HighlightResponse> highlights | ||
) { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,6 @@ | |
|
||
public record VoteResponse( | ||
String content, | ||
int count | ||
long count | ||
) { | ||
} |
13 changes: 13 additions & 0 deletions
13
...c/main/java/reviewme/review/service/exception/GatheredAnswersTypeNonUniformException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package reviewme.review.service.exception; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import reviewme.global.exception.DataInconsistencyException; | ||
|
||
@Slf4j | ||
public class GatheredAnswersTypeNonUniformException extends DataInconsistencyException { | ||
|
||
public GatheredAnswersTypeNonUniformException(Throwable cause) { | ||
super("서버 내부 오류가 발생했습니다."); | ||
log.error("The types of answers to questions are not uniform.", cause); | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
...d/src/main/java/reviewme/review/service/exception/SectionNotFoundInTemplateException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package reviewme.review.service.exception; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import reviewme.global.exception.NotFoundException; | ||
|
||
@Slf4j | ||
public class SectionNotFoundInTemplateException extends NotFoundException { | ||
|
||
public SectionNotFoundInTemplateException(long sectionId, long templateId) { | ||
super("섹션 정보를 찾을 수 없습니다."); | ||
log.info("Section not found in template - sectionId: {}, templateId: {}", sectionId, templateId, this); | ||
} | ||
} |
85 changes: 85 additions & 0 deletions
85
backend/src/main/java/reviewme/review/service/mapper/ReviewGatherMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package reviewme.review.service.mapper; | ||
|
||
import jakarta.annotation.Nullable; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
import reviewme.question.domain.OptionItem; | ||
import reviewme.question.domain.Question; | ||
import reviewme.question.repository.QuestionRepository; | ||
import reviewme.review.domain.Answer; | ||
import reviewme.review.domain.CheckboxAnswer; | ||
import reviewme.review.domain.CheckboxAnswerSelectedOption; | ||
import reviewme.review.domain.TextAnswer; | ||
import reviewme.review.service.dto.response.gathered.ReviewsGatheredByQuestionResponse; | ||
import reviewme.review.service.dto.response.gathered.ReviewsGatheredBySectionResponse; | ||
import reviewme.review.service.dto.response.gathered.SimpleQuestionResponse; | ||
import reviewme.review.service.dto.response.gathered.TextResponse; | ||
import reviewme.review.service.dto.response.gathered.VoteResponse; | ||
import reviewme.review.service.exception.GatheredAnswersTypeNonUniformException; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class ReviewGatherMapper { | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 확실히 조회 기능별로 mapper 생기는 건 점점 무섭긴 하네요.. |
||
private final QuestionRepository questionRepository; | ||
|
||
public ReviewsGatheredBySectionResponse mapToReviewsGatheredBySection(Map<Question, List<Answer>> questionAnswers) { | ||
List<ReviewsGatheredByQuestionResponse> reviews = questionAnswers.entrySet() | ||
.stream() | ||
.map(entry -> mapToReviewsGatheredByQuestion(entry.getKey(), entry.getValue())) | ||
.toList(); | ||
|
||
return new ReviewsGatheredBySectionResponse(reviews); | ||
} | ||
|
||
private ReviewsGatheredByQuestionResponse mapToReviewsGatheredByQuestion(Question question, List<Answer> answers) { | ||
return new ReviewsGatheredByQuestionResponse( | ||
new SimpleQuestionResponse(question.getId(), question.getContent(), question.getQuestionType()), | ||
mapToTextResponse(question, answers), | ||
mapToVoteResponse(question, answers) | ||
); | ||
} | ||
|
||
@Nullable | ||
private List<TextResponse> mapToTextResponse(Question question, List<Answer> answers) { | ||
if (question.isSelectable()) { | ||
return null; | ||
} | ||
|
||
List<TextAnswer> textAnswers = castAllOrThrow(answers, TextAnswer.class); | ||
return textAnswers.stream() | ||
.map(textAnswer -> new TextResponse(textAnswer.getId(), textAnswer.getContent(), List.of())) | ||
.toList(); | ||
} | ||
|
||
@Nullable | ||
private List<VoteResponse> mapToVoteResponse(Question question, List<Answer> answers) { | ||
if (!question.isSelectable()) { | ||
return null; | ||
} | ||
|
||
List<CheckboxAnswer> checkboxAnswers = castAllOrThrow(answers, CheckboxAnswer.class); | ||
Map<Long, Long> optionItemIdVoteCount = checkboxAnswers.stream() | ||
.flatMap(checkboxAnswer -> checkboxAnswer.getSelectedOptionIds().stream()) | ||
.collect(Collectors.groupingBy(CheckboxAnswerSelectedOption::getSelectedOptionId, | ||
Collectors.counting())); | ||
|
||
List<OptionItem> allOptionItem = questionRepository.findAllOptionItemsByIdOrderByPosition(question.getId()); | ||
return allOptionItem.stream() | ||
.map(optionItem -> new VoteResponse( | ||
optionItem.getContent(), | ||
optionItemIdVoteCount.getOrDefault(optionItem.getId(), 0L))) | ||
.toList(); | ||
} | ||
|
||
private <T extends Answer> List<T> castAllOrThrow(List<Answer> answers, Class<T> clazz) { | ||
try { | ||
return answers.stream().map(clazz::cast).toList(); | ||
} catch (Exception ex) { | ||
throw new GatheredAnswersTypeNonUniformException(ex); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Response
접미사를 빼면 남는 건Text
인데, 맥락 전달이 부족하지 않을까요? 기존AnswerContent
에서 바뀌게 된 이유가 무엇일까용?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Answer 라는 이름의 도메인이 있었기 때문이에요.
서비스 코드에서 Answer 도메인과 AnswerContentResponse 를 함께 사용하다보니AnswerContentResponse 이 기존의 의미인 "서술형 답변" 이라기보다
Answer 자체에 더 가깝다고 느껴졌어요!