feat(oa): 添加顺心捷达快递轨迹查询功能

- 新增 SxjdRouteQueryUtil工具类,用于查询顺心捷达快递轨迹
- 在OaExpressServiceImpl 中集成顺心捷达轨迹查询逻辑
- 实现了顺心捷达快递的轨迹查询和解析功能
This commit is contained in:
2025-08-13 11:31:40 +08:00
parent 7027a58143
commit 9093400922
2 changed files with 249 additions and 0 deletions

View File

@@ -244,6 +244,15 @@ public class OaExpressServiceImpl implements IOaExpressService {
oaExpressVo.setLastStatus(stoVo.getFirstStatusName());
}
}
if (expressType.equals("SXJD") && oaExpressVo.getStatus() == 1L) {
// 顺心捷达快递轨迹查询
String result = SxjdRouteQueryUtil.queryRoute(oaExpressVo.getExpressCode());
OaExpressVo sxjdVo = SxjdRouteQueryUtil.parseData(result);
if (sxjdVo != null) {
oaExpressVo.setLastUpdateTime(sxjdVo.getLastUpdateTime());
oaExpressVo.setLastStatus(sxjdVo.getLastStatus());
}
}
OaExpress add = BeanUtil.toBean(oaExpressVo, OaExpress.class);
baseMapper.updateById(add);;
}

View File

