物流一站式查詢之順豐接口篇


連載篇提前看

物流一站式查詢之TrackingMore篇

物流一站式查詢之順豐接口篇

物流一站式查詢之快遞100篇

前情提要

本篇內容承接上篇《物流包裹一站式查詢(TrackingMore) 文末所說,順豐物流關閉了對第三方的物流接口,導致眾多第三方物流平台查詢不到順豐快遞的物流信息。但是問題終歸是要解決滴,別家不行,咱就直接用順豐自家的。

原本網上找順豐物流信息查詢發現順豐開放平台  看了下介紹,因為也是順豐的平台,也沒多想,看到流程還是比較清晰的。

本來想找在線客服咨詢下,結果發現在線客服有的只是一個群號,而且還不能加人了,於是乎就按照接入流程開始操作,本地都開發的差不多了,后來意外聯系到一個順豐的IT人員,通過他得知,順豐物流信息接口已經轉到另一個部門和平台操作了,這個開放平台已經幾乎沒有人維護了。於是再他的協助下,我得到了最新的對接文檔。按找新文檔,之前的開發的全部得重寫,請求接口不一樣,數據傳輸和接收方式不一樣,由開放平台的Json格式到現在用XML傳輸。這里貼一下接入規范文檔的目錄

順便提一下 順豐路由查詢接口 就是 查詢物流信息的接口,不過再順豐平台使用此接口有個前提條件,就是必須是順豐的月結用戶。登陸 順豐平台 可以查看到基本信息

注:①不是順豐月結卡 用戶 或者企業,不能接入路由查詢   ② 不是通過順豐接口下單的運單號,不能接入路由推送接口,換而言之,如果是通過順豐大客戶發貨系統或者其他方式進行的打單獲取到的快遞單號,無法對此單進行訂閱推送操作。

 

開發篇

看完基本流程和接入規范之后,就可以按照文檔規范進行編碼。因為目前我只用到了標紅的三個接口,所以接下來對這三個接口一一講解。(注開發之前本機IP需要得到官方授權,不然會請求會返回IP未授權)

下單接口

1.1. 功能描述

下訂單接口根據客戶需要,可提供以下三個功能:

1)      客戶系統向順豐下發訂單。

2)      為訂單分配運單號。

3)      篩單(可選,具體商務溝通中雙方約定,由順豐內部為客戶配置)。

此接口也用於路由推送注冊。客戶的順豐運單號不是通過此下訂單接口獲取,但卻需要獲取BSP的路由推送時,需要通過此接口對相應的順豐運單進行注冊以使用BSP的路由推送接口。

按照接入文檔所說 接口通信協議支持WEBSERVICE及HTTP/POST協議,以下我是采用HTTP/POST協議 開發

其中 密匙生成規則:

  • 先把XML報文與checkword前后連接。
  • 把連接后的字符串做MD5編碼。
  • 把MD5編碼后的數據進行Base64編碼,此時編碼后的字符串即為校驗碼。

 

元素的請求和響應內容字段和描述比較多,這里就不一一貼出來了,文末會提供接口文檔下載地址。

