Java 获取微店商品详情接口完整指南

admin17小时前JAVA9
微店作为国内知名的移动电商平台,为开发者提供了丰富的 API 接口,其中 micro.item_get 接口用于获取商品详情数据。本文将详细介绍如何使用 Java 调用该接口,包括环境准备、认证授权、签名生成、接口调用及数据解析等完整流程。

一、接口概述

micro.item_get 是微店开放平台提供的核心接口,用于获取指定商品的详细信息,包括:
  • 商品基本信息(ID、标题、价格、库存)
  • 商品图片与描述
  • 商品规格与属性
  • 销量与评价数据
该接口采用 HTTP GET/POST 请求方式,返回 JSON 格式数据。

二、准备工作

1. 注册开发者账号

在调用接口前,需前往微店开放平台注册开发者账号,创建应用并获取以下凭证:
表格
凭证说明
app_key应用标识
app_secret应用密钥,用于签名
access_token访问令牌,用于身份验证

2. 项目依赖

使用 Maven 管理依赖,在 pom.xml 中添加:
xml
<dependencies>
    <!-- HTTP 请求 -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.14</version>
    </dependency>
    
    <!-- JSON 解析 -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>
    
    <!-- 日志 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>2.0.9</version>
    </dependency></dependencies>

三、完整代码实现

1. 签名工具类

微店 API 要求对请求参数进行签名验证,以下是基于 MD5 的签名实现:
java
import java.nio.charset.StandardCharsets;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Arrays;import java.util.Map;import java.util.stream.Collectors;public class WeidianSignUtil {
    
    /**
     * 生成微店 API 签名
     * 签名规则:将参数按 key 升序排列,拼接成 key1=value1&key2=value2 格式,末尾追加 app_secret,最后进行 MD5 加密
     */
    public static String generateSign(Map<String, String> params, String appSecret) {
        // 1. 过滤空值,按 key 升序排列
        String sortedParams = params.entrySet().stream()
            .filter(entry -> entry.getValue() != null && !entry.getValue().isEmpty())
            .sorted(Map.Entry.comparingByKey())
            .map(entry -> entry.getKey() + "=" + entry.getValue())
            .collect(Collectors.joining("&"));
        
        // 2. 拼接 app_secret
        String signStr = sortedParams + appSecret;
        
        // 3. MD5 加密
        return md5(signStr).toLowerCase();
    }
    
    private static String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5 algorithm not found", e);
        }
    }}

2. Access Token 获取

java
import org.apache.http.client.methods.HttpPost;import org.apache.http.client.entity.UrlEncodedFormEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.message.BasicNameValuePair;import org.apache.http.util.EntityUtils;import com.google.gson.JsonObject;import com.google.gson.JsonParser;import java.util.ArrayList;import java.util.List;public class WeidianAuthService {
    
    private static final String AUTH_URL = "https://open.weidian.com/api/oauth2/token";
    
    /**
     * 使用 client_credentials 模式获取 Access Token
     */
    public static String getAccessToken(String clientId, String clientSecret) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost post = new HttpPost(AUTH_URL);
            
            List<BasicNameValuePair> params = new ArrayList<>();
            params.add(new BasicNameValuePair("grant_type", "client_credentials"));
            params.add(new BasicNameValuePair("client_id", clientId));
            params.add(new BasicNameValuePair("client_secret", clientSecret));
            post.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
            
            String response = EntityUtils.toString(httpClient.execute(post).getEntity(), "UTF-8");
            JsonObject json = JsonParser.parseString(response).getAsJsonObject();
            
            if (json.has("access_token")) {
                return json.get("access_token").getAsString();
            } else {
                throw new RuntimeException("获取 Access Token 失败: " + response);
            }
        } catch (Exception e) {
            throw new RuntimeException("获取 Access Token 异常", e);
        }
    }}

3. 商品详情服务类

