모달 "적용하기" — 추가금 + 가격 정책 통합 저장
추가금 항목 체크 상태와 글로우비 특별가/기존가 비율을 저장하고 신청자별 가격을 일괄 재계산합니다.
모달 "적용하기" — 추가금 + 가격 정책 통합 저장
관리자 대시보드의 "추가금/수수료율" 모달에서 적용하기 클릭 시 호출되는 API입니다. 한 번의 트랜잭션으로 다음을 함께 수행합니다:
Collab.currentPriceRate,Collab.quotePriceRate저장- 현재 환율(
exchangeRate) 저장(업서트) — 캠페인당 1개 (TB_CAMPAIGN_EXCHANGE_RATE) - 이 캠페인에 등록된
TB_CAMPAIGN_ADDON_FEE행들(가이드라인 출처)의is_included토글 - 기타 수수료(
MISC) 행을 요청값으로 전체 대체(replace) - 외화 협의가를 환율로 KRW 환산한 뒤, 체크된 PERCENT 항목 합산(동적 글특가 비율)과 AMOUNT 항목 합산(
amountSum)을 반영해 신청자별currentPrice/quotePrice일괄 재계산
기존 PUT /ai/admin/dashboard/{campaignNo}/quote-price는 그대로 유지됩니다. 추가금 합산을 반영하지 않는 단순 비율 업데이트가 필요하면 그쪽을 사용하세요. 추가금 합산이 필요한 모달 "적용하기" 흐름은 이 API를 호출합니다.
HTTP 요청
PUT /ai/admin/dashboard/{campaignNo}/quote-price-with-addons
Authorization: Bearer {access_token}
Content-Type: application/jsonPath Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
campaignNo | Long | 예 | 캠페인 번호 |
Request Body
{
"currentPriceRate": 15,
"quotePriceRate": 50,
"exchangeRate": null,
"includedAddonFeeTypes": ["BEFORE_AFTER", "OUTDOOR"],
"miscFees": [
{ "valueType": "PERCENT", "value": -15 },
{ "valueType": "AMOUNT", "value": 50000 }
]
}| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
currentPriceRate | Double | 예 | 글로우비 특별가 디폴트 비율 (%, 추가금 합산 전 베이스) |
quotePriceRate | Double | 예 | 기존가 비율 (%, 글로우비 특별가에 적용) |
exchangeRate | Number | 아니오 | 현재 환율(외화 1단위당 KRW, 예: 엔화 9, 달러 1380). 해외 캠페인에서 협의가가 외화일 때 KRW협의가 = 협의가 × exchangeRate 환산에 사용. null이면 저장된 환율을 그대로 두며(기존값 유지), 저장값도 없으면 협의가를 그대로 KRW로 간주(국내 캠페인) |
includedAddonFeeTypes | Array<String> | 아니오 | 가격 계산에 포함될 AddonFeeType 코드 목록(가이드라인 출처). 비어있거나 null이면 합산 없이 베이스 비율만 적용 |
miscFees | Array | 아니오 | 기타 수수료 항목 목록. 요청 시 이 캠페인의 기존 기타 수수료를 전부 대체(replace). null/빈 배열이면 기존 기타 수수료를 모두 제거 |
miscFees[].valueType | String | 예 | PERCENT(%) / AMOUNT(원) |
miscFees[].value | Number | 예 | 값. 음수(할인) 허용 (예: -15, -50000). 라벨은 없으며 description은 "기타"로 저장 |
includedAddonFeeTypes에 명시되지 않은 캠페인 추가금 행은 is_included = false로 변경됩니다. 즉 프론트는 모달 합산 체크박스의 전체 ON 상태를 보냅니다.
체크된 항목이 캠페인에 등록되지 않은 경우(가이드라인에서 추가하지 않은 항목)는 무시됩니다 — 행이 없으면 만들지 않습니다.
miscFees는 includedAddonFeeTypes 토글과 무관합니다(항상 가격에 포함). 또한 가이드라인 sync 대상이 아니므로 sync가 기타 수수료를 생성/수정/삭제하지 않습니다.
계산 식
dynamicCurrentRate = currentPriceRate + Σ(included && PERCENT 항목 value) // 기타 수수료 PERCENT 포함
amountSum = Σ(included && AMOUNT 항목 value) // 가이드라인 AMOUNT + 기타 수수료 AMOUNT, 음수 가능
// 환율 환산 (effectiveRate = 요청 exchangeRate, 없으면 저장된 환율)
krwBase = (effectiveRate 가 있고 > 0) ? acceptedPrice × effectiveRate // 외화 협의가 → KRW
: acceptedPrice // 환율 없으면 협의가를 그대로 KRW로 간주
각 신청자에 대해:
currentPrice = krwBase × (1 + dynamicCurrentRate / 100) + amountSum
quotePrice = currentPrice × (1 + quotePriceRate / 100) + amountSumacceptedPrice는 해당 신청자의 가장 최근 수락된 PriceNegotiation.proposedPrice입니다(해외 캠페인은 외화 값). 수락된 협상이 없으면 그 신청자는 스킵됩니다.
국내(원화) 예)
acceptedPrice150,000 /exchangeRate없음 /currentPriceRate15% / included PERCENT 합 10% /amountSum50,000 /quotePriceRate50% →krwBase= 150,000 →currentPrice= 150,000 × 1.25 + 50,000 = 237,500,quotePrice= 237,500 × 1.5 + 50,000 = 406,250 금액(amountSum)은 글특가·기존가 양쪽에 각각 더해집니다.
해외(외화) 예)
acceptedPrice170,000(엔) /exchangeRate9 /currentPriceRate0% /quotePriceRate0% /amountSum0 →krwBase= 170,000 × 9 = 1,530,000원 →currentPrice=quotePrice= 1,530,000원
환율(exchangeRate) 동작
- 해외 크리에이터의 협의가(
PriceNegotiation.proposedPrice)는 외화(예: 엔화)로 저장됩니다. 환율을 받아외화 × 환율 = KRW로 환산한 값을 베이스로 글특가·기존가(원화)를 계산해ApplicationMatching에 저장합니다(브랜드는 원화로 노출). - 환율을
null로 보내면 저장된 환율을 그대로 두고, 저장값도 없으면 환산 없이 협의가를 KRW로 간주합니다 → 기존 국내(원화) 캠페인 동작 불변(하위호환). - 가격이 갱신되는 시점은 이 "적용하기"입니다. 환율만 바꾸고 다시 적용하지 않으면 저장된 원화 가격은 바뀌지 않습니다.
- 환산된 외화 금액은 따로 저장하지 않습니다(환율 1개만 저장). 크리에이터/계약서용 외화 표기는 원본 협의가를 그대로 사용합니다. 통화 종류는
Collab.currency를 사용합니다.
응답
성공 응답 (200 OK)
{
"status": 200,
"code": null,
"message": "추가금 + 가격 정책 저장 성공",
"data": {
"updatedCount": 12,
"updatedIds": [101, 102, 103],
"failedUpdates": [],
"message": "12건의 가격이 업데이트되었습니다. (동적 글특가 비율: 25.00%)"
}
}응답 필드
| 필드 | 타입 | 설명 |
|---|---|---|
updatedCount | Integer | 가격이 재계산된 신청 수 |
updatedIds | Array<Long> | 재계산된 신청 ID 목록 |
failedUpdates[] | Array | 재계산 실패 항목 |
failedUpdates[].applicationId | Long | 실패한 신청 ID |
failedUpdates[].error | String | 실패 사유 |
message | String | 결과 메시지 (동적 비율 포함) |
에러 응답
| 상태 코드 | 설명 |
|---|---|
400 | 존재하지 않는 캠페인입니다: {campaignNo} 또는 필수 필드 누락 |
401 | 인증 실패 |
403 | ADMIN 권한 필요 |