PUT /ai/influence/contents/review/{reviewId}/submit
검수 라운드 일괄 제출
검수 라운드 일괄 제출
검수 라운드의 모든 제출물 내용을 저장하고 일괄 제출합니다. 신규 제출과 재제출 모두 이 API를 사용합니다.
복수 스크립트 지원
스크립트 타입(SCRIPT_VIDEO, SCRIPT_BLOG)은 scriptIndex로 각 스크립트를 구분합니다.
- 기존 스크립트 수정:
itemId로 식별 - 신규 스크립트 생성:
itemId= null,scriptIndex지정 (null이면 자동 부여)
장면 순서는 요청 배열 순서로 저장됩니다
items에 담긴 스크립트가 배열 순서대로 scriptIndex 0, 1, 2… 를 받습니다.
요청에 담기지 않은 기존 장면은 그 뒤로 이어붙어, 번호는 항상 0..N-1 연속이 됩니다.
이전에는 요청의 scriptIndex를 받기만 하고 반영하지 않아, 화면에서 바꾼 순서가 저장되지 않고
조회 시 옛 순서로 되돌아갔습니다.
작성 도중 순서만 저장하려면 제출하지 않는 일괄 임시저장을 사용하세요.
신규 장면이 기존 장면을 덮어쓰지 않습니다
itemId 없이 스크립트를 제출할 때 요청한 scriptIndex가 이미 쓰이고 있으면,
그 장면을 재사용하지 않고 뒤 번호로 새 장면을 만듭니다.
예전에는 같은 번호의 기존 장면을 재사용해 내용을 덮어썼고, 그 결과 장면이 사라질 수 있었습니다.
주요 동작
- 각 제출물의 내용(filePath, editorState, comment)을 저장
- 모든 제출물을 제출 상태(
isSubmitted=true)로 변경 - 해당 검수 라운드의 모든 미해결 피드백을 해결완료(
isResolved=true) 처리 - 재제출 시 이전 버전은 히스토리로 자동 저장
HTTP 요청
PUT /ai/influence/contents/review/{reviewId}/submit
Authorization: Bearer {access_token}
Content-Type: application/jsonPath Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
reviewId | long | 예 | 검수 라운드 ID |
Request Body
{
"items": [
{
"itemId": 1,
"itemType": "SCRIPT_VIDEO",
"scriptIndex": 0,
"editorState": {"root": {"children": []}},
"comment": null
},
{
"itemId": 2,
"itemType": "SCRIPT_VIDEO",
"scriptIndex": 1,
"editorState": {"root": {"children": []}},
"comment": null
},
{
"itemId": 3,
"itemType": "CAPTION",
"filePath": null,
"editorState": {"root": {"children": []}},
"comment": null
},
{
"itemId": null,
"itemType": "HASHTAG",
"filePath": null,
"editorState": {"requiredHashtags": ["#브랜드"], "additionalHashtags": ["#추가"]},
"comment": null
}
]
}Request Body 스키마
| 필드명 | 타입 | 필수 | 설명 |
|---|---|---|---|
items | array | 예 | 제출할 제출물 목록 |
items[].itemId | long | 조건부 | 제출물 ID (기존 항목 수정 시 필수, 신규 시 null) |
items[].itemType | string | 조건부 | 제출물 타입 (신규 생성 시 필수) |
items[].scriptIndex | int | 아니오 | 스크립트 순서 인덱스 (스크립트 타입 전용, null이면 자동 부여) |
items[].filePath | string | 조건부 | S3 파일 URL (VIDEO, PHOTO 타입은 필수) |
items[].editorState | object | 아니오 | Lexical 에디터 상태 JSON (SCRIPT, CAPTION, HASHTAG 타입용) |
items[].comment | string | 아니오 | 코멘트 (선택) |
VIDEO/PHOTO 타입 필수 조건
VIDEO 또는 PHOTO 타입으로 제출할 때는 반드시 filePath에 업로드된 파일 URL이 있어야 합니다.
파일 없이 제출하면 에러가 반환됩니다.
- VIDEO 타입: "영상 파일을 업로드해주세요."
- PHOTO 타입: "사진 파일을 업로드해주세요."
itemId 사용 규칙
- 기존 제출물 수정:
itemId필수,itemType무시됨 - 신규 제출물 생성:
itemId= null,itemType필수
같은 타입의 제출물이 여러 개 있을 수 있으므로 (히스토리 관리), 반드시 itemId로 식별해야 합니다.
응답
성공 응답 (200 OK)
{
"status": 200,
"code": null,
"message": "일괄 제출 완료",
"data": {
"reviewId": 88,
"items": [
{
"id": 1,
"reviewId": 88,
"itemType": "SCRIPT_VIDEO",
"scriptIndex": 0,
"editorState": {"root": {"children": []}},
"status": "REVIEWING",
"isSubmitted": true,
"submittedAt": "2025-01-14T15:30:00",
"currentVersion": 1,
"feedbacks": []
},
{
"id": 2,
"reviewId": 88,
"itemType": "SCRIPT_VIDEO",
"scriptIndex": 1,
"editorState": {"root": {"children": []}},
"status": "REVIEWING",
"isSubmitted": true,
"submittedAt": "2025-01-14T15:30:00",
"currentVersion": 1,
"feedbacks": []
},
{
"id": 3,
"reviewId": 88,
"itemType": "CAPTION",
"scriptIndex": null,
"editorState": {"root": {"children": []}},
"status": "REVIEWING",
"isSubmitted": true,
"submittedAt": "2025-01-14T15:30:00",
"currentVersion": 1,
"feedbacks": []
}
],
"resolvedFeedbackCount": 3
}
}Response Body 스키마
| 필드명 | 타입 | 설명 |
|---|---|---|
reviewId | long | 검수 라운드 ID |
items | array | 제출된 제출물 목록 |
resolvedFeedbackCount | int | 자동으로 해결완료 처리된 피드백 수 |
에러 응답
| 상황 | 상태 코드 | 메시지 |
|---|---|---|
| VIDEO 파일 없음 | 400 | 영상 파일을 업로드해주세요. |
| PHOTO 파일 없음 | 400 | 사진 파일을 업로드해주세요. |
| 제출물 타입 누락 | 400 | 신규 생성 시 제출물 타입은 필수입니다. |
| 제출물 없음 | 400 | 제출할 제출물이 없습니다. |
세 가지 저장 경로 비교
| 항목 | PUT /{itemId} | PUT /review/{reviewId}/draft | PUT /review/{reviewId}/submit |
|---|---|---|---|
| 대상 | 단일 제출물 | 검수 라운드 전체 | 검수 라운드 전체 |
| 장면 순서 반영 | 불가 | 가능 | 가능 |
| 제출 처리 | isSubmit에 따라 | 안 함 | 무조건 제출 |
| 피드백 해결 | 안 함 | 안 함 | 자동 일괄 해결 |
| 히스토리 | 변경 시 저장 | 안 함 | 재제출 시 저장 |
| 용도 | 개별 항목 저장 | 작성 중 임시저장 | 최종 제출 |