This commit is contained in:
288
app/Service/PermissionService.php
Normal file
288
app/Service/PermissionService.php
Normal file
@ -0,0 +1,288 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Context\ApiUrlContext;
|
||||
use App\Context\UserContext;
|
||||
use App\Job\ColumnConfigJob;
|
||||
use App\Job\OpLogsJob;
|
||||
@ -60,6 +61,10 @@ class QueueService
|
||||
// 将当前用户信息添加到参数中
|
||||
$params['user'] = UserContext::getCurrentUser();
|
||||
|
||||
if (ApiUrlContext::hasApiUrl()) {
|
||||
$params['api_url'] = ApiUrlContext::getApiUrl();
|
||||
}
|
||||
|
||||
$this->params = $params;
|
||||
return $this;
|
||||
}
|
||||
|
104
app/Service/RelatedInfo.php
Normal file
104
app/Service/RelatedInfo.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/**
|
||||
* Author: ykxiao
|
||||
* Date: 2025/6/8
|
||||
* Time: 上午11:01
|
||||
* 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\JsonRpc\RoleServiceInterface;
|
||||
use App\Repository\Company\FirstCompanyRepository;
|
||||
use Hyperf\Collection\Collection;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use function Hyperf\Collection\collect;
|
||||
|
||||
class RelatedInfo
|
||||
{
|
||||
#[Inject]
|
||||
protected RoleServiceInterface $roleServiceInterface;
|
||||
|
||||
#[Inject]
|
||||
protected FirstCompanyRepository $firstCompanyRepository;
|
||||
|
||||
/**
|
||||
* 用户角色关联.
|
||||
* @param int $uid 用户ID
|
||||
* @return array 用户角色关联信息
|
||||
*/
|
||||
public function userRoles(int $uid): array
|
||||
{
|
||||
$data = [
|
||||
'user_id' => $uid,
|
||||
];
|
||||
|
||||
$rpcResult = $this->roleServiceInterface->getUserRoleInfo($data);
|
||||
return $rpcResult['result'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 公司信息关联.
|
||||
* @param string $companyName 公司名称
|
||||
* @return array|null 公司信息关联信息
|
||||
*/
|
||||
public function companyInfo(string $companyName): ?array
|
||||
{
|
||||
return $this->firstCompanyRepository->getCompanyByFullName($companyName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 公司信息关联.
|
||||
* @param int $companyId
|
||||
* @return array
|
||||
*/
|
||||
public function companyInfoById(int $companyId): array
|
||||
{
|
||||
return $this->firstCompanyRepository->getCompanyById($companyId, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色权限.
|
||||
* @param int $uid 用户ID
|
||||
* @return Collection 角色权限集合
|
||||
*/
|
||||
public function getRolePermissions(int $uid): Collection
|
||||
{
|
||||
// 获取用户角色表中的 role_ids 数组
|
||||
$roleIds = static::userRoles($uid)['role_ids_info'] ?? [];
|
||||
$userRolesIds = array_keys($roleIds);
|
||||
|
||||
$data = [
|
||||
'role_ids' => $userRolesIds
|
||||
];
|
||||
// 通过数组中的 role_id 查找对应的角色权限
|
||||
|
||||
$rpcResult = $this->roleServiceInterface->getRolePermissionSetByRoleIds($data);
|
||||
$res = $rpcResult['result'] ?? [];
|
||||
|
||||
return collect($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户数据权限关联.
|
||||
* @param int $uid 用户ID
|
||||
* @return array 数据权限信息
|
||||
* @return array
|
||||
*/
|
||||
public function dataPermissions(int $uid): array
|
||||
{
|
||||
$data = [
|
||||
'user_id' => $uid,
|
||||
];
|
||||
|
||||
$rpcResult = $this->roleServiceInterface->getUserDataPermissions($data);
|
||||
return $rpcResult['result'] ?? [];
|
||||
}
|
||||
}
|
@ -15,7 +15,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Repository\Company\CompanyModulesRepository;
|
||||
use Hyperf\Config\Annotation\Value;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\HttpServer\Router\Dispatched;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use function Hyperf\Collection\collect;
|
||||
|
||||
/**
|
||||
* Author: ykxiao
|
||||
@ -30,6 +35,104 @@ use Psr\Http\Message\ServerRequestInterface;
|
||||
*/
|
||||
class SysService
|
||||
{
|
||||
#[Inject]
|
||||
protected RelatedInfo $relatedInfo;
|
||||
|
||||
#[Inject]
|
||||
protected CompanyModulesRepository $companyModulesRepository;
|
||||
|
||||
#[Value('menu.menu')]
|
||||
protected array $menu;
|
||||
|
||||
#[Value('menu.data_permissions')]
|
||||
protected array $dataPermissions;
|
||||
|
||||
public function getDefaultMenu(): array
|
||||
{
|
||||
$list = treeToList($this->menu);
|
||||
foreach ($list as $item => $value) {
|
||||
$code = $value['meta']['code'];
|
||||
$value['meta']['data_permission'] = [];
|
||||
if (array_key_exists($code, $this->dataPermissions)) {
|
||||
$value['meta']['data_permission'] = $this->dataPermissions[$code];
|
||||
}
|
||||
$list[$item] = $value;
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前方法名
|
||||
* @param ServerRequestInterface $request
|
||||
* @return string
|
||||
*/
|
||||
public function method(ServerRequestInterface $request): string
|
||||
{
|
||||
$dispatched = $request->getAttribute(Dispatched::class);
|
||||
|
||||
$method = null;
|
||||
if ($dispatched instanceof Dispatched) {
|
||||
$handler = $dispatched->handler->callback ?? null;
|
||||
if ($handler) {
|
||||
$method = $handler[1];
|
||||
}
|
||||
}
|
||||
return $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的角色权限。
|
||||
* @param array $user
|
||||
* @return array
|
||||
*/
|
||||
public function roleAction(array $user): array
|
||||
{
|
||||
// 获取用户角色拥有的菜单权限
|
||||
$default_menu = (new SysService())->getDefaultMenu();
|
||||
return collect($default_menu)
|
||||
->whereIn('meta.code', $this->menuPermissions($user))
|
||||
->pluck('actions')
|
||||
->collapse()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的菜单权限。
|
||||
*
|
||||
* @param mixed $user 用户信息,预期包含用户ID
|
||||
* @return array 返回一个包含用户所有菜单权限的数组
|
||||
*/
|
||||
public function menuPermissions(mixed $user): array
|
||||
{
|
||||
// 获取用户角色的所有权限
|
||||
$role_permissions = $this->relatedInfo->getRolePermissions($user['id']);
|
||||
|
||||
// 收集角色权限码,去除重复,转换为数组格式后返回
|
||||
return $role_permissions
|
||||
->pluck('role_permission_code')
|
||||
->collapse()
|
||||
->unique()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单权限。
|
||||
* 该函数用于获取指定用户的菜单权限。
|
||||
*
|
||||
* @param mixed $user 用户信息,预期为包含用户ID的数组或对象
|
||||
* @return array 返回一个包含用户菜单权限动作的数组
|
||||
*/
|
||||
public function dataPermissions(mixed $user): array
|
||||
{
|
||||
$dataPermissions = $this->relatedInfo->dataPermissions($user['id']);
|
||||
|
||||
// 获取并转换用户的所有数据权限
|
||||
return collect($dataPermissions)
|
||||
->pluck('data_permissions')
|
||||
->collapse()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端IP信息
|
||||
* @param ServerRequestInterface $request
|
||||
@ -45,4 +148,31 @@ class SysService
|
||||
|
||||
return $clientIP ?: '127.0.0.1';
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证平台管理员权限。
|
||||
*/
|
||||
public function verifyPlatformAdmin(mixed $user, mixed $request): bool
|
||||
{
|
||||
$companyInfo = $user['company_info'];
|
||||
$module = $this->companyModulesRepository->codeList($companyInfo['id']);
|
||||
|
||||
// 菜单权限
|
||||
$default_menu = (new SysService())->getDefaultMenu();
|
||||
$role_permissions_result = collect($default_menu)
|
||||
->whereIn('meta.code', $module['role_permission_code'])
|
||||
->flatMap(fn($item) => $item['actions'])
|
||||
->toArray();
|
||||
|
||||
$data_permissions_result = collect($module['data_permission_code'])
|
||||
->pluck('actions')
|
||||
->toArray();
|
||||
|
||||
$method = $this->method($request);
|
||||
if ($method) {
|
||||
return in_array($method, $role_permissions_result, true) || in_array($method, $data_permissions_result, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user