① 編寫下單操作實體類

 #region 下單操作實體

    public class OrderService
    {
        /// <summary>
        ///     訂單號
        /// </summary>
        public string orderid { get; set; }

        /// <summary>
        ///     運單號 順豐運單號,一個訂單只能有一個母單號,如果是子母單的情況,以半角逗號分隔,主單號在第一個位置,如 “755123456789,001123456789,002123456789” ,
        ///     對於路由推送注冊,此字段為必填。
        /// </summary>
        public string mailno { get; set; }

        /// <summary>
        ///     寄件方公司名稱,如果需要 生成電子運單,則為必填。
        /// </summary>
        public string j_company { get; set; }

        /// <summary>
        ///     寄件方聯系人,如果需要生成電子運單,則為必填。
        /// </summary>
        public string j_contact { get; set; }

        /// <summary>
        ///     寄件方聯系電話,如果需要生成電子運單,則為必填。
        /// </summary>
        public string j_tel { get; set; }

        /// <summary>
        ///     寄件方手機
        /// </summary>
        public string j_mobile { get; set; }

        /// <summary>
        ///     寄件方詳細地址
        /// </summary>
        public string j_address { get; set; }

        /// <summary>
        ///     到件方公司名稱
        /// </summary>
        public string d_company { get; set; }

        /// <summary>
        ///     收件方聯系人
        /// </summary>
        public string d_contact { get; set; }

        /// <summary>
        ///     收件人聯系電話
        /// </summary>
        public string d_tel { get; set; }

        /// <summary>
        ///     收件人手機
        /// </summary>
        public string d_mobile { get; set; }

        /// <summary>
        ///     收件人詳細地址
        /// </summary>
        public string d_address { get; set; }

        /// <summary>
        ///     包裹數(1個包裹對應一個運單號)
        /// </summary>
        public int parcel_quantity { get; set; }

        /// <summary>
        ///     快件產品類別(只有再商務上與順豐約定的類別方可使用)
        /// </summary>
        public string express_type { get; set; }

        /// <summary>
        ///     順豐月結卡號
        /// </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

② 定義三個全局屬性,因為再幾個請求我們都會使用到這三個

        //開發環境URL(文檔中有提供)
        private static readonly string Posturl = "http://bsp-ois.sit.sf-express.com:9080/bsp-ois/sfexpressService";
        //開發環境編碼(文檔中有提供)
        private static readonly string Bianma = "" + ConfigHelper.GetKey("bianma") + "";
        //開發環境密匙(文檔中有提供)
        private static readonly string Checkword = "" + ConfigHelper.GetKey("checkword") + "";

③ 下單操作方法

       /// <summary>
        /// 下單操作方法
        /// </summary>
        /// <param name="model">下單操作實體</param>
        /// <returns> </returns>
        public static string GetHttpBack(OrderService model)
        {
            //得到下單XML請求體
            var xml = Getxml(model);
            //生成密匙
            var pass = Convert.ToBase64String(MD5(xml + Checkword));
            //下單請求
            var str = GethttpBack(Posturl, "xml=" + xml + "&verifyCode=" + pass);
            return str;
        }

下單XML請求體如下

/// <summary>
        /// 構建下單XML請求體
        /// </summary>
        /// <param name="model">下單操作實體</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;
                else
                    xml += "\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;
        }

下單請求方法如下

因為下單成功會返回訂單號和運單號 以及篩單結果,所以我們先定義一個返回的響應報文模型容器

  public class SfOrderResponse
    {
        /// <summary>
        ///     訂單號
        /// </summary>
        public string orderid { get; set; }

        /// <summary>
        ///     運單號
        /// </summary>

        public string mailno { get; set; }

        /// <summary>
        ///     篩單結果 1 人工確認 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 != "")
                {
                    //編碼,尤其是漢字,事先要看下抓取網頁的編碼方式
                    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資源中的指定節點內容
                    if (Convert.ToString(GetNodeValue(sMessage, "Head")) == "ERR")
                        sMessage = XElement.Parse(sMessage).Value;
                    else
                        sfresponse = new SfOrderResponse
                        {
                            //獲取xml中orderid、mailno、destcode等節點值
                            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;
                    else
                        sfresponse = new SfOrderResponse
                        {
                            //獲取xml中orderid、mailno、destcode等節點值
                            orderid = GetXmlNodeValue(sMessage, "OrderResponse", "orderid"),
                            mailno = GetXmlNodeValue(sMessage, "OrderResponse", "mailno"),
                            filter_result = GetXmlNodeValue(sMessage, "OrderResponse", "destcode")
                        };
                }
            }
            catch
            {
                if (sMessage.IndexOf("8001") > 0) sMessage = "IP未授權";
            }
            #endregion
            //釋放資源
            we.Dispose();
            return sMessage;
        }
View Code

其中讀取XML資源中指定節點內容的方法如下

 /// <summary>
        ///     讀取XML資源中的指定節點內容
        /// </summary>
        /// <param name="source">XML資源</param>
        /// <param name="nodeName">節點名稱</param>
        /// <returns>節點內容</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任意節點中某個屬性值的方法如下

 /// <summary>
        ///  獲取xml任意節點中某個屬性值
        /// </summary>
        /// <param name="strXml">xml</param>
        /// <param name="strNodeName">節點名稱</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