java
import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.util.EntityUtils;import com.google.gson.Gson;import com.google.gson.JsonObject;import java.util.HashMap;import java.util.Map;public class WeidianItemService {
    
    private static final String API_BASE_URL = "https://api.weidian.com/v1";
    private final String appKey;
    private final String appSecret;
    private final Gson gson = new Gson();
    
    public WeidianItemService(String appKey, String appSecret) {
        this.appKey = appKey;
        this.appSecret = appSecret;
    }
    
    /**
     * 获取商品详情
     * @param accessToken 访问令牌
     * @param itemId 商品ID
     * @return 商品详情对象
     */
    public ItemDetail getItemDetail(String accessToken, String itemId) {
        // 构建请求参数
        Map<String, String> params = new HashMap<>();
        params.put("app_key", appKey);
        params.put("access_token", accessToken);
        params.put("item_id", itemId);
        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        
        // 生成签名
        String sign = WeidianSignUtil.generateSign(params, appSecret);
        params.put("sign", sign);
        
        // 构建 URL
        StringBuilder urlBuilder = new StringBuilder(API_BASE_URL + "/items/details?");
        params.forEach((k, v) -> urlBuilder.append(k).append("=").append(v).append("&"));
        String url = urlBuilder.substring(0, urlBuilder.length() - 1);
        
        // 发送请求
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(url);
            httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            httpGet.setHeader("Accept", "application/json");
            
            String response = EntityUtils.toString(httpClient.execute(httpGet).getEntity(), "UTF-8");
            JsonObject jsonResponse = JsonParser.parseString(response).getAsJsonObject();
            
            // 解析响应
            return parseItemDetail(jsonResponse);
        } catch (Exception e) {
            throw new RuntimeException("获取商品详情失败", e);
        }
    }
    
    /**
     * 解析商品详情 JSON 响应
     */
    private ItemDetail parseItemDetail(JsonObject response) {
        if (!response.has("data") || response.get("data").isJsonNull()) {
            throw new RuntimeException("接口返回数据异常: " + response);
        }
        
        JsonObject data = response.getAsJsonObject("data");
        ItemDetail item = new ItemDetail();
        item.setItemId(data.has("item_id") ? data.get("item_id").getAsString() : "");
        item.setName(data.has("item_name") ? data.get("item_name").getAsString() : "");
        item.setPrice(data.has("price") ? data.get("price").getAsDouble() : 0.0);
        item.setOriginalPrice(data.has("original_price") ? data.get("original_price").getAsDouble() : 0.0);
        item.setStock(data.has("stock") ? data.get("stock").getAsInt() : 0);
        item.setSoldCount(data.has("sold_count") ? data.get("sold_count").getAsInt() : 0);
        item.setDescription(data.has("description") ? data.get("description").getAsString() : "");
        item.setMainImage(data.has("main_image") ? data.get("main_image").getAsString() : "");
        
        // 解析图片列表
        if (data.has("images") && data.get("images").isJsonArray()) {
            List<String> images = new ArrayList<>();
            data.getAsJsonArray("images").forEach(img -> images.add(img.getAsString()));
            item.setImages(images);
        }
        
        return item;
    }}

4. 商品详情实体类

java
import java.util.List;public class ItemDetail {
    private String itemId;          // 商品ID
    private String name;            // 商品名称
    private Double price;           // 当前售价
    private Double originalPrice;   // 原价
    private Integer stock;          // 库存
    private Integer soldCount;      // 销量
    private String description;     // 商品描述
    private String mainImage;       // 主图
    private List<String> images;    // 图片列表
    private List<Sku> skus;         // SKU 列表
    
    // 构造方法、Getter 和 Setter
    public ItemDetail() {}
    
    // Getters and Setters
    public String getItemId() { return itemId; }
    public void setItemId(String itemId) { this.itemId = itemId; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public Double getPrice() { return price; }
    public void setPrice(Double price) { this.price = price; }
    
    public Double getOriginalPrice() { return originalPrice; }
    public void setOriginalPrice(Double originalPrice) { this.originalPrice = originalPrice; }
    
    public Integer getStock() { return stock; }
    public void setStock(Integer stock) { this.stock = stock; }
    
    public Integer getSoldCount() { return soldCount; }
    public void setSoldCount(Integer soldCount) { this.soldCount = soldCount; }
    
    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }
    
    public String getMainImage() { return mainImage; }
    public void setMainImage(String mainImage) { this.mainImage = mainImage; }
    
    public List<String> getImages() { return images; }
    public void setImages(List<String> images) { this.images = images; }
    
    public List<Sku> getSkus() { return skus; }
    public void setSkus(List<Sku> skus) { this.skus = skus; }
    
    @Override
    public String toString() {
        return "ItemDetail{" +
            "itemId='" + itemId + '\'' +
            ", name='" + name + '\'' +
            ", price=" + price +
            ", stock=" + stock +
            ", soldCount=" + soldCount +
            '}';
    }
    
    // SKU 内部类
    public static class Sku {
        private String skuId;
        private String properties;
        private Double price;
        private Integer stock;
        
        // Getters and Setters...
    }}

5. 主程序入口

java
public class WeidianItemDemo {
    
    public static void main(String[] args) {
        // 配置信息(请替换为实际值)
        String appKey = "your_app_key";
        String appSecret = "your_app_secret";
        String clientId = "your_client_id";
        String clientSecret = "your_client_secret";
        String itemId = "your_item_id";
        
        try {
            // 1. 获取 Access Token
            System.out.println("正在获取 Access Token...");
            String accessToken = WeidianAuthService.getAccessToken(clientId, clientSecret);
            System.out.println("Access Token 获取成功: " + accessToken.substring(0, 10) + "...");
            
            // 2. 调用商品详情接口
            System.out.println("正在获取商品详情...");
            WeidianItemService itemService = new WeidianItemService(appKey, appSecret);
            ItemDetail item = itemService.getItemDetail(accessToken, itemId);
            
            // 3. 输出结果
            System.out.println("\n========== 商品详情 ==========");
            System.out.println("商品ID: " + item.getItemId());
            System.out.println("商品名称: " + item.getName());
            System.out.println("当前售价: ¥" + item.getPrice());
            System.out.println("原价: ¥" + item.getOriginalPrice());
            System.out.println("库存: " + item.getStock());
            System.out.println("销量: " + item.getSoldCount());
            System.out.println("商品描述: " + item.getDescription());
            System.out.println("主图: " + item.getMainImage());
            if (item.getImages() != null) {
                System.out.println("图片数量: " + item.getImages().size());
            }
            
        } catch (Exception e) {
            System.err.println("程序执行异常: " + e.getMessage());
            e.printStackTrace();
        }
    }}

