第一步:基础准备工作

对接的丰桥三方官网地址:

http://qiao.sf-express.com/pages/developDoc/index.html?level2=535247

服务器需要有java环境:需要1.6或以上版本(目前用的java版本是1.8),具体安装步骤可百度搜索一下,安装完后查看下版本。

查看服务器时区,使用命令timedatectl | grep "Time zone" 查看,查看是否是 echo "Asia/Shanghai" > /etc/timezone如果不是需要时区设置成这样,不然请求面单服务生成面单图片的时候,上面打印时间会与实际生成的时间相差八小时。

服务器还需要有宋体黑体库(simsun.ttf和simhei.ttf),因为生成的面单图片上面字母生成需要服务器有这字体库。使用命令fc-list查看字体库列表有没有这两种字体库。

从顺丰丰桥官网下载面单自助打印SDK,下面这个目录文件是生成面单图片的服务。

上传到服务器上,启动这个服务命令是java -Dfile.encoding=utf-8 -jar csim_waybill_print_service_V1.1.*.jar port

启动端口默认是4040,如果不特别设置的话。

启动端口需要登录服务器云管理平台,在安全组中增加开放端口。

端口设置后,命令执行后,浏览器打开

http://服务所在服务器ip:4040/servertest.html

显示OK代表面单图片服务开启成功。

此处ip建议换成域名,这样安全些。

备注:这个面单服务建议做成长链接编写一个脚本让这个服务器不能停,避免服务停止,服务停止了就无法生成面单图片。

顺丰面单图片打印机型号:汉印(N41BT)

面单图片所用打印纸:热敏打印纸

电脑所需安装驱动下载地址:https://cn.hprt.com/XiaZai.html(搜索N41BT这个打印机对应的驱动)

驱动具体安装步骤丰桥上应该有视频教程,如果没有可以联系丰桥那边技术客服咨询进行安装。

以上基础准备工作完成,接下来进行第二大步,项目里封装面单打印sdk。

第二步:tp5框架里封装面单打印类

(1)对接顺丰接口类

common目录下新增logistics目录,common\logistics目录下新增LogisticsSdk类文件

namespace app\common\logistics;
class LogisticsSdk {
    //配置信息
    public  $SURFACE_SINGLE = array(
        'partnerID'=>'',//例如ghdfnf
        'checkword'=>'',//例如NZvqbVTXENbFkvhIXliWQRbshjdhjHJSDhjhj
        'serviceCode'=>'EXP_RECE_CREATE_ORDER',
        'call_url_box'=>'https://sfapi.sf-express.com/std/service',
        'monthlyCard'=>'',//例如 0200179234
        'cancelOrder'=>'EXP_RECE_UPDATE_ORDER',
        'queryOrder'=>'EXP_RECE_SEARCH_ORDER_RESP',
        'queryRoutes'=>'EXP_RECE_SEARCH_ROUTES'
    );


    public function __construct()
    {
        $this->partnerID    =$this->SURFACE_SINGLE['partnerID'];//顾客编码;
        $this->checkword    =$this->SURFACE_SINGLE['checkword'];//校验码
        $this->serviceCode  =$this->SURFACE_SINGLE['serviceCode'];//生成面单图片服务接口
        $this->call_url_box =$this->SURFACE_SINGLE['call_url_box'];//环境的地址
        $this->monthlyCard  =$this->SURFACE_SINGLE['monthlyCard'];//月结卡号
        $this->queryOrder   =$this->SURFACE_SINGLE['queryOrder'];//查询订单服务接口
        $this->cancelOrder  =$this->SURFACE_SINGLE['cancelOrder'];//取消订单服务接口
        $this->queryRoutes  =$this->SURFACE_SINGLE['queryRoutes'];//路由查询接口
    }


    /*顺丰订单取消*/
    public function cancelOrder($xiao_order_id){
        //发送数据包
        $sendData=[
            'dealType'=>2,
            'language'=>'zh-CN',
            'orderId'=>$xiao_order_id,
            'totalWeight'=>0,
            'waybillNoInfoList'=>[]
        ];
        //发送参数
        $post_data = array(
            'serviceCode' => $this->cancelOrder,
        );
        $res=$this->sign_send($sendData,$post_data);
        $resu=json_decode($res['apiResultData'],true);
        if($resu['success']==1){
            return json_encode(['code'=>1,'msg'=>'取消成功','data'=>[]]);
        }else{
            return json_encode(['code'=>0,'msg'=>$resu['errorMsg'],'data'=>[]]);
        }
    }


    /*顺丰路由查询*/
    public function queryRoutes($express_order){
            $sendData=[
                'trackingType'=>1,
                'trackingNumber'=>$express_order,
                'language'=>'0',
                'methodType'=>1
            ];
            $post_data = array(
                'serviceCode' => $this->queryRoutes,
            );
            $res=$this->sign_send($sendData,$post_data);
            $apiResultData=json_decode($res['apiResultData'],true);
            if(!empty($apiResultData['msgData'])){
                $routeResps=$apiResultData['msgData']['routeResps'];
                $routes=$routeResps[0]['routes'];
                if(!empty($routes)){
                    //更新快递状态
                    $opCode=array_column($routes,'opCode');
                    return json_encode(['code'=>1,'msg'=>'请求成功','data'=>$opCode]);
                }else{
                    return json_encode(['code'=>0,'msg'=>'没有查询到信息','data'=>[]]);
                }
            }else{
                return json_encode(['code'=>0,'msg'=>'请求失败','data'=>'']);
            }
    }


    //生成签名
    public function sign_send($sendData,$post_data){
        $msgData=json_encode($sendData);
        $timestamp = time();
        //通过MD5和BASE64生成数字签名
        $msgDigest = base64_encode(md5((urlencode($msgData .$timestamp. $this->checkword)), TRUE));
        $post_data['partnerID']=$this->partnerID;
        $post_data['requestID']=$this->create_uuid();
        $post_data['timestamp']=$timestamp;
        $post_data['msgData']  =$msgData;
        $post_data['msgDigest']=$msgDigest;
        $resultCont = $this->send_post($this->call_url_box, $post_data);
        $res=json_decode($resultCont,true);
        return $res;
    }

