8.9 KiB
8.9 KiB
V2 Ozon 发布集成
状态:方案设计(待确认) 上游:V2 总览 · V2 架构 · 数据库 · API 官方文档:Ozon Seller API(中文)
1. 鉴权与店铺凭证
Ozon Seller API 用 两个请求头 鉴权(不是 OAuth):
Client-Id: <你的 Client ID>
Api-Key: <你的 API Key>
- 获取:Ozon 卖家后台 → 设置 → Seller API → 生成 Key(可选权限级别)。
- 凭证归属店铺:V2 里每店铺一条
shops记录,client_id/api_key加密落库,调用时解密拼头。 - 连通性校验:
POST /api/shops/:id/test调/v1/roles(返回该 key 的角色与可用方法),既验证凭证又看权限范围,零成本。
服务端封装 services/ozon_client.py:统一 base URL(https://api-seller.ozon.ru)、拼头、超时、错误映射(400/403/409/500 → 语义化 detail)、限流退避。
2. 核心接口(本项目用到)
| 用途 | 方法 | 说明 |
|---|---|---|
| 类目树 | POST /v1/description-category/tree |
返回 description_category_id / type_id / category_name / type_name / disabled / children;只有末级类目可建品 |
| 类目属性 | POST /v1/description-category/attribute |
入参 description_category_id + type_id;返回属性含 is_required / is_aspect / is_collection / dictionary_id / type / max_value_count |
| 属性值字典 | POST /v1/description-category/attribute/values |
入参 attribute_id + category_id + type_id + limit(≤2000) + last_value_id(分页) |
| 属性值搜索 | POST /v1/description-category/attribute/values/search |
按 value 模糊匹配参考值(≥2 字符,limit≤100) |
| 发布/更新商品 | POST /v3/product/import |
一次 ≤100 个 item;返回 task_id |
| 发布状态 | POST /v1/product/import/info |
入参 task_id;返回 items[{offer_id, product_id, status, errors[]}] |
| 商品列表/回填 | POST /v3/product/list |
用 offer_id/product_id 过滤取 product_id,或分页拉全部 |
| 跟卖复制 PDP | POST /v1/product/import-by-sku |
入参 sku + name + offer_id + price...;返回 task_id + unmatched_sku_list |
| 图片更新 | POST /v1/product/pictures/import |
按 product_id 覆盖 images/images360/color_image |
| 商品详情(含图片/审核错误) | POST /v3/product/info/list |
回读已发布商品的图片/状态/错误 |
3. 类目与属性字典(采集属性 → Ozon 属性的关键)
3.1 数据流
类目树(全局缓存)
└─ 用户选类目 → 得 description_category_id + type_id
└─ 拉该类目属性(按 category+type 缓存)
└─ 对每个「有字典」的属性,按需拉值(/values 或 /values/search)
- 类目树全局缓存:与店铺无关(虽然接口要凭证),服务端拉一次存
category_tree表 + 内存 LRU,TTL 24h。 - 属性按类目缓存:
category_attributes表,按(category_id, type_id)缓存。 - 属性值按需拉取:值目录可能非常大,只在用户映射到某个属性时才拉,且用
/values/search(按关键词搜)而非全量拉。
3.2 属性映射(采集的 raw.params → Ozon attributes[])
这是发布链路最重的工作。流程:
采集 raw.params: [{key:"Материал", value:"Нержавеющая сталь"}, …]
│ ① 自动匹配:key 与属性 name 模糊匹配(归一化 + 词干)
│ ② 有字典的属性:value 去 /values/search 找 dictionary_value_id
▼
属性映射 UI:自动匹配结果 + 人工确认未匹配项 + 必填项高亮
▼
products.attributes = [{complex_id:0, id, values:[{dictionary_value_id, value}]}]
必填项校验:服务端在「ready 校验」和「发布前」两次校验:category_attributes 里 is_required=true 的属性必须已映射,否则阻断发布并指出缺哪些。
4. 发布请求体组装(对齐 ImportProductsV3)
items[0] 字段(已核对官方示例):
| 字段 | 来源 | 说明 |
|---|---|---|
| offer_id | products.offer_id |
自己的货号,跟卖不能用竞品的 |
| name / description | products.name/description(俄文) |
|
| description_category_id / type_id | products.* |
从类目树选 |
| price / old_price / currency_code / vat | products.* |
currency 须与店铺设置一致(默认 RUB) |
| depth/width/height/dimension_unit/weight/weight_unit | products.* |
必填且不能为 0(官方硬约束) |
| barcode | products.barcode |
可选 |
| images | products.images(七牛 URL,≤15) |
顺序即展示顺序;首张为主图。必须 https 直链(实测 Ozon 不接受 http,见 §7) |
| primary_image | products.primary_image |
用 primary_image 则 images ≤14 |
| images360 / color_image | products.* |
可选 |
| attributes | products.attributes |
映射结果 |
| complex_attributes | products.complex_attributes |
视频/尺码表等 |
| pdf_list / promotions | 可选 | 一般留空 |
跟卖场景可选优化:若竞品允许复制 PDP,走 /v1/product/import-by-sku(只需 sku + 基本信息),更快且继承竞品详情——但受「卖家是否允许复制」限制,且不能更新,故作为可选快捷路径,主路径仍是 import。
5. 发布状态机与轮询
/v3/product/import 是异步的,返回 task_id。流程:
POST /v3/product/import → { task_id }
│ 建 publish_tasks(status=pending, ozon_task_id)
▼
后台协程轮询 POST /v1/product/import/info { task_id }
│ items[0].status ∈ imported | moderation | failed(+errors[])
▼
imported → products.stage=published, ozon_product_id=items[0].product_id
moderation → products.stage=publishing(继续轮询,通常 <1 天)
failed → products.stage=failed, publish_tasks.errors=items[0].errors
- 轮询间隔:先 5s,退避到 30s;
moderation状态降低频率到分钟级。 - 用
/v3/product/list(filter by offer_id)回读product_id兜底(轮询遗漏时)。 - 上架还需设置库存:
import成功后商品进入后台但不自动上架(architecture.md与官方文档均明确「只有设置库存后才开售」)。V2 一期发布到「已创建/审核」即可,库存设置(/v2/products/stocks)作为二期可选,或提示用户去后台补库存。
6. CSV 导出字段
服务端 GET /api/export/products.csv,带 BOM 的 UTF-8,Excel 直接打开不乱码。字段:
offer_id, product_id, name, description_category_id, type_id,
price, old_price, currency_code, vat,
weight, weight_unit, depth, width, height, dimension_unit,
barcode, primary_image, images, source_platform, source_item_id, source_url,
stage, published_at, created_at, updated_at
images用|拼接七牛 URL。- 未发布商品
product_id为空。 - 支持筛选
stage(collected/ready/published/failed/全部)与ids(勾选导出)。
对齐 V1:v1 登记表导出的
sku + 卢布预留价组合码,V2 里offer_id + price(卢布)即等价物;若要完全兼容 v1 组合码,可加一列combo(offer_id + 卢布预留价)。
7. 错误处理与限流
| Ozon 错误 | 含义 | 处理 |
|---|---|---|
| 400 Invalid parameter | 参数错误 | 把 detail 透传前端,定位字段 |
| 403 Access denied | 权限不足 | 提示检查 Api-Key 权限级别 |
| 409 Request conflict | 冲突(如 offer_id 重复) | 提示改 offer_id 或走更新 |
| 429 / 限流 | 频率超限 | 指数退避重试 |
item_limit_exceeded |
超过当日建/更新商品限额 | 提示限额,可查 /v4/product/info/limit |
publish_tasks.errors 完整保存 Ozon 返回的 errors 数组,前端发布结果页展示中文解读。
8. 店铺绑定交互
- 店铺管理页「新增店铺」:填
名称 + Client ID + API Key + 结算币种。 - 点「测试连接」→
/api/shops/:id/test→ 调/v1/roles→ 显示ok与角色列表,或失败原因(凭证错/权限不足/网络)。 - 保存后
client_id只显示尾号打码(如…1234),key 永不回显。 - 发布时从店铺下拉选择目标店铺。
9. 一期范围 vs 二期
| 能力 | 一期 | 二期 |
|---|---|---|
| 店铺绑定 + 连通性校验 | ✅ | |
| 类目树 + 属性 + 值字典(缓存) | ✅ | |
| 属性映射 UI(自动 + 人工) | ✅ 基础版 | 智能匹配优化 |
/v3/product/import 发布 + 轮询回填 |
✅ | |
/v1/product/import-by-sku 跟卖复制 |
🟡 可选 | |
| 库存设置(上架) | ❌(提示去后台) | /v2/products/stocks |
| 价格/库存批量更新 | ❌ | /v1/product/import/prices、/v2/products/stocks |
| 图片更新(换图) | ❌ | /v1/product/pictures/import |