GET /ai/admin/recruit-agent/campaign/{collabNo}/candidates
후보 조회 (가입자/비가입자 분리)
후보 조회 (가입자 / 비가입자)
모집 에이전트가 발굴·랭킹한 후보를 가입자(member) / 비가입자(non-member) 두 그룹으로 나눠 반환합니다. 프론트 후보 리스트 화면 전용입니다.
이 API는 랭킹 조회와 달리 에이전트 API 프록시를 거치지 않고 에이전트 PostgreSQL의
campaign_rankings 를 백엔드가 직접 조회합니다. 프록시 홉과 에이전트 콜드스타트 지연이 없어 화면 로딩이 빠릅니다.
(백엔드는 기존 인플루언서 프로필 조회와 동일한 postgres-readonly 데이터소스를 사용)
가입자 / 비가입자 구분
display_group 은 아래 두 값만 사용합니다. 그 외 값(null·platform_member·non_member 등 과거 어휘)의 행은 응답에서 제외됩니다.
| 그룹 | display_group | 화면 명칭 |
|---|---|---|
| 가입자(member) | recommended | 기존 풀 크리에이터 — 신청 이력이 있는 우리 회원 |
| 비가입자(non-member) | discovery | 인스타그램 추천 크리에이터 — 크롤링 풀에서 신규 발굴 |
HTTP 요청
GET /ai/admin/recruit-agent/campaign/{collabNo}/candidates
Authorization: Bearer {access_token}Path Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
collabNo | Long | 예 | 캠페인 번호 |
응답 (200 OK)
{
"status": 200,
"code": null,
"message": "후보 조회 완료",
"data": {
"campaignId": 3115,
"totalCount": 29,
"memberCount": 3,
"nonMemberCount": 26,
"members": [
{
"rank": 1,
"username": "eul_beauty",
"accountId": "...",
"accountLink": "https://www.instagram.com/eul_beauty",
"member": true,
"displayGroup": "recommended",
"influenceNo": 2438,
"rankingScore": 0.71,
"matchScore": 0.559,
"fitScore": 0.85,
"followerCount": 6006,
"estimatedPrice": 150000,
"reelsAvgViews": 89775,
"categoryTags": ["BEAUTY", "FASHION", "HEALTH"],
"recommendReason": "피부 시술, 트러블 등 구체적인 피부 고민을 다루는 콘텐츠...",
"profileImage": "https://d3mp6eqt0w2808.cloudfront.net/img/ai/portfolio/profile/...",
"listed": true,
"proposedPrice": 300000,
"dmOfferPrice": 240000
}
],
"nonMembers": [ /* 동일 구조, member=false, displayGroup="discovery", influenceNo=null, profileImage=null */ ]
}
}필드
| 필드 | 타입 | 설명 |
|---|---|---|
rank | Integer | 에이전트 통합 순위 (가입자+비가입자 합산). 그룹 내에서는 번호가 건너뛸 수 있음 — 백엔드 재산정 없음. 화면에 등수 표기는 하지 않는 방향 |
username | String | Instagram username |
accountLink | String | 프로필 링크 (없으면 username으로 복원) |
member | boolean | 가입자 여부 |
displayGroup | String | recommended(기존 풀 크리에이터) / discovery(인스타그램 추천 크리에이터) — 이 두 값만 |
influenceNo | Integer | 회원 번호(가입자만, 비가입자는 null). 제안하기에 이 값을 전달 |
proposed | boolean | 이 캠페인에서 제안 이메일을 이미 보냈는지 — "제안 완료" 뱃지는 이 값으로 렌더 (가입자만, 새로고침해도 유지) |
proposedAt | DateTime | 최근 제안 발송 시각 (proposed=true일 때) |
rankingScore | Double | 종합 랭킹 점수 (정렬 기준, 그대로 사용) |
matchScore | Double | 캠페인 적합도 — 벡터 매칭 점수(cosine 0.8 + category 0.2). 화면 라벨: "캠페인 적합도" |
fitScore | Double | AI 적합도 — LLM 평가 점수(0~1). 화면 라벨: "AI 적합도" |
followerCount | Integer | 팔로워 수 |
estimatedPrice | Integer | 예상가(랭크 가격, 원) — influencer_profile.tier(크롤러 산정가) 우선, 없으면 팔로워+릴스 조회수 공식(어드민 신청자 화면의 랭킹가와 동일). 산정 불가 시 null(화면 "-" 표시) |
reelsAvgViews | Double | 릴스 평균 조회수 |
categoryTags | String[] | 카테고리 태그 |
recommendReason | String | 추천 사유(LLM) |
profileImage | String | 프로필 이미지 URL. 가입자는 회원 DB에서 채워짐, 비가입자는 null(프론트 기본 아바타) |
listed | boolean | 리스팅 여부 — 이 캠페인의 리스트 관리에 담긴 후보인지. false면 아래 가격 두 필드가 모두 null |
proposedPrice | Long | 리스트 관리에 입력된 제안가(원) 원본. 미입력이면 null — 실제로 입력된 캠페인이 드물어 대부분 null |
dmOfferPrice | Long | 오토디엠 전달가(원) = 기준가 × 0.8, 만원 단위 반올림. 기준가는 proposedPrice 우선, 없으면 estimatedPrice 폴백. listed=false거나 두 기준가가 모두 없으면 null |
리스팅 가격(listed·proposedPrice·dmOfferPrice): 어드민 캠페인 관리의 [DM 자동 발송 내역] 모달용입니다.
후보군 전체를 발송 대상으로 그리되, 리스트 관리에 담긴 후보만 가격을 함께 표기합니다.
전달가가 기준가의 80%인 것은 "제안가보다 20% 저렴하게 전달했다"를 나타내기 위한 값입니다.
매칭은 account_id 기준입니다 — 데모 후보가 이 응답에서 그대로 복사돼 생성되므로 일치합니다.
수기·벌크로 등록한 데모 후보는 account_id 에 manual-/bulk- 접두가 붙어 어긋나므로 링크·username 으로도 매칭합니다.
스코어 표기(확정): 두 점수를 모두 노출합니다 — fitScore → "AI 적합도", matchScore → "캠페인 적합도".
정렬은 rankingScore 기준 그대로 사용합니다. 순위(등수) 표기는 하지 않습니다.
아직 모집이 돌지 않은 캠페인이면 totalCount: 0 과 빈 그룹이 반환됩니다(에러 아님).