425 lines
14 KiB
PHP
425 lines
14 KiB
PHP
<?php
|
||
namespace app\common\traits;
|
||
|
||
use Illuminate\Database\Eloquent\Builder;
|
||
use support\Db;
|
||
use support\exception\BusinessException;
|
||
|
||
class DataPermission
|
||
{
|
||
|
||
/*
|
||
ALTER TABLE `user` ADD COLUMN `report_to` bigint(20) unsigned DEFAULT '0' COMMENT '上级用户ID';
|
||
|
||
ALTER TABLE `roles`
|
||
ADD COLUMN `parent_id` bigint(20) unsigned DEFAULT '0' COMMENT '父角色ID,0表示顶级角色',
|
||
ADD INDEX `idx_parent_id` (`parent_id`) USING BTREE;
|
||
*/
|
||
|
||
/**
|
||
* 应用数据权限过滤到查询
|
||
*
|
||
* @param Builder $query 查询构造器
|
||
* @param string $type 权限类型:superadmin, personal, same_role, role_tree, relation
|
||
* @param int $currentUserId 当前用户ID
|
||
* @param int $currentRoleId 当前角色ID
|
||
* @param string $userField 用户ID字段名
|
||
* @param string $roleField 角色ID字段名
|
||
*/
|
||
public static function applyQueryPermission(
|
||
Builder $query,
|
||
string $type,
|
||
int $currentUserId,
|
||
int $currentRoleId,
|
||
string $userField = 'user_id',
|
||
string $roleField = 'role_id'
|
||
): void {
|
||
// 超管不需要过滤
|
||
if (self::isSuperAdmin($currentUserId)) {
|
||
return;
|
||
}
|
||
|
||
switch ($type) {
|
||
case 'personal':
|
||
// 个人权限:只能访问自己的数据
|
||
$query->where($userField, $currentUserId);
|
||
break;
|
||
|
||
case 'same_role':
|
||
// 同角色权限:访问同角色的数据
|
||
$userIds = self::getSameRoleUserIds($currentRoleId);
|
||
$query->whereIn($userField, $userIds);
|
||
break;
|
||
|
||
case 'role_tree':
|
||
// 角色树权限:访问自己角色及子角色的数据
|
||
$userIds = self::getRoleTreeUserIds($currentRoleId);
|
||
$query->whereIn($userField, $userIds);
|
||
break;
|
||
|
||
case 'relation':
|
||
// 从属关系权限:访问自己及下属的数据
|
||
$userIds = self::getRelationUserIds($currentUserId);
|
||
$query->whereIn($userField, $userIds);
|
||
break;
|
||
|
||
default:
|
||
// 默认个人权限
|
||
$query->where($userField, $currentUserId);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 应用数据权限到创建/更新操作
|
||
*
|
||
* @param mixed $target 创建时为数组,更新时为Model对象
|
||
* @param string $type 权限类型
|
||
* @param int $currentUserId 当前用户ID
|
||
* @param int $currentRoleId 当前角色ID
|
||
* @param array $newData 更新时的数据
|
||
* @param string $userField 用户ID字段名
|
||
* @param string $roleField 角色ID字段名
|
||
*/
|
||
public static function applyMutationPermission(
|
||
&$target,
|
||
string $type,
|
||
int $currentUserId,
|
||
int $currentRoleId,
|
||
array $newData = [],
|
||
string $userField = 'admin_id',
|
||
string $roleField = 'role_id'
|
||
): void {
|
||
// 超管不需要检查
|
||
if (self::isSuperAdmin($currentUserId)) {
|
||
return;
|
||
}
|
||
|
||
switch ($type) {
|
||
case 'personal':
|
||
self::handlePersonalPermission($target, $newData, $userField, $currentUserId);
|
||
break;
|
||
|
||
case 'same_role':
|
||
self::handleSameRolePermission($target, $newData, $userField, $roleField, $currentRoleId);
|
||
break;
|
||
|
||
case 'role_tree':
|
||
self::handleRoleTreePermission($target, $newData, $userField, $roleField, $currentRoleId);
|
||
break;
|
||
|
||
case 'relation':
|
||
self::handleRelationPermission($target, $newData, $userField, $currentUserId);
|
||
break;
|
||
|
||
default:
|
||
throw new BusinessException("未定义的数据权限类型: $type", 403);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取同角色的用户ID列表
|
||
*
|
||
* @param int $roleId 角色ID
|
||
* @return array 用户ID列表
|
||
*/
|
||
public static function getSameRoleUserIds(int $roleId): array
|
||
{
|
||
return Db::table('user_roles')
|
||
->where('role_id', $roleId)
|
||
->pluck('user_id')
|
||
->toArray();
|
||
}
|
||
|
||
/**
|
||
* 获取角色树下的所有用户ID列表
|
||
*
|
||
* @param int $roleId 当前角色ID
|
||
* @return array 用户ID列表
|
||
*/
|
||
public static function getRoleTreeUserIds(int $roleId): array
|
||
{
|
||
// 获取角色树中的所有角色ID
|
||
$roleIds = self::getRoleTreeIds($roleId);
|
||
|
||
// 获取这些角色下的所有用户ID
|
||
return Db::table('user_roles')
|
||
->whereIn('role_id', $roleIds)
|
||
->pluck('user_id')
|
||
->toArray();
|
||
}
|
||
|
||
/**
|
||
* 获取从属关系下的用户ID列表(包括自己和下属)
|
||
*
|
||
* @param int $userId 当前用户ID
|
||
* @return array 用户ID列表
|
||
*/
|
||
public static function getRelationUserIds(int $userId): array
|
||
{
|
||
// 获取所有下属用户ID(递归)
|
||
$subordinateIds = self::getSubordinateIds($userId);
|
||
|
||
// 返回自己和所有下属的ID
|
||
return array_merge([$userId], $subordinateIds);
|
||
}
|
||
|
||
/**
|
||
* 获取直接下属的用户ID列表
|
||
*
|
||
* @param int $userId 用户ID
|
||
* @return array 直接下属用户ID列表
|
||
*/
|
||
public static function getDirectSubordinateIds(int $userId): array
|
||
{
|
||
return Db::table('user')
|
||
->where('report_to', $userId)
|
||
->pluck('id')
|
||
->toArray();
|
||
}
|
||
|
||
/**
|
||
* 递归获取所有下属用户ID列表
|
||
*
|
||
* @param int $userId 用户ID
|
||
* @return array 所有下属用户ID列表
|
||
*/
|
||
private static function getSubordinateIds(int $userId): array
|
||
{
|
||
$subordinates = self::getDirectSubordinateIds($userId);
|
||
$allSubordinates = $subordinates;
|
||
|
||
foreach ($subordinates as $subordinateId) {
|
||
$allSubordinates = array_merge($allSubordinates, self::getSubordinateIds($subordinateId));
|
||
}
|
||
|
||
return $allSubordinates;
|
||
}
|
||
|
||
/**
|
||
* 处理个人权限
|
||
*/
|
||
private static function handlePersonalPermission(
|
||
&$target,
|
||
array $newData,
|
||
string $userField,
|
||
int $currentUserId
|
||
): void {
|
||
if (is_array($target)) {
|
||
// 创建时强制设置用户ID
|
||
$target[$userField] = $currentUserId;
|
||
} else {
|
||
// 更新时校验原始记录
|
||
if ($target->{$userField} !== $currentUserId) {
|
||
throw new BusinessException("无权操作该记录", 403);
|
||
}
|
||
|
||
// 禁止变更归属字段
|
||
if (isset($newData[$userField]) && $newData[$userField] !== $currentUserId) {
|
||
throw new BusinessException("无权变更归属字段", 403);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理同角色权限
|
||
*/
|
||
private static function handleSameRolePermission(
|
||
&$target,
|
||
array $newData,
|
||
string $userField,
|
||
string $roleField,
|
||
int $currentRoleId
|
||
): void {
|
||
// 获取同角色用户ID列表
|
||
$sameRoleUserIds = self::getSameRoleUserIds($currentRoleId);
|
||
|
||
if (is_array($target)) {
|
||
// 创建时设置归属用户并校验
|
||
if (isset($target[$userField])) {
|
||
if (!in_array($target[$userField], $sameRoleUserIds)) {
|
||
throw new BusinessException("无权为指定用户创建数据", 403);
|
||
}
|
||
} else {
|
||
$target[$userField] = $currentRoleId;
|
||
}
|
||
} else {
|
||
// 更新时校验原始记录
|
||
if (!in_array($target->{$userField}, $sameRoleUserIds)) {
|
||
throw new BusinessException("无权操作该记录", 403);
|
||
}
|
||
|
||
// 禁止变更归属字段到无权限的用户
|
||
if (isset($newData[$userField]) && !in_array($newData[$userField], $sameRoleUserIds)) {
|
||
throw new BusinessException("无权变更归属字段", 403);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理角色树权限
|
||
*/
|
||
private static function handleRoleTreePermission(
|
||
&$target,
|
||
array $newData,
|
||
string $userField,
|
||
string $roleField,
|
||
int $currentRoleId
|
||
): void {
|
||
// 获取角色树用户ID列表
|
||
$roleTreeUserIds = self::getRoleTreeUserIds($currentRoleId);
|
||
|
||
if (is_array($target)) {
|
||
// 创建时校验用户是否在允许范围内
|
||
if (isset($target[$userField])) {
|
||
if (!in_array($target[$userField], $roleTreeUserIds)) {
|
||
throw new BusinessException("无权为指定用户创建数据", 403);
|
||
}
|
||
} else {
|
||
$target[$userField] = $currentRoleId;
|
||
}
|
||
} else {
|
||
// 更新时校验原始记录用户
|
||
if (!in_array($target->{$userField}, $roleTreeUserIds)) {
|
||
throw new BusinessException("无权操作该记录", 403);
|
||
}
|
||
|
||
// 校验新用户是否在允许范围内
|
||
if (isset($newData[$userField])) {
|
||
if (!in_array($newData[$userField], $roleTreeUserIds)) {
|
||
throw new BusinessException("无权变更用户字段", 403);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理从属关系权限
|
||
*/
|
||
private static function handleRelationPermission(
|
||
&$target,
|
||
array $newData,
|
||
string $userField,
|
||
int $currentUserId
|
||
): void {
|
||
// 获取从属关系用户ID列表
|
||
$relationUserIds = self::getRelationUserIds($currentUserId);
|
||
|
||
if (is_array($target)) {
|
||
// 创建时校验用户是否在允许范围内
|
||
if (isset($target[$userField])) {
|
||
if (!in_array($target[$userField], $relationUserIds)) {
|
||
throw new BusinessException("无权为指定用户创建数据", 403);
|
||
}
|
||
} else {
|
||
$target[$userField] = $currentUserId;
|
||
}
|
||
} else {
|
||
// 更新时校验原始记录用户
|
||
if (!in_array($target->{$userField}, $relationUserIds)) {
|
||
throw new BusinessException("无权操作该记录", 403);
|
||
}
|
||
|
||
// 校验新用户是否在允许范围内
|
||
if (isset($newData[$userField])) {
|
||
if (!in_array($newData[$userField], $relationUserIds)) {
|
||
throw new BusinessException("无权变更用户字段", 403);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取角色树ID(当前角色及其所有子角色)
|
||
* 从数据库中动态获取角色树关系
|
||
*/
|
||
private static function getRoleTreeIds(int $roleId): array
|
||
{
|
||
static $roleTreeCache = [];
|
||
|
||
if (isset($roleTreeCache[$roleId])) {
|
||
return $roleTreeCache[$roleId];
|
||
}
|
||
|
||
// 从数据库中递归获取角色树
|
||
$ids = [$roleId];
|
||
$queue = [$roleId];
|
||
|
||
while (!empty($queue)) {
|
||
$current = array_shift($queue);
|
||
// 查找当前角色的所有子角色
|
||
$childRoles = Db::table('roles')
|
||
->where('parent_id', $current)
|
||
->where('status', 1) // 只获取启用的角色
|
||
->pluck('id')
|
||
->toArray();
|
||
|
||
foreach ($childRoles as $childId) {
|
||
if (!in_array($childId, $ids)) {
|
||
$ids[] = $childId;
|
||
$queue[] = $childId;
|
||
}
|
||
}
|
||
}
|
||
|
||
$roleTreeCache[$roleId] = $ids;
|
||
return $ids;
|
||
}
|
||
|
||
/**
|
||
* 检查是否为超管
|
||
*
|
||
* @param int $userId 用户ID
|
||
* @return bool 是否为超管
|
||
*/
|
||
public static function isSuperAdmin(int $userId): bool
|
||
{
|
||
// 检查用户是否关联了超级管理员角色
|
||
// 假设角色ID为1的是超级管理员角色
|
||
$superAdminRoleId = 1;
|
||
|
||
return Db::table('user_roles')
|
||
->where('user_id', $userId)
|
||
->where('role_id', $superAdminRoleId)
|
||
->exists();
|
||
}
|
||
|
||
/**
|
||
* 获取用户的角色ID
|
||
*
|
||
* @param int $userId 用户ID
|
||
* @return int 角色ID
|
||
*/
|
||
public static function getUserIdRoleId(int $userId): int
|
||
{
|
||
// 从 user_roles 表中获取当前用户的角色ID
|
||
$roleId = Db::table('user_roles')
|
||
->where('user_id', $userId)
|
||
->value('role_id');
|
||
|
||
return $roleId ?: 0;
|
||
}
|
||
|
||
/**
|
||
* 获取用户的所有角色ID(包括继承的角色)
|
||
*
|
||
* @param int $userId 用户ID
|
||
* @return array 角色ID列表
|
||
*/
|
||
public static function getUserAllRoleIds(int $userId): array
|
||
{
|
||
// 获取用户直接关联的角色
|
||
$directRoleIds = Db::table('user_roles')
|
||
->where('user_id', $userId)
|
||
->pluck('role_id')
|
||
->toArray();
|
||
|
||
// 获取所有继承的角色
|
||
$allRoleIds = [];
|
||
foreach ($directRoleIds as $roleId) {
|
||
$allRoleIds = array_merge($allRoleIds, self::getRoleTreeIds($roleId));
|
||
}
|
||
|
||
// 去重并返回
|
||
return array_unique($allRoleIds);
|
||
}
|
||
} |