- 发货明细(jst_order_express_details)可能停在作废前的旧分配(如先分工厂代发仓、后改外采由总仓出库;或换单后仍是旧快递公司), 工作平台按实际出库单显示。快递公司与出库单不一致或为空时改用出库单 logistics_company, 仓库按出库单 wms_co_id 映射(从快递公司一致的明细行学习仓库名,扫描结束统一回填)。 - 9 月核对:快递名称不一致 434/331,612,仓库不一致 85,其中外采快递、线下快递、换单类为此原因。 - jushuitan:sync-shipping 内存上限 1536M -> 4096M:整月窗口重跑在扫描后段内存耗尽(退出码 255)。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014nvGZkg27YMEdhry6eiuwf
95 lines
5.0 KiB
PHP
95 lines
5.0 KiB
PHP
<?php
|
||
|
||
namespace Modules\EcommerceFinance\Console;
|
||
|
||
use Illuminate\Console\Command;
|
||
use Illuminate\Support\Carbon;
|
||
use Modules\EcommerceFinance\Services\Jushuitan\ShippingReplicaSynchronizer;
|
||
|
||
/**
|
||
* 发货数据同步:从经营分析副本库(operations_replica)直读聚水潭发货明细,清洗进本地展示表。
|
||
*
|
||
* php artisan jushuitan:sync-shipping --from=2026-09-01 --to=2026-09-17 # 按发货时间窗口拉全量
|
||
* php artisan jushuitan:sync-shipping --days=3 # 滚动最近 3 天(每日调度用)
|
||
* php artisan jushuitan:sync-shipping # 最近 2000 条(快速看效果)
|
||
*
|
||
* 按 id 去重 upsert,任何时候补拉早期区间都不会重复。
|
||
*/
|
||
class SyncJushuitanShipping extends Command
|
||
{
|
||
protected $signature = 'jushuitan:sync-shipping {--from=} {--to=} {--days=} {--limit=2000}
|
||
{--skip-freight : 仅补发货数量;保留已有运费,新记录运费留空并标记部分完成}';
|
||
|
||
protected $description = '从经营分析副本库直读聚水潭发货明细,清洗写入 caiwu 发货数据表';
|
||
|
||
public function handle(): int
|
||
{
|
||
@ini_set('memory_limit', '4096M'); // 整月窗口重跑约需 2GB+,原 1536M 会在扫描后段内存耗尽
|
||
@set_time_limit(0);
|
||
$from = $this->option('from') ? Carbon::parse($this->option('from')) : null;
|
||
$to = $this->option('to') ? Carbon::parse($this->option('to')) : null;
|
||
|
||
// --days=N:滚动最近 N 天(含今天)。显式 --from/--to 优先。
|
||
if (! $from && ! $to && $this->option('days')) {
|
||
$days = max((int) $this->option('days'), 1);
|
||
$from = now()->startOfDay()->subDays($days - 1);
|
||
$to = now()->startOfDay()->addDay();
|
||
}
|
||
|
||
$skipFreight = (bool) $this->option('skip-freight');
|
||
if ($skipFreight && (! $from || ! $to)) {
|
||
$this->error('--skip-freight 需要指定 --from 和 --to,或使用 --days。');
|
||
return self::FAILURE;
|
||
}
|
||
|
||
$limit = (int) ($this->option('limit') ?: 2000);
|
||
|
||
$range = ($from && $to) ? $from->toDateString() . ' ~ ' . $to->toDateString() : '最近 ' . $limit . ' 条';
|
||
$this->info('开始从副本库同步发货明细(' . $range . ')...');
|
||
$progress = function (string $msg): void {
|
||
$this->info(' ' . $msg);
|
||
if (function_exists('fflush')) {
|
||
@fflush(STDOUT);
|
||
}
|
||
};
|
||
try {
|
||
$result = ShippingReplicaSynchronizer::make()->sync($from, $to, $limit, $progress, $skipFreight);
|
||
} catch (\Throwable $e) {
|
||
$this->error('同步失败:' . $e->getMessage());
|
||
return self::FAILURE;
|
||
}
|
||
|
||
$expressRows = (int) ($result['processed'] ?? 0);
|
||
$autoRows = (int) ($result['autoship'] ?? 0);
|
||
$outboundRows = (int) ($result['outbound'] ?? 0);
|
||
$purchaseOutRows = (int) ($result['purchase_out'] ?? 0);
|
||
$summary = sprintf('发货明细 %d 条,代发仓 %d 条,销售出库补漏 %d 条,采购出库补漏 %d 条,合计处理 %d 条明细', $expressRows, $autoRows, $outboundRows, $purchaseOutRows, $expressRows + $autoRows + $outboundRows + $purchaseOutRows);
|
||
$packageSummary = isset($result['unique_packages'])
|
||
? sprintf('唯一包裹源键 %d 个,包裹记录处理 %d 次;', $result['unique_packages'], $result['packages'] ?? 0)
|
||
: '';
|
||
if ($result['freight_incomplete'] ?? false) {
|
||
$this->warn('本次窗口' . $packageSummary . $summary . ';新记录运费与结算重量待补齐,空白不代表 0。');
|
||
} else {
|
||
$this->info('完成:' . $packageSummary . $summary . '。');
|
||
}
|
||
if (($result['packages_without_items'] ?? 0) > 0 || ($result['dws_failed_days'] ?? []) !== []) {
|
||
$this->warn(sprintf(
|
||
'商品或金额待核对:%d 个包裹未匹配商品,DWS 预载失败 %d 天;页面包裹数不等于商品明细完整性。',
|
||
$result['packages_without_items'] ?? 0,
|
||
count($result['dws_failed_days'] ?? []),
|
||
));
|
||
}
|
||
if (($result['packages_retained_for_missing_items'] ?? 0) > 0) {
|
||
$this->warn('其中 ' . $result['packages_retained_for_missing_items'] . ' 个包裹因新商品明细缺失而保留原有商品及金额,本次未完成这些包裹的更新。');
|
||
}
|
||
if (($result['packages_retained_for_unmatched_amount'] ?? 0) > 0) {
|
||
$this->warn('其中 ' . $result['packages_retained_for_unmatched_amount'] . ' 个包裹因商品金额无法安全匹配而保留原记录,金额待核对。');
|
||
}
|
||
if (($result['stale_mirror_packages'] ?? 0) > 0) {
|
||
$this->error('本窗口仍有 ' . $result['stale_mirror_packages'] . ' 个镜像包裹未在上游扫描中出现;已保留原记录,需按源键核对后才能认定数量完整。');
|
||
return self::FAILURE;
|
||
}
|
||
return self::SUCCESS;
|
||
}
|
||
}
|