Skip to content
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 @@ -13,6 +13,7 @@
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.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand Down Expand Up @@ -40,11 +41,12 @@ public ApiResponse<AnalysisResponse> analyze(
@GetMapping
public ApiResponse<AnalysisResponse> getAnalysis(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@PathVariable Long mockApplyId
@PathVariable Long mockApplyId,
@RequestParam(required = false) Integer sequence
) {
return ApiResponse.onSuccess(
"자소서 분석 결과 조회에 성공했습니다.",
analysisService.getAnalysis(getAuthenticatedUser(userDetails), mockApplyId)
analysisService.getAnalysis(getAuthenticatedUser(userDetails), mockApplyId, sequence)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,62 @@ public AnalysisResponse analyze(User user, Long mockApplyId) {
}

public AnalysisResponse getAnalysis(User user, Long mockApplyId) {
return getAnalysis(user, mockApplyId, null);
}

public AnalysisResponse getAnalysis(User user, Long mockApplyId, Integer sequence) {
MockApply mockApply = getOwnedMockApply(user, mockApplyId);
Analysis analysis = analysisRepository.findByMockApplyId(mockApply.getId())
if (sequence != null) {
mockApply = resolveMockApplyBySequence(mockApply, sequence);
}
Long resolvedMockApplyId = mockApply.getId();

Analysis analysis = analysisRepository.findByMockApplyId(resolvedMockApplyId)
.orElseThrow(() -> new GeneralException(
GeneralErrorCode.ANALYSIS_NOT_FOUND,
"해당 모의 서류 지원의 분석 결과를 찾을 수 없습니다. mockApplyId=" + mockApplyId
"해당 모의 서류 지원의 분석 결과를 찾을 수 없습니다. mockApplyId=" + resolvedMockApplyId
));
List<Question> questions = questionRepository.findAllByMockApplyIdOrderByIdAsc(mockApply.getId());
List<Question> questions = questionRepository.findAllByMockApplyIdOrderByIdAsc(resolvedMockApplyId);
List<QuestionAnalysis> questionAnalyses =
questionAnalysisRepository.findAllByAnalysisIdOrderByQuestionIdAscIdAsc(analysis.getId());

return toResponse(mockApply, analysis, questions, questionAnalyses);
}

private MockApply resolveMockApplyBySequence(MockApply baseMockApply, int sequence) {
if (sequence < 1) {
throw new GeneralException(
GeneralErrorCode.INVALID_PARAMETER,
"sequence는 1 이상의 값이어야 합니다."
);
}

List<MockApply> mockApplies = mockApplyRepository.findAllByUserIdAndJobPostingIdOrderByIdAsc(
baseMockApply.getUser().getId(),
baseMockApply.getJobPosting().getId()
);

int derivedSequence = 0;
for (MockApply mockApply : mockApplies) {
derivedSequence++;
int resolvedSequence = mockApply.getSequence() != null && mockApply.getSequence() > 0
? mockApply.getSequence()
: derivedSequence;

if (resolvedSequence == sequence) {
return mockApply;
}
}

throw new GeneralException(
GeneralErrorCode.MOCK_APPLY_NOT_FOUND,
"해당 순번의 모의 서류 지원을 찾을 수 없습니다. mockApplyId="
+ baseMockApply.getId()
+ ", sequence="
+ sequence
);
}

private void replaceExistingAnalysis(MockApply mockApply) {
Optional<Analysis> existingAnalysis = analysisRepository.findByMockApplyId(mockApply.getId());
if (existingAnalysis.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
public interface MockApplyRepository extends JpaRepository<MockApply, Long> {
List<MockApply> findAllByUserId(Long userId);
List<MockApply> findAllByJobPostingId(Long jobPostingId);
List<MockApply> findAllByUserIdAndJobPostingIdOrderByIdAsc(Long userId, Long jobPostingId);
long countByUserIdAndJobPostingId(Long userId, Long jobPostingId);

@Query("""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,106 @@ void getAnalysis() {
assertThat(response.questions().get(0).analyses()).hasSize(1);
}

@Test
@DisplayName("sequence 쿼리값으로 같은 공고의 특정 회차 분석 결과를 조회한다")
void getAnalysisBySequence() {
User user = saveUser("analysis-get-sequence@example.com");
JobPosting jobPosting = saveJobPosting(user);
MockApply firstMockApply = mockApplyRepository.save(MockApply.create(user, jobPosting, ApplyType.ACTUAL));
MockApply secondMockApply = mockApplyRepository.save(MockApply.create(user, jobPosting, ApplyType.ACTUAL));
saveQuestion(firstMockApply, "첫 번째 지원 동기", "첫 번째 답변입니다.");
Question secondQuestion = saveQuestion(secondMockApply, "두 번째 지원 동기", "두 번째 답변입니다.");
when(analysisAiClient.analyze(any(), any()))
.thenReturn(new AnalysisLlmResponse(
61,
62,
63,
64,
"첫 번째 분석 결과",
List.of()
))
.thenReturn(new AnalysisLlmResponse(
81,
82,
83,
84,
"두 번째 분석 결과",
List.of(new AnalysisLlmResponse.QuestionAnalysisItem(
secondQuestion.getId(),
"두 번째 답변입니다.",
"mentioned",
"근거가 더 필요합니다.",
"두 번째 답변에 구체적인 성과를 추가했습니다."
))
));

analysisService.analyze(user, firstMockApply.getId());
AnalysisResponse saved = analysisService.analyze(user, secondMockApply.getId());

AnalysisResponse response = analysisService.getAnalysis(user, firstMockApply.getId(), 2);

assertThat(response.analysisId()).isEqualTo(saved.analysisId());
assertThat(response.mockApplyId()).isEqualTo(secondMockApply.getId());
assertThat(response.sequence()).isEqualTo(2);
assertThat(response.feedback()).isEqualTo("두 번째 분석 결과");
}

@Test
@DisplayName("sequence 쿼리값 조회는 저장된 지원 순번을 우선 사용한다")
void getAnalysisByStoredSequence() {
User user = saveUser("analysis-get-stored-sequence@example.com");
JobPosting jobPosting = saveJobPosting(user);
MockApply firstMockApply = mockApplyRepository.save(MockApply.create(user, jobPosting, ApplyType.ACTUAL));
MockApply secondMockApply = mockApplyRepository.save(MockApply.create(user, jobPosting, ApplyType.ACTUAL, 4));
saveQuestion(firstMockApply, "첫 번째 지원 동기", "첫 번째 답변입니다.");
Question secondQuestion = saveQuestion(secondMockApply, "두 번째 지원 동기", "두 번째 답변입니다.");
when(analysisAiClient.analyze(any(), any()))
.thenReturn(new AnalysisLlmResponse(
61,
62,
63,
64,
"첫 번째 분석 결과",
List.of()
))
.thenReturn(new AnalysisLlmResponse(
81,
82,
83,
84,
"저장 순번 분석 결과",
List.of(new AnalysisLlmResponse.QuestionAnalysisItem(
secondQuestion.getId(),
"두 번째 답변입니다.",
"mentioned",
"근거가 더 필요합니다.",
"두 번째 답변에 구체적인 성과를 추가했습니다."
))
));

analysisService.analyze(user, firstMockApply.getId());
AnalysisResponse saved = analysisService.analyze(user, secondMockApply.getId());

AnalysisResponse response = analysisService.getAnalysis(user, firstMockApply.getId(), 4);

assertThat(response.analysisId()).isEqualTo(saved.analysisId());
assertThat(response.mockApplyId()).isEqualTo(secondMockApply.getId());
assertThat(response.sequence()).isEqualTo(4);
assertThat(response.feedback()).isEqualTo("저장 순번 분석 결과");
}

@Test
@DisplayName("존재하지 않는 sequence로 분석 결과 조회 시 예외를 던진다")
void getAnalysisThrowsWhenSequenceDoesNotExist() {
User user = saveUser("analysis-get-sequence-missing@example.com");
MockApply mockApply = saveMockApply(user);

assertThatThrownBy(() -> analysisService.getAnalysis(user, mockApply.getId(), 2))
.isInstanceOf(GeneralException.class)
.extracting("code")
.isEqualTo(GeneralErrorCode.MOCK_APPLY_NOT_FOUND);
}

@Test
@DisplayName("크레딧 1개인 사용자가 동시에 분석을 요청해도 하나만 성공한다")
void analyzeConcurrentlyUsesCreditOnlyOnce() throws Exception {
Expand Down
Loading