From d8c404f960a8b5bf7fb60ec083a6fdcaa5c77f25 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Tue, 20 Jan 2026 13:37:03 +0800 Subject: [PATCH] update --- .../Commands/MergeDuplicateUsersByMobile.php | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 app/Console/Commands/MergeDuplicateUsersByMobile.php diff --git a/app/Console/Commands/MergeDuplicateUsersByMobile.php b/app/Console/Commands/MergeDuplicateUsersByMobile.php new file mode 100644 index 0000000..258b7ef --- /dev/null +++ b/app/Console/Commands/MergeDuplicateUsersByMobile.php @@ -0,0 +1,194 @@ +option('dry-run'); + if ($dryRun) { + $this->warn('【预检模式】不会修改数据库'); + } + + $duplicateGroups = $this->getDuplicateMobileUsers(); + if ($duplicateGroups->isEmpty()) { + $this->info('没有需要合并的重复手机号。'); + return 0; + } + + $this->info('共 ' . $duplicateGroups->count() . ' 组重复手机号待处理。'); + + $merged = 0; + $failed = 0; + + foreach ($duplicateGroups as $mobile => $users) { + // 按 id 升序,id 最大为“新”,保留;其余为“旧”,合并后软删 + $sorted = $users->sortBy('id')->values(); + $newUser = $sorted->last(); + $oldUsers = $sorted->slice(0, -1); + + $newId = (int) $newUser->id; + $oldIds = $oldUsers->pluck('id')->map(fn($v) => (int) $v)->toArray(); + + $this->line(''); + $this->line("手机号: {$mobile} | 保留用户 id={$newId} ({$newUser->name}), 合并并删除: " . implode(', ', $oldIds)); + + if ($dryRun) { + $this->listTransfers($newId, $oldIds); + $merged++; + continue; + } + + try { + DB::transaction(function () use ($newId, $oldIds) { + foreach ($oldIds as $oldId) { + $this->transferRelations($oldId, $newId); + } + User::whereIn('id', $oldIds)->delete(); // 软删除 + }); + $this->info(" 已合并并软删除旧用户: " . implode(', ', $oldIds)); + $merged++; + } catch (\Throwable $e) { + $this->error(" 失败: " . $e->getMessage()); + $failed++; + } + } + + $this->line(''); + $this->info("完成: 成功 {$merged} 组" . ($failed > 0 ? ", 失败 {$failed} 组" : '') . ($dryRun ? '(未写入)' : '')); + return $failed > 0 ? 1 : 0; + } + + /** + * 查询:有报名审核通过且手机号重复的用户,按手机号分组 + */ + protected function getDuplicateMobileUsers() + { + $sql = " + SELECT u.id, u.name, u.mobile + FROM users u + WHERE u.deleted_at IS NULL + AND u.mobile IS NOT NULL AND TRIM(u.mobile) != '' + AND EXISTS ( + SELECT 1 FROM course_signs cs + WHERE cs.user_id = u.id AND cs.status = 1 AND cs.deleted_at IS NULL + ) + AND u.mobile IN ( + SELECT u2.mobile + FROM users u2 + INNER JOIN course_signs cs2 ON cs2.user_id = u2.id AND cs2.status = 1 AND cs2.deleted_at IS NULL + WHERE u2.deleted_at IS NULL + AND u2.mobile IS NOT NULL AND TRIM(u2.mobile) != '' + GROUP BY u2.mobile + HAVING COUNT(DISTINCT u2.id) > 1 + ) + ORDER BY u.mobile, u.id + "; + $rows = DB::select($sql); + return collect($rows)->groupBy('mobile'); + } + + /** + * 把 oldUserId 的关联全部改为 newUserId + */ + protected function transferRelations(int $oldUserId, int $newUserId): void + { + foreach ($this->tablesWithUserId as $table) { + if (!Schema::hasTable($table) || !Schema::hasColumn($table, 'user_id')) { + continue; + } + $n = DB::table($table)->where('user_id', $oldUserId)->update(['user_id' => $newUserId]); + if ($n > 0) { + $this->line(" {$table}.user_id: {$oldUserId} -> {$newUserId}, 更新 {$n} 行"); + } + } + + foreach ($this->tablesWithToUserId as $table) { + if (!Schema::hasTable($table)) { + continue; + } + if (Schema::hasColumn($table, 'user_id')) { + $n = DB::table($table)->where('user_id', $oldUserId)->update(['user_id' => $newUserId]); + if ($n > 0) { + $this->line(" {$table}.user_id: {$oldUserId} -> {$newUserId}, 更新 {$n} 行"); + } + } + if (Schema::hasColumn($table, 'to_user_id')) { + $n = DB::table($table)->where('to_user_id', $oldUserId)->update(['to_user_id' => $newUserId]); + if ($n > 0) { + $this->line(" {$table}.to_user_id: {$oldUserId} -> {$newUserId}, 更新 {$n} 行"); + } + } + } + } + + /** + * dry-run:只统计并打印每个表将更新的行数 + */ + protected function listTransfers(int $newUserId, array $oldIds): void + { + foreach ($oldIds as $oldId) { + foreach ($this->tablesWithUserId as $table) { + if (!Schema::hasTable($table) || !Schema::hasColumn($table, 'user_id')) { + continue; + } + $n = DB::table($table)->where('user_id', $oldId)->count(); + if ($n > 0) { + $this->line(" [拟] {$table}.user_id: {$oldId} -> {$newUserId}, 约 {$n} 行"); + } + } + foreach ($this->tablesWithToUserId as $table) { + if (!Schema::hasTable($table)) { + continue; + } + if (Schema::hasColumn($table, 'user_id')) { + $n = DB::table($table)->where('user_id', $oldId)->count(); + if ($n > 0) { + $this->line(" [拟] {$table}.user_id: {$oldId} -> {$newUserId}, 约 {$n} 行"); + } + } + if (Schema::hasColumn($table, 'to_user_id')) { + $n = DB::table($table)->where('to_user_id', $oldId)->count(); + if ($n > 0) { + $this->line(" [拟] {$table}.to_user_id: {$oldId} -> {$newUserId}, 约 {$n} 行"); + } + } + } + } + } +}