    //获取UUID
    public function create_uuid() {
        $chars = md5(uniqid(mt_rand(), true));
        $uuid = substr ( $chars, 0, 8 ) . '-'
            . substr ( $chars, 8, 4 ) . '-'
            . substr ( $chars, 12, 4 ) . '-'
            . substr ( $chars, 16, 4 ) . '-'
            . substr ( $chars, 20, 12 );
        return $uuid ;
    }

    //发送请求
    public function send_post($url,$post_data) {
        $postdata = http_build_query($post_data);
        $options = array(
            'http' => array(
                'method' => 'POST',
                'header' => 'Content-type:application/x-www-form-urlencoded;charset=utf-8',
                'content' => $postdata,
                'timeout' => 15 * 60 // 超时时间(单位:s)
            )
        );
        ini_set('user_agent','Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727;)');
        $context = stream_context_create($options);
        $result = file_get_contents($url, false, $context);
        return $result;
    }


    /*顺丰路由查询*/
    public function queryRoutesInfo($express_order){
        $sendData=[
            'trackingType'=>1,
            'trackingNumber'=>$express_order,
            'language'=>'0',
            'methodType'=>1
        ];
        $post_data = array(
            'serviceCode' => $this->queryRoutes,
        );
        $res=$this->sign_send($sendData,$post_data);

        $apiResultData=json_decode($res['apiResultData'],true);
        if($apiResultData['success']){
            if(!empty($apiResultData['msgData'])){
                $routeResps=$apiResultData['msgData']['routeResps'];
                $routes=$routeResps[0]['routes'];
                return json_encode(['code'=>1,'msg'=>'请求成功','data'=>$routes],320);
            }else{
                return json_encode(['code'=>0,'msg'=>'请求失败','data'=>'']);
            }
        }else{
            return json_encode(['code'=>0,'msg'=>'请求失败','data'=>'']);
        }

    }
}

(2) 调用顺丰面单服务类WaybillPrinterSdk文件

class WaybillPrinterSdk
{
    public  $host = 'http://test.logistics.xiaodongai.com';
    public  function createPhoto($data=[],$drug=[]){
        /**
         * *******2联150 丰密运单*************
         */
        /**
         * 调用打印机 不弹出窗口 适用于批量打印【二联单】
         */
//        $reqURL = "http://localhost:4040/sf/waybill/print?type=V2.0.FM_poster_100mm150mm&output=noAlertPrint";
        /**
         * 调用打印机 弹出窗口 可选择份数 适用于单张打印【二联单】
         */
//        $reqURL = "http://localhost:4040:4040/sf/waybill/print?type=V2.0.FM_poster_100mm150mm&output=print";
        /**
         * 直接输出图片的BASE64编码字符串 可以使用html标签直接转换成图片【二联单】
         */
        $reqURL = "http://面单服务所部署服务器ip:4040/sf/waybill/print?type=V2.0.FM_poster_100mm150mm&output=image";//ip建议换成域名,这样安全些
//        $reqURL = "http://localhost:4040/sf/waybill/print?type=V2.0.FM_poster_100mm150mm&output=image";

        /**
         * *******3联210 丰密运单*************
         */
        /**
         * 调用打印机 不弹出窗口 适用于批量打印【三联单】
         */
        $url10 = "http://localhost:4040/sf/waybill/print?type=V3.0.FM_poster_100mm210mm&output=noAlertPrint";
        /**
         * 调用打印机 弹出窗口 可选择份数 适用于单张打印【三联单】
         */
//        $reqURL = "http://localhost:4040/sf/waybill/print?type=V3.0.FM_poster_100mm210mm&output=print";

        /**
         * 直接输出图片的BASE64编码字符串 可以使用html标签直接转换成图片【三联单】
         */
//        $reqURL = "http://localhost:4040/sf/waybill/print?type=V3.0.FM_poster_100mm210mm&output=image";
        //1.根据业务需求确定请求地址,并确定是否替换版本
        $reqURL = $this->handle_url($reqURL,true);//true 不需要  false 需要

        //2.组装参数  丰密参数 true设置 false 不设置
        $post_json_data = $this->assembly_param($data,$drug);
        //3.发送请求
        $result = $this->send_post($reqURL, $post_json_data);
        $images_url='';
        if(empty($result)){
            return $images_url;
        }
        //如果url是打印图片 则保存图片到本地
        if(strpos($reqURL, "image")){
            //4.处理结果数据
            $imageData = $this->handle_data($result);
            //5图片保存到本地
            $images_url=$this->save_image($imageData);
        }
        return $images_url;
    }

