package com.gear.oa.utils; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.gear.oa.domain.vo.OaExpressVo; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; public class StoRouteQueryUtil { // 请填写你自己的key/secret private static final String APP_KEY = "CAKDSqybdwbzlUC"; private static final String APP_SECRET = "wGRMVXwIf8sp4m4W6tWJcOaDfY7EPblQ"; private static final String FROM_CODE = "CAKDSqybdwbzlUC"; // 你的from_code private static final String API_URL = "https://cloudinter-linkgateway.sto.cn/gateway/link.do"; // 申通签名算法 body+appSecret,MD5后Base64 public static String sign(String body, String secret) { String text = body + secret; byte[] md5 = DigestUtils.md5(text); return Base64.encodeBase64String(md5); } /** * 查询申通轨迹,支持批量单号 * @param waybillNos 运单号集合 * @return 查询结果JSON */ public static String queryRoute(List waybillNos) { try { // 构造content参数 JSONObject content = new JSONObject(); content.put("order", "asc"); JSONArray waybillNoList = new JSONArray(); for (String no : waybillNos) waybillNoList.add(no); content.put("waybillNoList", waybillNoList); String contentStr = content.toJSONString(); // 构造表单参数字符串 StringBuilder form = new StringBuilder(); form.append("api_name=STO_TRACE_QUERY_COMMON"); form.append("&content=").append(URLEncoder.encode(contentStr, "UTF-8")); form.append("&from_appkey=").append(APP_KEY); form.append("&from_code=").append(APP_KEY); form.append("&to_appkey=sto_trace_query"); form.append("&to_code=sto_trace_query"); form.append("&data_digest=").append(URLEncoder.encode(sign(contentStr, APP_SECRET), "UTF-8")); System.out.println("申通表单请求体:" + form); URL url = new URL(API_URL); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); OutputStream out = conn.getOutputStream(); out.write(form.toString().getBytes(StandardCharsets.UTF_8)); out.flush(); out.close(); StringBuilder sbResult = new StringBuilder(); if (200 == conn.getResponseCode()) { BufferedReader responseReader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); String readLine; while ((readLine = responseReader.readLine()) != null) { sbResult.append(readLine).append("\n"); } responseReader.close(); } return sbResult.toString(); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 解析申通返回数据,转为OaExpressVo列表 */ public static OaExpressVo parseData(String result) { if (result == null || result.trim().isEmpty()) { return null; } // 新增:如果返回是XML,直接打印并返回null if (result.trim().startsWith("<")) { System.err.println("申通返回XML错误:" + result); return null; } try { JSONObject json = JSON.parseObject(result); JSONObject data = json.getJSONObject("data"); if (data == null) return null; for (String waybillNo : data.keySet()) { JSONArray arr = data.getJSONArray(waybillNo); if (arr == null || arr.isEmpty()) continue; JSONObject last = arr.getJSONObject(arr.size() - 1); OaExpressVo vo = new OaExpressVo(); vo.setExpressCode(last.getString("waybillNo")); // 设置时间 String opTime = last.getString("opTime"); if (opTime != null) { try { // 解析时间格式 "2025-07-27 13:01:00" SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); vo.setAcceptTime(sdf.parse(opTime)); } catch (Exception e) { vo.setAcceptTime(null); } } // 设置状态 - 使用scanType作为状态 vo.setFirstStatusName(last.getString("memo")); // 设置备注 - 使用memo作为备注 vo.setRemark(last.getString("scanType")); return vo; } } catch (Exception e) { e.printStackTrace(); } return null; } // main方法测试 public static void main(String[] args) { List waybillNos = new ArrayList<>(); waybillNos.add("777326763655544"); // 测试单号 String result = queryRoute(waybillNos); System.out.println("申通原始返回:" + result); OaExpressVo vo = parseData(result); if (vo != null) { System.out.println("解析后:" + vo); } else { System.out.println("未解析到数据"); } } }