@@ -0,0 +1,240 @@
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.lang3.StringUtils;
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.security.MessageDigest;
import java.util.Date;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;
/**
* 顺心捷达路由地址查询工具
*/
public class SxjdRouteQueryUtil {
// 顺心捷达开放平台配置
private static final String APP_KEY = "d2f3f98ebff25410e1280ed89c01be75";
private static final String APP_CODE = "000466";
private static final String APP_SECRET = "902379cccfa1981f23bfb1833264de87";
private static final String SX_CUSTOMER_CODE = "C20001611";
// 顺心捷达API地址 - 根据环境选择
// 测试环境: https://oms.sit.sf-express.com:9010/api/gateway/service
// 生产环境: https://oms.sxjdfreight.com/api/gateway/service
// 沙箱环境: https://oms.sxjdfreight.com/api/gateway/sandbox
private static final String API_URL = "https://oms.sit.sf-express.com:9010/api/gateway/service";
/**
* 查询顺心捷达轨迹
* @param waybillId 运单号
* @return 查询结果JSON
*/
public static String queryRoute(String waybillId) {
return queryRoute(waybillId, null, null);
}
/**
* 查询顺心捷达轨迹(支持子单号查询)
* @param waybillId 运单号
* @param queryType 查询方式 1=查询母单轨迹 2=按子单号查询子单轨迹
* @param subWaybillId 子运单号
* @return 查询结果JSON
*/
public static String queryRoute(String waybillId, Integer queryType, String subWaybillId) {
try {
// 1. 构造请求数据
JSONObject data = new JSONObject();
data.put("waybillId", waybillId);
if (queryType != null) {
data.put("queryType", queryType);
}
if (StringUtils.isNotBlank(subWaybillId)) {
data.put("subWaybillId", subWaybillId);
}
String dataStr = data.toJSONString();
String timestamp = String.valueOf(System.currentTimeMillis());
String messageId = UUID.randomUUID().toString();
// 2. 生成签名
String dataDigest = generateDataDigest(dataStr, timestamp);
// 3. 构造表单参数
StringBuilder formParams = new StringBuilder();
formParams.append("apiType=SX_QUERY_TRACK");
formParams.append("&appKey=").append(URLEncoder.encode(APP_KEY, "UTF-8"));
formParams.append("&sxCustomerCode=").append(URLEncoder.encode(SX_CUSTOMER_CODE, "UTF-8"));
formParams.append("&timestamp=").append(URLEncoder.encode(timestamp, "UTF-8"));
formParams.append("&dataDigest=").append(URLEncoder.encode(dataDigest, "UTF-8"));
formParams.append("&messageId=").append(URLEncoder.encode(messageId, "UTF-8"));
formParams.append("&data=").append(URLEncoder.encode(dataStr, "UTF-8"));
String formBody = formParams.toString();
System.out.println("顺心捷达请求参数:" + formBody);
// 4. 发送POST请求
return doPost(API_URL, formBody);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 解析顺心捷达返回数据转为OaExpressVo
*/
public static OaExpressVo parseData(String result) {
if (result == null || result.trim().isEmpty()) {
return null;
}
try {
JSONObject json = JSON.parseObject(result);
// 检查响应状态
if (!json.getBooleanValue("success")) {
System.err.println("顺心捷达查询失败:" + json.getString("message"));
return null;
}
JSONObject data = json.getJSONObject("data");
if (data == null) return null;
JSONArray trackInfoList = data.getJSONArray("trackInfoList");
if (trackInfoList == null || trackInfoList.isEmpty()) return null;
// 获取最新的轨迹信息(第一条是最新的)
JSONObject latestTrack = trackInfoList.getJSONObject(0);
OaExpressVo vo = new OaExpressVo();
vo.setExpressCode(latestTrack.getString("waybillId"));
vo.setLastUpdateTime(new Date(latestTrack.getLongValue("operationTime")));
vo.setLastStatus(latestTrack.getString("step"));
vo.setRemark(latestTrack.getString("trackDesc"));
return vo;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 生成数据签名
* 签名算法MD5(data + timestamp + appSecret) 然后Base64编码
*/
private static String generateDataDigest(String data, String timestamp) throws Exception {
String signStr = data + timestamp + APP_SECRET;
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(signStr.getBytes(StandardCharsets.UTF_8));
byte[] digest = md.digest();
// 使用Base64编码URL安全模式
return new String(Base64.encodeBase64URLSafe(digest), StandardCharsets.UTF_8);
}
/**
* 发送POST请求
*/
private static String doPost(String urlStr, String formData) throws Exception {
URL url = new URL(urlStr);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Accept", "application/json");
connection.setConnectTimeout(10000);
connection.setReadTimeout(30000);
// 发送请求数据
try (OutputStream os = connection.getOutputStream()) {
byte[] input = formData.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
// 读取响应
StringBuilder response = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) {
response.append(line);
}
}
connection.disconnect();
return response.toString();
}
/**
* 判断是否为顺心捷达快递
* 根据运单号规则判断通常以S开头
*/
public static boolean isSxjdExpress(String waybillId) {
if (StringUtils.isBlank(waybillId)) {
return false;
}
// 顺心捷达运单号通常以S开头长度为12位
return waybillId.startsWith("S") && waybillId.length() >= 10;
}
/**
* MD5编码方法用于测试
*/
public static String md5Encode(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes(StandardCharsets.UTF_8));
byte[] digest = md.digest();
String r = new String(Base64.encodeBase64URLSafe(digest), StandardCharsets.UTF_8);
return r;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 测试方法
*/
public static void main(String[] args) {
// 测试签名生成
String data = "{ \"customerOrderNo\":\"QW2315464601\", \"orderChannelNo\":\"SE1231322\", \"orderType\":1,\"whetherEwb\":true,\"senderCompany\":\"测试公司890\", \"senderName\":\"测试收\", \"senderMobile\":\"12345667634\", \"senderTel\":\"0755-8658521\", \"senderProv\":\"广东省\", \"senderCity\":\"广州市\", \"senderArea\":\"越秀区\", \"senderAddress\":\"白云街道102号筒子巷\", \"receiverCompany\":\"绿色公司\", \"receiverName\":\"大师傅\", \"receiverTel\":\"021-85685452\", \"receiverMobile\":\"12313213213\", \"receiverProv\":\"广东省\", \"receiverCity\":\"广州市\", \"receiverArea\":\"海珠区\", \"receiverAddress\":\"华洲街道\", \"cargoName\":\"装修物品\", \"totalPackages\":3, \"totalWeight\":12, \"totalVolume\":15, \"pickupWay\":\"送货(不含上楼)\", \"backSignBill\":\"电子回单\", \"remark\":\"客户接入sxCustomerCode必填\", \"insuranceValue\":800, \"codAmount\":1024, \"fcodAmount\":620, \"getGoodMode\":\"上门接货\", \"orderSource\":\"PDD\",\"productName\":\"顺心包裹\",\"payType\":\"现付\",\"subMailNos\":\"20220426013,20220426014,20220426015\"}";
String timestamp = System.currentTimeMillis() + "";
String app_secret = "773bebe1bd75680a9e03ee579874d5a2";
String dataDigest = md5Encode(data + timestamp + app_secret);
System.out.println("测试签名生成:" + dataDigest);
// 测试轨迹查询
String waybillId = "S60201493742";
String result = queryRoute(waybillId);
System.out.println("顺心捷达原始返回:" + result);
OaExpressVo vo = parseData(result);
if (vo != null) {
System.out.println("解析后结果:");
System.out.println("运单号:" + vo.getExpressCode());
System.out.println("最新状态:" + vo.getLastStatus());
System.out.println("更新时间:" + vo.getLastUpdateTime());
System.out.println("描述:" + vo.getRemark());
} else {
System.out.println("解析失败或数据为空");
}
// 测试运单号判断
System.out.println("\n运单号判断测试");
System.out.println("S60201493742 是否为顺心捷达:" + isSxjdExpress("S60201493742"));
System.out.println("SF1234567890 是否为顺心捷达:" + isSxjdExpress("SF1234567890"));
System.out.println("1234567890 是否为顺心捷达:" + isSxjdExpress("1234567890"));
}
}