Conversation
최신 첨삭 버전의 문항별 최종 작성본을 전체 검증 후 저장하고, API-019 구현 상태와 REQ-006 진행 문서를 갱신한다.
📝 WalkthroughWalkthrough최신 첨삭 버전에 대한 문항별 최종 작성본 일괄 저장 PUT API가 추가되었다. 관련 예외 코드, 서비스 검증/저장 로직, DTO, 컨트롤러, 테스트, 문서가 함께 변경되었다. Changes최종 작성본 일괄 저장 기능
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
Backend CI Report
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/main/java/com/daon/rewrite/reviewversion/entity/ReviewVersionQuestionResult.java (1)
108-115: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
countCodePoints중복 정의.동일한 코드포인트 계산 로직이
ReviewVersionCommandService.countCodePoints(184-186행)에도 존재합니다. 두 구현이 향후 divergence 되면 저장된finalAnswerLength와 검증 시 사용하는 길이 기준이 어긋날 수 있습니다. 공용 유틸리티로 추출하는 것을 권장합니다.🤖 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/entity/ReviewVersionQuestionResult.java` around lines 108 - 115, `ReviewVersionQuestionResult.countCodePoints` duplicates the same code-point length logic already present in `ReviewVersionCommandService.countCodePoints`, so the two can drift apart. Extract this into a shared utility or common helper and have both `updateFinalAnswer` and `ReviewVersionCommandService` call that single implementation to keep `finalAnswerLength` and validation length consistent.src/test/java/com/daon/rewrite/reviewversion/service/ReviewVersionCommandServiceTest.java (1)
155-177: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win첨삭 버전(reviewVersion) NOT_FOUND 케이스 테스트 누락.
현재
saveMyFinalAnswersThrowsNotFoundWhenCoverLetterIsMissingOtherOwnerOrDeleted는 자기소개서(coverLetter) 기준의 NOT_FOUND만 검증합니다. PR 인수 기준은 "존재하지 않는 자기소개서/첨삭 버전" 모두에 대해 NOT_FOUND를 요구하지만, 존재하는 자기소개서에 대해 잘못된/존재하지 않는versionId를 전달하는 케이스에 대한 테스트가 없습니다.As per path instructions,
**/*Test.java: "Add or update tests for new or changed behavior."🤖 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/test/java/com/daon/rewrite/reviewversion/service/ReviewVersionCommandServiceTest.java` around lines 155 - 177, The current `ReviewVersionCommandServiceTest.saveMyFinalAnswersThrowsNotFoundWhenCoverLetterIsMissingOtherOwnerOrDeleted` only covers missing/unauthorized/deleted cover letters, but it does not verify the NOT_FOUND behavior for a valid cover letter paired with an invalid or nonexistent review version. Add a test in `ReviewVersionCommandServiceTest` that saves an accessible cover letter, then calls `service.saveMyFinalAnswers` with a bad `versionId` and asserts `BusinessException` with `ErrorCode.NOT_FOUND`, using the existing `assertNotFound` pattern or a dedicated helper if needed.Source: Path instructions
src/main/java/com/daon/rewrite/reviewversion/dto/SaveFinalAnswersResponse.java (1)
16-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win시간대 변환 로직 중복 가능성.
API_ZONE을 이용한 Instant→LocalDateTime 변환이 이미 다른 응답 DTO(예: 첨삭 버전 상세 응답의createdAt변환)에서도 동일하게 사용되는 것으로 보입니다. 새 DTO에서 동일한 상수/로직을 다시 정의하면 향후 시간대 정책이 바뀔 때 일부 DTO만 갱신되는 불일치 위험이 있습니다. 공통 유틸리티(예:TimeZoneUtils또는 매핑 헬퍼)로 추출하는 것을 권장합니다.♻️ 제안 예시
-public record SaveFinalAnswersResponse( - String coverLetterId, - String reviewVersionId, - List<QuestionResultResponse> questionResults, - LocalDateTime updatedAt -) { - private static final ZoneId API_ZONE = ZoneId.of("Asia/Seoul"); - - public static SaveFinalAnswersResponse from(SaveFinalAnswersResult result) { +public record SaveFinalAnswersResponse( + String coverLetterId, + String reviewVersionId, + List<QuestionResultResponse> questionResults, + LocalDateTime updatedAt +) { + public static SaveFinalAnswersResponse from(SaveFinalAnswersResult result) { return new SaveFinalAnswersResponse( result.coverLetterId(), result.reviewVersionId(), result.questionResults().stream() .map(QuestionResultResponse::from) .toList(), - LocalDateTime.ofInstant(result.updatedAt(), API_ZONE) + ApiTimeConverter.toLocalDateTime(result.updatedAt()) ); }🤖 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/SaveFinalAnswersResponse.java` around lines 16 - 27, `SaveFinalAnswersResponse.from` is duplicating the Instant-to-LocalDateTime conversion policy with its own `API_ZONE` constant, which can drift from other DTO mappings. Extract this conversion into the shared time-mapping utility or existing helper used by other response DTOs (for example the same place that handles `createdAt`), and have `from(SaveFinalAnswersResult)` delegate to that shared method instead of defining its own zone/logic.
🤖 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/daon/rewrite/reviewversion/service/ReviewVersionCommandService.java`:
- Around line 89-152: The duplicate `questionResultId` check in
`ReviewVersionCommandService` is tied to `normalizedAnswers`, so a repeated ID
can be missed when the first entry has a null/blank `finalAnswer`. Update the
validation flow to track already-seen IDs separately in
`validateQuestionResultId` (or the surrounding validation method) using a
dedicated Set, and use that Set for duplicate detection regardless of answer
validity. Keep the existing `normalizedAnswers` map only for collecting valid
answers, and ensure the duplicate error is added for the second occurrence even
if the first one failed answer validation.
---
Nitpick comments:
In
`@src/main/java/com/daon/rewrite/reviewversion/dto/SaveFinalAnswersResponse.java`:
- Around line 16-27: `SaveFinalAnswersResponse.from` is duplicating the
Instant-to-LocalDateTime conversion policy with its own `API_ZONE` constant,
which can drift from other DTO mappings. Extract this conversion into the shared
time-mapping utility or existing helper used by other response DTOs (for example
the same place that handles `createdAt`), and have
`from(SaveFinalAnswersResult)` delegate to that shared method instead of
defining its own zone/logic.
In
`@src/main/java/com/daon/rewrite/reviewversion/entity/ReviewVersionQuestionResult.java`:
- Around line 108-115: `ReviewVersionQuestionResult.countCodePoints` duplicates
the same code-point length logic already present in
`ReviewVersionCommandService.countCodePoints`, so the two can drift apart.
Extract this into a shared utility or common helper and have both
`updateFinalAnswer` and `ReviewVersionCommandService` call that single
implementation to keep `finalAnswerLength` and validation length consistent.
In
`@src/test/java/com/daon/rewrite/reviewversion/service/ReviewVersionCommandServiceTest.java`:
- Around line 155-177: The current
`ReviewVersionCommandServiceTest.saveMyFinalAnswersThrowsNotFoundWhenCoverLetterIsMissingOtherOwnerOrDeleted`
only covers missing/unauthorized/deleted cover letters, but it does not verify
the NOT_FOUND behavior for a valid cover letter paired with an invalid or
nonexistent review version. Add a test in `ReviewVersionCommandServiceTest` that
saves an accessible cover letter, then calls `service.saveMyFinalAnswers` with a
bad `versionId` and asserts `BusinessException` with `ErrorCode.NOT_FOUND`,
using the existing `assertNotFound` pattern or a dedicated helper if needed.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: accf64c8-3e0a-4e18-8dfc-4fb2b25cf500
📒 Files selected for processing (12)
docs/api/README.mddocs/status.mdsrc/main/java/com/daon/rewrite/global/exception/ErrorCode.javasrc/main/java/com/daon/rewrite/reviewversion/controller/ReviewVersionController.javasrc/main/java/com/daon/rewrite/reviewversion/dto/SaveFinalAnswersRequest.javasrc/main/java/com/daon/rewrite/reviewversion/dto/SaveFinalAnswersResponse.javasrc/main/java/com/daon/rewrite/reviewversion/entity/ReviewVersionQuestionResult.javasrc/main/java/com/daon/rewrite/reviewversion/service/ReviewVersionCommandService.javasrc/main/java/com/daon/rewrite/reviewversion/service/SaveFinalAnswerInput.javasrc/main/java/com/daon/rewrite/reviewversion/service/SaveFinalAnswersResult.javasrc/test/java/com/daon/rewrite/reviewversion/controller/ReviewVersionControllerTest.javasrc/test/java/com/daon/rewrite/reviewversion/service/ReviewVersionCommandServiceTest.java
중복 questionResultId 감지를 답변 유효성과 분리하고, 첨삭 버전 NOT_FOUND 및 finalAnswer 갱신 저장 테스트를 보강한다.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/test/java/com/daon/rewrite/reviewversion/repository/ReviewVersionRepositoryTest.java (1)
129-153: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value픽스처 생성 코드 중복.
saveFirstReviewVersionRoundTripsQuestionSnapshots(41-63행)와 동일한 CoverLetter/CoverLetterQuestion/ReviewVersion 생성 로직이 반복되고 있습니다. 공통 private 헬퍼 메서드로 추출하면 테스트 가독성과 유지보수성이 개선됩니다. 다만 필수는 아니며 우선순위는 낮습니다.🤖 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/test/java/com/daon/rewrite/reviewversion/repository/ReviewVersionRepositoryTest.java` around lines 129 - 153, The test setup in ReviewVersionRepositoryTest repeats the same CoverLetter, CoverLetterQuestion, and ReviewVersion fixture creation that already exists in saveFirstReviewVersionRoundTripsQuestionSnapshots. Extract the shared setup into a private helper method (or a small fixture builder) and reuse it from updateFinalAnswerRoundTripsUpdatedAnswerAndLength and the existing test, keeping the assertions unchanged. Use the existing create/save calls on CoverLetter.draft, CoverLetterQuestion.create, and ReviewVersion.first so the duplicated setup is centralized and easier to maintain.
🤖 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
`@src/test/java/com/daon/rewrite/reviewversion/repository/ReviewVersionRepositoryTest.java`:
- Around line 129-153: The test setup in ReviewVersionRepositoryTest repeats the
same CoverLetter, CoverLetterQuestion, and ReviewVersion fixture creation that
already exists in saveFirstReviewVersionRoundTripsQuestionSnapshots. Extract the
shared setup into a private helper method (or a small fixture builder) and reuse
it from updateFinalAnswerRoundTripsUpdatedAnswerAndLength and the existing test,
keeping the assertions unchanged. Use the existing create/save calls on
CoverLetter.draft, CoverLetterQuestion.create, and ReviewVersion.first so the
duplicated setup is centralized and easier to maintain.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 0b535531-b880-47e2-a32f-fe92efa0b68e
📒 Files selected for processing (3)
src/main/java/com/daon/rewrite/reviewversion/service/ReviewVersionCommandService.javasrc/test/java/com/daon/rewrite/reviewversion/repository/ReviewVersionRepositoryTest.javasrc/test/java/com/daon/rewrite/reviewversion/service/ReviewVersionCommandServiceTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
- src/test/java/com/daon/rewrite/reviewversion/service/ReviewVersionCommandServiceTest.java
- src/main/java/com/daon/rewrite/reviewversion/service/ReviewVersionCommandService.java
작업 내용
REVIEW_VERSION_NOT_LATEST로 응답합니다.finalAnswer를 전체 payload로 검증한 뒤 한 번에 저장합니다.finalAnswertrim, blank, 5000자 초과, 누락/중복/알 수 없는questionResultIdvalidation을 처리합니다.finalAnswerLength를 Unicode code point 기준으로 함께 갱신합니다.관련 이슈
문서 반영
docs/api/README.mddocs/status.md확인 결과
./gradlew test./gradlew checkgit diff --checkSummary by CodeRabbit