189 lines
10 KiB
Markdown
189 lines
10 KiB
Markdown
# 定价工具 API
|
||
|
||
> 所有接口要求 `X-Atlas-Platform-Id`。首期仅支持 Ozon、市场 `RU`、`rfbs` 和已审核启用的 CEL 规则。报价引擎只读取 Atlas 已持久化的汇率、佣金和运价版本;请求期间绝不直接请求 CBR、CEL 或 Ozon。
|
||
|
||
## 1. 统一口径
|
||
|
||
- API 使用规范单位:采购成本、国内费用为 `CNY` 字符串;重量为 `kg` 字符串;尺寸为 `cm` 字符串。前端的 g/mm 输入必须先转换。
|
||
- 所有比例以 `0`–`1` 字符串表示,如 `"0.20"` 是 20%。`desired_markup_rate` 是目标净利润 ÷ 采购成本;`list_price_discount_rate` 是买家相对划线价的折扣率。
|
||
- 销售价和划线价均为 `RUB`;`merchandise_value_cny` 是销售价按本次汇率折算得到的 CNY,专门用于选择 CEL 产品类型。
|
||
- 引擎枚举“佣金档 × CEL 产品类型 × 运输方式”,再回验佣金价格区间、CEL 货值区间、重量、尺寸及计费重。没有稳定有效组合时不能返回建议售价。
|
||
- 系统汇率、方案固定汇率、一次性覆盖及临时佣金率都固化到后续 SKU 报价快照,不会改写全局规则或定价方案。
|
||
|
||
## 2. 只读上下文
|
||
|
||
### 2.1 当前上下文
|
||
|
||
`GET /api/pricing/context`
|
||
|
||
返回当前平台、默认市场、当前工作空间该平台的默认店铺及默认定价方案。没有默认店铺返回 `422 DEFAULT_STORE_REQUIRED`,不猜测目标店铺。
|
||
|
||
```json
|
||
{ "data": {
|
||
"platform": { "id": "uuid", "code": "OZON", "name": "Ozon" },
|
||
"market_code": "RU",
|
||
"store": { "id": "uuid", "name": "Ozon 主店", "currency_code": "RUB" },
|
||
"default_pricing_policy": { "id": "uuid", "name": "CEL 常规" }
|
||
} }
|
||
```
|
||
|
||
### 2.2 商品类目、物流目录与系统汇率
|
||
|
||
`GET /api/platform-product-categories?parent_id={uuid|null}&q={keyword}&active_only=true`
|
||
|
||
返回 header 平台的商品类目。`parent_id`(懒加载树)与 `q`(搜索)不能同时使用。每项有 `id`、`name`、`path`、`has_children`、`pricing_rule_status`:
|
||
|
||
- `available`:当前市场、履约模式下存在唯一有效映射和正式佣金规则;
|
||
- `missing`:缺少有效映射或规则;
|
||
- `ambiguous`:多个同优先级映射,普通用户不能猜测。
|
||
|
||
不暴露佣金类目 ID 让客户端自行映射。
|
||
|
||
`GET /api/pricing/catalog?fulfillment=rfbs&store_id={uuid}`
|
||
|
||
`store_id` 省略时取默认店铺;传入时必须属于当前工作空间与 header 平台。仅返回当前已启用价卡、服务商和运输方式:
|
||
|
||
```json
|
||
{ "data": { "providers": [{
|
||
"id": "uuid", "code": "CEL", "name": "CEL",
|
||
"rate_card": { "id": "uuid", "version": "CEL-OZON-RFBS-V6.17", "effective_from": "2026-09-01T00:00:00Z" },
|
||
"transport_methods": [
|
||
{ "id": "uuid", "code": "air", "name": "陆空特快", "speed_label": "5–10 天" },
|
||
{ "id": "uuid", "code": "land_air", "name": "陆空标准", "speed_label": "10–15 天" },
|
||
{ "id": "uuid", "code": "land", "name": "陆运经济", "speed_label": "15–25 天" }
|
||
]
|
||
}] } }
|
||
```
|
||
|
||
若规则仍为 `draft` 或未通过真实账单复核,catalog 不返回它;试算请求返回 `422 PRICING_RULES_UNAVAILABLE`。
|
||
|
||
`GET /api/pricing/exchange-rate?base_currency_code=CNY"e_currency_code=RUB`
|
||
|
||
读取最新 CBR `official_reference`:
|
||
|
||
```json
|
||
{ "data": {
|
||
"id": "uuid", "base_currency_code": "CNY", "quote_currency_code": "RUB", "rate": "12.33430000",
|
||
"source": "cbr", "kind": "official_reference", "effective_on": "2026-09-02",
|
||
"fetched_at": "2026-09-02T02:00:00Z", "is_stale": false
|
||
} }
|
||
```
|
||
|
||
没有系统汇率返回 `404 SYSTEM_EXCHANGE_RATE_NOT_FOUND`;超过 72 小时仍返回但 `is_stale=true`,前端必须明确提示。
|
||
|
||
## 3. 即时试算
|
||
|
||
`POST /api/pricing/quote-previews`
|
||
|
||
Headers:`Idempotency-Key` 必填。接口不创建 `pricing_quotes`;`preview_id` 最多可用于后续 SKU 确认 15 分钟,确认时仍会重新计算。
|
||
|
||
```json
|
||
{
|
||
"store_id": "uuid",
|
||
"fulfillment": "rfbs",
|
||
"platform_product_category_id": "uuid",
|
||
"purchase_cost_cny": "80.00",
|
||
"package": { "weight_kg": "0.990", "length_cm": "30", "width_cm": "31", "height_cm": "11" },
|
||
"pricing": {
|
||
"pricing_policy_id": "uuid",
|
||
"transport_method_ids": ["uuid", "uuid", "uuid"],
|
||
"domestic_shipping_and_label_fee_cny": "5.00",
|
||
"ad_rate": "0.03", "last_mile_rate": "0.02", "withdrawal_rate": "0.01", "other_rate": "0.02",
|
||
"desired_markup_rate": "0.20", "list_price_discount_rate": "0.10",
|
||
"fx": { "mode": "system" },
|
||
"temporary_commission_rate": null
|
||
}
|
||
}
|
||
```
|
||
|
||
规则:
|
||
|
||
- `store_id` 可省略,服务端取默认店铺;非空时必须与 header 平台一致。
|
||
- `pricing_policy_id` 非空时方案提供默认值;请求中的费用、目标、运输方式和汇率仅覆盖本次试算,不修改方案。
|
||
- 无方案时所有费用、费率、目标、汇率模式和至少一个运输方式必填。
|
||
- `fx.mode` 为 `system`、`policy_fixed` 或 `one_time_override`;后者必填 `rate`,建议附 `reason`。`policy_fixed` 只对 `fx_strategy=fixed` 方案有效。
|
||
- 正式佣金规则缺失时,仅无方案即时试算或方案 `allow_commission_override=true` 可传 `temporary_commission_rate`。它必须被明确标记为临时覆盖。
|
||
- 未指定运输方式时取方案已启用的比较方式;仍为空则取方案默认运输方式。它们必须属于同一启用价卡。
|
||
|
||
返回:
|
||
|
||
```json
|
||
{ "data": {
|
||
"preview_id": "opaque-short-lived-id", "expires_at": "2026-09-02T08:15:00Z",
|
||
"context": {
|
||
"store_id": "uuid", "platform_id": "uuid", "market_code": "RU",
|
||
"rate_card": { "id": "uuid", "version": "CEL-OZON-RFBS-V6.17" },
|
||
"exchange_rate": { "rate": "12.33430000", "source": "cbr", "effective_on": "2026-09-02", "is_stale": false }
|
||
},
|
||
"recommended_option_id": "candidate-1",
|
||
"options": [{
|
||
"id": "candidate-1", "is_valid": true,
|
||
"transport_method": { "id": "uuid", "code": "land_air", "name": "陆空标准" },
|
||
"logistics_product_type": { "code": "budget", "name": "Budget" },
|
||
"commission": { "rate": "0.120000", "source": "manual_rule", "version": "2026-09", "price_range": { "min": null, "max": "1500.00", "max_inclusive": true } },
|
||
"prices": { "selling_price_rub": "999.00", "list_price_rub": "1110.00", "merchandise_value_cny": "80.99" },
|
||
"weights": { "actual_weight_kg": "0.990", "volume_weight_kg": null, "billable_weight_kg": "0.990", "billing_basis": "actual_only" },
|
||
"fees": {
|
||
"purchase_cost_rub": "986.74", "logistics_fee_cny": "49.6600", "logistics_fee_rub": "612.55",
|
||
"domestic_fee_cny": "5.0000", "domestic_fee_rub": "61.67", "commission_fee_rub": "119.88",
|
||
"ad_fee_rub": "29.97", "last_mile_fee_rub": "19.98", "withdrawal_fee_rub": "9.99", "other_fee_rub": "19.98",
|
||
"total_cost_rub": "1860.76", "profit_rub": "-861.76", "gross_margin_rate": "-0.862"
|
||
},
|
||
"calculation_trace": { "rounding": [], "formula_steps": [], "rule_versions": [] }, "warnings": []
|
||
}],
|
||
"unavailable_options": [{ "transport_method_id": "uuid", "code": "PACKAGE_DIMENSION_EXCEEDED", "message": "最长边超过该方式限制" }]
|
||
} }
|
||
```
|
||
|
||
数值只示意响应形状,非真实 CEL 结果。每种运输方式独立成功或列出不可报价原因;一个方式失败不阻断其他方式。推荐仅按完整成本、目标利润和规则有效性计算,不承诺时效。
|
||
|
||
缺少成本、有效包装规格、类目、汇率或佣金规则时返回 `422` 和可修复字段;不返回伪造价格。
|
||
|
||
## 4. 定价方案
|
||
|
||
方案属于 `store + platform`,币种从店铺读取。方案资源的核心形状:
|
||
|
||
```json
|
||
{
|
||
"id": "uuid", "store_id": "uuid", "platform_id": "uuid", "name": "CEL 常规", "note": null,
|
||
"status": "active", "is_default": true, "fulfillment": "rfbs",
|
||
"rate_card": { "id": "uuid", "version": "CEL-OZON-RFBS-V6.17", "provider": { "code": "CEL", "name": "CEL" } },
|
||
"default_transport_method_id": "uuid", "comparison_transport_method_ids": ["uuid", "uuid"],
|
||
"fees": { "domestic_shipping_and_label_fee_cny": "5.00", "ad_rate": "0.03", "last_mile_rate": "0.02", "withdrawal_rate": "0.01", "other_rate": "0.02" },
|
||
"desired_markup_rate": "0.20", "list_price_discount_rate": "0.10", "allow_commission_override": false,
|
||
"fx": { "strategy": "system", "fixed_cny_to_rub_rate": null }, "revision": 1
|
||
}
|
||
```
|
||
|
||
`GET /api/stores/{store_id}/pricing-policies?status=active&cursor={opaque}&limit=20`
|
||
|
||
按 `is_default DESC, updated_at DESC, id DESC` 返回方案。路径店铺必须属于当前工作空间与 header 平台。
|
||
|
||
`GET /api/pricing-policies/{policy_id}` 返回完整方案。
|
||
|
||
`POST /api/stores/{store_id}/pricing-policies`
|
||
|
||
Headers:`Idempotency-Key` 必填。请求体采用上方形状的可写字段:创建时 `rate_card_id` 代替 `rate_card`,运输方式以 UUID 数组提交。首个 active 方案强制默认;后续 `is_default=true` 在同一事务取消旧默认。
|
||
|
||
`PATCH /api/pricing-policies/{policy_id}`
|
||
|
||
Headers:`If-Match`、`Idempotency-Key` 必填。仅改提交字段;变更履约、价卡或运输方式时服务端重新校验所有关联。变更只影响未来试算/SKU 新确认,绝不重算旧报价。
|
||
|
||
`POST /api/pricing-policies/{policy_id}/set-default`
|
||
|
||
Headers:`Idempotency-Key` 必填。仅 active 方案可设默认。
|
||
|
||
`DELETE /api/pricing-policies/{policy_id}`
|
||
|
||
Headers:`Idempotency-Key` 必填。将方案置为 `archived`,不物理删除快照。删除默认方案时,若还有 active 方案则返回 `422 DEFAULT_PRICING_POLICY_REPLACEMENT_REQUIRED`,必须先选择替代默认;否则允许无默认方案。
|
||
|
||
## 5. SKU 确认衔接
|
||
|
||
即时试算不落 `pricing_quotes`。商品编辑模块将定义“确认 SKU 报价”:接收 `preview_id`、SKU、最终选中候选及可选一次性覆盖,重新计算后以事务写入 `pricing_quotes + pricing_quote_options`,固化输入、汇率、佣金、CEL 规则版本和费用拆分。
|
||
|
||
客户端不能提交预览中的价格、物流费或规则 ID 作为确认事实。预览过期、规则/汇率更新或包装规格变化时,服务端拒绝旧预览并要求重新试算。临时切换方案或运输方式只进入本次 SKU 快照,绝不修改默认方案。
|
||
|
||
## 6. 后台维护边界
|
||
|
||
普通卖家端不提供佣金类目、佣金规则、物流价卡或汇率历史的写接口。它们由受控运营后台、seed/migration 或 Worker 维护;若将来开放运营 API,必须独立鉴权、审计、版本发布和回滚。
|