130 lines
10 KiB
Markdown
130 lines
10 KiB
Markdown
# 定价试算与门店方案 API
|
||
|
||
> 2026-09-28:维护者批准 SKU 就地试算、详情弹窗、随商品草稿原子确认。本契约替换原规划的独立 `pricing-confirms` 入口。
|
||
> 稳定 DTO 位于 `packages/contracts/src/pricing.ts`;金额、汇率和比例均使用十进制字符串。
|
||
|
||
## 1. 目录
|
||
|
||
### GET `/pricing/catalog?store_id={uuid}`
|
||
|
||
需要已认证工作空间。返回 `{data: PricingCatalog}`:
|
||
|
||
- `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 `/pricing/categories?store_id={uuid}&q={text}`
|
||
|
||
检索门店平台下活动商品类目,返回 `{data:[{id,name,path}]}`,最多 50 条。`q` 最长 200 字;继续输入名称缩小范围。
|
||
|
||
## 2. POST `/pricing/quote-previews`
|
||
|
||
要求 `Idempotency-Key`。成功返回 201;同键同输入重放原响应,同键不同输入返回 409。
|
||
|
||
```json
|
||
{
|
||
"store_id": "<store UUID>",
|
||
"platform_product_category_id": "<category UUID>",
|
||
"fulfillment": "rfbs",
|
||
"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": "<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。已有正式规则禁止覆盖。
|
||
|
||
响应包含 `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 删除过期两天以上的预览。
|
||
|
||
## 3. 商品保存时确认
|
||
|
||
`PUT /products/{id}/editor-draft` 沿用商品/profile revision 的 `If-Match` 和 `Idempotency-Key`,每个 variant 增加:
|
||
|
||
```json
|
||
{
|
||
"pricing_draft": "本次 PricingParameters 对象或 null",
|
||
"pricing_selection": {"preview_id":"<UUID>","option_id":"<UUID>","input_hash":"<64-char SHA256>"}
|
||
}
|
||
```
|
||
|
||
两个字段均可省略以兼容既有客户端;读取 listing 返回 `pricing_draft,pricing_result,pricing_valid`。
|
||
|
||
确认在现有商品事务中执行:先保存草稿和新 SKU,再核对实际有效输入、用户绑定、有效期、审核规则摘要、币种及候选资格,最后创建不可变报价/候选快照并切换 listing 售价引用。
|
||
新 SKU 的 client_key 在同一事务映射到真实 ID。独立预览、其他用户或其他 SKU 的预览不能用于确认。
|
||
|
||
- 携带失效确认意图:409 `PRICING_PREVIEW_STALE`,details 定位具体 SKU,整笔保存回滚。
|
||
- 不携带确认意图:允许保存未定价草稿;输入/币种变化使报价失效。用户可在冲突弹窗明确选择“仅保存草稿,稍后定价”。
|
||
- 未变化的已确认报价继续使用原快照,无时间过期,不重新读规则或汇率。方案更新/归档不改变其价格。
|
||
- 发布校验要求 pricing_valid=true,无有效报价返回 `PRICING_REQUIRED`;已导入 SKU 沿用既有不可再次首次上架边界。
|
||
- 数据库复合外键保证 quote/profile/product/variant/store/platform 归属,RLS 隔离租户。确认快照拒绝 UPDATE;成本、包装、类目、店铺或币种变化触发失效。
|
||
|
||
## 4. 门店方案
|
||
|
||
| 方法 | 路径 | 约束 |
|
||
|---|---|---|
|
||
| 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}` |
|
||
|
||
写入 `{name,note,parameters,allow_commission_override}`。只保存费用、加价、折扣、固定/系统汇率与运输偏好;采购价、包装、手动售价及临时佣金不属于方案持久字段。
|
||
方案的默认和比较运输必须属于同一可用费率卡;正式环境仅允许已审核卡。历史快照不会被方案修改;尚未确认的预览因方案版本改变要求重算。
|
||
|
||
## 5. 数据启用与错误
|
||
|
||
迁移中的 CEL 目录为 `draft`,覆盖六种产品类型、三种运输方式,香港空运不纳入。真实账单复核、有效日期/审核人以及平台类目佣金映射仍是正式使用前置条件,接口不会回退到示例费率或固定佣金。
|
||
|
||
商品编辑联调期间,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`,直至使用已审核费率重新试算确认。佣金和汇率仍按现有输入规则处理。
|
||
|
||
### 系统管理员审核 CEL 费率
|
||
|
||
系统管理「物流费率审核」使用以下接口;全部要求登录且具备实时有效的 `system_admin_grants` 授权,普通门店用户返回 403 `SYSTEM_ADMIN_REQUIRED`:
|
||
|
||
| 方法 | 路径 | 行为 |
|
||
|---|---|---|
|
||
| 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 |
|
||
|
||
审核请求包含 `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。真实账单来源和口径仍由审核人负责确认,系统不会凭示例数字自行审核。
|
||
|
||
仅支持 Ozon/CEL 草稿版本。规则必须仍为 16 条 draft,类型为指定六种,且 `rules_hash` 与管理员查看时一致;否则返回 409/422。通过后在同一数据库事务保存不可修改的审核记录、把规则与费率卡改为 active、设置审核人与生效日期。若生效日期在未来,直到该时间才出现在卖家端可用费率中。数据库拒绝改写已审核的费率卡、产品类型、运输方式和规则;修改资费应新建版本重新审核,不覆盖已确认 SKU 报价。已启用费率不提供再次审核入口。
|
||
|
||
系统管理不会自动配置平台类目佣金映射;缺少正式佣金规则时,卖家试算仍需满足临时佣金覆盖条件。当前 CEL 种子保持 draft,尚无真实账单证据时不得提交启用。
|
||
|
||
| 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 路由和报价历史列表。
|