×

Java获取1688商品详情API接口(item_get)实战指南

admin admin 发表于2026-02-12 17:55:37 浏览7 评论0

抢沙发发表评论

作为B2B电商领域的技术开发者,对接1688开放平台获取商品详情是供应链系统、ERP采购模块的常见需求。本文将详细介绍如何使用Java调用1688的item_get(或alibaba.product.get)接口获取商品全量数据。

一、接口概述与前置准备

1.1 接口定位

1688商品详情API是阿里巴巴开放平台提供的核心数据接口,主要用于:
  • 基础信息获取:商品ID、标题、类目、品牌、起订量(MOQ)
  • 价格体系查询:批发价、阶梯价、建议零售价、分销代发价
  • 库存与SKU:多规格组合、实时库存、SKU对应价格
  • 多媒体数据:主图URL、详情图列表、商品描述HTML
  • 商家资质:供应商名称、诚信通等级、公司信息

1.2 接入前置条件

  1. 注册开发者账号:访问1688开放平台完成企业实名认证
  2. 创建应用:在控制台"应用管理"中创建应用,获取App KeyApp Secret
  3. 申请权限:申请alibaba.product.getitem_get接口调用权限

二、核心技术要点

2.1 签名机制

1688 API采用MD5签名算法,核心步骤如下:
  1. 将所有请求参数(除sign外)按参数名ASCII升序排序
  2. 拼接为key1value1key2value2...格式(注意:无连接符
  3. 在字符串首尾各拼接一次app_secret
  4. 进行MD5加密,结果转大写

2.2 认证流程

接口采用OAuth 2.0认证,调用流程:
  1. 使用app_keyapp_secret获取access_token
  2. 在业务接口请求中携带access_token
  3. access_token有效期通常为3600秒,需实现缓存刷新机制

三、Java完整实现代码

3.1 项目依赖(Maven)

xml
复制
<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>
    
    <!-- Lombok简化代码(可选) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.30</version>
        <scope>provided</scope>
    </dependency></dependencies>

3.2 签名工具类

java
复制
import java.security.MessageDigest;import java.util.Map;import java.util.TreeMap;/**
 * 1688 API签名工具类
 */public class AlibabaApiSigner {
    
    /**
     * 生成MD5签名(1688官方算法)
     * @param params 请求参数(不含sign)
     * @param appSecret 应用密钥
     * @return 大写签名字符串
     */
    public static String generateSign(Map<String, String> params, String appSecret) {
        // 1. 使用TreeMap按ASCII升序排序
        TreeMap<String, String> sortedParams = new TreeMap<>(params);
        
        // 2. 拼接字符串:key+value(无分隔符)
        StringBuilder signStr = new StringBuilder();
        for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            // 跳过空值和sign参数本身
            if (value != null && !value.isEmpty() && !"sign".equals(key)) {
                signStr.append(key).append(value);
            }
        }
        
        // 3. 首尾拼接appSecret
        String finalStr = appSecret + signStr.toString() + appSecret;
        
        // 4. MD5加密并转大写
        return md5(finalStr).toUpperCase();
    }
    
    /**
     * MD5加密
     */
    private static String md5(String str) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] bytes = md.digest(str.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte b : bytes) {
                String hex = Integer.toHexString(b & 0xFF);
                if (hex.length() == 1) {
                    sb.append("0");
                }
                sb.append(hex);
            }
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException("MD5加密失败", e);
        }
    }}

3.3 Token管理器(带缓存)

