Files
wh-api/app/Service/PermissionService.php
ykxiao 8a3ab17b25
Some checks failed
Build Docker / build (push) Has been cancelled
增加基础配置
2025-07-12 11:59:33 +08:00

288 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Author: ykxiao
* Date: 2025/7/10
* Time: 下午5:17
* Description:
*
* (c) ykxiao <yk_9001@hotmail.com>
*
* 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();
}
}