物流一站式查询之顺丰接口篇
連載篇提前看
物流一站式查詢之TrackingMore篇
物流一站式查詢之順豐接口篇
物流一站式查詢之快遞100篇
前情提要
本篇內(nèi)容承接上篇《物流包裹一站式查詢(TrackingMore) 文末所說,順豐物流關(guān)閉了對(duì)第三方的物流接口,導(dǎo)致眾多第三方物流平臺(tái)查詢不到順豐快遞的物流信息。但是問題終歸是要解決滴,別家不行,咱就直接用順豐自家的。
原本網(wǎng)上找順豐物流信息查詢發(fā)現(xiàn)順豐開放平臺(tái)? 看了下介紹,因?yàn)橐彩琼権S的平臺(tái),也沒多想,看到流程還是比較清晰的。
本來想找在線客服咨詢下,結(jié)果發(fā)現(xiàn)在線客服有的只是一個(gè)群號(hào),而且還不能加人了,于是乎就按照接入流程開始操作,本地都開發(fā)的差不多了,后來意外聯(lián)系到一個(gè)順豐的IT人員,通過他得知,順豐物流信息接口已經(jīng)轉(zhuǎn)到另一個(gè)部門和平臺(tái)操作了,這個(gè)開放平臺(tái)已經(jīng)幾乎沒有人維護(hù)了。于是再他的協(xié)助下,我得到了最新的對(duì)接文檔。按找新文檔,之前的開發(fā)的全部得重寫,請(qǐng)求接口不一樣,數(shù)據(jù)傳輸和接收方式不一樣,由開放平臺(tái)的Json格式到現(xiàn)在用XML傳輸。這里貼一下接入規(guī)范文檔的目錄
順便提一下 順豐路由查詢接口 就是 查詢物流信息的接口,不過再順豐平臺(tái)使用此接口有個(gè)前提條件,就是必須是順豐的月結(jié)用戶。登陸 順豐平臺(tái) 可以查看到基本信息
注:①不是順豐月結(jié)卡 用戶 或者企業(yè),不能接入路由查詢?? ② 不是通過順豐接口下單的運(yùn)單號(hào),不能接入路由推送接口,換而言之,如果是通過順豐大客戶發(fā)貨系統(tǒng)或者其他方式進(jìn)行的打單獲取到的快遞單號(hào),無法對(duì)此單進(jìn)行訂閱推送操作。
?
開發(fā)篇
看完基本流程和接入規(guī)范之后,就可以按照文檔規(guī)范進(jìn)行編碼。因?yàn)槟壳拔抑挥玫搅藰?biāo)紅的三個(gè)接口,所以接下來對(duì)這三個(gè)接口一一講解。(注開發(fā)之前本機(jī)IP需要得到官方授權(quán),不然會(huì)請(qǐng)求會(huì)返回IP未授權(quán))
下單接口
1.1. 功能描述
下訂單接口根據(jù)客戶需要,可提供以下三個(gè)功能:
1)????? 客戶系統(tǒng)向順豐下發(fā)訂單。
2)????? 為訂單分配運(yùn)單號(hào)。
3)????? 篩單(可選,具體商務(wù)溝通中雙方約定,由順豐內(nèi)部為客戶配置)。
此接口也用于路由推送注冊(cè)。客戶的順豐運(yùn)單號(hào)不是通過此下訂單接口獲取,但卻需要獲取BSP的路由推送時(shí),需要通過此接口對(duì)相應(yīng)的順豐運(yùn)單進(jìn)行注冊(cè)以使用BSP的路由推送接口。
按照接入文檔所說 接口通信協(xié)議支持WEBSERVICE及HTTP/POST協(xié)議,以下我是采用HTTP/POST協(xié)議 開發(fā)
其中 密匙生成規(guī)則:
- 先把XML報(bào)文與checkword前后連接。
- 把連接后的字符串做MD5編碼。
- 把MD5編碼后的數(shù)據(jù)進(jìn)行Base64編碼,此時(shí)編碼后的字符串即為校驗(yàn)碼。
?
元素的請(qǐng)求和響應(yīng)內(nèi)容字段和描述比較多,這里就不一一貼出來了,文末會(huì)提供接口文檔下載地址。
① 編寫下單操作實(shí)體類
#region 下單操作實(shí)體public class OrderService{/// <summary>/// 訂單號(hào)/// </summary>public string orderid { get; set; }/// <summary>/// 運(yùn)單號(hào) 順豐運(yùn)單號(hào),一個(gè)訂單只能有一個(gè)母單號(hào),如果是子母單的情況,以半角逗號(hào)分隔,主單號(hào)在第一個(gè)位置,如 “755123456789,001123456789,002123456789” ,/// 對(duì)于路由推送注冊(cè),此字段為必填。/// </summary>public string mailno { get; set; }/// <summary>/// 寄件方公司名稱,如果需要 生成電子運(yùn)單,則為必填。/// </summary>public string j_company { get; set; }/// <summary>/// 寄件方聯(lián)系人,如果需要生成電子運(yùn)單,則為必填。/// </summary>public string j_contact { get; set; }/// <summary>/// 寄件方聯(lián)系電話,如果需要生成電子運(yùn)單,則為必填。/// </summary>public string j_tel { get; set; }/// <summary>/// 寄件方手機(jī)/// </summary>public string j_mobile { get; set; }/// <summary>/// 寄件方詳細(xì)地址/// </summary>public string j_address { get; set; }/// <summary>/// 到件方公司名稱/// </summary>public string d_company { get; set; }/// <summary>/// 收件方聯(lián)系人/// </summary>public string d_contact { get; set; }/// <summary>/// 收件人聯(lián)系電話/// </summary>public string d_tel { get; set; }/// <summary>/// 收件人手機(jī)/// </summary>public string d_mobile { get; set; }/// <summary>/// 收件人詳細(xì)地址/// </summary>public string d_address { get; set; }/// <summary>/// 包裹數(shù)(1個(gè)包裹對(duì)應(yīng)一個(gè)運(yùn)單號(hào))/// </summary>public int parcel_quantity { get; set; }/// <summary>/// 快件產(chǎn)品類別(只有再商務(wù)上與順豐約定的類別方可使用)/// </summary>public string express_type { get; set; }/// <summary>/// 順豐月結(jié)卡號(hào)/// </summary>public string custid { get; set; }/// <summary>/// 備注/// </summary>public string remark { get; set; }/// <summary>/// 訂單元素/// </summary>public OrderCargo OrderCargos { get; set; }}public class OrderCargo{/// <summary>/// 貨物名稱/// </summary>public string name { get; set; }}#endregion
View Code
② 定義三個(gè)全局屬性,因?yàn)樵賻讉€(gè)請(qǐng)求我們都會(huì)使用到這三個(gè)
//開發(fā)環(huán)境URL(文檔中有提供)private static readonly string Posturl = "http://bsp-ois.sit.sf-express.com:9080/bsp-ois/sfexpressService";//開發(fā)環(huán)境編碼(文檔中有提供)private static readonly string Bianma = "" + ConfigHelper.GetKey("bianma") + "";//開發(fā)環(huán)境密匙(文檔中有提供)private static readonly string Checkword = "" + ConfigHelper.GetKey("checkword") + "";
③ 下單操作方法
/// <summary>/// 下單操作方法/// </summary>/// <param name="model">下單操作實(shí)體</param>/// <returns> </returns>public static string GetHttpBack(OrderService model){//得到下單XML請(qǐng)求體var xml = Getxml(model);//生成密匙var pass = Convert.ToBase64String(MD5(xml + Checkword));//下單請(qǐng)求var str = GethttpBack(Posturl, "xml=" + xml + "&verifyCode=" + pass);return str;}
下單XML請(qǐng)求體如下
/// <summary>/// 構(gòu)建下單XML請(qǐng)求體/// </summary>/// <param name="model">下單操作實(shí)體</param>/// <returns></returns>private static string Getxml(OrderService model){string[] xmls ={"<Request service='OrderService' lang='zh-CN'>","<Head>" + ConfigHelper.GetKey("bianma") + "</Head>","<Body>","<Order","orderid='" + model.orderid + "'","j_company='" + model.j_company + "'","j_contact='" + model.j_contact + "'","j_tel='" + model.j_tel + "'","j_mobile='" + model.j_mobile + "'","j_address='" + model.j_address + "'","d_company='" + model.d_company + "'","d_contact='" + model.d_contact + "' ","d_tel='" + model.d_tel + "'","d_mobile='" + model.d_mobile + "'","d_address='" + model.d_address + "'","parcel_quantity='" + model.parcel_quantity + "'","express_type='" + model.express_type + "'","custid='" + model.custid + "'","remark='" + model.remark + "'>","<Cargo name='" + model.OrderCargos.name + "'></Cargo>","</Order>","</Body>","</Request>"};var xml = "";foreach (var s in xmls)if (xml == "")xml = s;elsexml += "\r\n" + s;return xml;}
View Code
MD5編碼方法如下
private static byte[] MD5(string str){var result = Encoding.UTF8.GetBytes(str);MD5 md5 = new MD5CryptoServiceProvider();var output = md5.ComputeHash(result);return output;}
下單請(qǐng)求方法如下
因?yàn)橄聠纬晒?huì)返回訂單號(hào)和運(yùn)單號(hào) 以及篩單結(jié)果,所以我們先定義一個(gè)返回的響應(yīng)報(bào)文模型容器
public class SfOrderResponse{/// <summary>/// 訂單號(hào)/// </summary>public string orderid { get; set; }/// <summary>/// 運(yùn)單號(hào)/// </summary>public string mailno { get; set; }/// <summary>/// 篩單結(jié)果 1 人工確認(rèn) 2 可收派 3 不可以收派/// </summary>public string filter_result { get; set; }}
View Code
private static string GethttpBack(string add, string post){var we = new WebClient();var sMessage = "";#region 下單try{SfOrderResponse sfresponse;if (post != ""){//編碼,尤其是漢字,事先要看下抓取網(wǎng)頁的編碼方式var postData = Encoding.UTF8.GetBytes(post);we.Headers.Clear();//采取POST方式必須加的header,如果改為GET方式的話就去掉這句話即可we.Headers.Add("Content-Type", "application/x-www-form-urlencoded");sMessage = Encoding.UTF8.GetString(we.UploadData(add, "POST", postData));//讀取XML資源中的指定節(jié)點(diǎn)內(nèi)容if (Convert.ToString(GetNodeValue(sMessage, "Head")) == "ERR")sMessage = XElement.Parse(sMessage).Value;elsesfresponse = new SfOrderResponse{//獲取xml中orderid、mailno、destcode等節(jié)點(diǎn)值orderid = GetXmlNodeValue(sMessage, "OrderResponse", "orderid"),mailno = GetXmlNodeValue(sMessage, "OrderResponse", "mailno"),filter_result = GetXmlNodeValue(sMessage, "OrderResponse", "filter_result")};}else{sMessage = Encoding.UTF8.GetString(we.DownloadData(add));if (Convert.ToString(GetNodeValue(sMessage, "Head")) == "ERR")sMessage = XElement.Parse(sMessage).Value;elsesfresponse = new SfOrderResponse{//獲取xml中orderid、mailno、destcode等節(jié)點(diǎn)值orderid = GetXmlNodeValue(sMessage, "OrderResponse", "orderid"),mailno = GetXmlNodeValue(sMessage, "OrderResponse", "mailno"),filter_result = GetXmlNodeValue(sMessage, "OrderResponse", "destcode")};}}catch{if (sMessage.IndexOf("8001") > 0) sMessage = "IP未授權(quán)";}#endregion//釋放資源
we.Dispose();return sMessage;}
View Code
其中讀取XML資源中指定節(jié)點(diǎn)內(nèi)容的方法如下
/// <summary>/// 讀取XML資源中的指定節(jié)點(diǎn)內(nèi)容/// </summary>/// <param name="source">XML資源</param>/// <param name="nodeName">節(jié)點(diǎn)名稱</param>/// <returns>節(jié)點(diǎn)內(nèi)容</returns>public static object GetNodeValue(string source, string nodeName){if (source == null || nodeName == null || source == "" || nodeName == "" ||source.Length < nodeName.Length * 2) return null;var start = source.IndexOf("<" + nodeName + ">") + nodeName.Length + 2;var end = source.IndexOf("</" + nodeName + ">");if (start == -1 || end == -1)return null;if (start >= end)return null;return source.Substring(start, end - start);}
View Code
獲取XML任意節(jié)點(diǎn)中某個(gè)屬性值的方法如下
/// <summary>/// 獲取xml任意節(jié)點(diǎn)中某個(gè)屬性值/// </summary>/// <param name="strXml">xml</param>/// <param name="strNodeName">節(jié)點(diǎn)名稱</param>/// <param name="strValueName">屬性名稱</param>/// <returns></returns>public static string GetXmlNodeValue(string strXml, string strNodeName, string strValueName){try{var xmlDoc = new XmlDocument();xmlDoc.LoadXml(strXml);var xNode = xmlDoc.SelectSingleNode("//" + strNodeName + "");var strValue = xNode.Attributes[strValueName].Value;return strValue;}catch (Exception ex){return ex.Message;}}
View Code
這樣我們就大概寫完了下單的請(qǐng)求邏輯代碼,現(xiàn)在我們可以寫一條測(cè)試數(shù)據(jù)進(jìn)行測(cè)試:
SfExpress.GetHttpBack(new OrderService(){orderid = ConfigHelper.GetKey("orderID"),j_company = "深圳市*******有限公司",j_contact = "瀟十一郎",j_mobile = "123456789",j_address = "這是發(fā)貨地址",d_company = "收件公司名稱",d_contact = "收件人",d_tel = "13237157517",d_mobile = "78946561",d_address = "收貨地址",parcel_quantity = 1,express_type = "1",custid = "9999999999",remark = "下單測(cè)試",OrderCargos = new OrderCargo() { name = "顯示屏" }});
請(qǐng)求過程全過程如下(若每個(gè)訂單號(hào)只能下單一次,若重復(fù)下單則會(huì)返回Err 重復(fù)下單,下面演示中,第一次演示的是重復(fù)下單,后來修改了訂單號(hào)重新跑了一次,就返回了OK,順利拿到了運(yùn)單號(hào))
?
?
路由查詢
有了上一個(gè)下單作為鋪墊,那我們路由查詢就比較好處理了。
1.1. 功能描述
客戶可通過此接口查詢順豐運(yùn)單路由,BSP會(huì)在響應(yīng)XML報(bào)文返回當(dāng)時(shí)點(diǎn)要求的全部路由節(jié)點(diǎn)信息。
此路由查詢接口支持兩類查詢方式:
1)????? 根據(jù)順豐運(yùn)單號(hào)查詢:查詢請(qǐng)求中提供接入編碼與運(yùn)單號(hào),BSP將驗(yàn)證接入編碼與所有請(qǐng)求運(yùn)單號(hào)的歸屬關(guān)系,系統(tǒng)只返回具有正確歸屬關(guān)系的運(yùn)單路由信息。
2)????? 根據(jù)客戶訂單號(hào)查詢:查詢請(qǐng)求中提供接入編碼與訂單號(hào),BSP將驗(yàn)證接入編碼與所有請(qǐng)求訂單號(hào)的歸屬關(guān)系,對(duì)于歸屬關(guān)系正確的訂單號(hào),找到對(duì)應(yīng)的運(yùn)單號(hào),然后返回訂單對(duì)應(yīng)運(yùn)單號(hào)的路由信息。適用于通過BSP下單的客戶訂單。
①編寫請(qǐng)求對(duì)應(yīng)的模型容器
/// <summary>/// 路由請(qǐng)求實(shí)體/// </summary>public class RotueSehachService{/// <summary>/// 查詢號(hào)類別 1運(yùn)單號(hào)查詢 2 訂單號(hào)查詢/// </summary>public int tracking_type { get; set; } = 1;/// <summary>/// 查詢號(hào) tracking_type=1 則此值是運(yùn)單號(hào) tracking_type為1=2 此值是訂單號(hào)/// </summary>public string tracking_number { get; set; }/// <summary>/// 1 標(biāo)準(zhǔn)路由查詢 2定制路由查詢/// </summary>public int method_type { get; set; } = 1;}
View Code
?
?
②根據(jù)響應(yīng)報(bào)文編寫對(duì)應(yīng)模型容器
public class RouteResponse{public string mailno { get; set; }public Route Route { get; set; }public string failMessage { get; set; } = string.Empty;}public class Route{public string accept_time { get; set; }public string accept_address { get; set; }public string remark { get; set; }public string opcode { get; set; }}
③路由查詢
public static List<RouteResponse> GetHttpRotueSheach(RotueSehachService model){//返回路由查詢XML請(qǐng)求體var xml = GetRoutexml(model);//MD5編碼var pass = Convert.ToBase64String(MD5(xml + Checkword));var strData = "xml=" + xml + "&verifyCode=" + pass;var result = GetRouteack(Posturl, "xml=" + xml + "&verifyCode=" + pass);return result;}
其中,構(gòu)建查詢XML請(qǐng)求體方法如下
/// <summary>/// 構(gòu)建路由查詢XML請(qǐng)求體/// </summary>/// <param name="model">路由查詢請(qǐng)求模型</param>/// <returns></returns>private static string GetRoutexml(RotueSehachService model){string[] xmls ={"<Request service='RouteService' lang='zh-CN'>","<Head>" + ConfigHelper.GetKey("bianma") + "</Head>","<Body>","<RouteRequest","tracking_type='" + model.tracking_type + "'","method_type='" + model.method_type + "'","tracking_number='" + model.tracking_number + "'/>","</Body>","</Request>"};var xml = "";foreach (var s in xmls)if (xml == "")xml = s;elsexml += "\r\n" + s;return xml;}
View Code
MD5方法下單請(qǐng)求中已經(jīng)貼出,其中GetRouteack請(qǐng)求方法如下
/// <summary>/// 路由查詢/// </summary>/// <param name="add">URL地址</param>/// <param name="post">編碼后的請(qǐng)求體</param>/// <returns></returns>private static List<RouteResponse> GetRouteack(string add, string post){var client = new WebClient();var sXml = "";var rutoelist = new List<RouteResponse>();try{//編碼,尤其是漢字,事先要看下抓取網(wǎng)頁的編碼方式var postData = Encoding.UTF8.GetBytes(post);client.Headers.Clear();//采取POST方式必須加的header,如果改為GET方式的話就去掉這句話即可client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");//sXml = Encoding.UTF8.GetString(client.UploadData(add, "POST", postData));sXml ="<?xml version='1.0' encoding='UTF-8'?><Response service='RouteService'><Head>OK</Head><Body>" +"<RouteResponse mailno='444032636081'><Route remark='順豐速運(yùn) 已達(dá)到武漢航空總站' accept_time='2018-01-19 06:50:55' accept_address='武漢市' opcode='50'/></RouteResponse>" +"<RouteResponse mailno='444032636081'><Route remark='順豐速運(yùn) 快遞正在發(fā)往武漢航空總站' accept_time='2018-01-18 18:18:55' accept_address='深圳市' opcode='50'/></RouteResponse>" +"<RouteResponse mailno='444032636081'><Route remark='順豐速運(yùn) 快遞達(dá)到深圳中轉(zhuǎn)站' accept_time='2018-01-18 15:12:55' accept_address='深圳市' opcode='50'/></RouteResponse>" +"<RouteResponse mailno='444032636081'><Route remark='順豐速運(yùn) 已收取快件' accept_time='2018-01-18 10:10:55' accept_address='深圳市' opcode='50'/></RouteResponse>" +"</Body></Response>";//判斷返回響應(yīng)體是否是ERRif (Convert.ToString(GetNodeValue(sXml, "Head")) == "ERR"){sXml = XElement.Parse(sXml).Value;rutoelist.Add(new RouteResponse {failMessage = XElement.Parse(sXml).Value});}//將xml字符串轉(zhuǎn)換為XML文檔var xmlDoc = XDocument.Parse(sXml);//獲取 文檔中子代元素 RouteResponse 的所有集合var nodelist = xmlDoc.Descendants("RouteResponse");if (nodelist != null){//獲取源集合中每個(gè)元素和子元素的集合var pieceareas = nodelist.Elements();foreach (var item in pieceareas)//循環(huán)添加到路由響應(yīng)容器集合中rutoelist.Add(new RouteResponse{//獲取XML mailno 屬性值mailno = GetXmlNodeValue(sXml, "RouteResponse", "mailno"),Route = new Route{opcode = item.Attribute("opcode").Value,accept_address = item.Attribute("accept_address").Value,accept_time = item.Attribute("accept_time").Value,remark = item.Attribute("remark").Value}});}}catch (Exception e){client.Dispose();rutoelist.Add(new RouteResponse {failMessage = XElement.Parse(sXml).Value});return rutoelist;}client.Dispose();return rutoelist;}
View Code
因?yàn)殚_發(fā)測(cè)試環(huán)境,沒有真實(shí)返回路由數(shù)據(jù),所以我這里構(gòu)造了幾個(gè)返回?cái)?shù)據(jù),寫到這里,我們路由查詢的大部分邏輯已經(jīng)完成,剩下就是調(diào)用和獲取到路由返回集合,循環(huán)拼接輸出我們想要的物流信息,具體操作可參考如下
#region 物流查詢var message = "";//順豐單獨(dú)處理if (companyNumber == "sf-express"){#region 獲取順豐物流信息//先進(jìn)行路由查詢,若返回未下單則再進(jìn)行下單操作。List<RouteResponse> result = SfExpress.GetHttpRotueSheach(new RotueSehachService(){tracking_number = ConfigHelper.GetKey("orderID")});if (result.Count >0){//循環(huán)返回的路由集合foreach (var item in result){//判斷錯(cuò)誤信息不為空if (item.failMessage == string.Empty){//將所有路由信息 按時(shí)間+路由地址拼接message += item.Route.accept_time + " " + item.Route.accept_address + "\r\n <br/>";}else{message += item.failMessage + "\r\n <br/> ";}}}else{return Content("暫無物流信息,請(qǐng)稍后重試");}#endregion
請(qǐng)求全過程如下
不知道大家留意看沒,再請(qǐng)求成功后往集合里面添加了第一條數(shù)據(jù)后,我返到上面查看了下收到的數(shù)據(jù),有一個(gè)opcode=50 的,順便說下,這個(gè)opcode是返回的路由操作碼,順豐有個(gè)專門的文檔記錄這些返回操作碼,那現(xiàn)在問題就來了,只是返回這個(gè)操作碼,我們并不能馬上知道操作碼對(duì)應(yīng)的文字描述。拿到操作碼去官方文檔上一個(gè)一個(gè)對(duì),也不是我們程序員的思維。所以,此時(shí)我們應(yīng)該想辦法解決,當(dāng)我們收到這個(gè)請(qǐng)求碼的時(shí)候,就自動(dòng)獲取它的描述,這樣就一目了然,方便我們后面操作。
?
單獨(dú)處理路由操作碼
其實(shí)改起來也不麻煩,我們需要把之前定義的Route 模型稍微改下 讓它繼承我們定義的操作碼和對(duì)應(yīng)的操作碼描述類即可 代碼如下:
public class Route: SfErrorEntity{public string accept_time { get; set; }public string accept_address { get; set; }public string remark { get; set; }// public string opcode { get; set; }}
在SfErrorEntity 中,我們?cè)偃ヌ幚聿僮鞔a和文字描述的問題。首先我們需要找到操作碼,操作碼模樣如下
?
?我們可以再項(xiàng)目中新建一個(gè)資源文件,名字叫SFcode.resx,然后添加資源我們選擇添加文本文件叫 sfResourse
接下來我們把文檔中的操作碼和文字描述 復(fù)制進(jìn)去
最后一步 就是編寫SfErrorEntity 類如下
public class SfErrorEntity{/// <summary>/// 定義一個(gè)字典存放操作碼和對(duì)應(yīng)描述/// </summary>private static Dictionary<int, string> _errorDic;/// <summary>/// 定一個(gè)操作碼集合/// </summary>public static Dictionary<int, string> ErrList{get{//獲取時(shí)先判斷字典中是否存在數(shù)據(jù),若存在則直接返回if (_errorDic != null && _errorDic.Count > 0)return _errorDic;//不存在,首先實(shí)例化當(dāng)前操作字典_errorDic = new Dictionary<int, string>();//讀取資源文件中的文本文件 先按行分隔var temp = SFCode.sfResourse.Split(new char[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);//拿到所有行,再按空格分隔foreach (var result in temp.Select(m => m.Split('\t'))){//將對(duì)應(yīng)的操作碼和描述添加到當(dāng)前字典中_errorDic.Add(int.Parse(result[0]), result[1]);}return _errorDic;}}/// <summary>/// 存放操作碼描述/// </summary>public string ErrDescription { get; set; }/// <summary>/// 操作碼/// </summary>private int _opcode;public int Opcode{get { return _opcode; }set{//寫入操作碼時(shí),就拿當(dāng)前寫入的值和操作碼集合中的數(shù)據(jù)做比較,同時(shí)返回對(duì)應(yīng)的操作碼描述_opcode = value;ErrDescription = ErrList.FirstOrDefault(m => m.Key == value).Value;}}}
View Code
最后的效果如下:
路由推送
上文中也提到過,只有是通過順豐接口下的訂單,才能走路由推送這個(gè)接口,還是先看一下關(guān)于路由推送的描述
1.1. 功能描述
該接口用于當(dāng)路由信息生產(chǎn)后向客戶主動(dòng)推送要求的順豐運(yùn)單路由信息。推送方式為增量推送,對(duì)于同一個(gè)順豐運(yùn)單的同一個(gè)路由節(jié)點(diǎn),不重復(fù)推送。
客戶需提供一個(gè)符合以下規(guī)范的HTTP URL,以接收BSP推送的信息。
1)????? 請(qǐng)求方法為:“application/x-www-form-urlencoded; charset=UTF-8”。
Key:content
2)????? 信息以URL編碼(字符集為UTF-8)的XML格式,通過HTTP POST方式推送給客戶。
3)????? 客戶在接收到信息后,需要先對(duì)其進(jìn)行URL解碼,得到相應(yīng)的XML。
4)????? 在客戶處理XML信息后,向BSP返回響應(yīng)XML報(bào)文,響應(yīng)XML報(bào)文結(jié)果只能為OK/ERR(參見XML報(bào)文說明),BSP將重新推送此次交易的所有信息。
路由推送整體和上面兩個(gè)類似,這個(gè)路有推送返回更簡(jiǎn)單,只有OK,或者Err。操作代碼如下
①定義推送模型容器
/// <summary>/// 路由推送模型/// </summary>public class RoutePushService:SfErrorEntity{/// <summary>/// 路由節(jié)點(diǎn)信息編號(hào),每一個(gè)id 代表一條不同的路由節(jié)點(diǎn)信息/// </summary>public int id { get; set; }/// <summary>/// 順豐運(yùn)單號(hào)/// </summary>public string mailno { get; set; }/// <summary>/// 客戶訂單號(hào)/// </summary>public string orderid { get; set; }/// <summary>/// 路由節(jié)點(diǎn)產(chǎn)生的時(shí)間,格式:YYYY-MM-DDHH24:MM:SS/// </summary>public DateTime acceptTime { get; set; }/// <summary>/// 路由節(jié)點(diǎn)發(fā)生的城市/// </summary>public string acceptAddress { get; set; } = "深圳";/// <summary>/// 路由節(jié)點(diǎn)具體描述/// </summary>public string remark { get; set; } = "上門收件";/// <summary>/// 路由節(jié)點(diǎn)操作碼/// </summary>// public string opCode { get; set; } = "50";}
View Code
#region 路由推送public static string RoutePushService(RoutePushService model){var xml = GetPushxml(model);var pass = Convert.ToBase64String(MD5(xml + Checkword));var strData = "xml=" + xml + "&verifyCode=" + pass;var strHeaders = "Content-Type: application/x-www-form-urlencoded\r\n";var bytePost = Encoding.UTF8.GetBytes(strData);var byteHeaders = Encoding.UTF8.GetBytes(strHeaders);var str = GetPushBack(Posturl, "xml=" + xml + "&verifyCode=" + pass);return str;}/// <summary>/// 構(gòu)建路由推送XML/// </summary>/// <param name="model">推送容器</param>/// <returns></returns>private static string GetPushxml(RoutePushService model){string[] xmls ={"<Request service='RouteService' lang='zh-CN'>","<Body>","<WaybillRoute","id='" + model.id + "'","mailno='" + model.mailno + "'","acceptTime='" + model.acceptTime.ToString("yyyy-MM-dd HH:mm:ss") + "'","acceptAddress='" + model.acceptAddress + "'","remark='" + model.remark + "'","opCode='50'/>","orderid='" + model.orderid + "'/>","</Body>","</Request>"};var xml = "";foreach (var s in xmls)if (xml == "")xml = s;elsexml += "\r\n" + s;return xml;}/// <summary>/// 推送/// </summary>/// <param name="add">URL</param>/// <param name="post">數(shù)據(jù)</param>/// <returns></returns>private static string GetPushBack(string add, string post){var pusthclient = new WebClient();var sXml = "";try{//編碼,尤其是漢字,事先要看下抓取網(wǎng)頁的編碼方式var postData = Encoding.UTF8.GetBytes(post);pusthclient.Headers.Clear();//采取POST方式必須加的header,如果改為GET方式的話就去掉這句話即可pusthclient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");sXml = Encoding.UTF8.GetString(pusthclient.UploadData(add, "POST", postData));//獲取Head是否是ERR,若不是,直接返回OKif (Convert.ToString(GetNodeValue(sXml, "Head")) == "ERR")sXml = XElement.Parse(sXml).Value;elsesXml = "OK";}catch (Exception e){sXml = e.Message;}pusthclient.Dispose();return sXml;}#endregion
這個(gè)路由推送 ,可以寫再服務(wù)里面,例如:客戶下單了,是代發(fā)貨狀態(tài),那么可以篩選出來,寫一個(gè)服務(wù),調(diào)用這個(gè)推送的接口,返回OK,則往數(shù)據(jù)庫一個(gè)標(biāo)示字段注明訂閱成功,否則反。
有了路由推送,然后就是回調(diào)了,推送成功之后,順豐會(huì)根據(jù)當(dāng)前物流單,若有物流信息發(fā)生改變,則會(huì)主動(dòng)推向提前約定好的回調(diào)地址上,此時(shí)只需要再回調(diào)方法中做一些業(yè)務(wù)上的處理即可。
?做完以上的開發(fā),剩下的就是給順豐那邊接口對(duì)接人員發(fā)郵件申請(qǐng)進(jìn)入聯(lián)調(diào)測(cè)試階段,聯(lián)調(diào)測(cè)試通過之后再那那邊簽協(xié)議接入正式環(huán)境投入使用。
注:聯(lián)調(diào)本機(jī)開發(fā)環(huán)境即可,無需部署線上。
?
關(guān)于順豐接口大概就這么多。下一篇 關(guān)于快遞100 物流接口詳解。
?
轉(zhuǎn)載于:https://www.cnblogs.com/zhangxiaoyong/p/8317229.html
總結(jié)
以上是生活随笔為你收集整理的物流一站式查询之顺丰接口篇的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: outlook怎么设置smtp认证(ou
- 下一篇: #边学边记 第三章 信息系统安全、服务管