소스 검색

滴滴订单状态更新回调与滴滴查询订单详情回调

shizhongqi 7 달 전
부모
커밋
baf12828e1
2개의 변경된 파일390개의 추가작업 그리고 6개의 파일을 삭제
  1. 25 5
      app-ghs/controllers/DeliveryController.php
  2. 365 1
      common/components/delivery/platform/didi/CallBackHandler.php

+ 25 - 5
app-ghs/controllers/DeliveryController.php

@@ -251,19 +251,19 @@ class DeliveryController extends BaseController
 
         $post = $form->getAttributes();
         $orderId = $post['orderId'] ?? 0; // xhGhsOrder.id
-        $params = $post['allParams'] ?? '';
+        $params = $post['allParams'] ?? [];
         $platform = $params['en_name'] ?? '';// 平台英文名,$post['platform'] 是中文
         if (empty($platform)) {
             util::fail('请选择跑腿');
         }
-        $pickupTime = $post['pickupTime'] ?? '';
+        $pickupTime = $post['pickupTime'];
 
         $params['mainId'] = $this->mainId;
         $params['ip'] = Yii::$app->request->userIP;
-        $params['weight'] = $post['weight'] ?? 1;
+        $params['weight'] = $post['weight'];
         $params['remark'] = $post['remark'] ?? '';
-        $params['total_price_fen'] = $post['price'] ?? 1;
-        $params['pickupTime'] = $pickupTime['value'] ?? '';
+        $params['total_price_fen'] = $post['price'];
+        $params['pickupTime'] = $pickupTime['value'];
 
         $ghsOrder = OrderClass::getById($orderId, true);
         if (empty($ghsOrder)) {
@@ -651,6 +651,26 @@ class DeliveryController extends BaseController
         }
     }
 
+    /**
+     * 滴滴回调接口
+     */
+    public function actionDidiCallback()
+    {
+        $post = Yii::$app->request->post();
+        Yii::info('didi callback: ' . json_encode($post));
+        $cbh = new \common\components\delivery\platform\didi\CallBackHandler();
+        $ret = $cbh->handle($post);// 各回调处理
+        if ($ret) {
+            if(is_array($ret)){
+                return $this->asJson($ret);
+            }
+            return $this->asJson(['error_code' => 0, 'error_msg' => 'success']);
+        } else {
+            Yii::error('顺丰回调失败: ' . json_encode($post, JSON_UNESCAPED_UNICODE));
+            return $this->asJson(['error_code' => -1, 'error_msg' => 'failed']);
+        }
+    }
+
 
     // ---------------------------------------------------- 授权回调接口 ----------------------------------------------------------
 

+ 365 - 1
common/components/delivery/platform/didi/CallBackHandler.php

@@ -1,7 +1,371 @@
 <?php
 
+namespace common\components\delivery\platform\didi;
 
