一、接口概述
微店开放平台提供的 micro.item_get 接口用于获取指定商品的详细信息,包括商品标题、价格、库存、SKU、主图、详情描述等核心数据。
接口基本信息
| 属性 | 说明 |
|---|---|
| 接口名称 | micro.item_get |
| 请求方式 | GET / POST |
| 数据格式 | JSON |
| 权限要求 | 需要申请商品读取权限 |
| 频率限制 | 通常 100次/分钟(视账号等级) |
二、准备工作
1. 注册开发者账号
-
注册开发者账号并创建应用
-
获取 AppKey 和 AppSecret
-
申请
micro.item_get接口权限
2. Maven依赖配置
<dependencies>
<!-- HTTP请求 -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>
<!-- JSON处理 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.43</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<!-- Lombok简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
</dependencies>
三、核心代码实现
1. 配置类
package com.example.weidian.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "weidian")
public class WeidianConfig {
/** 应用Key */
private String appKey;
/** 应用密钥 */
private String appSecret;
/** API网关地址 */
private String gatewayUrl = "https://api.vdian.com/api";
/** 接口版本 */
private String version = "1.0";
/** 数据格式 */
private String format = "json";
}
2. API请求工具类
package com.example.weidian.util;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
@Slf4j
@Component
public class WeidianApiClient {
private final CloseableHttpClient httpClient;
public WeidianApiClient() {
this.httpClient = HttpClients.createDefault();
}
/**
* 发送GET请求
*/
public JSONObject doGet(String url, Map<String, String> params) throws Exception {
// 构建URL参数
StringBuilder fullUrl = new StringBuilder(url);
if (!params.isEmpty()) {
fullUrl.append("?");
List<String> paramList = new ArrayList<>();
for (Map.Entry<String, String> entry : params.entrySet()) {
paramList.add(entry.getKey() + "=" + entry.getValue());
}
fullUrl.append(String.join("&", paramList));
}
HttpGet httpGet = new HttpGet(fullUrl.toString());
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
log.debug("API响应: {}", result);
return JSON.parseObject(result);
}
}
/**
* 发送POST请求
*/
public JSONObject doPost(String url, Map<String, String> params) throws Exception {
HttpPost httpPost = new HttpPost(url);
// 构建表单参数
List<NameValuePair> formParams = new ArrayList<>();
for (Map.Entry<String, String> entry : params.entrySet()) {
formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
httpPost.setEntity(new UrlEncodedFormEntity(formParams, StandardCharsets.UTF_8));
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
log.debug("API响应: {}", result);
return JSON.parseObject(result);
}
}
/**
* 生成签名(MD5加密)
* 签名规则:按参数名ASCII排序后拼接成字符串,最后拼接AppSecret,整体MD5加密
*/
public String generateSign(Map<String, String> params, String appSecret) {
// 过滤空值和sign字段
Map<String, String> filteredParams = new HashMap<>();
for (Map.Entry<String, String> entry : params.entrySet()) {
if (entry.getValue() != null && !entry.getValue().isEmpty()
&& !"sign".equals(entry.getKey())) {
filteredParams.put(entry.getKey(), entry.getValue());
}
}
// 按ASCII排序
List<String> keys = new ArrayList<>(filteredParams.keySet());
Collections.sort(keys);
// 拼接字符串
StringBuilder signStr = new StringBuilder();
for (String key : keys) {
signStr.append(key).append(filteredParams.get(key));
}
signStr.append(appSecret);
// MD5加密
return md5(signStr.toString());
}
/**
* MD5加密
*/
private String md5(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(str.getBytes(StandardCharsets.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().toLowerCase();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5加密失败", e);
}
}
}
3. 商品服务类(核心)
package com.example.weidian.service;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.example.weidian.config.WeidianConfig;
import com.example.weidian.model.ItemDetail;
import com.example.weidian.util.WeidianApiClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service
@RequiredArgsConstructor
public class WeidianItemService {
private final WeidianConfig config;
private final WeidianApiClient apiClient;
/**
* 获取商品详情
*
* @param itemId 商品ID
* @return 商品详情对象
*/
public ItemDetail getItemDetail(String itemId) {
try {
// 1. 构建公共参数
Map<String, String> params = new HashMap<>();
params.put("appkey", config.getAppKey());
params.put("method", "micro.item_get");
params.put("version", config.getVersion());
params.put("format", config.getFormat());
params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
// 2. 构建业务参数
JSONObject bizParam = new JSONObject();
bizParam.put("itemid", itemId);
params.put("param", bizParam.toJSONString());
// 3. 生成签名
String sign = apiClient.generateSign(params, config.getAppSecret());
params.put("sign", sign);
// 4. 发送请求
log.info("请求商品详情, itemId: {}, 参数: {}", itemId, params);
JSONObject response = apiClient.doPost(config.getGatewayUrl(), params);
// 5. 解析响应
return parseItemResponse(response);
} catch (Exception e) {
log.error("获取商品详情失败, itemId: {}", itemId, e);
throw new RuntimeException("获取商品详情失败: " + e.getMessage());
}
}
/**
* 解析API响应
*/
private ItemDetail parseItemResponse(JSONObject response) {
// 检查状态
Integer status = response.getInteger("status");
if (status == null || status != 0) {
String msg = response.getString("msg");
throw new RuntimeException("API错误: " + msg);
}
// 解析结果
JSONObject result = response.getJSONObject("result");
if (result == null) {
throw new RuntimeException("响应结果为空");
}
// 转换为实体对象
ItemDetail item = new ItemDetail();
item.setItemId(result.getString("itemid"));
item.setTitle(result.getString("item_name"));
item.setPrice(result.getBigDecimal("price"));
item.setOriginalPrice(result.getBigDecimal("original_price"));
item.setStock(result.getInteger("stock"));
item.setSoldNum(result.getInteger("sold_num"));
item.setMainImgs(result.getList("imgs", String.class));
item.setDetailHtml(result.getString("detail"));
item.setStatus(result.getInteger("status")); // 0-下架 1-上架
// SKU信息
if (result.containsKey("skus")) {
item.setSkus(JSON.parseArray(result.getString("skus"), ItemDetail.Sku.class));
}
// 店铺信息
item.setShopId(result.getString("shop_id"));
item.setShopName(result.getString("shop_name"));
log.info("商品解析成功: {}", item.getTitle());
return item;
}
}
4. 数据模型类
package com.example.weidian.model;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
public class ItemDetail {
/** 商品ID */
@JSONField(name = "itemid")
private String itemId;
/** 商品标题 */
@JSONField(name = "item_name")
private String title;
/** 当前售价 */
private BigDecimal price;
/** 原价 */
@JSONField(name = "original_price")
private BigDecimal originalPrice;
/** 库存数量 */
private Integer stock;
/** 销量 */
@JSONField(name = "sold_num")
private Integer soldNum;
/** 主图列表 */
private List<String> imgs;
/** 商品详情HTML */
private String detail;
/** 商品状态 0-下架 1-上架 */
private Integer status;
/** 店铺ID */
@JSONField(name = "shop_id")
private String shopId;
/** 店铺名称 */
@JSONField(name = "shop_name")
private String shopName;
/** SKU列表 */
private List<Sku> skus;
@Data
public static class Sku {
/** SKU ID */
@JSONField(name = "sku_id")
private String skuId;
/** SKU名称(如:红色-L码) */
@JSONField(name = "title")
private String title;
/** SKU价格 */
private BigDecimal price;
/** SKU库存 */
private Integer stock;
/** 属性组合(颜色、尺码等) */
@JSONField(name = "properties")
private String properties;
}
}
四、API响应示例
成功响应示例
{
"status": 0,
"msg": "success",
"result": {
"itemid": "123456789",
"item_name": "2024新款夏季纯棉T恤",
"price": "59.00",
"original_price": "99.00",
"stock": 150,
"sold_num": 2300,
"status": 1,
"imgs": [
"https://img.vdian.com/xxx1.jpg",
"https://img.vdian.com/xxx2.jpg"
],
"detail": "<p>商品详情HTML...</p>",
"shop_id": "987654321",
"shop_name": "时尚潮品店",
"skus": [
{
"sku_id": "sku_001",
"title": "白色-M",
"price": "59.00",
"stock": 50,
"properties": "颜色:白色;尺码:M"
},
{
"sku_id": "sku_002",
"title": "白色-L",
"price": "59.00",
"stock": 50,
"properties": "颜色:白色;尺码:L"
}
]
}
}
错误响应示例
{
"status": 1001,
"msg": "商品不存在或已下架",
"result": null
}
五、使用示例
package com.example.weidian.controller;
import com.example.weidian.model.ItemDetail;
import com.example.weidian.service.WeidianItemService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/item")
@RequiredArgsConstructor
public class ItemController {
private final WeidianItemService itemService;
/**
* 获取商品详情
* GET /api/item/detail?itemId=123456789
*/
@GetMapping("/detail")
public ItemDetail getItemDetail(@RequestParam String itemId) {
return itemService.getItemDetail(itemId);
}
}
六、关键注意事项
| 注意点 | 说明 |
|---|---|
| 签名算法 | 必须严格按ASCII排序参数,MD5结果转小写 |
| 时间戳 | 服务器时间需与微店服务器同步(误差<5分钟) |
| 频率限制 | 建议本地缓存,避免频繁调用 |
| 错误处理 | 需处理商品下架、权限不足、参数错误等场景 |
| 图片防盗链 | 微店图片可能有防盗链,需做代理或转存 |
七、进阶优化建议
-
缓存策略:使用Redis缓存商品信息,设置TTL(如30分钟)
-
异步更新:库存变动时通过Webhook实时同步
-
批量查询:如需批量获取,使用
micro.items_get接口 -
熔断降级:接口异常时返回缓存数据或默认值
522



被折叠的 条评论
为什么被折叠?