    /**
     * 处理url
     * @param unknown $reqURL
     * @param unknown $notTopLogo true 不需要  false 需要
     * @return mixed
     */
    function handle_url($reqURL,$notTopLogo){

        if ( $notTopLogo && strpos($reqURL, "V2.0"))
        {
            $reqURL = str_replace("V2.0", "V2.1",$reqURL);;
        }

        if ($notTopLogo && strpos($reqURL,"V3.0"))
        {
            $reqURL = str_replace("V3.0", "V3.1",$reqURL);
        }

        return $reqURL;
    }
    /**
     * 组装参数
     * @param unknown $fengmi
     * @return string
     */
    /**
     * 组装参数
     * @param unknown $fengmi
     * @return string
     */
    function assembly_param($fengmi,$drug){
        //这个必填
        $waybillDtos['appId'] = $fengmi['appId'];//"SLKJ2019"; //对应丰桥平台获取的clientCode
        $waybillDtos['appKey'] = $fengmi['appKey'];//"FBIqMkZjzxbsZgo7jTpeq7PD8CVzLT4Q"; //对应丰桥平台获取的checkWord
//        $waybillDtos['isPrintLogo'] = "1"; // 1 需要打印logo,0 不需要
        $waybillDtos['mailNo'] =$fengmi['mailNo'];
        $waybillDtos['electric'] =$fengmi['electric'];
        //$waybillDto->mailNo="SF7551234567890,SF2000601520988,SF2000601520997";//子母单方式
        //签回单号  签单返回服务POD 会打印两份快单 其中第二份作为返寄的单==如有签回单业务需要传此字段值
        $waybillDtos['consignerProvince'] = $fengmi['consignerProvince'];
        $waybillDtos['consignerCity'] = $fengmi['consignerCity'];
        $waybillDtos['consignerCounty'] =$fengmi['consignerCounty'];
        $waybillDtos['consignerAddress'] = $fengmi['consignerAddress']; //详细地址建议最多30个字  字段过长影响打印效果
//        $waybillDtos['consignerCompany'] = $fengmi['consignerCompany'];
        $waybillDtos['consignerMobile'] = $fengmi['consignerMobile'];
        $waybillDtos['consignerName'] = $fengmi['consignerName'];
//        $waybillDtos['consignerShipperCode'] =$fengmi['consignerShipperCode'];
//        $waybillDtos['consignerTel'] = $fengmi['consignerTel'];
        //寄件人信息
        $waybillDtos['deliverProvince'] = $fengmi['deliverProvince'];
        $waybillDtos['deliverCity'] = $fengmi['deliverCity'];
        $waybillDtos['deliverCounty'] = $fengmi['deliverCounty'];
        $waybillDtos['deliverCompany'] = $fengmi['deliverCompany'];
        $waybillDtos['deliverAddress'] = $fengmi['deliverAddress']; //详细地址建议最多30个字  字段过长影响打印效果
        $waybillDtos['deliverName'] = $fengmi['deliverName'];
        $waybillDtos['deliverMobile'] = $fengmi['deliverMobile'];
//        $waybillDtos['deliverShipperCode'] = $fengmi['deliverShipperCode'];
//        $waybillDtos['deliverTel'] = $fengmi['deliverTel'];
//        $waybillDtos['destCode'] = $fengmi['destCode']; //目的地代码 参考顺丰地区编号
//        $waybillDtos['zipCode'] = $fengmi['zipCode']; //原寄地代码 参考顺丰地区编号
        //1 :标准快递   2.顺丰特惠   3: 电商特惠   5:顺丰次晨  6:顺丰即日  7.电商速配   15:生鲜速配
        $waybillDtos['expressType'] = $fengmi['expressType'];
        ///addedService
        //   COD代收货款价值 单位元   此项和月结卡号绑定的增值服务相关
//        $waybillDtos['codValue'] = $fengmi['codValue'];
//        $waybillDtos['insureValue'] = $fengmi['insureValue']; //声明保价价值  单位元
        $waybillDtos['monthAccount'] = $fengmi['monthAccount']; //月结卡号
        $waybillDtos['orderNo'] = "";
        $waybillDtos['payMethod'] = $fengmi['payMethod']; // 1寄方付 2收方付 3第三方月结支付
        $waybillDtos['childRemark'] = "";//子单号备注
        $waybillDtos['mainRemark'] = "";//主运单备注
        $waybillDtos['returnTrackingRemark'] = "";//签回单备注
        //加密项
        $waybillDtos['encryptCustName'] = false;//加密寄件人及收件人名称
        $waybillDtos['encryptMobile'] = true;//加密寄件人及收件人联系手机
        $waybillDtos['cargoInfoDtoList'] = $drug;
        $waybillDtos['returnTrackingNo']='';
        if ($fengmi)
        {
            $rlsMains['abFlag']=$fengmi['abFlag'];
            $rlsMains['codingMapping']=$fengmi['codingMapping'];
            $rlsMains['codingMappingOut']=$fengmi['codingMappingOut'];
            $rlsMains['destRouteLabel']=$fengmi['destRouteLabel'];
            $rlsMains['destTeamCode']=$fengmi['destTeamCode'];
            $rlsMains['printIcon']=$fengmi['printIcon'];
            $rlsMains['proCode']=$fengmi['proCode'];
            $rlsMains['qrcode']=$fengmi['qrcode'];
            $rlsMains['sourceTransferCode']=$fengmi['sourceTransferCode'];
            $rlsMains['waybillNo']=$fengmi['waybillNo'];
            $rlsMains['xbFlag']=$fengmi['xbFlag'];
            $rlsInfoDtoList=array($rlsMains);

            if (null != ($waybillDtos['returnTrackingNo']))
            {
                $rlsBacks['waybillNo'] = $waybillDtos['returnTrackingNo'];
                $rlsBacks['destRouteLabel'] = $fengmi['destRouteLabel'];
                $rlsBacks['printIcon'] = $fengmi['printIcon'];
                $rlsBacks['proCode'] = $fengmi['proCode'];
                $rlsBacks['abFlag'] =$fengmi['xbFlag'];
                $rlsBacks['xbFlag'] =$fengmi['xbFlag'];
                $rlsBacks['codingMapping'] =$fengmi['codingMapping'];
                $rlsBacks['codingMappingOut'] =$fengmi['codingMappingOut'];
                $rlsBacks['destTeamCode'] =$fengmi['destTeamCode'];
                $rlsBacks['sourceTransferCode'] =$fengmi['sourceTransferCode'];
                //对应下订单设置路由标签返回字段twoDimensionCode 该参
                $rlsBacks['qrcode'] =$fengmi['qrcode'];
                array_push($rlsInfoDtoList,$rlsBacks);
            }

            $waybillDtos['rlsInfoDtoList'] = $rlsInfoDtoList;

        }

        $waybillDtoList = array($waybillDtos);
        $post_json_data = json_encode($waybillDtoList,JSON_UNESCAPED_UNICODE);
        return  $post_json_data;
    }
    /**
     * 发送post请求
     *
     * @param string $url
     *            请求地址
     * @param array $post_data
     *            post键值对数据
     * @return string
     */
    function send_post($reqURL, $post_data)
    {
        //curl验证成功
        $ch = curl_init($reqURL);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_POSTFIELDS,$post_data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'Content-Type: application/json',
            'Content-Length: ' . strlen($post_data)
        ));

        $result = curl_exec($ch);
        if (curl_errno($ch)) {
            print curl_error($ch);
        }
        curl_close($ch);

        if(strpos($reqURL, "image")=== false){
            echo "\n";
            echo "返回:".$result;
        }
        return $result;
    }

    /**
     * 处理数据
     * @param unknown $result
     */
    function handle_data($result){
        $startIndex = strpos($result,"[")+1;
        $substrLen = strrpos($result,"]") - $startIndex;
        $imageData = substr($result,$startIndex,$substrLen);

        /**
         * 如果以 \ 开头 ,截取
         */
        if(strpos($imageData,"\\")===0){
            $imageData = substr($imageData,1);
        }

        /**
         * 如果以 \ 结尾 ,截取
         */
        if(substr_compare($imageData, "\\", -strlen("\\")) === 0){
            $imageData = substr($imageData,0,(strlen($imageData)-1));
        }

        //换行符替换为空
        str_replace("\\n", "",$imageData);
        return $imageData;
    }


    /**
     *保存图片到腾讯云cos
     * @param unknown $imageData
     */
    function save_image($imageData){
        $img_url    = base64_decode($imageData);
        $path       = 'logistics/'.date('Ymd');
        //调用上传sdk
        $CosSdk = new CosSdk();
        $res= $CosSdk->upload($img_url,$path);//此处上传到腾讯云cos,如果要上传到别的地方这块需调整存储位置
        $result = json_decode($res,true);

        if($result['code']==1){
            return $result['data']['url'];
        }else{
            echo  " 图片生成失败\n";
        }
    }

}

