Bläddra i källkod

1. 授权处理
2. 表单验证模型修改
3. 订单接口修改

shizhongqi 9 månader sedan
förälder
incheckning
94d8e6e951

+ 140 - 41
app-ghs/controllers/DeliveryController.php

@@ -3,11 +3,14 @@ namespace ghs\controllers;
 
 use biz\shop\classes\ShopClass;
 use bizGhs\express\classes\GhsDeliveryOrderClass;
-use bizGhs\express\classes\ShansAuthTokenClass;
+use bizGhs\express\classes\DeliveryAuthTokenClass;
 use bizGhs\order\classes\OrderClass;
 use common\components\delivery\services\adapter\HuolalaAdapter;
 use common\components\util;
 use common\components\delivery\services\DispatchService;
+use ghs\models\delivery\CancelOrderForm;
+use ghs\models\delivery\CreateOrderForm;
+use ghs\models\delivery\OrderDetailForm;
 use Yii;
 
 /**
@@ -19,9 +22,45 @@ class DeliveryController extends BaseController
 {
     public $guestAccess = ['callback', 'shansong-auth-callback', 'huolala-auth-callback', 'fengniao-callback'];
 
+    public function actionList()
+    {
+        $mainId = $this->mainId;
+        DeliveryAuthTokenClass::getAllByCondition(['mainId'=>$mainId]);
+//        [
+//            { id: 'dada', name: '达达', logo: '📦', is_authorized: true, balance: 178.0, access_type: 'oauth' },
+//            { id: 'sf-tongcheng', name: '顺丰同城', logo: '🚚', is_authorized: true, balance: 174.0, access_type: 'oauth' },
+//        ];
+    }
+
+    public function actionGetAuthUrl()
+    {
+        $platform = Yii::$app->request->get('platformId');
+        switch($platform){
+            case 'shansong':
+                return $this->actionShansongAuth();
+                break;
+            case 'huolala':
+                return $this->actionHuolalaAuth();
+                break;
+            case 'fengniao':
+                return $this->actionFengniaoAuth();
+                break;
+            case 'dada':
+                return $this->actionDadaAuth();
+                break;
+            case 'shunfeng':
+                break;
+            case '':
+                break;
+            default:
+                util::error(-1, $platform . '不存在');
+                break;
+        }
+    }
+
     // ------------------ 授权(账号绑定) ---------------------
     //授权
-    public function actionMerchantAuth()
+    public function actionShansongAuth()
     {
         //闪送授权(商户授权:授权后能为商户下所有门店发单)
         $auth = new \common\components\delivery\platform\shansong\Auth();
@@ -29,7 +68,8 @@ class DeliveryController extends BaseController
         $authUrl = $auth->generateMerchantAuthUrl($this->mainId, $redirectUrl);
 
         // 重定向用户
-        header('Location: ' . $authUrl);
+        //header('Location: ' . $authUrl);
+        util::success(['url'=>$authUrl]);
     }
 
     public function actionHuolalaAuth()
@@ -39,7 +79,8 @@ class DeliveryController extends BaseController
         $redirectUrl = 'https://api.shop.hzghd.com/delivery/huolala-auth-callback' . '?mainId=' . $mainId;
         $authUrl = $huoLalaAuth->generateAuthUrl($redirectUrl);
 
-        header('Location: ' . $authUrl);
+        //header('Location: ' . $authUrl);
+        util::success(['url'=>$authUrl]);
     }
 
     public function actionFengniaoAuth()
@@ -49,7 +90,8 @@ class DeliveryController extends BaseController
         $redirectUrl = 'https://api.shop.hzghd.com/delivery/fengniao-auth-callback' . '?mainId=' . $mainId;
         $authUrl = $fengnaioAuth->generateAuthUrl($redirectUrl);
 
-        header('Location: ' . $authUrl);
+        //header('Location: ' . $authUrl);
+        util::success(['url'=>$authUrl]);
     }
 
     public function actionDadaAuth()
@@ -62,13 +104,26 @@ class DeliveryController extends BaseController
         $shopNumber = '40d8391f9b05477b';
         $state = 'huidiao_biaoshi';
         $authUrl = $dadaAuth->generateAuthUrl($ticket, $state, $shopNumber);
-        header('Location: ' . $authUrl);
+        //header('Location: ' . $authUrl);
+        util::success(['url'=>$authUrl]);
+    }
+
+    public function actionShunfengAuth()
+    {
+        $shunfengAuth = new \common\components\delivery\platform\shunfeng\Auth();
+
+        $mainId = $this->mainId;
+        $redirectUrl = 'https://api.shop.hzghd.com/delivery/dada-auth-callback' . '?mainId=' . $mainId;
+        $shopNumber = '40d8391f9b05477b';
+        $state = 'huidiao_biaoshi';
+        $authUrl = $shunfengAuth->generateAuthUrl($state, $shopNumber);
+        util::success(['url'=>$authUrl]);
     }
 
     public function actionHuolalaVehicle()
     {
         $mainId = $this->mainId;
-        $authPlatforms = ShansAuthTokenClass::getByCondition(['user_id'=>$mainId, 'platform'=>'huolala']);
+        $authPlatforms = DeliveryAuthTokenClass::getByCondition(['mainId'=>$mainId, 'platform'=>'huolala']);
         $huolalaAdapter = new HuolalaAdapter($authPlatforms['access_token']);
         $cities = $huolalaAdapter->getCityVehicleList(1006);
         return $this->asJson($cities);
@@ -229,7 +284,10 @@ class DeliveryController extends BaseController
     // 创建订单(真实下单)
     public function actionCreateOrder()
     {
-        $post = Yii::$app->request->post();
+        $form = new CreateOrderForm();
+        $form->loadAndValidate();
+
+        $post = $form->getAttributes();
         $orderId = $post['orderId']; // xhGhsOrder.id
         $params = $post['allParams'];
         $platform = $params['en_name'];// $post['platform'] 是中文
@@ -245,11 +303,11 @@ class DeliveryController extends BaseController
         $ghsOrder = OrderClass::getById($orderId, true);
         // if ($ghsOrder['status'] != 2) {
         //     util::fail('订单状态不是待配送');
+        // // }
+        // if(!in_array($ghsOrder['sendStatus'], [-4, -1])) {
+        //     Yii::error('订单已经发过跑腿');
+        //     util::fail('订单已经发过跑腿');
         // }
-        if(!in_array($ghsOrder['sendStatus'], [-4, -1])) {
-            Yii::error('订单已经发过跑腿');
-            util::fail('订单已经发过跑腿');
-        }
 
         $ds = new DispatchService($this->mainId, $platform);
         $ret = $ds->createOrder($ghsOrder, $shopId, $params);
@@ -285,13 +343,15 @@ class DeliveryController extends BaseController
     }
 
     /**
-     * 查询订单
+     * 查询订单详情
      *
      * 1. 蜂鸟平台 orderId 可以使用 "XSD_CS26322799" (即:表 xhGhsOrder.orderSn)
      */