四、接口返回数据示例

调用成功后,接口返回的 JSON 数据结构如下:
JSON
{
    "status": {
        "status_code": 0,
        "status_reason": "success"
    },
    "data": {
        "item_id": "123456789",
        "item_name": "示例商品标题",
        "price": 99.00,
        "original_price": 199.00,
        "stock": 100,
        "sold_count": 500,
        "description": "<p>商品详细描述...</p>",
        "main_image": "https://img.weidian.com/xxx.jpg",
        "images": [
            "https://img.weidian.com/xxx1.jpg",
            "https://img.weidian.com/xxx2.jpg"
        ],
        "skus": [
            {
                "sku_id": "sku_001",
                "properties": "颜色:红色;尺码:M",
                "price": 99.00,
                "stock": 30
            }
        ]
    }}

五、常见问题与解决方案

表格
问题原因解决方案
403 权限不足未申请接口权限或凭证错误检查 app_keyaccess_token 是否正确,确认已申请 micro.item_get 接口权限
签名错误签名算法或参数排序有误确认参数按 key 升序排列,签名字符串末尾正确追加 app_secret
429 请求频繁超出接口调用频率限制增加请求间隔,使用限流策略,或申请提升调用配额
数据字段为空商品信息未设置或权限未开启检查商品是否已上架,确认店铺开启了商品信息开放权限
access_token 过期Token 有效期通常为 7200 秒实现 Token 自动刷新机制,缓存 Token 并在过期前重新获取

六、进阶优化建议

1. Token 自动刷新与缓存

java
public class TokenManager {
    private String accessToken;
    private long expireTime;
    
    public synchronized String getValidToken(String clientId, String clientSecret) {
        if (accessToken == null || System.currentTimeMillis() > expireTime - 60000) {
            // 提前 1 分钟刷新
            accessToken = WeidianAuthService.getAccessToken(clientId, clientSecret);
            expireTime = System.currentTimeMillis() + 7200 * 1000;
        }
        return accessToken;
    }}

2. 连接池配置

java
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;public class HttpClientPool {
    private static final CloseableHttpClient httpClient;
    
    static {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(200);        // 最大连接数
        cm.setDefaultMaxPerRoute(50); // 每个路由最大连接数
        
        httpClient = HttpClients.custom()
            .setConnectionManager(cm)
            .setConnectionManagerShared(true)
            .build();
    }
    
    public static CloseableHttpClient getClient() {
        return httpClient;
    }}

3. 批量获取与异步处理

对于需要批量获取商品详情的场景,建议使用线程池配合 CompletableFuture 实现异步并行调用,提升整体效率。

七、总结

本文完整介绍了使用 Java 调用微店 micro.item_get 商品详情接口的流程,包括:
  1. 认证授权:通过 OAuth2 获取 access_token
  2. 签名机制:按规范生成 MD5 签名确保请求安全
  3. 接口调用:使用 Apache HttpClient 发送 HTTP 请求
  4. 数据解析:使用 Gson 解析 JSON 响应并映射为 Java 对象
  5. 异常处理:针对常见错误提供解决方案
在实际开发中,建议根据业务需求进一步完善异常处理、日志记录和性能优化。同时,务必遵守微店开放平台的使用协议,合理控制调用频率,避免对平台造成过大压力


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

返回列表

上一篇:淘宝类目详情接口开发文档

没有最新的文章了...

相关文章

接口添加白名单 IP 地址的作用与价值

在数字化转型加速的今天,API 接口已成为企业数据流通和业务协作的核心枢纽。然而,接口暴露在公网的同时也带来了巨大的安全隐患。IP 白名单机制作为一种"默认拒绝、显式授权"的安全策...

Java 是什么?—— 一门改变世界的编程语言深度解析

一、Java 的诞生:一杯咖啡引发的革命1991年,Sun Microsystems(太阳微系统公司)的工程师詹姆斯·高斯林(James Gosling)带领团队启动了一个名为"Green...

商品详情接口高并发架构:独立资源池与并发控制实战

电商平台的商品详情接口是流量最密集的入口之一。在秒杀、大促等场景下,QPS 可达数万甚至数十万。本文将深入讲解如何设计一个支持高并发访问、具备独立资源池隔离的商品详情 API 接口,涵盖线程池、连接池...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。