(3) 操作顺丰业务类LogoptSdk

namespace app\common\logistics;
use app\common\logistics\LogisticsSdk;
use app\common\logistics\WaybillPrinterSdk;

class LogoptSdk {
    public  $Logistics;
    public  $xiao_order_id;
    public function __construct()
    {
        //实例化对象
        $this->Logistics    =  new LogisticsSdk;
        $this->partnerID    =$this->Logistics->partnerID;//顾客编码;
        $this->checkword    =$this->Logistics->checkword;//校验码
        $this->serviceCode  =$this->Logistics->serviceCode;//生成面单图片服务接口
        $this->call_url_box =$this->Logistics->call_url_box;//环境的地址
        $this->monthlyCard  =$this->Logistics->monthlyCard;//月结卡号
        $this->queryOrder   =$this->Logistics->queryOrder;//查询订单服务接口
        $this->cancelOrder  =$this->Logistics->cancelOrder;//取消订单服务接口
        $this->queryRoutes  =$this->Logistics->queryRoutes;//路由查询接口
    }

    //生成唯一订单id,此订单号是用于从顺丰下单,自己生成的一个顺丰订单号
    public function createOrderid(){
        //获取时间戳
        $unix=microtime(true);
        $orderid=str_replace(".","",substr($unix,-11));
        $date=date('Ymd',time());
        $this ->xiao_order_id ='XIAO-'.$date.'-'.$orderid;

        return $this ->xiao_order_id;
    }

    /*1.生成运单号*/
    public function createOrder($drug_pay,$xiao_order_id){

        //发送数据包
        $sendData=[
            'cargoDetails'=> [
                ['amount'=>$drug_pay['drug_money'],'count'=>$drug_pay['total'],'name'=>'药品','unit'=>'盒','volume'=>0.0,'weight'=>0.1],
            ],
            'contactInfoList'=>[
                ['address'=>'广东省广州市天河区渔兴路小马驹创意园xx栋xxx室','city'=>'广州市','contact'=>'寄件人名称','county'=>'天河区','contactType'=>1,'mobile'=>'寄件人手机号','province'=>'广东省'],
                ['address'=>$drug_pay['detail_address'],'city'=>$drug_pay['city'],'company'=>'','contact'=>$drug_pay['name'],'contactType'=>2,'county'=>$drug_pay['city'].$drug_pay['area'],'mobile'=>$drug_pay['phone'],'province'=>$drug_pay['province']],
            ],
            'customsInfo'=>(object)[],
            'expressTypeId'=>202,//下单的时候选的顺丰产品类型,202是顺丰微小件产品类型,具体自己公司需要哪种产品类型结合自己公司需要
            'extraInfoList'=>[],
            'isOneselfPickup'=>0,
            'language'=>'zh-CN',
            'monthlyCard'=>$this->monthlyCard,
            'orderId'=>$xiao_order_id,
            'parcelQty'=>1,    //包裹数,一个包裹对应一个运单号;若包裹数大于1,则返回一个母运单号和N-1个子运单号
            'payMethod'=>1,  //付款方式,支持以下值:1:寄方付 2:收方付 3:第三方付
            'totalWeight'=>0,
            'is_docall'=>0      //等于0代表不需要通知顺丰小哥上门取件,等于1代表需要下单后自动通知顺丰小哥去寄件地址上门取件
        ];

        //发送参数
        $post_data = array(
            'serviceCode' => $this->serviceCode,
        );
        $res=$this->Logistics->sign_send($sendData,$post_data);

        return  $res;
    }

