更新
This commit is contained in:
@@ -39,6 +39,7 @@ class ExpressAutoUpdate extends Command
|
|||||||
$result = ExpressTrackingService::autoUpdateBatch(1000);
|
$result = ExpressTrackingService::autoUpdateBatch(1000);
|
||||||
$recon = ExpressTrackingService::reconcileAssistantReleaseForShippedPrescriptionOrders(1000);
|
$recon = ExpressTrackingService::reconcileAssistantReleaseForShippedPrescriptionOrders(1000);
|
||||||
|
|
||||||
|
|
||||||
$duration = round(microtime(true) - $startTime, 2);
|
$duration = round(microtime(true) - $startTime, 2);
|
||||||
|
|
||||||
$output->writeln("更新完成!");
|
$output->writeln("更新完成!");
|
||||||
|
|||||||
@@ -63,29 +63,94 @@ class ExpressTrackService
|
|||||||
|
|
||||||
$cfg = Config::get('logistics.kuaidi100', []);
|
$cfg = Config::get('logistics.kuaidi100', []);
|
||||||
$enable = !empty($cfg['enable']);
|
$enable = !empty($cfg['enable']);
|
||||||
|
|
||||||
|
$result = $out;
|
||||||
if (! $enable) {
|
if (! $enable) {
|
||||||
$out['hint'] = '未配置快递100查询密钥或已关闭(LOGISTICS_KUAIDI100_DISABLE),仅可打开官网查件。请在 .env 中配置 LOGISTICS_KUAIDI100_CUSTOMER、LOGISTICS_KUAIDI100_KEY';
|
$result['hint'] = '未配置快递100查询密钥或已关闭(LOGISTICS_KUAIDI100_DISABLE),仅可打开官网查件。请在 .env 中配置 LOGISTICS_KUAIDI100_CUSTOMER、LOGISTICS_KUAIDI100_KEY';
|
||||||
|
} elseif ($phoneForKuaidi === '' && self::kuaidiPhoneRequired($kuaidiCom)) {
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 顺丰(及快递100 要求电话的承运商)无 phone 时不请求接口,避免无效调用
|
// 顺丰(及快递100 要求电话的承运商)无 phone 时不请求接口,避免无效调用
|
||||||
if ($phoneForKuaidi === '' && self::kuaidiPhoneRequired($kuaidiCom)) {
|
$result['hint'] = '顺丰查询需在快递100 中同时提交单号与收/寄件人电话(可与面单一致的完整手机号或后四位)。请填写收件电话后点「刷新轨迹」。';
|
||||||
$out['hint'] = '顺丰查询需在快递100 中同时提交单号与收/寄件人电话(可与面单一致的完整手机号或后四位)。请填写收件电话后点「刷新轨迹」。';
|
} else {
|
||||||
|
$matched = null;
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
$lastFail = null;
|
$lastFail = null;
|
||||||
foreach ($comCandidates as $tryCom) {
|
foreach ($comCandidates as $tryCom) {
|
||||||
$tryOut = self::queryKuaidiOnce($cfg, $tryCom, $num, $phoneForKuaidi, $carrier, $label);
|
$tryOut = self::queryKuaidiOnce($cfg, $tryCom, $num, $phoneForKuaidi, $carrier, $label);
|
||||||
if (!empty($tryOut['traces']) || ($tryOut['state'] ?? '') !== '') {
|
if (!empty($tryOut['traces']) || ($tryOut['state'] ?? '') !== '') {
|
||||||
return $tryOut;
|
$matched = $tryOut;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$lastFail = $tryOut;
|
$lastFail = $tryOut;
|
||||||
}
|
}
|
||||||
|
$result = $matched ?? $lastFail ?? $out;
|
||||||
|
}
|
||||||
|
|
||||||
return $lastFail ?? $out;
|
// 京东自营单(JDVE…)兜底:快递100 无轨迹/陈旧时,用京东官方接口(更新或更全才采用)。
|
||||||
|
// 未配置京东官方接口时 isConfigured()=false,本段跳过,行为与原先一致。
|
||||||
|
if ($carrier === 'jd' && JdLogisticsService::isConfigured()) {
|
||||||
|
try {
|
||||||
|
$jd = JdLogisticsService::queryTrace($num);
|
||||||
|
if ($jd !== null && !empty($jd['traces']) && self::jdResultPreferred($jd, $result)) {
|
||||||
|
$result['traces'] = $jd['traces'];
|
||||||
|
$result['state'] = (string) $jd['state'];
|
||||||
|
$result['state_text'] = (string) $jd['state_text'];
|
||||||
|
$result['source'] = 'jd_official';
|
||||||
|
$result['hint'] = '';
|
||||||
|
// carrier / carrier_label / official_url / kuaidi_com 保留原值
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::warning('ExpressTrackService jd official fallback failed', [
|
||||||
|
'num' => $num,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 京东官方轨迹是否应优先于快递100 结果采用:
|
||||||
|
* 快递100 无轨迹 → 直接用;否则京东更「新」(最新轨迹时间更晚)或同样新但条目更多 → 用。
|
||||||
|
*
|
||||||
|
* @param array{traces?:array,newest_unix?:int} $jd
|
||||||
|
* @param array{traces?:array} $kuaidi
|
||||||
|
*/
|
||||||
|
private static function jdResultPreferred(array $jd, array $kuaidi): bool
|
||||||
|
{
|
||||||
|
$kuaidiTraces = is_array($kuaidi['traces'] ?? null) ? $kuaidi['traces'] : [];
|
||||||
|
if ($kuaidiTraces === []) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$jdNewest = (int) ($jd['newest_unix'] ?? 0);
|
||||||
|
$kuaidiNewest = self::newestUnixFromTraces($kuaidiTraces);
|
||||||
|
if ($jdNewest > $kuaidiNewest) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ($jdNewest === $kuaidiNewest && $jdNewest > 0) {
|
||||||
|
return count($jd['traces'] ?? []) > count($kuaidiTraces);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, array{time?:string}> $traces
|
||||||
|
*/
|
||||||
|
private static function newestUnixFromTraces(array $traces): int
|
||||||
|
{
|
||||||
|
$best = 0;
|
||||||
|
foreach ($traces as $t) {
|
||||||
|
if (!is_array($t)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$p = strtotime((string) ($t['time'] ?? ''));
|
||||||
|
if ($p !== false && (int) $p > $best) {
|
||||||
|
$best = (int) $p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $best;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,6 +41,32 @@ $forceDisable = filter_var($forceDisableRaw, FILTER_VALIDATE_BOOLEAN);
|
|||||||
// 已填 CUSTOMER+KEY 即启用;需临时关闭时设 LOGISTICS_KUAIDI100_DISABLE=true(LOGISTICS_KUAIDI100_ENABLE 已废弃,可删)
|
// 已填 CUSTOMER+KEY 即启用;需临时关闭时设 LOGISTICS_KUAIDI100_DISABLE=true(LOGISTICS_KUAIDI100_ENABLE 已废弃,可删)
|
||||||
$kuaidiEnabled = ! $forceDisable && $hasCreds;
|
$kuaidiEnabled = ! $forceDisable && $hasCreds;
|
||||||
|
|
||||||
|
// 京东官方物流接口(JOS 开放平台 https://api.jd.com/routerjson)
|
||||||
|
// 仅当填好 APP_KEY / APP_SECRET / ACCESS_TOKEN 时启用;作为快递100 对京东自营单(JDVE…)轨迹陈旧时的兜底数据源。
|
||||||
|
// 未配置时本段 enable=false,ExpressTrackService 完全沿用现有快递100 逻辑,互不影响。
|
||||||
|
$envJd = static function (string $suffix, $default = '') use ($stripQuotes) {
|
||||||
|
$top = 'JD_LOGISTICS_' . $suffix;
|
||||||
|
$v = env($top, null);
|
||||||
|
if ($v !== null && $v !== '') {
|
||||||
|
return $stripQuotes((string) $v);
|
||||||
|
}
|
||||||
|
// 兼容误写进 [trtc] 等分区的情况
|
||||||
|
$nested = env('trtc.' . $top, $default);
|
||||||
|
|
||||||
|
return $stripQuotes((string) $nested);
|
||||||
|
};
|
||||||
|
|
||||||
|
$jdAppKey = $envJd('APP_KEY', '');
|
||||||
|
$jdAppSecret = $envJd('APP_SECRET', '');
|
||||||
|
$jdAccessToken = $envJd('ACCESS_TOKEN', '');
|
||||||
|
$jdHasCreds = $jdAppKey !== '' && $jdAppSecret !== '' && $jdAccessToken !== '';
|
||||||
|
$jdDisableRaw = env('JD_LOGISTICS_DISABLE', null);
|
||||||
|
if ($jdDisableRaw === null || $jdDisableRaw === '') {
|
||||||
|
$jdDisableRaw = env('trtc.JD_LOGISTICS_DISABLE', false);
|
||||||
|
}
|
||||||
|
$jdDisabled = filter_var($jdDisableRaw, FILTER_VALIDATE_BOOLEAN);
|
||||||
|
$jdEnabled = ! $jdDisabled && $jdHasCreds;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'kuaidi100' => [
|
'kuaidi100' => [
|
||||||
'enable' => $kuaidiEnabled,
|
'enable' => $kuaidiEnabled,
|
||||||
@@ -48,4 +74,18 @@ return [
|
|||||||
'key' => $key,
|
'key' => $key,
|
||||||
'query_url' => $envKuaidi('QUERY_URL', 'https://poll.kuaidi100.com/poll/query.do'),
|
'query_url' => $envKuaidi('QUERY_URL', 'https://poll.kuaidi100.com/poll/query.do'),
|
||||||
],
|
],
|
||||||
|
'jd' => [
|
||||||
|
'enable' => $jdEnabled,
|
||||||
|
'app_key' => $jdAppKey,
|
||||||
|
'app_secret' => $jdAppSecret,
|
||||||
|
'access_token' => $jdAccessToken,
|
||||||
|
'gateway' => $envJd('GATEWAY', 'https://api.jd.com/routerjson'),
|
||||||
|
// 青龙运单轨迹查询;如改用 ECLP 等可在 .env 覆盖 method / param_key / waybill_field
|
||||||
|
'method' => $envJd('METHOD', 'jingdong.ldop.receive.trace.get'),
|
||||||
|
'param_key' => $envJd('PARAM_KEY', '360buy_param_json'),
|
||||||
|
'waybill_field' => $envJd('WAYBILL_FIELD', 'waybillCode'),
|
||||||
|
// 部分接口需商家编码 / 事业部编码,可选
|
||||||
|
'customer_code' => $envJd('CUSTOMER_CODE', ''),
|
||||||
|
'api_version' => $envJd('API_VERSION', '2.0'),
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user