297 行
11 KiB
PHP
297 行
11 KiB
PHP
<?php
|
||
|
||
namespace addons\shopro_aliyun_express\library;
|
||
|
||
class TrackNormalizer
|
||
{
|
||
/**
|
||
* 将 Provider 原始响应归一化为统一 trackList
|
||
*/
|
||
public static function normalize(array $providerResult)
|
||
{
|
||
$provider = $providerResult['provider'] ?? '';
|
||
switch ($provider) {
|
||
case 'aliyun':
|
||
return self::fromAliyun($providerResult);
|
||
case 'kdniao':
|
||
case 'expressbird':
|
||
return self::fromKdniao($providerResult);
|
||
default:
|
||
return [
|
||
'success' => false,
|
||
'trackList' => [],
|
||
'expressStatus' => '',
|
||
'expressCompanyName' => '',
|
||
'expressCompanyCode' => '',
|
||
];
|
||
}
|
||
}
|
||
|
||
protected static function fromAliyun(array $providerResult)
|
||
{
|
||
$root = $providerResult['raw'] ?? ($providerResult['data'] ?? []);
|
||
|
||
// kzexpress:logisticsTraceDetailList / logisticsTraceDetails
|
||
$dataBlock = $root['data'] ?? $root;
|
||
$traceDetails = self::pickKzexpressTraceList($dataBlock);
|
||
if (!empty($traceDetails)) {
|
||
return self::fromKzexpressTrace($providerResult, $dataBlock, $traceDetails);
|
||
}
|
||
|
||
// 文档格式:data.logisticsEvents
|
||
if (!empty($root['data']['logisticsEvents'])) {
|
||
return self::fromExprLogisticsEvents($providerResult, $root['data']);
|
||
}
|
||
|
||
// wuliu.market alicloudapi.com/kdi
|
||
if (!empty($root['result']['list'])) {
|
||
return self::fromWuliuResult($providerResult, $root['result'], $root);
|
||
}
|
||
|
||
// expQuery 等:data[] 含 context/time
|
||
if (!empty($root['data']) && is_array($root['data']) && isset($root['data'][0]['context'])) {
|
||
return self::fromExpQueryData($providerResult, $root);
|
||
}
|
||
|
||
$data = $root['data'] ?? $root;
|
||
if (!empty($data['logisticsEvents'])) {
|
||
return self::fromExprLogisticsEvents($providerResult, $data);
|
||
}
|
||
|
||
return [
|
||
'success' => !empty($providerResult['success']),
|
||
'trackList' => [],
|
||
'expressStatus' => is_string($data['status'] ?? null) ? $data['status'] : '',
|
||
'expressCompanyName' => $data['expressCompanyName'] ?? ($root['expTextName'] ?? ''),
|
||
'expressCompanyCode' => $data['expressCompany'] ?? ($root['expSpellName'] ?? ''),
|
||
'lastDescription' => $root['msg'] ?? '',
|
||
'lastTime' => '',
|
||
];
|
||
}
|
||
|
||
protected static function pickKzexpressTraceList(array $data)
|
||
{
|
||
foreach (['logisticsTraceDetailList', 'logisticsTraceDetails', 'logisticsTraceDetail'] as $key) {
|
||
if (!empty($data[$key]) && is_array($data[$key])) {
|
||
return $data[$key];
|
||
}
|
||
}
|
||
return [];
|
||
}
|
||
|
||
protected static function fromKzexpressTrace(array $providerResult, array $data, array $traceDetails)
|
||
{
|
||
$trackList = [];
|
||
foreach ($traceDetails as $item) {
|
||
$time = $item['timeDesc'] ?? '';
|
||
if ($time === '' && !empty($item['time']) && is_numeric($item['time'])) {
|
||
$ts = (string) $item['time'];
|
||
if (strlen($ts) >= 13) {
|
||
$time = date('Y-m-d H:i:s', (int) floor($item['time'] / 1000));
|
||
} elseif (strlen($ts) === 10) {
|
||
$time = date('Y-m-d H:i:s', (int) $item['time']);
|
||
}
|
||
}
|
||
|
||
$desc = trim((string) ($item['desc'] ?? ($item['description'] ?? '')));
|
||
if ($desc === '') {
|
||
$area = trim((string) ($item['areaName'] ?? ($item['location'] ?? '')));
|
||
$statusText = trim((string) ($item['subLogisticsStatus'] ?? ($item['logisticsStatus'] ?? '')));
|
||
$desc = trim($area . ($statusText !== '' ? ' ' . $statusText : ''));
|
||
}
|
||
|
||
$trackList[] = [
|
||
'AcceptTime' => $time,
|
||
'AcceptStation' => $desc,
|
||
'Location' => $item['areaName'] ?? ($item['city'] ?? ''),
|
||
'Action' => self::mapAliyunStateToAction($item['logisticsStatus'] ?? ($item['subLogisticsStatus'] ?? '')),
|
||
];
|
||
}
|
||
|
||
usort($trackList, function ($a, $b) {
|
||
return strcmp($b['AcceptTime'], $a['AcceptTime']);
|
||
});
|
||
|
||
return [
|
||
'success' => !empty($providerResult['success']),
|
||
'trackList' => $trackList,
|
||
'expressStatus' => $data['logisticsStatusDesc'] ?? ($data['logisticsStatus'] ?? ''),
|
||
'expressCompanyName' => $data['logisticsCompanyName'] ?? ($data['cpName'] ?? ''),
|
||
'expressCompanyCode' => $data['cpCode'] ?? '',
|
||
'expressNo' => $data['mailNo'] ?? ($data['expressNo'] ?? ''),
|
||
'lastDescription' => $data['theLastMessage'] ?? (!empty($trackList) ? ($trackList[0]['AcceptStation'] ?? '') : ''),
|
||
'lastTime' => $data['theLastTime'] ?? (!empty($trackList) ? ($trackList[0]['AcceptTime'] ?? '') : ''),
|
||
];
|
||
}
|
||
|
||
protected static function fromExprLogisticsEvents(array $providerResult, array $data)
|
||
{
|
||
$trackList = [];
|
||
foreach ($data['logisticsEvents'] ?? [] as $event) {
|
||
$trackList[] = [
|
||
'AcceptTime' => $event['date'] ?? '',
|
||
'AcceptStation' => $event['description'] ?? '',
|
||
'Location' => $event['location'] ?? ($event['city'] ?? ''),
|
||
'Action' => self::mapAliyunStateToAction($event['state'] ?? ''),
|
||
];
|
||
}
|
||
usort($trackList, function ($a, $b) {
|
||
return strcmp($b['AcceptTime'], $a['AcceptTime']);
|
||
});
|
||
|
||
return [
|
||
'success' => !empty($providerResult['success']),
|
||
'trackList' => $trackList,
|
||
'expressStatus' => $data['status'] ?? '',
|
||
'expressCompanyName' => $data['expressCompanyName'] ?? '',
|
||
'expressCompanyCode' => $data['expressCompany'] ?? '',
|
||
'lastDescription' => $data['lastDescription'] ?? '',
|
||
'lastTime' => $data['lastTime'] ?? '',
|
||
];
|
||
}
|
||
|
||
protected static function fromWuliuResult(array $providerResult, array $result, array $root)
|
||
{
|
||
$trackList = [];
|
||
foreach ($result['list'] ?? [] as $item) {
|
||
$trackList[] = [
|
||
'AcceptTime' => $item['time'] ?? ($item['ftime'] ?? ''),
|
||
'AcceptStation' => $item['status'] ?? ($item['context'] ?? ''),
|
||
'Location' => $item['location'] ?? '',
|
||
'Action' => self::mapAliyunStateToAction($item['status'] ?? ''),
|
||
];
|
||
}
|
||
|
||
return [
|
||
'success' => !empty($providerResult['success']),
|
||
'trackList' => $trackList,
|
||
'expressStatus' => $result['deliverystatus'] ?? ($root['msg'] ?? ''),
|
||
'expressCompanyName' => $result['expName'] ?? ($result['type'] ?? ''),
|
||
'expressCompanyCode' => $result['type'] ?? '',
|
||
'lastDescription' => !empty($trackList) ? ($trackList[0]['AcceptStation'] ?? '') : '',
|
||
'lastTime' => !empty($trackList) ? ($trackList[0]['AcceptTime'] ?? '') : '',
|
||
];
|
||
}
|
||
|
||
protected static function fromExpQueryData(array $providerResult, array $root)
|
||
{
|
||
$trackList = [];
|
||
foreach ($root['data'] as $item) {
|
||
$trackList[] = [
|
||
'AcceptTime' => $item['time'] ?? '',
|
||
'AcceptStation' => $item['context'] ?? '',
|
||
'Location' => $item['location'] ?? '',
|
||
'Action' => self::mapAliyunStateToAction($item['status'] ?? ''),
|
||
];
|
||
}
|
||
|
||
return [
|
||
'success' => !empty($providerResult['success']),
|
||
'trackList' => $trackList,
|
||
'expressStatus' => $root['updateStr'] ?? ($root['msg'] ?? ''),
|
||
'expressCompanyName' => $root['expTextName'] ?? '',
|
||
'expressCompanyCode' => $root['expSpellName'] ?? '',
|
||
'lastDescription' => !empty($trackList) ? ($trackList[0]['AcceptStation'] ?? '') : '',
|
||
'lastTime' => !empty($trackList) ? ($trackList[0]['AcceptTime'] ?? '') : '',
|
||
];
|
||
}
|
||
|
||
protected static function fromKdniao(array $providerResult)
|
||
{
|
||
$data = $providerResult['data'] ?? [];
|
||
$traces = $data['Traces'] ?? [];
|
||
$trackList = [];
|
||
|
||
foreach ($traces as $trace) {
|
||
$trackList[] = [
|
||
'AcceptTime' => $trace['AcceptTime'] ?? '',
|
||
'AcceptStation' => $trace['AcceptStation'] ?? '',
|
||
'Location' => $trace['Location'] ?? '',
|
||
'Action' => $trace['Action'] ?? ($data['State'] ?? 0),
|
||
];
|
||
}
|
||
|
||
$statusMap = [
|
||
'0' => '暂无轨迹',
|
||
'1' => '已揽收',
|
||
'2' => '运输中',
|
||
'3' => '已签收',
|
||
'4' => '问题件',
|
||
];
|
||
$state = (string) ($data['State'] ?? '');
|
||
|
||
return [
|
||
'success' => !empty($providerResult['success']),
|
||
'trackList' => $trackList,
|
||
'expressStatus' => $statusMap[$state] ?? '',
|
||
'expressCompanyName' => $data['ShipperCode'] ?? '',
|
||
'expressCompanyCode' => $data['ShipperCode'] ?? '',
|
||
'lastDescription' => !empty($trackList) ? ($trackList[0]['AcceptStation'] ?? '') : '',
|
||
'lastTime' => !empty($trackList) ? ($trackList[0]['AcceptTime'] ?? '') : '',
|
||
];
|
||
}
|
||
|
||
public static function mapAliyunStateToAction($state)
|
||
{
|
||
$state = trim((string) $state);
|
||
$map = [
|
||
'已揽收' => 1,
|
||
'收件扫描' => 1,
|
||
'ACCEPT' => 1,
|
||
'运送中' => 2,
|
||
'运输中' => 2,
|
||
'转运中' => 2,
|
||
'入中转' => 2,
|
||
'出中转' => 2,
|
||
'TRANSPORT'=> 2,
|
||
'派送中' => 202,
|
||
'待派送' => 202,
|
||
'DELIVERING' => 202,
|
||
'已签收' => 3,
|
||
'SIGN' => 3,
|
||
'STA_SIGN' => 3,
|
||
'AGENT_SIGN' => 304,
|
||
'第三方代收' => 304,
|
||
'AGENT_SIGN' => 304,
|
||
'问题件' => 4,
|
||
'拒收' => 4,
|
||
'退回件' => 4,
|
||
'FAILED' => 4,
|
||
];
|
||
|
||
if (isset($map[$state])) {
|
||
return $map[$state];
|
||
}
|
||
|
||
foreach ($map as $keyword => $action) {
|
||
if ($state !== '' && mb_strpos($state, $keyword) !== false) {
|
||
return $action;
|
||
}
|
||
}
|
||
|
||
return 2;
|
||
}
|
||
|
||
/**
|
||
* 修正派件图标(与原 OrderExpress::detail 逻辑一致)
|
||
*/
|
||
public static function fixDeliveringIcons(array $trackList)
|
||
{
|
||
if (empty($trackList)) {
|
||
return $trackList;
|
||
}
|
||
|
||
foreach ($trackList as $k => &$v) {
|
||
if (($v['Action'] ?? null) == 202) {
|
||
if ($k >= 2) {
|
||
$trackList[$k - 2]['Action'] = 3;
|
||
$trackList[$k - 1]['Action'] = 4;
|
||
}
|
||
}
|
||
}
|
||
unset($v);
|
||
|
||
return $trackList;
|
||
}
|
||
}
|