Files
ozon-seller-kit/docs/contracts/product-json.md
T
2026-08-11 17:09:23 +08:00

6.1 KiB
Raw Blame History

契约:商品文件夹与 product.json

状态:设计(待确认) 上游:总体架构 §3 Ozon APIProductAPI_ImportProductsV3

插件、发布工作台、服务端三方唯一的耦合点。改这份文档等于改三端接口。


1. 文件夹结构

<商品名>/
├── product.json
├── sources.json
└── images/
    ├── main/     main-001.jpg …
    ├── sku/      sku-001-синий.jpg …
    ├── detail/   detail-001.jpg …
    └── video/    video-001.mp4

命名规则:

规则 理由
文件夹名 商品名清洗后取前 80 字符 保留可读性,避开文件系统长度限制
图片文件名 <组>-<3位序号>[-<规格名>].<ext> 序号补零保证字典序 = 展示序
规格名 保留原文(含俄文/中文),清洗非法字符 跟卖时规格名要对应回 Ozon 变体
非法字符 `< > : " / \ ? *_`

序号从 1 开始,按页面上的出现顺序,不重排。main-001 即主图第一张,通常就是 Ozon 的封面图。


2. product.json

{
  "_meta": {
    "schemaVersion": 1,
    "stage": "collected",          // collected | edited | published
    "createdAt": "2026-08-11T06:12:00Z",
    "updatedAt": "2026-08-11T06:12:00Z"
  },

  // ── Ozon 字段(对齐 ImportProductsV3)──
  "offer_id": "",                  // 自己的货号,采集阶段必空
  "name": "Термокружка детская 316",
  "description": "…",
  "description_category_id": null,
  "type_id": null,

  "price": "1290",                 // 采到的竞品价,仅参考
  "old_price": "",
  "currency_code": "RUB",
  "vat": "0",

  "depth": null, "width": null, "height": null,
  "dimension_unit": "mm",
  "weight": null,
  "weight_unit": "g",

  "images": [],                    // 发布时才填公网 URL
  "primary_image": "",
  "images360": [],
  "color_image": "",

  "attributes": [],                // 需类目字典映射,见 §4
  "complex_attributes": [],

  // ── 本地扩展字段(下划线前缀,提交 Ozon 前剥离)──
  "_images": {
    "main":   [{ "file": "images/main/main-001.jpg", "sourceUrl": "https://…", "w": 1200, "h": 1200 }],
    "sku":    [{ "file": "images/sku/sku-001-синий.jpg", "variantName": "синий", "sourceUrl": "https://…" }],
    "detail": [],
    "video":  []
  },

  "_raw": {
    "title":  "Термокружка детская 316",
    "price":  "1 290 ₽",
    "params": [{ "key": "Материал", "value": "Нержавеющая сталь" }],
    "desc":   "…",
    "sellingPoints": "…"
  },

  "_pricing": null                 // studio 计价结果,结构见 §5
}

2.1 为什么分 Ozon 字段 / _ 扩展字段

提交 Ozon 时把所有 _ 开头的键剥掉,剩下的就是请求体的 items[0]。这样避免了维护两套结构和一层映射代码。

_raw 保留采集原文:name 会被工作台改写(翻译/优化),改坏了要能回溯原始值。


3. stage 状态机

collected ──(工作台编辑)──> edited ──(发布成功)──> published
stage 谁写 必须满足
collected 插件 name 非空,_images 至少一张 main
edited 工作台 offer_iddescription_category_id、尺寸重量、_pricing 均已填
published 服务端 追加 _ozon.product_id_ozon.publishedAt

工作台导入时按 stage 决定界面:collected 走完整编辑流程,edited 直接进复核,published 只读 + 提示"已发布"。


4. attributes 的处理边界

插件不碰 attributes Ozon 的属性需要 { id, complex_id, values[{ dictionary_value_id | value }] },其中 iddictionary_value_id 都得查类目属性字典(/v1/description-category/attribute/v1/description-category/attribute/values),而字典依赖类目——采集阶段还不知道类目。

所以:

插件      → _raw.params 存原始 kv 文本
工作台    → 定类目 → 拉字典 → 映射成 attributes
服务端    → 提交前按字典校验必填项

映射交互(自动匹配 + 人工确认未匹配项)属于 studio 设计范围,见 docs/studio/


5. _pricing

复用 v1 计价器(web/js/app.js)的公式,结构对齐它现有的输出:

"_pricing": {
  "purchasePrice": 18.5,        // 进货价 ¥
  "profitRate": 30,            // 净利率 %
  "logisticsLevel": "high",    // low | high | high2
  "weightG": 320,
  "dims": { "l": 12, "w": 8, "h": 20 },
  "logisticsFee": 0,
  "fullCommission": 0,
  "totalCost": 0,
  "sellingPriceCny": 0,
  "sellingPriceRub": 0,
  "discountReserve": 50,
  "fxRate": 11.8,
  "calculatedAt": "2026-08-11T06:30:00Z"
}

字段名沿用 v1 页面里的 id 命名,方便对照。


6. sources.json

{
  "sources": [
    {
      "platform": "ozon",
      "itemId": "123456789",
      "url": "https://www.ozon.ru/product/…",
      "collectedAt": "2026-08-11T06:12:00Z",
      "counts": { "main": 6, "sku": 4, "detail": 9, "video": 0 }
    },
    {
      "platform": "1688",
      "itemId": "987654321",
      "url": "https://detail.1688.com/offer/987654321.html",
      "collectedAt": "2026-08-11T07:40:00Z",
      "counts": { "main": 5, "detail": 12 }
    }
  ],
  "dedupeKeys": ["https://cdn1.ozon.ru/…", "…"]
}

dedupeKeys 是已采集图片的归一化 URL 指纹。二次采集时插件读这个文件,命中的图标记「已收集」并默认不勾选——这是跨平台追加采集不重复的机制,纯本地实现,不需要服务端。


7. 双语言实现

位置 角色
Python server/schemas/product.py 真源Pydantic 模型 + 运行时校验
TS packages/schema/src/product.ts 手写 interface,与真源对齐
fixture packages/schema/fixtures/*.json 两端都跑一遍,防漂移

schemaVersion 变更时两端同步改,fixture 加一份新版本样例。当前 v1。