From c02345ed3ffa834a61a968cdb41ad18b7b227534 Mon Sep 17 00:00:00 2001 From: WuYongJie <549727955@qq.com> Date: Wed, 9 Sep 2026 15:25:54 +0800 Subject: [PATCH] fix: index courier detail pagination and avoid duplicate aggregation --- config/upstream_mirrors.php | 2 +- ...160000_index_courier_detail_pagination.php | 40 +++++++++++++ ...rFreightReconciliationMirrorRepository.php | 6 +- .../CourierDetailPaginationMigrationTest.php | 59 +++++++++++++++++++ 4 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 database/migrations/2026_09_09_160000_index_courier_detail_pagination.php create mode 100644 tests/Unit/EcommerceFinance/CourierDetailPaginationMigrationTest.php diff --git a/config/upstream_mirrors.php b/config/upstream_mirrors.php index 1ca2b58c..9a3ac008 100644 --- a/config/upstream_mirrors.php +++ b/config/upstream_mirrors.php @@ -410,7 +410,7 @@ return [ 'jiansun.dbo.jst_order_express_details' => [ 'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'jst_order_express_details', 'target' => 'js_jst_order_express_details', 'keys' => ['id'], 'watermark' => 'updatedAt', - 'indexes' => [['send_time', 'id'], ['inner_order_id', 'id']], + 'indexes' => [['send_time', 'id'], ['inner_order_id', 'id'], ['id', 'logistic_id']], 'columns' => [ 'id', 'updatedAt', 'inner_order_id', 'so_id', 'logistic_id', 'logistic_company_name', 'logistic_status_name', 'platform_name', 'shop_name', 'warehouse_name', 'send_time', diff --git a/database/migrations/2026_09_09_160000_index_courier_detail_pagination.php b/database/migrations/2026_09_09_160000_index_courier_detail_pagination.php new file mode 100644 index 00000000..14b177d8 --- /dev/null +++ b/database/migrations/2026_09_09_160000_index_courier_detail_pagination.php @@ -0,0 +1,40 @@ +hasTable(self::TABLE) || $schema->hasIndex(self::TABLE, self::INDEX)) return; + $db = DB::connection($connection); + $length = $db->getDriverName() === 'sqlite' ? 'length' : 'char_length'; + if ($db->table(self::TABLE)->whereRaw($length . '(logistic_id) > 100')->exists()) { + throw new RuntimeException('快递单号超过上游 varchar(100) 定义,已停止变更。'); + } + $schema->table(self::TABLE, static function (Blueprint $table): void { + $table->string('logistic_id', 100)->nullable()->change(); + $table->index(['id', 'logistic_id'], self::INDEX); + }); + } + + public function down(): void + { + if (config('system_database.mode', 'bootstrap') === 'bootstrap') return; + $schema = Schema::connection((string) config('upstream_mirrors.connection', 'business_mirror')); + if (! $schema->hasTable(self::TABLE) || ! $schema->hasIndex(self::TABLE, self::INDEX)) return; + $schema->table(self::TABLE, static function (Blueprint $table): void { + $table->dropIndex(self::INDEX); + $table->longText('logistic_id')->nullable()->change(); + }); + } +}; diff --git a/modules/EcommerceFinance/Repositories/CourierFreightReconciliationMirrorRepository.php b/modules/EcommerceFinance/Repositories/CourierFreightReconciliationMirrorRepository.php index f8b35f97..9a842e38 100644 --- a/modules/EcommerceFinance/Repositories/CourierFreightReconciliationMirrorRepository.php +++ b/modules/EcommerceFinance/Repositories/CourierFreightReconciliationMirrorRepository.php @@ -165,10 +165,12 @@ class CourierFreightReconciliationMirrorRepository $page = max($page, 1); $perPage = min(max($perPage, 1), 100); $query = $this->filteredQuery($filters); - $total = (clone $query)->count('d.id'); + // Both joins select at most one latest row, so this count is also the + // paginator total. Avoid traversing the entire joined history twice. $summary = (clone $query)->selectRaw( - 'COUNT(DISTINCT d.id) AS aggregate_count, COALESCE(SUM(o.freight), 0) AS freight', + 'COUNT(d.id) AS aggregate_count, COALESCE(SUM(o.freight), 0) AS freight', )->first(); + $total = (int) ($summary->aggregate_count ?? 0); $rows = (clone $query)->select($this->selectColumns()) ->orderByDesc('d.id') ->forPage($page, $perPage) diff --git a/tests/Unit/EcommerceFinance/CourierDetailPaginationMigrationTest.php b/tests/Unit/EcommerceFinance/CourierDetailPaginationMigrationTest.php new file mode 100644 index 00000000..861f2abf --- /dev/null +++ b/tests/Unit/EcommerceFinance/CourierDetailPaginationMigrationTest.php @@ -0,0 +1,59 @@ + 'connected', + 'upstream_mirrors.connection' => 'courier_migration_test', + 'database.connections.courier_migration_test' => [ + 'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '', + ], + ]); + DB::purge('courier_migration_test'); + Schema::connection('courier_migration_test')->create('js_jst_order_express_details', function (Blueprint $table): void { + $table->integer('id'); + $table->longText('logistic_id')->nullable(); + }); + } + + public function test_migration_and_rollback_preserve_waybills_and_nulls(): void + { + $table = DB::connection('courier_migration_test')->table('js_jst_order_express_details'); + $rows = [['id' => 1, 'logistic_id' => '001234567890123456789'], ['id' => 2, 'logistic_id' => null]]; + $table->insert($rows); + $migration = require base_path('database/migrations/2026_09_09_160000_index_courier_detail_pagination.php'); + $migration->up(); + $migration->up(); + $this->assertTrue(Schema::connection('courier_migration_test')->hasIndex('js_jst_order_express_details', 'idx_courier_detail_id_waybill')); + $this->assertSame($rows, $table->orderBy('id')->get()->map(fn ($row) => (array) $row)->all()); + $migration->down(); + $this->assertFalse(Schema::connection('courier_migration_test')->hasIndex('js_jst_order_express_details', 'idx_courier_detail_id_waybill')); + $this->assertSame($rows, $table->orderBy('id')->get()->map(fn ($row) => (array) $row)->all()); + } + + public function test_oversize_waybill_stops_migration_without_truncation(): void + { + $value = str_repeat('A', 101); + $table = DB::connection('courier_migration_test')->table('js_jst_order_express_details'); + $table->insert(['id' => 1, 'logistic_id' => $value]); + $migration = require base_path('database/migrations/2026_09_09_160000_index_courier_detail_pagination.php'); + try { + $migration->up(); + $this->fail('Oversize source data must stop migration.'); + } catch (RuntimeException $exception) { + $this->assertSame($value, $table->value('logistic_id')); + $this->assertFalse(Schema::connection('courier_migration_test')->hasIndex('js_jst_order_express_details', 'idx_courier_detail_id_waybill')); + } + } +} -- 2.47.2