diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 64d8d09b..b4492d2d 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -88,12 +88,13 @@ class Kernel extends ConsoleKernel ->runInBackground(); } - // 聚水潭发货数据每日重拉最近三天,覆盖副本库迟到更新。 + // 聚水潭发货数据:凌晨重拉最近 40 天。聚水潭常在发货后数天到数周内换单/改快递公司/作废采购退货出库, + // 只拉最近几天会让上月数据停在旧值(快递名称不一致、已作废的单仍在);40 天覆盖上月结算期。 // 默认关闭(JST_SYNC_ENABLED=false);填好凭证并完成迁移后再打开。 if ((bool) env('JST_SYNC_ENABLED', false)) { - $schedule->command('jushuitan:sync-shipping --days=3') + $schedule->command('jushuitan:sync-shipping --days=40') ->dailyAt('01:28') - ->withoutOverlapping(120) + ->withoutOverlapping(300) ->runInBackground(); // 凌晨副本库最近一两天的订单/商品明细常未到齐(视图、订单商品表滞后), // 早上再拉一次最近 3 天,补齐凌晨只写入单号、缺商品明细的包裹。 diff --git a/modules/EcommerceFinance/Services/Jushuitan/ShippingReplicaSynchronizer.php b/modules/EcommerceFinance/Services/Jushuitan/ShippingReplicaSynchronizer.php index cc159c50..097a0e9b 100644 --- a/modules/EcommerceFinance/Services/Jushuitan/ShippingReplicaSynchronizer.php +++ b/modules/EcommerceFinance/Services/Jushuitan/ShippingReplicaSynchronizer.php @@ -73,6 +73,8 @@ class ShippingReplicaSynchronizer private int $packagesRetainedForUnmatchedAmount = 0; private int $staleMirrorPackages = 0; private int $supersededRemoved = 0; + private int $outboundScanned = 0; + private int $purchaseScanned = 0; /** @var array */ private array $seenSourceIds = []; @@ -183,6 +185,8 @@ class ShippingReplicaSynchronizer $this->packagesRetainedForUnmatchedAmount = 0; $this->staleMirrorPackages = 0; $this->supersededRemoved = 0; + $this->outboundScanned = 0; + $this->purchaseScanned = 0; $this->seenSourceIds = []; $this->expressOrderIds = []; $this->dwsFailedDays = []; @@ -857,6 +861,7 @@ class ShippingReplicaSynchronizer [$a, $a + $step, $fromS, $toS] ); $scanned += count($rows); + $this->outboundScanned += count($rows); $pick = []; foreach ($rows as $r) { $io = (string) ($r['io_id'] ?? ''); @@ -959,6 +964,7 @@ class ShippingReplicaSynchronizer . 'receiver_address, receiver_name, receiver_mobile FROM ' . self::PURCHASE_OUT . ' WHERE io_date >= ? AND io_date < ?', [$fromS, $toS] ); + $this->purchaseScanned += count($rows); $pick = []; $skip = ['done' => 0, 'cancel' => 0, 'noitem' => 0]; foreach ($rows as $r) { @@ -2389,6 +2395,12 @@ class ShippingReplicaSynchronizer } // 凌晨按自动发货仓入库、当时还没有快递单号的旧包裹:订单之后有了发货明细(改走发货明细来源键), // 或同一交易单号本次已由其它包裹吐出 → 视为被取代 + // 出库单兜底/采购退货出库:本次已完整扫描窗口内的出库单与采购退货单,未再出现说明上游已作废、换单或不再收录(工作平台也不再显示) + if (($id >= self::PURCHASE_OUT_OFFSET && $this->purchaseScanned > 0) + || ($id >= self::OUTBOUND_OFFSET && $id < self::PURCHASE_OUT_OFFSET && $this->outboundScanned > 0)) { + $superseded[] = $id; + continue; + } $so = trim((string) ($row->trade_no ?? '')); $isAuto = $id >= self::AUTOSHIP_OFFSET && $id < self::OUTBOUND_OFFSET; if (($isAuto && isset($this->expressOrderIds[(string) ($id - self::AUTOSHIP_OFFSET)])) diff --git a/tests/Unit/EcommerceFinance/ShippingReplicaSynchronizerTest.php b/tests/Unit/EcommerceFinance/ShippingReplicaSynchronizerTest.php index 8eab6cd0..9fd60ebe 100644 --- a/tests/Unit/EcommerceFinance/ShippingReplicaSynchronizerTest.php +++ b/tests/Unit/EcommerceFinance/ShippingReplicaSynchronizerTest.php @@ -1326,6 +1326,9 @@ class ShippingReplicaSynchronizerTest extends TestCase 'trade_no' => null, 'barcode' => null, 'product_name' => null, 'amount' => null, 'receiver_state' => null], ['source_id' => 1000000888, 'sku_source_id' => 1000000888, 'ship_time' => '2026-09-02 14:00:00', 'express_no' => 'ZZ9', 'trade_no' => null, 'barcode' => null, 'product_name' => null, 'amount' => null, 'receiver_state' => null], + // 采购退货出库:上游已作废,本次未再出现 + ['source_id' => 3000000555, 'sku_source_id' => 3000000555, 'ship_time' => '2026-09-02 15:00:00', 'express_no' => 'SF555', + 'trade_no' => null, 'barcode' => null, 'product_name' => null, 'amount' => null, 'receiver_state' => null], // 凌晨入库时还没有快递单号的自动发货仓包裹,同一交易单号本次已由 6000 吐出 ['source_id' => 1000000999, 'sku_source_id' => 1000000999, 'ship_time' => '2026-09-02 13:30:00', 'express_no' => null, 'trade_no' => 'so-2', 'barcode' => null, 'product_name' => null, 'amount' => null, 'receiver_state' => null], @@ -1346,6 +1349,9 @@ class ShippingReplicaSynchronizerTest extends TestCase ['o_id' => 'order-2', 'io_id' => 'outbound-2', 'l_id' => 'YT6'], ]; } + if (str_contains($sql, 'FROM rpt.jst_purchaseout WHERE io_date')) { + return [['io_id' => '555', 'io_date' => '2026-09-02 15:00:00', 'status' => 'Cancelled', 'l_id' => 'SF555']]; + } if (str_contains($sql, 'FROM rpt.Jst_SendData_View_extension')) { return [ ['io_id' => 'outbound-1', 'sku_ids' => 'new-sku', 'qty' => 1, 'type' => 0, 'sku_name' => '新商品甲', 'volume' => null], @@ -1363,7 +1369,8 @@ class ShippingReplicaSynchronizerTest extends TestCase ); $this->assertSame(1, $result['packages_retained_for_unmatched_amount']); - $this->assertSame(2, $result['superseded_packages_removed']); + $this->assertSame(3, $result['superseded_packages_removed']); + $this->assertSame(0, $db->table('jst_oms_shipping')->where('source_id', 3000000555)->count()); $this->assertSame(0, $db->table('jst_oms_shipping')->where('source_id', 1000000999)->count()); $this->assertSame(1, $result['stale_mirror_packages']); // 保留的旧包裹:商品金额不动,省市区按上游刷新