需要各平台的key和secret,还需要开通物流详情或者物流轨迹的服务才行

This commit is contained in:
2025-07-23 15:26:27 +08:00
parent d6d75302f5
commit f0478a9f9f
4 changed files with 144 additions and 104 deletions

View File

@@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.oa.utils.BestRouteQueryUtil;
import com.ruoyi.oa.utils.SfRouteQueryUtil;
import com.ruoyi.oa.utils.StoRouteQueryUtil;
import com.ruoyi.oa.utils.ZtoTrackQueryUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -19,6 +20,7 @@ import com.ruoyi.oa.mapper.OaExpressMapper;
import com.ruoyi.oa.service.IOaExpressService;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Collection;
@@ -162,7 +164,8 @@ public class OaExpressServiceImpl implements IOaExpressService {
oaExpressVo.setLastUpdateTime(oaExpressVo1.getAcceptTime());
oaExpressVo.setLastStatus(oaExpressVo1.getFirstStatusName());
}
}else if (expressType.equals("YD") && oaExpressVo.getStatus() == 1L) {
}
if (expressType.equals("YD") && oaExpressVo.getStatus() == 1L) {
// 韵达快递轨迹查询
String result = com.ruoyi.oa.utils.YdRouteQueryUtil.queryRoute(oaExpressVo.getExpressCode());
OaExpressVo ydVo = com.ruoyi.oa.utils.YdRouteQueryUtil.parseData(result);
@@ -170,7 +173,8 @@ public class OaExpressServiceImpl implements IOaExpressService {
oaExpressVo.setLastUpdateTime(ydVo.getLastUpdateTime());
oaExpressVo.setLastStatus(ydVo.getLastStatus());
}
} else if (expressType.equals("YT") && oaExpressVo.getStatus() == 1L) {
}
if (expressType.equals("YT") && oaExpressVo.getStatus() == 1L) {
// 圆通快递轨迹查询
String result = com.ruoyi.oa.utils.YtRouteQueryUtil.queryRoute(oaExpressVo.getExpressCode());
OaExpressVo ytVo = com.ruoyi.oa.utils.YtRouteQueryUtil.parseData(result);
@@ -178,9 +182,10 @@ public class OaExpressServiceImpl implements IOaExpressService {
oaExpressVo.setLastUpdateTime(ytVo.getLastUpdateTime());
oaExpressVo.setLastStatus(ytVo.getLastStatus());
}
} else if (expressType.equals("STO") && oaExpressVo.getStatus() == 1L) {
}
if (expressType.equals("STO") && oaExpressVo.getStatus() == 1L) {
// 申通快递轨迹查询
String result = com.ruoyi.oa.utils.StoRouteQueryUtil.queryRoute(oaExpressVo.getExpressCode());
String result = StoRouteQueryUtil.queryRoute(Collections.singletonList(oaExpressVo.getExpressCode()));
OaExpressVo stoVo = com.ruoyi.oa.utils.StoRouteQueryUtil.parseData(result);
if (stoVo != null) {
oaExpressVo.setLastUpdateTime(stoVo.getLastUpdateTime());

View File

@@ -1,6 +1,7 @@
package com.ruoyi.oa.utils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.oa.domain.vo.OaExpressVo;
import org.apache.commons.codec.binary.Base64;
@@ -11,29 +12,47 @@ import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class StoRouteQueryUtil {
// 请填写你自己的key/secret
private static final String APP_KEY = "";
private static final String APP_KEY = "CAKWRqzGkVRVVFj";
private static final String APP_SECRET = "";
private static final String API_URL = "https://open.sto.cn/openapi/traceQuery"; // 申通轨迹查询接口
private static final String FROM_CODE = ""; // 你的from_code
private static final String API_URL = "http://cloudinter-linkgatewaytest.sto.cn/gateway/link.do";
// 申通签名算法 body+appSecretMD5后Base64
public static String sign(String body, String secret) {
String text = body + secret;
byte[] md5 = DigestUtils.md5(text);
return Base64.encodeBase64String(md5);
}
/**
* 查询申通轨迹
* @param mailNo 运单号
* 查询申通轨迹,支持批量单号
* @param waybillNos 运单号集合
* @return 查询结果JSON
*/
public static String queryRoute(String mailNo) {
public static String queryRoute(List<String> 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();
// 构造请求参数
JSONObject params = new JSONObject();
params.put("appKey", APP_KEY);
params.put("timestamp", System.currentTimeMillis());
params.put("sign", ""); // 签名算法见官方文档
params.put("method", "sto.trace.query.common");
params.put("format", "json");
params.put("billNo", mailNo);
// 其他参数可根据实际需求补充
params.put("api_name", "STO_TRACE_QUERY_COMMON");
params.put("content", contentStr);
params.put("from_appkey", APP_KEY);
params.put("from_code", FROM_CODE);
params.put("to_appkey", "sto_trace_query");
params.put("to_code", "sto_trace_query");
params.put("data_digest", sign(contentStr, APP_SECRET));
String jsonParams = params.toJSONString();
URL url = new URL(API_URL);
@@ -64,44 +83,44 @@ public class StoRouteQueryUtil {
}
/**
* 解析申通返回数据转为OaExpressVo
* 解析申通返回数据转为OaExpressVo列表
*/
public static OaExpressVo parseData(String result) {
if (result == null || result.isEmpty()) return null;
try {
JSONObject json = JSON.parseObject(result);
// 具体字段请根据申通返回格式调整
JSONObject data = json.getJSONObject("data");
if (data == null) return null;
OaExpressVo vo = new OaExpressVo();
vo.setExpressCode(data.getString("billNo"));
vo.setLastStatus(data.getString("status"));
vo.setLastUpdateTime(data.getDate("updateTime"));
// 你可以根据实际返回内容补充更多字段
return vo;
} catch (Exception e) {
e.printStackTrace();
if (result == null || result.trim().isEmpty()) {
return null;
}
}
// 申通签名算法 body+appSecretMD5后Base64
public static String sign(String body, String secret) {
String text = body + secret;
byte[] md5 = DigestUtils.md5(text);
return Base64.encodeBase64String(md5);
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"));
vo.setLastUpdateTime(last.getDate("opTime"));
vo.setLastStatus(last.getString("scanType"));
vo.setRemark(last.getString("memo"));
return vo;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// main方法测试
public static void main(String[] args) {
String mailNo = "测试单号";
String body = "{\"orderNo\":\"" + mailNo + "\"}";
String secret = "你的密钥";
String sign = sign(body, secret);
System.out.println("申通签名:" + sign);
String result = queryRoute(mailNo); // 你可以根据实际参数完善queryRoute
List<String> waybillNos = new ArrayList<>();
waybillNos.add("777031922725111"); // 测试单号
String result = queryRoute(waybillNos);
System.out.println("申通原始返回:" + result);
OaExpressVo vo = parseData(result);
System.out.println("解析后:" + vo);
if (vo != null) {
System.out.println("解析后:" + vo);
} else {
System.out.println("未解析到数据");
}
}
}

View File

@@ -3,14 +3,17 @@ package com.ruoyi.oa.utils;
import com.yundasys.OpenApiHttpUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONArray;
import com.ruoyi.oa.domain.vo.OaExpressVo;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
public class YdRouteQueryUtil {
// 请根据实际环境切换key/secret
private static final String APP_KEY = "004160"; // 生产环境
private static final String APP_SECRET = "ac9ee46d749e44b98e7a9e38f3c8f8f3"; // 生产环境
private static final String API_URL = "https://u-openapi.yundasys.com/openapi/outer/logictis/query";
private static final String API_URL = "https://openapi.yundaex.com/openapi/outer/logictis/query";
/**
* 查询韵达轨迹
@@ -26,22 +29,26 @@ public class YdRouteQueryUtil {
* 解析韵达返回数据转为OaExpressVo
*/
public static OaExpressVo parseData(String result) {
if (result == null || result.isEmpty()) return null;
if (result == null || result.trim().isEmpty()) {
return null;
}
try {
JSONObject json = JSON.parseObject(result);
// 具体字段请根据韵达返回格式调整
JSONObject data = json.getJSONObject("data");
if (data == null) return null;
JSONArray steps = data.getJSONArray("steps");
if (steps == null || steps.isEmpty()) return null;
JSONObject last = steps.getJSONObject(steps.size() - 1);
OaExpressVo vo = new OaExpressVo();
vo.setExpressCode(data.getString("mailNo"));
vo.setLastStatus(data.getString("status"));
vo.setLastUpdateTime(data.getDate("updateTime"));
// 你可以根据实际返回内容补充更多字段
vo.setExpressCode(data.getString("mailno"));
vo.setLastUpdateTime(last.getDate("time"));
vo.setLastStatus(last.getString("action"));
vo.setRemark(last.getString("description"));
return vo;
} catch (Exception e) {
e.printStackTrace();
return null;
}
return null;
}
// MD5签名算法如需自定义签名可用此方法
@@ -60,7 +67,7 @@ public class YdRouteQueryUtil {
// main方法测试
public static void main(String[] args) {
String mailNo = "测试单号";
String mailNo = "464569453467736";
String result = queryRoute(mailNo);
System.out.println("韵达原始返回:" + result);
OaExpressVo vo = parseData(result);

View File

@@ -1,6 +1,7 @@
package com.ruoyi.oa.utils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.oa.domain.vo.OaExpressVo;
import org.apache.commons.codec.binary.Base64;
@@ -11,32 +12,43 @@ import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class YtRouteQueryUtil {
// 请填写你自己的key/secret
private static final String APP_KEY = "";
private static final String APP_SECRET = "";
private static final String API_URL = "https://openapi.yto.net.cn/openApi/queryTrack"; // 圆通轨迹查询接口
// 请填写你自己的密钥、method、v
private static final String SECRET = "TEST";
private static final String METHOD = "123456"; // 示例
private static final String VERSION = "v1"; // 示例
private static final String API_URL = " https://openuat.yto56test.com:6443/open/track_query_adapter/v1/hpLkUl/TEST";
// 圆通签名算法 param+method+v+secretMD5后Base64
public static String sign(String param, String method, String v, String secret) {
String data = param + method + v;
byte[] md5 = DigestUtils.md5(data + secret);
return Base64.encodeBase64String(md5);
}
/**
* 查询圆通轨迹
* @param mailNo 运单号
* @param number 运单号
* @return 查询结果JSON
*/
public static String queryRoute(String mailNo) {
public static String queryRoute(String number) {
try {
// 构造请求参数,具体参数请参考圆通官方文档
JSONObject params = new JSONObject();
params.put("app_key", APP_KEY);
params.put("timestamp", System.currentTimeMillis());
params.put("sign", ""); // 签名算法见官方文档
params.put("method", "yto.marketing.waybill.trace.get");
params.put("format", "json");
params.put("user_id", ""); // 用户ID如有
params.put("waybill_no", mailNo);
// 其他参数可根据实际需求补充
String jsonParams = params.toJSONString();
String timestamp = String.valueOf(System.currentTimeMillis());
JSONObject paramObj = new JSONObject();
paramObj.put("NUMBER", number);
String param = paramObj.toJSONString();
String sign = sign(param, METHOD, VERSION, SECRET);
JSONObject req = new JSONObject();
req.put("timestamp", timestamp);
req.put("param", param);
req.put("sign", sign);
req.put("format", "JSON");
req.put("method", METHOD);
req.put("v", VERSION);
String jsonParams = req.toJSONString();
URL url = new URL(API_URL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
@@ -65,46 +77,43 @@ public class YtRouteQueryUtil {
}
/**
* 解析圆通返回数据转为OaExpressVo
* 解析圆通返回数据转为OaExpressVo列表
*/
public static OaExpressVo parseData(String result) {
if (result == null || result.isEmpty()) return null;
try {
JSONObject json = JSON.parseObject(result);
// 具体字段请根据圆通返回格式调整
JSONObject data = json.getJSONObject("data");
if (data == null) return null;
OaExpressVo vo = new OaExpressVo();
vo.setExpressCode(data.getString("mailNo"));
vo.setLastStatus(data.getString("status"));
vo.setLastUpdateTime(data.getDate("updateTime"));
// 你可以根据实际返回内容补充更多字段
return vo;
} catch (Exception e) {
e.printStackTrace();
if (result == null || result.trim().isEmpty()) {
return null;
}
}
// 圆通签名算法 param+method+v+secretMD5后Base64
public static String sign(String param, String method, String v, String secret) {
String data = param + method + v;
byte[] md5 = DigestUtils.md5(data + secret);
return Base64.encodeBase64String(md5);
try {
if (result.trim().startsWith("{")) {
JSONObject json = JSON.parseObject(result);
if (json.containsKey("map")) return null; // 查询为空
} else if (result.trim().startsWith("[")) {
JSONArray arr = JSON.parseArray(result);
if (arr.isEmpty()) return null;
JSONObject last = arr.getJSONObject(arr.size() - 1);
OaExpressVo vo = new OaExpressVo();
vo.setExpressCode(last.getString("waybill_No"));
vo.setLastUpdateTime(last.getDate("upload_Time"));
vo.setLastStatus(last.getString("infoContent"));
vo.setRemark(last.getString("processInfo"));
return vo;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// main方法测试
public static void main(String[] args) {
String mailNo = "测试单号";
String param = "{\"NUMBER\":\"" + mailNo + "\"}";
String method = "yto.marketing.waybill.trace.get"; // 请根据实际接口填写
String v = "1.01"; // 请根据实际接口填写
String secret = "你的密钥";
String sign = sign(param, method, v, secret);
System.out.println("圆通签名:" + sign);
String result = queryRoute(mailNo); // 你可以根据实际参数完善queryRoute
String number = "YT2600227881409"; // 测试单号
String result = queryRoute(number);
System.out.println("圆通原始返回:" + result);
OaExpressVo vo = parseData(result);
System.out.println("解析后:" + vo);
if (vo != null) {
System.out.println("解析后:" + vo);
} else {
System.out.println("解析后无数据");
}
}
}