    /*2.生成面单图片*/
    public function createPhoto($raw,$drug_pay)
    {

        $data['mailNo']       = $raw['waybillNo'];
        //二维码信息
        $data['abFlag']       = $raw['abFlag'];
        $data['codingMapping']=$raw['codingMapping'];
        $data['codingMappingOut']=$raw['codingMappingOut'];
        $data['destRouteLabel']  =$raw['destRouteLabel'];
        $data['destTeamCode']    =$raw['destTeamCode'];
        $data['printIcon']       ='00000000';
        $data['proCode']         =$raw['proCode'];
        $data['qrcode']          =$raw['qrcode'];
        $data['sourceTransferCode']=$raw['sourceTransferCode'];
        $data['waybillNo']         =$raw['waybillNo'];
        $data['xbFlag']            =$raw['xbFlag'];

        //基础信息
        $data['appId'] =$this->partnerID;
        $data['appKey']=$this->checkword;
        $data['electric']='E';
        $data['expressType']=2;
        $data['monthAccount']=$this->monthlyCard;
        $data['payMethod']=1;

        //收件人信息
        $data['consignerProvince']=$drug_pay['province'];
        $data['consignerCity']=$drug_pay['city'];
        $data['consignerCounty']=$drug_pay['area'];
        $data['consignerAddress']=$drug_pay['detail_address'];
        $data['consignerCompany']='';
        $data['consignerMobile']=$drug_pay['phone'];
        $data['consignerName']=$drug_pay['name'];


        //寄件人信息
        $data['deliverProvince']='广东省';
        $data['deliverCity']='广州市';
        $data['deliverCounty']='天河区';
        $data['deliverCompany']='xxxx';  //寄件人
        $data['deliverAddress']='广东省广州市天河区渔兴路小马驹创意园xxx栋xxx室';
        $data['deliverName']='';               //寄件人
        $data['deliverMobile']='';             //寄件人手机号
        $WaybillPrinter= new WaybillPrinterSdk();
        $images_url=$WaybillPrinter->createPhoto($data,[]);

        return  $images_url;
    }

    /*3.顺丰订单取消*/
    public function cancelOrder($face_order){
        $res=$this->Logistics->cancelOrder($face_order);
        return  $res;
    }

    /*4.顺丰路由节点操作码查询*/
    public function queryRoutes($express_order){
        $res=$this->Logistics->queryRoutes($express_order);
        return  $res;
    }
    /*5.顺丰路由信息查询*/
    public function queryRoutesInfo($express_order){
        $res=$this->Logistics->queryRoutesInfo($express_order);
        return  $res;
    }


}

以上三个文件是框架里封装的类。

第三步:控制器中接口调用封装好的面单类库

