[Feat] 모의 서류 재도전 API 구현#85
Conversation
- 기존 모의 서류 지원을 기준으로 새 회차 지원을 생성하는 재도전 API 추가 - 재도전 시 기존 공고 정보를 새 JobPosting으로 복제하도록 구현 - 기존 선택 문항을 새 지원에 복사하고 답변은 빈 값으로 초기화 - 문항이 복사된 재도전 지원은 자소서 작성 단계 상태로 설정 - 홈 목록 응답에 sequence 필드를 추가해 회차 정보를 표시할 수 있도록 수정 - 재도전 생성 및 홈 목록 sequence 응답 테스트 추가
📝 WalkthroughWalkthroughThis PR introduces a retry feature for mock job applications. Users can retry a submitted mock apply; the system clones the original job posting, duplicates associated questions with reset answers, creates a new mock apply with incremented sequence, and returns comprehensive retry metadata via a new REST endpoint. ChangesMock Apply Retry Feature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/main/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyService.java`:
- Around line 81-121: The retry flow in retryMockApply is annotated with
Propagation.NOT_SUPPORTED while inner saves (jobPostingRepository.save,
saveMockApplyWithSequence, mockApplyPersistenceService.saveQuestions,
mockApplyPersistenceService.saveAndFlush) commit separately, allowing partial
persistence on failure; make the whole retry creation atomic by removing
NOT_SUPPORTED and annotating retryMockApply with `@Transactional` (default
REQUIRED) so all saves participate in one transaction, and ensure the
MockApplyPersistenceService methods used here do not force REQUIRES_NEW (adjust
their propagation to join the caller) or alternatively implement a compensating
rollback cleanup on failure; target symbols: retryMockApply,
getOwnedMockApplyWithJobPosting, jobPostingRepository.save,
saveMockApplyWithSequence, questionRepository.findAllByMockApplyIdOrderByIdAsc,
mockApplyPersistenceService.saveQuestions,
mockApplyPersistenceService.saveAndFlush, MockApplyStatus.ANSWER_WRITE.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: b65357d0-a4aa-40a9-9e59-27aae95db764
📒 Files selected for processing (7)
src/main/java/com/jobdri/jobdri_api/domain/mockapply/controller/MockApplyController.javasrc/main/java/com/jobdri/jobdri_api/domain/mockapply/dto/response/MockApplyHomeItemResponse.javasrc/main/java/com/jobdri/jobdri_api/domain/mockapply/dto/response/MockApplyRetryResponse.javasrc/main/java/com/jobdri/jobdri_api/domain/mockapply/repository/MockApplyRepository.javasrc/main/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyPersistenceService.javasrc/main/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyService.javasrc/test/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyServiceTest.java
| @Transactional(propagation = Propagation.NOT_SUPPORTED) | ||
| @AuditLogEvent(action = "MOCK_APPLY_RETRY", targetType = "MOCK_APPLY", targetId = "#result.mockApplyId()") | ||
| public MockApplyRetryResponse retryMockApply(User user, Long mockApplyId) { | ||
| User validatedUser = userService.validateUser(user); | ||
| MockApply sourceMockApply = getOwnedMockApplyWithJobPosting(validatedUser, mockApplyId); | ||
| JobPosting sourceJobPosting = sourceMockApply.getJobPosting(); | ||
|
|
||
| JobPosting clonedJobPosting = jobPostingRepository.save(JobPosting.create( | ||
| validatedUser, | ||
| sourceJobPosting.getCompany(), | ||
| sourceJobPosting.getDetailClassification(), | ||
| sourceJobPosting.getTask(), | ||
| sourceJobPosting.getRequirement(), | ||
| sourceJobPosting.getPreferred() | ||
| )); | ||
|
|
||
| MockApply retryMockApply = saveMockApplyWithSequence( | ||
| validatedUser, | ||
| clonedJobPosting, | ||
| sourceMockApply.getApplyType(), | ||
| null | ||
| ); | ||
|
|
||
| List<Question> sourceQuestions = questionRepository.findAllByMockApplyIdOrderByIdAsc(sourceMockApply.getId()); | ||
| if (!sourceQuestions.isEmpty()) { | ||
| MockApply targetMockApply = retryMockApply; | ||
| List<Question> retryQuestions = sourceQuestions.stream() | ||
| .map(question -> Question.create( | ||
| targetMockApply, | ||
| question.getContent(), | ||
| question.getLimit(), | ||
| "" | ||
| )) | ||
| .toList(); | ||
| mockApplyPersistenceService.saveQuestions(retryQuestions); | ||
| retryMockApply.updateStatus(MockApplyStatus.ANSWER_WRITE); | ||
| retryMockApply = mockApplyPersistenceService.saveAndFlush(retryMockApply); | ||
| } | ||
|
|
||
| return MockApplyRetryResponse.of(sourceMockApply.getId(), retryMockApply); | ||
| } |
There was a problem hiding this comment.
Make retry creation atomic to prevent partial persisted state.
retryMockApply runs with NOT_SUPPORTED, while inner saves commit in separate transactions (jobPostingRepository.save, saveAndFlush, saveQuestions). If question copy or status update fails, previously committed entities remain, leaving inconsistent retry data.
Suggested direction
- `@Transactional`(propagation = Propagation.NOT_SUPPORTED)
+ `@Transactional`
`@AuditLogEvent`(action = "MOCK_APPLY_RETRY", targetType = "MOCK_APPLY", targetId = "`#result.mockApplyId`()")
public MockApplyRetryResponse retryMockApply(User user, Long mockApplyId) {
...
- mockApplyPersistenceService.saveQuestions(retryQuestions);
+ mockApplyPersistenceService.saveQuestions(retryQuestions); // align this path to join outer tx
retryMockApply.updateStatus(MockApplyStatus.ANSWER_WRITE);
- retryMockApply = mockApplyPersistenceService.saveAndFlush(retryMockApply);
+ retryMockApply = mockApplyPersistenceService.saveAndFlush(retryMockApply);
...
}Also align MockApplyPersistenceService methods used in this flow to join the caller transaction (remove REQUIRES_NEW for this path), or add explicit compensation cleanup on failure.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/main/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyService.java`
around lines 81 - 121, The retry flow in retryMockApply is annotated with
Propagation.NOT_SUPPORTED while inner saves (jobPostingRepository.save,
saveMockApplyWithSequence, mockApplyPersistenceService.saveQuestions,
mockApplyPersistenceService.saveAndFlush) commit separately, allowing partial
persistence on failure; make the whole retry creation atomic by removing
NOT_SUPPORTED and annotating retryMockApply with `@Transactional` (default
REQUIRED) so all saves participate in one transaction, and ensure the
MockApplyPersistenceService methods used here do not force REQUIRES_NEW (adjust
their propagation to join the caller) or alternatively implement a compensating
rollback cleanup on failure; target symbols: retryMockApply,
getOwnedMockApplyWithJobPosting, jobPostingRepository.save,
saveMockApplyWithSequence, questionRepository.findAllByMockApplyIdOrderByIdAsc,
mockApplyPersistenceService.saveQuestions,
mockApplyPersistenceService.saveAndFlush, MockApplyStatus.ANSWER_WRITE.
✨ 어떤 이유로 PR를 하셨나요?
📋 세부 내용 - 왜 해당 PR이 필요한지 작업 내용을 자세하게 설명해주세요
📸 작업 화면 스크린샷
🚨 관련 이슈 번호 [#38 ]
Summary by CodeRabbit
New Features
Tests