Skip to content

[Feat] 모의 서류 재도전 API 구현#85

Merged
whc9999 merged 1 commit into
mainfrom
feat/#38-mock-apply-retry
May 26, 2026
Merged

[Feat] 모의 서류 재도전 API 구현#85
whc9999 merged 1 commit into
mainfrom
feat/#38-mock-apply-retry

Conversation

@whc9999
Copy link
Copy Markdown
Collaborator

@whc9999 whc9999 commented May 26, 2026

✨ 어떤 이유로 PR를 하셨나요?

  • feature 병합
  • 버그 수정(아래에 issue #를 남겨주세요)
  • 코드 개선
  • 코드 수정
  • 배포
  • 기타(아래에 자세한 내용 기입해주세요)

📋 세부 내용 - 왜 해당 PR이 필요한지 작업 내용을 자세하게 설명해주세요

  • 기존 모의 서류 지원을 기준으로 새 회차 지원을 생성하는 재도전 API 추가
  • 재도전 시 기존 공고 정보를 새 JobPosting으로 복제하도록 구현
  • 기존 선택 문항을 새 지원에 복사하고 답변은 빈 값으로 초기화
  • 문항이 복사된 재도전 지원은 자소서 작성 단계 상태로 설정
  • 홈 목록 응답에 sequence 필드를 추가해 회차 정보를 표시할 수 있도록 수정
  • 재도전 생성 및 홈 목록 sequence 응답 테스트 추가

📸 작업 화면 스크린샷

⚠️ PR하기 전에 확인해주세요

  • 로컬테스트를 진행하셨나요?
  • 머지할 브랜치를 확인하셨나요?
  • 관련 label을 선택하셨나요?

🚨 관련 이슈 번호 [#38 ]

Summary by CodeRabbit

  • New Features

    • Added the ability to retry mock applications, enabling users to start a new attempt based on a previous application while preserving related questions.
    • Mock applications now include sequence tracking to better organize application instances.
  • Tests

    • Added test coverage for retry functionality and sequence tracking.

Review Change Stack

- 기존 모의 서류 지원을 기준으로 새 회차 지원을 생성하는 재도전 API 추가
- 재도전 시 기존 공고 정보를 새 JobPosting으로 복제하도록 구현
- 기존 선택 문항을 새 지원에 복사하고 답변은 빈 값으로 초기화
- 문항이 복사된 재도전 지원은 자소서 작성 단계 상태로 설정
- 홈 목록 응답에 sequence 필드를 추가해 회차 정보를 표시할 수 있도록 수정
- 재도전 생성 및 홈 목록 sequence 응답 테스트 추가
@whc9999 whc9999 self-assigned this May 26, 2026
@whc9999 whc9999 added the ✨ feat New feature or request label May 26, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

This 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.

Changes

Mock Apply Retry Feature

Layer / File(s) Summary
Response contracts and sequence field
src/main/java/com/jobdri/jobdri_api/domain/mockapply/dto/response/MockApplyRetryResponse.java, src/main/java/com/jobdri/jobdri_api/domain/mockapply/dto/response/MockApplyHomeItemResponse.java
MockApplyRetryResponse record exposes source/target apply IDs and state; MockApplyHomeItemResponse adds sequence field with null-safe default of 1.
Query optimization and question persistence
src/main/java/com/jobdri/jobdri_api/domain/mockapply/repository/MockApplyRepository.java, src/main/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyPersistenceService.java
Repository method findByIdWithJobPosting eager-loads associated entities (user, job posting, company, classification); MockApplyPersistenceService.saveQuestions persists questions with REQUIRES_NEW transactional scope.
Core retry service logic
src/main/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyService.java
retryMockApply validates ownership, clones job posting, creates new mock apply with same apply type, duplicates and resets questions, transitions status to ANSWER_WRITE, and returns retry response; helper getOwnedMockApplyWithJobPosting consolidates safe loading and ownership checks.
REST endpoint for retry
src/main/java/com/jobdri/jobdri_api/domain/mockapply/controller/MockApplyController.java
POST /{mockApplyId}/retry endpoint with OpenAPI metadata extracts authenticated user and delegates to service, returning ApiResponse<MockApplyRetryResponse>.
Test coverage for retry feature
src/test/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyServiceTest.java
New retryMockApply test verifies cloned job posting, incremented sequence, ANSWER_WRITE status, and question duplication with reset answers; home-list assertions extended to validate sequence field; saveQuestion helper supports transactional test setup.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • JobDri-Developer/BackEnd#69: Introduces MockApplyHomeItemResponse DTO that is now extended with the sequence field in this PR.
  • JobDri-Developer/BackEnd#82: Adds sequence-aware persistence and entity/creation logic for MockApply that this PR's retry flow depends on.
  • JobDri-Developer/BackEnd#76: Introduces audit-log infrastructure that this PR's retryMockApply method now uses with MOCK_APPLY_RETRY audit event.

Suggested reviewers

  • shinae1023

🐰 A user retries, the questions bloom anew,
Clone the job, reset answers—a fresh path to pursue.
Sequences increment, statuses dance with care,
Another chance at practice, with logic everywhere! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title '[Feat] 모의 서류 재도전 API 구현' clearly describes the main change: implementing a retry API for mock applications, which matches the primary objective of the changeset.
Description check ✅ Passed The description covers the required template sections with detailed implementation details, confirms pre-merge checks, and links to related issue #38, meeting the repository's documentation standards.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#38-mock-apply-retry

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between b470b0c and febda50.

📒 Files selected for processing (7)
  • src/main/java/com/jobdri/jobdri_api/domain/mockapply/controller/MockApplyController.java
  • src/main/java/com/jobdri/jobdri_api/domain/mockapply/dto/response/MockApplyHomeItemResponse.java
  • src/main/java/com/jobdri/jobdri_api/domain/mockapply/dto/response/MockApplyRetryResponse.java
  • src/main/java/com/jobdri/jobdri_api/domain/mockapply/repository/MockApplyRepository.java
  • src/main/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyPersistenceService.java
  • src/main/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyService.java
  • src/test/java/com/jobdri/jobdri_api/domain/mockapply/service/MockApplyServiceTest.java

Comment on lines +81 to +121
@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);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

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.

@whc9999 whc9999 merged commit ccc0d74 into main May 26, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feat New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant