Files
useds/app/common/traits/DataPermission.php
T

425 lines
14 KiB
PHP
Raw Normal View History

2026-01-05 14:33:21 +08:00
<?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);
}
}