/*1.顺丰下单接口 和 面单图片生成
     1.7version(100*150)
    */
    public function alertPrint(){
        //订单id
        $id=input('post.id');
        $ordernumber=input('post.ordernumber');
        if(empty($id)&&empty($ordernumber)){
            return json(['code'=>201,'msg'=>'不存在该订单信息!','data'=>[]]);
        }
        if(!empty($ordernumber)){
            $where = array('dp.ordernumber'=>$ordernumber);
        }else{
            $where = array('dp.id'=>$id);
        }

        //防止重复提交
        $key='ordernumber.online.'.$ordernumber;
        $redis  = new RedisCache();

        $key_ordernumber=$redis->get($key);
        if($key_ordernumber){
            return json(['code'=>201,'msg'=>'同一个订单号一分钟只允许请求一次接口','data'=>[]]);
        }else{
            $redis->set($key, $ordernumber, 60);//設置有效時間為1分鐘
        }


        $orderlist=Db::table('wechat_drug_pay')
            ->alias('dp')
            ->field('dp.id,op.drug_total as drug_money,op.drug_num,dp.ordernumber,wop.store_id,dp.freight,dp.address,dp.unitid,dp.consultant_unionid,dp.patient_unionid,dp.inquiry_id,dp.prescription,ad.take_over,ad.city,ad.name,ad.area,ad.phone,ad.province,ad.detail_address,um.user_mobile')
            ->join('wechat_address ad','dp.address = ad.id','LEFT')
            ->join('wechat_order_price op','dp.ordernumber = op.ordernumber','LEFT')
            ->join('wechat_pharmacy_order wop','dp.id = wop.order_id','LEFT')
            ->join('wechat_user_message um','dp.id = um.order_id','LEFT')
            ->where($where)
            ->find();
        if(empty($orderlist)){
            return json(['code'=>201,'msg'=>'不存在该订单信息。','data'=>$orderlist]);
        }

        $xdOrder=$orderlist['ordernumber'];
         //物流信息(重打面单)
        $logisticslist=Db::table('wechat_logistics')
            ->alias('wl')
            ->where(['wl.ordernumber'=>$xdOrder])
            ->find();
        if(empty($logisticslist)){
            $logisticslist= [];
        }
        //运单号
        $express_order=!empty($logisticslist['sf_waybillno'])?$logisticslist['sf_waybillno']:'';

        $drug_pay['id']=!empty($orderlist['id'])?$orderlist['id']:$id;//订单ID
        $drug_pay['route_label_data'] = !empty($logisticslist['route_label_data'])?$logisticslist['route_label_data']:input('post.route_label_data'); //路由信息
        $drug_pay['face_order']=!empty($logisticslist['sf_order'])?$logisticslist['sf_order']:input('post.face_order');//面单订单号
        $drug_pay['total']=!empty($orderlist['drug_num'])?$orderlist['drug_num']:input('post.total');//药方药物总数
        $drug_pay['drug_money']=!empty($orderlist['drug_money'])?$orderlist['drug_money']:input('post.drug_money');//药品总价
        $drug_pay['store_id']  =isset($orderlist['store_id'])?$orderlist['store_id']:input('post.store_id');//药房ID
        $drug_pay['ordernumber']  =!empty($orderlist['ordernumber'])?$orderlist['ordernumber']:input('post.ordernumber');//订单号
        $drug_pay['express_order']= $express_order;//顺丰运单号
        $drug_pay['address_id']=input('post.address_id');//收货地址ID
        $drug_pay['freight']   =!empty(input('post.freight'))?input('post.freight'):12;//运费

        $drug_pay['take_over']=!empty($orderlist['take_over'])?$orderlist['take_over']:input('post.take_over');//收货地址
        $drug_pay['city']=!empty($orderlist['city'])?$orderlist['city']:input('post.city');//市
        $drug_pay['name']=!empty($orderlist['name'])?$orderlist['name']:input('post.name');//收货人
        $drug_pay['area']=!empty($orderlist['area'])?$orderlist['area']:input('post.area');//区
        $drug_pay['phone']=!empty($orderlist['phone'])?$orderlist['phone']:input('post.phone');//收货人联系方式
        $drug_pay['province']=!empty($orderlist['province'])?$orderlist['province']:input('post.province');//省
        $drug_pay['detail_address']=!empty($orderlist['detail_address'])?$orderlist['detail_address']:input('post.detail_address');//详细地址
        $route_label_data=json_decode($drug_pay['route_label_data'],true);

        $present_address_id = $orderlist['address'];
        $face_order=$drug_pay['face_order'];

   //查询收货人信息
        $address=DB::table('wechat_address')->field('id,take_over,city,name,area,phone,province,detail_address')->where(['id'=>$present_address_id])->find();
        $orderlist['address']=$address;

        $LogoptSdk = new LogoptSdk();
        $xiao_order_id= $LogoptSdk->createOrderid();

        if(empty($express_order)){
            //1.首次生成面单打印
            $res  =  $LogoptSdk ->createOrder($drug_pay,$xiao_order_id);
            //记录失败记录
            if(empty($res['apiResultData'])){
                if(!empty($res['apiErrorMsg'])){
                    $apiErrorMsg=$res['apiErrorMsg'];
                }else{
                    $apiErrorMsg='顺丰下单失败';
                }
                $this->dingdingSend($xdOrder.$apiErrorMsg);//发送钉钉机器人通知消息
                $this->expressFail($xdOrder,$apiErrorMsg);//记录顺丰下单失败记录表
                return json(['code'=>201,'msg'=>'顺丰下单失败!','data'=>$res,'drug_pay'=>$drug_pay]);
            }

            $resu=json_decode($res['apiResultData'],true);
            //记录失败记录
            $errorCode=$resu['errorCode'];
            if($errorCode!=='S0000'){
                $this->dingdingSend($xdOrder.$resu['errorMsg']);
                $this->expressFail($xdOrder,$resu['errorMsg']);
                return json(['code'=>201,'msg'=>$resu['errorMsg'],'data'=>[]]);
            }
            $routeLabelData = '';
            if(!empty($resu['msgData']['routeLabelInfo'][0]['routeLabelData'])){
                $routeLabelData=$resu['msgData']['routeLabelInfo'][0]['routeLabelData'];
            }else{
                $resmsg =!empty($resu['errorMsg'])?$resu['errorMsg']:'下单失败,没有路由信息';
                if(!empty($resu['msgData']['remark'])) {
                    if ($resu['msgData']['remark'] == 1) {
                        $resmsg = $resu['msgData']['remark'] . '、收方超范围'.$drug_pay['take_over'].$drug_pay['detail_address'];
                    } else if ($resu['msgData']['remark'] == 2) {
                        $resmsg = $resu['msgData']['remark'] . '、派方超范围'.$drug_pay['take_over'].$drug_pay['detail_address'];
                    } else if ($resu['msgData']['remark']) {
                        $resmsg = $resu['msgData']['remark'] . '、其它原因'.$drug_pay['take_over'].$drug_pay['detail_address'];
                    }
                }
                $this->dingdingSend($xdOrder.$resmsg);
                $this->expressFail($xdOrder,$resmsg);
                return json(['code'=>201,'msg'=>$resmsg,'data'=>[]]);
            }
            $route_label_data = $routeLabelData;
            if(empty($routeLabelData['waybillNo'])){
                if(!empty($resu['errorMsg'])){
                    $errorMsg=$resu['errorMsg'];
                }else{
                    if(!empty($resu['msgData']['remark'])){
                        $remark=$resu['msgData']['remark'];
                        if($remark==2){
                            $errorMsg='配送超范围';
                        }
                    }
                }
                $this->dingdingSend($xdOrder.$errorMsg);
                $this->expressFail($xdOrder,$errorMsg);
                return json(['code'=>201,'msg'=>'顺丰下单失败,运单号空','data'=>[]]);
            }else{
                $data['mailNo']=$routeLabelData['waybillNo'];
                //写入顺丰单号
                $sfOrderArr=explode(',',$routeLabelData['waybillNo']);
                $address   =$present_address_id;
                $addresslist=Db::table('wechat_address') ->where(['id'=>$address])->find();

                //发送短信和推送模板消息
                $sms_data = array (
                    'xdOrder' => $xdOrder,
                    'sfOrder' => $sfOrderArr,
                    'user_mobile' => $orderlist['user_mobile'],
                    'id' => $orderlist['id'],
                    'phone' => $orderlist['phone'],
                    'address_id' => $orderlist['address'],
                    'prescription' => $orderlist['prescription'],
                    'patient_unionid' => $orderlist['patient_unionid'],
                    'consultant_unionid' => $orderlist['consultant_unionid'],
                    'unitid' => $orderlist['unitid'],
                    'inquiry_id' => $orderlist['inquiry_id'],
                    'address_arr'=> $addresslist
                );

                $this->expressOrder($sms_data);//给收件人发送顺丰单号短信通知消息

                //二维码信息
                $data['abFlag']=$routeLabelData['abFlag'];
                $data['codingMapping']=$routeLabelData['codingMapping'];
                $data['codingMappingOut']=$routeLabelData['codingMappingOut'];
                $data['destRouteLabel']=$routeLabelData['destRouteLabel'];
                $data['destTeamCode']=$routeLabelData['destTeamCode'];
                $data['printIcon']='00000000';
                $data['proCode']=$routeLabelData['proCode'];
                $data['qrcode']=$routeLabelData['twoDimensionCode'];
                $data['sourceTransferCode']=$routeLabelData['sourceTransferCode'];
                $data['waybillNo']=$routeLabelData['waybillNo'];
                $data['xbFlag']=$routeLabelData['xbFlag'];
                $routeLabel=json_encode($routeLabelData);
            }
        }else{
            $xiao_order_id = !empty($logisticslist['sf_order'])?$logisticslist['sf_order']:'';

            //2.重复生成面单打印
            $data['mailNo']=$express_order;
            //二维码信息
            $data['abFlag']='';
            $data['codingMapping']='';
            $data['codingMappingOut']='';
            $data['destRouteLabel']=$route_label_data['destRouteLabel'];
            $data['destTeamCode']='';
            $data['printIcon']='00000000';
            $data['proCode']=$route_label_data['proCode'];
            $data['qrcode']=$route_label_data['twoDimensionCode'];
            $data['sourceTransferCode']='';
            $data['waybillNo']=$express_order;
            $data['xbFlag']='';
        }

        $orderlist['face_order']       = $xiao_order_id;
        $orderlist['route_label_data'] = $route_label_data;

        $images_url=$LogoptSdk->createPhoto($data,$drug_pay);
        if(!empty($images_url)){
            if(empty($express_order)){
                // 启动事务
                Db::startTrans();
                try {
                    $list['order_id']=$drug_pay['id'];
                    $list['ordernumber']=$xdOrder;
                    $list['sf_order']=$xiao_order_id;
                    $list['sf_waybillno']=$data['mailNo'];
                    $list['face_images_url']=$images_url;
                    $list['route_label_data']=$routeLabel;
                    $list['status']=0;
                    $list['Logistics_type']=0;
                    $list['Merge_order']=0;
                    $list['Sf_cansol_status']=0;
                    $list['create_time']=time();
                    $result  =  Db::table('wechat_logistics')->insert($list);//加入物流表

                    if(empty($result)){
                        return json(['code'=>201,'msg'=>'添加物流信息失败','data'=>[]]);
                    }
                  
                    //写入跟进记录信息
                    $contents = json_encode(array('生成物流面单图片'),320);
                    //面单图片生成成功后,操作对应的其他需要操作的表逻辑
                 
                    Db::commit();
                    return json_encode(['code'=>200,'msg'=>'请求成功,第一次生成面单图片','data'=>['images_url'=>$images_url,'xiao_order_id'=>$xiao_order_id,'sfOrder'=>$data['mailNo'],'order'=>$orderlist]],JSON_UNESCAPED_SLASHES);

                } catch (\Exception $e) {
                    // 日志写入
                    Log::record(json_encode($e->getMessage()), 'express_error');
                    // 回滚事务
                    Db::rollback();
                    return Error::codeMsg(4002);
                }
            }else{
                return json_encode(['code'=>200,'msg'=>'请求成功,第二次生成面单图片','data'=>['images_url'=>$images_url,'xiao_order_id'=>$face_order,'sfOrder'=>$express_order,'order'=>$orderlist]],JSON_UNESCAPED_SLASHES);
            }
        }else{
            if(empty($express_order)){
                $this->dingdingSend($xdOrder.'请求失败,生成面单失败');
                $this->expressFail($xdOrder,'请求失败,生成面单失败');
                return json(['code'=>201,'msg'=>'请求失败,生成面单失败','data'=>['images_url'=>$images_url,'xiao_order_id'=>$xiao_order_id,'address_dev'=>$address_dev,'order'=>$orderlist]],JSON_UNESCAPED_SLASHES);
            }else{
                $this->dingdingSend($xdOrder.'已有顺丰单号,不用重新请求,谢谢');
                $this->expressFail($xdOrder,'已有顺丰单号,不用重新请求,谢谢');
                return json(['code'=>201,'msg'=>'已有顺丰单号,不用重新请求,谢谢','data'=>['images_url'=>$images_url,'xiao_order_id'=>$xiao_order_id,'address_dev'=>$address_dev,'order'=>$orderlist]],JSON_UNESCAPED_SLASHES);
            }
        }
    }

