all(); $data = (new Admin())->with(["roles", "department"])->orderBy("sortnumber"); if (request()->keyword) { $data = $data->where(function ($query) { $query->where("name", "like", "%" . request()->keyword . "%") ->orWhere("username", "like", "%" . request()->keyword . "%") ->orWhere("mobile", "like", "%" . request()->keyword . "%"); }); } $data = $data->orderBy($all['sort_name'] ?? 'id', $all['sort_type'] ?? 'desc')->paginate($all['page_size'] ?? 20); return $this->success($data); } /** * @OA\Get( * path="/api/admin/show", * tags={"后台管理"}, * summary="获取后台用户详情", * description="", * @OA\Parameter(name="id", in="query", @OA\Schema(type="string"), required=true, description="id"), * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"), * @OA\Response( * response="200", * description="获取后台用户" * ) * ) */ public function show() { $data = (new Admin())->with(["roles", "department"])->find(request()->id); return $this->success($data); } /** * @OA\Post( * path="/api/admin/admin/save", * tags={"后台管理"}, * summary="保存后台用户", * description="", * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"), * @OA\Parameter(name="id", in="query", @OA\Schema(type="integer"), description="菜单ID,为空表示新增,不为空表示更新"), * @OA\Parameter(name="name", in="query", @OA\Schema(type="string"), required=true, description="名称"), * @OA\Parameter(name="username", in="query", @OA\Schema(type="string"), required=true, description="用户名"), * @OA\Parameter(name="password", in="query", @OA\Schema(type="string"), description="密码,新建时请前端处理为必填,修改时留空表示不修改"), * @OA\Parameter(name="mobile", in="query", @OA\Schema(type="string"), description="手机号码"), * @OA\Parameter(name="department_id", in="query", @OA\Schema(type="integer"), description="所属部门ID"), * @OA\Parameter(name="position", in="query", @OA\Schema(type="string"), description="职位"), * @OA\Parameter(name="birthday", in="query", @OA\Schema(type="string"), description="生日"), * @OA\Parameter(name="email", in="query", @OA\Schema(type="string"), description="邮箱"), * @OA\Parameter(name="status", in="query", @OA\Schema(type="string"), description="状态0禁用1启用"), * @OA\Parameter(name="sortnumber", in="query", @OA\Schema(type="integer"), description="同级排序,默认为0"), * @OA\Response( * response="200", * description="保存后台用户" * ) * ) */ public function save() { if (request()->id) { return $this->update(); } else { return $this->store(); } } public function store() { DB::beginTransaction(); try { $data = (new Admin())->filterRequestColumns(request(), ["id"]); $model = Admin::create($data); DB::commit(); // 加日志 OperateLog::addLogs($this->getUser(), "新增管理员[{$model->name}]成功"); return $this->success($model); } catch (\Exception $exception) { DB::rollBack(); // 加日志 OperateLog::addLogs($this->getUser(), "新增管理员失败", $exception->getMessage()); return $this->fail([$exception->getCode(), $exception->getMessage()]); } } public function update() { DB::beginTransaction(); $model = Admin::find(request()->id); try { $data = $model->filterRequestColumns(request(), ["id"]); $model->update($data); DB::commit(); // 加日志 OperateLog::addLogs($this->getUser(), "更新管理员[{$model->name}]信息成功"); return $this->success($model); } catch (\Exception $exception) { DB::rollBack(); // 加日志 OperateLog::addLogs($this->getUser(), "更新管理员[{$model->name}]信息失败", $exception->getMessage()); return $this->fail([$exception->getCode(), $exception->getMessage()]); } } /** * @OA\Post( * path="/api/admin/admin/delete", * tags={"后台管理"}, * summary="删除后台用户", * description="", * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"), * @OA\Parameter(name="id", in="query", @OA\Schema(type="integer"), description="ID"), * @OA\Response( * response="200", * description="删除后台用户" * ) * ) */ public function delete(Request $request) { $admin = Admin::find($request->id); try { $admin->delete(); // 加日志 OperateLog::addLogs($this->getUser(), "删除管理员[{$admin->name}]用户成功"); return $this->success("删除成功"); } catch (\Exception $exception) { // 加日志 OperateLog::addLogs($this->getUser(), "删除管理员[{$admin->name}]失败", $exception->getMessage()); return $this->fail([$exception->getCode(), $exception->getMessage()]); } } /** * @OA\Post( * path="/api/admin/admin/set-roles", * tags={"后台管理"}, * summary="后台用户授权", * description="", * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"), * @OA\Parameter(name="id", in="query", @OA\Schema(type="integer"), required=true, description="ID"), * @OA\Parameter(name="role_id", in="query", @OA\Schema(type="object"), required=true, description="角色id,数组形式"), * @OA\Response( * response="200", * description="后台用户授权" * ) * ) */ public function setRoles(Request $request) { $admin = (new Admin())->find($request->id); $roles = (new Role())->whereIn("id", (array)$request->role_id)->get(); try { $admin->syncRoles($roles); // 加日志 OperateLog::addLogs($this->getUser(), "用户[{$admin->name}]授权成功"); return $this->success("授权成功"); } catch (\Exception $exception) { // 加日志 OperateLog::addLogs($this->getUser(), "用户[{$admin->name}]授权失败", $exception->getMessage()); return $this->fail([$exception->getCode(), $exception->getMessage()]); } } /** * @OA\Post( * path="/api/admin/admin/set-roles-many", * tags={"后台管理"}, * summary="后台用户授权批量处理", * description="", * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"), * @OA\Parameter(name="ids", in="query", @OA\Schema(type="object"), required=true, description="用户id数组"), * @OA\Parameter(name="role_id", in="query", @OA\Schema(type="object"), required=true, description="角色id"), * @OA\Response( * response="200", * description="后台用户授权" * ) * ) */ public function setRolesMany(Request $request) { $ids = $request->get('ids', []); DB::beginTransaction(); try { $model_has_roles = config("permission.table_names.model_has_roles"); $admin_roles = []; foreach ($ids as $id) { $admin_roles[] = [ "model_type" => Admin::class, "model_id" => $id, "role_id" => $request->role_id ]; } DB::table($model_has_roles)->where('role_id', $request->role_id)->where('model_type', Admin::class)->delete(); DB::table($model_has_roles)->insert($admin_roles); // 加日志 OperateLog::addLogs($this->getUser(), '用户批量授权成功'); DB::commit(); return $this->success("授权成功"); } catch (\Exception $exception) { DB::rollBack(); // 加日志 OperateLog::addLogs($this->getUser(), '用户批量授权失败', $exception->getMessage()); return $this->fail([$exception->getCode(), $exception->getMessage()]); } } public function importPreview(Request $request) { try { $rows = $this->parseImportRows($request); $previewRows = []; $existsCount = 0; $missingDepartmentRows = []; foreach ($rows as $row) { if ($row['exists']) { $existsCount++; } if (!$row['department_exists']) { $missingDepartmentRows[] = $row['line_no']; } $previewRows[] = [ 'line_no' => $row['line_no'], 'name' => $row['name'], 'username' => $row['username'], 'mobile' => $row['mobile'], 'department' => $row['department'], 'position' => $row['position'], 'birthday' => $row['birthday'], 'email' => $row['email'], 'password_filled' => $row['password_filled'], 'department_exists' => $row['department_exists'], 'exists' => $row['exists'], 'message' => $row['message'], ]; } return $this->success([ 'rows' => $previewRows, 'exists_count' => $existsCount, 'missing_department_rows' => $missingDepartmentRows, 'can_import' => count($missingDepartmentRows) === 0, ]); } catch (\Exception $exception) { return $this->fail([$exception->getCode(), $exception->getMessage()]); } } public function importSubmit(Request $request) { try { $rows = $this->parseImportRows($request); $forceUpdate = (bool)$request->get('force_update', false); $missingDepartmentNames = []; $existsUsernames = []; foreach ($rows as $row) { if (!$row['department_exists']) { $missingDepartmentNames[] = $row['department']; } if ($row['exists']) { $existsUsernames[] = $row['username']; } } if (!empty($missingDepartmentNames)) { $missingDepartmentNames = array_values(array_unique($missingDepartmentNames)); return $this->fail([ResponseCode::ERROR_BUSINESS, '部门不存在:' . implode('、', $missingDepartmentNames) . ',请先创建部门后再导入']); } if (!$forceUpdate && !empty($existsUsernames)) { $existsUsernames = array_values(array_unique($existsUsernames)); return $this->fail([ResponseCode::ERROR_BUSINESS, '用户' . implode('、', $existsUsernames) . '已存在,是否直接更新?']); } DB::beginTransaction(); $created = 0; $updated = 0; foreach ($rows as $row) { $data = [ 'name' => $row['name'], 'username' => $row['username'], 'department_id' => $row['department_id'], 'mobile' => $row['mobile'], 'position' => $row['position'] ?: null, 'birthday' => $row['birthday'], 'email' => $row['email'] ?: null, ]; $model = Admin::where('username', $row['username'])->first(); if ($model) { if (!empty($row['password_plain'])) { $data['password'] = Hash::make($row['password_plain']); } $model->update($data); $updated++; } else { if (!empty($row['password_plain'])) { $data['password'] = Hash::make($row['password_plain']); } else { $data['password'] = Hash::make('Admin' . date('Y')); } Admin::create($data); $created++; } } DB::commit(); return $this->success([ 'created' => $created, 'updated' => $updated, 'total' => count($rows), ]); } catch (\Exception $exception) { if (DB::transactionLevel() > 0) { DB::rollBack(); } return $this->fail([$exception->getCode(), $exception->getMessage()]); } } // 保留旧接口兼容,默认按“允许更新已存在用户”执行 public function import(Request $request) { $request->merge(['force_update' => 1]); return $this->importSubmit($request); } private function parseImportRows(Request $request) { $file = $request->file('file'); if (!($request->hasFile('file') && $file->isValid())) { throw new \Exception('文件不存在或无效', ResponseCode::ERROR_BUSINESS); } $sizeKb = floor($file->getSize() / 1024); if ($sizeKb >= 5 * 1024) { throw new \Exception('文件必须小于5M', ResponseCode::ERROR_BUSINESS); } $ext = strtolower($file->getClientOriginalExtension()); if (!in_array($ext, ['xls', 'xlsx', 'csv'])) { throw new \Exception('仅支持xls/xlsx/csv格式', ResponseCode::ERROR_BUSINESS); } $dataArray = (new FastExcel)->import($file->getRealPath())->toArray(); if (empty($dataArray)) { throw new \Exception('导入文件为空', ResponseCode::ERROR_BUSINESS); } $keyList = array_keys($dataArray[0]); $requiredHeaders = ['姓名', '用户名', '手机号', '所属部门']; foreach ($requiredHeaders as $header) { if (!in_array($header, $keyList)) { throw new \Exception($header . '字段不存在', ResponseCode::ERROR_BUSINESS); } } $rows = []; foreach ($dataArray as $index => $value) { $name = trim((string)($value['姓名'] ?? '')); $username = trim((string)($value['用户名'] ?? '')); $mobile = trim((string)($value['手机号'] ?? '')); $department = trim((string)($value['所属部门'] ?? '')); $position = trim((string)($value['职位'] ?? '')); $birthday = $this->normalizeImportBirthday($value['生日'] ?? null); $email = trim((string)($value['邮箱'] ?? '')); $passwordPlain = trim((string)($value['密码'] ?? '')); $passwordFilled = $passwordPlain !== ''; if ($username === '' && $department === '' && $name === '' && $mobile === '') { continue; } if ($username === '' || $department === '' || $name === '' || $mobile === '') { throw new \Exception('第' . ($index + 2) . '行数据不完整(姓名/用户名/手机号/所属部门为必填)', ResponseCode::ERROR_BUSINESS); } $departmentId = Department::where('name', $department)->value('id'); $exists = Admin::where('username', $username)->exists(); $message = ''; if (!$departmentId) { $message = '请先创建部门'; } elseif ($exists) { $message = '用户' . $username . '已存在,是否直接更新'; } $rows[] = [ 'line_no' => $index + 2, 'username' => $username, 'department' => $department, 'department_id' => $departmentId, 'department_exists' => !empty($departmentId), 'name' => $name, 'mobile' => $mobile, 'position' => $position, 'birthday' => $birthday, 'email' => $email, 'password_plain' => $passwordPlain, 'password_filled' => $passwordFilled, 'exists' => $exists, 'message' => $message, ]; } if (empty($rows)) { throw new \Exception('导入文件无有效数据', ResponseCode::ERROR_BUSINESS); } return $rows; } /** * 生日:支持 yyyy-MM-dd 文本或 Excel 日期序列号。 */ private function normalizeImportBirthday($value): ?string { if ($value === null || $value === '') { return null; } if (is_numeric($value)) { $excel = (float)$value; if ($excel > 20000 && $excel < 60000) { $unix = (int)(($excel - 25569) * 86400); return gmdate('Y-m-d', $unix); } } $s = trim((string)$value); if ($s === '') { return null; } if (preg_match('/^\d{4}-\d{2}-\d{2}/', $s)) { return substr($s, 0, 10); } return $s; } }