feat(pricing): 开发定价方案

This commit is contained in:
R524809
2026-09-28 17:34:33 +08:00
parent a29dd92676
commit 84b230e69a
54 changed files with 6264 additions and 236 deletions
+86 -145
View File
@@ -1,188 +1,129 @@
# 定价工具 API
# 定价试算与门店方案 API
> 所有接口要求 `X-Atlas-Platform-Id`。首期仅支持 Ozon、市场 `RU`、`rfbs` 和已审核启用的 CEL 规则。报价引擎只读取 Atlas 已持久化的汇率、佣金和运价版本;请求期间绝不直接请求 CBR、CEL 或 Ozon。
> 2026-09-28:维护者批准 SKU 就地试算、详情弹窗、随商品草稿原子确认。本契约替换原规划的独立 `pricing-confirms` 入口。
> 稳定 DTO 位于 `packages/contracts/src/pricing.ts`;金额、汇率和比例均使用十进制字符串。
## 1. 统一口径
## 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 报价快照,不会改写全局规则或定价方案。
### GET `/pricing/catalog?store_id={uuid}`
## 2. 只读上下文
需要已认证工作空间。返回 `{data: PricingCatalog}`:
### 2.1 当前上下文
- `store`:门店 ID、名称及报价币种(CNY / RUB)。
- `rate_cards[]`:该门店平台、RU / rFBS 的有效费率卡及运输方式。正式环境必须 active、在有效期内、有审核时间及审核人。`development_only=true` 表示本地开发模式临时可选的 draft 卡。
- `pending_rate_cards[]`:该平台 RU / rFBS 待审核草稿的名称、版本和状态,仅用于说明阻断原因,不作为方案或报价可选项。
- `policies[]`:该门店活动方案,默认方案优先,包含完整参数和 revision。
- `exchange_rate`:CBR 最新 CNY→RUB 官方参考汇率,包含 ID、日期和 stale;缺少数据时为 null。超过 72 小时提示过期参考,不自动代填其他来源。
`GET /api/pricing/context`
### GET `/pricing/categories?store_id={uuid}&q={text}`
返回当前平台、默认市场、当前工作空间该平台的默认店铺及默认定价方案。没有默认店铺返回 `422 DEFAULT_STORE_REQUIRED`,不猜测目标店铺。
检索门店平台下活动商品类目,返回 `{data:[{id,name,path}]}`,最多 50 条。`q` 最长 200 字;继续输入名称缩小范围。
```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. POST `/pricing/quote-previews`
### 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&quote_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 分钟,确认时仍会重新计算。
要求 `Idempotency-Key`。成功返回 201;同键同输入重放原响应,同键不同输入返回 409。
```json
{
"store_id": "uuid",
"store_id": "<store UUID>",
"platform_product_category_id": "<category 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" },
"binding": {"profile_id":"<profile UUID>","variant_id":"<variant UUID>","client_key":null},
"purchase_cost_cny": "28.00",
"package": {"shipping_weight_g":"380","length_mm":"220","width_mm":"160","height_mm":"90","status":"confirmed"},
"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
"pricing_policy_id": "<policy UUID>",
"rate_card_id": "<rate-card UUID>",
"transport_method_ids": ["<transport UUID>"],
"default_transport_method_id": "<transport UUID>",
"domestic_shipping_and_label_fee_cny":"3",
"ad_rate":"0.03","last_mile_rate":"0.02","withdrawal_rate":"0.01","other_rate":"0.02",
"desired_markup_rate":"0.50","list_price_discount_rate":"0.20",
"mode":"target_markup","selling_price":null,
"fx":{"mode":"policy_fixed"},"temporary_commission_rate":null
}
}
```
规则:
- 独立试算 `binding=null`;SKU 必须提供 profile,已有 variant_id 与新建 client_key 二选一。验证资源归属,已上架 SKU 不接受新报价。
- 包装输入是编辑草稿合并后的有效 g/mm 值,必须已确认;尺寸最多三位小数,重量至少 1 g。保存时从实际持久化草稿重新合并并核对,不能仅信任客户端摘要。
- `mode=target_markup`:目标利润=采购成本×加价率;求解销售价。`fixed_sale`:selling_price 必填,按该售价核算利润。
- 百分比费用以销售额为基数;除加价率外每项小于 1、最多六位小数。引擎拒绝总扣费率≥1。划线价=销售价÷(1−折扣率)。
- 枚举运输方式×产品类型×佣金档,按最终币种舍入后回验货值、佣金、重量和体积重边界。默认运输不可用时 recommended_option_id=null,不替换为另一方式。
- `fx.mode=system` 读取 PostgreSQL 最新 CBR;`policy_fixed` 必须绑定有固定汇率的方案;`one_time_override` 必须明确 rate 和 reason。固定汇率取方案值,不信任客户端覆盖。采用的汇率固定在本次预览中,15 分钟内确认不重新拉取外部汇率。
- 只有不存在有效正式佣金规则且无方案或方案允许时,才能 temporary_commission_rate。已有正式规则禁止覆盖。
- `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`。它必须被明确标记为临时覆盖。
- 未指定运输方式时取方案已启用的比较方式;仍为空则取方案默认运输方式。它们必须属于同一启用价卡。
响应包含 `preview_id,expires_at,input_hash,recommended_option_id,options,unavailable_options,context`。
每个 option 包含目标币种销售/划线价、CNY/RUB 利润、计费重、佣金/物流引用、费用明细、warnings 和 can_confirm。
亏损可查看,但 can_confirm=false;判定使用未舍入利润,显示为 0.00 的微亏也不能确认。
context 保存规则版本、实际汇率与来源,以及系统汇率记录 ID / 日期。参数/规则快照由服务端持久化。
返回:
预览按用户、工作空间、门店、SKU、规范化输入与规则摘要绑定,15 分钟有效。Worker 删除过期两天以上的预览。
```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": "最长边超过该方式限制" }]
} }
```
## 3. 商品保存时确认
数值只示意响应形状,非真实 CEL 结果。每种运输方式独立成功或列出不可报价原因;一个方式失败不阻断其他方式。推荐仅按完整成本、目标利润和规则有效性计算,不承诺时效。
缺少成本、有效包装规格、类目、汇率或佣金规则时返回 `422` 和可修复字段;不返回伪造价格。
## 4. 定价方案
方案属于 `store + platform`,币种从店铺读取。方案资源的核心形状:
`PUT /products/{id}/editor-draft` 沿用商品/profile revision 的 `If-Match` 和 `Idempotency-Key`,每个 variant 增加:
```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
"pricing_draft": "本次 PricingParameters 对象或 null",
"pricing_selection": {"preview_id":"<UUID>","option_id":"<UUID>","input_hash":"<64-char SHA256>"}
}
```
`GET /api/stores/{store_id}/pricing-policies?status=active&cursor={opaque}&limit=20`
两个字段均可省略以兼容既有客户端;读取 listing 返回 `pricing_draft,pricing_result,pricing_valid`。
按 `is_default DESC, updated_at DESC, id DESC` 返回方案。路径店铺必须属于当前工作空间与 header 平台。
确认在现有商品事务中执行:先保存草稿和新 SKU,再核对实际有效输入、用户绑定、有效期、审核规则摘要、币种及候选资格,最后创建不可变报价/候选快照并切换 listing 售价引用。
新 SKU 的 client_key 在同一事务映射到真实 ID。独立预览、其他用户或其他 SKU 的预览不能用于确认。
`GET /api/pricing-policies/{policy_id}` 返回完整方案。
- 携带失效确认意图:409 `PRICING_PREVIEW_STALE`,details 定位具体 SKU,整笔保存回滚。
- 不携带确认意图:允许保存未定价草稿;输入/币种变化使报价失效。用户可在冲突弹窗明确选择“仅保存草稿,稍后定价”。
- 未变化的已确认报价继续使用原快照,无时间过期,不重新读规则或汇率。方案更新/归档不改变其价格。
- 发布校验要求 pricing_valid=true,无有效报价返回 `PRICING_REQUIRED`;已导入 SKU 沿用既有不可再次首次上架边界。
- 数据库复合外键保证 quote/profile/product/variant/store/platform 归属,RLS 隔离租户。确认快照拒绝 UPDATE;成本、包装、类目、店铺或币种变化触发失效。
`POST /api/stores/{store_id}/pricing-policies`
## 4. 门店方案
Headers:`Idempotency-Key` 必填。请求体采用上方形状的可写字段:创建时 `rate_card_id` 代替 `rate_card`,运输方式以 UUID 数组提交。首个 active 方案强制默认;后续 `is_default=true` 在同一事务取消旧默认。
| 方法 | 路径 | 约束 |
|---|---|---|
| GET | `/stores/{id}/pricing-policies` | 返回当前门店活动方案数组 |
| POST | `/stores/{id}/pricing-policies` | Idempotency-Key;成功 201 |
| PATCH | `/pricing-policies/{id}` | Idempotency-Key + If-Match(带引号的 revision);冲突 409 |
| POST | `/pricing-policies/{id}/set-default` | Idempotency-Key;门店锁保证唯一默认;成功 200 |
| DELETE | `/pricing-policies/{id}` | Idempotency-Key;归档并清除默认标志,返回 `{id,archived:true}` |
`PATCH /api/pricing-policies/{policy_id}`
写入 `{name,note,parameters,allow_commission_override}`。只保存费用、加价、折扣、固定/系统汇率与运输偏好;采购价、包装、手动售价及临时佣金不属于方案持久字段。
方案的默认和比较运输必须属于同一可用费率卡;正式环境仅允许已审核卡。历史快照不会被方案修改;尚未确认的预览因方案版本改变要求重算。
Headers:`If-Match`、`Idempotency-Key` 必填。仅改提交字段;变更履约、价卡或运输方式时服务端重新校验所有关联。变更只影响未来试算/SKU 新确认,绝不重算旧报价。
## 5. 数据启用与错误
`POST /api/pricing-policies/{policy_id}/set-default`
迁移中的 CEL 目录为 `draft`,覆盖六种产品类型、三种运输方式,香港空运不纳入。真实账单复核、有效日期/审核人以及平台类目佣金映射仍是正式使用前置条件,接口不会回退到示例费率或固定佣金。
Headers:`Idempotency-Key` 必填。仅 active 方案可设默认。
商品编辑联调期间,API 在 `NODE_ENV=development` 时默认允许 draft 费率卡参与方案保存、预览与草稿报价确认;可设 `PRICING_ALLOW_DRAFT_RATES=false` 关闭。测试环境只有显式设 `PRICING_ALLOW_DRAFT_RATES=true` 才开启;生产环境始终关闭。该模式不修改费率卡状态、不生成审核记录。目录的 `rate_cards[].development_only` 和预览的 `context.development_only` 标识试算性质;基于此类预览确认的 SKU 报价可在编辑页查看,但首次上架校验必须返回 `PRICING_RATE_REVIEW_REQUIRED`,直至使用已审核费率重新试算确认。佣金和汇率仍按现有输入规则处理。
`DELETE /api/pricing-policies/{policy_id}`
### 系统管理员审核 CEL 费率
Headers:`Idempotency-Key` 必填。将方案置为 `archived`,不物理删除快照。删除默认方案时,若还有 active 方案则返回 `422 DEFAULT_PRICING_POLICY_REPLACEMENT_REQUIRED`,必须先选择替代默认;否则允许无默认方案。
系统管理「物流费率审核」使用以下接口;全部要求登录且具备实时有效的 `system_admin_grants` 授权,普通门店用户返回 403 `SYSTEM_ADMIN_REQUIRED`:
## 5. SKU 确认衔接
| 方法 | 路径 | 行为 |
|---|---|---|
| GET | `/system/pricing/rate-cards?platform_id={uuid}` | 查看该平台 RU/rFBS 草稿及已启用费率,含产品类型、规则、审核记录与 `rules_hash` |
| GET | `/system/pricing/rate-cards/{id}` | 重读单个费率版本 |
| POST | `/system/pricing/rate-cards/{id}/reviews` | `Idempotency-Key` UUID 必填;原子保存审核证据并启用费率,成功 201 |
即时试算不落 `pricing_quotes`。商品编辑模块将定义“确认 SKU 报价”:接收 `preview_id`、SKU、最终选中候选及可选一次性覆盖,重新计算后以事务写入 `pricing_quotes + pricing_quote_options`,固化输入、汇率、佣金、CEL 规则版本和费用拆分。
审核请求包含 `rules_hash`、`source_reference`(资费合同/实际账单批次)、至少 20 字的 `review_note`、`effective_from`(含时区 ISO 时间)和 `samples`。每条样本包含 `rule_id`、唯一的 `invoice_reference`、`merchandise_value_cny`、包装重量 g / 长宽高 mm、`billed_logistics_cny`。每种 CEL 产品类型至少一条,合计不少于六条。服务端使用与正式报价相同的包装适用性和运费函数复算;样本货值须命中对应类型,账单运费与规则计算差额不得超过 0.01 CNY。真实账单来源和口径仍由审核人负责确认,系统不会凭示例数字自行审核。
客户端不能提交预览中的价格、物流费或规则 ID 作为确认事实。预览过期、规则/汇率更新或包装规格变化时,服务端拒绝旧预览并要求重新试算。临时切换方案或运输方式只进入本次 SKU 快照,绝不修改默认方案。
仅支持 Ozon/CEL 草稿版本。规则必须仍为 16 条 draft,类型为指定六种,且 `rules_hash` 与管理员查看时一致;否则返回 409/422。通过后在同一数据库事务保存不可修改的审核记录、把规则与费率卡改为 active、设置审核人与生效日期。若生效日期在未来,直到该时间才出现在卖家端可用费率中。数据库拒绝改写已审核的费率卡、产品类型、运输方式和规则;修改资费应新建版本重新审核,不覆盖已确认 SKU 报价。已启用费率不提供再次审核入口。
## 6. 后台维护边界
系统管理不会自动配置平台类目佣金映射;缺少正式佣金规则时,卖家试算仍需满足临时佣金覆盖条件。当前 CEL 种子保持 draft,尚无真实账单证据时不得提交启用。
普通卖家端不提供佣金类目、佣金规则、物流价卡或汇率历史的写接口。它们由受控运营后台、seed/migration 或 Worker 维护;若将来开放运营 API,必须独立鉴权、审计、版本发布和回滚。
| HTTP / code | 用户动作 |
|---|---|
| 422 VALIDATION_ERROR | 修正输入格式或缺项 |
| 422 PRICING_RULES_UNAVAILABLE | 配置并审核物流费率 |
| 422 PRICING_COMMISSION_UNAVAILABLE | 配置正式映射,或使用允许临时佣金的方案 |
| 422 PRICING_FX_UNAVAILABLE | 等待 CBR 同步,或明确采用临时/方案固定汇率 |
| 409 PRICING_PREVIEW_STALE | 重算、比较并重新应用,或显式仅保存草稿 |
| 409 VERSION_CONFLICT | 重新读取版本后修改 |
独立上下文、汇率信息整合在 catalog;本期不开放原规划的独立 context、exchange-rate、pricing-confirms 路由和报价历史列表。
+12 -9
View File
@@ -1,6 +1,6 @@
# 商品编辑与首次上架 API
> M3.0~M3.4 已实现编辑上下文、草稿、SKU、来源素材、类目和动态属性。平台目录刷新方案第 1~4 步已把类目树、类型属性、字典读取及发布预检迁至平台级缓存:编辑器按需刷新,首次发布预检只刷新当前类型和实际使用的字典。AI 填报任务已实现;定价确认和真正调用 Ozon 的首次发布 job 仍未实现。
> M3.0~M3.4 已实现编辑上下文、草稿、SKU、来源素材、类目和动态属性。平台目录刷新方案第 1~4 步已把类目树、类型属性、字典读取及发布预检迁至平台级缓存:编辑器按需刷新,首次发布预检只刷新当前类型和实际使用的字典。AI 填报任务及随商品保存的定价确认已实现;真正调用 Ozon 的首次发布 job 仍未实现。
> 此处的商品始终是采集箱内的 Atlas 草稿。所有平台范围接口要求 `X-Atlas-Platform-Id`。商品通用资料可以跨平台复用;`product_platform_profile`、SKU 首次导入状态和在线商品不能跨平台/门店复用。
@@ -248,17 +248,13 @@ Body:`{ "variant_ids": ["uuid", "uuid"] }`,最多 100 个且不能重复。
## 4. SKU 定价确认
`POST /api/product-platform-profiles/{profile_id}/variants/{variant_id}/pricing-confirms`
定价确认与 `PUT /api/products/{product_id}/editor-draft` 同一事务完成。每个 SKU 可携带 `pricing_draft` 和 `pricing_selection: { preview_id, option_id, input_hash }`;新 SKU 使用 `client_key` 绑定预览,保存时映射为真实 ID。独立试算不能直接确认到 SKU。
Headers:`Idempotency-Key` 必填。Body:
服务端核对预览归属、15 分钟有效期、当前采购价、有效包装、类目、门店、币种及规则版本,再写入不可变 `pricing_quotes`、全部候选和 listing 价格。任何一个明确选择的报价失效,整个商品保存回滚。客户端可提示用户重新计算,或经用户选择清除本次确认意图后仅保存草稿。
```json
{ "preview_id": "opaque-id", "selected_option_id": "candidate-1", "one_time_overrides": null }
```
没有报价时仍可保存商品,但首次发布预检返回 `PRICING_REQUIRED`。已确认报价不会随预览到期而失效;采购、包装、目标门店/类目、履约或门店币种改变后须重新确认。调整默认方案、汇率或费率不会反向改写历史快照。详见[定价 API](./pricing.md)。
服务端重新解析 SKU 的有效包装规格、验证预览仍未过期且配置/汇率/规则未变化,重新计算后写入 `pricing_quotes`、全部 `pricing_quote_options` 和当前平台 listing 的售价/划线价/币种/报价引用。成功返回 `201` 与不可变报价摘要。
包装规格缺失或 `ambiguous`、预览失效、候选不属于预览、目标店铺不一致、物流不适用时返回 `422`,不覆盖既有价格。临时更换方案或运输方式仅写入本次报价,不改门店默认方案。
开发环境可按定价 API 的草稿费率模式完成商品编辑联调;这种报价即使已保存,也会在首次发布预检返回 `PRICING_RATE_REVIEW_REQUIRED`。费率完成正式审核后,须用已审核卡重新试算并确认 SKU。
## 5. 素材选择与水印派生
@@ -319,3 +315,10 @@ Headers:`Idempotency-Key` 必填。Body:
## 8. 已替代的旧草案
本文替代 [`../product/api.md`](../product/api.md) 中与商品编辑有关的路径。旧草案保留为历史背景,实施以本文件、[`pricing.md`](./pricing.md) 与 [`collection-and-extension.md`](./collection-and-extension.md) 为准。
### 2026-09-28 SKU 定价确认修订
草稿的每个 variant 可携带 `pricing_draft` 和 `pricing_selection`,与商品保存原子提交。
读取 listing 返回 `pricing_draft`、`pricing_result` 和 `pricing_valid`。
确认失败整笔回滚并定位 SKU;无报价允许保存,但发布预检返回 `PRICING_REQUIRED`。
输入变化使报价失效;已确认快照保留。具体 DTO 与错误见 pricing.md 实施修订。