這樣我們就大概寫完了下單的請求邏輯代碼,現在我們可以寫一條測試數據進行測試:

   SfExpress.GetHttpBack(new OrderService()
            {
                orderid = ConfigHelper.GetKey("orderID"),
                j_company = "深圳市*******有限公司",
                j_contact = "瀟十一郎",
                j_mobile = "123456789",
                j_address = "這是發貨地址",
                d_company = "收件公司名稱",
                d_contact = "收件人",
                d_tel = "13237157517",
                d_mobile = "78946561",
                d_address = "收貨地址",
                parcel_quantity = 1,
                express_type = "1",
                custid = "9999999999",
                remark = "下單測試",
                OrderCargos = new OrderCargo() { name = "顯示屏" }
            });

請求過程全過程如下(若每個訂單號只能下單一次,若重復下單則會返回Err 重復下單,下面演示中,第一次演示的是重復下單,后來修改了訂單號重新跑了一次,就返回了OK,順利拿到了運單號)

 

 

路由查詢

有了上一個下單作為鋪墊,那我們路由查詢就比較好處理了。

1.1. 功能描述

客戶可通過此接口查詢順豐運單路由,BSP會在響應XML報文返回當時點要求的全部路由節點信息。

此路由查詢接口支持兩類查詢方式:

1)      根據順豐運單號查詢:查詢請求中提供接入編碼與運單號,BSP將驗證接入編碼與所有請求運單號的歸屬關系,系統只返回具有正確歸屬關系的運單路由信息。

2)      根據客戶訂單號查詢:查詢請求中提供接入編碼與訂單號,BSP將驗證接入編碼與所有請求訂單號的歸屬關系,對於歸屬關系正確的訂單號,找到對應的運單號,然后返回訂單對應運單號的路由信息。適用於通過BSP下單的客戶訂單。

①編寫請求對應的模型容器

/// <summary>
    ///     路由請求實體
    /// </summary>
    public class RotueSehachService
    {
        /// <summary>
        ///     查詢號類別 1運單號查詢  2 訂單號查詢
        /// </summary>
        public int tracking_type { get; set; } = 1;

        /// <summary>
        ///     查詢號 tracking_type=1 則此值是運單號 tracking_type為1=2 此值是訂單號
        /// </summary>
        public string tracking_number { get; set; }

        /// <summary>
        ///     1 標准路由查詢 2定制路由查詢
        /// </summary>
        public int method_type { get; set; } = 1;
    }
View Code

 

 

②根據響應報文編寫對應模型容器

 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請求體
            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;
        }

其中,構建查詢XML請求體方法如下

 /// <summary>
        /// 構建路由查詢XML請求體
        /// </summary>
        /// <param name="model">路由查詢請求模型</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;
                else
                    xml += "\r\n" + s;
            return xml;
        }
View Code

