logistic_number = $logistic_number; } /** * Execute the job. * * @return void */ public function handle() { // ini_set('max_execution_time', 10); $zopResult = []; $response = $this->sentRequestToZT(); if(is_null($response)) return; if ($response->status) { $zopResult[] = [ 'routes' => $response->result, 'logisticNum' => $this->logistic_number, ]; } $result = $this->transformRoutes($zopResult); /* @var $orderPackageReceivedSyncService OrderPackageReceivedSyncService */ $orderPackageReceivedSyncService = app('OrderPackageReceivedSyncService'); !empty($result) && $orderPackageReceivedSyncService->update($result); } /** * 转换快递路由信息 * @param array $routs 快递路由 * @return array */ public function transformRoutes(array $routs): array { $result = []; foreach ($routs as $route) { $result = $this->transformRouteItem($route, $result); } return $result; } /** * @param $route * @param array $result * @return array */ private function transformRouteItem($route, array $result): array { $resultItem = []; $resultItem['logistic_number'] = $route['logisticNum']; $itemRoutes = $route['routes']; $lastRoute = null; if (!empty($itemRoutes)) { $lastRoute = $itemRoutes[count($itemRoutes) - 1]; $resultItem = $this->getNormalStatusAndReceivedAt($lastRoute, $resultItem); $resultItem['transfer_status'] = $this->getTransferStatus($itemRoutes); } else { $resultItem['status'] = null; $resultItem['transfer_status'] = []; } if (!array_key_exists('status', $resultItem)) { $resultItem['status'] = null; $resultItem['transfer_status'] = []; } try { $resultItem = $this->setExceptionType($resultItem, $lastRoute ? $lastRoute->scanDate / 1000 : null); } catch (\Exception $e) { } if ($resultItem['status'] == null) { unset($resultItem['status']); unset($resultItem['transfer_status']); } //如果没有发现额外的异常,且查询到物流轨迹,将异常置为无 if (!array_key_exists('exception', $resultItem) && !array_key_exists('exception_type', $resultItem) && array_key_exists('transfer_status', $resultItem) ) { $resultItem['exception_type'] = '无'; $resultItem['exception'] = '否'; } $result[] = $resultItem; return $result; } /** * @param array $data * @param $lastRouteDate * @return array */ private function setExceptionType(array $data, $lastRouteDate = null): array { $logistic_number = $data['logistic_number']; /** @var OrderPackage $orderPackage */ $orderPackage = OrderPackage::query()->with('order')->where('logistic_number', $logistic_number)->first(); $delivered_duration = now()->diffInHours(Carbon::parse($orderPackage['sent_at'])); $last_routed_duration = now()->diffInHours(Carbon::parse($lastRouteDate)); $VALID_HOURS = 4; $SHORT_RESPONSE_HOURS = (function ($province) { switch ($province) { case '浙江省': case '江苏省': case '上海': case '安徽省': return 24; case '北京': case '天津': case '江西省': case '湖北省': case '湖南省': case '广东省': case '福建省': case '山东省': case '河北省': case '河南省': case '山西省': case '四川省': case '陕西省': case '重庆': case '广西壮族自治区': case '贵州省': case '云南省': case '海南省': case '吉林省': case '黑龙江省': case '辽宁省': return 72; case '青海省': case '宁夏回族自治区': case '甘肃省': case '内蒙古自治区': case '新疆维吾尔自治区': case '西藏自治区': return 120; default: return 24; } })($orderPackage->order->province); $LONG_RESPONSE_HOURS = (function ($province) { switch ($province) { case '浙江省': case '江苏省': case '上海': case '安徽省': return 72; case '北京': case '天津': case '江西省': case '湖北省': case '湖南省': case '广东省': case '福建省': case '山东省': case '河北省': case '河南省': case '山西省': case '四川省': case '陕西省': case '重庆': case '广西壮族自治区': case '贵州省': case '云南省': case '海南省': case '吉林省': case '黑龙江省': case '辽宁省': return 120; case '青海省': case '宁夏回族自治区': case '甘肃省': case '内蒙古自治区': case '新疆维吾尔自治区': case '西藏自治区': return 168; default: return 72; } })($orderPackage->order->province); $SENDING_RESPONSE_HOURS = 48; $IS_ROUTED = 1; //0000 0001 有路由信息 $IS_IN_VALID_TIME = 2; //0000 0010 大于4小时 $IS_WEIGHED = 4; //0000 0100 称重过 $IS_RECEIVED = 8; //0000 1000 已经收货 $IS_SENDING = 16; //0001 0000 正在派送 $IS_SHORT_NO_RESPONSE = 32; //0010 0000 中转异常 $IS_LONG_NO_RESPONSE = 64; //0010 0000 疑似丢件 $IS_SENDING_NO_RESPONSE = 128; //0010 0000 派送异常 $conclusion = (function () use ( $data, $delivered_duration, $last_routed_duration, $VALID_HOURS, $IS_ROUTED, $IS_IN_VALID_TIME, $IS_WEIGHED, $IS_RECEIVED, $IS_SENDING, $IS_SHORT_NO_RESPONSE, $IS_LONG_NO_RESPONSE, $IS_SENDING_NO_RESPONSE, $SHORT_RESPONSE_HOURS, $LONG_RESPONSE_HOURS, $SENDING_RESPONSE_HOURS, $orderPackage ) { $conclusion = 0; $conclusion |= !empty($data['transfer_status']) ? $IS_ROUTED : 0; $conclusion |= ($delivered_duration > $VALID_HOURS) ? $IS_IN_VALID_TIME : 0; $conclusion |= ($orderPackage->weighed_at) ? $IS_WEIGHED : 0; $conclusion |= ($data['status'] == '已收件') ? $IS_RECEIVED : 0; $conclusion |= ($data['status'] == '派送中') ? $IS_SENDING : 0;// $conclusion |= ($last_routed_duration > $SHORT_RESPONSE_HOURS && $last_routed_duration < $LONG_RESPONSE_HOURS) ? $IS_SHORT_NO_RESPONSE : 0; $conclusion |= ($last_routed_duration > $LONG_RESPONSE_HOURS) ? $IS_LONG_NO_RESPONSE : 0; $conclusion |= ($last_routed_duration > $SENDING_RESPONSE_HOURS && $data['status'] == '派送中') ? $IS_SENDING_NO_RESPONSE : 0; return $conclusion; })(); switch ($conclusion) { case $IS_IN_VALID_TIME: $data['exception_type'] = '疑似库内丢件'; break; case $IS_IN_VALID_TIME | $IS_WEIGHED: $data['exception_type'] = '揽件异常'; break; case $IS_ROUTED | $IS_IN_VALID_TIME | $IS_SHORT_NO_RESPONSE: case $IS_ROUTED | $IS_IN_VALID_TIME | $IS_SHORT_NO_RESPONSE | $IS_WEIGHED: $data['exception_type'] = '中转异常'; break; case $IS_ROUTED | $IS_IN_VALID_TIME | $IS_LONG_NO_RESPONSE: case $IS_ROUTED | $IS_IN_VALID_TIME | $IS_LONG_NO_RESPONSE | $IS_WEIGHED: $data['exception_type'] = '疑似丢件'; break; default: break; } if ($conclusion == ($conclusion | $IS_ROUTED | $IS_IN_VALID_TIME | $IS_SENDING | $IS_SENDING_NO_RESPONSE)) { $data['exception_type'] = '派件异常'; } switch ($conclusion) { case $IS_IN_VALID_TIME: case $IS_IN_VALID_TIME | $IS_WEIGHED: case $IS_ROUTED | $IS_SHORT_NO_RESPONSE: case $IS_LONG_NO_RESPONSE: $data['exception'] = '是'; break; default: break; } return $data; } /** * 正常的状态与签收时间 * @param $lastRoute * @param array $resultItem * @return array */ private function getNormalStatusAndReceivedAt($lastRoute, array $resultItem): array { switch ($lastRoute->scanType) { case '收件': $resultItem['status'] = '已揽收'; break; case '到件': case '发件': $resultItem['status'] = '在途'; break; case 'ARRIVAL': case '派件': $resultItem['status'] = '派送中'; break; case 'SIGNED': case '签收': $resultItem['status'] = '已收件'; $resultItem['received_at'] = Carbon::parse($lastRoute->scanDate / 1000)->toDateTimeString(); break; default: $resultItem['status'] = '无'; break; } return $resultItem; } /** * @param $itemRoutes * @return array */ private function getTransferStatus($itemRoutes): array { $transfer_status = []; foreach ($itemRoutes as $item) { $data = []; $data['accept_time'] = Carbon::parse($item->scanDate / 1000)->toDateTimeString(); $scanSite = $item->scanSite; $data['accept_address'] = $scanSite->prov . '-' . $scanSite->name; $data['remark'] = $item->scanType; $transfer_status[] = $data; } return $transfer_status; } /** * 发送请求到中通 * @return mixed */ private function sentRequestToZT() { $url = config('api_logistic.ZTO.url'); $xAppKey = config('api_logistic.ZTO.x-appKey'); $appSecret = config('api_logistic.ZTO.appSecret'); $properties = new ZopProperties($xAppKey, $appSecret); $client = new ZopClient($properties); $request = new ZopRequest(); $request->setUrl($url); $request->setBody(json_encode([ 'billCode' => $this->logistic_number, ],JSON_UNESCAPED_UNICODE)); return json_decode($client->execute($request)); } }