Files

10 KiB
Raw Permalink Blame History

定价工具 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,不猜测目标店铺。

{ "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 平台。仅返回当前已启用价卡、服务商和运输方式:

{ "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&quote_currency_code=RUB

读取最新 CBR official_reference:

{ "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 分钟,确认时仍会重新计算。

{
  "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。它必须被明确标记为临时覆盖。
  • 未指定运输方式时取方案已启用的比较方式;仍为空则取方案默认运输方式。它们必须属于同一启用价卡。

返回:

{ "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,币种从店铺读取。方案资源的核心形状:

{
  "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,必须独立鉴权、审计、版本发布和回滚。