* * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ declare(strict_types=1); namespace App\Service; use App\Constants\RoleTypeConst; use App\Context\UserContext; use App\Exception\ApiException; use App\Repository\Company\CompanyModulesRepository; use Exception; use Hyperf\Config\Annotation\Value; use Hyperf\Di\Annotation\Inject; use Psr\Http\Message\ServerRequestInterface; use function Hyperf\Collection\collect; class PermissionService { #[Value('menu.data_permissions')] protected array $dataPermissions; // 跳过验证的菜单 #[Value('menu.ignore_menu')] protected array $ignoreMenu; #[Inject] protected CompanyModulesRepository $companyModulesRepository; #[Inject] protected SysService $sysService; #[Inject] protected RelatedInfo $relatedInfo; /** * 验证用户是否有权限执行当前操作. * * @param ServerRequestInterface $request 表示当前的服务器请求 * @return bool 如果用户有权限,则返回true;否则返回false * @throws Exception 如果用户没有登录,则抛出一个异常 */ public function verifyUserPermission(ServerRequestInterface $request): bool { $user = UserContext::getCurrentUser(); return match ($user['role_type']) { RoleTypeConst::SUPER_ADMIN => true, RoleTypeConst::ADMIN => $this->sysService->verifyPlatformAdmin($user, $request), default => $this->checkUserRequestPermission($user, $request), }; } /** * @param array $user * @param ServerRequestInterface $request * @return bool * @throws Exception */ protected function checkUserRequestPermission(array $user, ServerRequestInterface $request): bool { $method = $this->sysService->method($request); if (!$method) { return false; } $dataPermissions = $this->sysService->dataPermissions($user); $actions = array_column($dataPermissions, 'actions'); $roleActions = $this->sysService->roleAction($user); return in_array($method, $roleActions, true) || in_array($method, $actions, true); } /** * 返回权限选中列表。 * 根据提供的权限码和菜单数据,检查并返回用户有权限访问的菜单树。 * * @param array $permissionCode 包含角色权限码和数据权限码的数组。 * 'role_permission_code' 键包含角色的权限码, * 'data_permission_code' 键包含数据权限的详情(如果有的话)。 * @return array 返回经过权限检查后的菜单树 */ public function checkMenu(array $permissionCode): array { $user = UserContext::getCurrentUser(); $menu = match ($user['role_type']) { RoleTypeConst::SUPER_ADMIN => $this->adminMenu()['menu'], RoleTypeConst::ADMIN => $this->platformAdminMenu()['menu'], default => $this->userMenu($user)['menu'], }; $menuList = treeToList($menu); $menuPermissions = $permissionCode['role_permission_code'] ?? []; $dataPermissions = collect($permissionCode['data_permission_code'] ?? []) ->pluck('actions') ->toArray(); checkPermissions($menuList, $menuPermissions, $dataPermissions); return listToTree($menuList); } /** * 获取指定用户的权限列表。 * * @param mixed $user 用户信息,预期包含is_admin和is_platform字段来区分用户类型 * @return array 返回一个包含菜单权限 menu_permissions 和数据权限 data_permissions 的数组 */ public function myPermissions(mixed $user): array { return match ($user['role_type']) { RoleTypeConst::SUPER_ADMIN => $this->formatPermissions($this->adminMenu()['permissions']), RoleTypeConst::ADMIN => $this->formatPermissions($this->platformAdminMenu()['permissions']), default => [ 'menu_permissions' => $this->sysService->menuPermissions($user), 'data_permissions' => $this->sysService->dataPermissions($user), ], }; } protected function formatPermissions(array $permissions): array { return [ 'menu_permissions' => $permissions['menu_permissions'], 'data_permissions' => mergePermissions($permissions['data_permissions']), ]; } /** * 获取超级管理员的菜单和权限配置。 * 该函数不接受任何参数。 * * @return array 返回一个包含菜单结构和权限信息的数组。 * 其中' menu'键包含经过树化处理的菜单结构, * 'permissions'键包含两个子项:'menu_permissions'和'data_permissions'。 * 'menu_permissions'表示菜单权限代码数组, * 'data_permissions'表示数据权限数组。 */ public function adminMenu(): array { $menu = (new SysService())->getDefaultMenu(); $menu_permissions = collect($menu)->pluck('meta.code')->toArray(); $data_permissions = array_values($this->dataPermissions); return [ 'menu' => listToTree($menu), 'permissions' => compact('menu_permissions', 'data_permissions'), ]; } /** * 生成公司管理员的功能菜单模块. * * 根据提供的模块代码数组,筛选出对应菜单项,并将其转换为树状结构,同时返回菜单项的权限信息。 * * @return array 返回一个包含菜单树结构和权限信息的数组 */ public function platformAdminMenu(): array { $user = UserContext::getCurrentUser(); if (empty($user)) { throw new ApiException('当前用户信息获取失败'); } $companyModule = $this->companyModulesRepository->codeList($user['company']['id']); $moduleCode = $companyModule['role_permission_code']; $company_data_permissions = array_column($companyModule['data_permission_code'] ?? [], 'actions'); // 获取系统默认菜单 $menu = (new SysService())->getDefaultMenu(); $filteredMenu = $this->checkSysMenu($menu, $moduleCode); // 筛选出当前模块代码对应的菜单项,并提取出菜单代码 $menu_permissions = collect($filteredMenu)->pluck('meta.code')->toArray(); // 获取数据权限 $data_permission = $this->dataPermissions; // 将数据权限整理为键值对形式的数组 $data_permissions = []; foreach ($data_permission as $value) { $value = collect($value)->whereIn('actions', $company_data_permissions)->values()->toArray(); if (empty($value)) { continue; } $data_permissions[] = $value; } // 返回处理后的菜单树和权限信息 return [ 'menu' => listToTree($filteredMenu), // 转换菜单项为树状结构 'permissions' => compact('menu_permissions', 'data_permissions'), // 权限信息 ]; } /** * 根据用户角色获取普通用户菜单权限。 * * @param mixed $user 用户信息,预期为包含角色权限信息的对象 * @return array 返回包含菜单结构和权限信息的数组。 * 其中: * - 'menu' 键包含经过树化处理的菜单结构。 * - 'permissions' 键包含两个子项: * - 'menu_permissions' 用户的角色菜单权限代码集合。 * - 'data_permissions' 用户的数据权限信息。 */ public function userMenu(mixed $user): array { $rolePermissions = $this->relatedInfo->getRolePermissions($user['id']); $data_permissions = collect($this->relatedInfo->dataPermissions($user['id']))->pluck('data_permissions')->toArray(); $menu_permissions = array_unique(array_merge( collect($rolePermissions)->pluck('role_permission_code')->collapse()->unique()->values()->toArray(), $this->ignoreMenu )); $menu = (new SysService())->getDefaultMenu(); $filteredMenu = $this->checkSysMenu($menu, $menu_permissions); return [ 'menu' => listToTree($filteredMenu), 'permissions' => compact('menu_permissions', 'data_permissions'), ]; } /** * 合并处理权限CODE。 * 该函数接收一个包含菜单信息的参数,并从中提取权限码,然后合并这些权限码。 * * @param mixed $menu 包含菜单权限和数据权限信息的数组或对象 * @return array 返回一个包含角色权限码和数据权限码的关联数组 */ public function mergeCode(mixed $menu): array { // 提取菜单权限码 $role_permission_code = $menu['menu_permissions'] ?? []; // 提取数据权限码 $data_permissions = $menu['data_permissions'] ?? []; $data_permission_code = []; foreach ($data_permissions as $value) { $data_permission_code[] = $value; // 将数据权限码添加到数组中 } // 合并数据权限码 $data_permission_code = mergePermissions($data_permission_code); // 返回合并后的权限码 return compact('role_permission_code', 'data_permission_code'); } /** * 获取用户的菜单权限。 * 该函数接收一个用户信息数组作为参数,并返回该用户的菜单权限。 * * @param array $user 用户信息数组 * @return array 返回用户的菜单权限。 * @throws Exception */ public function getMenuByUserType(array $user): array { return match (true) { $user['role_type'] === RoleTypeConst::SUPER_ADMIN => $this->adminMenu(), $user['role_type'] === RoleTypeConst::ADMIN => $this->platformAdminMenu(), default => $this->userMenu($user), }; } /** * 筛选出当前模块代码对应的菜单项,转换为数组。 */ public function checkSysMenu(array $menu, array $module_code): array { // 非平台用户的菜单筛选 return collect($menu) ->whereIn('meta.code', $module_code) ->toArray(); } }