MD5方法下單請求中已經貼出,其中GetRouteack請求方法如下

 /// <summary>
        /// 路由查詢
        /// </summary>
        /// <param name="add">URL地址</param>
        /// <param name="post">編碼后的請求體</param>
        /// <returns></returns>
        private static List<RouteResponse> GetRouteack(string add, string post)
        {
            var client = new WebClient();
            var sXml = "";
            var rutoelist = new List<RouteResponse>();
            try
            {
                //編碼,尤其是漢字,事先要看下抓取網頁的編碼方式
                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='順豐速運 已達到武漢航空總站' accept_time='2018-01-19 06:50:55' accept_address='武漢市' opcode='50'/></RouteResponse>" +
                    "<RouteResponse mailno='444032636081'><Route remark='順豐速運 快遞正在發往武漢航空總站' accept_time='2018-01-18 18:18:55' accept_address='深圳市' opcode='50'/></RouteResponse>" +
                    "<RouteResponse mailno='444032636081'><Route remark='順豐速運 快遞達到深圳中轉站' accept_time='2018-01-18 15:12:55' accept_address='深圳市' opcode='50'/></RouteResponse>" +
                    "<RouteResponse mailno='444032636081'><Route remark='順豐速運 已收取快件' accept_time='2018-01-18 10:10:55' accept_address='深圳市' opcode='50'/></RouteResponse>" +
                    "</Body></Response>";
                //判斷返回響應體是否是ERR
                if (Convert.ToString(GetNodeValue(sXml, "Head")) == "ERR")
                {
                    sXml = XElement.Parse(sXml).Value;
                    rutoelist.Add(new RouteResponse {failMessage = XElement.Parse(sXml).Value});
                }

                //將xml字符串轉換為XML文檔
                var xmlDoc = XDocument.Parse(sXml);
                //獲取 文檔中子代元素 RouteResponse 的所有集合
                var nodelist = xmlDoc.Descendants("RouteResponse");
                if (nodelist != null)
                {
                    //獲取源集合中每個元素和子元素的集合
                    var pieceareas = nodelist.Elements();
                    foreach (var item in pieceareas)
                        //循環添加到路由響應容器集合中
                        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

因為開發測試環境,沒有真實返回路由數據,所以我這里構造了幾個返回數據,寫到這里,我們路由查詢的大部分邏輯已經完成,剩下就是調用和獲取到路由返回集合,循環拼接輸出我們想要的物流信息,具體操作可參考如下

      #region 物流查詢
            var message = "";
            //順豐單獨處理
            if (companyNumber == "sf-express")
            {
                #region 獲取順豐物流信息
                //先進行路由查詢,若返回未下單則再進行下單操作。
                List<RouteResponse> result = SfExpress.GetHttpRotueSheach(new RotueSehachService()
                {
                    tracking_number = ConfigHelper.GetKey("orderID")
                });
                if (result.Count >0)
                {
                    //循環返回的路由集合
                    foreach (var item in result)
                    {
                        //判斷錯誤信息不為空
                        if (item.failMessage == string.Empty)
                        {
                            //將所有路由信息 按時間+路由地址拼接
                            message += item.Route.accept_time + "   " + item.Route.accept_address + "\r\n <br/>";
                        }
                        else
                        {
                            message += item.failMessage + "\r\n <br/>  ";
                        }
                    }
                }
                else
                {
                    return Content("暫無物流信息,請稍后重試");
                }

            #endregion

請求全過程如下

不知道大家留意看沒,再請求成功后往集合里面添加了第一條數據后,我返到上面查看了下收到的數據,有一個opcode=50 的,順便說下,這個opcode是返回的路由操作碼,順豐有個專門的文檔記錄這些返回操作碼,那現在問題就來了,只是返回這個操作碼,我們並不能馬上知道操作碼對應的文字描述。拿到操作碼去官方文檔上一個一個對,也不是我們程序員的思維。所以,此時我們應該想辦法解決,當我們收到這個請求碼的時候,就自動獲取它的描述,這樣就一目了然,方便我們后面操作。

 

單獨處理路由操作碼

其實改起來也不麻煩,我們需要把之前定義的Route 模型稍微改下 讓它繼承我們定義的操作碼和對應的操作碼描述類即可 代碼如下:

    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 中,我們再去處理操作碼和文字描述的問題。首先我們需要找到操作碼,操作碼模樣如下

 

 我們可以再項目中新建一個資源文件,名字叫SFcode.resx,然后添加資源我們選擇添加文本文件叫 sfResourse

接下來我們把文檔中的操作碼和文字描述 復制進去

最后一步 就是編寫SfErrorEntity 類如下

  public class SfErrorEntity
    {
        /// <summary>
        /// 定義一個字典存放操作碼和對應描述
        /// </summary>
        private static Dictionary<int, string> _errorDic;

        /// <summary>
        /// 定一個操作碼集合
        /// </summary>
        public static Dictionary<int, string> ErrList
        {
            get
            {
                //獲取時先判斷字典中是否存在數據,若存在則直接返回
                if (_errorDic != null && _errorDic.Count > 0)
                    return _errorDic;
                //不存在,首先實例化當前操作字典
                _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')))
                {
                    //將對應的操作碼和描述添加到當前字典中
                    _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
            {
                //寫入操作碼時,就拿當前寫入的值和操作碼集合中的數據做比較,同時返回對應的操作碼描述
                _opcode = value;
                ErrDescription = ErrList.FirstOrDefault(m => m.Key == value).Value;
            }
        }
    }
View Code

最后的效果如下:

路由推送

上文中也提到過,只有是通過順豐接口下的訂單,才能走路由推送這個接口,還是先看一下關於路由推送的描述

1.1. 功能描述

該接口用於當路由信息生產后向客戶主動推送要求的順豐運單路由信息。推送方式為增量推送,對於同一個順豐運單的同一個路由節點,不重復推送。

客戶需提供一個符合以下規范的HTTP URL,以接收BSP推送的信息。

1)      請求方法為:“application/x-www-form-urlencoded; charset=UTF-8”。

Key:content

2)      信息以URL編碼(字符集為UTF-8)的XML格式,通過HTTP POST方式推送給客戶。

3)      客戶在接收到信息后,需要先對其進行URL解碼,得到相應的XML。

4)      在客戶處理XML信息后,向BSP返回響應XML報文,響應XML報文結果只能為OK/ERR(參見XML報文說明),BSP將重新推送此次交易的所有信息。

路由推送整體和上面兩個類似,這個路有推送返回更簡單,只有OK,或者Err。操作代碼如下

①定義推送模型容器

 /// <summary>
    ///     路由推送模型
    /// </summary>
    public class RoutePushService:SfErrorEntity
    {
        /// <summary>
        ///     路由節點信息編號,每一個id 代表一條不同的路由節點信息
        /// </summary>
        public int id { get; set; }

        /// <summary>
        ///     順豐運單號
        /// </summary>
        public string mailno { get; set; }

        /// <summary>
        ///     客戶訂單號
        /// </summary>
        public string orderid { get; set; }

        /// <summary>
        ///     路由節點產生的時間,格式:YYYY-MM-DDHH24:MM:SS
        /// </summary>
        public DateTime acceptTime { get; set; }

        /// <summary>
        ///     路由節點發生的城市
        /// </summary>
        public string acceptAddress { get; set; } = "深圳";

        /// <summary>
        ///     路由節點具體描述
        /// </summary>
        public string remark { get; set; } = "上門收件";

        /// <summary>
        ///     路由節點操作碼
        /// </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>
        /// 構建路由推送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;
                else
                    xml += "\r\n" + s;
            return xml;
        }

       /// <summary>
       /// 推送
       /// </summary>
       /// <param name="add">URL</param>
       /// <param name="post">數據</param>
       /// <returns></returns>
        private static string GetPushBack(string add, string post)
        {
            var pusthclient = new WebClient();
            var sXml = "";
            try
            {
                //編碼,尤其是漢字,事先要看下抓取網頁的編碼方式
                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,若不是,直接返回OK
                if (Convert.ToString(GetNodeValue(sXml, "Head")) == "ERR")
                    sXml = XElement.Parse(sXml).Value;
                else
                    sXml = "OK";
            }
            catch (Exception e)
            {
                sXml = e.Message;
            }
            pusthclient.Dispose();
            return sXml;
        }
        #endregion

這個路由推送 ,可以寫再服務里面,例如:客戶下單了,是代發貨狀態,那么可以篩選出來,寫一個服務,調用這個推送的接口,返回OK,則往數據庫一個標示字段注明訂閱成功,否則反。

有了路由推送,然后就是回調了,推送成功之后,順豐會根據當前物流單,若有物流信息發生改變,則會主動推向提前約定好的回調地址上,此時只需要再回調方法中做一些業務上的處理即可。

 做完以上的開發,剩下的就是給順豐那邊接口對接人員發郵件申請進入聯調測試階段,聯調測試通過之后再那那邊簽協議接入正式環境投入使用。

注:聯調本機開發環境即可,無需部署線上。

 

關於順豐接口大概就這么多。下一篇 關於快遞100 物流接口詳解。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM