Files
atlas-seller-kit/docs/integrations/ozon-seller-api/02-category-tree.md
T
2026-09-20 15:23:44 +08:00

9.4 KiB
Raw Blame History

类目树查询 API

官方文档:https://docs.ozon.ru/api/seller/zh/#operation/DescriptionCategoryAPI_GetTree


接口信息

项 值
方法 POST
路径 /v1/description-category/tree
鉴权 需要 Client-Id + Api-Key
用途 获取 Ozon 商品类目树(选择类目后才能发布商品)

请求

请求体

{
  "language": "RU"
}

参数说明

字段 类型 必填 说明
language string 可选 语言代码:DEFAULT(默认俄文)、RU(俄文)、EN(英文)、TR(土耳其文)、ZH_HANS(简体中文)

响应

成功响应(200)

{
  "result": [
    {
      "description_category_id": 17000001,
      "category_name": "Дом и сад",
      "type_id": 0,
      "type_name": "",
      "disabled": true,
      "children": [
        {
          "description_category_id": 17000002,
          "category_name": "Сувениры и подарки",
          "type_id": 0,
          "type_name": "",
          "disabled": true,
          "children": [
            {
              "description_category_id": 17032010,
              "category_name": "Копилки",
              "type_id": 93770,
              "type_name": "Копилка",
              "disabled": false,
              "children": []
            }
          ]
        }
      ]
    }
  ]
}

字段说明

字段 类型 说明
description_category_id integer 类目 ID(发布商品时必填)
category_name string 类目名称
type_id integer 商品类型 ID(发布商品时必填,与 category_id 配对)
type_name string 商品类型名称
disabled boolean 是否可建品。true = 当前节点不可用于建品;false = 可用于建品(通常是末级类目)
children array 子类目(递归结构)

类型叶子例外:Ozon 的最后一层可能只返回 type_name、type_id、disabled 和空 children,而不重复返回 description_category_id、category_name。此时它属于最近的父类目:缓存时应继承父类目的类目 ID,展示名称使用 type_name,发布时使用“父类目 description_category_id + 叶子 type_id”。


关键约束

  1. 只有可发布的末级类目可建品:选择结果必须是没有 children 且 disabled=false 的节点
  2. 必须配对使用:发布时需同时提供 description_category_id + type_id
  3. 层级结构:children 是递归结构,类目可有三级或更深层级;不能假定只有两层,也不能只展开一层

使用场景

场景 1:前端类目选择器

① 请求类目树(language=RU,给俄文用户看)
② 递归展开树形结构
③ 所有有子节点的目录都保持可展开;不要把目录节点映射为组件的 disabled
④ 仅允许选择没有子节点且 disabled=false 的节点
⑤ 选中后保存 description_category_id + type_id

场景 2:Atlas 平台级参考缓存(目标架构,未实施)

① 按平台的定时任务用 Atlas 专用参考源凭据拉取完整类目树(Ozon 首期 `ZH_HANS`)
② 原子更新该平台共享目录;同平台门店读取同一份树,不建门店覆盖层
③ 同步失败保留上次成功版本,由后台任务重试并告警
④ 用户选类目时只查 Atlas 缓存,不频繁调 Ozon API

示例代码

Python(服务端缓存)

import httpx
from typing import List, Dict, Any

async def fetch_category_tree(
    client_id: str,
    api_key: str,
    language: str = "DEFAULT"
) -> List[Dict[str, Any]]:
    """拉取类目树并返回扁平化列表"""
    headers = {
        "Client-Id": client_id,
        "Api-Key": api_key,
        "Content-Type": "application/json"
    }
    
    async with httpx.AsyncClient(timeout=30.0) as client:
        resp = await client.post(
            "https://api-seller.ozon.ru/v1/description-category/tree",
            headers=headers,
            json={"language": language}
        )
        resp.raise_for_status()
        data = resp.json()
    
    # 递归扁平化;不要限制层级数。
    def flatten(nodes: List[Dict], level: int = 0, parent_id: int = 0):
        flat = []
        for node in nodes:
            flat.append({
                "description_category_id": node["description_category_id"],
                "parent_id": parent_id,
                "category_name": node["category_name"],
                "type_id": node["type_id"],
                "type_name": node["type_name"],
                "disabled": node["disabled"],
                "level": level,
                "lang": language
            })
            if node.get("children"):
                flat.extend(flatten(
                    node["children"],
                    level + 1,
                    node["description_category_id"]
                ))
        return flat
    
    return flatten(data.get("result", []))

