feat: 初始化雄戈商城服务端代码与Webhook自动部署配置
这个提交包含在:
@@ -0,0 +1,296 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户