feat: AI 판결 스키마 분리 및 판결 로직 구현 - #70
Conversation
- AiJudgment 스키마: reasoning/advice → aFault/bFault/aSuggestedLine/bSuggestedLine 분리 - prisma generate 반영 및 seed.ts 필드 업데이트 - src/lib/ai/judgment.ts: extractDisputeMeta(Step1), generateAiJudgment(Step2) 구현 - judge route: TODO 제거 후 실제 AI 판결 로직 연결 (DB 갈등유형 조회 → AI 호출 → AiJudgment 저장) - judgment.mapper.ts / types/judgment.ts: 새 필드 반영 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- POST /api/disputes/[id]/statements/submit: 진술 최종 제출 (멱등성 보장) - GET /api/users/me: 경로 스펙 수정 (/api/user/me → /api/users/me) - StatementSubmitResponse 타입 추가 (src/types/dispute.ts) - dispute.api.ts: fetchDispute, saveStatement, submitStatement, requestJudgment - dispute.hooks.ts: useDispute, useSaveStatement, useSubmitStatement, useRequestJudgment Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- page.tsx: useEffect+raw fetch → useDispute/useRequestJudgment 훅으로 교체 - runJudge onError에 useToastStore.show 연결 - layout.tsx: <Toast /> 전역 마운트 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughPrisma 스키마에서 일부 모델 제거 및 신규 enum 7종 추가, AI 판결 DTO 필드 재구성(A/B 역할별 분리), Gemini 기반 AI 판결 생성 함수 신규 구현, 진술 제출/저장 API 개선, 분쟁 도메인 API 래퍼 및 React Query 훅 도입, 프론트엔드 훅 전환 및 글로벌 Toast 마운트가 이루어졌습니다. ChangesAI 판결 실행 및 분쟁 도메인 레이어 구현
Sequence Diagram(s)sequenceDiagram
participant Client as 클라이언트
participant DisputePage as disputes/[id]/page.tsx
participant SubmitRoute as POST /statements/submit
participant JudgeRoute as POST /judge
participant GenAI as generateAiJudgment
participant DB as Prisma DB
rect rgba(59, 130, 246, 0.5)
note over Client, DB: 진술 최종 제출 흐름
Client->>DisputePage: 진술 제출 버튼 클릭
DisputePage->>SubmitRoute: submitStatement(disputeId)
SubmitRoute->>DB: 참여자/진술/분쟁 검증
alt submittedAt 이미 존재
SubmitRoute-->>DisputePage: 200 멱등 응답
else 미제출
SubmitRoute->>DB: $transaction(submittedAt, status 갱신)
SubmitRoute-->>DisputePage: 200 StatementSubmitResponse
end
DisputePage->>DisputePage: disputeKeys.detail 캐시 invalidate
end
rect rgba(16, 185, 129, 0.5)
note over Client, DB: AI 판결 요청 흐름
Client->>DisputePage: 판결받기 버튼 클릭
DisputePage->>JudgeRoute: requestJudgment(disputeId)
JudgeRoute->>DB: conflictTypeDetail, A/B 진술 조회
alt A 진술 누락
JudgeRoute-->>DisputePage: 422 STATEMENT_NOT_FOUND
else 진술 존재
JudgeRoute->>GenAI: generateAiJudgment(JudgmentInput)
GenAI-->>JudgeRoute: JudgmentResult
JudgeRoute->>DB: $transaction(judgmentResultCard, aiJudgment, dispute JUDGED)
JudgeRoute-->>DisputePage: 200 AiJudgmentDto
DisputePage->>DisputePage: router.refresh()
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 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: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/app/api/disputes/[id]/judge/route.ts (1)
121-126:⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy liftJUDGING 잠금이 원자적이지 않아 상태가 역행할 수 있습니다.
Line 121-126은 읽기 후 갱신(TOCTOU)이라 동시 요청이 함께
JUDGING으로 진입할 수 있고, 이후 Line 199-202의 무조건 원복이 성공 요청의JUDGED상태를BOTH_SUBMITTED로 되돌릴 수 있습니다(데이터 불일치).예시 수정안
- await prisma.dispute.update({ - where: { id }, - data: { status: 'JUDGING' }, - }) + const lock = await prisma.dispute.updateMany({ + where: { + id, + deletedAt: null, + status: isSolo ? 'WAITING_OPPONENT' : 'BOTH_SUBMITTED', + }, + data: { status: 'JUDGING' }, + }) + if (lock.count === 0) { + return NextResponse.json<ApiResponse>( + { + success: false, + error: { code: 'JUDGMENT_IN_PROGRESS', message: '이미 판결이 진행 중이거나 상태가 변경되었습니다.' }, + }, + { status: 409 }, + ) + } @@ - await prisma.dispute - .update({ where: { id }, data: { status: previousStatus } }) - .catch(() => {}) + await prisma.dispute + .updateMany({ where: { id, status: 'JUDGING' }, data: { status: previousStatus } }) + .catch(() => {})As per coding guidelines,
src/app/api/**/*.{ts,tsx}: AI judgment request must implement idempotency to prevent duplicate processing.Also applies to: 199-202
🤖 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/app/api/disputes/`[id]/judge/route.ts around lines 121 - 126, The status update to JUDGING in the dispute update operation (lines 121-126) is not atomic and allows multiple concurrent requests to enter the JUDGING state, causing a race condition. Additionally, the unconditional status rollback at lines 199-202 can revert a successful JUDGED status back to BOTH_SUBMITTED. To fix this, modify the prisma.dispute.update call to include a conditional where clause that checks the current status is BOTH_SUBMITTED before transitioning to JUDGING (using atomic update with status validation), and change the rollback logic at lines 199-202 to only revert the status if the current state is still JUDGING, not unconditionally, ensuring idempotent behavior where duplicate requests do not corrupt the final judgment state.Source: Coding guidelines
🤖 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 `@prisma/schema.prisma`:
- Around line 53-59: The Account model in the Prisma schema has field names in
snake_case (refresh_token, access_token, id_token, session_state) which violates
the coding guideline requiring camelCase field names with `@map` annotations.
Convert these four field names to camelCase (refreshToken, accessToken, idToken,
sessionState) and add a `@map` annotation to each field that maps the snake_case
database column name, ensuring the database schema remains unchanged while
aligning the Prisma field naming convention.
In `@src/app/api/disputes/`[id]/judge/route.ts:
- Around line 134-143: The code validates that statementA exists but does not
validate that statementB exists, which can cause duo dispute cases to be
incorrectly processed as solo cases when passed to generateAiJudgment. After the
existing check for missing statementA, add a similar validation check for
statementB to ensure both statements are present before proceeding. When
statementB is missing, update the dispute status back to the previousStatus, set
statusSetToJudging to false, and return an error response with an appropriate
error code and message for the B-side statement not found condition.
In `@src/app/api/disputes/`[id]/statements/submit/route.ts:
- Around line 28-31: The dispute status validation is missing before allowing
statement submissions. In the section where you fetch the participant using
prisma.disputeParticipant.findFirst, you need to also validate that the dispute
exists and is in a valid state (not deleted, closed, expired, or judged). Add a
check to retrieve the dispute first and verify its status, then return an
appropriate error response (409 or 410) if the dispute is in an invalid state.
Apply this same validation logic to the other location mentioned in the comment
(lines 70-73 range) to ensure consistent protection across all endpoints.
- Around line 49-74: The idempotency check on statement.submittedAt at line 50
occurs outside the transaction, creating a race condition with concurrent
requests. Additionally, the newDisputeStatus determination at lines 62-64 uses
only participant.role to decide between WAITING_OPPONENT and BOTH_SUBMITTED, but
this should instead be based on the actual count of submitted statements in the
database. Move the idempotency logic inside the transaction and replace the
role-based status assignment with logic that counts how many dispute statements
have been submitted (submittedAt is not null) for the given disputeId, setting
the status to BOTH_SUBMITTED only when both participants have actually submitted
their statements. This ensures the state transition reflects actual submission
counts rather than role assumptions.
In `@src/domains/dispute/dispute.api.ts`:
- Around line 9-12: The current implementation directly calls res.json() without
checking the HTTP response status (res.ok) or handling JSON parsing errors,
which causes raw SyntaxErrors when the server returns non-JSON responses like
HTML error pages. Create a common parser function that first checks if res.ok is
true, wraps the res.json() call in a try-catch block to gracefully handle JSON
parsing failures, and throws a consistent error message in both cases. Update
all fetch call sites in the dispute.api.ts file (the ones at lines 9-12, 21-23,
30-32, and 37-38 mentioned in the comment) to use this common parser function
instead of directly calling res.json().
In `@src/lib/ai/judgment.ts`:
- Line 156: The solo judgment detection logic in the isSolo variable assignment
is using a falsy check on input.statementB which incorrectly treats empty
strings as solo cases. Replace the falsy check with an explicit check for
undefined or null values to properly distinguish between a missing statementB
(true solo case) and an empty string statementB (which should be treated as a
two-person case). This ensures that actual two-person judgment requests are not
misrouted to the SOLO prompt.
- Around line 203-204: The scoreA and scoreB assignments lack validation for
ensuring the scores are within the valid 0-100 range and that they sum to
exactly 100, which can compromise data integrity. After rounding the parsed
values in the scoreA and scoreB assignments, add validation checks to ensure
both scores are between 0 and 100 inclusive, and that their sum equals 100. If
validation fails, provide sensible defaults (such as setting both scores to 0)
or implement appropriate error handling to prevent invalid judgment data from
being stored.
---
Outside diff comments:
In `@src/app/api/disputes/`[id]/judge/route.ts:
- Around line 121-126: The status update to JUDGING in the dispute update
operation (lines 121-126) is not atomic and allows multiple concurrent requests
to enter the JUDGING state, causing a race condition. Additionally, the
unconditional status rollback at lines 199-202 can revert a successful JUDGED
status back to BOTH_SUBMITTED. To fix this, modify the prisma.dispute.update
call to include a conditional where clause that checks the current status is
BOTH_SUBMITTED before transitioning to JUDGING (using atomic update with status
validation), and change the rollback logic at lines 199-202 to only revert the
status if the current state is still JUDGING, not unconditionally, ensuring
idempotent behavior where duplicate requests do not corrupt the final judgment
state.
🪄 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 (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 49dca64f-23d4-4669-802e-60e8adf0b485
📒 Files selected for processing (13)
prisma/schema.prismaprisma/seed.tssrc/app/(page)/disputes/[id]/page.tsxsrc/app/api/disputes/[id]/judge/route.tssrc/app/api/disputes/[id]/statements/submit/route.tssrc/app/api/users/me/route.tssrc/app/layout.tsxsrc/domains/dispute/dispute.api.tssrc/domains/dispute/dispute.hooks.tssrc/domains/judgement/judgment.mapper.tssrc/lib/ai/judgment.tssrc/types/dispute.tssrc/types/judgment.ts
| refresh_token String? | ||
| access_token String? | ||
| expires_at Int? | ||
| token_type String? | ||
| scope String? | ||
| id_token String? | ||
| session_state String? |
There was a problem hiding this comment.
Account 모델의 Prisma 필드 네이밍이 규칙과 충돌합니다.
Line 53-59의 refresh_token, access_token, id_token, session_state는 Prisma 필드명으로 snake_case를 직접 사용하고 있어, camelCase 필드 + @map 규칙과 맞지 않습니다. DB 컬럼은 snake_case로 유지하고 Prisma 필드만 camelCase로 바꿔주세요.
예시 수정안
- refresh_token String?
- access_token String?
+ refreshToken String? `@map`("refresh_token")
+ accessToken String? `@map`("access_token")
expires_at Int?
token_type String?
scope String?
- id_token String?
- session_state String?
+ idToken String? `@map`("id_token")
+ sessionState String? `@map`("session_state")As per coding guidelines, **/*.prisma: Prisma field names must use camelCase with @map annotation for snake_case column mapping.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| refresh_token String? | |
| access_token String? | |
| expires_at Int? | |
| token_type String? | |
| scope String? | |
| id_token String? | |
| session_state String? | |
| refreshToken String? `@map`("refresh_token") | |
| accessToken String? `@map`("access_token") | |
| expires_at Int? | |
| token_type String? | |
| scope String? | |
| idToken String? `@map`("id_token") | |
| sessionState String? `@map`("session_state") |
🤖 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 `@prisma/schema.prisma` around lines 53 - 59, The Account model in the Prisma
schema has field names in snake_case (refresh_token, access_token, id_token,
session_state) which violates the coding guideline requiring camelCase field
names with `@map` annotations. Convert these four field names to camelCase
(refreshToken, accessToken, idToken, sessionState) and add a `@map` annotation to
each field that maps the snake_case database column name, ensuring the database
schema remains unchanged while aligning the Prisma field naming convention.
Source: Coding guidelines
| const statementA = dispute.statements.find((s) => s.role === 'ROLE_A' && s.submittedAt)?.content | ||
| const statementB = dispute.statements.find((s) => s.role === 'ROLE_B' && s.submittedAt)?.content | ||
|
|
||
| if (!statementA) { | ||
| await prisma.dispute.update({ where: { id }, data: { status: previousStatus } }) | ||
| statusSetToJudging = false | ||
| return NextResponse.json<ApiResponse>( | ||
| { success: false, error: { code: 'STATEMENT_NOT_FOUND', message: 'A측 진술을 찾을 수 없습니다.' } }, | ||
| { status: 422 }, | ||
| ) |
There was a problem hiding this comment.
2인 판결에서 B측 진술 누락 검증이 빠져 있습니다.
Line 134-143은 statementA만 검증하고 statementB는 검증하지 않습니다. 이 상태로 generateAiJudgment에 넘기면 duo 사건이어도 단독 분기 처리될 수 있어 결과가 왜곡됩니다.
예시 수정안
if (!statementA) {
await prisma.dispute.update({ where: { id }, data: { status: previousStatus } })
statusSetToJudging = false
return NextResponse.json<ApiResponse>(
{ success: false, error: { code: 'STATEMENT_NOT_FOUND', message: 'A측 진술을 찾을 수 없습니다.' } },
{ status: 422 },
)
}
+ if (!isSolo && !statementB) {
+ await prisma.dispute.update({ where: { id }, data: { status: previousStatus } })
+ statusSetToJudging = false
+ return NextResponse.json<ApiResponse>(
+ { success: false, error: { code: 'STATEMENT_NOT_FOUND', message: 'B측 진술을 찾을 수 없습니다.' } },
+ { status: 422 },
+ )
+ }🤖 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/app/api/disputes/`[id]/judge/route.ts around lines 134 - 143, The code
validates that statementA exists but does not validate that statementB exists,
which can cause duo dispute cases to be incorrectly processed as solo cases when
passed to generateAiJudgment. After the existing check for missing statementA,
add a similar validation check for statementB to ensure both statements are
present before proceeding. When statementB is missing, update the dispute status
back to the previousStatus, set statusSetToJudging to false, and return an error
response with an appropriate error code and message for the B-side statement not
found condition.
| // 이미 제출된 경우 멱등성 처리 | ||
| if (statement.submittedAt) { | ||
| const dispute = await prisma.dispute.findUniqueOrThrow({ where: { id: disputeId } }) | ||
| return NextResponse.json<ApiResponse<StatementSubmitResponse>>({ | ||
| success: true, | ||
| data: { | ||
| id: statement.id, | ||
| submittedAt: statement.submittedAt.toISOString(), | ||
| disputeStatus: dispute.status.toLowerCase() as DisputeStatus, | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| const newDisputeStatus = | ||
| participant.role === 'ROLE_A' ? ('WAITING_OPPONENT' as const) : ('BOTH_SUBMITTED' as const) | ||
|
|
||
| const [updatedStatement, updatedDispute] = await prisma.$transaction([ | ||
| prisma.disputeStatement.update({ | ||
| where: { id: statement.id }, | ||
| data: { submittedAt: new Date() }, | ||
| }), | ||
| prisma.dispute.update({ | ||
| where: { id: disputeId }, | ||
| data: { status: newDisputeStatus }, | ||
| }), | ||
| ]) |
There was a problem hiding this comment.
멱등성과 상태 전이를 같은 트랜잭션에서 실제 제출 수 기준으로 계산해야 합니다.
Line 50의 선조회(statement.submittedAt)는 동시 요청에서 TOCTOU가 발생하고, Line 62-64의 role 기반 상태 결정은 실제 양측 제출 여부와 불일치할 수 있습니다. 결과적으로 both_submitted가 조기 기록되어 후속 판결 흐름에서 불일치 상태를 만들 수 있습니다.
수정 제안 (interactive transaction + 조건부 업데이트)
- // 이미 제출된 경우 멱등성 처리
- if (statement.submittedAt) {
- const dispute = await prisma.dispute.findUniqueOrThrow({ where: { id: disputeId } })
- return NextResponse.json<ApiResponse<StatementSubmitResponse>>({
- success: true,
- data: {
- id: statement.id,
- submittedAt: statement.submittedAt.toISOString(),
- disputeStatus: dispute.status.toLowerCase() as DisputeStatus,
- },
- })
- }
-
- const newDisputeStatus =
- participant.role === 'ROLE_A' ? ('WAITING_OPPONENT' as const) : ('BOTH_SUBMITTED' as const)
-
- const [updatedStatement, updatedDispute] = await prisma.$transaction([
- prisma.disputeStatement.update({
- where: { id: statement.id },
- data: { submittedAt: new Date() },
- }),
- prisma.dispute.update({
- where: { id: disputeId },
- data: { status: newDisputeStatus },
- }),
- ])
+ const now = new Date()
+ const result = await prisma.$transaction(async (tx) => {
+ // 멱등성: 아직 제출되지 않은 경우에만 갱신
+ const updated = await tx.disputeStatement.updateMany({
+ where: { id: statement.id, submittedAt: null },
+ data: { submittedAt: now },
+ })
+
+ const currentStatement = await tx.disputeStatement.findUniqueOrThrow({
+ where: { id: statement.id },
+ select: { id: true, submittedAt: true },
+ })
+
+ // 중복 요청이면 현재 분쟁 상태 그대로 반환
+ if (updated.count === 0) {
+ const currentDispute = await tx.dispute.findUniqueOrThrow({
+ where: { id: disputeId },
+ select: { status: true },
+ })
+ return { statement: currentStatement, dispute: currentDispute }
+ }
+
+ // 실제 제출 수 기준으로 상태 계산
+ const submittedCount = await tx.disputeStatement.count({
+ where: { disputeId, submittedAt: { not: null } },
+ })
+ const nextStatus = submittedCount >= 2 ? 'BOTH_SUBMITTED' : 'WAITING_OPPONENT'
+
+ const currentDispute = await tx.dispute.update({
+ where: { id: disputeId },
+ data: { status: nextStatus },
+ select: { status: true },
+ })
+ return { statement: currentStatement, dispute: currentDispute }
+ })
@@
- id: updatedStatement.id,
- submittedAt: updatedStatement.submittedAt!.toISOString(),
- disputeStatus: updatedDispute.status.toLowerCase() as DisputeStatus,
+ id: result.statement.id,
+ submittedAt: result.statement.submittedAt!.toISOString(),
+ disputeStatus: result.dispute.status.toLowerCase() as DisputeStatus,
},
})As per coding guidelines, "AI judgment request, statement completion, invitation participation, and deletion/anonymization requests must implement idempotency to prevent duplicate processing."
🤖 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/app/api/disputes/`[id]/statements/submit/route.ts around lines 49 - 74,
The idempotency check on statement.submittedAt at line 50 occurs outside the
transaction, creating a race condition with concurrent requests. Additionally,
the newDisputeStatus determination at lines 62-64 uses only participant.role to
decide between WAITING_OPPONENT and BOTH_SUBMITTED, but this should instead be
based on the actual count of submitted statements in the database. Move the
idempotency logic inside the transaction and replace the role-based status
assignment with logic that counts how many dispute statements have been
submitted (submittedAt is not null) for the given disputeId, setting the status
to BOTH_SUBMITTED only when both participants have actually submitted their
statements. This ensures the state transition reflects actual submission counts
rather than role assumptions.
Source: Coding guidelines
- statements/route.ts: 수정 허용 (CONFLICT 체크 제거, 조건부 상태 전이) - 최초 저장 시에만 DRAFT→WAITING_OPPONENT / OPPONENT_JOINED→BOTH_SUBMITTED 전이 - 욕설 필터링 통과 후 extractDisputeMeta 비동기 호출 → dispute.title/description 업데이트 - StatementConflictError 제거 - statements/submit/route.ts: ROLE_B 제출 시 OPPONENT_JOINED 상태 검증 추가 - lib/ai/judgment.ts: categoryGroup 한글 매핑(ROMANCE→연애 등) AI 프롬프트 적용 - judge/route.ts: cardTitle을 dispute.title로 교체 (summary.slice 제거) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- judgment.ts: isSolo 판별에 빈 문자열 케이스 추가 (statementB.trim() === '') - dispute.api.ts: res.json() 직접 호출 제거, parseJson 헬퍼로 교체 - 비JSON/빈 본문 응답에서 SyntaxError 대신 일관된 에러 메시지 반환 - submit/route.ts: 삭제·종료·판결 완료 상태 선검증 추가 (DELETED/CLOSED/EXPIRED/JUDGED/JUDGING → 409) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/app/api/disputes/[id]/statements/route.ts (1)
199-210: 💤 Low valueP2002 catch 블록이 에러를 다시 throw만 하고 있어 실질적으로 불필요한 코드입니다.
StatementConflictError제거 후 P2002 에러에 대한 특별 처리 로직이 사라졌습니다. 현재throw e는 외부 catch로 전달되어 500 응답으로 처리되는데, 이는 catch 블록 없이도 동일한 동작입니다.♻️ 불필요한 catch 블록 제거 제안
if (isNew) { - try { - statement = await prisma.disputeStatement.create({ - data: { - disputeId, - participantId: participant.id, - userId, - role: participant.role, - content, - moderationStatus: 'pending', - submittedAt: new Date(), - }, - }) - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') { - throw e - } - throw e - } + statement = await prisma.disputeStatement.create({ + data: { + disputeId, + participantId: participant.id, + userId, + role: participant.role, + content, + moderationStatus: 'pending', + submittedAt: new Date(), + }, + }) } else {Lines 276-281의 트랜잭션 내 동일 패턴도 함께 정리 필요합니다.
🤖 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/app/api/disputes/`[id]/statements/route.ts around lines 199 - 210, The catch block in the Prisma create operation (checking for Prisma.PrismaClientKnownRequestError with code P2002) is redundant because it throws the error in both the if and else branches, providing no special error handling. Since the StatementConflictError special handling was removed, this entire catch block serves no purpose and should be deleted entirely. The same cleanup pattern should be applied to the similar catch block in the transaction section around lines 276-281.
🤖 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/app/api/disputes/`[id]/statements/route.ts:
- Around line 310-318: The extractDisputeMeta function call is executed whenever
any statement is saved, which causes ROLE_B's statements to overwrite the title
and description originally created by ROLE_A. To fix this, wrap the
extractDisputeMeta call with a conditional check to ensure it only executes when
the statement is being saved by ROLE_A (the dispute initiator). This preserves
the original dispute metadata based on the initiator's statement while allowing
ROLE_B to save statements without overwriting the dispute title and summary.
---
Nitpick comments:
In `@src/app/api/disputes/`[id]/statements/route.ts:
- Around line 199-210: The catch block in the Prisma create operation (checking
for Prisma.PrismaClientKnownRequestError with code P2002) is redundant because
it throws the error in both the if and else branches, providing no special error
handling. Since the StatementConflictError special handling was removed, this
entire catch block serves no purpose and should be deleted entirely. The same
cleanup pattern should be applied to the similar catch block in the transaction
section around lines 276-281.
🪄 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 (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 47ca6980-b2ab-4be7-a000-491649bf3dd7
📒 Files selected for processing (5)
src/app/api/disputes/[id]/judge/route.tssrc/app/api/disputes/[id]/statements/route.tssrc/app/api/disputes/[id]/statements/submit/route.tssrc/domains/dispute/dispute.api.tssrc/lib/ai/judgment.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- src/domains/dispute/dispute.api.ts
- src/app/api/disputes/[id]/judge/route.ts
- src/lib/ai/judgment.ts
Summary
AiJudgment스키마 필드 분리:reasoning/advice→aFault/bFault/aSuggestedLine/bSuggestedLine(역할별)src/lib/ai/judgment.ts신규 구현: Step1extractDisputeMeta(진술 저장 시 title/summary 추출), Step2generateAiJudgment(판결받기 시 전체 판결 JSON)POST /api/disputes/[id]/judgeTODO 제거 후 실제 AI 판결 로직 연결 (DB 갈등유형 조회 → Gemini 호출 → AiJudgment 저장)POST /api/disputes/[id]/statements/submit신규 라우트 (멱등성 보장)GET /api/users/me경로 스펙 수정 (/api/user/me→/api/users/me)dispute.api.ts/dispute.hooks.ts신규 — TanStack Query 기반 도메인 훅disputes/[id]/page.tsxraw fetch →useDispute/useRequestJudgment훅으로 교체layout.tsx에<Toast />전역 마운트, 판결 에러 시 토스트 연결Test plan
POST /api/disputes/[id]/statements/submit— 진술 미작성 시 404, 중복 요청 시 동일 응답 반환 확인POST /api/disputes/[id]/judge— 1인(WAITING_OPPONENT) / 2인(BOTH_SUBMITTED) 각각 판결 요청 후 JUDGED 상태 전환 확인GET /api/users/me경로 변경 후 기존 호출부 정상 동작 확인disputes/[id]페이지 — TanStack Query 캐시 갱신 후 상태 반영 확인🤖 Generated with Claude Code
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선 사항