TypeScript(前端选择器)

以下嵌套 CategoryNode 和 /api/categories/tree 是早期资料中的概念示例,不是当前 Atlas 联调契约。当前商品编辑器从 GET /api/product-platform-profiles/{profile_id}/category-options 读取带 parent_id 的平台级扁平节点,再在前端重建树;请以商品编辑与发布 API为准。

interface CategoryNode {
  description_category_id: number;
  category_name: string;
  type_id: number;
  type_name: string;
  disabled: boolean;
  children: CategoryNode[];
}

async function fetchCategoryTree(language = 'RU'): Promise<CategoryNode[]> {
  const resp = await fetch('/api/categories/tree?lang=' + language);
  const data = await resp.json();
  return data.result;
}

// 转为 antd Cascader 数据结构。目录永远不能设为 disabled,
// 否则用户无法继续展开到下一级。
function toTreeData(nodes: CategoryNode[]): any[] {
  return nodes.map(node => {
    const hasChildren = node.children.length > 0;
    return {
      value: `${node.description_category_id}-${node.type_id}`,
      label: node.category_name,
      isLeaf: !hasChildren,
      // 仅不可发布的叶子不可选择;目录节点必须可展开。
      disabled: !hasChildren && node.disabled,
      children: hasChildren ? toTreeData(node.children) : undefined,
      data: {
        description_category_id: node.description_category_id,
        type_id: node.type_id
      }
    };
  });
}

缓存策略

Atlas 已按 platform-product-reference-catalog.md 为每个平台分别维护共享目录:定时自动同步与系统管理员手动触发共用异步任务,失败保留上次成功版本;商品编辑器只读 Atlas 平台缓存。同平台的门店不再各自复制类目树,本阶段也不维护门店差异层。


常见问题

Q1: 类目树很大吗?

A: 完整树在一次响应中以递归 children 返回;具体节点数和响应大小以实际同步记录为准,后续从 Atlas 缓存读取。

Q2: 多久更新一次?

A: Ozon 接口未规定 Atlas 的缓存 TTL。Atlas 固定在最近一次成功后 7 天自动同步完整树,并提供系统管理员手动排队同步入口;失败不会更新成功时间或清空旧树。

Q3: 不同语言的类目树结构一样吗?

A: 接口支持语言参数,不应未经验证就假定各语言的 ID 和翻译映射完全相同。当前 Worker 使用 Ozon 平台参考源统一缓存 ZH_HANS,同平台所有门店共用。前端不能携带平台或门店密钥直连 Ozon。

Q4: type_id=0 是什么意思?

A: 目录节点通常 type_id=0,但前端不能据此把所有目录设为 disabled,否则它们无法展开。是否允许提交必须同时判断:它是叶子节点、disabled=false,并且有可用的 type_id。


Atlas 当前集成

  • Worker:使用系统后台配置的平台参考源凭据调用 Ozon,递归持久化完整 children 树及父子关系;同一平台活动任务按作用域合并。
  • API:按当前平台返回缓存的扁平节点(包含 parent_id);不会将 Client-Id 或 Api-Key 返回给浏览器。
  • 前端:根据 parent_id 重建任意深度的 Cascader;目录可展开,只有可发布的叶子能写入商品草稿。
  • 调试:若需要查看 Ozon 原始响应,应由受权限保护的服务端调试能力获取并脱敏展示;浏览器不得直接用门店密钥请求 Ozon。
  • 同步维护:完整树每 7 天自动同步,也可由系统管理员手动触发;树任务不自动展开为所有类型属性或字典任务。
  • 发布边界:当前发布预检校验所选本地类目仍活动,但尚未对树级新鲜度创建刷新任务;该边界需在真正发布 job 接入前确认或补齐。

相关文档