更新
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use app\common\service\gancao\GancaoLogisticsRouteService;
|
||||
use app\common\service\gancao\GancaoScmRecipelService;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
|
||||
/**
|
||||
* 同步甘草订单的物流路由信息到本地物流追踪表
|
||||
*
|
||||
* 数据流:
|
||||
* zyt_tcm_prescription_order (甘草已上传)
|
||||
* ↓ 调用 igc_scm.logistics.client_opt.pull / GET_TASK_ROUTE_LIST
|
||||
* zyt_express_tracking + zyt_express_trace + zyt_express_state_log + zyt_express_query_log
|
||||
*
|
||||
* 使用方法:
|
||||
* php think gancao:sync-logistics 默认拉 100 单
|
||||
* php think gancao:sync-logistics --limit=200 自定义拉取上限
|
||||
* php think gancao:sync-logistics --order-id=123 只跑指定订单
|
||||
* php think gancao:sync-logistics --detail 打印每单详细
|
||||
*
|
||||
* 建议 crontab(每 30 分钟执行一次):
|
||||
* 0,30 * * * * cd /path/to/server && php think gancao:sync-logistics --limit=200 >> runtime/log/gancao_sync.log 2>&1
|
||||
*/
|
||||
class GancaoSyncLogisticsRoute extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('gancao:sync-logistics')
|
||||
->setDescription('同步甘草订单的物流路由(GET_TASK_ROUTE_LIST)到本地物流追踪表')
|
||||
->addOption('limit', 'l', Option::VALUE_OPTIONAL, '本次最多处理多少条订单', 100)
|
||||
->addOption('order-id', null, Option::VALUE_OPTIONAL, '只同步指定 prescription_order.id', null)
|
||||
->addOption('detail', 'd', Option::VALUE_NONE, '打印每单详细结果');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$limit = max(1, (int) $input->getOption('limit'));
|
||||
$onlyOrderId = $input->getOption('order-id');
|
||||
$onlyOrderId = $onlyOrderId !== null ? (int) $onlyOrderId : null;
|
||||
$verbose = (bool) $input->getOption('detail');
|
||||
|
||||
$output->writeln('========================================');
|
||||
$output->writeln('甘草物流路由同步');
|
||||
$output->writeln('========================================');
|
||||
|
||||
if (!GancaoScmRecipelService::isConfigured()) {
|
||||
$output->error('甘草 SCM 未配置:' . GancaoScmRecipelService::whyNotConfigured());
|
||||
return 1;
|
||||
}
|
||||
|
||||
$start = microtime(true);
|
||||
$output->writeln('开始同步...(limit=' . $limit . ($onlyOrderId ? ', order_id=' . $onlyOrderId : '') . ')');
|
||||
|
||||
try {
|
||||
$stats = GancaoLogisticsRouteService::syncBatch($limit, $onlyOrderId);
|
||||
} catch (\Throwable $e) {
|
||||
$output->error('同步异常:' . $e->getMessage());
|
||||
return 1;
|
||||
}
|
||||
|
||||
$duration = round(microtime(true) - $start, 2);
|
||||
|
||||
if ($verbose && !empty($stats['details'])) {
|
||||
$output->writeln('');
|
||||
$output->writeln('--- 详细 ---');
|
||||
foreach ($stats['details'] as $row) {
|
||||
$tag = !empty($row['success']) ? '[OK]' : '[FAIL]';
|
||||
$line = sprintf(
|
||||
'%s order_id=%s order_no=%s app_order_no=%s tn=%s state=%s traces=+%s msg=%s',
|
||||
$tag,
|
||||
$row['order_id'] ?? '',
|
||||
$row['order_no'] ?? '',
|
||||
$row['app_order_no'] ?? '',
|
||||
$row['tracking_number'] ?? '',
|
||||
$row['state'] ?? '',
|
||||
$row['traces'] ?? 0,
|
||||
$row['message'] ?? ''
|
||||
);
|
||||
$output->writeln($line);
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln('');
|
||||
$output->writeln('========================================');
|
||||
$output->writeln('同步完成');
|
||||
$output->writeln('========================================');
|
||||
$output->writeln('总数:' . $stats['total']);
|
||||
$output->writeln('成功:' . $stats['success']);
|
||||
$output->writeln('失败:' . $stats['failed']);
|
||||
$output->writeln('耗时:' . $duration . 's');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\common\service\gancao;
|
||||
|
||||
use app\common\model\ExpressQueryLog;
|
||||
use app\common\model\ExpressStateLog;
|
||||
use app\common\model\ExpressTrace;
|
||||
use app\common\model\ExpressTracking;
|
||||
use app\common\model\tcm\PrescriptionOrder;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 甘草 SCM「获取物流路由信息」(GET_TASK_ROUTE_LIST)
|
||||
*
|
||||
* 入参:app_order_no = 甘草处方订单号 recipel_order_no(不是我方 order_no),get_all=1
|
||||
* 出参 result.route_list[] = [{accept_time, remark, route_type, route_type_name}]
|
||||
*
|
||||
* 路由状态(route_type):
|
||||
* 0 任务创建
|
||||
* 10 已下单(待揽件)
|
||||
* 11 已揽件
|
||||
* 12 转运中
|
||||
* 13 已送达
|
||||
* 20 取消订单
|
||||
* 21 派件异常
|
||||
*
|
||||
* @see https://apidoc.igancao.com/service-doc/scm-outer-recipel.html#%E8%8E%B7%E5%8F%96%E7%89%A9%E6%B5%81%E8%B7%AF%E7%94%B1%E4%BF%A1%E6%81%AF
|
||||
*/
|
||||
final class GancaoLogisticsRouteService
|
||||
{
|
||||
/**
|
||||
* 路由状态 → 系统物流状态(与 ExpressTracking 常量对齐)
|
||||
*/
|
||||
private const ROUTE_TYPE_TO_STATE = [
|
||||
0 => ExpressTracking::STATE_IN_TRANSIT, // 任务创建 → 在途
|
||||
10 => ExpressTracking::STATE_IN_TRANSIT, // 已下单待揽件 → 在途
|
||||
11 => ExpressTracking::STATE_COLLECTED, // 已揽件 → 揽收
|
||||
12 => ExpressTracking::STATE_IN_TRANSIT, // 转运中 → 在途
|
||||
13 => ExpressTracking::STATE_SIGNED, // 已送达 → 签收
|
||||
20 => ExpressTracking::STATE_RETURN_SIGNED, // 取消订单 → 退签
|
||||
21 => ExpressTracking::STATE_PROBLEM, // 派件异常 → 疑难
|
||||
];
|
||||
|
||||
private const ROUTE_TYPE_NAMES = [
|
||||
0 => '任务创建',
|
||||
10 => '已下单(待揽件)',
|
||||
11 => '已揽件',
|
||||
12 => '转运中',
|
||||
13 => '已送达',
|
||||
20 => '取消订单',
|
||||
21 => '派件异常',
|
||||
];
|
||||
|
||||
/**
|
||||
* 拉取并落库单个订单的物流路由
|
||||
*
|
||||
* @param PrescriptionOrder $order
|
||||
* @return array{success:bool, message:string, traces_count:int, state:string, raw?:array}
|
||||
*/
|
||||
public static function syncOne(PrescriptionOrder $order): array
|
||||
{
|
||||
$appOrderNo = trim((string) ($order->gancao_reciperl_order_no ?? ''));
|
||||
if ($appOrderNo === '') {
|
||||
return ['success' => false, 'message' => '订单未关联甘草处方单号', 'traces_count' => 0, 'state' => ''];
|
||||
}
|
||||
|
||||
$resp = self::callRouteApi($appOrderNo);
|
||||
if (!$resp['success']) {
|
||||
self::logQuery($order, '', 'auto', false, 0, $resp['message']);
|
||||
return ['success' => false, 'message' => $resp['message'], 'traces_count' => 0, 'state' => ''];
|
||||
}
|
||||
|
||||
$result = $resp['result'];
|
||||
$trackingNumber = trim((string) ($result['sp_order_no'] ?? ($order->tracking_number ?? '')));
|
||||
$spName = trim((string) ($result['sp_name'] ?? ($order->gancao_shipping_name ?? '')));
|
||||
$routeList = is_array($result['route_list'] ?? null) ? $result['route_list'] : [];
|
||||
|
||||
if ($trackingNumber === '') {
|
||||
self::logQuery($order, '', 'auto', false, $resp['runtime_ms'], '甘草未返回 sp_order_no(快递单号),可能尚未发货');
|
||||
return ['success' => false, 'message' => '尚未发货(无快递单号)', 'traces_count' => 0, 'state' => ''];
|
||||
}
|
||||
|
||||
$tracking = self::ensureTracking($order, $trackingNumber, $spName);
|
||||
|
||||
// 回写订单的快递单号 / 物流公司(如果之前空着)
|
||||
$orderDirty = false;
|
||||
if (trim((string) $order->tracking_number) === '') {
|
||||
$order->tracking_number = mb_substr($trackingNumber, 0, 80);
|
||||
$orderDirty = true;
|
||||
}
|
||||
if ($spName !== '' && trim((string) ($order->gancao_shipping_name ?? '')) === '') {
|
||||
$order->gancao_shipping_name = mb_substr($spName, 0, 50);
|
||||
$orderDirty = true;
|
||||
}
|
||||
if ($orderDirty) {
|
||||
try {
|
||||
$order->save();
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('Gancao route: backfill order failed: ' . $e->getMessage(), ['order_id' => $order->id]);
|
||||
}
|
||||
}
|
||||
|
||||
$tracesCount = self::saveRoutes($tracking, $routeList);
|
||||
$newState = self::resolveCurrentState($routeList);
|
||||
self::updateTrackingHead($tracking, $newState, $routeList, $result);
|
||||
|
||||
self::logQuery($order, $trackingNumber, 'auto', true, $resp['runtime_ms'], '', count($routeList));
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'ok',
|
||||
'traces_count' => $tracesCount,
|
||||
'state' => $newState,
|
||||
'raw' => $result,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用甘草 GET_TASK_ROUTE_LIST 接口
|
||||
*
|
||||
* @return array{success:bool, message:string, result?:array, runtime_ms:int}
|
||||
*/
|
||||
private static function callRouteApi(string $appOrderNo): array
|
||||
{
|
||||
$token = GancaoScmRecipelService::getToken();
|
||||
if ($token === null || $token === '') {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => '获取 token 失败:' . GancaoScmRecipelService::getLastGetTokenError(),
|
||||
'runtime_ms' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'token' => $token,
|
||||
'app_order_no' => $appOrderNo,
|
||||
'get_all' => 1,
|
||||
'package' => 'igc_scm.logistics.client_opt.pull',
|
||||
'class' => 'GET_TASK_ROUTE_LIST',
|
||||
];
|
||||
|
||||
$start = microtime(true);
|
||||
$ret = self::transport()->post($payload);
|
||||
$runtimeMs = (int) ((microtime(true) - $start) * 1000);
|
||||
|
||||
if ((int) ($ret['state'] ?? 0) !== 1) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => '通信失败:' . (string) ($ret['msg'] ?? ''),
|
||||
'runtime_ms' => $runtimeMs,
|
||||
];
|
||||
}
|
||||
$body = $ret['body'] ?? [];
|
||||
if (!GancaoScmRecipelService::isApiSuccess($body)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'API 错误:' . GancaoScmRecipelService::apiStatusMessage($body),
|
||||
'runtime_ms' => $runtimeMs,
|
||||
];
|
||||
}
|
||||
$result = is_array($body['result'] ?? null) ? $body['result'] : [];
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'ok',
|
||||
'result' => $result,
|
||||
'runtime_ms' => $runtimeMs,
|
||||
];
|
||||
}
|
||||
|
||||
private static function transport(): GancaoOpenApiTransport
|
||||
{
|
||||
$c = \think\facade\Config::get('gancao_scm', []);
|
||||
|
||||
return new GancaoOpenApiTransport(
|
||||
(string) $c['gateway_url'],
|
||||
(string) $c['gateway_ak'],
|
||||
(string) $c['gateway_sk']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保证 zyt_express_tracking 主记录存在
|
||||
*/
|
||||
private static function ensureTracking(PrescriptionOrder $order, string $trackingNumber, string $spName): ExpressTracking
|
||||
{
|
||||
$tracking = ExpressTracking::where('tracking_number', $trackingNumber)
|
||||
->whereNull('delete_time')
|
||||
->find();
|
||||
$now = time();
|
||||
|
||||
if (!$tracking) {
|
||||
$tracking = new ExpressTracking();
|
||||
$tracking->tracking_number = $trackingNumber;
|
||||
$tracking->create_time = $now;
|
||||
$tracking->next_update_time = $now;
|
||||
}
|
||||
|
||||
$tracking->order_id = (int) $order->id;
|
||||
$tracking->order_type = 'prescription';
|
||||
$tracking->express_company = self::expressCodeFromGancaoName($spName) ?: 'auto';
|
||||
$tracking->express_company_name = $spName !== '' ? mb_substr($spName, 0, 100) : (string) $tracking->express_company_name;
|
||||
$tracking->recipient_phone = (string) ($order->recipient_phone ?? '');
|
||||
$tracking->recipient_name = (string) ($order->recipient_name ?? '');
|
||||
$tracking->recipient_address = (string) ($order->shipping_address ?? '');
|
||||
$tracking->update_time = $now;
|
||||
$tracking->save();
|
||||
|
||||
return $tracking;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把甘草的 route_list 写入 zyt_express_trace(按 (tracking_id, trace_time, trace_context) 去重)
|
||||
*/
|
||||
private static function saveRoutes(ExpressTracking $tracking, array $routeList): int
|
||||
{
|
||||
$inserted = 0;
|
||||
foreach ($routeList as $route) {
|
||||
$time = trim((string) ($route['accept_time'] ?? ''));
|
||||
$remark = trim((string) ($route['remark'] ?? ''));
|
||||
if ($time === '' && $remark === '') {
|
||||
continue;
|
||||
}
|
||||
$routeType = (int) ($route['route_type'] ?? -1);
|
||||
$routeName = trim((string) ($route['route_type_name'] ?? '')) ?: (self::ROUTE_TYPE_NAMES[$routeType] ?? '');
|
||||
|
||||
$exists = ExpressTrace::where('tracking_id', $tracking->id)
|
||||
->where('trace_time', $time)
|
||||
->where('trace_context', $remark)
|
||||
->count();
|
||||
if ($exists > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$model = new ExpressTrace();
|
||||
$model->tracking_id = (int) $tracking->id;
|
||||
$model->tracking_number = (string) $tracking->tracking_number;
|
||||
$model->trace_time = mb_substr($time, 0, 50);
|
||||
$model->trace_time_stamp = $time !== '' ? (strtotime($time) ?: time()) : time();
|
||||
$model->trace_context = mb_substr($remark, 0, 1000);
|
||||
$model->status = mb_substr($routeName, 0, 50);
|
||||
$model->status_code = (string) $routeType;
|
||||
$model->location = '';
|
||||
$model->extra_data = json_encode($route, JSON_UNESCAPED_UNICODE);
|
||||
$model->create_time = time();
|
||||
$model->save();
|
||||
$inserted++;
|
||||
}
|
||||
return $inserted;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用最新的 route 更新 tracking 头部(current_state、最新轨迹、签收等)
|
||||
*
|
||||
* @param array<int, array<string,mixed>> $routeList 原始路由数组
|
||||
* @param array<string, mixed> $result 完整 result(含 t_status 等)
|
||||
*/
|
||||
private static function updateTrackingHead(ExpressTracking $tracking, string $newState, array $routeList, array $result): void
|
||||
{
|
||||
$now = time();
|
||||
$oldState = (string) $tracking->current_state;
|
||||
|
||||
$tracking->current_state = $newState !== '' ? $newState : (string) $tracking->current_state;
|
||||
$tracking->current_state_text = ExpressTracking::getStateText($tracking->current_state);
|
||||
$tracking->data_source = 'gancao';
|
||||
$tracking->last_query_time = $now;
|
||||
$tracking->query_count = (int) $tracking->query_count + 1;
|
||||
|
||||
$latest = self::pickLatestRoute($routeList);
|
||||
if ($latest !== null) {
|
||||
$tracking->latest_trace_time = mb_substr((string) ($latest['accept_time'] ?? ''), 0, 50);
|
||||
$tracking->latest_trace_context = mb_substr((string) ($latest['remark'] ?? ''), 0, 500);
|
||||
$tracking->latest_location = '';
|
||||
}
|
||||
|
||||
$tracking->route_info = json_encode($result, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
if (ExpressTracking::isFinalState($tracking->current_state)) {
|
||||
$tracking->is_signed = 1;
|
||||
$tracking->sign_time = $latest && !empty($latest['accept_time']) ? (strtotime((string) $latest['accept_time']) ?: $now) : $now;
|
||||
$tracking->auto_update = 0;
|
||||
}
|
||||
|
||||
if ((int) $tracking->auto_update === 1 && !ExpressTracking::isFinalState($tracking->current_state)) {
|
||||
$interval = (int) ($tracking->update_interval ?: 1800);
|
||||
$tracking->next_update_time = $now + $interval;
|
||||
}
|
||||
|
||||
$tracking->update_time = $now;
|
||||
$tracking->save();
|
||||
|
||||
if ($oldState !== '' && $oldState !== $tracking->current_state) {
|
||||
$log = new ExpressStateLog();
|
||||
$log->tracking_id = (int) $tracking->id;
|
||||
$log->tracking_number = (string) $tracking->tracking_number;
|
||||
$log->old_state = $oldState;
|
||||
$log->old_state_text = ExpressTracking::getStateText($oldState);
|
||||
$log->new_state = (string) $tracking->current_state;
|
||||
$log->new_state_text = (string) $tracking->current_state_text;
|
||||
$log->change_time = $now;
|
||||
$log->change_reason = (string) $tracking->latest_trace_context;
|
||||
$log->create_time = $now;
|
||||
try {
|
||||
$log->save();
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('Gancao route: state log save failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取 route_list 中时间最新的一条
|
||||
*
|
||||
* @param array<int, array<string,mixed>> $routeList
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private static function pickLatestRoute(array $routeList): ?array
|
||||
{
|
||||
if (empty($routeList)) {
|
||||
return null;
|
||||
}
|
||||
usort($routeList, function ($a, $b) {
|
||||
$ta = strtotime((string) ($a['accept_time'] ?? '')) ?: 0;
|
||||
$tb = strtotime((string) ($b['accept_time'] ?? '')) ?: 0;
|
||||
return $tb <=> $ta;
|
||||
});
|
||||
return $routeList[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据所有路由确定当前最终状态:取出现过的 route_type 中最高优先级
|
||||
*/
|
||||
private static function resolveCurrentState(array $routeList): string
|
||||
{
|
||||
$hasSigned = false;
|
||||
$hasProblem = false;
|
||||
$hasCancel = false;
|
||||
$hasCollected = false;
|
||||
$hasInTransit = false;
|
||||
foreach ($routeList as $r) {
|
||||
$t = (int) ($r['route_type'] ?? -1);
|
||||
if ($t === 13) $hasSigned = true;
|
||||
elseif ($t === 21) $hasProblem = true;
|
||||
elseif ($t === 20) $hasCancel = true;
|
||||
elseif ($t === 11) $hasCollected = true;
|
||||
elseif (in_array($t, [0, 10, 12], true)) $hasInTransit = true;
|
||||
}
|
||||
if ($hasSigned) return ExpressTracking::STATE_SIGNED;
|
||||
if ($hasCancel) return ExpressTracking::STATE_RETURN_SIGNED;
|
||||
if ($hasProblem) return ExpressTracking::STATE_PROBLEM;
|
||||
if ($hasCollected) return ExpressTracking::STATE_COLLECTED;
|
||||
if ($hasInTransit) return ExpressTracking::STATE_IN_TRANSIT;
|
||||
return ExpressTracking::STATE_IN_TRANSIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* 甘草 sp_name → 系统 express_company 编码(与 GancaoCallbackController::EXPRESS_MAP 对齐)
|
||||
*/
|
||||
private static function expressCodeFromGancaoName(string $name): string
|
||||
{
|
||||
$map = [
|
||||
'顺丰' => 'sf',
|
||||
'京东' => 'jd',
|
||||
'极兔' => 'jt',
|
||||
'圆通' => 'yt',
|
||||
'中通' => 'zt',
|
||||
'韵达' => 'yd',
|
||||
'申通' => 'st',
|
||||
'邮政' => 'yz',
|
||||
'EMS' => 'ems',
|
||||
];
|
||||
foreach ($map as $kw => $code) {
|
||||
if ($name !== '' && mb_strpos($name, $kw) !== false) {
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 写一条 zyt_express_query_log(沿用现有结构,便于在管理后台统一查询)
|
||||
*/
|
||||
private static function logQuery(
|
||||
PrescriptionOrder $order,
|
||||
string $trackingNumber,
|
||||
string $queryType,
|
||||
bool $success,
|
||||
int $runtimeMs,
|
||||
string $errMsg,
|
||||
int $traceCount = 0
|
||||
): void {
|
||||
try {
|
||||
$log = new ExpressQueryLog();
|
||||
$log->tracking_number = $trackingNumber !== '' ? $trackingNumber : (string) ($order->tracking_number ?? '');
|
||||
$log->express_company = (string) ($order->express_company ?? 'auto');
|
||||
$log->query_type = $queryType;
|
||||
$log->query_source = 'gancao';
|
||||
$log->query_time = time();
|
||||
$log->is_success = $success ? 1 : 0;
|
||||
$log->error_code = $success ? '' : '500';
|
||||
$log->error_message = mb_substr($errMsg, 0, 500);
|
||||
$log->response_time = $runtimeMs;
|
||||
$log->trace_count = $traceCount;
|
||||
$log->create_time = time();
|
||||
$log->save();
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('Gancao route: query log save failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量同步:选取候选订单(已上传甘草、且未签收/未取消的)
|
||||
*
|
||||
* @return array{total:int, success:int, failed:int, skipped:int, details:array<int, array<string,mixed>>}
|
||||
*/
|
||||
public static function syncBatch(int $limit = 100, ?int $onlyOrderId = null): array
|
||||
{
|
||||
$stats = ['total' => 0, 'success' => 0, 'failed' => 0, 'skipped' => 0, 'details' => []];
|
||||
|
||||
$q = PrescriptionOrder::whereNull('delete_time')
|
||||
->where('gancao_reciperl_order_no', '<>', '');
|
||||
|
||||
if ($onlyOrderId !== null && $onlyOrderId > 0) {
|
||||
$q->where('id', $onlyOrderId);
|
||||
} else {
|
||||
// 仅同步未完成/未取消、且甘草已进入物流或更晚阶段的(state 110 / 20 / 30 / 90)
|
||||
// state 含义见 GancaoCallbackController::STATE_MAP
|
||||
$q->where(function ($w) {
|
||||
$w->whereIn('gancao_order_state', [110, 20, 30, 90])
|
||||
->whereOr('tracking_number', '<>', '');
|
||||
});
|
||||
$q->whereNotIn('fulfillment_status', [4]); // 排除已取消;已完成(3)也跑一次以补齐路由
|
||||
}
|
||||
|
||||
$orders = $q->order('id', 'desc')->limit($limit)->select();
|
||||
foreach ($orders as $order) {
|
||||
$stats['total']++;
|
||||
try {
|
||||
$r = self::syncOne($order);
|
||||
$detail = [
|
||||
'order_id' => (int) $order->id,
|
||||
'order_no' => (string) $order->order_no,
|
||||
'app_order_no' => (string) $order->gancao_reciperl_order_no,
|
||||
'tracking_number' => (string) $order->tracking_number,
|
||||
'success' => $r['success'],
|
||||
'message' => $r['message'],
|
||||
'traces' => $r['traces_count'],
|
||||
'state' => $r['state'],
|
||||
];
|
||||
if ($r['success']) {
|
||||
$stats['success']++;
|
||||
} else {
|
||||
$stats['failed']++;
|
||||
}
|
||||
$stats['details'][] = $detail;
|
||||
} catch (\Throwable $e) {
|
||||
$stats['failed']++;
|
||||
$stats['details'][] = [
|
||||
'order_id' => (int) $order->id,
|
||||
'success' => false,
|
||||
'message' => '异常:' . $e->getMessage(),
|
||||
];
|
||||
Log::error('Gancao route sync exception: ' . $e->getMessage(), [
|
||||
'order_id' => (int) $order->id,
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $stats;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user