Files

346 lines
11 KiB
PHP
Raw Permalink Normal View History

2026-08-31 11:33:59 +08:00
<?php
declare(strict_types=1);
/**
*+------------------
* madong
*+------------------
* Copyright (c) https://gitee.com/motion-code All rights reserved.
*+------------------
* Author: Mr. April (405784684@qq.com)
*+------------------
* Official Website: http://www.madong.tech
*/
namespace app\service\admin\system;
use app\dao\system\RoleDao;
use app\model\system\Role;
use core\base\BaseService;
use core\exception\handler\AdminException;
use madong\helper\Arr;
use madong\helper\PropertyCopier;
use support\Container;
class RoleService extends BaseService
{
/**
* 缓存key
*/
const CACHE_ALL_ROLES_DATA = 'roles_all_data';
public function __construct(RoleDao $dao)
{
$this->dao = $dao;
}
/**
* 获取所有的角色
*
* @param array $where
*
* @return array
* @throws \Exception
*/
public function getAllRoles(array $where = []): array
{
// 1. 优先从缓存获取所有角色数据
$allRoles = $this->cacheDriver()->remember(
self::CACHE_ALL_ROLES_DATA, // 缓存键名
function () {
$results = $this->dao->getModel()
->where('enabled', 1)
->orderBy('sort', 'asc')
->get(["id",
"pid",
"name",
"code",
"is_super_admin",
"role_type",
"data_scope",
"enabled",
"sort"]);
return $results->toArray();
}
);
// 2.$where 条件筛数据
return Arr::filterByWhere($allRoles, $where);
}
/**
* 传入角色id获取权限
*
* @param array $where
* @param string $mode merge|unique
*
* @return array
* @throws \Exception
*/
public function getPermissions(array $where = [], string $mode = 'merge'): array
{
// 1. 优先从缓存获取所有全量角色数据
$data = $this->cacheDriver()->remember(
self::CACHE_ALL_ROLES_DATA, // 缓存键名
function () {
return $this->dao->getRolePermissions();
}
);
//2. 条件过滤
$result = Arr::filterByWhere($data, $where);
if ($mode == 'unique') {
return $result['menu'] ?? [];
}
return [
'menu' => array_merge([], ...array_column($result, 'menu')),
'api' => array_merge([], ...array_column($result, 'api')),
'button' => array_merge([], ...array_column($result, 'button')),
];
}
/**
* 通过角色id 获取菜单权限IDS
*
* @param int|string $id
* @param string|null $tenantId
*
* @return array
*/
public function getPermissionColumns(int|string $id, string|null $tenantId = null): array
{
$role = $this->dao->get($id);
if (!$role) {
return [];
}
// 通过关联关系获取菜单ID
return $role->menus()->pluck('id')->toArray();
}
/**
* save
*
* @param array $data
*
* @return Role|null
* @throws \core\exception\handler\AdminException
*/
public function save(array $data): Role|null
{
try {
return $this->transaction(function () use ($data) {
$model = $this->dao->save($data);
$handlePermissions = isset($data['permissions']) && $data['permissions'] !== null;
// 有传权限key,才处理权限
if ($handlePermissions) {
$menus = $data['permissions'];
// 同步角色菜单关联
$model->menus()->sync($menus);
// 清理相关用户的权限缓存
$this->clearUserPermissionCacheByRole($model->id);
}
return $model;
});
} catch (\Throwable $e) {
throw new AdminException($e->getMessage());
}
}
/**
* 编辑
*
* @param $id
* @param $data
*
* @return void
* @throws \core\exception\handler\AdminException
*/
// public function update($id, $data): void
// {
// try {
// $this->transaction(function () use ($id, $data) {
// // 获取传入的权限列表(如果存在)
// $menus = $data['permissions'] ?? [];
// // 从数据访问对象中获取模型实例
// $model = $this->dao->get($id);
// if (!$model) {
// return;
// }
// // 使用属性复制器将传入的数据复制到模型中
// PropertyCopier::copyProperties((object)$data, $model);
// $model->save();
//
// // 格式化用户标识符,用于 Casbin
// $roleId = PolicyPrefix::ROLE->value . strval($model->id);
// $domain = '*';
//
// // 获取当前用户在 Casbin 中的隐式资源权限
// $currentPolicies = Permission::getImplicitResourcesForUser($roleId, $domain);
//
// // 提取完整的当前权限(0-5)
// $currentPermissions = array_map(
// function ($policy) {
// return $policy; // 保留完整的策略数组
// },
// $currentPolicies
// );
//
// // 将传入的菜单数据转换为 Casbin 策略格式
// $menuService = new SysMenuService();
// $policyService = new SysCasbinService();
//
// $menusData = $menuService->getAllMenus(['id' => $menus], 'menu', false);
// // 构建传入的权限列表,格式与当前权限相同
// $incomingPolicies = [];
// foreach ($menusData as $item) {
// $path = $policyService->resolvePermissionIdentifier($item);
// // 构建策略数组
// $policy = [
// $roleId, // sub
// $domain, // dom
// $path, // obj
// '*', // act(根据你的业务需求调整)
// $item['methods'], // method
// PolicyPrefix::MENU->value . $item['id'], // trace_id 资源归属用于追踪
// ];
// // 保留完整的策略数组
// $incomingPolicies[] = $policy;
// }
// // 更新 Casbin 权限
// $policyService->updateCasbinPolicies($currentPermissions, $incomingPolicies);
// // 同步 Casbin 权限,确保只有授权角色保留
// $model->casbin()->sync([$roleId]);
// });
// } catch (\Throwable $e) {
// // 捕获异常并抛出自定义异常
// throw new AdminException($e->getMessage());
// }
/**
* 编辑
*
* @param $id
* @param $data
*
* @return void
* @throws \core\exception\handler\AdminException
*/
public function update($id, $data): void
{
try {
$this->transaction(function () use ($id, $data) {
$model = $this->dao->get($id);
if (!$model) {
return;
}
PropertyCopier::copyProperties((object)$data, $model);
$model->save();
// 检查 permissions 是否存在且不为 null(未定义或显式 null 时不处理权限)
$handlePermissions = isset($data['permissions']) && $data['permissions'] !== null;
if ($handlePermissions) {
$menus = $data['permissions']; // 获取权限列表
// 同步角色菜单关联
$model->menus()->sync($menus);
// 清理相关用户的权限缓存
$this->clearUserPermissionCacheByRole($model->id);
}
});
} catch (\Throwable $e) {
// 捕获异常并抛出自定义异常
throw new AdminException($e->getMessage());
}
}
/**
* 删除角色
*
* @param array $id
* @param bool $force
*
* @return mixed
* @throws \core\exception\handler\AdminException
*/
public function destroy(array $id, bool $force = false): mixed
{
try {
return $this->transaction(function () use ($id, $force) {
$data = Arr::normalize($id);
$deletedIds = [];
foreach ($data as $id) {
$item = $this->get($id);
if ($item) {
$primaryKey = $item->getPk();
// 清理相关用户的权限缓存
$this->clearUserPermissionCacheByRole($id);
$item->delete();
$deletedIds[] = $item->{$primaryKey};
}
}
return $deletedIds;
});
} catch (\Throwable $e) {
throw new AdminException($e->getMessage());
}
}
/**
* 分配数据权限
*
* @param $id
* @param $data
*
* @throws \core\exception\handler\AdminException
*/
public function updateScope($id, $data): void
{
try {
$this->transaction(function () use ($id, $data) {
$scope = $data['scopes'] ?? [];
$model = $this->dao->get($id);
PropertyCopier::copyProperties((object)$data, $model);
if (!empty($model)) {
$model->save();
if ($model->data_scope !== 2) {
$scope = [];
}
$model->scopes()->sync($scope);
}
$this->cacheDriver()->delete(self::CACHE_ALL_ROLES_DATA);
});
} catch (\Throwable $e) {
throw new AdminException($e->getMessage());
}
}
/**
* 清理角色的用户权限缓存
*
* @param int|string $roleId
*/
private function clearUserPermissionCacheByRole(int|string $roleId): void
{
// 获取拥有此角色的所有用户
/** @var AdminRoleService $adminRoleService */
$adminRoleService = Container::make(AdminRoleService::class);
$userIds = $adminRoleService->getUserIdsByRoleId($roleId);
if (!empty($userIds)) {
// 清理每个用户的权限缓存
foreach ($userIds as $userId) {
$cacheKey = 'user_permissions_' . $userId;
$this->cacheDriver()->delete($cacheKey);
}
}
}
}