Conversation
📝 WalkthroughWalkthrough자기소개서 최신 첨삭 버전의 최종 작성본을 기준으로 AI 첨삭을 다시 요청하는 재첨삭(Re-Review) 기능이 추가되었다. Changes재첨삭 기능 구현
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
Backend CI Report
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
🧹 Nitpick comments (5)
docs/api/review-versions.md (1)
87-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winValidation 목록에
latestReviewVersionId존재 조건 누락
ReviewVersionCommandService.requestMyReReview는status != REVIEWED || latestReviewVersionId == null조건에서 모두CONFLICT를 던지지만, 문서의 Validation 목록에는 REVIEWED 상태 조건만 기재되어 있습니다. PR 목표에도 두 조건이 함께 명시되어 있으므로 문서에도 반영하는 것이 좋습니다.📝 제안
requestInstruction: 선택, 최대 1000자 같은 자기소개서에 PENDING 또는 PROCESSING 상태의 LLM Job이 없어야 한다. 자기소개서 상태는 REVIEWED여야 한다. +최신 첨삭 버전(latestReviewVersionId)이 존재해야 한다.🤖 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 `@docs/api/review-versions.md` around lines 87 - 93, Validation 목록에 `latestReviewVersionId` 존재 조건이 빠져 있어 `ReviewVersionCommandService.requestMyReReview`의 실제 `CONFLICT` 조건과 문서가 불일치합니다. `requestMyReReview`의 검증 기준을 정리하는 부분에서 `status == REVIEWED` 조건만 두지 말고 `latestReviewVersionId != null` 조건도 함께 명시하도록 `review-versions.md`의 Validation 항목을 수정하세요.src/main/java/com/daon/rewrite/reviewversion/dto/RequestReReviewResponse.java (1)
14-21: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
coverLetterStatus를 하드코딩하지 말고 실제 값을 사용하세요.현재는
requestMyReReview진입 조건상 상태가 항상REVIEWED이므로 결과적으로 맞지만, 실제 커버레터 엔티티 값을 조회하지 않고 상수를 응답에 박아두는 것은 향후 상태 전이 로직이 바뀔 경우(예: 재첨삭 진행 중 별도 상태 추가) 응답이 실제 상태와 어긋날 위험이 있습니다.♻️ 제안 방향
- public static RequestReReviewResponse from(RequestReReviewResult result) { - return new RequestReReviewResponse( - result.job().getId(), - result.coverLetterId(), - CoverLetterStatus.REVIEWED, - result.job().getStatus() - ); - } + public static RequestReReviewResponse from(RequestReReviewResult result) { + return new RequestReReviewResponse( + result.job().getId(), + result.coverLetterId(), + result.coverLetterStatus(), + result.job().getStatus() + ); + }
RequestReReviewResult에 실제CoverLetter.getStatus()값을 함께 담아 전달하도록 수정이 필요합니다.🤖 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/daon/rewrite/reviewversion/dto/RequestReReviewResponse.java` around lines 14 - 21, `RequestReReviewResponse.from(RequestReReviewResult)` is hardcoding `coverLetterStatus` to REVIEWED instead of using the actual entity state. Update `RequestReReviewResult` to carry the real `CoverLetter.getStatus()` value and have `from(...)` read that field when building `RequestReReviewResponse`, so the response stays aligned with future status changes.src/main/java/com/daon/rewrite/reviewversion/service/ReReviewJobWorker.java (1)
11-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
FirstReviewJobWorker와 거의 동일한 구조의 중복 코드입니다.
transactionService.start→ 외부 클라이언트 호출 → 성공 시complete*, 실패 시fail처리하는 흐름이 (그래프 컨텍스트상)FirstReviewJobWorker.execute와 동일합니다.completeReReview/completeFirstReview호출부만 파라미터화하면 공통 템플릿으로 추출할 수 있습니다. PR에서 의도적으로 워커를 분리했다는 점을 고려하면 우선순위는 낮습니다.🤖 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/daon/rewrite/reviewversion/service/ReReviewJobWorker.java` around lines 11 - 41, `ReReviewJobWorker.execute` duplicates the same start/call/success/fail workflow already present in `FirstReviewJobWorker.execute`; extract that shared template into a common helper or base flow and parameterize only the completion step (`completeReReview` vs `completeFirstReview`). Keep `transactionService.start`, the external client call (`firstReviewClient.review`), and `transactionService.fail` handling in the shared path, with only the result-mapping and `reviewVersionService.completeReReview` remaining specific.src/main/java/com/daon/rewrite/reviewversion/service/FirstReviewJobEventListener.java (1)
26-33: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win처리되지 않은 job 타입에 대한 로깅 추가를 고려하세요.
default분기가 아무 동작 없이 무시되어, 향후 새LlmJobType이 추가되고 분기 처리가 누락되면 해당 job은 아무 로그 없이PENDING상태로 영구히 방치될 수 있습니다.♻️ 제안 diff
private void dispatch(LlmJob job) { switch (job.getType()) { case COVER_LETTER_REVIEW -> firstReviewJobWorker.execute(job.getId()); case COVER_LETTER_RE_REVIEW -> reReviewJobWorker.execute(job.getId()); - default -> { - } + default -> log.warn("처리할 수 없는 LlmJobType입니다. jobId={}, type={}", job.getId(), job.getType()); } }🤖 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/daon/rewrite/reviewversion/service/FirstReviewJobEventListener.java` around lines 26 - 33, Unhandled job types in FirstReviewJobEventListener.dispatch are currently ignored, which can leave jobs stuck without any visibility. Update the switch over job.getType() so the default branch logs the unexpected LlmJobType and job id, using the dispatch method and the existing worker calls (firstReviewJobWorker.execute and reReviewJobWorker.execute) as the surrounding context. Keep the behavior non-failing if that is intended, but make sure the log clearly identifies the unsupported type so future missing LlmJobType cases are easy to detect.src/main/java/com/daon/rewrite/reviewversion/service/ReviewVersionService.java (1)
252-255: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winCount 기반 버전 넘버링은 데이터 삭제 시 충돌 위험이 있습니다.
countByCoverLetterId + 1방식은 현재는 row-lock으로 동시성 문제는 방지되지만, 추후ReviewVersion행이 물리적으로 삭제되는 경로가 생기면(예: soft-delete cascade, 데이터 정리 배치 등) 카운트가 줄어들어 이미 사용된 버전 번호가 재사용될 수 있습니다. 기존 버전들 중 최대 patch 번호를 조회해+1하는 방식이 더 안전합니다.♻️ 제안: MAX 기반 버전 산정
- private String nextVersion(String coverLetterId) { - long nextPatch = reviewVersionRepository.countByCoverLetterId(coverLetterId) + 1; - return "v0." + nextPatch; - } + private String nextVersion(String coverLetterId) { + long nextPatch = reviewVersionRepository.findMaxPatchByCoverLetterId(coverLetterId) + .orElse(0L) + 1; + return "v0." + nextPatch; + }🤖 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/daon/rewrite/reviewversion/service/ReviewVersionService.java` around lines 252 - 255, The current nextVersion method in ReviewVersionService derives the patch number from countByCoverLetterId, which can reuse an old version if ReviewVersion rows are ever removed. Change the version calculation to use the maximum existing patch number for the given coverLetterId and increment that value instead, so nextVersion always advances from the highest used version rather than the row count.
🤖 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.
Nitpick comments:
In `@docs/api/review-versions.md`:
- Around line 87-93: Validation 목록에 `latestReviewVersionId` 존재 조건이 빠져 있어
`ReviewVersionCommandService.requestMyReReview`의 실제 `CONFLICT` 조건과 문서가 불일치합니다.
`requestMyReReview`의 검증 기준을 정리하는 부분에서 `status == REVIEWED` 조건만 두지 말고
`latestReviewVersionId != null` 조건도 함께 명시하도록 `review-versions.md`의 Validation
항목을 수정하세요.
In
`@src/main/java/com/daon/rewrite/reviewversion/dto/RequestReReviewResponse.java`:
- Around line 14-21: `RequestReReviewResponse.from(RequestReReviewResult)` is
hardcoding `coverLetterStatus` to REVIEWED instead of using the actual entity
state. Update `RequestReReviewResult` to carry the real
`CoverLetter.getStatus()` value and have `from(...)` read that field when
building `RequestReReviewResponse`, so the response stays aligned with future
status changes.
In
`@src/main/java/com/daon/rewrite/reviewversion/service/FirstReviewJobEventListener.java`:
- Around line 26-33: Unhandled job types in FirstReviewJobEventListener.dispatch
are currently ignored, which can leave jobs stuck without any visibility. Update
the switch over job.getType() so the default branch logs the unexpected
LlmJobType and job id, using the dispatch method and the existing worker calls
(firstReviewJobWorker.execute and reReviewJobWorker.execute) as the surrounding
context. Keep the behavior non-failing if that is intended, but make sure the
log clearly identifies the unsupported type so future missing LlmJobType cases
are easy to detect.
In `@src/main/java/com/daon/rewrite/reviewversion/service/ReReviewJobWorker.java`:
- Around line 11-41: `ReReviewJobWorker.execute` duplicates the same
start/call/success/fail workflow already present in
`FirstReviewJobWorker.execute`; extract that shared template into a common
helper or base flow and parameterize only the completion step
(`completeReReview` vs `completeFirstReview`). Keep `transactionService.start`,
the external client call (`firstReviewClient.review`), and
`transactionService.fail` handling in the shared path, with only the
result-mapping and `reviewVersionService.completeReReview` remaining specific.
In
`@src/main/java/com/daon/rewrite/reviewversion/service/ReviewVersionService.java`:
- Around line 252-255: The current nextVersion method in ReviewVersionService
derives the patch number from countByCoverLetterId, which can reuse an old
version if ReviewVersion rows are ever removed. Change the version calculation
to use the maximum existing patch number for the given coverLetterId and
increment that value instead, so nextVersion always advances from the highest
used version rather than the row count.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b2efa216-7f71-4b72-b26f-b36f66c9167d
📒 Files selected for processing (27)
docs/api/README.mddocs/api/llm-jobs.mddocs/api/review-versions.mddocs/erd.mddocs/status.mdsrc/main/java/com/daon/rewrite/llmjob/entity/LlmJob.javasrc/main/java/com/daon/rewrite/reviewversion/client/FirstReviewRequest.javasrc/main/java/com/daon/rewrite/reviewversion/client/OpenAiFirstReviewClient.javasrc/main/java/com/daon/rewrite/reviewversion/controller/ReviewVersionController.javasrc/main/java/com/daon/rewrite/reviewversion/dto/RequestReReviewRequest.javasrc/main/java/com/daon/rewrite/reviewversion/dto/RequestReReviewResponse.javasrc/main/java/com/daon/rewrite/reviewversion/entity/ReviewVersion.javasrc/main/java/com/daon/rewrite/reviewversion/entity/ReviewVersionQuestionResult.javasrc/main/java/com/daon/rewrite/reviewversion/repository/ReviewVersionRepository.javasrc/main/java/com/daon/rewrite/reviewversion/service/FirstReviewJobEventListener.javasrc/main/java/com/daon/rewrite/reviewversion/service/ReReviewJobTransactionService.javasrc/main/java/com/daon/rewrite/reviewversion/service/ReReviewJobWorker.javasrc/main/java/com/daon/rewrite/reviewversion/service/RequestReReviewResult.javasrc/main/java/com/daon/rewrite/reviewversion/service/ReviewVersionCommandService.javasrc/main/java/com/daon/rewrite/reviewversion/service/ReviewVersionService.javasrc/test/java/com/daon/rewrite/llmjob/repository/LlmJobRepositoryTest.javasrc/test/java/com/daon/rewrite/reviewversion/client/OpenAiFirstReviewClientTest.javasrc/test/java/com/daon/rewrite/reviewversion/controller/ReviewVersionControllerTest.javasrc/test/java/com/daon/rewrite/reviewversion/service/FirstReviewJobEventIntegrationTest.javasrc/test/java/com/daon/rewrite/reviewversion/service/ReReviewJobWorkerTest.javasrc/test/java/com/daon/rewrite/reviewversion/service/ReviewVersionCommandServiceTest.javasrc/test/java/com/daon/rewrite/reviewversion/service/ReviewVersionServiceTest.java
작업 내용
POST /cover-letters/{coverLetterId}/review-versionsAI 첨삭 다시받기 API를 추가했습니다.REVIEWED상태와 최신 첨삭 버전 존재 여부를 검증합니다.PENDING또는PROCESSINGLLM Job이 있으면LLM_JOB_ALREADY_RUNNING으로 막습니다.llm_jobs.request_instruction을 추가해 재첨삭 요구사항을 Job에 저장하고, 완료된 새ReviewVersion.requestInstruction으로 이어지게 했습니다.ReviewVersion의 문항별finalAnswer를 새 첨삭 입력 원본으로 사용합니다.v0.2,v0.3처럼 새ReviewVersion을 생성하고CoverLetter.latestReviewVersionId를 갱신합니다.CoverLetter.status=REVIEWED는 유지하고 Job만FAILED로 저장합니다.관련 이슈
문서 반영
docs/api/README.mddocs/api/review-versions.mddocs/api/llm-jobs.mddocs/erd.mddocs/status.md확인 결과
./gradlew test./gradlew checkgit diff --checkSummary by CodeRabbit
New Features
Bug Fixes
Documentation