From 7885c1581dcf4802f8eedec20d45f73faac0579f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Mar 2026 19:57:50 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[user]=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=98=81=EA=B5=AC=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20FK=20?= =?UTF-8?q?=EC=A0=9C=EC=95=BD=20=EC=9C=84=EB=B0=98=20500=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - users.id를 참조하는 모든 FK를 information_schema에서 동적 조회 - NULLABLE FK → NULL 설정, NOT NULL FK → 관련 행 삭제 - 기존 5개 테이블만 처리하던 것을 전체 FK 대응으로 확장 --- app/Services/UserService.php | 47 +++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/app/Services/UserService.php b/app/Services/UserService.php index db786fc0..e84be546 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -384,25 +384,48 @@ public function forceDeleteUser(int $id): bool // 1. 아카이브에 저장 (복원 가능하도록) $this->archiveService->archiveUser($user); - // 2. 관련 데이터 삭제 - $user->tenants()->detach(); // user_tenants 관계 삭제 - - // 2-1. user_roles 영구 삭제 (외래 키 제약 때문에 forceDelete 필요) + // 2. 피벗/소유 테이블 삭제 + $user->tenants()->detach(); DB::table('user_roles')->where('user_id', $user->id)->delete(); - - // 2-2. department_user 영구 삭제 DB::table('department_user')->where('user_id', $user->id)->delete(); - - // 2-3. sales_partners 삭제 (영업파트너) DB::table('sales_partners')->where('user_id', $user->id)->delete(); - - // 2-4. sales_manager_documents 삭제 (영업파트너 서류) DB::table('sales_manager_documents')->where('user_id', $user->id)->delete(); + DB::table('login_tokens')->where('user_id', $user->id)->delete(); + DB::table('notification_settings')->where('user_id', $user->id)->delete(); + DB::table('notification_setting_group_states')->where('user_id', $user->id)->delete(); - // 2-5. 하위 사용자의 parent_id 해제 + // 3. 하위 사용자의 parent_id 해제 User::where('parent_id', $user->id)->update(['parent_id' => null]); - // 3. 사용자 영구 삭제 + // 4. DB에서 users.id를 참조하는 모든 FK를 동적 조회 → NULL 또는 삭제 + $dbName = DB::getDatabaseName(); + $fkRefs = DB::select(" + SELECT kcu.TABLE_NAME, kcu.COLUMN_NAME, c.IS_NULLABLE + FROM information_schema.KEY_COLUMN_USAGE kcu + JOIN information_schema.COLUMNS c + ON c.TABLE_SCHEMA = kcu.TABLE_SCHEMA + AND c.TABLE_NAME = kcu.TABLE_NAME + AND c.COLUMN_NAME = kcu.COLUMN_NAME + WHERE kcu.REFERENCED_TABLE_NAME = 'users' + AND kcu.REFERENCED_COLUMN_NAME = 'id' + AND kcu.TABLE_SCHEMA = ? + ", [$dbName]); + + $skipTables = ['user_tenants', 'user_roles', 'department_user', 'sales_partners', 'sales_manager_documents', 'login_tokens', 'notification_settings', 'notification_setting_group_states', 'users']; + + foreach ($fkRefs as $ref) { + if (in_array($ref->TABLE_NAME, $skipTables)) { + continue; + } + + if ($ref->IS_NULLABLE === 'YES') { + DB::table($ref->TABLE_NAME)->where($ref->COLUMN_NAME, $user->id)->update([$ref->COLUMN_NAME => null]); + } else { + DB::table($ref->TABLE_NAME)->where($ref->COLUMN_NAME, $user->id)->delete(); + } + } + + // 5. 사용자 영구 삭제 return $user->forceDelete(); }); }