279 lines
8.8 KiB
PHP
279 lines
8.8 KiB
PHP
<?php
|
||
|
||
namespace app\common\library\app\physical\express\provider;
|
||
|
||
/**
|
||
* 阿里云物流查询驱动
|
||
*
|
||
* 服务文档:https://market.aliyun.com/detail/cmapi00071922
|
||
*
|
||
* 接口地址:https://swexquery.market.alicloudapi.com/query/expressquery
|
||
* 请求方式:POST
|
||
* 认证方式:APPCODE
|
||
*
|
||
* 配置信息:
|
||
* - AppKey:203944259
|
||
* - AppSecret:R8rx9QLNZlRKxHtvgjBlocFI8f0hq12J
|
||
* - AppCode:d6beec8b9be945bfa3c07b9c712f6bce
|
||
*/
|
||
class Aliyun extends Base
|
||
{
|
||
protected $config = [];
|
||
|
||
public function __construct($config = [])
|
||
{
|
||
parent::__construct();
|
||
$this->config = $config;
|
||
}
|
||
|
||
/**
|
||
* 查询物流信息
|
||
*
|
||
* @param array $data
|
||
* @param mixed $orderExpress
|
||
* @return array|null
|
||
*/
|
||
public function search(array $data, $orderExpress = null)
|
||
{
|
||
$host = "https://swexquery.market.alicloudapi.com";
|
||
$path = "/query/expressquery";
|
||
$method = "POST";
|
||
|
||
// 优先使用 AppCode,如果未配置则使用 AppKey 和 AppSecret 生成签名
|
||
$appcode = $this->config['appcode'] ?? '';
|
||
|
||
if (empty($appcode)) {
|
||
$appkey = $this->config['appkey'] ?? '';
|
||
$appsecret = $this->config['appsecret'] ?? '';
|
||
|
||
if (empty($appkey) || empty($appsecret)) {
|
||
throw new \Exception('阿里云AppCode或AppKey/AppSecret未配置');
|
||
}
|
||
|
||
// 使用 AppKey 和 AppSecret 生成 AppCode(参考阿里云文档)
|
||
$appcode = base64_encode($appkey . ':' . $appsecret);
|
||
}
|
||
|
||
$headers = [];
|
||
$headers[] = "Authorization:APPCODE " . $appcode;
|
||
$headers[] = "Content-Type:application/x-www-form-urlencoded; charset=UTF-8";
|
||
|
||
// 构建请求参数
|
||
$expressNumber = $data['express_no'] ?? '';
|
||
$expressType = $data['express_code'] ?? '';
|
||
$phoneNumber = $data['mobile'] ?? '';
|
||
|
||
$bodys = "express_number=" . urlencode($expressNumber);
|
||
// if (!empty($expressType)) {
|
||
// $bodys .= "&express_type=" . urlencode($expressType);
|
||
// }
|
||
if (!empty($phoneNumber)) {
|
||
$bodys .= "&phone_number=" . urlencode($phoneNumber);
|
||
}
|
||
|
||
|
||
$url = $host . $path;
|
||
|
||
$curl = curl_init();
|
||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
|
||
curl_setopt($curl, CURLOPT_URL, $url);
|
||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||
curl_setopt($curl, CURLOPT_FAILONERROR, false);
|
||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||
curl_setopt($curl, CURLOPT_HEADER, false);
|
||
|
||
if (1 == strpos("$" . $host, "https://")) {
|
||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||
}
|
||
|
||
|
||
curl_setopt($curl, CURLOPT_POSTFIELDS, $bodys);
|
||
$response = curl_exec($curl);
|
||
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||
curl_close($curl);
|
||
|
||
if ($httpCode != 200 || empty($response)) {
|
||
throw new \Exception('阿里云物流查询接口请求失败');
|
||
}
|
||
|
||
|
||
$result = json_decode($response, true);
|
||
|
||
if (empty($result)) {
|
||
throw new \Exception('阿里云物流查询接口返回数据解析失败');
|
||
}
|
||
|
||
// 解析阿里云返回的数据
|
||
$expressData = $this->parseResponse($result);
|
||
|
||
if ($orderExpress) {
|
||
$this->updateExpress($expressData, $orderExpress);
|
||
}
|
||
|
||
return $expressData;
|
||
}
|
||
|
||
/**
|
||
* 解析阿里云返回的数据
|
||
*
|
||
* @param array $result
|
||
* @return array
|
||
*/
|
||
protected function parseResponse($result)
|
||
{
|
||
// 阿里云返回格式参考
|
||
// 成功格式:
|
||
// {
|
||
// "charge": 1,
|
||
// "result": {
|
||
// "info": [
|
||
// {
|
||
// "cpCode": "STO",
|
||
// "theLastTime": "2025-09-08 18:55:18",
|
||
// "mailNo": "772****722",
|
||
// "theLastMessage": "已签收,签收人凭取货码签收",
|
||
// "cpMobile": "95543",
|
||
// "logisticsCompanyName": "申通快递",
|
||
// "cpUrl": "http://www.sto.cn",
|
||
// "takeTime": "1天18小时7分",
|
||
// "courier": "**路北**14-8店",
|
||
// "courierPhone": "135**3135",
|
||
// "logisticsStatusDesc": "已签收",
|
||
// "logisticsTraceDetailList": [
|
||
// {
|
||
// "areaCode": "CN110100000000",
|
||
// "areaName": "北京,北京市",
|
||
// "subLogisticsStatus": "ACCEPT",
|
||
// "time": "1773037503000",
|
||
// "logisticsStatus": "ACCEPT",
|
||
// "desc": "顺丰速运 已收取快件,您的期待,我们定竭诚守护,不负所托。"
|
||
// }
|
||
// ],
|
||
// "logisticsStatus": "SIGN"
|
||
// }
|
||
// ]
|
||
// },
|
||
// "result_code": "0000",
|
||
// "result_msg": "调用成功",
|
||
// "request_id": "TID762ff34c093040b69e4c04b5f01a560a"
|
||
// }
|
||
//
|
||
// 失败格式:
|
||
// {
|
||
// "charge": 2,
|
||
// "result_code": "2102",
|
||
// "result_msg": "请求参数错误",
|
||
// "request_id": "TID7b21ec5e862d445fac39b0af4c36008d"
|
||
// }
|
||
|
||
// 检查错误格式
|
||
if (isset($result['result_code'])) {
|
||
if ($result['result_code'] != '0000') {
|
||
$msg = $result['result_msg'] ?? '查询失败';
|
||
throw new \Exception('阿里云物流查询:' . $msg);
|
||
}
|
||
}
|
||
|
||
// 获取物流信息
|
||
$infoList = $result['result']['info'] ?? [];
|
||
if (empty($infoList)) {
|
||
throw new \Exception('阿里云物流查询:未找到物流信息');
|
||
}
|
||
|
||
$info = $infoList[0];
|
||
|
||
// 转换物流状态
|
||
$status = $this->convertStatus($info['logisticsStatus'] ?? '');
|
||
|
||
// 转换物流轨迹
|
||
$traces = [];
|
||
$traceList = $info['logisticsTraceDetailList'] ?? [];
|
||
if (is_array($traceList)) {
|
||
foreach ($traceList as $trace) {
|
||
$timestamp = $trace['time'] ?? '';
|
||
$changeDate = '';
|
||
if (!empty($timestamp)) {
|
||
// 时间戳转换为日期格式
|
||
if (strlen($timestamp) == 13) {
|
||
$changeDate = date('Y-m-d H:i:s', $timestamp / 1000);
|
||
} else {
|
||
$changeDate = date('Y-m-d H:i:s', $timestamp);
|
||
}
|
||
}
|
||
|
||
$traces[] = [
|
||
'content' => $trace['desc'] ?? $trace['content'] ?? '',
|
||
'change_date' => $changeDate ?: date('Y-m-d H:i:s'),
|
||
'status' => $status
|
||
];
|
||
}
|
||
}
|
||
|
||
return [
|
||
'status' => $status,
|
||
'traces' => $traces
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 转换物流状态
|
||
*
|
||
* @param string $status
|
||
* @return string
|
||
*/
|
||
protected function convertStatus($status)
|
||
{
|
||
// 阿里云状态映射到系统状态
|
||
$statusMap = [
|
||
'NOINFO' => 'noinfo', // 无信息
|
||
'ACCEPT' => 'collect', // 已揽件
|
||
'TRANSPORT' => 'transport', // 运输中
|
||
'DELIVERY' => 'delivery', // 派送中
|
||
'SIGN' => 'signfor', // 已签收
|
||
'REFUSE' => 'refuse', // 拒收
|
||
'DIFFICULTY' => 'difficulty', // 问题件
|
||
'INVALID' => 'invalid', // 无效件
|
||
'TIMEOUT' => 'timeout', // 超时
|
||
'FAIL' => 'fail', // 签收失败
|
||
'BACK' => 'back' // 退回
|
||
];
|
||
|
||
return $statusMap[$status] ?? 'noinfo';
|
||
}
|
||
|
||
/**
|
||
* 订阅物流信息推送
|
||
*
|
||
* @param array $data
|
||
* @return void
|
||
*/
|
||
public function subscribe(array $data)
|
||
{
|
||
throw new \Exception('阿里云物流查询不支持订阅功能');
|
||
}
|
||
|
||
/**
|
||
* 接收物流推送
|
||
*
|
||
* @param array $data
|
||
* @return void
|
||
*/
|
||
public function push(array $data)
|
||
{
|
||
throw new \Exception('阿里云物流查询不支持推送功能');
|
||
}
|
||
|
||
/**
|
||
* 电子面单
|
||
*
|
||
* @param array $data
|
||
* @param array $items
|
||
* @return void
|
||
*/
|
||
public function eOrder(array $data, $items)
|
||
{
|
||
throw new \Exception('阿里云物流查询不支持电子面单功能');
|
||
}
|
||
}
|