java
复制
import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpPost;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.util.EntityUtils;import java.io.IOException;import java.util.HashMap;import java.util.Map;import java.util.concurrent.TimeUnit;/**
 * AccessToken管理器(支持自动刷新)
 */public class AccessTokenManager {
    
    private final String appKey;
    private final String appSecret;
    private final String tokenUrl = "https://gw.open.1688.com/openapi/http/1/system.oauth2/getToken";
    
    private volatile String accessToken;
    private volatile long expireTime; // 过期时间戳(毫秒)
    private final long refreshThreshold = 60 * 1000; // 提前60秒刷新
    
    public AccessTokenManager(String appKey, String appSecret) {
        this.appKey = appKey;
        this.appSecret = appSecret;
    }
    
    /**
     * 获取有效Token(线程安全)
     */
    public synchronized String getValidToken() throws IOException {
        long now = System.currentTimeMillis();
        
        // 如果Token有效且未接近过期,直接返回
        if (accessToken != null && now < (expireTime - refreshThreshold)) {
            return accessToken;
        }
        
        // 重新获取Token
        return refreshToken();
    }
    
    /**
     * 刷新Token
     */
    private String refreshToken() throws IOException {
        Map<String, String> params = new HashMap<>();
        params.put("grant_type", "client_credentials");
        params.put("client_id", appKey);
        params.put("client_secret", appSecret);
        
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost post = new HttpPost(tokenUrl + "?" + buildQueryString(params));
        
        try (CloseableHttpResponse response = client.execute(post)) {
            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
            JSONObject json = JSON.parseObject(result);
            
            if (json.containsKey("error")) {
                throw new RuntimeException("获取Token失败: " + json.getString("error_description"));
            }
            
            this.accessToken = json.getString("access_token");
            long expiresIn = json.getLongValue("expires_in") * 1000; // 转换为毫秒
            this.expireTime = System.currentTimeMillis() + expiresIn;
            
            System.out.println("Token刷新成功,有效期至: " + new java.util.Date(expireTime));
            return accessToken;
        }
    }
    
    private String buildQueryString(Map<String, String> params) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (sb.length() > 0) sb.append("&");
            sb.append(entry.getKey()).append("=").append(entry.getValue());
        }
        return sb.toString();
    }}

3.4 商品详情API客户端

java
复制
import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import org.apache.http.client.methods.CloseableHttpResponse;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.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.Map;/**
 * 1688商品详情API客户端
 */public class Item1688ApiClient {
    
    private final String appKey;
    private final String appSecret;
    private final AccessTokenManager tokenManager;
    
    // 正式环境地址
    private static final String API_URL = "https://gw.open.1688.com/openapi/param2/2/portals.open/api.item.get/APP_KEY";
    
    public Item1688ApiClient(String appKey, String appSecret) {
        this.appKey = appKey;
        this.appSecret = appSecret;
        this.tokenManager = new AccessTokenManager(appKey, appSecret);
    }
    
    /**
     * 获取商品详情
     * @param offerId 1688商品ID
     * @param fields 指定返回字段(可选)
     * @return 商品详情JSON对象
     */
    public JSONObject getItemDetail(String offerId, String fields) throws Exception {
        // 1. 获取有效Token
        String accessToken = tokenManager.getValidToken();
        
        // 2. 组装公共参数
        Map<String, String> params = new HashMap<>();
        params.put("app_key", appKey);
        params.put("access_token", accessToken);
        params.put("method", "portals.open.api.item.get");
        params.put("format", "json");
        params.put("v", "1.0");
        params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        
        // 3. 组装业务参数
        params.put("offerId", offerId);
        if (fields != null && !fields.isEmpty()) {
            params.put("fields", fields);
        }
        
        // 4. 生成签名
        String sign = AlibabaApiSigner.generateSign(params, appSecret);
        params.put("sign", sign);
        
        // 5. 发送POST请求
        String apiUrl = API_URL.replace("APP_KEY", appKey);
        String response = sendPostRequest(apiUrl, params);
        
        // 6. 解析响应
        JSONObject result = JSON.parseObject(response);
        if (result.containsKey("error_response")) {
            JSONObject error = result.getJSONObject("error_response");
            throw new RuntimeException("API调用失败: " + error.getString("msg") 
                + " (错误码: " + error.getString("code") + ")");
        }
        
        return result.getJSONObject("item_get_response");
    }
    
    /**
     * 发送HTTP POST请求
     */
    private String sendPostRequest(String url, Map<String, String> params) throws IOException {
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost post = new HttpPost(url);
        
        // 构建表单参数
        StringBuilder formData = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (formData.length() > 0) formData.append("&");
            formData.append(entry.getKey()).append("=")
                   .append(java.net.URLEncoder.encode(entry.getValue(), "UTF-8"));
        }
        
        post.setEntity(new StringEntity(formData.toString(), "application/x-www-form-urlencoded", "UTF-8"));
        
        try (CloseableHttpResponse response = client.execute(post)) {
            return EntityUtils.toString(response.getEntity(), "UTF-8");
        }
    }
    
    /**
     * 简化调用(返回全量字段)
     */
    public JSONObject getItemDetail(String offerId) throws Exception {
        return getItemDetail(offerId, null);
    }}

3.5 使用示例与字段解析

