10 KiB
多 SKU 的实物规格与包装物流规格设计
状态:调研与设计结论,尚未实施。 关联:多 SKU 商品处理与试算页扩展方案。本文补充其“尺寸与重量”的双口径模型。 核心原则:每个 SKU 可以有独立的实物规格和包装物流规格;两者绝不混用。
1. 问题与结论
尺寸与重量有两种不同的业务口径:
| 口径 | 数据 | 用途 |
|---|---|---|
| 实物规格 | 商品本体长、宽、高、净重 | 商品规格展示、俄文文案、AI 尺寸标注图 |
| 包装物流规格 | 发货包装长、宽、高、含包装毛重 | 物流尺寸限制、物流费、价格试算、CSV 上品 |
同一个商品还可能有多个 SKU。容量、尺寸、套装数等 SKU 往往在两组数据上都不同;仅颜色不同的 SKU 则通常可继承父商品的两组默认值。
因此目标模型不是“商品加一张 SKU 价格表”,而是:
商品(SPU / 父记录)
├─ 共用:标题、描述、采购链接、图片、文案、计价默认值
├─ 默认实物规格:长宽高、净重
├─ 默认包装物流规格:长宽高、包装毛重
├─ SKU A:规格、两组规格覆盖、采购价、试算结果、货号
├─ SKU B:规格、两组规格覆盖、采购价、试算结果、货号
└─ SKU C:规格、两组规格覆盖、采购价、试算结果、货号
SKU 的某个值为空代表继承父商品默认值,不代表零。采集不到或来源未说明口径的“尺寸/重量”必须标记为待确认,不能猜测写入任意一组。
2. 当前实现风险
当前 /trial/:id 将 price、old_price、weight、depth/width/height、offer_id 放在单一 products 记录中:
TrialPricingPanel使用重量尺寸做物流费和尺寸校验,且会回填商品价格;一个 SKU 会覆盖另一个 SKU。- AI 套图规划从采集参数表寻找“尺寸”,无法确认取到的是实物尺寸还是包装尺寸。
/api/materials的_apply_weight_dims以“包装重量/重量”“包装长度/长度”等泛关键词解析,通用“产品尺寸”“重量”可能被当成物流规格。- 图片资产仅保存
variant_name,显示名能重复或改名,无法稳定绑定 SKU。 - CSV 目前按商品一行导出,无法表达 SKU 的独立包装数据和报价。
因此不能只给当前价格卡增加输入框,必须先建立 SKU 与双规格的持久化模型。
3. 数据模型
3.1 命名、单位和继承
数据库统一使用 mm、g;前端用 cm、g 编辑。新功能不得再用裸 weight、depth、width、height 表达业务规格。
父商品和 product_variants 均使用相同、口径明确的字段:
| 字段组 | 字段 | 使用者 |
|---|---|---|
| 实物规格 | physical_length_mm、physical_width_mm、physical_height_mm、net_weight_g |
尺寸图、规格展示、文案 |
| 包装物流规格 | package_length_mm、package_width_mm、package_height_mm、shipping_weight_g |
试算、物流校验、导出 |
| SKU 身份 | source_variant_key、variant_name、specs |
平台原始 SKU ID、展示规格、颜色/尺寸/容量 |
| 销售与计价 | source_price、purchase_price、offer_id、barcode、logistics_level、pricing |
SKU 独立的输入、结果和上品标识 |
| 审计 | measurements_meta、enabled、时间戳 |
来源、原始文本、确认状态、是否参与导出 |
measurements_meta 至少记录两组数据各自的 status(confirmed / ambiguous / missing)、来源、原始文本和更新时间。采集提供的数据不是人工确认的事实时,不应伪装为已确认。
SKU 的有效值按“SKU 覆盖 → 父商品默认值”逐字段合并。实物规格与包装物流规格独立继承:实物尺寸相同不代表包装尺寸也相同。
3.2 关联和旧字段
- 新增持久化
product_variants表,而不是长期把 SKU 数组放进product.pricingJSON。 product_assets增加可空product_variant_id;现有variant_name仅保留展示兼容。- 父商品
pricing只保存共享默认参数,如汇率、利润率、贴单费和默认物流等级;SKUpricing保存输入快照、有效包装规格、计算结果和计算时间。 - 当前
products.depth/width/height/weight在迁移期间仅是兼容读写别名;双规格字段是新功能唯一事实来源。
4. 采集和后端接口
POST /api/materials 保持旧契约兼容,并新增可选 variants:
{
"source": { "platform": "1688", "itemId": "...", "url": "..." },
"variants": [
{
"source_variant_key": "平台原始 SKU ID",
"variant_name": "白色 / 33 cm",
"specs": { "颜色": "白色", "尺寸": "33 cm" },
"source_price": 28.5,
"physical_measurements": {
"length_mm": 150, "width_mm": 150, "height_mm": 330, "net_weight_g": null,
"status": "confirmed"
},
"package_measurements": {
"length_mm": 180, "width_mm": 180, "height_mm": 380, "shipping_weight_g": 850,
"status": "confirmed"
},
"image_url": "..."
}
]
}
规则:
source_variant_key优先使用来源平台稳定 ID;展示名不能作为幂等键。- 未采到的字段传
null;无法判断口径时保留原始参数、设status: "ambiguous"。 - 只有明确“包装/外箱/发货”口径的参数可写入包装物流规格;明确“商品/产品/本体”的参数可写入实物规格;泛称参数不自动落入任何组。
- 服务端按来源 SKU ID 幂等更新,并把 SKU 图片关联至
product_variant_id。 - SKU 使用独立接口,例如
GET/POST /api/products/{id}/variants、PATCH/DELETE /api/products/{id}/variants/{variant_id};不要用整份ProductUpdate承担 SKU 并发编辑。
服务端新增统一的 PricingService:前端可即时预览,但保存与导出时必须按“有效包装物流规格”重新计算,避免浏览器传入结果被篡改或前后端公式漂移。
5. 前端设计
页面拆成两个清晰区域:
- 商品规格与尺寸图:维护实物长宽高和净重;多 SKU 时可维护商品默认值,或为一个 SKU 覆盖。尺寸标注图唯一读取这里的已确认实物尺寸。
- 价格试算:维护包装长宽高、包装毛重、进货价、利润率、物流等级、贴单费、汇率;物流校验与价格结果只在此区域展示。
多 SKU 时,价格区使用明细表,列为“规格 / 实物规格摘要 / 包装规格摘要 / 进货价 / 销售价 / 状态”。点击一行打开抽屉或展开编辑器,而不是把八项字段强塞进表格:
实物规格(用于尺寸图) 包装物流规格(用于试算)
长 / 宽 / 高 / 净重 长 / 宽 / 高 / 包装毛重
状态:SKU 覆盖 / 继承 / 待确认 状态:SKU 覆盖 / 继承 / 待确认
交互约束:
- 每个字段可以“恢复继承”;空值不是
0。 - 修改父商品默认值只影响未覆盖字段;可以将某一组默认规格批量应用给选中的 SKU。
- 包装物流规格缺失/无效时,该 SKU 不能试算和导出,但其他 SKU 不受影响。
- 实物规格缺失时仍可试算/导出,但不能生成有数值的尺寸标注图。
- 父商品列表不应任选一个 SKU 回写
product.price;应展示最低价、价格区间,或显式选择代表 SKU。
6. AI 尺寸图、导出与迁移
AI 尺寸图
尺寸标注图是实物信息,不是物流信息。规划器、提示词和任务上下文必须接收显式 physical_* 字段,不能从混合原始参数表兜底。
- 多 SKU 实物尺寸不同:生成前必须选择 SKU;任务保存
product_variant_id与实物规格快照。 - 多 SKU 实物尺寸相同:可以使用父商品默认规格生成一张通用图。
- 实物规格为
missing或ambiguous:规划器不安排尺寸图,也不能提交带数值的尺寸图任务。 - 规格修改后,历史图依其快照标记为“基于旧规格生成”。
CSV 和上品
CSV 从“每商品一行”改为“每启用 SKU 一行”。进货价、包装毛重、包装尺寸和物流等级来自合并后的 SKU 有效值;实物尺寸绝不作为物流尺寸导出。导出前逐 SKU 校验货号、价格和包装物流规格,并列出失败原因。
每 SKU 一个 Ozon offer、只上代表 SKU、或未来平台变体合并,仍是待确认的商业策略;未确认前系统只生成可审阅的 SKU 级 CSV。
历史数据迁移
历史商品无 SKU 时视为一个隐式 SKU。把旧 pricing、weight、depth/width/height、offer_id 迁为默认 SKU 的包装物流规格,因为它们原本用于价格试算;实物规格一律为 missing,不得依据旧字段自动生成尺寸图。
7. 实施顺序与验收
| 阶段 | 内容 | 验收标准 |
|---|---|---|
| 0. 业务确认 | 明确 Ozon offer 策略、货号规则、尺寸图是否逐 SKU 生成 | 形成 CSV/上品规则 |
| 1. 数据和采集 | SKU 表、父/SKU 双规格字段、迁移、variants 契约;收窄泛规格解析 |
未知口径不被伪造成实物或包装数据 |
| 2. 服务端计算 | 继承合并、统一计算/校验、SKU 级保存和查询接口 | 两 SKU、两组规格不相互覆盖 |
| 3. 试算页 | 实物规格区、包装物流试算区、SKU 表和编辑抽屉 | 刷新后继承、状态、价格和告警一致 |
| 4. 导出和图片 | SKU 行 CSV;尺寸图上下文绑定 SKU 与实物快照 | 包装尺寸绝不进入尺寸图 |
至少覆盖:单 SKU 回归、多 SKU 不同价格、仅颜色不同继承两组规格、实物相同包装不同、包装相同实物不同、泛称规格待确认、一个 SKU 导出失败但其他可导出、尺寸图只使用实物规格、SKU 改名但稳定 ID 不变、历史商品迁移。
8. 待确认决策
- Ozon 中多 SKU 是否一律拆成独立 offer,还是要保留变体合并能力?
- 货号由父商品自动派生,还是必须人工逐 SKU 维护?
- 哪些采集来源的实物/包装字段可信,哪些只能作为待确认原始信息?
- 尺寸图是否必须逐 SKU 生成;AI 主图和场景图由谁指定代表 SKU?
- 父商品在采集箱中展示最低价、价格区间,还是代表 SKU 的价格?