diff --git a/database/migrations/2026_10_07_150000_reconcile_shipping_merged_columns.php b/database/migrations/2026_10_07_150000_reconcile_shipping_merged_columns.php new file mode 100644 index 00000000..78a0fa28 --- /dev/null +++ b/database/migrations/2026_10_07_150000_reconcile_shipping_merged_columns.php @@ -0,0 +1,23 @@ +hasTable('jst_oms_shipping')) { + throw new RuntimeException('发货镜像表不存在,不能将合并字段迁移标为完成。'); + } + $migration = require base_path('modules/EcommerceFinance/Database/Migrations/2026_10_05_000001_add_merged_cols_to_jst_oms_shipping.php'); + $migration->up(); + } + + public function down(): void + { + // 字段属于原迁移;校准回滚保留原迁移及镜像数据。 + } +}; diff --git a/modules/EcommerceFinance/Console/SyncJushuitanShipping.php b/modules/EcommerceFinance/Console/SyncJushuitanShipping.php index a27f74ea..b14b5e00 100644 --- a/modules/EcommerceFinance/Console/SyncJushuitanShipping.php +++ b/modules/EcommerceFinance/Console/SyncJushuitanShipping.php @@ -61,7 +61,9 @@ class SyncJushuitanShipping extends Command $expressRows = (int) ($result['processed'] ?? 0); $autoRows = (int) ($result['autoship'] ?? 0); - $summary = sprintf('发货明细 %d 条,代发仓 %d 条,合计处理 %d 条明细', $expressRows, $autoRows, $expressRows + $autoRows); + $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) : ''; diff --git a/modules/EcommerceFinance/Repositories/ShippingOrderRepository.php b/modules/EcommerceFinance/Repositories/ShippingOrderRepository.php index 91915fb3..c3bdc35a 100644 --- a/modules/EcommerceFinance/Repositories/ShippingOrderRepository.php +++ b/modules/EcommerceFinance/Repositories/ShippingOrderRepository.php @@ -49,12 +49,18 @@ class ShippingOrderRepository // 合并:按包裹(source_id)归并 $base = $this->filteredQuery($filters); $total = (clone $base)->distinct()->count('source_id'); + $pageKeys = (clone $base)->select('source_id')->selectRaw('MAX(ship_time) as ship_time') + ->groupBy('source_id')->orderByDesc('ship_time')->orderByDesc('source_id') + ->forPage($page, $perPage)->pluck('source_id')->all(); + if ($pageKeys === []) { + return ['data' => [], 'total' => $total]; + } $rows = (clone $base) + ->whereIn('source_id', $pageKeys) ->groupBy('source_id') ->selectRaw($this->mergedSelectRaw()) ->orderByDesc('ship_time') ->orderByDesc('source_id') - ->forPage($page, $perPage) ->get()->map(fn ($r) => (array) $r)->all(); return ['data' => $rows, 'total' => $total]; @@ -157,7 +163,12 @@ class ShippingOrderRepository if ($last !== null) { $query->where('source_id', '>', $last); } - $rows = $query->groupBy('source_id')->selectRaw($this->mergedSelectRaw()) + $keys = (clone $query)->select('source_id')->distinct() + ->orderBy('source_id')->limit($batchSize)->pluck('source_id')->all(); + if ($keys === []) { + return; + } + $rows = $query->whereIn('source_id', $keys)->groupBy('source_id')->selectRaw($this->mergedSelectRaw()) ->orderBy('source_id')->limit($batchSize)->get()->all(); if ($rows === []) { return; diff --git a/tests/Unit/EcommerceFinance/ShippingMergedColumnsMigrationTest.php b/tests/Unit/EcommerceFinance/ShippingMergedColumnsMigrationTest.php new file mode 100644 index 00000000..69dc805f --- /dev/null +++ b/tests/Unit/EcommerceFinance/ShippingMergedColumnsMigrationTest.php @@ -0,0 +1,31 @@ +getSchemaBuilder(); + $columns = ['m_name', 'm_volume_cm3', 'm_length_cm', 'm_width_cm', 'm_height_cm']; + $schema->table('jst_oms_shipping', fn ($table) => $table->dropColumn($columns)); + $migration = require base_path('database/migrations/2026_10_07_150000_reconcile_shipping_merged_columns.php'); + $migration->up(); + $migration->up(); + foreach ($columns as $column) { + $this->assertTrue($schema->hasColumn('jst_oms_shipping', $column)); + } + } + + public function test_missing_target_is_reported_instead_of_marked_complete(): void + { + DB::connection('business_mirror')->getSchemaBuilder()->drop('jst_oms_shipping'); + $migration = require base_path('database/migrations/2026_10_07_150000_reconcile_shipping_merged_columns.php'); + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('发货镜像表不存在'); + $migration->up(); + } +} diff --git a/tests/Unit/EcommerceFinance/ShippingReplicaSynchronizerTest.php b/tests/Unit/EcommerceFinance/ShippingReplicaSynchronizerTest.php index ab2b853f..aada17b3 100644 --- a/tests/Unit/EcommerceFinance/ShippingReplicaSynchronizerTest.php +++ b/tests/Unit/EcommerceFinance/ShippingReplicaSynchronizerTest.php @@ -844,7 +844,7 @@ class ShippingReplicaSynchronizerTest extends TestCase $this->artisan('jushuitan:sync-shipping', [ '--from' => '2026-09-01', '--to' => '2026-09-05', '--skip-freight' => true, - ])->expectsOutputToContain('唯一包裹源键 2 个,包裹记录处理 2 次;发货明细 1 条,代发仓 1 条,合计处理 2 条明细') + ])->expectsOutputToContain('唯一包裹源键 2 个,包裹记录处理 2 次;发货明细 1 条,代发仓 1 条,销售出库补漏 0 条,采购出库补漏 0 条,合计处理 2 条明细') ->assertExitCode(0); }