做商城项目的,最好是可以直接在商城中查询订单的物流信息。所以新增物流查询功能。所用的物流查询,都由快递鸟提供。由于每天查询的次数不可预知,所以需要将查询的数据存放至数据库,避免重复查询,占用资源。
即时查询
使用情况:若从数据库查询不到物流消息时,则调用即时查询,将接收到的数据添加至数据库。作用情况:物流查询功能未更新之前的订单。 每日调用次数限制不超过3000次,API地址:http://www.kdniao.com/api-track
- 请求参数RequestData
//OrderCode 订单编号--订单编号可以为空,不是必填
private String OrderCode;
//ShipperCode 快递公司编码
private String ShipperCode;
//LogisticCode 物流单号
private String LogisticCode;
- 即时查询方法,MD5加密和base64编码官方demo中有,所以不再重复贴
//请求url
private static final String ReqURL="http://api.kdniao.cc/Ebusiness/EbusinessOrderHandle.aspx";
/**
* 即时查询
* @param ShipperCode 快递公司编码
* @param LogisticCode 物流运单号
* @throws Exception
*/
public static String TrackQuery(String ShipperCode, String LogisticCode) throws Exception {
TrackQueryRequestData trackQueryRequestData = new TrackQueryRequestData();
trackQueryRequestData.setShipperCode(ShipperCode);
trackQueryRequestData.setLogisticCode(LogisticCode);
String requestData = JSONObject.fromObject(trackQueryRequestData).toString();
Map<String, String> params = new HashMap<String, String>();
params.put("RequestData", urlEncoder(requestData, "UTF-8"));
params.put("EBusinessID", Commons.getKDNEBusinessID());
params.put("RequestType", "1002");
String dataSign = encrypt(requestData, Commons.getKDNAppKey(), "UTF-8");
params.put("DataSign", urlEncoder(dataSign, "UTF-8"));
params.put("DataType", "2");
String result = sendPost(ReqURL, params);
return result;
}
物流查询
使用情况:没新增一个订单,发货时去订阅物流消息,并将快递公司编码和快递单号存放至数据库,当物流消息更新之后,接到快递鸟推送过来的信息,再将信息update至数据库。 适用于日查询量>3000次的网站和系统,请求参数同即时查询。
- 接口地址: API测试地址:http://testapi.kdniao.cc:8081/api/dist
- API正式地址:http://api.kdniao.cc/api/dist 物流查询分为两个部分:订阅和推送。
订阅
根据快递公司编码和快递单号,对物流信息进行订阅,可以进入调试平台进行调试,方法如下:
//请求url--正式
private static final String ReqURL="http://api.kdniao.cc/api/dist";
//请求url--测试
// private static String ReqURL="http://testapi.kdniao.cc:8081/api/dist";
/**
* Json方式 物流追踪
* @throws Exception
*/
public static String SubscribeOrderTraces(String ShipperCode, String LogisticCode) throws Exception{
SubscribeRequestData subscribeRequestData = new SubscribeRequestData();
subscribeRequestData.setLogisticCode(LogisticCode);
subscribeRequestData.setShipperCode(ShipperCode);
String requestData = JSONObject.fromObject(subscribeRequestData).toString();
Map<String, String> params = new HashMap<String, String>();
params.put("RequestData", urlEncoder(requestData, "UTF-8"));
params.put("EBusinessID", Commons.getKDNEBusinessID());
params.put("RequestType", "1008");
String dataSign=encrypt(requestData, Commons.getKDNAppKey(), "UTF-8");
params.put("DataSign", urlEncoder(dataSign, "UTF-8"));
params.put("DataType", "2");
String result=sendPost(ReqURL, params);
//根据公司业务处理返回的信息......
return result;
}
订阅成功后,快递鸟会返回一个JSON数据,例如:
{
"EBusinessID": "1151847",
"UpdateTime": "2016-08-09 16:42:38",
"Success": true,
"Reason": ""
}
success返回true,则表示订阅成功。
推送
订阅成功后,当物流信息有更新的时候,快递鸟会发送一条推送,需要一个借口去接收消息,并将接收到的消息更新至数据库。方法如下:
/**
* 物流推送
* 接收快递鸟推送过来的消息,并将消息存放至数据库
* @param request
*/
@RequestMapping(value="subscribeExpress")
@ResponseBody
public String subscribeExpress(HttpServletRequest request){
JSONObject object = JSONObject.fromObject(request.getParameter("RequestData"));
ExpressDetail expressDetail;
JSONArray data = object.getJSONArray("Data");
if (!Commons.isNull(data) && data.size() > 0){
for (int i = 0; i < data.size(); i++) {
JSONObject detail = data.getJSONObject(i);
String shippercode = detail.getString("ShipperCode");
String logisticcode = detail.getString("LogisticCode");
ExpressDetail expressDetail1 = expressDetailService.selectByShipperAndLogistic(shippercode,logisticcode);
if (Commons.isNull(expressDetail1)) {
expressDetail = new ExpressDetail();
expressDetail.setLogisticcode(logisticcode);
expressDetail.setShippercode(shippercode);
expressDetail.setEdittime(new Date());
if (detail.containsKey("State")){
expressDetail.setState(detail.getString("State"));
}else {
expressDetail.setState("4");
}
if (detail.getString("Traces") == null || "[]".equals(detail.getString("Traces"))){
expressDetail.setTracedetail("[{\"error\":\""+detail.getString("Reason")+"\"}]");
} else {
expressDetail.setTracedetail(detail.getString("Traces"));
}
expressDetailService.insert(expressDetail);
} else {
expressDetail = new ExpressDetail();
expressDetail.setId(expressDetail1.getId());
expressDetail.setEdittime(new Date());
expressDetail.setState(detail.getString("State"));
expressDetail.setTracedetail(detail.getString("Traces"));
expressDetailService.updateById(expressDetail);
}
}
}
JSONObject result = new JSONObject();
result.put("EBusinessID", Commons.getKDNEBusinessID());
result.put("UpdateTime", new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
result.put("Success", true);
result.put("Reason","");
return result.toString();
}
注意:接收到推送之后,一定要返回一个数据,告诉快递鸟接受推送成功。如果传递至快递鸟的订单数据有误,则返回的信息里面,可能没有state和traces的值,此时返回值中,Reason应有原因,这种情况需要进行处理。
以下是代码
- 数据库字段
private Long id;
private String orderid;
private String shippercode;
private String logisticcode;
//物流状态:0-无轨迹,1-已揽收, 2-在途中 201-到达派件城市,3-签收,4-问题件
private String state;
private Date edittime;
private String tracedetail;
- 新订单发货
//发货时去订阅物流消息
String shippercode = GetShipperCode.getShipperCode(deliverOrder.getExpress());
String detail = ExpresseSubscribe.SubscribeOrderTraces(shippercode,deliverOrder.getExpressno());
JSONObject obj = JSONObject.fromObject(detail);
if (obj.containsValue("true")) {
//订阅消息之后,将相应数据存入数据库
ExpressDetail expressDetail = new ExpressDetail();
expressDetail.setOrderid(String.valueOf(orderid));
expressDetail.setShippercode(shippercode);
expressDetail.setLogisticcode(deliverOrder.getExpressno());
expressDetail.setEdittime(new Date());
expressDetail.setTracedetail("暂时没有物流信息。");
expressDetailService.insert(expressDetail);
}
- 获取数据库数据
/**
* 获取物流详情
*/
@RequestMapping(value = "getExpresseDetail",method = RequestMethod.POST)
@ResponseBody
public Result getExpresseDetail(@RequestBody Long orderid,HttpServletRequest request){
DeliverOrder deliverOrder = orderDao.queryDeliverOrderByOrderId(orderid);
if (deliverOrder==null){
return new Result(Result.Status.ERROR, "deliverOrder为空");
}
if (deliverOrder.getExpress()==null){
return new Result(Result.Status.ERROR, "快递为空");
}
if (deliverOrder.getExpressno()==null){
return new Result(Result.Status.ERROR, "快递编号为空");
}
String shippercode = GetShipperCode.getShipperCode(deliverOrder.getExpress());
ExpressDetail detail = expressDetailService.selectByShipperAndLogistic(shippercode,deliverOrder.getExpressno());
if (Commons.isNull(detail)) {
try {
String result = ExpresseTrack.TrackQuery(shippercode,deliverOrder.getExpressno());
JSONObject objDetail = JSONObject.fromObject(result);
ExpressDetail expressDetail = new ExpressDetail();
expressDetail.setOrderid(String.valueOf(orderid));
expressDetail.setShippercode(shippercode);
expressDetail.setLogisticcode(deliverOrder.getExpressno());
expressDetail.setEdittime(new Date());
if (objDetail.containsKey("State")){
expressDetail.setState(objDetail.getString("State"));
}
if (objDetail.getString("Traces")==null||objDetail.getString("Traces")=="[]"){
expressDetail.setTracedetail(objDetail.getString("Reason"));
}
expressDetail.setTracedetail(objDetail.getString("Traces"));
expressDetailService.insert(expressDetail);
detail = expressDetailService.selectByShipperAndLogistic(shippercode,deliverOrder.getExpressno());
} catch (Exception e) {
e.printStackTrace();
}
}
return new Result(Result.Status.OK,JSONObject.fromObject(detail));
}
- 通过快递公司编码和快递单号查询,订单编号是可选字段,如需要也可加上订单编号
@Override
public ExpressDetail selectByShipperAndLogistic(String shipperCode, String logisticCode) {
ExpressDetail expressDetail = new ExpressDetail();
expressDetail.setShippercode(shipperCode);
expressDetail.setLogisticcode(logisticCode);
ExpressDetail detail = expressDetailDao.selectOne(expressDetail);
return detail;
}
- ctrl.js--ng的数据处理
/**
* 物流详情
*/
scope.expresseDetail = function(){
var id = routeParams.id;
orderService.getExpresseDetail(id,function(data){
if (data.status == 'OK'){
console.log(data.message);
scope.expressDetail = data.message;
scope.tracedetails = [];
var detail = JSON.parse(scope.expressDetail.tracedetail);
console.log(scope.expressDetail);
//某事件内物流详情
var tracedetail = {};
//用于存放某天内的所有物流信息
var traces = [];
//用于存放整个物流所需多少天
var traces1 = [];
var dateForDay = {};
for (var i = 0; i < detail.length; i++){
var accepttime = detail[i].AcceptTime;
var time = accepttime.split(" ");
if (i == 0) {
dateForDay.date = time[0];
dateForDay.week = scope.getWeekName(new Date(time[0]).getDay());
tracedetail = {};
tracedetail.date = time[1];
tracedetail.detail = detail[i].AcceptStation;
traces1.push(tracedetail);
} else {
var upaccepttime = detail[i - 1].AcceptTime;
var uptime = upaccepttime.split(" ");
if (time[0] == uptime[0]) {
tracedetail = {};
tracedetail.date = time[1];
tracedetail.detail = detail[i].AcceptStation;
traces1.push(tracedetail);
if (i == detail.length -1) {
dateForDay.traces = traces1;
traces.push(dateForDay);
}
} else {
dateForDay.traces = traces1;
traces.push(dateForDay);
dateForDay = {};
dateForDay.date = time[0];
dateForDay.week = scope.getWeekName(new Date(time[0]).getDay());
traces1 = [];
tracedetail = {};
tracedetail.date = time[1];
tracedetail.detail = detail[i].AcceptStation;
traces1.push(tracedetail);
if (i == detail.length -1) {
dateForDay.traces = traces1;
traces.push(dateForDay);
}
}
}
}
scope.traces = traces;
console.log(scope.traces);
}
})
};
scope.getWeekName = function(index) {
var result = "";
switch (index){
case 0:
result = "周日";
break;
case 1:
result = "周一";
break;
case 2:
result = "周二";
break;
case 3:
result = "周三";
break;
case 4:
result = "周四";
break;
case 5:
result = "周五";
break;
case 6:
result = "周六";
break;
}
return result;
};
- 配置文件设置
- Commons.java
public static String getKDNEBusinessID(){
String EBusinessID = ConfigurationHolder.getInstance().getProperty("kuaidiniao.ebusinessid");
return EBusinessID;
}
public static String getKDNAppKey(){
String AppKey = ConfigurationHolder.getInstance().getProperty("kuaidiniao.appkey");
return AppKey;
}
2.application.properties,具体用****代替
#快递鸟
kuaidiniao.ebusinessid=********
kuaidiniao.appkey=*******************************
- spring-mvc.xml
<mvc:exclude-mapping path="/expressdetail/subscribeExpress"/>
4.applicationContext-cluster.xml
<property name="filterChainDefinitions">
<value>
/expressdetail/subscribeExpress = anon
</value>
</property>
最后
很多相似的内容就没有全部放上来。项目中设有拦截,所以配置文件中要做相应的处理。以上既是所有。