至此从顺丰下单,并生成面单图片已完成。

第四步:编写更新物流状态接口

/*1.更新顺丰的物流状态 update此接口需要用脚本定时请求或通过定时任务,时间间隔可以设置成一分钟或两分钟一次
    1.7version(100*150)
  */
    public function update()
    {

        $date = date('Y-m-d H:i:s');
        $time = strtotime("$date -10 day");//查询出近10天数据进行更新
        $where = "dp.status in (1,4,5,6,7) and dp.create_time >=$time and wl.status in (0,1)";//wl.status等于0的是未揽收的,wl.status等于1的是已揽收的
        $limit = 100;
        //总的条数
        $count = Db::table('wechat_drug_pay')
            ->alias('dp')
            ->field('dp.id,dp.ordernumber,wl.sf_waybillno')
            ->join('wechat_logistics wl', 'dp.ordernumber = wl.ordernumber')
            ->where($where)
            ->count();
        if (empty($count)) {
            return json_encode(['code' => 201, 'msg' => '不存在订单信息', 'data' => []]);
        }
        //防止重复提交
        $pagekey = 'page.online';
        $redis = new RedisCache();

        $page_val = !empty($redis->get($pagekey)) ? $redis->get($pagekey) : 1;//此处将页码存在redis了
        if (!$page_val) {
            $page_val = 1;
            $redis->set($pagekey, 1);
        }

        $max_page = ceil($count / $limit);
        $max_page = !empty($max_page) ? $max_page : 1;
        if ($page_val >= $max_page) {
            $page = intval($max_page);
            $redis->set($pagekey, 1);
        } else {
            $page = intval($page_val);
            $page_val++;
            $redis->set($pagekey, $page_val);
        }
        $offest = ($page - 1) * $limit;
        //运单号
        $orderlist = Db::table('wechat_drug_pay')
            ->alias('dp')
            ->field('dp.id,dp.ordernumber,wl.sf_waybillno')
            ->join('wechat_logistics wl', 'dp.ordernumber = wl.ordernumber')
            ->where($where)
            ->order(" dp.id", "desc")
            ->limit($offest, $limit)
            ->select();

        if (empty($orderlist)) {
            return json_encode(['code' => 201, 'msg' => '不存在订单信息', 'data' => []]);
        }

        if (!empty($orderlist)) {
            $key = 0;  //实际更新条数
            foreach ($orderlist as $k => $v) {
                $num = $k + 1;
                $ordernumber = $v['ordernumber'];
                $sf_waybillno = $v['sf_waybillno'];
                $res = $this->queryExpressStatus($sf_waybillno, $ordernumber);
                if (!empty($res['data'])) {
                    $status = $res['data']['status'];
                    $key++;
                    Db::table('wechat_logistics')->where(array("ordernumber" => $ordernumber))->update(['status' => $status, 'update_time' => time()]);
                    //向订单跟进记录表中插入一条记录
                    if ($status == 0) {
                        $type = 4;     //未揽收
                        $contents = '物流未揽收';
                    } elseif ($status == 1) {
                        $LogoptSdk = new LogoptSdk();
                        $res = $LogoptSdk->queryRoutes($sf_waybillno);
                        $result = json_decode($res, true);
                        $opCode = !empty($result['data'][0]['opCode']) ? $result['data'][0]['opCode'] : '';
                        $time = !empty($result['data'][0]['acceptTime']) ? $result['data'][0]['acceptTime'] : '';
                        $sf_harvest_time = !empty(strtotime($time)) ? strtotime($time) : '';
                        $type = 9;    //已揽收
                        if ($opCode == 50 || $opCode == 54) {
                            $contents = '在' . $time . ' 物流已揽收';
                        } else {
                            $contents = '物流已揽收';
                        }
                        //更新揽收时间
                        Db::table('wechat_logistics')->where(array("ordernumber" => $ordernumber))->update(['sf_harvest_time' => $sf_harvest_time, 'update_time' => time()]);
                    } elseif ($status == 2 || $status == 4 || $status == 5) {
                        $LogoptSdk = new LogoptSdk();
                        $res = $LogoptSdk->queryRoutesInfo($sf_waybillno);
                        $result = json_decode($res, true);
                        $result_routes=$result['data'];
                        $brr = array();
                        foreach ($result_routes as $keyes => $values) {
                            if(!empty($values['opCode'])){
                                if ($values['opCode'] == 204) {
                                    $brr['remark'] = $values['remark'];
                                    $brr['acceptTime']=strtotime($values['acceptTime']);
                                }else{
                                    $brr['acceptTime']=strtotime($values['acceptTime']);
                                }
                            }
                        }
                        $contents = '';
                        if (!empty($brr)) {
                            $remark = trim($brr['remark']);
                            $name = substr($remark, 4 * 3, (stripos($remark, ',') - 4 * 3));
                            $phone = substr($remark, stripos($remark, ':') + 3, 11);
                            $contents = '快递员:' . $name . '电话:' . $phone;
                        }
                        if ($status == 2) {
                            $type = 10;    //已签收
                            $contents = '物流已签收。' . $contents;
                        } else {
                            $type = 11;    //物流异常
                            if ($status == 4) {
                                $contents = '物流已拒收。' . $contents;
                            } else {
                                $contents = '物流派件异常。' . $contents;
                            }
                        }
                    }
                    $result = Db::table('wechat_drug_order_recordlog')->where(array("ordernumber" => $ordernumber, 'type' => $type))->find();
                    if (empty($result)) {
                        $content = json_encode(array($contents), 320);
                        $arr = array();
                        $arr['user_id'] = '';
                        $arr['ordernumber'] = $ordernumber;
                        $arr['type'] = $type;
                        $arr['contents'] = $content;
                        $arr['create_time'] = time();
                        $arr['update_time'] = time();
                        Db::table('wechat_drug_order_recordlog')->insert($arr);
                    }
                }
                if ($num == count($orderlist)) {
                    return json_encode(['code' => 200, 'msg' => '物流状态修改成功', 'data' => ['num' => '请求接口查询' . $num . '条', 'page' => $page, 'max_page' => $max_page, 'key' => '路由信息更新' . $key . '条']]);
                }
            }
        }
    }

