From 1e291f840823abccd8e82eb371d0b793c8ebbfaa Mon Sep 17 00:00:00 2001 From: yinjianm Date: Thu, 26 Feb 2026 04:39:42 +0800 Subject: [PATCH] fix stat_user unique key with record_type and deduplicate before index --- app/Jobs/StatUserJob.php | 4 +- ..._add_node_fields_to_v2_stat_user_table.php | 44 ++++++++++++++++++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/app/Jobs/StatUserJob.php b/app/Jobs/StatUserJob.php index da03a3f..98f7347 100644 --- a/app/Jobs/StatUserJob.php +++ b/app/Jobs/StatUserJob.php @@ -129,7 +129,7 @@ class StatUserJob implements ShouldQueue 'created_at' => time(), 'updated_at' => time(), ], - ['user_id', 'server_rate', 'server_id', 'server_type', 'record_at'], + ['user_id', 'server_rate', 'server_id', 'server_type', 'record_at', 'record_type'], [ 'u' => DB::raw("u + VALUES(u)"), 'd' => DB::raw("d + VALUES(d)"), @@ -152,7 +152,7 @@ class StatUserJob implements ShouldQueue $sql = "INSERT INTO {$table} (user_id, server_rate, server_id, server_type, record_at, record_type, u, d, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - ON CONFLICT (user_id, server_rate, server_id, server_type, record_at) + ON CONFLICT (user_id, server_rate, server_id, server_type, record_at, record_type) DO UPDATE SET u = {$table}.u + EXCLUDED.u, d = {$table}.d + EXCLUDED.d, diff --git a/database/migrations/2026_02_25_000001_add_node_fields_to_v2_stat_user_table.php b/database/migrations/2026_02_25_000001_add_node_fields_to_v2_stat_user_table.php index 06782de..55ceed5 100644 --- a/database/migrations/2026_02_25_000001_add_node_fields_to_v2_stat_user_table.php +++ b/database/migrations/2026_02_25_000001_add_node_fields_to_v2_stat_user_table.php @@ -28,17 +28,19 @@ return new class extends Migration { } }); + $this->mergeDuplicateRows(); + $this->dropUniqueIfExists(self::TABLE, self::OLD_UNIQUE_INDEX); $this->dropIndexIfExists(self::TABLE, self::OLD_COMPOSITE_INDEX); $this->addUniqueIfNotExists( self::TABLE, - ['user_id', 'server_rate', 'server_id', 'server_type', 'record_at'], + ['user_id', 'server_rate', 'server_id', 'server_type', 'record_at', 'record_type'], self::NEW_UNIQUE_INDEX ); $this->addIndexIfNotExists( self::TABLE, - ['user_id', 'server_id', 'server_type', 'record_at'], + ['user_id', 'server_id', 'server_type', 'record_at', 'record_type'], self::NEW_COMPOSITE_INDEX ); $this->addIndexIfNotExists(self::TABLE, ['server_id'], self::NEW_SERVER_ID_INDEX); @@ -132,4 +134,42 @@ return new class extends Migration { default => false, }; } + + /** + * Merge historical duplicates before adding the new unique index. + * Duplicate criteria is aligned with the new unique key columns. + */ + private function mergeDuplicateRows(): void + { + $duplicateRows = DB::table(self::TABLE) + ->selectRaw( + 'MIN(id) AS keep_id, user_id, server_rate, server_id, server_type, record_at, record_type, ' . + 'SUM(u) AS total_u, SUM(d) AS total_d, MIN(created_at) AS min_created_at, MAX(updated_at) AS max_updated_at' + ) + ->groupBy('user_id', 'server_rate', 'server_id', 'server_type', 'record_at', 'record_type') + ->havingRaw('COUNT(*) > 1') + ->orderBy('keep_id') + ->cursor(); + + foreach ($duplicateRows as $row) { + DB::table(self::TABLE) + ->where('id', $row->keep_id) + ->update([ + 'u' => (int) $row->total_u, + 'd' => (int) $row->total_d, + 'created_at' => (int) $row->min_created_at, + 'updated_at' => (int) $row->max_updated_at, + ]); + + DB::table(self::TABLE) + ->where('user_id', $row->user_id) + ->where('server_rate', $row->server_rate) + ->where('server_id', $row->server_id) + ->where('server_type', $row->server_type) + ->where('record_at', $row->record_at) + ->where('record_type', $row->record_type) + ->where('id', '!=', $row->keep_id) + ->delete(); + } + } };