93 lines
4.7 KiB
PHP
93 lines
4.7 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', '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);
|
||
$summary = sprintf('发货明细 %d 条,代发仓 %d 条,合计处理 %d 条明细', $expressRows, $autoRows, $expressRows + $autoRows);
|
||
$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;
|
||
}
|
||
}
|