×

京东JOS宙斯迁移实录:item.jd.get → jingdong.item.read.get 平滑切(2026版)(附Python源码)

万邦科技Lex 万邦科技Lex 发表于2026-08-03 11:41:17 浏览24 评论0

抢沙发发表评论

2026年6月24日京东发布《关于宙斯开发者中心融合迁移的通知》:宙斯开发者中心于2026年7月1日融合至京东商家开放平台,原宙斯官网及控制台2026年8月30日前关闭。这意味着两件事:一是应用创建入口统一收口到 https://open.jd.com ;二是老接口 item.jd.get(及 360buy.ware.getjingdong.ware.get)进入维护末期,新应用必须走 jingdong.item.read.get 等新版只读接口
本文给你一份可直接落地的迁移实录:从老接口到新接口的兼容封装、参数映射、返回结构差异、灰度切换策略,以及2026年宙斯融合后的新入口注意事项。

一、为什么必须切:2026的两个硬理由

理由1:宙斯平台入口已迁移

时间节点
事件
2026-06-25前
宙斯停止新增应用创建
2026-07-01
宙斯整体融合至京东商家开放平台
2026-08-30前
宙斯原域名直接跳转至京东商家开放平台,官网及控制台正式关闭
⚠️ 8月30日之后,jos.jd.com 原控制台将无法独立访问。存量的应用可在京东商家开放平台后台继续运维,但新接入方必须走 open.jd.com

理由2:老接口停止新申请

item.jd.get / 360buy.ware.get / jingdong.ware.get 这一批老商品读取接口已进入维护期,新应用不再支持申请,官方推荐统一迁移至 jingdong.item.read.get

二、老 vs 新接口对照

维度
老接口 item.jd.get
新接口 jingdong.item.read.get
功能定位
商品详情查询(旧宙斯)
商品基础信息只读(新版标准化)
Method
item.jd.get / jd.item.get
jingdong.item.read.get
网关
https://api.jd.com/routerjson
完全相同
签名算法
MD5(AppSecret+KV_ASCII+AppSecret)
完全相同(一行不改)
必传参数
skuIditemId + fields
skuIditemId + fields
批量能力
不支持
支持 sku_ids 批量,最多20个
返回根节点
item_get_response / jingdong_ware_get_response
jingdong_item_read_get_response
QPS
个人2/s,企业5~50/s
相同
权限申请入口
宙斯控制台(已迁移)
京东商家开放平台 → 应用 → 接口权限
迁移量评估:网关、签名、AccessToken、鉴权流程一行不动,只需要改 method 字符串 + 微调 fields + 适配返回根节点。代码改动量极小,真正的坑在返回结构解析批量能力利用上。

三、参数与返回结构的关键差异

入参变化

老:item.jd.get
  skuId (Long)      二选一必填
  itemId (Long)     二选一必填
  fields (String)   可选,逗号分隔

新:jingdong.item.read.get
  skuId (Long)      单查必填
  sku_ids (String)  批量必填,逗号分隔,≤20个
  itemId (Long)     可选
  fields (String)   可选,推荐显式指定

推荐 fields(补全库存与SKU)

sku_id,item_id,title,brand_info,category_info,
price_info,stock_info,sku_list,image_list,
sales_info,shop_info,promotion_info

返回结构差异(重点)

老接口返回:
{
  "item_get_response": {
    "code": 200,
    "message": "success",
    "data": { "skuId": "...", "title": "..." }
  }
}
新接口返回:
{
  "jingdong_item_read_get_response": {
    "code": 200,
    "message": "success",
    "data": {
      "sku_id": "100012345678",
      "item_id": "100012345678",
      "title": "2026夏季新款纯棉透气短袖T恤",
      "price_info": { "original_price": "129.00", "promotion_price": "59.00" },
      "stock_info": { "stock_num": 320, "is_available": true },
      "sku_list": [ { "sku_id": "...", "price": "59.00", "stock_num": 85 } ]
    }
  }
}
💡 新接口返回字段命名从驼峰转为下划线风格,sku_list 内的 stock_num 才是真实库存,解析层必须双写兼容

四、Python源码:兼容封装 + 平滑迁移

下面这份代码做了三件事:
  1. MD5签名严格按京东JOS规范:AppSecret + KV_ASCII_sorted + AppSecret

  2. method可切换:通过 use_new_api 参数一键切新老接口

  3. 返回结构自适应:自动识别老/新根节点

import hashlib
import json
import time
import requests
from typing import Optional, Dict, Any, List

