edit inquiry
This commit is contained in:
+424
-51
@@ -4,8 +4,8 @@ namespace app\common\utils;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use plugin\admin\app\common\Auth;
|
||||
use support\exception\BusinessException;
|
||||
use support\Db;
|
||||
|
||||
/**
|
||||
* 构建查询工具类
|
||||
@@ -49,6 +49,13 @@ class CrudUtil
|
||||
* 'roles' => ['id','name'], // 关联表字段选择
|
||||
* 'posts' => ['id','title','user_id'],
|
||||
* ],
|
||||
* 'dataLimit' => [
|
||||
* 'type' => 'personal|same_role|role_tree|relation', // personal:个人数据,same_role:同角色,role_tree:角色树,relation:从属关联数据
|
||||
* 'userId' => 1,
|
||||
* 'roleId' => 2,
|
||||
* 'userField' => 'user_id',
|
||||
* 'roleField' => 'role_id'
|
||||
* ]
|
||||
* ];
|
||||
*/
|
||||
public static function buildQuery(array $config): Builder
|
||||
@@ -354,78 +361,444 @@ class CrudUtil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ================== Data Limit ==================
|
||||
|
||||
/**
|
||||
* 应用数据权限过滤到查询(buildQuery 场景)
|
||||
*/
|
||||
protected static function applyDataLimit(Builder $q, array $dataLimit): void
|
||||
{
|
||||
if (Auth::isSuperAdmin()) {
|
||||
return;
|
||||
}
|
||||
$type = $dataLimit['type'] ?? null;
|
||||
$field = $dataLimit['field'] ?? 'admin_id';
|
||||
$type = $dataLimit['type'] ?? 'personal';
|
||||
$userId = $dataLimit['userId'] ?? 0;
|
||||
$roleId = $dataLimit['roleId'] ?? 0;
|
||||
$userField = $dataLimit['userField'] ?? 'user_id';
|
||||
$roleField = $dataLimit['roleField'] ?? 'role_id';
|
||||
|
||||
if ($type === 'personal') {
|
||||
$q->where($field, admin_id());
|
||||
} elseif ($type === 'auth') {
|
||||
$q->whereIn($field, Auth::getScopeAdminIds(true));
|
||||
}
|
||||
self::applyQueryPermission($q, $type, $userId, $roleId, $userField, $roleField);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用数据权限到“创建/更新”操作
|
||||
* 应用数据权限到"创建/更新"操作
|
||||
*
|
||||
* - $target 可能是 array|Model 对象
|
||||
* - $newData 仅在 updateByPk 场景时传入,用以校验变更归属
|
||||
*/
|
||||
protected static function applyDataLimitOnMutate(&$target, array $dataLimit, array $newData = []): void
|
||||
{
|
||||
if (Auth::isSuperAdmin()) {
|
||||
$type = $dataLimit['type'] ?? 'personal';
|
||||
$userId = $dataLimit['userId'] ?? 0;
|
||||
$roleId = $dataLimit['roleId'] ?? 0;
|
||||
$userField = $dataLimit['userField'] ?? 'user_id';
|
||||
$roleField = $dataLimit['roleField'] ?? 'role_id';
|
||||
|
||||
self::applyMutationPermission($target, $type, $userId, $roleId, $newData, $userField, $roleField);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用数据权限过滤到查询
|
||||
*
|
||||
* @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 = 0,
|
||||
int $currentRoleId = 0,
|
||||
string $userField = 'user_id',
|
||||
string $roleField = 'role_id'
|
||||
): void {
|
||||
// 超管不需要过滤
|
||||
if (self::isSuperAdmin($currentUserId)) {
|
||||
return;
|
||||
}
|
||||
$type = $dataLimit['type'] ?? null;
|
||||
$field = $dataLimit['field'] ?? 'admin_id';
|
||||
|
||||
// 读取当前归属:create 时 $target 是 array,update 时是 Model
|
||||
$current = is_array($target) ? null : $target->{$field};
|
||||
|
||||
// 1. personal → 只能自己
|
||||
if ($type === 'personal') {
|
||||
// create: 强制设置
|
||||
if (is_array($target)) {
|
||||
$target[$field] = admin_id();
|
||||
}
|
||||
// update: 校验当前归属
|
||||
elseif ($current !== admin_id()) {
|
||||
throw new BusinessException("无权操作该记录", 403);
|
||||
}
|
||||
// 若要修改归属,也必须和自己一致
|
||||
if (isset($newData[$field]) && $newData[$field] !== admin_id()) {
|
||||
throw new BusinessException("无权变更归属字段", 403);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 2. auth → 自己及下属
|
||||
if ($type === 'auth') {
|
||||
$scope = Auth::getScopeAdminIds(true);
|
||||
// create: 若前端指定归属,则校验范围;否则默认自己
|
||||
if (is_array($target)) {
|
||||
if (isset($target[$field])) {
|
||||
if (!in_array($target[$field], $scope)) {
|
||||
throw new BusinessException("无权为指定用户操作", 403);
|
||||
}
|
||||
} else {
|
||||
$target[$field] = admin_id();
|
||||
}
|
||||
}
|
||||
// update: 校验当前归属
|
||||
elseif (!in_array($current, $scope)) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用数据权限到创建/更新操作
|
||||
*
|
||||
* @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 = 'user_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[$field]) && !in_array($newData[$field], $scope)) {
|
||||
|
||||
// 禁止变更归属字段
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user