User::class, * 'where' => [ * 'status' => 1, * 'name' => ['like','%张%'], * 'roles.name' => '管理员', // 关联表字段过滤 * 'posts.created_at'=> ['>','2025-01-01'], * ], * 'order' => ['id'=>'desc'], * 'fields' => ['id','name','email'], * 'with' => ['roles','posts'], * 'withFields' => [ * '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 { /** @var Model $model */ $model = new $config['model']; $q = $model->newQuery(); // eager-load:支持 ['rel','rel2'] 和 ['rel'=>fields[], ...] if (! empty($config['with'])) { foreach ($config['with'] as $rel => $fields) { if (is_int($rel)) { // 普通关联 $q->with($fields); } else { // 带字段筛选的关联 $q->with([$rel => function ($qr) use ($fields) { $key = $qr->getModel()->getKeyName(); if (! in_array($key, $fields, true)) { $fields[] = $key; } $qr->select($fields); }]); } } } // 表字段 if (!empty($config['fields'])) { $q->select($config['fields']); } // where if (!empty($config['where'])) { foreach ($config['where'] as $col => $val) { if (strpos($col, '.') !== false) { // 多层支持 $segments = explode('.', $col); $leaf = array_pop($segments); self::applyNestedWhere($q, $segments, $leaf, $val); } else { self::applyWhere($q, $col, $val); } } } // or where if (!empty($config['orWhere'])) { $q->where(function($q) use($config) { foreach ($config['orWhere'] as $cond) { [$col,$op,$val] = $cond; $q->orWhere($col,$op,$val); } }); } // 排序 if (!empty($config['order'])) { foreach ($config['order'] as $col => $dir) { $q->orderBy($col, strtolower($dir) === 'asc' ? 'asc' : 'desc'); } } // 数据权限过滤 if (!empty($config['dataLimit'])) { self::applyDataLimit($q, $config['dataLimit']); } return $q; } /** * 分页查询 * * @param Builder $query * @param int $perPage * @param int $page * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ public static function paginate(Builder $q, int $perPage, int $page) { return $q->paginate($perPage, ['*'], 'page', $page); } /** * 非分页查询 * * @param Builder $query * @return \Illuminate\Database\Eloquent\Collection */ public static function getAll(Builder $q) { return $q->get(); } /** * 执行复杂查询——仅在显式调用时生效 * * @param string $modelClass 模型类 * @param callable $callback function(Builder $q) { ... } * @return mixed 由 callback 返回的结果 */ public static function executeComplex(string $modelClass, callable $cb) { /** @var Model $model */ $model = new $modelClass; return $cb($model->newQuery()); } /** * 主键单条查询 * * @param string $modelClass * @param mixed $id * @param array $with * @return Model * @throws BusinessException */ public static function findByPk(string $modelClass, $id, array $with = []): Model { $m = $modelClass::with($with)->find($id); if (!$m) { throw new BusinessException("记录 ID={$id} 不存在", 404); } return $m; } /** * 任意条件单条查询,支持 dataLimit * * @param Builder $query * @return Model * @throws BusinessException */ public static function findOneBy(Builder $q): Model { $model = $q->first(); if (!$model) { throw new BusinessException("记录不存在", 404); } return $model; } /** * 创建,支持 dataLimit * * @param string $modelClass * @param array $data * @param array $dataLimit * @return Model * @throws BusinessException */ public static function create( string $modelClass, array $data, array $dataLimit = [] ): Model { // dataLimit 检查/注入 if (!empty($dataLimit)) { self::applyDataLimitOnMutate($data, $dataLimit); } return $modelClass::create($data); } /** * 主键更新,支持 dataLimit * * @param string $modelClass * @param mixed $id * @param array $data * @param array $dataLimit * @return Model * @throws BusinessException */ public static function updateByPk( string $modelClass, $id, array $data, array $dataLimit = [] ): Model { /** @var Model $m */ $m = $modelClass::find($id); if (!$m) { throw new BusinessException("记录 ID={$id} 不存在", 404); } // dataLimit 校验 if (!empty($dataLimit)) { self::applyDataLimitOnMutate($m, $dataLimit, $data); } $m->fill($data)->save(); return $m; } /** * 非主键条件更新(批量),支持 dataLimit * * @param Builder $query * @param array $data * @return int 受影响行数 */ public static function updateBy(Builder $q, array $data): int { return $q->update($data); } /** * 主键删除(支持批量) * * @param string $modelClass * @param array $ids * @return int 删除条数 */ public static function deleteByPk(string $modelClass, array $ids): int { return $modelClass::destroy($ids); } /** * 非主键条件删除,支持 dataLimit * * @param Builder $query * @return int 删除条数 */ public static function deleteBy(Builder $q): int { return $q->delete(); } //=================== Internal Methods ===================// /** * 支持多层的 whereHas:$relations=['roles','permissions'],$leaf='name' */ protected static function applyNestedWhere(Builder $q, array $relations, string $leaf, $val): void { $rel = array_shift($relations); $q->whereHas($rel, function (Builder $qr) use ($relations, $leaf, $val) { if ($relations) { // 还有更深层,继续递归 self::applyNestedWhere($qr, $relations, $leaf, $val); } else { // 最后一层,使用普通 applyWhere self::applyWhere($qr, $leaf, $val); } }); } /** * 单字段 where 支持: * - 多条件数组 [ [op1,val1], [op2,val2], … ] * - 单条件 ['op', val] * - 直接值 * * @param Builder $q * @param string $col * @param mixed $val * @return void */ protected static function applyWhere(Builder $q, string $col, $val): void { // 多条件数组 if (is_array($val) && isset($val[0]) && is_array($val[0])) { foreach ($val as $pair) { if (is_array($pair) && count($pair) === 2) { self::applySingleWhere($q, $col, $pair[0], $pair[1]); } } } // 单条件 elseif (is_array($val) && count($val) === 2) { self::applySingleWhere($q, $col, $val[0], $val[1]); } // 直接等值 else { $q->where($col, $val); } } /** * 执行条件 where */ protected static function applySingleWhere(Builder $q, string $col, string $op, $v): void { switch (strtolower($op)) { case 'like': $q->where($col, 'like', $v); break; case 'in': $q->whereIn($col, (array)$v); break; case 'not in': $q->whereNotIn($col, (array)$v); break; case 'null': $q->whereNull($col); break; case 'not null': $q->whereNotNull($col); break; case 'between': $q->whereBetween($col, (array)$v); break; default: $q->where($col, $op, $v); } } // ================== Data Limit ================== /** * 应用数据权限过滤到查询(buildQuery 场景) */ protected static function applyDataLimit(Builder $q, array $dataLimit): void { $type = $dataLimit['type'] ?? 'personal'; $userId = $dataLimit['userId'] ?? 0; $roleId = $dataLimit['roleId'] ?? 0; $userField = $dataLimit['userField'] ?? 'user_id'; $roleField = $dataLimit['roleField'] ?? 'role_id'; self::applyQueryPermission($q, $type, $userId, $roleId, $userField, $roleField); } /** * 应用数据权限到"创建/更新"操作 * * - $target 可能是 array|Model 对象 * - $newData 仅在 updateByPk 场景时传入,用以校验变更归属 */ protected static function applyDataLimitOnMutate(&$target, array $dataLimit, array $newData = []): void { $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; } 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 = '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[$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); } }