×

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

万邦科技Lex 万邦科技Lex 发表于2026-07-17 09:41:16 浏览16 评论0

抢沙发发表评论

🔄《京东JOS宙斯迁移实录:jingdong.ware.getjingdong.ware.read.get 平滑切(2026版)》(附Python源码)

京东宙斯(JOS)在2024~2026年间持续推进接口标准化,老接口jingdong.ware.get(及别名360buy.ware.get)已进入维护期,新应用不再支持申请,官方推荐统一迁移至jingdong.ware.read.get(只读商品读取接口,权限更清晰、支持SKU快照字段)。
迁移只需改method + 微调fields,网关/签名/AccessToken完全不变,下面给你一份可直接执行的平滑切换实录。

一、老 vs 新接口对照(一目了然)

维度
老接口(即将下线)
新接口(推荐)
Method
jingdong.ware.get / 360buy.ware.get
jingdong.ware.read.get
网关
https://api.jd.com/routerjson
完全相同
签名算法
MD5(AppSecret+KV_ASCII+AppSecret) 秒级timestamp
完全相同(一行不改)
必传参数
wareId, fields
完全相同wareId, fields
返回根节点
jingdong_ware_get_response.ware
jingdong_ware_read_get_response.ware
SKU库存可见
需传seller token
相同(传seller token可看stockNum in skus_json
权限申请
老入口(已关闭新申请)
应用→接口权限→申请jingdong.ware.read.get(即时生效)
迁移量:代码中只改 method 字符串 + fields 补全 skus_json,stock_num,其余(Client/签名/网关/token)一行不动。

二、Python:老接口 vs 新接口调用对比 + 兼容封装

# jd_ware_migration_2026.py
"""
京东JOS宙斯迁移实录
old: jingdong.ware.get  →  new: jingdong.ware.read.get
同一个 JdClient 复用,仅 method + fields 不同
支持 沙箱/生产 切换
"""
import hashlib
import json
import requests
import time
from typing import Dict, Optional


class JdWareClient:
    """京东JOS商品接口客户端(兼容新旧两套)"""
    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.ak = app_key
        self.ask = app_secret
        self.gw = self.GW_SANDBOX if sandbox else self.GW_PROD

    # ───── JOS MD5签名(秒级timestamp,紧凑JSON)─────
    def _sign(self, params: Dict) -> str:
        filt = sorted(
            (k, v) for k, v in params.items()
            if k != "sign" and v is not None and str(v).strip() != ""
        )
        qs = "".join(f"{k}{v}" for k, v in filt)
        raw = f"{self.ask}{qs}{self.ask}"
        return hashlib.md5(raw.encode("utf-8")).hexdigest().upper()

    def _call(self, method: str, biz: Dict, token: str = None) -> Dict:
        p = {
            "app_key": self.ak,
            "method": method,
            "timestamp": str(int(time.time())),   # ← 秒级!不是毫秒
            "format": "json",
            "v": "2.0",
            "sign_method": "md5",
            "360buy_param_json": json.dumps(biz, ensure_ascii=False,
                                             separators=(',', ':'))
        }
        if token:
            p["access_token"] = token
        p["sign"] = self._sign(p)

        r = requests.post(self.gw, data=p, timeout=15)
        r.raise_for_status()
        d = r.json()

        # 提取响应(兼容新旧method对应的resp_key)
        resp_key = method.replace(".", "_") + "_response"
        if resp_key not in d:
            for k in d:
                if k.endswith("_response"):
                    resp_key = k
                    break
        data = d.get(resp_key, d)

        if "error_response" in str(data):
            err = d.get(resp_key, {}).get("error_response") or d.get("error_response")
            if err:
                code = str(err.get("code", ""))
                zh = err.get("zh_desc") or err.get("en_desc")
                sub = err.get("sub_code", "")
                if "no permission" in zh:
                    raise PermissionError(
                        f"❌ 无权限:确认已申请 {method} 接口,订单类需传卖家AccessToken\n原始: {zh}"
                    )
                raise Exception(f"JOS[{code}]: {zh} sub={sub}")
            raise Exception(f"JOS error: {d}")
        return data

    # ─── 老接口(不推荐,仅作对比/过渡期兼容)───
    def get_ware_old(self, ware_id: str,
                     fields: str = None, token: str = None) -> Dict:
        fields = fields or (
            "ware_id,title,price,jd_price,stock_num,skus_json,image_path"
        )
        return self._call(
            "jingdong.ware.get",          # ← 老 method
            {"wareId": ware_id, "fields": fields},
            token
        ).get("ware", {})

    # ─── ✅ 新接口(推荐迁移目标)───
    def get_ware_new(self, ware_id: str,
                     fields: str = None, token: str = None) -> Dict:
        fields = fields or (
            "ware_id,title,brand_name,cid,category_name,"
            "price,jd_price,market_price,stock_num,"
            "skus_json,image_path,desc,outer_id,approve_status"
        )
        return self._call(
            "jingdong.ware.read.get",     # ← 新 method
            {"wareId": ware_id, "fields": fields},
            token
        ).get("ware", {})


# =========================================================
# 使用示例:平滑切换
# =========================================================
if __name__ == "__main__":
    client = JdWareClient(
        app_key="YOUR_JD_APP_KEY",
        app_secret="YOUR_JD_APP_SECRET",
        sandbox=False          # 沙箱测试 = True
    )
    WARE_ID = "100012345678"   # ← 替换真实 wareId
    SELLER_TOKEN = None        # 查自己店铺SKU库存需传卖家AccessToken

    try:
        # ★ 迁移后只用这一行 ★
        ware = client.get_ware_new(WARE_ID, token=SELLER_TOKEN)

        print("✅ [新接口] 商品标题:", ware.get("title"))
        print("   京东价:", ware.get("jd_price"))
        print("   SPU库存:", ware.get("stock_num"))

        # skus_json 解析(查自己店铺时才有真实库存)
        import json as _j
        skus_raw = ware.get("skus_json") or ""
        if skus_raw:
            skus = _j.loads(skus_raw)
            print(f"   SKU数: {len(skus)}")
            for s in skus[:3]:
                print(f"     skuId:{s.get('skuId')} 价:{s.get('jdPrice')} "
                      f"库:{s.get('stockNum')} 商家码:{s.get('outerId','')}")

        print("\n→ 迁移完成!仅需改 method='jingdong.ware.read.get' + fields补skus_json")

    except PermissionError as pe:
        print(pe)
    except Exception as e:
        print("❌", e)
        print("→ 确认: AppKey/Secret正确、接口已申请、wareId有效")

三、迁移 Checklist(照着做,5分钟切完)

  1. 应用后台申请新权限
    → 接口权限 → 勾选 jingdong.ware.read.get → 提交(通常即时生效)

  2. 代码改动(极小)
    - method = "jingdong.ware.get"
    + method = "jingdong.ware.read.get"
    - fields = "ware_id,title,price"
    + fields = "ware_id,title,price,jd_price,stock_num,skus_json,image_path"
    wareId 参数名不变(驼峰,同老接口)

  3. 回归测试(5分钟)

    • 沙箱调通签名 ✓

    • 生产比对新旧返回字段一致性(标题/价格/SKU数应一致)

    • 自家店铺查 SKU 库存需传 access_token(同旧逻辑)

  4. 下线老调用
    确认所有环境切新 method 后,删除 get_ware_old() 分支


四、避坑清单

现象
解决
新接口返回 403
未申请 jingdong.ware.read.get
应用→接口权限→申请
skus_json
公开查询别人商品 / 未传 token
查自己店铺商品须传卖家 AccessToken
老代码 wareId 写成 ware_id
部分SDK自动映射,推荐用 wareId(首字母大写)
与文档保持一致
返回结构 key 变
jingdong_ware_get_responsejingdong_ware_read_get_response
解析用 .get("ware") 不依赖外层 key
timestamp毫秒
Invalid Timestamp
JOS用秒级 int(time.time())
沙箱返回 mock 数据
正常,沙箱只验签不返真数据
切生产验证

五、面试/方案一句话

京东JOS宙斯迁移:老接口 jingdong.ware.get → 新接口 jingdong.ware.read.get只改 method 字符串 + fields 补 skus_json,stock_num,网关/签名/AccessToken 完全不变;应用需申请新接口权限,返回值结构外层key变但内层 ware 节点字段一致,解析取 .get("ware") 即兼容,5分钟平滑切换。
需要我补 京东订单增量同步(jingdong.pop.order.search) 或 库存快照定时校正脚本 的完整迁移示例吗?


群贤毕至

访客