-    public function actionSelectOrder()
+    public function actionOrderDetail()
     {
-        $post = Yii::$app->request->post();
+        $form = new OrderDetailForm();
+        $form->loadAndValidate();
+        $post = $form->getAttributes();
         $platform = $post['platform'];
         $orderId = $post['orderId'];
         $orderSn = isset($post['orderSn']) ? $post['orderSn'] : '';
@@ -309,12 +369,14 @@ class DeliveryController extends BaseController
     // 取消订单
     public function actionCancelOrder()
     {
-        $post = Yii::$app->request->post();
+        $form = new CancelOrderForm();
+        $form->loadAndValidate();
+        $post = $form->getAttributes();
         $orderId = $post['orderId'] ?? 0; // 平台的orderId,
         $platform = $post['platform'] ?? '';
-        $ghsOrderId = $post['ghsOrderId'] ?? 0;
+        $ghsOrderId = $post['ghsOrderId'];
 
-        if($platform == '') {
+        if($platform == '' || $orderId == 0) {
            $gdo = GhsDeliveryOrderClass::getByCondition(['mainId'=>$this->mainId, 'ghsOrderId'=>$ghsOrderId, 'orderStatus!='=>-1], true);
            if(!empty($gdo)) {
                $orderId = $gdo->orderId;
@@ -325,10 +387,24 @@ class DeliveryController extends BaseController
         }
 
         $ds = new DispatchService($this->mainId, $platform);
-        $ret = $ds->cancelOrder($orderId, '');
+
+        $cancelCostCent = 0;
+        if($platform == 'fengniao') {
+            //请求订单预取消接口,获取是否可取消。如果运单状态不允许取消,则直接返回
+            $cancelParams = ['order_id'=>$orderId, 'order_cancel_code'=>$post['order_cancel_code']];
+            $res = $ds->getAdapter()->preCancelOrder($cancelParams);
+            if($res['code'] != 0){
+                util::fail('运单状态不允许取消');
+            }
+            $cancelCostCent = $res['data']['actual_cancel_cost_cent']; //取消实际需金额(单位:分)
+        }
+
+        $post['order_cancel_role'] = 1; //1:商户取消, 2:用户取消
+        $ret = $ds->cancelOrder($orderId, $post);
         if($ret['code'] == 0) {
             // 更新跑腿订单与批发订单状态
             $gdo->orderStatus = -1;
+            $gdo->cancelCost = $cancelCostCent;
             $gdo->save();
 
             $ghsOrder = OrderClass::getById($ghsOrderId, true, 'id, sendDistance, sendStatus, deliveryId');
@@ -343,6 +419,29 @@ class DeliveryController extends BaseController
         util::fail('取消失败');
     }
 
+    /**
+     * 获取订单取消原因
+     */
+    public function actionCancelReason()
+    {
+        $post = Yii::$app->request->post();
+        $ghsOrderId = $post['ghsOrderId'];
+        $platform = $post['platform'];
+
+        $gdo = GhsDeliveryOrderClass::getByCondition(['mainId'=>$this->mainId, 'ghsOrderId'=>$ghsOrderId, 'orderStatus!='=>-1], true);
+        if(!empty($gdo)) {
+            $orderId = $gdo->orderId;
+            //$platform = $gdo->deliveryId;
+        } else {
+            util::fail('未找到对应订单: ' . $ghsOrderId);
+        }
+
+        $ds = new DispatchService($this->mainId, $platform);
+        $ret = $ds->getCancelReasonList($orderId);
+
+        util::success($ret);
+    }
+
     // 第三方回调入口
     /**
      * 回调接口
@@ -426,13 +525,13 @@ class DeliveryController extends BaseController
 
         // 保存授权信息到数据库
         $data = [
-            'user_id' => $state,
-            'shop_id' => $shopId,
-            'access_token' => $result['access_token'],
-            'refresh_token' => $result['refresh_token'],
-            'expires_at' => time() + $result['expires_in'],
-            'auth_type' => $isAllStoreAuth ? 'all_store' : 'single_store',
-            'platform_type' => 'shansong'
+            'mainId' => $state,
+            'shopId' => $shopId, //闪送商户ID
+            'accessToken' => $result['access_token'],
+            'refreshToken' => $result['refresh_token'],
+            'expiresAt' => time() + $result['expires_in'],
+            'authType' => $isAllStoreAuth ? 'all_store' : 'single_store',
+            'platform' => 'shansong'
         ];
 
         if (!$isAllStoreAuth) {
@@ -440,7 +539,7 @@ class DeliveryController extends BaseController
             $data['shans_store_id'] = $storeId;
         }
 
-        ShansAuthTokenClass::add($data);
+        DeliveryAuthTokenClass::add($data);
         if (false) {
             throw new \yii\web\HttpException(500, '保存授权信息失败');
         }
@@ -480,16 +579,16 @@ class DeliveryController extends BaseController
 
         // 保存授权信息到数据库
         $data = [
-            'user_id' => $mainId,
-            'shop_id' => $result['auth_mobile'],
-            'access_token' => $result['access_token'],
-            'refresh_token' => $result['refresh_token'],
-            'expires_at' => strtotime($result['auth_end_time']),
-            'auth_type' => 'all_store',
+            'shopId' => $mainId,
+            'shopId' => '',
+            'accessToken' => $result['access_token'],
+            'refreshToken' => $result['refresh_token'],
+            'expiresAt' => strtotime($result['auth_end_time']),
+            'authType' => 'all_store',
             'platform' => 'huolala'
         ];
 
-        ShansAuthTokenClass::add($data);
+        DeliveryAuthTokenClass::add($data);
 
         return $this->asJson(['return_code' => 0, 'return_msg' => 'OK']);
     }
@@ -530,16 +629,16 @@ class DeliveryController extends BaseController
 
         // 保存授权信息到数据库
         $data = [
-            'user_id' => $mainId,
-            'shop_id' => '',
-            'access_token' => $result['access_token'],
-            'refresh_token' => $result['refresh_token'],
-            'expires_at' => $result['expire_in'],
-            'auth_type' => 'all_store',
+            'mainId' => $mainId,
+            'shopId' => '',
+            'accessToken' => $result['access_token'],
+            'refreshToken' => $result['refresh_token'],
+            'expiresAt' => $result['expire_in'],
+            'authType' => 'all_store',
             'platform' => 'fengniao'
         ];
 
-        ShansAuthTokenClass::add($data);
+        DeliveryAuthTokenClass::add($data);
 
         return $this->asJson(['return_code' => 0, 'return_msg' => 'OK']);
     }

+ 66 - 0
app-ghs/models/BaseForm.php

@@ -3,6 +3,7 @@ namespace ghs\models;
 
 use yii\base\Model;
 use common\components\util;
+use Yii;
 
 /**
  * 管理员表单基类
@@ -11,8 +12,10 @@ class BaseForm extends Model
 {
     /**
      * 验证表单数据并返回结果
+     * 如果验证失败,返回错误响应
      *
      * @return bool 验证是否通过
+     * @throws \Exception
      */
     public function validateForm()
     {
@@ -24,4 +27,67 @@ class BaseForm extends Model
         }
         return true;
     }
+
+    /**
+     * 自动加载并验证请求数据
+     *
+     * 自动检测请求类型(GET 或 POST),加载对应的数据并进行验证。
+     * 相比手动指定 $method 参数,使用 $request->isGet 和 $request->isPost
+     * 是 Yii 框架推荐的做法,更符合框架风格。
+     *
+     * 使用示例:
+     * - POST 请求自动加载:$form->loadAndValidate();
+     * - GET 请求自动加载:$form->loadAndValidate();
+     *
+     * @param string $formName 表单名称,用于嵌套数据
+     * @return bool 验证成功返回 true,失败自动输出错误响应
+     * @throws \yii\base\InvalidConfigException
+     */
+    public function loadAndValidate($formName = '')
+    {
+        $request = Yii::$app->request;
+
+        // ✨ 优化:使用 Yii 提供的方法检查请求类型
+        // 相比手动字符串比较 ($method === 'get'),这种方式更符合框架风格
+        if ($request->isGet) {
+            $data = $request->get();
+        } elseif ($request->isPost) {
+            $data = $request->post();
+        } else {
+            util::fail('不支持的请求方法');
+            return false;
+        }
+
+        if (!$this->load($data, '')) {
+            util::fail('数据加载失败');
+            return false;
+        }
+
+        return $this->validateForm();
+    }
+
+    /**
+     * 批量设置属性到模型
+     *
+     * @param $model 目标模型
+     * @param array $allowedFields 允许的字段列表
+     * @return 目标模型
+     */
+    public function assignToModel($model, $allowedFields = [])
+    {
+        $properties = array_keys($this->attributes);
+
+        foreach ($properties as $prop) {
+            // 如果指定了允许字段列表,检查是否在其中
+            if (!empty($allowedFields) && !in_array($prop, $allowedFields)) {
+                continue;
+            }
+
+            if (isset($this->$prop)) {
+                $model->$prop = $this->$prop;
+            }
+        }
+
+        return $model;
+    }
 }

+ 51 - 0
app-ghs/models/delivery/AddressForm.php

@@ -0,0 +1,51 @@
+<?php
+namespace ghs\models\delivery;
+
+use bizGhs\order\classes\OrderClass;
+use ghs\models\BaseForm;
+use Yii;
+
+/**
+ * 获取配送地址表单模型
+ * 用于 actionAddress 的参数验证
+ */
+class AddressForm extends BaseForm
+{
+    public $orderId;           // xhGhsOrder.id
+
+    public function rules()
+    {
+        return [
+            ['orderId', 'required'],
+            ['orderId', 'integer'],
+            ['orderId', 'validateOrderId'],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'orderId' => '订单ID',
+        ];
+    }
+
+    /**
+     * 验证订单ID是否存在且属于当前商户
+     * @param $attribute
+     */
+    public function validateOrderId($attribute)
+    {
+        if (!$this->hasErrors()) {
+            $order = OrderClass::getById($this->orderId, false, 'id, mainId');
+            if (empty($order)) {
+                $this->addError($attribute, '订单不存在');
+                return;
+            }
+            
+            if ($order['mainId'] != Yii::$app->controller->mainId) {
+                $this->addError($attribute, '订单不属于当前商户');
+            }
+        }
+    }
+}
+

+ 54 - 0
app-ghs/models/delivery/AllDeliveryQuotesForm.php

@@ -0,0 +1,54 @@
+<?php
+namespace ghs\models\delivery;
+
+use bizGhs\order\classes\OrderClass;
+use ghs\models\BaseForm;
+use Yii;
+
+/**
+ * 获取多个平台配送报价表单模型
+ * 用于 actionAllDeliveryQuotes 的参数验证
+ */
+class AllDeliveryQuotesForm extends BaseForm
+{
+    public $orderId;           // xhGhsOrder.id
+    public $pickupTime;        // 取件时间
+
+    public function rules()
+    {
+        return [
+            [['orderId'], 'required'],
+            ['orderId', 'integer'],
+            ['orderId', 'validateOrderId'],
+            ['pickupTime', 'safe'],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'orderId' => '订单ID',
+            'pickupTime' => '取件时间',
+        ];
+    }
+
+    /**
+     * 验证订单ID是否存在且属于当前商户
+     * @param $attribute
+     */
+    public function validateOrderId($attribute)
+    {
+        if (!$this->hasErrors()) {
+            $order = OrderClass::getById($this->orderId, false, 'id, mainId');
+            if (empty($order)) {
+                $this->addError($attribute, '订单不存在');
+                return;
+            }
+            
+            if ($order['mainId'] != Yii::$app->controller->mainId) {
+                $this->addError($attribute, '订单不属于当前商户');
+            }
+        }
+    }
+}
+

+ 61 - 0
app-ghs/models/delivery/CancelOrderForm.php

@@ -0,0 +1,61 @@
+<?php
+namespace ghs\models\delivery;
+
+use bizGhs\express\classes\GhsDeliveryOrderClass;
+use ghs\models\BaseForm;
+use Yii;
+
+/**
+ * 取消配送订单表单模型
+ * 用于 actionCancelOrder 的参数验证
+ */
+class CancelOrderForm extends BaseForm
+{
+    public $ghsOrderId;        // 批发订单ID (必需)
+    public $orderId;           // 第三方平台订单ID (可选)
+    public $platform;          // 配送平台 (可选)
+    public $order_cancel_code; // 订单取消原因代码 (用于蜂鸟平台)
+
+    public function rules()
+    {
+        return [
+            ['ghsOrderId', 'required'],
+            ['ghsOrderId', 'integer'],
+            ['ghsOrderId', 'validateGhsOrderId'],
+            ['orderId', 'string'],
+            ['platform', 'string'],
+            ['order_cancel_code', 'string'],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'ghsOrderId' => '批发订单ID',
+            'orderId' => '平台订单ID',
+            'platform' => '配送平台',
+            'order_cancel_code' => '取消原因代码',
+        ];
+    }
+
+    /**
+     * 验证批发订单ID
+     * @param $attribute
+     */
+    public function validateGhsOrderId($attribute)
+    {
+        if (!$this->hasErrors()) {
+            // 检查配送订单是否存在
+            $gdo = GhsDeliveryOrderClass::getByCondition(
+                ['mainId' => Yii::$app->controller->mainId, 'ghsOrderId' => $this->ghsOrderId, 'orderStatus!=' => -1],
+                false,
+                'orderId, deliveryId'
+            );
+            
+            if (empty($gdo)) {
+                $this->addError($attribute, '未找到对应的配送订单');
+            }
+        }
+    }
+}
+

+ 54 - 0
app-ghs/models/delivery/CancelReasonForm.php

@@ -0,0 +1,54 @@
+<?php
+namespace ghs\models\delivery;
+
+use bizGhs\express\classes\GhsDeliveryOrderClass;
+use ghs\models\BaseForm;
+use Yii;
+
+/**
+ * 获取订单取消原因表单模型
+ * 用于 actionCancelReason 的参数验证
+ */
+class CancelReasonForm extends BaseForm
+{
+    public $ghsOrderId;        // 批发订单ID
+    public $platform;          // 配送平台
+
+    public function rules()
+    {
+        return [
+            [['ghsOrderId', 'platform'], 'required'],
+            ['ghsOrderId', 'integer'],
+            ['ghsOrderId', 'validateGhsOrderId'],
+            ['platform', 'string'],
+            ['platform', 'in', 'range' => ['shansong', 'fengniao', 'huolala', 'dada']],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'ghsOrderId' => '批发订单ID',
+            'platform' => '配送平台',
+        ];
+    }
+
+    /**
+     * 验证批发订单ID
+     * @param $attribute
+     */
+    public function validateGhsOrderId($attribute)
+    {
+        if (!$this->hasErrors()) {
+            $gdo = GhsDeliveryOrderClass::getByCondition(
+                ['mainId' => Yii::$app->controller->mainId, 'ghsOrderId' => $this->ghsOrderId, 'orderStatus!=' => -1],
+                false
+            );
+            
+            if (empty($gdo)) {
+                $this->addError($attribute, '未找到对应的配送订单');
+            }
+        }
+    }
+}
+

+ 68 - 0
app-ghs/models/delivery/CreateOrderForm.php

@@ -0,0 +1,68 @@
+<?php
+namespace ghs\models\delivery;
+
+use bizGhs\order\classes\OrderClass;
+use ghs\models\BaseForm;
+use Yii;
+
+/**
+ * 创建配送订单表单模型
+ * 用于 actionCreateOrder 的参数验证
+ */
+class CreateOrderForm extends BaseForm
+{
+    public $orderId;           // xhGhsOrder.id
+    public $remark;            // 订单备注
+    public $price;             // 配送费(单位:分)
+    public $pickupTime;        // 取件时间
+    public $allParams;         // 配送平台所需的所有参数
+
+    public function rules()
+    {
+        return [
+            [['orderId', 'price'], 'required'],
+            [['orderId', 'price'], 'integer'],
+            ['orderId', 'validateOrderId'],
+            ['pickupTime', 'safe'],
+            ['allParams', 'safe'],
+            ['remark', 'string', 'max' => 500],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'orderId' => '订单ID',
+            'remark' => '备注',
+            'price' => '配送费用',
+            'pickupTime' => '取件时间',
+            'allParams' => '配送参数',
+        ];
+    }
+
+    /**
+     * 验证订单ID是否存在且属于当前商户
+     * @param $attribute
+     */
+    public function validateOrderId($attribute)
+    {
+        if (!$this->hasErrors()) {
+            $order = OrderClass::getById($this->orderId, false, 'id, mainId, sendStatus');
+            if (empty($order)) {
+                $this->addError($attribute, '订单不存在');
+                return;
+            }
+            
+            if ($order['mainId'] != Yii::$app->controller->mainId) {
+                $this->addError($attribute, '订单不属于当前商户');
+                return;
+            }
+
+            // 检查订单是否已发过跑腿
+            if (!in_array($order['sendStatus'], [-4, -1])) {
+                $this->addError($attribute, '订单已经发过跑腿');
+            }
+        }
+    }
+}
+

+ 35 - 0
app-ghs/models/delivery/OrderDetailForm.php

@@ -0,0 +1,35 @@
+<?php
+namespace ghs\models\delivery;
+
+use ghs\models\BaseForm;
+
+/**
+ * 查询配送订单详情表单模型
+ * 用于 actionOrderDetail 的参数验证
+ */
+class OrderDetailForm extends BaseForm
+{
+    public $platform;          // 配送平台 (shansong, fengniao, huolala 等)
+    public $orderId;           // 第三方平台订单ID
+    public $orderSn;           // 订单编号(可选,用于蜂鸟平台)
+
+    public function rules()
+    {
+        return [
+            [['platform', 'orderId'], 'required'],
+            [['platform', 'orderId'], 'string'],
+            ['platform', 'in', 'range' => ['shansong', 'fengniao', 'huolala', 'dada']],
+            ['orderSn', 'string'],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'platform' => '配送平台',
+            'orderId' => '平台订单ID',
+            'orderSn' => '订单编号',
+        ];
+    }
+}
+

+ 1 - 1
biz-ghs/express/classes/ShansAuthTokenClass.php → biz-ghs/express/classes/DeliveryAuthTokenClass.php

@@ -5,7 +5,7 @@ namespace bizGhs\express\classes;
 
 use bizGhs\base\classes\BaseClass;
 
-class ShansAuthTokenClass extends BaseClass
+class DeliveryAuthTokenClass extends BaseClass
 {
     public static $baseFile = '\bizGhs\express\models\ShansAuthToken';
 }

+ 7 - 6
common/components/delivery/services/DispatchService.php

@@ -3,7 +3,7 @@
 namespace common\components\delivery\services;
 
 use biz\shop\classes\ShopClass;
-use bizGhs\express\classes\ShansAuthTokenClass;
+use bizGhs\express\classes\DeliveryAuthTokenClass;
 use bizGhs\order\classes\OrderClass;
 use bizGhs\order\classes\OrderItemClass;
 use common\components\delivery\helpers\HttpClient;
@@ -24,7 +24,7 @@ class DispatchService
     public function __construct($mainId, $platform='')
     {
         if ($platform == '') {
-            $authPlatforms = ShansAuthTokenClass::getAllByCondition(['user_id'=>$mainId]);
+            $authPlatforms = DeliveryAuthTokenClass::getAllByCondition(['mainId'=>$mainId]);
             foreach($authPlatforms as $pt) {
                 switch ($pt['platform']) {
                     case 'shansong':
@@ -41,7 +41,7 @@ class DispatchService
                 }
             }
         } else {
-            $authPlatform = ShansAuthTokenClass::getByCondition(['user_id'=>$mainId, 'platform'=>$platform]);
+            $authPlatform = DeliveryAuthTokenClass::getByCondition(['mainId'=>$mainId, 'platform'=>$platform]);
             switch ($platform) {
                 case 'shansong':
                     $this->adapters['shansong'] = new ShansongAdapter($authPlatform['access_token']);
@@ -100,12 +100,13 @@ class DispatchService
         return $adapter->cancelOrder($orderId, $reason);
     }
 
-    protected function setPlatform()
+    public function getCancelReasonList($orderId)
     {
-
+        $adapter = $this->getAdapter();
+        return $adapter->getCancelReasonList($orderId);
     }
 
-    protected function getAdapter()
+    public function getAdapter()
     {
         return $this->adapters[$this->platformName];
     }

+ 50 - 52
common/components/delivery/services/adapter/FengniaoAdapter.php

@@ -707,8 +707,13 @@ class FengniaoAdapter implements Adapter
     public function getCancelReasonList($data)
     {
         // 验证 order_id 和 partner_order_code 必填一个
-        $orderId = $data['order_id'] ?? null;
-        $partnerOrderCode = $data['partner_order_code'] ?? null;
+        $orderId = $data['order_id'] ?? $data;
+        $partnerOrderCode = null;
+        // 如果 $orderId 是 XSD_ 开头,则给 partnerOrderCode 赋值为 $orderId
+        if (strpos($orderId, 'XSD_') === 0) {
+            $partnerOrderCode = $orderId;
+            $orderId = null;
+        }
         
         if (empty($orderId) && empty($partnerOrderCode)) {
             Yii::warning("[FengniaoAdapter] getCancelReasonList failed: order_id and partner_order_code cannot both be empty");
@@ -738,7 +743,6 @@ class FengniaoAdapter implements Adapter
         $resp = HttpClient::post($url, $payload, [
             'Content-Type' => 'application/json',
         ]);
-
         Yii::info("[FengniaoAdapter] getCancelReasonList Response: " . json_encode($resp));
 
         // 处理响应
@@ -807,10 +811,9 @@ class FengniaoAdapter implements Adapter
      *   成功:['code' => 0, 'data' => ['result' => true]]
      *   失败:['code' => 错误码, 'msg' => 错误信息, 'data' => null]
      */
-    public function cancelOrder($data)
+    public function cancelOrder($orderId, $params)
     {
         // 验证 order_id 和 partner_order_code 必填一个
-        $orderId = $data['order_id'] ?? $data;
         $partnerOrderCode = null;
         // 如果 $orderId 是 XSD_ 开头,则给 partnerOrderCode 赋值为 $orderId
         if (strpos($orderId, 'XSD_') === 0) {
@@ -829,7 +832,6 @@ class FengniaoAdapter implements Adapter
 
         // 构建业务数据
         $businessData = [];
-
         // 必填参数 - 订单标识(二选一)
         if (!empty($orderId)) {
             $businessData['order_id'] = $orderId;
@@ -839,52 +841,49 @@ class FengniaoAdapter implements Adapter
         }
 
         // 必填参数 - 取消角色
-        $businessData['order_cancel_role'] = 1; // 默认为 1商户取消
-        if (isset($data['order_cancel_role'])) {
-            $businessData['order_cancel_role'] = (int)$data['order_cancel_role'];
-        } //else {
-            //Yii::warning("[FengniaoAdapter] cancelOrder failed: order_cancel_role is required");
-            //return [
-                //'code' => -1,
-                //'msg' => 'order_cancel_role is required (1:商户取消, 2:用户取消)',
-                //'data' => null
-            //];
-        //}
+        if (isset($params['order_cancel_role'])) {
+            $businessData['order_cancel_role'] = (int)$params['order_cancel_role'];
+        } else {
+            Yii::warning("[FengniaoAdapter] cancelOrder failed: order_cancel_role is required");
+            return [
+                'code' => -1,
+                'msg' => 'order_cancel_role is required (1:商户取消, 2:用户取消)',
+                'data' => null
+            ];
+        }
 
         // 必填参数 - 取消原因code
-        $businessData['order_cancel_code'] = 9;
-//        if (isset($data['order_cancel_code'])) {
-//            $businessData['order_cancel_code'] = (int)$data['order_cancel_code'];
-//        } else {
-//            Yii::warning("[FengniaoAdapter] cancelOrder failed: order_cancel_code is required");
-//            return [
-//                'code' => -1,
-//                'msg' => 'order_cancel_code is required',
-//                'data' => null
-//            ];
-//        }
+        if (isset($params['order_cancel_code'])) {
+            $businessData['order_cancel_code'] = (int)$params['order_cancel_code'];
+        } else {
+            Yii::warning("[FengniaoAdapter] cancelOrder failed: order_cancel_code is required");
+            return [
+                'code' => -1,
+                'msg' => 'order_cancel_code is required',
+                'data' => null
+            ];
+        }
 
         // 必填参数 - 取消原因补充
-        $businessData['order_cancel_other_reason'] = '顾客下错单';
-//        if (!empty($data['order_cancel_other_reason'])) {
-//            $businessData['order_cancel_other_reason'] = $data['order_cancel_other_reason'];
-//        } else {
-//            Yii::warning("[FengniaoAdapter] cancelOrder failed: order_cancel_other_reason is required");
-//            return [
-//                'code' => -1,
-//                'msg' => 'order_cancel_other_reason is required (20字以内)',
-//                'data' => null
-//            ];
-//        }
+        if (!empty($params['order_cancel_reason'])) {
+            $businessData['order_cancel_other_reason'] = $params['order_cancel_reason'];
+        } else {
+            Yii::warning("[FengniaoAdapter] cancelOrder failed: order_cancel_other_reason is required");
+            return [
+                'code' => -1,
+                'msg' => 'order_cancel_other_reason is required (20字以内)',
+                'data' => null
+            ];
+        }
 
         // 可选参数 - 是否期望发起售中逆向
-        if (isset($data['need_reverse_order']) && $data['need_reverse_order'] !== null) {
-            $businessData['need_reverse_order'] = (bool)$data['need_reverse_order'];
+        if (isset($params['need_reverse_order']) && $params['need_reverse_order'] !== null) {
+            $businessData['need_reverse_order'] = (bool)$params['need_reverse_order'];
         }
 
         // 可选参数 - 取消价格
-        if (isset($data['actual_cancel_cost_cent']) && $data['actual_cancel_cost_cent'] !== null) {
-            $businessData['actual_cancel_cost_cent'] = (int)$data['actual_cancel_cost_cent'];
+        if (isset($params['actual_cancel_cost_cent']) && $params['actual_cancel_cost_cent'] !== null) {
+            $businessData['actual_cancel_cost_cent'] = (int)$params['actual_cancel_cost_cent'];
         }
 
         // 构建请求参数
@@ -895,7 +894,6 @@ class FengniaoAdapter implements Adapter
         $resp = HttpClient::post($url, $payload, [
             'Content-Type' => 'application/json',
         ]);
-
         Yii::info("[FengniaoAdapter] cancelOrder Response: " . json_encode($resp));
 
         // 处理响应
@@ -962,8 +960,13 @@ class FengniaoAdapter implements Adapter
     public function preCancelOrder($data)
     {
         // 验证 order_id 和 partner_order_code 必填一个
-        $orderId = $data['order_id'] ?? null;
-        $partnerOrderCode = $data['partner_order_code'] ?? null;
+        $orderId = $data['order_id'];
+        $partnerOrderCode = null;
+        // 如果 $orderId 是 XSD_ 开头,则给 partnerOrderCode 赋值为 $orderId
+        if (strpos($orderId, 'XSD_') === 0) {
+            $partnerOrderCode = $orderId;
+            $orderId = null;
+        }
         
         if (empty($orderId) && empty($partnerOrderCode)) {
             Yii::warning("[FengniaoAdapter] preCancelOrder failed: order_id and partner_order_code cannot both be empty");
@@ -986,7 +989,6 @@ class FengniaoAdapter implements Adapter
 
         // 构建业务数据
         $businessData = [];
-
         // 订单标识(二选一)
         if (!empty($orderId)) {
             $businessData['order_id'] = (int)$orderId;
@@ -994,19 +996,16 @@ class FengniaoAdapter implements Adapter
         if (!empty($partnerOrderCode)) {
             $businessData['partner_order_code'] = $partnerOrderCode;
         }
-
         // 必填参数 - 取消原因code
         $businessData['order_cancel_code'] = (int)$data['order_cancel_code'];
 
         // 构建请求参数
         $payload = $this->buildRequestPayload('preCancelOrder', $businessData);
-
         // 发送请求
         $url = $this->baseUrl . '/preCancelOrder';
         $resp = HttpClient::post($url, $payload, [
             'Content-Type' => 'application/json',
         ]);
-
         Yii::info("[FengniaoAdapter] preCancelOrder Response: " . json_encode($resp));
 
         // 处理响应
@@ -1272,7 +1271,7 @@ class FengniaoAdapter implements Adapter
         $businessData = [];
         // 订单标识(二选一)
         if (!empty($orderId)) {
-            $businessData['order_id'] = (int)$orderId;
+            $businessData['order_id'] = $orderId;
         }
         if (!empty($partnerOrderCode)) {
             $businessData['partner_order_code'] = $partnerOrderCode;
@@ -1286,7 +1285,6 @@ class FengniaoAdapter implements Adapter
         $resp = HttpClient::post($url, $payload, [
             'Content-Type' => 'application/json',
         ]);
-
         Yii::info("[FengniaoAdapter] getOrderDetail Response: " . json_encode($resp));
 
         // 处理响应

+ 5 - 1
common/components/util.php

@@ -271,7 +271,11 @@ class util
         return $root;
     }
 
-    //错误输出
+    /**
+     * 错误输出
+     * @param string $msg
+     * @throws \Exception
+     */
     public static function fail($msg = '操作失败')
     {
         Yii::info($msg);