model = RoleModel::class; $this->selectField = ['id', 'name', 'value', 'pid', 'desc', 'status', 'color', 'created_at']; $this->queryField = ['name' => 'like', 'value' => 'like', 'pid' => '=', 'status' => '=']; $this->orderBy = ['name' => 'id', 'sort' => 'asc']; } /** * 获取列表 * @return array * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ public function list(): array { return $this->getPageList(); } /** * 获取下拉列表 * @return mixed */ public function option(): mixed { return $this->getOption(); } /** * 根据角色ID获取菜单ID * @param $roleId * @return mixed * @throws Exception */ public function getMenuIdsByRoleIds($roleId): mixed { if (empty($roleId)) $this->utils->errorThrow('请选择角色'); return RoleMenuRelationModel::where('role_id', $roleId)->pluck('menu_id'); } /** * 保存角色菜单关系 * @param $roleId * @param $menuIds * @return true * @throws Exception */ public function saveRoleMenu($roleId, $menuIds): true { $menus = array_unique($menuIds); // 查询角色当前绑定的菜单 $bindResources = RoleMenuRelationModel::where('role_id', $roleId)->pluck('menu_id'); // 开启事务 DB::beginTransaction(); try { // 提取查询到的菜单ID $existingResourceIds = $bindResources->toArray(); // 传值中不存在的菜单ID,从数据库中删除 $menusToDelete = array_diff($existingResourceIds, $menus); if (!empty($menusToDelete)) { $delBindResources = RoleMenuRelationModel::where('role_id', $roleId)->whereIn('menu_id', array_values($menusToDelete))->delete(); if (!$delBindResources) { $this->utils->errorThrow('更新失败!'); } } // 传值中存在但数据库中不存在的菜单ID,新增到数据库 $menusToAdd = array_diff($menus, $existingResourceIds); $insertData = []; foreach ($menusToAdd as $menusId) { $insertData[] = [ 'role_id' => $roleId, 'menu_id' => $menusId, 'created_at' => time() ]; } // 只取消勾选、没有新增时 $insertData 为空,insert([]) 返回 false 会误判成失败 if (!empty($insertData) && !RoleMenuRelationModel::insert($insertData)) { $this->utils->errorThrow('更新失败!'); } DB::commit(); $this->flushRoleCache((int) $roleId); } catch (\Exception $e) { DB::rollBack(); $this->utils->errorThrow($e->getMessage()); } return true; } /** * 接口授权树 */ public function endpointTree(): array { return PermissionService::getInstance()->endpointTree(); } /** * 角色已授权的接口 id * @throws Exception */ public function getEndpointIdsByRoleId($roleId): array { if (empty($roleId)) $this->utils->errorThrow('请选择角色'); return PermissionService::getInstance()->grantedIds((int) $roleId); } /** * 保存角色接口授权 * @throws Exception */ public function saveRoleEndpoint(int $roleId, array $endpointIds): bool { if ($roleId <= 0) $this->utils->errorThrow('请选择角色'); if ($roleId === 1) $this->utils->errorThrow('超级管理员默认拥有全部接口权限,无需授权'); return PermissionService::getInstance()->grant($roleId, $endpointIds); } /** * 启用 / 停用角色 * * 停用后该角色下的账号仍能登录但权限为空,所以要顺手清掉权限与菜单缓存 * @throws Exception */ public function status($id, $status): mixed { $id = (int) $id; if ($id === 1) $this->utils->errorThrow('超级管理员角色禁止停用'); $result = $this->save($id, ['status' => (int) $status]); $this->flushRoleCache($id); return $result; } /** * 获取数据详情 * @param $id * @return mixed * @throws Exception */ public function detail($id): mixed { return $this->getDetail($id); } /** * 新增数据 * @param $params * @return mixed */ public function create($params): mixed { return $this->insert($params); } /** * 编辑数据 * @param $id * @param $params * @return mixed * @throws Exception */ public function update($id, $params): mixed { return $this->save($id, $params); } /** * 删除数据 * @param $id * @return mixed|true * @throws Exception */ public function delete($id): mixed { // BaseController::delete 传入的是 ids 数组,直接 in_array($id,[1,2]) 会因「数组与标量比较恒 false」绕过保护, // 这里统一归一为数组后用 array_intersect 判断是否命中内置角色(1超管 / 2默认) $ids = is_array($id) ? $id : [$id]; if (array_intersect(array_map('intval', $ids), [1, 2])) { $this->utils->errorThrow('管理员角色禁止删除'); } $result = $this->del($id); foreach ($ids as $roleId) { $this->flushRoleCache((int) $roleId); } return $result; } /** * 角色的菜单与权限都按角色缓存,改动后必须一起清,否则要等 1 小时才生效 */ private function flushRoleCache(int $roleId): void { RedisService::getInstance()->init(config('nl.redis.menu_key'))->del($roleId); PermissionService::getInstance()->clear($roleId); } }