+use biz\common\classes\GhsNotifyClass;
+use biz\common\classes\HdNotifyClass;
+use biz\shop\classes\ShopClass;
+use bizGhs\express\classes\GhsDeliveryOrderClass;
+use bizHd\express\classes\HdDeliveryOrderClass;
+use bizGhs\order\classes\OrderClass;
+use bizHd\order\classes\OrderClass as HdOrderClass;
+use bizHd\purchase\classes\PurchaseClass;
+use common\components\delivery\services\adapter\Functions;
+use common\components\noticeUtil;
+use common\components\util;
+use Yii;
+
+/**
+ * 滴滴回调处理类
+ */
 class CallBackHandler
 {
+    /**
+     * 处理滴滴回调
+     *
+     * @param array $data 回调数据
+     * @return bool|array 处理结果
+     */
+    public function handle($data)
+    {
+        try {
+            // 验证签名 (如有需要请实现)
+            // if (!$this->verifySignature($data)) { ... }
+
+            // 区分业务类型
+            // 订单详情查询: 包含 orderNo 和 outOrderNo (且不包含 status/updateTime)
+            if (isset($data['orderNo']) && isset($data['outOrderNo'])) {
+                return $this->queryOrderDetails($data);
+            }
+
+            // 订单状态变更: 包含 didiOrderNo, status, updateTime
+            if (isset($data['didiOrderNo']) && isset($data['status'])) {
+                return $this->processOrderCallback($data);
+            }
+            
+            Yii::warning('滴滴回调未知类型: ' . json_encode($data, JSON_UNESCAPED_UNICODE), 'didi-callback');
+            return false;
+
+        } catch (\Exception $e) {
+            Yii::error('滴滴回调处理异常: ' . $e->getMessage() . ' | ' . $e->getTraceAsString(), 'didi-callback');
+            return false;
+        }
+    }
+
+    /**
+     * 查询订单详情 (返回给滴滴)
+     * 
+     * @param array $data
+     * @return array
+     */
+    private function queryOrderDetails($data)
+    {
+        $didiOrderNo = $data['orderNo'];
+        $outOrderNo = $data['outOrderNo'];
+
+        // 查找主订单
+        $order = OrderClass::getByCondition(['orderSn' => $outOrderNo], true);
+        $isGhs = true;
+
+        if (!$order) {
+            $order = HdOrderClass::getByCondition(['orderSn' => $outOrderNo], true);
+            $isGhs = false;
+        }
+
+        if (!$order) {
+            Yii::error('滴滴查询详情未找到对应订单: ' . $outOrderNo, 'didi-callback');
+            // 返回空或错误信息? 根据协议,这里应该返回错误,但方法定义返回数组。
+            // 暂时返回空数组或抛异常
+            return [];
+        }
+
+        // 查找配送订单以获取 shopId
+        $deliveryOrder = $this->getDeliveryOrder($order, $isGhs);
+        if (!$deliveryOrder) {
+            Yii::error('滴滴查询详情未找到配送订单: ' . $outOrderNo, 'didi-callback');
+            return [];
+        }
+
+        // 获取店铺信息
+        $shopId = isset($deliveryOrder->shopId) ? $deliveryOrder->shopId : 0;
+        if (empty($shopId) && isset($order->shopId)) { // 尝试从订单获取
+             $shopId = $order->shopId;
+        }
+
+        $shop = ShopClass::getById($shopId, false, 'id,merchantName,fullAddress,lat,long,city,dist,floor,mobile,address');
+        if (!$shop) {
+             Yii::error('滴滴查询详情未找到店铺: ' . $shopId, 'didi-callback');
+             // 构造一个兜底的 shop 信息?
+             $shop = [
+                 'merchantName' => '未知店铺',
+                 'mobile' => '',
+                 'fullAddress' => '',
+                 'lat' => 0,
+                 'long' => 0,
+                 'address' => '',
+                 'floor' => ''
+             ];
+        }
+
+        // 构造返回数据
+        // 1. 发货人信息 (Location Info)
+        $fromAddress = [
+            'name' => $shop['merchantName'], //联系名称
+            'poiName' => '', //地图获取的poi名称
+            'poiAddress' => $shop['fullAddress'],//地图获取的地址信息
+            'addressInfo' => $shop['floor'], //用户补充地址信息(门牌号等)
+            'latitude' => (float)$shop['lat'],
+            'longitude' => (float)$shop['long'],
+            'phone' => $shop['mobile'],
+            'virtualPhoneType' => 0,
+        ];
+
+        // 2. 收货人信息 (Location Info)
+        $toAddress = [
+            'name' => $order['customName'],
+            'poiName' => '', //地图获取的poi名称
+            'poiAddress' => $order['fullAddress'],
+            'addressInfo' => $order['floor'],
+            'latitude' => (float)$order['lat'],
+            'longitude' => (float)$order['long'],
+            'phone' => Functions::getMobile($order),
+            'virtualPhoneType' => 0,
+        ];
+
+        // 3. 计算费用
+        // 订单金额 (单位:分) - 这里取实际支付金额/商品金额
+        $fee = (int)($order['actPrice'] * 100); 
+        // 优惠金额 (单位:分)
+        $prePrice = isset($order['prePrice']) ? $order['prePrice'] : $order['actPrice'];
+        $discountFee = (int)(($prePrice - $order['actPrice']) * 100);
+        if ($discountFee < 0) $discountFee = 0;
+
+        // 重量 (单位:克)
+        $weight = isset($order['weight']) ? (int)($order['weight'] * 1000) : 1000;
+
+        Yii::info('滴滴订单返回成功', 'didi-callback');
+        return [
+            'didiOrderNo' => (int)$didiOrderNo, // Long
+            'outOrderNo' => (string)$outOrderNo,
+            'status' => $this->convertStatusToDidi($deliveryOrder->orderStatus), // 转换回滴滴状态? 或者直接返回当前状态
+            'fromAddress' => $fromAddress,
+            'toAddress' => $toAddress,
+            'weight' => $weight,
+            // 'size' => 0, // 非必填
+            'goodsType' => 'FLOWER',
+            'goodsPrice' => (int)($order['actPrice'] * 100), // 保价费物品价值
+            'remark' => $order['remark'] ?? '',
+            'fee' => $fee,
+            'discountFee' => $discountFee,
+        ];
+    }
+
+    /**
+     * 处理订单回调
+     */
+    private function processOrderCallback($data)
+    {
+        $didiOrderNo = isset($data['didiOrderNo']) ? $data['didiOrderNo'] : '';
+        $outOrderNo = isset($data['outOrderNo']) ? $data['outOrderNo'] : '';
+        $didiStatus = isset($data['status']) ? $data['status'] : '';
+        
+        if (empty($outOrderNo)) {
+             Yii::error('滴滴回调缺少outOrderNo', 'didi-callback');
+             return false;
+        }
+
+        // 查找主订单 (假设 outOrderNo 为 orderSn)
+        $order = OrderClass::getByCondition(['orderSn' => $outOrderNo], true);
+        $isGhs = true;
+
+        if (!$order) {
+            $order = HdOrderClass::getByCondition(['orderSn' => $outOrderNo], true);
+            $isGhs = false;
+        }
+
+        if (!$order) {
+            Yii::error('滴滴回调未找到对应订单: ' . $outOrderNo, 'didi-callback');
+            return false;
+        }
+
+        // 查找配送订单
+        $deliveryOrder = $this->getDeliveryOrder($order, $isGhs);
+        if (!$deliveryOrder) {
+            Yii::error('滴滴配送订单不存在: orderSn=' . $outOrderNo . ' ghsOrderId=' . $order->id, 'didi-callback');
+            return false;
+        }
+
+        // 更新 didiOrderNo
+        if (!empty($didiOrderNo) && empty($deliveryOrder->orderId)) {
+             $deliveryOrder->orderId = (string)$didiOrderNo;
+             $deliveryOrder->save();
+        }
+
+        $sendStatus = $this->convertOrderSendStatus($didiStatus);
+        
+        // 如果状态无法映射,则忽略
+        if ($sendStatus === null) {
+            return true; 
+        }
+
+        // 更新配送订单状态
+        $deliveryOrder->orderStatus = $sendStatus;
+        $deliveryOrder->save();
+
+        // 更新主订单
+        if ($isGhs) {
+            util::checkRepeatCommit('ghs_pt_' . $order->id . '_' . $sendStatus, 2);
+            
+            if ($order) {
+                // 已送达
+                if ($sendStatus == 5) {
+                     OrderClass::confirmReach($order, ['staffId' => 0, 'staffName' => '']);
+                }
+                // 取消/异常 (且当前状态未取消)
+                if (!in_array($order->sendStatus, [-2, -1]) && in_array($sendStatus, [-1, -2])) {
+                    OrderClass::cancelOrderSend($order, ['staffId' => 0, 'staffName' => '']);
+                    $event = $this->cancelFrom($data);
+                    GhsNotifyClass::ptErrorNotify($order->id, $event);
+                }
+
+                $cg = PurchaseClass::getById($order->purchaseId, true, 'id,sendStatus');
+                if ($cg) {
+                    $cg->sendStatus = $sendStatus;
+                    $cg->save();
+                }
+
+                $order->sendStatus = $sendStatus;
+                // 转换主订单状态
+                $orderStatus = $this->convertOrderStatus($didiStatus);
+                if ($orderStatus !== null) {
+                    $order->status = $orderStatus;
+                }
+                $order->save();
+            }
+        } else {
+             // 零售订单处理
+            util::checkRepeatCommit('hd_pt_' . $order->id . '_' . $sendStatus, 2);
+            
+            if (!in_array($order->sendStatus, [-2, -1]) && in_array($sendStatus, [-1, -2])) {
+                $event = $this->cancelFrom($data);
+                HdNotifyClass::ptErrorNotify($order->id, $event);
+            }
+
+            if ($order) {
+                $order->sendStatus = $sendStatus;
+                $orderStatus = $this->convertOrderStatus($didiStatus);
+                if ($orderStatus !== null) {
+                    $order->status = $orderStatus;
+                }
+                $order->save();
+            }
+        }
+
+        Yii::info('滴滴回调处理成功', 'didi-callback');
+        return true;
+    }
+
+    private function getDeliveryOrder($order, $isGhs)
+    {
+        if ($isGhs) {
+             $gdos = GhsDeliveryOrderClass::getAllByCondition([
+                'deliveryId' => 'didi',
+                'ghsOrderId' => $order->id,
+                'orderStatus!=' => -1
+            ], 'id DESC', '*', null, true);
+            if(count($gdos)>=2){
+                noticeUtil::push("xhGhsDeliveryOrder ghsOrderId={$order->id} 有两笔以上是在配送中");
+                $gdo = $gdos[0];
+            }elseif(count($gdos)==1){
+                $gdo = $gdos[0];
+            }else{
+                $gdo = null;
+            }
+            return $gdo;
+        } else {
+            $hdos = HdDeliveryOrderClass::getAllByCondition([
+                'deliveryId' => 'didi',
+                'hdOrderId' => $order->id,
+                'orderStatus!=' => -1
+            ], 'id DESC', '*', null, true);
+            if(count($hdos)>=2){
+                noticeUtil::push("xhHdDeliveryOrder hdOrderId={$order->id} 有两笔以上是在配送中");
+                $hdo = $hdos[0];
+            }elseif(count($hdos)==1){
+                $hdo = $hdos[0];
+            }else{
+                $hdo = null;
+            }
+            return $hdo;
+        }
+    }
+
+    private function convertOrderSendStatus($didiStatus)
+    {
+        $map = [
+            'WAIT_ACCEPT' => 2,         // 待接单 -> 派单中
+            'PAIRED_DRIVER' => 20,      // 已接单 -> 已分配骑手
+            'ARRIVED_FROM_ADDRESS' => 20, // 到达起点 -> 已分配骑手(或30骑手已到店)
+            'LEAVE_FROM_ADDRESS' => 4,  // 已取货 -> 配送中
+            'ARRIVED_TO_ADDRESS' => 4,  // 到达终点 -> 配送中
+            'SERVICE_FINISH' => 5,      // 已送达
+            'SUBMIT_CHARGE' => 5,       // 费用提交 -> 已送达
+            'COMPLETED' => 5,           // 已完成 -> 已送达
+            'CANCEL' => -1,             // 已取消
+        ];
+        return isset($map[$didiStatus]) ? $map[$didiStatus] : null;
+    }
+
+    private function convertOrderStatus($didiStatus)
+    {
+         // 对应 OrderClass 的 status
+         // 3: 配送中, 4: 已完成
+         $map = [
+             'WAIT_ACCEPT' => 3, 
+             'PAIRED_DRIVER' => 3,
+             'ARRIVED_FROM_ADDRESS' => 3,
+             'LEAVE_FROM_ADDRESS' => 3,
+             'ARRIVED_TO_ADDRESS' => 3,
+             'SERVICE_FINISH' => 4,
+             'SUBMIT_CHARGE' => 4,
+             'COMPLETED' => 4,
+         ];
+         return isset($map[$didiStatus]) ? $map[$didiStatus] : null;
+    }
+
+    /**
+     * 将系统状态转换为滴滴状态 (用于订单详情查询)
+     * 这里只能做近似映射,因为系统状态比滴滴状态少
+     */
+    private function convertStatusToDidi($sendStatus)
+    {
+        $map = [
+            2 => 'WAIT_ACCEPT',
+            20 => 'PAIRED_DRIVER',
+            30 => 'ARRIVED_FROM_ADDRESS',
+            4 => 'LEAVE_FROM_ADDRESS', // or ARRIVED_TO_ADDRESS
+            5 => 'COMPLETED',
+            -1 => 'CANCEL',
+            -2 => 'CANCEL', // 异常也暂视为取消? 或没有对应
+        ];
+        return isset($map[$sendStatus]) ? $map[$sendStatus] : 'WAIT_ACCEPT';
+    }
+
+    private function cancelFrom($data)
+    {
+        if (!isset($data['cancelInfo'])) {
+            return '未知原因';
+        }
 
-}
+        $info = $data['cancelInfo'];
+        $sourceMap = [
+            1 => '用户',
+            2 => '司机',
+            3 => '客服',
+            4 => '系统'
+        ];
+        $source = isset($sourceMap[$info['cancelSource']]) ? $sourceMap[$info['cancelSource']] : '未知';
+        $reason = isset($info['cancelReason']) ? $info['cancelReason'] : '';
+        return "{$source}-取消: {$reason}";
+    }
+}