# 多 SKU 商品处理与试算页扩展方案 > 状态:设计方案,尚未实施。 > 范围:采集 → 采集箱商品 → `/trial/:id` 价格试算 → CSV 导出;Ozon API 直传仍处于冻结状态。 > 目标:同一商品的不同规格可以独立试算,避免以一个 SKU 的价格、重量和尺寸覆盖全部规格。 --- ## 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;它们会直接影响表结构、导出格式和后续上品方式。