修复历史发货代发仓扫描超时 #265
@ -63,6 +63,9 @@ class ShippingReplicaSynchronizer
|
||||
/** @var array<int,true> */
|
||||
private array $seenSourceIds = [];
|
||||
|
||||
/** @var array<string,true> 发货明细已占用的窗口内订单号 */
|
||||
private array $expressOrderIds = [];
|
||||
|
||||
/** @var list<string> */
|
||||
private array $dwsFailedDays = [];
|
||||
|
||||
@ -106,6 +109,7 @@ class ShippingReplicaSynchronizer
|
||||
$this->packagesRetainedForUnmatchedAmount = 0;
|
||||
$this->staleMirrorPackages = 0;
|
||||
$this->seenSourceIds = [];
|
||||
$this->expressOrderIds = [];
|
||||
$this->dwsFailedDays = [];
|
||||
$now = now();
|
||||
$fromS = $windowed ? $from->format('Y-m-d H:i:s') : null;
|
||||
@ -187,6 +191,7 @@ class ShippingReplicaSynchronizer
|
||||
$this->replaceWindow = false;
|
||||
$this->dwsFailedDays = [];
|
||||
$this->seenSourceIds = [];
|
||||
$this->expressOrderIds = [];
|
||||
gc_collect_cycles();
|
||||
}
|
||||
}
|
||||
@ -276,11 +281,16 @@ class ShippingReplicaSynchronizer
|
||||
if ($windowed) {
|
||||
$kept = [];
|
||||
foreach ($rows as $r) {
|
||||
$jd = $r['jo_send'] ?? null;
|
||||
$joTs = ($jd !== null && $jd !== '') ? strtotime((string) $jd) : null;
|
||||
$oid = (string) ($r['inner_order_id'] ?? '');
|
||||
if ($oid !== '' && $joTs !== null && $joTs !== false && $joTs >= $fromTs && $joTs < $toTs) {
|
||||
$this->expressOrderIds[$oid] = true;
|
||||
}
|
||||
$st = $r['send_time'] ?? null;
|
||||
$ts = ($st !== null && $st !== '') ? strtotime((string) $st) : null;
|
||||
if ($ts === null) {
|
||||
$jd = $r['jo_send'] ?? null;
|
||||
$ts = ($jd !== null && $jd !== '') ? strtotime((string) $jd) : null;
|
||||
$ts = $joTs;
|
||||
}
|
||||
if ($ts !== null && $ts >= $fromTs && $ts < $toTs) {
|
||||
$kept[] = $r;
|
||||
@ -382,8 +392,7 @@ class ShippingReplicaSynchronizer
|
||||
$lastRid = null;
|
||||
|
||||
for ($b = 0; $b <= $maxBatches; $b++) {
|
||||
$where = "o.status = 'Sent' AND o.send_date >= ? AND o.send_date < ? "
|
||||
. 'AND NOT EXISTS (SELECT 1 FROM rpt.jst_order_express_details e WHERE e.inner_order_id = o.o_id)';
|
||||
$where = "o.status = 'Sent' AND o.send_date >= ? AND o.send_date < ?";
|
||||
$bind = [$fromS, $toS];
|
||||
if ($lastRid !== null) {
|
||||
$where .= ' AND o.Id < ?';
|
||||
@ -398,6 +407,17 @@ class ShippingReplicaSynchronizer
|
||||
throw new RuntimeException('代发仓扫描达到批次上限,仍有未读取的上游数据。');
|
||||
}
|
||||
|
||||
$cnt = count($rows);
|
||||
$eligible = [];
|
||||
foreach ($rows as $r) {
|
||||
$lastRid = $r['rid'];
|
||||
$oid = (string) ($r['o_id'] ?? '');
|
||||
if ($oid !== '' && ! isset($this->expressOrderIds[$oid])) {
|
||||
$eligible[] = $r;
|
||||
}
|
||||
}
|
||||
$rows = $eligible;
|
||||
|
||||
$oids = [];
|
||||
$soIds = [];
|
||||
$logs = [];
|
||||
@ -414,7 +434,6 @@ class ShippingReplicaSynchronizer
|
||||
if ($lg !== '') {
|
||||
$logs[$lg] = true;
|
||||
}
|
||||
$lastRid = $r['rid'];
|
||||
}
|
||||
[$byOid, $bySo] = $this->loadAllItems(array_keys($oids), array_keys($soIds), false);
|
||||
$dws = [];
|
||||
@ -435,7 +454,6 @@ class ShippingReplicaSynchronizer
|
||||
$skus = $this->loadSkus($this->collectSkuIds($byOid, $bySo, $dws, $viewItems));
|
||||
$freight = $this->skipFreight ? [] : $this->loadFreight(array_keys($soIds));
|
||||
|
||||
$cnt = count($rows);
|
||||
foreach ($rows as $r) {
|
||||
$oid = (string) ($r['o_id'] ?? '');
|
||||
if ($oid === '') {
|
||||
|
||||
@ -234,6 +234,86 @@ class ShippingReplicaSynchronizerTest extends TestCase
|
||||
->where('dataset_key', 'jst.sctrade')->value('status'));
|
||||
}
|
||||
|
||||
public function test_auto_ship_excludes_every_order_with_express_detail_even_when_detail_is_outside_window_or_label_excluded(): void
|
||||
{
|
||||
$orderQueries = [];
|
||||
$upstream = $this->createMock(UpstreamReadOnlyQuery::class);
|
||||
$upstream->method('select')->willReturnCallback(function (string $sql, array $bindings) use (&$orderQueries): array {
|
||||
if (str_contains($sql, 'FROM rpt.jst_order_express_details e LEFT JOIN')) {
|
||||
return [
|
||||
['id' => 1001, 'inner_order_id' => '123', 'jo_send' => '2026-09-02 13:00:00',
|
||||
'send_time' => '2026-08-31 12:00:00'],
|
||||
['id' => 1002, 'inner_order_id' => '456', 'jo_send' => '2026-09-02 13:00:00',
|
||||
'send_time' => '2026-09-02 12:00:00', 'order_labels' => '线上发货'],
|
||||
['id' => 1003, 'inner_order_id' => '789', 'jo_send' => '2026-09-02 13:00:00',
|
||||
'send_time' => '2026-09-02 12:00:00'],
|
||||
];
|
||||
}
|
||||
if (str_contains($sql, 'FROM rpt.jst_Orders o WHERE')) {
|
||||
$orderQueries[] = [$sql, $bindings];
|
||||
return array_map(fn (int $oid): array => [
|
||||
'rid' => $oid, 'o_id' => (string) $oid, 'send_date' => '2026-09-02 13:00:00',
|
||||
], [789, 456, 321, 123]);
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
$factory = $this->createMock(UpstreamReadOnlyQueryFactory::class);
|
||||
$factory->method('forSource')->with('operations_replica')->willReturn($upstream);
|
||||
|
||||
$result = (new ShippingReplicaSynchronizer($factory))->sync(
|
||||
Carbon::parse('2026-09-01'), Carbon::parse('2026-09-05'), 2000, null, true,
|
||||
);
|
||||
|
||||
$this->assertCount(1, $orderQueries);
|
||||
$this->assertStringNotContainsString('NOT EXISTS', $orderQueries[0][0]);
|
||||
$this->assertSame(['2026-09-01 00:00:00', '2026-09-05 00:00:00'], $orderQueries[0][1]);
|
||||
$this->assertSame(1, $result['processed']);
|
||||
$this->assertSame(1, $result['autoship']);
|
||||
$this->assertSame([1003, 1000000321], DB::connection('business_mirror')->table('jst_oms_shipping')
|
||||
->orderBy('source_id')->pluck('source_id')->map(fn ($id) => (int) $id)->all());
|
||||
}
|
||||
|
||||
public function test_auto_ship_advances_raw_page_cursor_when_all_400_orders_have_express_details(): void
|
||||
{
|
||||
$orderBindings = [];
|
||||
$upstream = $this->createMock(UpstreamReadOnlyQuery::class);
|
||||
$upstream->method('select')->willReturnCallback(function (string $sql, array $bindings) use (&$orderBindings): array {
|
||||
if (str_contains($sql, 'FROM rpt.jst_order_express_details e LEFT JOIN')) {
|
||||
return array_map(fn (int $oid): array => [
|
||||
'id' => $oid, 'inner_order_id' => (string) $oid,
|
||||
'jo_send' => '2026-09-02 13:00:00', 'send_time' => '2026-08-31 12:00:00',
|
||||
], range(1000, 601));
|
||||
}
|
||||
if (str_contains($sql, 'FROM rpt.jst_Orders o WHERE')) {
|
||||
$orderBindings[] = $bindings;
|
||||
if (count($orderBindings) === 1) {
|
||||
return array_map(fn (int $oid): array => [
|
||||
'rid' => $oid, 'o_id' => (string) $oid, 'send_date' => '2026-09-02 13:00:00',
|
||||
], range(1000, 601));
|
||||
}
|
||||
return [['rid' => 600, 'o_id' => '500', 'send_date' => '2026-09-02 13:00:00']];
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
$factory = $this->createMock(UpstreamReadOnlyQueryFactory::class);
|
||||
$factory->method('forSource')->with('operations_replica')->willReturn($upstream);
|
||||
|
||||
$result = (new ShippingReplicaSynchronizer($factory))->sync(
|
||||
Carbon::parse('2026-09-01'), Carbon::parse('2026-09-05'), 2000, null, true,
|
||||
);
|
||||
|
||||
$this->assertSame([
|
||||
['2026-09-01 00:00:00', '2026-09-05 00:00:00'],
|
||||
['2026-09-01 00:00:00', '2026-09-05 00:00:00', 601],
|
||||
], $orderBindings);
|
||||
$this->assertSame(0, $result['processed']);
|
||||
$this->assertSame(1, $result['autoship']);
|
||||
$this->assertSame([1000000500], DB::connection('business_mirror')->table('jst_oms_shipping')
|
||||
->pluck('source_id')->map(fn ($id) => (int) $id)->all());
|
||||
}
|
||||
|
||||
public function test_skip_freight_preserves_existing_amounts_and_marks_new_amounts_incomplete(): void
|
||||
{
|
||||
$db = DB::connection('business_mirror');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user