|
|
@@ -104,6 +104,29 @@ class DispatchService
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ public function validateOrder($order)
|
|
|
+ {
|
|
|
+ if(empty($order['address'])){
|
|
|
+ throw new \Exception('客户地址缺失');
|
|
|
+ }
|
|
|
+
|
|
|
+ if(empty($order['long']) || empty($order['lat'])){
|
|
|
+ throw new \Exception('客户地址信息缺失');
|
|
|
+ }
|
|
|
+
|
|
|
+ if(empty($order['customName'])){
|
|
|
+ throw new \Exception('客户姓名缺失');
|
|
|
+ }
|
|
|
+
|
|
|
+ if(empty($order['city'])){
|
|
|
+ throw new \Exception('客户城市信息缺失');
|
|
|
+ }
|
|
|
+
|
|
|
+ if(empty($order['customMobile']) && empty($order['receiveMobile']) && empty($order['bookMobile'])){
|
|
|
+ throw new \Exception('客户联系电话缺失');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* @param $order
|
|
|
* @param string $orderType 订单类型:xhOrder 或 xhGhsOrder
|
|
|
@@ -227,7 +250,7 @@ class DispatchService
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 获取所有平台的最佳报价(使用 Guzzle 并发请求)
|
|
|
+ * 获取所有平台的报价(使用 Guzzle 并发请求)
|
|
|
*
|
|
|
* 流程说明:
|
|
|
* 1. 同步准备各平台的前置数据(如城市信息、订单商品等)
|
|
|
@@ -236,19 +259,20 @@ class DispatchService
|
|
|
*
|
|
|
* @param array $order 订单信息
|
|
|
* @param array $shop 店铺信息
|
|
|
+ * @param string $pickupTime 取件时间
|
|
|
* @return array 返回所有平台的报价结果
|
|
|
*/
|
|
|
- public function getAllPlatformPrice($order, $shop, $orderTime)
|
|
|
+ public function getAllPlatformPrice($order, $shop, $pickupTime)
|
|
|
{
|
|
|
// 第一步:准备前置数据(同步进行,因为某些平台需要这些数据)
|
|
|
- $preparedData = $this->preparePlatformData($order, $shop, $orderTime);
|
|
|
+ $preparedData = $this->preparePlatformData($order, $shop, $pickupTime);
|
|
|
|
|
|
if (empty($preparedData)) {
|
|
|
return ['error' => '所有平台数据准备失败'];
|
|
|
}
|
|
|
|
|
|
// 第二步:并发调用各平台的报价接口
|
|
|
- $results = $this->concurrentGetPrices($preparedData, $orderTime);
|
|
|
+ $results = $this->concurrentGetPrices($preparedData, $pickupTime);
|
|
|
|
|
|
if (empty($results['success']) && empty($results['failed'])) {
|
|
|
return ['error' => '没有可用的物流平台'];
|
|
|
@@ -263,12 +287,12 @@ class DispatchService
|
|
|
|
|
|
/**
|
|
|
* 为各平台准备订单数据(前置数据同步获取)
|
|
|
- *
|
|
|
* @param array $order 订单信息
|
|
|
* @param array $shop 店铺信息
|
|
|
+ * @param string $pickupTime 取件时间
|
|
|
* @return array 各平台的订单数据
|
|
|
*/
|
|
|
- private function preparePlatformData($order, $shop, $orderTime)
|
|
|
+ private function preparePlatformData($order, $shop, $pickupTime)
|
|
|
{
|
|
|
$preparedData = [];
|
|
|
|
|
|
@@ -296,7 +320,7 @@ class DispatchService
|
|
|
// 准备 Shansong 数据
|
|
|
if (isset($this->adapters['shansong'])) {
|
|
|
try {
|
|
|
- $preparedData['shansong'] = $this->prepareShansongData($order, $shop, $orderTime);
|
|
|
+ $preparedData['shansong'] = $this->prepareShansongData($order, $shop, $pickupTime);
|
|
|
} catch (\Exception $e) {
|
|
|
Yii::warning("Shansong 数据准备失败: {$e->getMessage()}");
|
|
|
}
|
|
|
@@ -318,7 +342,7 @@ class DispatchService
|
|
|
if(in_array($isPersonDirect, [1, 2])){ // 支持专人直送
|
|
|
$customSettings = ['isPersonDirect' => 1];//自定义用户设置:专人直送
|
|
|
$preparedData['shunfeng_1v1'] = $this->prepareShunfengData($order, $shop, $customSettings);
|
|
|
- Yii::info('预创建订单 shunfeng_1v1 request data: ' . json_encode($preparedData['shunfeng_1v1']));
|
|
|
+ // Yii::info('预创建订单 shunfeng_1v1 request data: ' . json_encode($preparedData['shunfeng_1v1']));
|
|
|
}
|
|
|
// else{
|
|
|
// $customSettings = [];//自定义用户设置:默认不专人直送
|
|
|
@@ -334,7 +358,14 @@ class DispatchService
|
|
|
try {
|
|
|
$dadaAdapter = $this->adapters['dada'];
|
|
|
$shopNo = $dadaAdapter->getShopNo();
|
|
|
- $preparedData['dada'] = $this->prepareDadaData($order, $shop, $shopNo);
|
|
|
+
|
|
|
+ // 拼送
|
|
|
+ $customSettings = ['is_direct_delivery' => 0];
|
|
|
+ $preparedData['dada'] = $this->prepareDadaData($order, $shopNo, $customSettings);
|
|
|
+
|
|
|
+ // 专送
|
|
|
+ //$customSettings = ['is_direct_delivery' => 1];
|
|
|
+ //$preparedData['dada_1v1'] = $this->prepareDadaData($order, $shopNo, $customSettings);
|
|
|
} catch (\Exception $e) {
|
|
|
Yii::warning("Dada 数据准备失败: {$e->getMessage()}");
|
|
|
}
|
|
|
@@ -380,7 +411,7 @@ class DispatchService
|
|
|
}
|
|
|
$specReq = array_filter($specReq);
|
|
|
|
|
|
- // 构建所有车型数据
|
|
|
+ // 构建所需车型数据
|
|
|
$vehicleTypeList = [];
|
|
|
foreach ($vehicleList as $vehicle) {
|
|
|
if(!in_array($vehicle['vehicle_name'], ['跑腿', '小面', '微面'])){ // && !$this->adapters['huolala']->getIsSandbox()
|
|
|
@@ -463,13 +494,13 @@ class DispatchService
|
|
|
'receiver_latitude' => (float)$order['lat'],
|
|
|
'receiver_longitude' => (float)$order['long'],
|
|
|
'receiver_address' => $order['fullAddress'],
|
|
|
- 'position_source' => 3,
|
|
|
+ 'position_source' => 3, //坐标经纬度来源(1:腾讯地图,2:百度地图, 3:高德地图)
|
|
|
'goods_count' => count($goodsItemList),
|
|
|
'goods_weight' => (float)$order['weight'],
|
|
|
'goods_total_amount_cent' => (int)($order['actPrice'] * 100),
|
|
|
'goods_actual_amount_cent' => (int)($order['prePrice'] * 100),
|
|
|
'goods_item_list' => $goodsItemList,
|
|
|
- 'order_type' => 1,
|
|
|
+ //'order_type' => 1,
|
|
|
'chain_store_id' => $platformShopId, // 蜂鸟平台 shopId
|
|
|
'order_remark' => $order['remark'] ?? '',
|
|
|
];
|
|
|
@@ -478,7 +509,7 @@ class DispatchService
|
|
|
/**
|
|
|
* 准备闪送订单数据
|
|
|
*/
|
|
|
- private function prepareShansongData($order, $shop, $orderTime)
|
|
|
+ private function prepareShansongData($order, $shop, $pickupTime)
|
|
|
{
|
|
|
$data = [
|
|
|
'platform' => 'shansong',
|
|
|
@@ -513,11 +544,15 @@ class DispatchService
|
|
|
'sendNum' => $order['sendNum'],
|
|
|
];
|
|
|
|
|
|
- $timestamp = strtotime($orderTime);
|
|
|
+ $timestamp = strtotime($pickupTime);
|
|
|
$currentTime = time() + 3600;
|
|
|
if($timestamp > $currentTime){
|
|
|
- $data['appointType'] = 1; // 修改预约类型
|
|
|
- $data['appointmentDate'] = $orderTime; // 预约时间, yyyy-MM-dd HH:mm格式(例如:2020-02-02 22:00),指的是预约取件时间,只支持一个小时以后两天以内
|
|
|
+ $data['appointType'] = 1; // 修改成 1预约单
|
|
|
+ $data['appointmentDate'] = $pickupTime; // 预约时间, yyyy-MM-dd HH:mm格式(例如:2020-02-02 22:00),指的是预约取件时间,只支持一个小时以后
|
|
|
+ //两天以内
|
|
|
+ if($timestamp > time() + 172800){
|
|
|
+ throw new \Exception('取件时间不能超过两天');
|
|
|
+ }
|
|
|
|
|
|
// 'expectStartTime' 期望送达时间起始时间 -- 毫秒级时间戳。期望送达时间为时间点时,无需传本参数;期望送达时间为时间段时,期望送达时间为expectStartTime至expectEndTime
|
|
|
// 'expectEndTime' 期望送达时间终止时间 -- 毫秒级时间戳。期望送达时间为时间点时,只需传本参数;期望送达时间为时间段时,期望送达时间为expectStartTime至expectEndTime
|
|
|
@@ -555,7 +590,7 @@ class DispatchService
|
|
|
'user_lat' => (string)$order['lat'], // 用户地址纬度
|
|
|
'user_address' => $order['fullAddress'], // 用户详细地址
|
|
|
'weight' => $order['weight'] * 1000, // 物品重量(单位:克)
|
|
|
- 'product_type' => 14, // 物品类型(14:鲜花)
|
|
|
+ 'product_type' => 14, // 物品类型(14:鲜花)
|
|
|
'push_time' => time(), // 推单时间(秒级时间戳)
|
|
|
//shop 平台级开发者需要传入
|
|
|
// 'shop' => [ // 发货店铺信息
|
|
|
@@ -578,7 +613,7 @@ class DispatchService
|
|
|
'lbs_type' => 2, // 坐标类型(2:高德坐标)
|
|
|
'is_insured' => 0, // 是否保价(0:非保价)
|
|
|
'is_person_direct' => $customSettings['isPersonDirect'] ?? 0, // 是否专人直送(0:否 1:是)
|
|
|
- 'vehicle' => 0, // 配送交通工具(0:否)
|
|
|
+ 'vehicle' => 0, // 配送交通工具,0:否;1:电动车;2:小轿车
|
|
|
//'four_wheeler_type' => 0, // 车型 -- 只有vehicle = 2,⻋型字段才有效。
|
|
|
//'declared_value' => 0, // 保价金额(单位:分)
|
|
|
//'gratuity_fee' => 0, // 订单小费(单位:分)
|
|
|
@@ -591,7 +626,7 @@ class DispatchService
|
|
|
/**
|
|
|
* 准备达达订单数据
|
|
|
*/
|
|
|
- private function prepareDadaData($order, $shop, $shopNo)
|
|
|
+ private function prepareDadaData($order, $shopNo, $customSettings)
|
|
|
{
|
|
|
$productList = [];
|
|
|
$itemInfos = OrderItemClass::getAllByCondition(['orderSn' => $order['orderSn']], null, 'id, name, unitPrice, num, bigNum, smallNum, xhUnitName');
|
|
|
@@ -607,7 +642,7 @@ class DispatchService
|
|
|
// 回调地址
|
|
|
$callbackUrl = Yii::$app->params['ghsHost'] . '/delivery/dada-callback';
|
|
|
|
|
|
- return [
|
|
|
+ $request = [
|
|
|
'platform' => 'dada',
|
|
|
'shop_no' => $shopNo,
|
|
|
'origin_id' => $order['orderSn'],
|
|
|
@@ -624,6 +659,13 @@ class DispatchService
|
|
|
'info' => $order['remark'] ?? '',
|
|
|
'product_list' => $productList,
|
|
|
];
|
|
|
+
|
|
|
+ if(isset($customSettings['is_direct_delivery']) && $customSettings['is_direct_delivery'] == 1){
|
|
|
+ // $request['origin_id'] = $order['orderSn'] . '_1v1'; // 根据达达的情况,必然会造成订单号出错
|
|
|
+ $request['is_direct_delivery'] = 1; //是否选择直拿直送(0:不需要;1:需要。选择直拿直送后,同一时间骑士只能配送此订单至完成,同时,也会相应的增加配送费用)
|
|
|
+ }
|
|
|
+
|
|
|
+ return $request;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -659,7 +701,7 @@ class DispatchService
|
|
|
/**
|
|
|
* 并发获取各平台报价(核心实现)
|
|
|
*
|
|
|
- * 使用 HttpClient::postConcurrent 实现真正的并发调用,每个平台请求 5 秒超时。
|
|
|
+ * 使用 HttpClient::postConcurrent 实现真正的并发调用,每个平台请求 10 秒超时。
|
|
|
* 某个平台的超时或失败不会影响其他平台的执行。
|
|
|
*
|
|
|
* @param array $preparedData 准备好的各平台数据
|
|
|
@@ -682,8 +724,8 @@ class DispatchService
|
|
|
$adapterKey = $platform; //由于要支持特殊逻辑处理,把 $this->adapters[xxx] 的 xxx 单独取变量
|
|
|
|
|
|
if (!isset($this->adapters[$platform])) {
|
|
|
- if($platform == 'shunfeng_1v1'){ //特殊处理 -- 专人直送的订单,使用顺丰平台适配器
|
|
|
- $adapterKey = 'shunfeng';
|
|
|
+ if (substr($platform, -4) === '_1v1') { //特殊处理 -- 专人直送的订单,使用对应平台适配器
|
|
|
+ $adapterKey = substr($platform, 0, -4);
|
|
|
} else {
|
|
|
$results['failed'][$platform] = '平台适配器未初始化';
|
|
|
Yii::warning("[DispatchService] {$platform} 平台适配器未初始化");
|
|
|
@@ -735,8 +777,8 @@ class DispatchService
|
|
|
|
|
|
try {
|
|
|
$adapterKey = $platform; //由于要支持特殊逻辑处理,把 $this->adapters[xxx] 的 xxx 单独取变量
|
|
|
- if($platform == 'shunfeng_1v1'){ //特殊处理 -- 专人直送的订单,使用顺丰平台适配器
|
|
|
- $adapterKey = 'shunfeng';
|
|
|
+ if (substr($platform, -4) === '_1v1') { //特殊处理 -- 专人直送的订单,使用对应平台适配器
|
|
|
+ $adapterKey = substr($platform, 0, -4);
|
|
|
}
|
|
|
$adapter = $this->adapters[$adapterKey];
|
|
|
$quote = null;
|
|
|
@@ -768,7 +810,7 @@ class DispatchService
|
|
|
Yii::warning("[DispatchService] {$platform} 报价返回错误: {$errorMsg}");
|
|
|
}
|
|
|
} else {
|
|
|
- // 蜂鸟和闪送的响应处理
|
|
|
+ // 响应处理
|
|
|
$quote = $adapter->processPriceResponse($response);
|
|
|
if ($quote && !isset($quote['error'])) {
|
|
|
$quote['_duration'] = $totalDuration;
|
|
|
@@ -980,7 +1022,7 @@ class DispatchService
|
|
|
break;
|
|
|
case 'dada':
|
|
|
$list = [
|
|
|
- 'name' => '达达',
|
|
|
+ 'name' => '达达'. ($platformKey == 'dada_1v1' ? ' (专送)' : ''),
|
|
|
'en_name' => 'dada',
|
|
|
'price' => $item['fee'],
|
|
|
'distance' => $item['distance'],
|