'mailno', 'transfer_status' => 'remark', 'received_at' => 'accept_time', ]; /** * 获取顺丰快递揽收数据 * @param array $logisticNums [logisticNums]快递单号数组 * @return array 快递揽收信息数组 * @throws Exception 未知的丰桥opCode */ public function get(array $logisticNums): array { // 将$logisticNums以10个位单位进行分割,返回二维数组 $logisticNums_size_10 = array_chunk($logisticNums, config('api_logistic.SF.max_size', 10)); // 遍历二维数组批量查询顺丰接口 //将查询到的结果整合到一起,更新order_packages.received_at $result = []; foreach ($logisticNums_size_10 as $numbers) { $numbersStr = implode(',', $numbers); $result_10 = $this->getResultFromSF(config('api_logistic.SF.head'), $numbersStr, config('api_logistic.SF.check_word'), config('api_logistic.SF.url')); $result = array_merge($result, $result_10); } return $result; } /** * @param string $head 客户号 * @param string $numbers 快递单号字符串,多个以','分隔 * @param string $checkWord 客户秘钥 * @param string $url 顺丰接口地址 * @return array * @throws Exception 未知的丰桥opCode */ public function getResultFromSF(string $head, string $numbers, string $checkWord, string $url): array { try { $responseBody = get_object_vars(simplexml_load_string($this->sendHttpToSF($head, $numbers, $checkWord, $url))->Body)['RouteResponse']; } catch (Exception $e) {//index RouteResponse 异常处理 return []; } $result = []; if (is_array($responseBody)) {//SF返回多个单号的查询结果 $result = $this->transformSFMoreToArr($responseBody, $result); } else { $result[] = $this->transformSFOneToArr(get_object_vars($responseBody), []); } return $result; } /** * 构建顺丰xml请求体 * @param string $head * @param string $number * @return string */ private function buildXmlStr(string $head, string $number): string { return << $head xml; } /** * 将单个单号的顺丰数据转换为数组 * @param array $routeResponse * @param array $data * @return array * @throws Exception */ public function transformSFOneToArr(array $routeResponse, array $data): array { $data['logistic_number'] = $routeResponse['@attributes'][$this->protected_switch['logistic_number']]; try { $lastRoute = get_object_vars($routeResponse['Route'][count($routeResponse['Route']) - 1])['@attributes'];//获取最新的路由信息 $data = $this->switchOpCodeToStatus($lastRoute, $data); $data['transfer_status'] = $this->transformRoutes($routeResponse['Route']); } catch (Exception $e) { // throw new WarningException("单号没有查询到快递路由信息','LogisticSFService->transformSFOneToArr->{$data['logistic_number']}"); } finally { $data['routes_length'] = array_key_exists('transfer_status', $data) ? count($data['transfer_status']) : 0; return $data; } } /** * 转换快递路由信息 * @param $routs 快递路由 * @return array */ public function transformRoutes($routs): array { $result = []; if (!is_array($routs)) { $routs = [$routs]; } foreach ($routs as $route) { $route = get_object_vars($route)['@attributes']; $data['accept_time'] = $route['accept_time'] ?? ''; $data['accept_address'] = $route['accept_address'] ?? ''; $data['remark'] = $route['remark'] ?? ''; $result[] = $data; } return $result; } /** * 将最新路由信息转换为数组 * @param array $lastRoute * @param array $data * @return array * @throws Exception */ public function switchOpCodeToStatus(array $lastRoute, array $data): array { try { switch ($lastRoute['opcode']) { case 123: case 130: case 3036: case 31: case 30: case 36: case 106://航空板箱到达 $data['status'] = '在途'; break; case 204: case 44: $data['status'] = '派送中'; break; case 50: $data['status'] = '已揽件'; break; case 607: case 8000: case 125: case 80: $data['status'] = '已签收'; $data['received_at'] = $lastRoute[$this->protected_switch['received_at']]; break; case 70: $data['status'] = '其他'; break; default: $data['status'] = '其他'; throw new WarningException("未知的丰桥状态码: {$lastRoute['opcode']}->{json_encode($lastRoute)}"); } } catch (WarningException $e) { $data['status'] = '其他异常'; } finally { return $data; } } /** * @param string $head * @param string $numbers * @param string $checkWord * @param string $url * @return Response * @throws Exception */ public function sendHttpToSF(string $head, string $numbers, string $checkWord, string $url): Response { $xml = $this->buildXmlStr($head, $numbers); $checkingJson = $xml . $checkWord; $verifyCode = base64_encode(md5($checkingJson, true)); try { $response = Http::withHeaders(['Content-Type' => 'text/xml'])->get($url, ['xml' => $xml, 'verifyCode' => $verifyCode]); } catch (Exception $e) { // throw new WarningException("HTTP请求顺丰接口异常->{$e->getMessage()}"); } return $response; } /** * 将多个顺丰的单号转换为数组 * @param array $responseBody * @param array $result * @return array * @throws Exception */ public function transformSFMoreToArr(array $responseBody, array $result): array { foreach ($responseBody as $routeResponse) { $result[] = $this->transformSFOneToArr(get_object_vars($routeResponse), []); } return $result; } }