/*2.顺丰物流状态查询*/
    public function queryExpressStatus($express_order,$ordernumber=''){

        if(empty($express_order)){
            if(!empty($ordernumber)){
                $orderlist=Db::table('wechat_drug_pay')
                    ->alias('dp')
                    ->field('dp.id,wl.sf_waybillno')
                    ->join('wechat_logistics wl','dp.id = wl.order_id','LEFT')
                    ->where(['dp.ordernumber'=>$ordernumber])
                    ->find();

                $express_order = !empty($orderlist['sf_waybillno'])?$orderlist['sf_waybillno']:'';
            }
        }
        if(empty($express_order)){
            return json(['code'=>201,'msg'=>'参数缺失','data'=>[]]);
        }

        $LogoptSdk = new LogoptSdk();
        $res = $LogoptSdk->queryRoutes($express_order);
        $res_data  = json_decode($res,true);
        if($res_data['code']==1){
            $opCode = $res_data['data'];
            $str=implode(',',$opCode);
            if (strpos($str, '80') === false && (strpos($str, '50') !== false||strpos($str, '54') !== false)) {
                return ['code'=>200,'msg'=>'查询成功','data'=>['status'=>1,'info'=>'已揽收']];
            }elseif(strpos($str,'80')!==false){
                return ['code'=>200,'msg'=>'查询成功','data'=>['status'=>2,'info'=>'已签收']];
            }elseif(strpos($str,'33')!==false){
                return ['code'=>200,'msg'=>'查询成功','data'=>['status'=>4,'info'=>'已拒收']];
            }elseif(strpos($str,'70')!==false){
                return ['code'=>200,'msg'=>'查询成功','data'=>['status'=>5,'info'=>'派件异常']];
            }elseif(strpos($str,'30')!==false||strpos($str,'31')!==false||strpos($str,'36')!==false||strpos($str,'43')!==false||strpos($str,'44')!==false||strpos($str,'105')!==false||strpos($str,'106')!==false||strpos($str,'204')!==false||strpos($str,'657')!==false||strpos($str,'658')!==false){
                return ['code'=>200,'msg'=>'查询成功','data'=>['status'=>6,'info'=>'物流运输中']];
            }else{
                return ['code'=>201,'msg'=>'查询成功','data'=>['status'=>0,'info'=>'未揽收']];
            }
        }else{
            return ['code'=>201,'msg'=>'暂无信息','data'=>[]];
        }
    }

 

 

 

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