121 lines
5.2 KiB
PHP
121 lines
5.2 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\command;
|
||
|
||
use app\common\service\ExpressTrackingService;
|
||
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
|
||
*
|
||
* 已对非二中心医助执行发货/签收自动释放的诊单会写入 tcm_diagnosis.shipped_non_er_assistant_cleared_at,
|
||
* 后续履约核对不再重复扫描(需先执行 sql/1.9.20260507/add_diagnosis_shipped_non_er_assistant_cleared_at.sql)。
|
||
*/
|
||
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);
|
||
$recon = ExpressTrackingService::reconcileAssistantReleaseForShippedPrescriptionOrders(200);
|
||
$stats['reconcile_cleared'] = (int) ($recon['cleared'] ?? 0);
|
||
$stats['reconcile_scanned'] = (int) ($recon['scanned'] ?? 0);
|
||
$stats['reconcile_lines'] = $recon['lines'] ?? [];
|
||
} 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('医助已移除(甘草同步+指派日志):' . (int) ($stats['assistant_cleared'] ?? 0));
|
||
$output->writeln('医助已移除(履约已发货/签收核对):' . (int) ($stats['reconcile_cleared'] ?? 0) . '(扫描 ' . (int) ($stats['reconcile_scanned'] ?? 0) . ' 单)');
|
||
$lines = array_merge($stats['assistant_lines'] ?? [], $stats['reconcile_lines'] ?? []);
|
||
if ($lines === []) {
|
||
$output->writeln('医助明细:(无)');
|
||
} else {
|
||
$output->writeln('医助明细:');
|
||
foreach ($lines as $line) {
|
||
$output->writeln(' ' . $line);
|
||
}
|
||
}
|
||
$output->writeln('耗时:' . $duration . 's');
|
||
|
||
return 0;
|
||
}
|
||
}
|