From b4683636f84f0187b4e7181caeffdf9500fb2407 Mon Sep 17 00:00:00 2001 From: shinae1023 Date: Wed, 27 May 2026 11:37:12 +0900 Subject: [PATCH] Add job posting sequence analysis lookup --- .../controller/AnalysisController.java | 6 +-- .../JobPostingAnalysisController.java | 46 +++++++++++++++++++ .../analysis/service/AnalysisService.java | 41 ++++++++++------- .../analysis/service/AnalysisServiceTest.java | 22 +++++---- 4 files changed, 86 insertions(+), 29 deletions(-) create mode 100644 src/main/java/com/jobdri/jobdri_api/domain/analysis/controller/JobPostingAnalysisController.java diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/controller/AnalysisController.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/controller/AnalysisController.java index 71874c9..d5e552d 100644 --- a/src/main/java/com/jobdri/jobdri_api/domain/analysis/controller/AnalysisController.java +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/controller/AnalysisController.java @@ -13,7 +13,6 @@ 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; @@ -41,12 +40,11 @@ public ApiResponse analyze( @GetMapping public ApiResponse getAnalysis( @AuthenticationPrincipal UserDetailsImpl userDetails, - @PathVariable Long mockApplyId, - @RequestParam(required = false) Integer sequence + @PathVariable Long mockApplyId ) { return ApiResponse.onSuccess( "자소서 분석 결과 조회에 성공했습니다.", - analysisService.getAnalysis(getAuthenticatedUser(userDetails), mockApplyId, sequence) + analysisService.getAnalysis(getAuthenticatedUser(userDetails), mockApplyId) ); } diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/controller/JobPostingAnalysisController.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/controller/JobPostingAnalysisController.java new file mode 100644 index 0000000..b556c40 --- /dev/null +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/controller/JobPostingAnalysisController.java @@ -0,0 +1,46 @@ +package com.jobdri.jobdri_api.domain.analysis.controller; + +import com.jobdri.jobdri_api.domain.analysis.dto.response.AnalysisResponse; +import com.jobdri.jobdri_api.domain.analysis.service.AnalysisService; +import com.jobdri.jobdri_api.global.apiPayload.ApiResponse; +import com.jobdri.jobdri_api.global.apiPayload.code.GeneralErrorCode; +import com.jobdri.jobdri_api.global.apiPayload.exception.GeneralException; +import com.jobdri.jobdri_api.global.security.UserDetailsImpl; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/job-postings/{jobPostingId}/analysis") +@Tag(name = "Analysis", description = "자소서 분석 API") +public class JobPostingAnalysisController { + + private final AnalysisService analysisService; + + @Operation(summary = "지원 회차별 자소서 분석 결과 조회", description = "공고 기준으로 지정한 지원 회차의 자소서 분석 결과를 조회합니다.") + @GetMapping + public ApiResponse getAnalysisBySequence( + @AuthenticationPrincipal UserDetailsImpl userDetails, + @PathVariable Long jobPostingId, + @RequestParam Integer sequence + ) { + return ApiResponse.onSuccess( + "자소서 분석 결과 조회에 성공했습니다.", + analysisService.getAnalysisByJobPostingSequence(getAuthenticatedUser(userDetails), jobPostingId, sequence) + ); + } + + private com.jobdri.jobdri_api.domain.user.entity.User getAuthenticatedUser(UserDetailsImpl userDetails) { + if (userDetails == null || userDetails.getUser() == null) { + throw new GeneralException(GeneralErrorCode.MISSING_AUTH_INFO, "인증 정보가 누락되었습니다."); + } + return userDetails.getUser(); + } +} diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisService.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisService.java index f1ef976..fd86c4d 100644 --- a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisService.java +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisService.java @@ -12,6 +12,8 @@ import com.jobdri.jobdri_api.domain.analysis.repository.QuestionAnalysisRepository; import com.jobdri.jobdri_api.domain.analysis.repository.QuestionRepository; import com.jobdri.jobdri_api.domain.audit.annotation.AuditLogEvent; +import com.jobdri.jobdri_api.domain.jobposting.entity.JobPosting; +import com.jobdri.jobdri_api.domain.jobposting.service.JobPostingService; import com.jobdri.jobdri_api.domain.mockapply.entity.MockApply; import com.jobdri.jobdri_api.domain.mockapply.entity.MockApplyStatus; import com.jobdri.jobdri_api.domain.mockapply.repository.MockApplyRepository; @@ -41,6 +43,7 @@ public class AnalysisService { private final QuestionRepository questionRepository; private final AnalysisRepository analysisRepository; private final QuestionAnalysisRepository questionAnalysisRepository; + private final JobPostingService jobPostingService; private final AnalysisAiClient analysisAiClient; private final CreditService creditService; @@ -88,29 +91,35 @@ 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); - if (sequence != null) { - mockApply = resolveMockApplyBySequence(mockApply, sequence); - } - Long resolvedMockApplyId = mockApply.getId(); + Analysis analysis = analysisRepository.findByMockApplyId(mockApply.getId()) + .orElseThrow(() -> new GeneralException( + GeneralErrorCode.ANALYSIS_NOT_FOUND, + "해당 모의 서류 지원의 분석 결과를 찾을 수 없습니다. mockApplyId=" + mockApply.getId() + )); + List questions = questionRepository.findAllByMockApplyIdOrderByIdAsc(mockApply.getId()); + List questionAnalyses = + questionAnalysisRepository.findAllByAnalysisIdOrderByQuestionIdAscIdAsc(analysis.getId()); + + return toResponse(mockApply, analysis, questions, questionAnalyses); + } - Analysis analysis = analysisRepository.findByMockApplyId(resolvedMockApplyId) + public AnalysisResponse getAnalysisByJobPostingSequence(User user, Long jobPostingId, int sequence) { + JobPosting jobPosting = jobPostingService.getOwnedJobPosting(user, jobPostingId); + MockApply mockApply = resolveMockApplyBySequence(jobPosting, sequence); + Analysis analysis = analysisRepository.findByMockApplyId(mockApply.getId()) .orElseThrow(() -> new GeneralException( GeneralErrorCode.ANALYSIS_NOT_FOUND, - "해당 모의 서류 지원의 분석 결과를 찾을 수 없습니다. mockApplyId=" + resolvedMockApplyId + "해당 모의 서류 지원의 분석 결과를 찾을 수 없습니다. mockApplyId=" + mockApply.getId() )); - List questions = questionRepository.findAllByMockApplyIdOrderByIdAsc(resolvedMockApplyId); + List questions = questionRepository.findAllByMockApplyIdOrderByIdAsc(mockApply.getId()); List questionAnalyses = questionAnalysisRepository.findAllByAnalysisIdOrderByQuestionIdAscIdAsc(analysis.getId()); return toResponse(mockApply, analysis, questions, questionAnalyses); } - private MockApply resolveMockApplyBySequence(MockApply baseMockApply, int sequence) { + private MockApply resolveMockApplyBySequence(JobPosting jobPosting, int sequence) { if (sequence < 1) { throw new GeneralException( GeneralErrorCode.INVALID_PARAMETER, @@ -119,8 +128,8 @@ private MockApply resolveMockApplyBySequence(MockApply baseMockApply, int sequen } List mockApplies = mockApplyRepository.findAllByUserIdAndJobPostingIdOrderByIdAsc( - baseMockApply.getUser().getId(), - baseMockApply.getJobPosting().getId() + jobPosting.getUser().getId(), + jobPosting.getId() ); int derivedSequence = 0; @@ -137,8 +146,8 @@ private MockApply resolveMockApplyBySequence(MockApply baseMockApply, int sequen throw new GeneralException( GeneralErrorCode.MOCK_APPLY_NOT_FOUND, - "해당 순번의 모의 서류 지원을 찾을 수 없습니다. mockApplyId=" - + baseMockApply.getId() + "해당 순번의 모의 서류 지원을 찾을 수 없습니다. jobPostingId=" + + jobPosting.getId() + ", sequence=" + sequence ); diff --git a/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisServiceTest.java b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisServiceTest.java index d014a49..03143af 100644 --- a/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisServiceTest.java +++ b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisServiceTest.java @@ -464,8 +464,8 @@ void getAnalysis() { } @Test - @DisplayName("sequence 쿼리값으로 같은 공고의 특정 회차 분석 결과를 조회한다") - void getAnalysisBySequence() { + @DisplayName("jobPosting 기준 sequence로 특정 회차 분석 결과를 조회한다") + void getAnalysisByJobPostingSequence() { User user = saveUser("analysis-get-sequence@example.com"); JobPosting jobPosting = saveJobPosting(user); MockApply firstMockApply = mockApplyRepository.save(MockApply.create(user, jobPosting, ApplyType.ACTUAL)); @@ -499,7 +499,7 @@ void getAnalysisBySequence() { analysisService.analyze(user, firstMockApply.getId()); AnalysisResponse saved = analysisService.analyze(user, secondMockApply.getId()); - AnalysisResponse response = analysisService.getAnalysis(user, firstMockApply.getId(), 2); + AnalysisResponse response = analysisService.getAnalysisByJobPostingSequence(user, jobPosting.getId(), 2); assertThat(response.analysisId()).isEqualTo(saved.analysisId()); assertThat(response.mockApplyId()).isEqualTo(secondMockApply.getId()); @@ -508,8 +508,8 @@ void getAnalysisBySequence() { } @Test - @DisplayName("sequence 쿼리값 조회는 저장된 지원 순번을 우선 사용한다") - void getAnalysisByStoredSequence() { + @DisplayName("jobPosting 기준 sequence 조회는 저장된 지원 순번을 우선 사용한다") + void getAnalysisByJobPostingStoredSequence() { User user = saveUser("analysis-get-stored-sequence@example.com"); JobPosting jobPosting = saveJobPosting(user); MockApply firstMockApply = mockApplyRepository.save(MockApply.create(user, jobPosting, ApplyType.ACTUAL)); @@ -543,7 +543,7 @@ void getAnalysisByStoredSequence() { analysisService.analyze(user, firstMockApply.getId()); AnalysisResponse saved = analysisService.analyze(user, secondMockApply.getId()); - AnalysisResponse response = analysisService.getAnalysis(user, firstMockApply.getId(), 4); + AnalysisResponse response = analysisService.getAnalysisByJobPostingSequence(user, jobPosting.getId(), 4); assertThat(response.analysisId()).isEqualTo(saved.analysisId()); assertThat(response.mockApplyId()).isEqualTo(secondMockApply.getId()); @@ -552,12 +552,16 @@ void getAnalysisByStoredSequence() { } @Test - @DisplayName("존재하지 않는 sequence로 분석 결과 조회 시 예외를 던진다") - void getAnalysisThrowsWhenSequenceDoesNotExist() { + @DisplayName("존재하지 않는 sequence로 jobPosting 분석 결과 조회 시 예외를 던진다") + void getAnalysisByJobPostingThrowsWhenSequenceDoesNotExist() { User user = saveUser("analysis-get-sequence-missing@example.com"); MockApply mockApply = saveMockApply(user); - assertThatThrownBy(() -> analysisService.getAnalysis(user, mockApply.getId(), 2)) + assertThatThrownBy(() -> analysisService.getAnalysisByJobPostingSequence( + user, + mockApply.getJobPosting().getId(), + 2 + )) .isInstanceOf(GeneralException.class) .extracting("code") .isEqualTo(GeneralErrorCode.MOCK_APPLY_NOT_FOUND);