Files
ozon-seller-kit/docs/v2.1/multi-sku-pricing.md

173 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 多 SKU 商品处理与试算页扩展方案
> 状态:设计方案,尚未实施。
> 范围:采集 → 采集箱商品 → `/trial/:id` 价格试算 → CSV 导出;Ozon API 直传仍处于冻结状态。
> 目标:同一商品的不同规格可以独立试算,避免以一个 SKU 的价格、重量和尺寸覆盖全部规格。
> 尺寸/重量的实物与包装物流双口径设计见 [`multi-sku-measurements.md`](./multi-sku-measurements.md);该文补充并约束本方案。
---
## 1. 问题与目标
采集到的商品可能是多 SKU 商品:不同颜色、容量、套装、尺寸或型号往往有不同的进货价、包装重量和长宽高。当前试算页将这些字段保存在 `products` 的单一商品记录中,因此只能计算一个结果;若直接在现有页面逐个修改,会覆盖前一个 SKU 的数据,导出也无法还原各规格的价格。
本方案的目标是把商品拆分为「共享商品信息」和「可独立销售的 SKU」,并保持单 SKU 商品的现有操作不变:
```text
商品(SPU / 父记录)
├─ 共用:标题、俄文文案、采购地址、描述、图片、统一计价默认值
├─ SKU A:规格、采购价、重量、尺寸、独立试算结果、货号
├─ SKU B:规格、采购价、重量、尺寸、独立试算结果、货号
└─ SKU C:规格、采购价、重量、尺寸、独立试算结果、货号
```
不应从图片名或规格文案中猜测重量、尺寸和价格。采集不到的值必须保留为空、继承共用默认值,或要求人工补齐。
## 2. 当前现状与限制
目前的 `/trial/:id` 以一条 `products` 记录作为一个可售单元:
- `price`、`old_price`、`weight`、`depth/width/height`、`offer_id` 各只有一个值;
- `product.pricing` 保存一份试算输入和结果,修改后会回填上述商品字段;
- CSV 导出按商品生成一行,无法表达同一商品的多个独立报价;
- 采集侧已能识别 SKU 名称和 SKU 图片;1688 采集状态中也可能含 SKU 价格或规格信息,但当前上报 `/api/materials` 时尚未稳定传递 SKU 级的价格、重量和尺寸。
因此,不能只在现有「02 价格试算」中添加一张前端表格:那样数据仍会落回一份商品字段,刷新、导出和后续上架都会丢失区分。
## 3. 业务边界
### 3.1 父商品与 SKU 的职责
| 层级 | 保存内容 | 原则 |
|---|---|---|
| 商品(父记录) | 标题、描述、采购链接、参数、共用图片、AI 套图、文案、计价默认值 | 一处维护,供所有 SKU 继承 |
| SKU(子记录) | 规格名/规格值、来源 SKU 标识、SKU 图片、进货价、重量、尺寸、物流等级覆盖、货号、独立计价结果、启用状态 | 每个可单独售卖的规格一条 |
仅颜色不同且包装、进货价相同的 SKU 可以完全继承父商品参数;容量、套装数或尺寸不同的 SKU 通常需要各自维护价格和包装数据。
### 3.2 不在本阶段解决的问题
- 不恢复 Ozon Seller API 自动发布;V2.1 当前仍以 CSV / 组合码辅助人工上品为主。
- 不强行将平台的变体关系映射为 Ozon 的变体关系。是否将每个 SKU 建成独立 Ozon offer,需要在导出/上架阶段单独确认。
- 不把 AI 套图按 SKU 数量机械复制。图片规划应以商品卖点和规格差异为依据;多 SKU 图片选择规则另行细化。
## 4. 推荐数据模型
推荐新增持久化的 `product_variants`(或语义等价的 `product_skus`)表,而不是长期将全部 SKU 塞入 `product.pricing` JSON。独立表可以支持单行导出、筛选、校验、后续独立 offer 和稳定的外部 SKU 映射。
建议字段如下:
| 字段 | 用途 |
|---|---|
| `id`、`product_id` | SKU 主键及所属父商品 |
| `source_variant_key` | 来源平台的稳定 SKU 标识;不能只用展示名称匹配 |
| `variant_name`、`specs` | 规格展示名和结构化规格值,如颜色/尺寸/容量 |
| `source_price` / `purchase_price` | 采集到的价格与人工确认后的进货价 |
| `weight_g`、`length_cm`、`width_cm`、`height_cm` | SKU 包装数据;为空时继承父商品默认值 |
| `logistics_level`、`td_price`、`profit_rate` 等覆盖字段 | 仅保存与父商品默认值不同的项 |
| `pricing` | 该 SKU 的计算快照、告警和 `calculated_at` |
| `offer_id`、`barcode` | 导出/人工上品所需的 SKU 级标识 |
| `image_asset_id`、`enabled`、时间戳 | SKU 主图、是否参与试算/导出和审计 |
父商品的 `pricing` 应转为「默认计价参数」,例如汇率、默认利润率、贴单费和物流等级。SKU 计算时按「SKU 覆盖值 → 父商品默认值」合并,再复用现有 `calculatePricing` 和尺寸校验逻辑。
### 为什么不建议仅扩展 JSON
短期可在 `product.pricing.variants` 中暂存数组,但这会让查询、校验、并发更新、CSV 行导出和后续 offer 对应关系都更复杂。若只是验证交互可暂用 JSON;只要计划实际导出多个 SKU,就应迁移到独立表。
## 5. 采集与接口契约
采集上报需要在保留现有单 SKU 字段的同时,可选地提交 `variants`:
```jsonc
{
"source_url": "...",
"title": "...",
"variants": [
{
"source_variant_key": "平台原始 SKU ID",
"variant_name": "白色 / 33 cm",
"specs": { "颜色": "白色", "尺寸": "33 cm" },
"source_price": 28.5,
"weight_g": null,
"dimensions_cm": { "l": null, "w": null, "h": null },
"image_url": "..."
}
]
}
```
约束:
- `source_variant_key` 优先使用平台原始 ID;显示名称可能重复,不能作为唯一键。
- 不同平台可分步补齐字段。未采到的值传 `null`,不伪造默认值。
- 服务端根据来源 SKU 标识做幂等更新;没有 `variants` 的历史采集商品继续按单 SKU 处理。
- 采集图与 SKU 的关系应单独保存,避免仅靠图名在后续页面猜测归属。
## 6. 试算页交互
现有「02 价格试算」建议扩展为两层,单 SKU 商品默认保持当前单卡片体验:
1. **通用参数**:汇率、默认净利率、贴单费、默认物流等级、划线价倍数等,修改后可批量应用到选中的 SKU。
2. **SKU 明细**:每行显示规格、SKU 图片、进货价、重量、长宽高、物流等级、销售价 ¥/₽、划线价、利润和校验告警;可单行编辑、恢复继承、批量填充或只计算已选 SKU。
建议交互规则:
- 值为空表示「继承商品默认值」,而不是零;界面须明确显示继承来源。
- 修改父级默认值只影响未覆盖该字段的 SKU。
- 某 SKU 尺寸/价格无效时,只阻止该 SKU 的结果和导出,不应影响其他 SKU。
- SKU 可被停用;停用项保留历史但不参与默认计算和导出。
- 父商品顶部不应再把任意一个 SKU 的报价写回 `product.price`。过渡期可展示「¥最低价起」或显式指定一个代表 SKU;具体语义必须固定,避免误导。
## 7. 计算、导出与人工上品
每个启用 SKU 独立调用既有计价公式与尺寸校验,使用同一汇率快照。计算结果应与输入一起持久化,便于追溯导出时的报价来源。
CSV 导出应从「每商品一行」改为「每启用 SKU 一行」:
| CSV 字段 | 来源 |
|---|---|
| 商品共用标题、描述、采购地址 | 父商品 |
| 规格、货号、条码 | SKU |
| 进货价、重量、尺寸、物流等级 | SKU 覆盖值或继承后的有效值 |
| 销售价、划线价、利润、告警状态 | SKU 最新试算结果 |
导出前需校验每个启用 SKU 都有货号、有效价格和有效包装数据;失败项应列出原因,不能静默使用父商品或其他 SKU 的结果。
对于 Ozon 人工上品,必须先确认商业策略:
- **每个 SKU 一个 Ozon offer**:CSV 每个 SKU 一行,最易与独立价格和物流数据对应;
- **只上一个代表 SKU**:在试算页显式选择代表 SKU,其余 SKU 仅作采购参考;
- **平台变体合并**:需要重新启用并完成 Ozon 变体属性映射,不属于 V2.1 当前导出主链路。
在策略确认前,系统只应生成可审阅的 SKU 级试算与 CSV,不自动假定平台变体关系。
## 8. 兼容与迁移
- 既有商品无 `product_variants` 时视为一个隐式 SKU,继续使用当前页面和导出逻辑。
- 新采集的多 SKU 商品创建父商品及 SKU 子记录;可选地将第一个/最低价 SKU 作为界面初始代表,但不覆盖其余数据。
- 迁移时把历史 `product.pricing`、重量、尺寸、`offer_id` 复制为一条默认 SKU;保留父商品字段直到所有读取路径完成切换。
- 试算接口和前端应同时兼容单 SKU、隐式 SKU 和已迁移多 SKU,避免一次性迁移造成历史商品不可用。
## 9. 分阶段实施与验收
| 阶段 | 内容 | 验收标准 |
|---|---|---|
| 0. 业务确认 | 确认 Ozon 上品策略、货号规则、何时需要 SKU 独立包装数据 | 形成明确的 offer/CSV 规则 |
| 1. 数据与采集 | 增加 SKU 表、迁移与 `/api/materials` 的 `variants` 契约;1688 优先接入 | 多 SKU 采集后能稳定落库,缺失字段不被伪造 |
| 2. 试算页 | 增加通用参数和 SKU 明细表,逐行计算/校验/保存 | 两个 SKU 互不覆盖;刷新后数据和告警一致 |
| 3. 导出 | CSV 按启用 SKU 出行,提供失败明细 | 多 SKU 商品导出行数、货号、价格和尺寸与明细一致 |
| 4. 后续发布 | 如恢复 API 发布,再设计 Ozon 变体/offer 映射 | 不影响 V2.1 的人工 CSV 主流程 |
至少覆盖以下测试:单 SKU 回归、多 SKU 不同价格、仅颜色不同且继承包装、缺重量/尺寸、一个 SKU 校验失败但其他可导出、来源 SKU 改名但稳定 ID 未变、历史商品迁移。
## 10. 待确认决策
1. Ozon 后台中,多 SKU 是否一律拆成独立 offer,还是需要保留变体合并能力?
2. 货号由父商品后缀自动派生,还是必须人工逐 SKU 录入?
3. 哪些采集来源可视为 SKU 级重量/尺寸可信,哪些仅作为商品级参考?
4. 多 SKU 的 AI 套图是否要指定代表 SKU,或按 SKU 分组生成?
5. 父商品列表中价格展示为最低价、价格区间,还是指定代表 SKU 的价格?
这些决策确定后,再开始阶段 1;它们会直接影响表结构、导出格式和后续上品方式。