select(['logistic_number', 'order_id']) ->with(['order' => function ($query) { return $query->with('logistic'); }]); if (Carbon::now()->lte($initDate)) {//当前时间小于等于初始化时间 //初始化查询一个月的数据,exception为否 $query = $query->where('sent_at', '>=', $initDate->subDays((int)config('api_logistic.days'))->toDateTimeString()) ->whereNull('received_at'); } else {//查询20天以内的数据 $query = $query->where('sent_at', '>=', now()->subDays(2)) ->whereNull('received_at'); } $query->chunk(2000, function ($orderPackages) { $logisticNumbers = $this->buildData($orderPackages); //sf if (array_key_exists('SF', $logisticNumbers)) { $SFLogisticNumbers = $logisticNumbers['SF']; foreach ($SFLogisticNumbers as $logisticNumber) { LogisticSFSync::dispatch($logisticNumber); } } //更新中通 if (array_key_exists('ZTO', $logisticNumbers)) { $ZTOLogisticNumbers = $logisticNumbers['ZTO']; foreach ($ZTOLogisticNumbers as $logisticNumber) { LogisticZopSync::dispatch($logisticNumber); } } //更新韵达 if (array_key_exists('YUNDA', $logisticNumbers)) { $YDLogisticNumbers = $logisticNumbers['YUNDA']; foreach ($YDLogisticNumbers as $logistic_number) { LogisticYDSync::dispatch($logistic_number); } } //更新圆通 $YTOLogisticNumbers = $logisticNumbers['YTO']; foreach ($YTOLogisticNumbers as $logistic_number) { LogisticYTOSync::dispatch($logistic_number); } }); } /** * 根据传递的承运商与快递单号更新快递信息 * @param array $logisticNumbers 快递单号 * example: ['SF' => ['SF1038651915891', 'SF1038651413847', 'SF1038611050071'],'ZT'=>['75424148714142','548464120822', '75424147834290']....] * @throws Exception 快递接口调用或者返回的信息有误,无法更新指定的快递路由信息 */ public function syncLogisticRouteApi(array $logisticNumbers) { $this->update($this->getLogisticRoutes($logisticNumbers)); } /** * 获取快件揽收信息 * @param array $request [ * 'SF' => ['SF1038651915891', 'SF1038651413847', 'SF1038611050071'], * 'ZT'=>['75424148714142','548464120822', '75424147834290'] * ] * @return array * @throws Exception */ public function getLogisticRoutes(array $request): array { $this->logisticSFService = new LogisticSFService(); $resultSF = []; $resultYD = []; $resultYT = []; $resultOther = []; foreach ($request as $key => $logisticNums) { switch ($key) { case "SF": $resultSF = $this->logisticSFService->get($logisticNums); break; case "YD": $resultYD = []; break; case "YT": $resultYT = []; break; default: $resultOther = []; break; } } return array_merge($resultSF, $resultYD, $resultYT, $resultOther); } /** * 根据快递单号更新状态 * @param array $orderPackages */ public function update(array $orderPackages) { foreach ($orderPackages as $data) { $orderPackage = OrderPackage::query()->where('logistic_number', $data['logistic_number'])->first(); if (isset($data['status'])) $orderPackage->status = $data['status']; if (isset($data['received_at'])) $orderPackage->received_at = $data['received_at']; if (isset($data['exception'])) $orderPackage->exception = $data['exception']; if (isset($data['transfer_status'])) $orderPackage->transfer_status = $data['transfer_status']; if (isset($data['exception_type'])) $orderPackage->exception_type = $data['exception_type']; $orderPackage->save(); } } /** * 将orderPackage集合分类并摘取指定数据 * @param Collection $orderPackages * @return array */ private function buildData(Collection $orderPackages): array { $data = []; foreach ($orderPackages as $orderPackage) { try { $logisticCode = $orderPackage->order->logistic->code; } catch (Exception $e) { continue; } $key = config('api_logistic.logistic.' . $logisticCode); if (!isset($data[$key])) { $data[$key] = []; } $data[$key][] = $orderPackage->logistic_number; } return $data; } /** * @param array $data * @param $lastRouteDate * @return array */ public function setExceptionType(array $data, $lastRouteDate): 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; $HAVEN_SECOND_GOT_HOURS = 24; $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 派送异常 $IS_SECOND_ROUTE_HAVE = 256; //0100 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, $HAVEN_SECOND_GOT_HOURS, $IS_SECOND_ROUTE_HAVE, $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; $conclusion |= ($delivered_duration > $HAVEN_SECOND_GOT_HOURS && $data['routes_length'] < 2) ? $IS_SECOND_ROUTE_HAVE : 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'] = '派件异常'; } if ($conclusion == ($conclusion | $IS_SECOND_ROUTE_HAVE)) { $data['exception_type'] = '派件异常'; $data['exception'] = '是'; } 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 [ 'exception_type' => array_key_exists('exception_type', $data) ? $data['exception_type'] : null, 'exception' => array_key_exists('exception', $data) ? $data['exception'] : null, ]; } }