GET /ai/admin/dashboard/{campaignNo}/progress
캠페인 진행 상황 조회
캠페인 진행 상황 조회
캠페인의 진행 상황 테이블을 조회합니다. adminVisible 무관하게 전체 조회됩니다.
배송 정보, 검수 정보, 인플루언서 플래그 등 모든 진행 상황 정보를 포함합니다.
HTTP 요청
GET /ai/admin/dashboard/{campaignNo}/progress
Authorization: Bearer {access_token}Path Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
campaignNo | long | 예 | 캠페인 번호 |
응답
성공 응답 (200 OK)
{
"status": 200,
"code": null,
"message": "진행 상황 조회 성공",
"data": {
"step": "APPLICATION_CONTRACT",
"items": [
{
"id": 123,
"creator": "크리에이터A",
"profileImg": "https://...",
"creatorLink": "https://www.instagram.com/creator_a",
"flags": [
{
"id": 1,
"text": "추천",
"color": "#FF6F00",
"category": "POSITIVE",
"isPreset": true,
"presetCode": "RECOMMENDED"
},
{
"id": 2,
"text": "우수 크리에이터",
"color": "#9C27B0",
"category": "POSITIVE",
"isPreset": false,
"presetCode": null
}
],
"adminVisible": true,
"selectionStatus": "SELECTED",
"businessSelectionStatus": null,
"pendingSelectionApproval": false,
"phase": "PRODUCT_DELIVERY",
"appliedAt": "2025-01-15T10:30:00",
"selectedAt": "2025-01-16T14:20:00",
"rejectedAt": null,
"defaultUnitPrice": 450000,
"quotePrice": 500000,
"currentPrice": 550000,
"matchingStatus": "confirmed",
"deliveryStatus": "배송 중",
"trackingNumber": "1234567890",
"courierCode": "kr.cjlogistics",
"customCourierName": null,
"uploadUrl": null,
"file": null,
"aiCheckReady": false,
"aiCheck": null,
"personCheck": null,
"campaignResult": null,
"uploadStatus": null,
"recommendReason": "팔로워 대비 릴스 조회수가 높고, 뷰티 콘텐츠 제작 경험이 풍부합니다.",
"collaborationNote": null,
"reviewInfo": {
"hasNewSubmission": false,
"reviews": [],
"uploadApproved": null,
"finalSubmission": null
},
"submissionTimeline": {
"script": {
"exists": true,
"reviewRound": 1,
"reviewStatus": "APPROVED",
"submittedAt": "2026-06-09T14:20:00",
"deadlines": {
"submissionDeadline": "2026-06-10T23:59:59",
"submissionOriginalDeadline": "2026-06-08T23:59:59",
"submissionDeadlineConfirmed": true,
"reviewDeadline": "2026-06-12T23:59:59",
"feedbackDeadline": null
},
"items": [
{
"id": 9001,
"itemType": "SCRIPT_VIDEO",
"scriptIndex": 0,
"currentVersion": 1,
"status": "APPROVED",
"isSubmitted": true,
"submittedAt": "2026-06-09T14:20:00"
}
],
"events": [
{
"triggerType": "TRIGGER_SCRIPT_SUBMITTED",
"label": "크리에이터 스크립트 제출/재제출 — 검수 마감 산정 (시트: 제출일+2영업일)",
"phase": "SCRIPT_REVIEW_DEADLINE",
"triggeredAt": "2026-06-09T14:20:00",
"previousDeadline": null,
"newDeadline": "2026-06-11T23:59:59"
}
]
},
"video": {
"exists": true,
"reviewRound": 2,
"reviewStatus": "REVIEWING",
"submittedAt": "2026-06-16T09:00:00",
"deadlines": {
"submissionDeadline": "2026-06-15T23:59:59",
"submissionOriginalDeadline": "2026-06-15T23:59:59",
"submissionDeadlineConfirmed": true,
"reviewDeadline": "2026-06-17T23:59:59",
"feedbackDeadline": "2026-06-16T23:59:59"
},
"items": [
{
"id": 9100,
"itemType": "VIDEO",
"scriptIndex": null,
"currentVersion": 2,
"status": "REVIEWING",
"isSubmitted": true,
"submittedAt": "2026-06-16T09:00:00"
}
],
"events": [
{
"triggerType": "TRIGGER_VIDEO_SUBMITTED",
"label": "크리에이터 영상 제출/재제출 — 검수 마감 산정 (시트: 재제출일+2영업일)",
"phase": "CONTENT_REVIEW_DEADLINE",
"triggeredAt": "2026-06-14T10:00:00",
"previousDeadline": null,
"newDeadline": "2026-06-16T23:59:59"
},
{
"triggerType": "TRIGGER_3R_VIDEO_RESUBMIT_REQUEST",
"label": "영상 재제출 요청 (Trigger 3 재제출 사이클, 광고주 요청)",
"phase": "CONTENT_SUBMISSION_DEADLINE",
"triggeredAt": "2026-06-15T11:00:00",
"previousDeadline": "2026-06-15T23:59:59",
"newDeadline": "2026-06-17T23:59:59"
},
{
"triggerType": "TRIGGER_VIDEO_SUBMITTED",
"label": "크리에이터 영상 제출/재제출 — 검수 마감 산정 (시트: 재제출일+2영업일)",
"phase": "CONTENT_REVIEW_DEADLINE",
"triggeredAt": "2026-06-16T09:00:00",
"previousDeadline": "2026-06-16T23:59:59",
"newDeadline": "2026-06-18T23:59:59"
}
]
},
"upload": {
"uploadDate": "2026-06-20T23:59:59",
"uploadType": "OPTIMAL",
"uploadDeadline": "2026-06-22T23:59:59",
"deadline": null,
"events": []
},
"finalSubmission": {
"submittedAt": null,
"finalSubmissionDeadline": "2026-06-22T23:59:59",
"finalResubmissionDeadline": null,
"settlementDeadline": null,
"contentLink": null,
"isSubmitted": false,
"events": []
}
},
"adjustedEngagementRate": 4.6282,
"recentPosts": [
{
"postId": "abc123",
"postLink": "https://instagram.com/p/abc123",
"mediaUrl": "https://video.cdninstagram.com/...",
"mediaType": "VIDEO",
"publishedAt": "2025-01-20T14:30:00Z"
},
{
"postId": "def456",
"postLink": "https://instagram.com/p/def456",
"mediaUrl": "https://scontent.cdninstagram.com/...",
"mediaType": "IMAGE",
"publishedAt": "2025-01-18T10:00:00Z"
}
],
"selectedOptions": [
{
"optionId": 1,
"title": "색상",
"selected": [
{ "choiceId": 11, "value": "아이보리" }
]
}
]
}
],
"isLegacy": false,
"adminSelectionApprovalEnabled": false
}
}응답 필드 설명
data 객체
| 필드 | 타입 | 설명 |
|---|---|---|
step | String | 현재 진행 단계 |
items | Array | 신청 목록 |
isLegacy | Boolean | 레거시 캠페인 여부 |
adminSelectionApprovalEnabled | Boolean | 선정의 선정 게이트 ON/OFF. true면 기업의 선정이 관리자 승인 후 확정된다. 관리자 전용 필드로, 기업/크리에이터 대상 API에는 노출되지 않는다. |
items 배열 내 각 항목
| 필드 | 타입 | 설명 |
|---|---|---|
id | Long | 신청 ID |
creator | String | 크리에이터 이름 |
profileImg | String | 프로필 이미지 URL |
creatorLink | String | SNS 계정 링크 |
flags | Array<FlagDto> | 인플루언서 플래그 목록 (추천, 주의사항 등). 전역·캠페인·타 캠페인을 모두 합친 값 |
globalFlags | Array<FlagDto> | 전 캠페인 공통 비고 |
campaignFlags | Array<FlagDto> | 이 캠페인 전용 비고 |
adminVisible | Boolean | 기업 대시보드 노출 여부 |
selectionStatus | String | 선정 상태 (WAITING, SELECTED, RESERVED, REJECTED, ELIMINATED, PROPOSAL, EXPOSURE_CANDIDATE) — 실제(확정) 상태. EXPOSURE_CANDIDATE(노출 후보)는 관리자 전용이라 기업·크리에이터 응답에는 대기로 마스킹되어 나갑니다 |
businessSelectionStatus | String | 기업 시점 임시 선정 상태 (선정의 선정 게이트 ON 전용, 게이트 OFF면 null). 기업이 선정을 제안했지만 관리자 승인 전인 상태를 나타낸다. 관리자 전용, 기업/크리에이터에는 노출되지 않는다. |
pendingSelectionApproval | Boolean | 관리자 선정 승인 대기 여부. 게이트 ON에서 기업이 선정을 제안한 뒤 관리자 승인 전이면 true. 관리자 전용, 기업/크리에이터에는 노출되지 않는다. |
phase | String | 진행 단계 |
appliedAt | LocalDateTime | 신청일 |
selectedAt | LocalDateTime | 선정일 |
rejectedAt | LocalDateTime | 거절일 |
defaultUnitPrice | Long | 희망가 (크리에이터가 입력한 단가) |
quotePrice | Long | 기존가. 글특가 x 1.5 만원올림 |
currentPrice | Long | 글특가 (광고주에게 노출되는 가격). 기준가 x (1 + 수수료율) 만원올림 |
matchingStatus | String | 매칭 상태 (waiting, confirmed, reserved, rejected, eliminated, proposal, exposureCandidate) |
recommendReason | String | 추천 사유 (AI 추천 또는 수동 입력) |
deliveryStatus | String | 배송 상태 |
trackingNumber | String | 운송장 번호 |
courierCode | String | 택배사 코드. 지원 택배사는 kr.xxx(kr.cjlogistics 등), 기타는 OTHER. legacy 건은 한글 택배사명이 직접 들어있음 |
customCourierName | String | 기타 택배사 자유입력명. 지원 택배사(kr.*)면 null. 목록 다운로드의 택배사명 컬럼은 이 값을 사용 |
reviewInfo | Object | 검수 정보 (기존, 유지) |
submissionTimeline | SubmissionTimeline | [TASK-081] 콘텐츠 제출 일정 4그룹(스크립트/영상/업로드/최종제출물). 각 그룹의 마감일·제출일·제출기록(언제 제출/재제출/검수했는지 이벤트 타임라인). 아래 SubmissionTimeline 참고 |
adjustedEngagementRate | Double | 보정 참여율(%). PostgreSQL influencer_profile.adjusted_engagement_rate 원본값. 미산정 계정은 null. 아래 보정 참여율 참고 |
recentPosts | Array<PostMediaDto> | 최근 게시물 미디어 (최대 3개) |
selectedOptions | Array | 캠페인 제공 옵션 — 크리에이터가 선택한 값 (그룹별로 묶음). 옵션 미정의/미선택 시 빈 배열. 자세한 내용은 캠페인 제공 옵션 개요 참고 |
보정 참여율
adjustedEngagementRate는 크롤링 PostgreSQL의 influencer_profile.adjusted_engagement_rate 값을 반올림 없이 그대로 내려줍니다.
- 단위: 퍼센트(%). 예를 들어
4.6282는 4.6282% 를 뜻합니다. - 조회 기준:
is_current = true인 프로필을 신청자의sns_account_link로 매칭합니다. null인 경우: 매칭되는 프로필이 없거나, 프로필은 있어도 참여율이 아직 산정되지 않은 계정. 전체 프로필 중 약 1/3 만 값이 채워져 있으므로 프런트는null처리를 반드시 넣어야 합니다.
recentPosts 배열 내 각 항목 (PostMediaDto)
| 필드 | 타입 | 설명 |
|---|---|---|
postId | String | 게시물 ID |
postLink | String | 게시물 링크 |
mediaUrl | String | 미디어 URL (video_url 또는 images 중 첫 번째) |
mediaType | String | 미디어 타입 (VIDEO 또는 IMAGE) |
publishedAt | OffsetDateTime | 게시일 |
flags 배열 내 각 항목 (FlagDto)
| 필드 | 타입 | 설명 |
|---|---|---|
id | Long | 플래그 ID |
text | String | 표시할 텍스트 |
color | String | HEX 색상 코드 (예: #FF6F00) |
category | String | 카테고리 (POSITIVE: 화이트리스트, NEGATIVE: 블랙리스트, NEUTRAL: 중립) |
isPreset | Boolean | 프리셋 플래그 여부 |
presetCode | String | 프리셋 코드 (프리셋인 경우, 예: RECOMMENDED, SCHEDULE_VIOLATION) |
count | Integer | 같은 문구가 그 범위 안에 몇 건인지 |
scope | String | 적용 범위 (GLOBAL: 전 캠페인 공통, CAMPAIGN: 해당 캠페인 전용) |
collabNo | Integer | 캠페인 번호. scope=GLOBAL 이면 null |
source | String | 등록 출처 (ADMIN / AUTO_LABEL / BLACKLIST) |
editable | Boolean | 어드민이 삭제할 수 있는지. source=ADMIN 일 때만 true |
프리셋 플래그 코드
| 코드 | 텍스트 | 카테고리 | 설명 |
|---|---|---|---|
RECOMMENDED | 추천 | POSITIVE | 추천 인플루언서 |
BUSINESS_PREFERRED | 기업 선호 | POSITIVE | 기업이 선호하는 인플루언서 |
HIGH_QUALITY | 고품질 콘텐츠 | POSITIVE | 고품질 콘텐츠 제작자 |
SCHEDULE_VIOLATION | 일정 위반 | NEGATIVE | 일정 준수 미흡 |
POOR_COMMUNICATION | 소통 미흡 | NEGATIVE | 소통이 원활하지 않음 |
QUALITY_ISSUE | 품질 문제 | NEGATIVE | 콘텐츠 품질 문제 |
SubmissionTimeline
[TASK-081] 콘텐츠 제출 일정을 4그룹으로 묶어 내려줍니다. 데드라인 / 제출일 / 제출기록(이벤트 타임라인)을 한 번에 표시하기 위한 블록입니다. 기존 reviewInfo는 그대로 유지되며 이 블록이 추가됩니다. 기업 진행테이블(GET /ai/progress-table/item/{id})과 동일한 구조입니다.
| 그룹 | 키 | 대상 |
|---|---|---|
| 스크립트 | script | 2회검수(대본검수) 캠페인만 존재. 1회검수 캠페인은 null |
| 영상 | video | 영상/제작물 검수 |
| 업로드 | upload | 업로드일자·업로드타입·업로드 데드라인 |
| 최종제출물 | finalSubmission | 최종제출물(콘텐츠 링크/파일)·재제출 마감·정산 |
그룹 객체 (script / video) — SubmissionPhaseGroup
| 필드 | 타입 | 설명 |
|---|---|---|
exists | boolean | 그룹 존재 여부 |
reviewRound | int | 1=스크립트, 2=영상 |
reviewStatus | string | 해당 라운드 검수 상태 (PENDING, REVIEWING, APPROVED, REJECTED). 없으면 null |
submittedAt | LocalDateTime | 제출일(최신 제출 시각). 미제출 시 null |
deadlines | GroupDeadlines | 마감 세트 (아래) |
items | SubmissionItemSnapshot[] | 현재(최신 버전) 제출물 목록 |
events | TimelineEvent[] | 제출/재제출/검수 이벤트 타임라인 (시각 오름차순) |
GroupDeadlines
| 필드 | 타입 | 설명 |
|---|---|---|
submissionDeadline | LocalDateTime | 제출 마감 |
submissionOriginalDeadline | LocalDateTime | 최초 제출 마감(불변, 참조용) |
submissionDeadlineConfirmed | boolean | 트리거 발동으로 확정됐는지. false면 임시(placeholder) 마감 |
reviewDeadline | LocalDateTime | 검수 마감(기업쪽). 없으면 null |
feedbackDeadline | LocalDateTime | 피드백 반영 마감(재제출 발생 시). 없으면 null |
upload — UploadGroup
| 필드 | 타입 | 설명 |
|---|---|---|
uploadDate | LocalDateTime | 업로드일자 (업로드 허용일) |
uploadType | string | 업로드타입 (OPTIMAL, CANDIDATE, ASAP 등) |
uploadDeadline | LocalDateTime | [태스크21] 업로드/최종제출물 제출 마감 = 업로드 허용일 + 2일(ASAP/OPTIMAL), CANDIDATE는 고른 날 그대로. 업로드 단계(영상검수 승인) 도달 전이면 null. 아래 deadline(재제출 마감)과 별개 |
deadline | LocalDateTime | 업로드 데드라인 (= 최종제출물 재제출 마감과 동일 값). 없으면 null |
events | TimelineEvent[] | 업로드 관련 이벤트 타임라인 |
finalSubmission — FinalSubmissionGroup
| 필드 | 타입 | 설명 |
|---|---|---|
submittedAt | LocalDateTime | 최종제출물 제출 시각 |
finalSubmissionDeadline | LocalDateTime | [태스크21] 최종제출물 제출 마감 (= upload.uploadDeadline과 동일 값, 업로드 허용일 + 2일). 업로드 단계 도달 전이면 null. finalResubmissionDeadline(재제출 마감)과 별개 |
finalResubmissionDeadline | LocalDateTime | 재제출 마감(재요청 시). 없으면 null |
settlementDeadline | LocalDateTime | 정산 마감(업로드 완료 후). 없으면 null |
contentLink | string | 업로드한 콘텐츠 링크 |
isSubmitted | boolean | 최종제출 여부 |
events | TimelineEvent[] | 최종제출/재요청/정산 이벤트 타임라인 |
SubmissionItemSnapshot (현재 제출물)
| 필드 | 타입 | 설명 |
|---|---|---|
id | long | 제출물 ID |
itemType | string | SCRIPT_VIDEO, SCRIPT_BLOG, VIDEO, PHOTO, CAPTION, HASHTAG |
scriptIndex | int | 복수 스크립트 슬롯 구분 (비스크립트면 null) |
currentVersion | int | 현재 버전 번호 |
status | string | 제출물 검수 상태 |
isSubmitted | boolean | 제출 완료 여부 |
submittedAt | LocalDateTime | 현재 버전 제출 시각 |
TimelineEvent (제출기록 = 언제 무슨 일이 있었는지)
TB_TRIGGER_LOG 기반의 append-only 이벤트입니다. triggeredAt이 실제 이벤트가 일어난 시각(제출/재제출요청/검수완료 등)입니다. 재제출이 여러 번이면 같은 그룹의 events에 여러 행이 시각순으로 누적됩니다.
| 필드 | 타입 | 설명 |
|---|---|---|
triggerType | string | 트리거 종류 (예: TRIGGER_SCRIPT_SUBMITTED, TRIGGER_VIDEO_SUBMITTED, TRIGGER_3R_VIDEO_RESUBMIT_REQUEST, TRIGGER_2_SCRIPT_REVIEW_DONE) |
label | string | 트리거 한국어 설명 |
phase | string | 갱신된 일정 단계 (ApplicationSchedulePhase) |
triggeredAt | LocalDateTime | 이벤트 발생 시각 (= 제출/재제출/검수 시점) |
previousDeadline | LocalDateTime | 이 이벤트 직전 마감일 |
newDeadline | LocalDateTime | 이 이벤트로 갱신된 마감일 |
그룹 분류 규칙: ContentReview.reviewRound(1=스크립트, 2=영상) 기준입니다. events는 phase로 그룹에 매핑되며, 라운드가 공유하는 FEEDBACK_DEADLINE은 triggerType으로 분리됩니다. 스크립트 검수가 없는 1회검수 캠페인은 script가 null입니다.
events는 일정(마감)에 영향을 준 트리거만 기록합니다(순수 임시저장은 제외). 실제 제출/재제출/검수승인은 모두 기록됩니다. 버전별 파일 내용은 items(현재 버전)와 reviewInfo를 함께 참고하세요.