Files

222 lines
6.3 KiB
PHP
Raw Permalink Normal View History

2025-05-12 16:13:46 +08:00
<?php
namespace App\Service;
use App\BaseApp\BaseService;
use App\Models\RoleMenuRelationModel;
use App\Models\RoleModel;
use App\Service\common\RedisService;
use Exception;
use Illuminate\Support\Facades\DB;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
/**
* 角色服务类
*/
class RoleService extends BaseService
{
public function __construct()
{
parent::__construct();
$this->model = RoleModel::class;
$this->selectField = ['id', 'name', 'value', 'pid', 'desc', 'status', 'color', 'created_at'];
$this->queryField = ['name' => 'like', 'value' => 'like', 'pid' => '=', 'status' => '='];
2025-05-12 16:13:46 +08:00
$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)) {
2025-05-12 16:13:46 +08:00
$this->utils->errorThrow('更新失败!');
}
DB::commit();
$this->flushRoleCache((int) $roleId);
2025-05-12 16:13:46 +08:00
} 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;
}
2025-05-12 16:13:46 +08:00
/**
* 获取数据详情
* @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])) {
2025-05-12 16:13:46 +08:00
$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);
2025-05-12 16:13:46 +08:00
}
}