java
复制
/**
 * 使用示例:获取1688商品详情
 */public class Item1688Demo {
    
    public static void main(String[] args) {
        // 替换为你的应用凭证
        String appKey = "your_app_key_here";
        String appSecret = "your_app_secret_here";
        String offerId = "610947572360"; // 1688商品ID
        
        try {
            Item1688ApiClient client = new Item1688ApiClient(appKey, appSecret);
            
            // 指定返回字段(优化性能)
            String fields = "offerId,title,price,rprice,minAmount,maxAmount,amount," +
                           "picUrl,detailUrl,shopName,companyName,specInfo,skuInfo";
            
            JSONObject response = client.getItemDetail(offerId, fields);
            JSONObject item = response.getJSONObject("item");
            
            // 解析核心字段
            System.out.println("=== 商品基础信息 ===");
            System.out.println("商品ID: " + item.getString("offerId"));
            System.out.println("商品标题: " + item.getString("title"));
            System.out.println("批发价: " + item.getString("price"));
            System.out.println("建议零售价: " + item.getString("rprice"));
            
            System.out.println("\n=== 采购信息 ===");
            System.out.println("起订量: " + item.getInteger("minAmount"));
            System.out.println("最大采购量: " + item.getInteger("maxAmount"));
            System.out.println("可售数量: " + item.getInteger("amount"));
            
            System.out.println("\n=== 供应商信息 ===");
            System.out.println("店铺名称: " + item.getString("shopName"));
            System.out.println("公司名称: " + item.getString("companyName"));
            
            System.out.println("\n=== 多媒体信息 ===");
            System.out.println("主图URL: " + item.getString("picUrl"));
            System.out.println("详情页链接: " + item.getString("detailUrl"));
            
            // SKU信息解析
            if (item.containsKey("skuInfo")) {
                System.out.println("\n=== SKU规格信息 ===");
                System.out.println(item.getString("skuInfo"));
            }
            
        } catch (Exception e) {
            System.err.println("获取商品详情失败: " + e.getMessage());
            e.printStackTrace();
        }
    }}

四、关键字段映射表

表格
复制
字段名说明适用场景
offerId商品唯一标识数据同步主键
title商品标题展示、搜索
price当前批发价采购成本计算
rprice建议零售价分销定价参考
priceRange阶梯价区间B2B批量采购
minAmount最小起订量(MOQ)采购门槛判断
amount实时可售库存库存同步
skuInfo规格组合信息多SKU管理
picUrl主图URL图片展示
detailUrlPC详情页链接跳转采购
mixAmount是否支持混批批发策略
shopName店铺名称供应商管理
companyName注册公司名资质审核

五、最佳实践与注意事项

5.1 性能优化建议

  1. Token缓存:务必实现access_token本地缓存,避免每次请求都重新获取
  2. 字段裁剪:使用fields参数按需获取字段,减少数据传输量
  3. 本地缓存:商品数据建议本地缓存30-60分钟,平衡实时性与API调用频次
  4. 连接池:生产环境使用HTTP连接池(如Apache HttpClient的PoolManager)

5.2 错误处理策略

java
复制
// 常见错误码处理switch (errorCode) {
    case "1001": // 签名错误
        // 检查系统时间是否同步,重新生成签名
        break;
    case "110": // Token过期
        // 强制刷新Token并重试
        break;
    case "403": // 权限不足
        // 申请接口权限或检查应用状态
        break;
    case "500": // 服务器错误
        // 指数退避重试
        break;}

5.3 合规与安全

  • 密钥保护app_secret严禁前端暴露,仅服务端使用
  • HTTPS强制:生产环境必须使用HTTPS协议
  • 限流处理:免费版通常5次/秒,专业版50次/秒,需实现限流器
  • 数据合规:遵守《1688开放平台服务协议》,不得用于爬虫或数据倒卖

六、总结

通过Java对接1688商品详情API的核心在于签名算法实现Token生命周期管理。本文提供的代码实现了完整的签名生成、自动刷新Token、字段定制获取等功能,可直接用于供应链系统、ERP采购模块、选品工具等B2B场景的开发。
对于批发特性的业务适配,需特别关注minAmount(起订量)、阶梯价区间、混批规则等1688特有的字段,这些是与淘宝/天猫API的主要差异点。

如遇任何疑问或有进一步的需求,请随时与我私信或者评论联系。

群贤毕至

访客