From 81b64f25aa8f5b670be5be9bd373f2a7550b8dcf 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 21:46:37 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[hr]=20=EC=82=AC=EC=9B=90=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=ED=87=B4=EC=A7=81=EC=9E=90=20=EC=98=81=EA=B5=AC?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 슈퍼관리자만 퇴직 상태 사원을 영구삭제 가능 - 관련 첨부파일도 함께 삭제 - DELETE /admin/hr/employees/{id}/force 엔드포인트 추가 --- .../Api/Admin/HR/EmployeeController.php | 37 +++++++++++++++++++ app/Services/HR/EmployeeService.php | 28 ++++++++++++++ .../hr/employees/partials/table.blade.php | 15 ++++++++ routes/api.php | 1 + 4 files changed, 81 insertions(+) diff --git a/app/Http/Controllers/Api/Admin/HR/EmployeeController.php b/app/Http/Controllers/Api/Admin/HR/EmployeeController.php index 1e11f31d..06804c44 100644 --- a/app/Http/Controllers/Api/Admin/HR/EmployeeController.php +++ b/app/Http/Controllers/Api/Admin/HR/EmployeeController.php @@ -263,6 +263,43 @@ public function destroy(Request $request, int $id): JsonResponse|Response } } + /** + * 사원 영구삭제 (퇴직자만, 슈퍼관리자 전용) + */ + public function forceDestroy(Request $request, int $id): JsonResponse|Response + { + if (! auth()->user()?->is_super_admin) { + return response()->json([ + 'success' => false, + 'message' => '슈퍼관리자만 영구삭제할 수 있습니다.', + ], 403); + } + + try { + $result = $this->employeeService->forceDeleteEmployee($id); + + if (! $result['success']) { + return response()->json($result, 422); + } + + if ($request->header('HX-Request')) { + $employees = $this->employeeService->getEmployees($request->all(), $request->integer('per_page', 20)); + + return response(view('hr.employees.partials.table', compact('employees'))); + } + + return response()->json($result); + } catch (\Throwable $e) { + report($e); + + return response()->json([ + 'success' => false, + 'message' => '영구삭제 중 오류가 발생했습니다.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } + /** * 사원 첨부파일 업로드 (로컬 + GCS 듀얼 저장) */ diff --git a/app/Services/HR/EmployeeService.php b/app/Services/HR/EmployeeService.php index cb2602e6..18153b19 100644 --- a/app/Services/HR/EmployeeService.php +++ b/app/Services/HR/EmployeeService.php @@ -366,6 +366,34 @@ public function deleteEmployee(int $id): bool return true; } + /** + * 사원 영구 삭제 (퇴직자만, 슈퍼관리자 전용) + */ + public function forceDeleteEmployee(int $id): array + { + $employee = $this->getEmployeeById($id); + if (! $employee) { + return ['success' => false, 'message' => '사원 정보를 찾을 수 없습니다.']; + } + + if ($employee->employee_status !== 'resigned') { + return ['success' => false, 'message' => '퇴직 상태인 사원만 영구삭제할 수 있습니다.']; + } + + $name = $employee->display_name ?? $employee->user?->name ?? '알 수 없음'; + + // 관련 첨부파일 삭제 + \App\Models\Boards\File::where('document_type', 'employee_profile') + ->where('document_id', $employee->id) + ->where('tenant_id', session('selected_tenant_id')) + ->delete(); + + // tenant_user_profiles 레코드 영구 삭제 + $employee->delete(); + + return ['success' => true, 'message' => "사원 '{$name}'이(가) 영구삭제되었습니다."]; + } + /** * 부서 목록 (드롭다운용) */ diff --git a/resources/views/hr/employees/partials/table.blade.php b/resources/views/hr/employees/partials/table.blade.php index 619fee64..6d681939 100644 --- a/resources/views/hr/employees/partials/table.blade.php +++ b/resources/views/hr/employees/partials/table.blade.php @@ -143,6 +143,21 @@ class="text-red-600 hover:text-red-800" title="퇴직처리"> @endif + + {{-- 영구삭제 (퇴직자 + 슈퍼관리자만) --}} + @if($employee->employee_status === 'resigned' && auth()->user()?->is_super_admin) + + @endif diff --git a/routes/api.php b/routes/api.php index c035daea..cb3bc768 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1141,6 +1141,7 @@ Route::get('/{id}', [\App\Http\Controllers\Api\Admin\HR\EmployeeController::class, 'show'])->name('show'); Route::put('/{id}', [\App\Http\Controllers\Api\Admin\HR\EmployeeController::class, 'update'])->name('update'); Route::delete('/{id}', [\App\Http\Controllers\Api\Admin\HR\EmployeeController::class, 'destroy'])->name('destroy'); + Route::delete('/{id}/force', [\App\Http\Controllers\Api\Admin\HR\EmployeeController::class, 'forceDestroy'])->name('force-destroy'); // 첨부파일 Route::post('/{id}/files', [\App\Http\Controllers\Api\Admin\HR\EmployeeController::class, 'uploadFile'])->name('upload-file');