class JdItemReadClient:
    """
    京东商品详情API客户端
    兼容老接口 item.jd.get / jd.item.get
    新接口 jingdong.item.read.get
    """

    GW_PROD = "https://api.jd.com/routerjson"
    GW_SANDBOX = "https://api.sandbox.jd.com/routerjson"

    def __init__(self, app_key: str, app_secret: str, sandbox: bool = False):
        self.app_key = app_key
        self.app_secret = app_secret
        self.gw = self.GW_SANDBOX if sandbox else self.GW_PROD

    # ─────────────────────────────────────────────
    # 1. JOS MD5 签名(标准规范)
    # ─────────────────────────────────────────────
    def _sign(self, params: Dict[str, Any]) -> str:
        """
        签名规则:
        1. 过滤掉 sign 和空值
        2. 按 key ASCII 升序排序
        3. AppSecret + 拼接串 + AppSecret
        4. MD5 加密后转大写
        """
        filtered = sorted(
            (k, v) for k, v in params.items()
            if k != "sign" and v is not None and str(v).strip() != ""
        )
        query = self.app_secret
        for k, v in filtered:
            query += f"{k}{v}"
        query += self.app_secret
        return hashlib.md5(query.encode("utf-8")).hexdigest().upper()

    # ─────────────────────────────────────────────
    # 2. 通用调用
    # ─────────────────────────────────────────────
    def _execute(self, method: str, biz_params: Dict[str, Any],
                 access_token: Optional[str] = None) -> Dict[str, Any]:
        sys_params = {
            "app_key": self.app_key,
            "method": method,
            "timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
            "format": "json",
            "v": "2.0",
            "sign_method": "md5",
        }
        if access_token:
            sys_params["access_token"] = access_token

        # 业务参数序列化为 360buy_param_json
        all_params = dict(sys_params)
        all_params["360buy_param_json"] = json.dumps(
            biz_params, ensure_ascii=False, separators=(",", ":")
        )

        all_params["sign"] = self._sign(all_params)

        resp = requests.post(self.gw, data=all_params, timeout=15)
        resp.raise_for_status()
        return resp.json()

    # ─────────────────────────────────────────────
    # 3. 兼容封装:单商品查询
    # ─────────────────────────────────────────────
    def get_item(
        self,
        access_token: str,
        *,
        sku_id: Optional[str] = None,
        item_id: Optional[str] = None,
        fields: Optional[str] = None,
        use_new_api: bool = True,
    ) -> Dict[str, Any]:
        """
        单商品详情查询
        老: item.jd.get / jd.item.get
        新: jingdong.item.read.get(推荐)
        """
        if not sku_id and not item_id:
            raise ValueError("sku_id 或 item_id 至少传一个")

        if use_new_api:
            method = "jingdong.item.read.get"
            biz = {}
            if sku_id:
                biz["skuId"] = sku_id
            if item_id:
                biz["itemId"] = item_id
            if fields:
                biz["fields"] = fields
            else:
                # 新接口推荐显式指定 fields,补全库存与SKU
                biz["fields"] = (
                    "sku_id,item_id,title,brand_info,category_info,"
                    "price_info,stock_info,sku_list,image_list,"
                    "sales_info,shop_info,promotion_info"
                )
        else:
            method = "item.jd.get"
            biz = {}
            if sku_id:
                biz["skuId"] = sku_id
            if item_id:
                biz["itemId"] = item_id
            if fields:
                biz["fields"] = fields

        return self._execute(method, biz, access_token)

    # ─────────────────────────────────────────────
    # 4. 新接口独占能力:批量查询(≤20个)
    # ─────────────────────────────────────────────
    def get_items_batch(
        self,
        access_token: str,
        sku_ids: List[str],
        fields: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        批量商品查询(新接口独有)
        sku_ids: 最多20个,逗号分隔
        """
        if len(sku_ids) > 20:
            raise ValueError("sku_ids 最多20个")

        biz = {
            "sku_ids": ",".join(sku_ids),
            "fields": fields or (
                "sku_id,title,price_info,stock_info,sku_list,image_list"
            ),
        }
        return self._execute("jingdong.item.read.get", biz, access_token)

    # ─────────────────────────────────────────────
    # 5. 返回结构解析(兼容新老根节点)
    # ─────────────────────────────────────────────
    @staticmethod
    def extract_item_data(raw: Dict[str, Any]) -> Dict[str, Any]:
        """
        从返回JSON中提取 data 节点,兼容:
        - 老:item_get_response / jingdong_ware_get_response
        - 新:jingdong_item_read_get_response
        """
        for key in ("jingdong_item_read_get_response",
                    "item_get_response",
                    "jingdong_ware_get_response"):
            if key in raw:
                node = raw[key]
                return node.get("data", node)
        return raw  # 兜底


# ─────────────────────────────────────────────
# 使用示例 + 灰度切换
# ─────────────────────────────────────────────
if __name__ == "__main__":
    client = JdItemReadClient(
        app_key="YOUR_APP_KEY",
        app_secret="YOUR_APP_SECRET",
        sandbox=False,
    )
    ACCESS_TOKEN = "YOUR_ACCESS_TOKEN"

    # 灰度开关:通过配置中心/环境变量控制
    USE_NEW_API = True  # 迁移期可先置 False,验证通过后切 True

    # 单品查询
    result = client.get_item(
        access_token=ACCESS_TOKEN,
        sku_id="100012345678",
        use_new_api=USE_NEW_API,
    )
    data = JdItemReadClient.extract_item_data(result)
    print(f"商品标题: {data.get('title')}")
    print(f"促销价: {data.get('price_info', {}).get('promotion_price')}")

    # 批量查询(仅新接口支持)
    batch = client.get_items_batch(
        access_token=ACCESS_TOKEN,
        sku_ids=["100012345678", "100012345679", "100012345680"],
    )
    print(f"批量查询结果: {batch}")

五、平滑迁移的5步灰度策略

Step 1:双method并行(1-2周)

代码层通过 use_new_api 开关控制,先小流量(1%~5%)走新接口,对比返回数据一致性。

Step 2:返回结构双解析

解析层同时兼容 item_get_response / jingdong_item_read_get_response 两个根节点(代码已体现),避免切换瞬间解析失败。

Step 3:批量能力利用

新接口支持 sku_ids 批量查询(≤20个),原来20次单查 = 现在1次批量,调用量直接降到1/20,成本与限流压力骤降。

Step 4:fields显式声明

新接口强烈建议显式传 fields不传则返回全量字段,报文体积大、解析慢。推荐最小化字段集合(见源码)。

Step 5:控制台迁移

2026年8月30日前,将原宙斯应用迁移至京东商家开放平台(open.jd.com),新接口权限在此处申请。

六、2026迁移踩坑清单

⚠️ 上线前必查:
  1. 返回字段命名风格变了:驼峰 → 下划线(skuIdsku_id),所有下游解析代码要双写兼容

  2. 库存字段位置:真实库存藏在 data.stock_info.stock_numdata.sku_list[].stock_num 两处

  3. QPS限制:个人应用默认≤2/s,企业需申请提至5~50/s,超量返回 code=16 ISP_FLOW_CONTROL_LIMIT

  4. 批量≤20个:超过会报参数错误,需业务层分批

  5. 8月30日宙斯关闭:老控制台将无法访问,务必提前迁移至 open.jd.com

  6. 签名timestamp格式:必须是 yyyy-MM-dd HH:mm:ss 北京时间,用 time.strftime 而非Unix时间戳


七、成本影响测算

按新接口批量能力优化后:
场景
老接口调用量
新接口调用量
降幅
日同步1000个SKU
1000次
50次(20个/批)
95% ↓
月成本(鼎内¥0.01/百次)
¥0.10
¥0.005
几乎可忽略
💡 结合上一期讲的"京东API收费结构":商家按量接口鼎内¥0.01/百次,用批量接口把调用量压下去,是2026年最直接的降本手段

八、写在最后

这次迁移的本质不是"换了个method名",而是京东零售对外开放平台整体收敛的一部分——宙斯作为独立平台的历史正在结束,所有能力收口到京东商家开放平台。对ISV和商家自研团队来说,2026年剩下的窗口期只有两件事:
  1. 8月30日前完成控制台与应用迁移

  2. 新老接口并行期内完成 item.jd.getjingdong.item.read.get 的灰度切换

代码层面改动量很小(method + fields + 返回解析),真正的成本是业务侧的回归测试下游系统的字段映射——这部分建议留足2-3周缓冲。
📌 数据口径:本文接口参数与返回结构综合自京东JOS官方公告与2026年开发者实测,具体字段以京东商家开放平台最新API文档为准。

横向对比预告:京东这套"老接口维护期+新只读接口+平台融合"的节奏,在淘宝TOP、1688、拼多多、抖店的2026版开放平台里都能看到影子。下一期我们把《2026九大电商API免费额度&收费对照表》完整拆解,看看同样一笔"商品详情查询",在九家平台分别要怎么调、花多少钱。关注不迷路。


群贤毕至

访客