在电商多平台运营时代,商家需要将商品从货源平台(如1688、淘宝)同步上架至京东。传统手动铺货模式效率低下、错误率高,而通过京东开放平台(JOS)提供的标准化接口,可以实现从分类查询 → 图片上传 → 商品发布的全流程自动化。本文将详细介绍如何使用 Java 调用京东相关接口完成铺货上架。
一、整体流程概览
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 1.类目查询 │ → │ 2.图片上传 │ → │ 3.商品发布 │ → │ 4.状态查询 │
│ 获取类目ID │ │ 上传主图/详情 │ │ 填写商品信息 │ │ 确认上架成功 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
二、准备工作
1. 注册京东开放平台开发者账号
- 注册开发者账号
- 完成企业认证,创建应用获取凭证:
| 凭证 | 说明 |
|---|---|
App Key | 应用唯一标识 |
App Secret | 应用密钥,用于签名 |
Access Token | 店铺授权令牌(OAuth2.0) |
2. 申请接口权限
铺货上架需要申请以下接口权限:
| 接口权限 | 用途 |
|---|---|
jingdong.category.read.getAttrs | 查询类目属性 |
jingdong.image.write.upload | 上传商品图片 |
jingdong.seller.vender.info.get | 获取商家信息 |
jingdong.ware.write.add | 发布新商品 |
jingdong.ware.read.search | 查询商品状态 |
3. Maven 依赖配置
<dependencies>
<!-- HTTP 请求 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
<!-- JSON 解析 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.43</version>
</dependency>
<!-- MD5 加密 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.16.0</version>
</dependency>
<!-- 图片处理 -->
<dependency>
<groupId>javax.imageio</groupId>
<artifactId>imageio-core</artifactId>
<version>3.8.3</version>
</dependency>
<!-- Base64编码 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.16.0</version>
</dependency>
</dependencies>
三、核心代码实现
1. 签名工具类(MD5 算法)
京东JOS签名规则:参数按key ASCII升序排列,拼接为 key1+value1+key2+value2 格式,首尾追加 AppSecret,最后进行 MD5 加密并转大写。
import org.apache.commons.codec.digest.DigestUtils;
import java.util.Map;
import java.util.TreeMap;
/**
* 京东JOS签名工具类
*/
public class JdSignUtil {
/**
* 生成京东API签名
* 规则:AppSecret + 按key升序拼接(key+value) + AppSecret → MD5 → 大写
*/
public static String generateSign(Map<String, String> params, String appSecret) {
// 1. 剔除sign和空值,按key升序排列
TreeMap<String, String> sortedParams = new TreeMap<>();
for (Map.Entry<String, String> entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (!"sign".equals(key) && value != null && !value.trim().isEmpty()) {
sortedParams.put(key, value);
}
}
// 2. 按key升序拼接 key+value
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
sb.append(entry.getKey()).append(entry.getValue());
}
// 3. 首尾拼接AppSecret
String toSign = appSecret + sb.toString() + appSecret;
// 4. MD5加密,转大写
return DigestUtils.md5Hex(toSign).toUpperCase();
}
}
2. 基础请求工具类
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.util.HashMap;
import java.util.Map;
/**
* 京东API基础请求工具
*/
public class JdApiClient {
private static final String API_URL = "https://api.jd.com/routerjson";
private static final String VERSION = "2.0";
private final String appKey;
private final String appSecret;
private final String accessToken;
public JdApiClient(String appKey, String appSecret, String accessToken) {
this.appKey = appKey;
this.appSecret = appSecret;
this.accessToken = accessToken;
}
/**
* 发送API请求
* @param method 接口方法名
* @param paramJson 业务参数JSON
* @return 响应JSON
*/
public JSONObject execute(String method, String paramJson) {
try {
Map<String, String> params = new HashMap<>();
params.put("method", method);
params.put("app_key", appKey);
params.put("access_token", accessToken);
params.put("timestamp", getCurrentTimestamp());
params.put("v", VERSION);
params.put("format", "json");
params.put("360buy_param_json", paramJson);
// 生成签名
String sign = JdSignUtil.generateSign(params, appSecret);
params.put("sign", sign);
// 发送POST请求
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost(API_URL);
httpPost.setHeader("Content-Type", "application/json");
httpPost.setEntity(new StringEntity(JSON.toJSONString(params), "UTF-8"));
String response = EntityUtils.toString(
client.execute(httpPost).getEntity(), "UTF-8");
return JSON.parseObject(response);
}
} catch (Exception e) {
throw new RuntimeException("API请求失败: " + e.getMessage(), e);
}
}
/**
* 获取当前时间戳(格式:yyyy-MM-dd HH:mm:ss)
*/
private String getCurrentTimestamp() {
return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new java.util.Date());
}
}
3. 类目查询服务
京东商品发布必须先确定类目,通过类目查询接口获取类目ID和属性信息。
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.util.ArrayList;
import java.util.List;
/**
* 京东类目查询服务
*/
public class JdCategoryService {
private final JdApiClient apiClient;
public JdCategoryService(JdApiClient apiClient) {
this.apiClient = apiClient;
}
/**
* 获取一级类目列表
*/
public List<Category> getTopCategories() {
JSONObject param = new JSONObject();
param.put("fields", "cid,name,isParent");
JSONObject response = apiClient.execute(
"jingdong.category.read.findFirstLevelCategories",
param.toJSONString());
return parseCategories(response);
}
/**
* 获取子类目列表
* @param parentCid 父类目ID
*/
public List<Category> getChildCategories(long parentCid) {
JSONObject param = new JSONObject();
param.put("parentCid", parentCid);
param.put("fields", "cid,name,isParent");
JSONObject response = apiClient.execute(
"jingdong.category.read.findByPId",
param.toJSONString());
return parseCategories(response);
}
/**
* 获取类目属性(发布商品前必须调用)
* @param cid 三级类目ID
*/
public List<CategoryAttribute> getCategoryAttributes(long cid) {
JSONObject param = new JSONObject();
param.put("cid", cid);
param.put("type", 0); // 0=销售属性,1=关键属性,2=非关键属性
JSONObject response = apiClient.execute(
"jingdong.category.read.getAttrs",
param.toJSONString());
List<CategoryAttribute> attrs = new ArrayList<>();
if (response.containsKey("category_attrs")) {
JSONArray attrArray = response.getJSONArray("category_attrs");
for (int i = 0; i < attrArray.size(); i++) {
JSONObject attr = attrArray.getJSONObject(i);
CategoryAttribute attribute = new CategoryAttribute();
attribute.setAttrId(attr.getLong("aid"));
attribute.setAttrName(attr.getString("name"));
attribute.setRequired(attr.getBoolean("is_required"));
attribute.setInputType(attr.getString("input_type")); // text/select/checkbox
// 解析属性值选项
if (attr.containsKey("attrValues")) {
JSONArray values = attr.getJSONArray("attrValues");
List<AttrValue> valueList = new ArrayList<>();
for (int j = 0; j < values.size(); j++) {
JSONObject v = values.getJSONObject(j);
AttrValue av = new AttrValue();
av.setValueId(v.getLong("vid"));
av.setValueName(v.getString("name"));
valueList.add(av);
}
attribute.setValues(valueList);
}
attrs.add(attribute);
}
}
return attrs;
}
/**
* 根据关键词搜索类目(辅助选类目)
* @param keyword 类目关键词
*/
public List<Category> searchCategory(String keyword) {
JSONObject param = new JSONObject();
param.put("keyword", keyword);
JSONObject response = apiClient.execute(
"jingdong.category.read.search",
param.toJSONString());
return parseCategories(response);
}
private List<Category> parseCategories(JSONObject response) {
List<Category> categories = new ArrayList<>();
if (response.containsKey("categories")) {
JSONArray array = response.getJSONArray("categories");
for (int i = 0; i < array.size(); i++) {
JSONObject obj = array.getJSONObject(i);
Category cat = new Category();
cat.setCid(obj.getLong("cid"));
cat.setName(obj.getString("name"));
cat.setParent(obj.getBooleanValue("isParent"));
categories.add(cat);
}
}
return categories;
}
// 实体类
public static class Category {
private long cid;
private String name;
private boolean isParent;
// Getters and Setters
public long getCid() { return cid; }
public void setCid(long cid) { this.cid = cid; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public boolean isParent() { return isParent; }
public void setParent(boolean parent) { isParent = parent; }
}
public static class CategoryAttribute {
private long attrId;
private String attrName;
private boolean required;
private String inputType;
private List<AttrValue> values;
// Getters and Setters...
public long getAttrId() { return attrId; }
public void setAttrId(long attrId) { this.attrId = attrId; }
public String getAttrName() { return attrName; }
public void setAttrName(String attrName) { this.attrName = attrName; }
public boolean isRequired() { return required; }
public void setRequired(boolean required) { this.required = required; }
public String getInputType() { return inputType; }
public void setInputType(String inputType) { this.inputType = inputType; }
public List<AttrValue> getValues() { return values; }
public void setValues(List<AttrValue> values) { this.values = values; }
}
public static class AttrValue {
private long valueId;
private String valueName;
// Getters and Setters...
public long getValueId() { return valueId; }
public void setValueId(long valueId) { this.valueId = valueId; }
public String getValueName() { return valueName; }
public void setValueName(String valueName) { this.valueName = valueName; }
}
}
4. 图片上传服务
京东要求商品图片先上传到京东服务器,获取图片URL后再用于商品发布。图片需满足:白底、800×800像素、≤2MB、JPG/PNG格式。
java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* 京东图片上传服务
*/
public class JdImageService {
private final JdApiClient apiClient;
public JdImageService(JdApiClient apiClient) {
this.apiClient = apiClient;
}
/**
* 上传本地图片到京东
* @param imagePath 本地图片路径
* @return 京东图片URL
*/
public String uploadLocalImage(String imagePath) {
try {
File file = new File(imagePath);
BufferedImage image = ImageIO.read(file);
// 图片预处理:调整尺寸、压缩
BufferedImage processedImage = processImage(image);
// 转为Base64
String base64Image = imageToBase64(processedImage);
// 调用上传接口
return uploadToJd(base64Image);
} catch (Exception e) {
throw new RuntimeException("图片上传失败: " + e.getMessage(), e);
}
}
/**
* 上传网络图片到京东
* @param imageUrl 图片URL
* @return 京东图片URL
*/
public String uploadRemoteImage(String imageUrl) {
try {
URL url = new URL(imageUrl);
BufferedImage image = ImageIO.read(url);
BufferedImage processedImage = processImage(image);
String base64Image = imageToBase64(processedImage);
return uploadToJd(base64Image);
} catch (Exception e) {
throw new RuntimeException("网络图片上传失败: " + e.getMessage(), e);
}
}
/**
* 批量上传图片
* @param imagePaths 图片路径列表
* @return 京东图片URL列表
*/
public List<String> batchUploadImages(List<String> imagePaths) {
List<String> urls = new ArrayList<>();
for (String path : imagePaths) {
String jdUrl = uploadLocalImage(path);
urls.add(jdUrl);
// 控制上传频率,避免触发限流
try { Thread.sleep(500); } catch (InterruptedException ignored) {}
}
return urls;
}
/**
* 图片预处理:调整尺寸、白底、压缩
*/
private BufferedImage processImage(BufferedImage source) {
// 1. 调整尺寸为800x800
BufferedImage resized = new BufferedImage(800, 800, BufferedImage.TYPE_INT_RGB);
Graphics2D g = resized.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, 800, 800);
g.drawImage(source, 0, 0, 800, 800, null);
g.dispose();
// 2. 压缩质量(后续在Base64编码时控制)
return resized;
}
/**
* 图片转Base64
*/
private String imageToBase64(BufferedImage image) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", baos);
byte[] bytes = baos.toByteArray();
return Base64.encodeBase64String(bytes);
}
/**
* 调用京东图片上传接口
*/
private String uploadToJd(String base64Image) {
JSONObject param = new JSONObject();
param.put("imageData", base64Image);
JSONObject response = apiClient.execute(
"jingdong.image.write.upload",
param.toJSONString());
if (response.getIntValue("code") == 0) {
return response.getJSONObject("url").getString("url");
} else {
throw new RuntimeException("上传失败: " + response.getString("message"));
}
}
}
5. 商品发布服务
商品发布是铺货的核心环节,需要组装完整的商品信息并调用发布接口。
java
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.util.ArrayList;
import java.util.List;
/**
* 京东商品发布服务
*/
public class JdPublishService {
private final JdApiClient apiClient;
private final JdImageService imageService;
public JdPublishService(JdApiClient apiClient, JdImageService imageService) {
this.apiClient = apiClient;
this.imageService = imageService;
}
/**
* 发布新商品
* @param product 商品信息
* @return 商品ID
*/
public String publishProduct(ProductInfo product) {
try {
// 1. 上传图片并获取京东图片URL
List<String> jdImageUrls = new ArrayList<>();
for (String imgUrl : product.getSourceImageUrls()) {
String jdUrl = imageService.uploadRemoteImage(imgUrl);
jdImageUrls.add(jdUrl);
}
// 2. 构建商品发布参数
JSONObject ware = new JSONObject();
ware.put("title", product.getTitle()); // 商品标题
ware.put("secondTitle", product.getSubTitle()); // 副标题
ware.put("cid", product.getCategoryId()); // 类目ID(三级类目)
ware.put("brandId", product.getBrandId()); // 品牌ID
ware.put("shopCategorys", product.getShopCategoryIds()); // 店内分类
ware.put("wrap", product.getPackaging()); // 包装清单
ware.put("weight", product.getWeight()); // 重量(kg)
ware.put("prodectArea", product.getOrigin()); // 产地
ware.put("upc", product.getUpc()); // UPC码
ware.put("wareStatus", 1); // 1=上架,2=下架
// 3. 商品属性
JSONArray attributes = new JSONArray();
for (ProductAttribute attr : product.getAttributes()) {
JSONObject attrObj = new JSONObject();
attrObj.put("attrId", attr.getAttrId());
attrObj.put("attrValues", attr.getAttrValues());
attributes.add(attrObj);
}
ware.put("attributes", attributes);
// 4. SKU信息
JSONArray skus = new JSONArray();
for (SkuInfo sku : product.getSkus()) {
JSONObject skuObj = new JSONObject();
skuObj.put("skuId", sku.getSkuId());
skuObj.put("outerId", sku.getOuterId()); // 商家编码
skuObj.put("jdPrice", sku.getJdPrice()); // 京东价
skuObj.put("costPrice", sku.getCostPrice()); // 成本价
skuObj.put("stockNum", sku.getStockNum()); // 库存
skuObj.put("skuName", sku.getSkuName()); // SKU名称
// SKU图片
if (sku.getImageUrl() != null) {
skuObj.put("imagePath", sku.getImageUrl());
}
// SKU属性(颜色、尺码等)
JSONArray skuProps = new JSONArray();
for (SkuProperty prop : sku.getProperties()) {
JSONObject propObj = new JSONObject();
propObj.put("propId", prop.getPropId());
propObj.put("propValues", prop.getPropValues());
skuProps.add(propObj);
}
skuObj.put("skuProps", skuProps);
skus.add(skuObj);
}
ware.put("skus", skus);
// 5. 商品主图
ware.put("wareImage", jdImageUrls.get(0)); // 主图
JSONArray imageList = new JSONArray();
for (int i = 0; i < jdImageUrls.size() && i < 10; i++) {
imageList.add(jdImageUrls.get(i));
}
ware.put("wareImageList", imageList); // 轮播图
// 6. 详情页(HTML格式)
ware.put("introduction", buildDetailHtml(product, jdImageUrls));
// 7. 售后服务
ware.put("afterSaleDesc", product.getAfterSaleDesc());
ware.put("mobileDesc", product.getMobileDesc()); // 移动端详情
// 8. 物流信息
ware.put("transportId", product.getTransportId()); // 运费模板ID
// 调用发布接口
JSONObject param = new JSONObject();
param.put("ware", ware.toJSONString());
JSONObject response = apiClient.execute(
"jingdong.ware.write.add",
param.toJSONString());
if (response.getIntValue("code") == 0) {
return response.getJSONObject("ware_id").getString("wareId");
} else {
throw new RuntimeException("发布失败: " + response.getString("message"));
}
} catch (Exception e) {
throw new RuntimeException("商品发布异常: " + e.getMessage(), e);
}
}
/**
* 构建详情页HTML
*/
private String buildDetailHtml(ProductInfo product, List<String> imageUrls) {
StringBuilder html = new StringBuilder();
html.append("<div style='width:750px;margin:0 auto;'>");
// 商品描述
html.append("<p>").append(product.getDescription()).append("</p>");
// 详情图片
for (String url : imageUrls) {
html.append("<img src='").append(url).append("' style='width:100%;'/>");
}
// 规格参数表
html.append("<table style='width:100%;border-collapse:collapse;'>");
html.append("<tr><th style='border:1px solid #ddd;padding:8px;'>参数</th>");
html.append("<th style='border:1px solid #ddd;padding:8px;'>值</th></tr>");
for (ProductAttribute attr : product.getAttributes()) {
html.append("<tr>");
html.append("<td style='border:1px solid #ddd;padding:8px;'>")
.append(attr.getAttrName()).append("</td>");
html.append("<td style='border:1px solid #ddd;padding:8px;'>")
.append(attr.getAttrValues()).append("</td>");
html.append("</tr>");
}
html.append("</table>");
html.append("</div>");
return html.toString();
}
/**
* 查询商品发布状态
*/
public String getProductStatus(String wareId) {
JSONObject param = new JSONObject();
param.put("wareId", wareId);
param.put("fields", "wareId,title,wareStatus,stockNum");
JSONObject response = apiClient.execute(
"jingdong.ware.read.get",
param.toJSONString());
return response.toJSONString();
}
// 商品信息实体类
public static class ProductInfo {
private String title;
private String subTitle;
private long categoryId;
private long brandId;
private String[] shopCategoryIds;
private String packaging;
private double weight;
private String origin;
private String upc;
private List<ProductAttribute> attributes;
private List<SkuInfo> skus;
private List<String> sourceImageUrls;
private String description;
private String afterSaleDesc;
private String mobileDesc;
private long transportId;
// Getters and Setters...
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getSubTitle() { return subTitle; }
public void setSubTitle(String subTitle) { this.subTitle = subTitle; }
public long getCategoryId() { return categoryId; }
public void setCategoryId(long categoryId) { this.categoryId = categoryId; }
public long getBrandId() { return brandId; }
public void setBrandId(long brandId) { this.brandId = brandId; }
public String[] getShopCategoryIds() { return shopCategoryIds; }
public void setShopCategoryIds(String[] shopCategoryIds) { this.shopCategoryIds = shopCategoryIds; }
public String getPackaging() { return packaging; }
public void setPackaging(String packaging) { this.packaging = packaging; }
public double getWeight() { return weight; }
public void setWeight(double weight) { this.weight = weight; }
public String getOrigin() { return origin; }
public void setOrigin(String origin) { this.origin = origin; }
public String getUpc() { return upc; }
public void setUpc(String upc) { this.upc = upc; }
public List<ProductAttribute> getAttributes() { return attributes; }
public void setAttributes(List<ProductAttribute> attributes) { this.attributes = attributes; }
public List<SkuInfo> getSkus() { return skus; }
public void setSkus(List<SkuInfo> skus) { this.skus = skus; }
public List<String> getSourceImageUrls() { return sourceImageUrls; }
public void setSourceImageUrls(List<String> sourceImageUrls) { this.sourceImageUrls = sourceImageUrls; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getAfterSaleDesc() { return afterSaleDesc; }
public void setAfterSaleDesc(String afterSaleDesc) { this.afterSaleDesc = afterSaleDesc; }
public String getMobileDesc() { return mobileDesc; }
public void setMobileDesc(String mobileDesc) { this.mobileDesc = mobileDesc; }
public long getTransportId() { return transportId; }
public void setTransportId(long transportId) { this.transportId = transportId; }
}
public static class ProductAttribute {
private long attrId;
private String attrName;
private String attrValues;
// Getters and Setters...
public long getAttrId() { return attrId; }
public void setAttrId(long attrId) { this.attrId = attrId; }
public String getAttrName() { return attrName; }
public void setAttrName(String attrName) { this.attrName = attrName; }
public String getAttrValues() { return attrValues; }
public void setAttrValues(String attrValues) { this.attrValues = attrValues; }
}
public static class SkuInfo {
private String skuId;
private String outerId;
private double jdPrice;
private double costPrice;
private int stockNum;
private String skuName;
private String imageUrl;
private List<SkuProperty> properties;
// Getters and Setters...
public String getSkuId() { return skuId; }
public void setSkuId(String skuId) { this.skuId = skuId; }
public String getOuterId() { return outerId; }
public void setOuterId(String outerId) { this.outerId = outerId; }
public double getJdPrice() { return jdPrice; }
public void setJdPrice(double jdPrice) { this.jdPrice = jdPrice; }
public double getCostPrice() { return costPrice; }
public void setCostPrice(double costPrice) { this.costPrice = costPrice; }
public int getStockNum() { return stockNum; }
public void setStockNum(int stockNum) { this.stockNum = stockNum; }
public String getSkuName() { return skuName; }
public void setSkuName(String skuName) { this.skuName = skuName; }
public String getImageUrl() { return imageUrl; }
public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
public List<SkuProperty> getProperties() { return properties; }
public void setProperties(List<SkuProperty> properties) { this.properties = properties; }
}
public static class SkuProperty {
private long propId;
private String propValues;
// Getters and Setters...
public long getPropId() { return propId; }
public void setPropId(long propId) { this.propId = propId; }
public String getPropValues() { return propValues; }
public void setPropValues(String propValues) { this.propValues = propValues; }
}
}
6. 铺货主程序入口
java
public class JdPublishingDemo {
public static void main(String[] args) {
// 配置信息
String appKey = "your_app_key";
String appSecret = "your_app_secret";
String accessToken = "your_access_token";
try {
// 初始化服务
JdApiClient apiClient = new JdApiClient(appKey, appSecret, accessToken);
JdImageService imageService = new JdImageService(apiClient);
JdCategoryService categoryService = new JdCategoryService(apiClient);
JdPublishService publishService = new JdPublishService(apiClient, imageService);
// ========== Step 1: 查询类目 ==========
System.out.println("【Step 1】查询类目...");
System.out.println("=".repeat(50));
// 搜索类目
List<JdCategoryService.Category> categories =
categoryService.searchCategory("手机");
System.out.println("找到类目:");
for (JdCategoryService.Category cat : categories) {
System.out.println(" " + cat.getCid() + " - " + cat.getName());
}
// 假设选择三级类目ID为 9987(手机)
long categoryId = 9987L;
// 获取类目属性
List<JdCategoryService.CategoryAttribute> attrs =
categoryService.getCategoryAttributes(categoryId);
System.out.println("\n类目必填属性:");
for (JdCategoryService.CategoryAttribute attr : attrs) {
if (attr.isRequired()) {
System.out.println(" [必填] " + attr.getAttrName()
+ " (" + attr.getInputType() + ")");
}
}
// ========== Step 2: 准备商品数据 ==========
System.out.println("\n【Step 2】准备商品数据...");
System.out.println("=".repeat(50));
JdPublishService.ProductInfo product = new JdPublishService.ProductInfo();
product.setTitle("Apple iPhone 16 Pro Max 256GB 钛金属");
product.setSubTitle("A18芯片 4800万像素 超长续航");
product.setCategoryId(categoryId);
product.setBrandId(14026L); // Apple品牌ID
product.setWeight(0.227);
product.setOrigin("中国");
product.setDescription("iPhone 16 Pro Max 搭载全新A18芯片...");
// 设置商品属性
List<JdPublishService.ProductAttribute> attributes = new ArrayList<>();
JdPublishService.ProductAttribute attr1 = new JdPublishService.ProductAttribute();
attr1.setAttrId(100000); // 颜色属性ID
attr1.setAttrValues("钛金属");
attributes.add(attr1);
product.setAttributes(attributes);
// 设置SKU
List<JdPublishService.SkuInfo> skus = new ArrayList<>();
JdPublishService.SkuInfo sku1 = new JdPublishService.SkuInfo();
sku1.setOuterId("IP16PM-256-TITAN");
sku1.setJdPrice(9999.00);
sku1.setCostPrice(8500.00);
sku1.setStockNum(100);
sku1.setSkuName("256GB 钛金属");
List<JdPublishService.SkuProperty> skuProps = new ArrayList<>();
JdPublishService.SkuProperty prop1 = new JdPublishService.SkuProperty();
prop1.setPropId(100000);
prop1.setPropValues("钛金属");
skuProps.add(prop1);
sku1.setProperties(skuProps);
skus.add(sku1);
product.setSkus(skus);
// 设置图片(从货源平台获取的图片URL)
List<String> imageUrls = new ArrayList<>();
imageUrls.add("https://img.example.com/iphone16-main.jpg");
imageUrls.add("https://img.example.com/iphone16-detail1.jpg");
imageUrls.add("https://img.example.com/iphone16-detail2.jpg");
product.setSourceImageUrls(imageUrls);
// ========== Step 3: 发布商品 ==========
System.out.println("\n【Step 3】发布商品到京东...");
System.out.println("=".repeat(50));
String wareId = publishService.publishProduct(product);
System.out.println("商品发布成功!商品ID: " + wareId);
// ========== Step 4: 查询状态 ==========
System.out.println("\n【Step 4】查询商品状态...");
System.out.println("=".repeat(50));
String status = publishService.getProductStatus(wareId);
System.out.println("商品状态: " + status);
} catch (Exception e) {
System.err.println("铺货失败: " + e.getMessage());
e.printStackTrace();
}
}
}
四、跨平台铺货数据映射
当从其他平台(如1688、淘宝)采集商品数据铺货到京东时,需要进行字段映射和转换:
| 业务字段 | 1688数据源 | 淘宝数据源 | 京东目标字段 |
|---|---|---|---|
| 商品标题 | item.title | title | title |
| 销售价格 | item.price | price | jdPrice |
| 库存数量 | item.stock | num | stockNum |
| 商品主图 | item.pic_url | pic_url | wareImage |
| 详情图片 | item.desc_img | desc_img | introduction |
| 类目路径 | item.category_path | cid | cid(需映射) |
| SKU规格 | item.sku | skus | skus |
| 品牌 | item.brand | brand | brandId |
| 重量 | item.weight | weight | weight类目映射示例:java |
public class CategoryMapping {
// 类目映射表:源平台类目 → 京东类目
private static final Map<String, Long> CATEGORY_MAP = new HashMap<>();
static {
CATEGORY_MAP.put("tb:50008165", 9987L); // 淘宝手机 → 京东手机
CATEGORY_MAP.put("tb:50013864", 652L); // 淘宝女装 → 京东女装
CATEGORY_MAP.put("1688:7", 9987L); // 1688手机 → 京东手机
// ... 更多映射
}
public static Long mapToJdCategory(String sourceCategory) {
return CATEGORY_MAP.getOrDefault(sourceCategory, 0L);
}
}
五、图片处理规范
京东对商品图片有严格要求,铺货前需进行预处理:
| 要求项 | 规范 | 处理方式 |
|---|---|---|
| 主图尺寸 | 800×800像素 | 强制缩放至800×800 |
| 主图背景 | 白底(RGB 255,255,255) | 自动填充白色背景 |
| 图片格式 | JPG/PNG | 统一转为JPG |
| 图片大小 | ≤2MB | 压缩至2MB以内 |
| 图片数量 | 主图5张,详情图≤20张 | 自动截取前5张为主图 |
| 水印 | 不得有第三方平台水印 | 自动去除或覆盖 |
六、常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
类目不存在 | 类目ID错误或已下线 | 重新查询最新类目树 |
品牌未授权 | 品牌ID未在店铺授权 | 申请品牌授权或选择其他品牌 |
图片上传失败 | 图片格式/大小不符 | 按规范预处理图片 |
SKU属性不匹配 | 属性值不在类目允许范围内 | 查询类目属性可选值 |
价格低于最低价 | 低于类目最低限价 | 调整价格或更换类目 |
库存必须为整数 | 传入小数 | 四舍五入取整 |
商品审核不通过 | 信息不完整或违规 | 根据审核意见修改后重新提交 |
七、进阶优化建议
1. 异步批量铺货
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
public class BatchPublishService {
public List<String> batchPublish(List<ProductInfo> products) {
return products.stream()
.map(p -> CompletableFuture.supplyAsync(() -> {
try {
return publishService.publishProduct(p);
} catch (Exception e) {
return "FAILED: " + p.getTitle();
}
}))
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
}
2. 铺货结果监控
public class PublishMonitor {
// 记录铺货日志
public void logPublishResult(String sourceId, String jdWareId, boolean success, String message) {
// 写入数据库或发送通知
}
// 定时同步库存
public void syncStock(String sourceId, String jdWareId) {
// 从货源平台获取最新库存,同步到京东
}
}
八、总结
本文完整介绍了使用 Java 实现京东商品铺货上架的流程:
- 类目查询:通过关键词搜索或层级遍历获取正确的三级类目ID
- 图片上传:将货源图片预处理(尺寸、格式、白底)后上传至京东服务器
- 商品发布:组装完整的商品信息(标题、价格、SKU、属性、详情页)调用发布接口
- 状态查询:确认商品审核状态,处理异常
- 在实际开发中,建议建立类目映射表和品牌映射表,实现跨平台数据的标准化转换。同时,务必遵守京东开放平台的使用协议,合理控制调用频率,确保铺货操作的合规性和稳定性。
如遇任何疑问或有进一步的需求,请随时与我私信或者评论联系。

