Files
fdesk/app/common/utils/CrudUtil_bak.php
T

843 lines
26 KiB
PHP
Raw Normal View History

2025-08-06 18:55:37 +08:00
<?php
namespace app\common\utils;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use support\exception\BusinessException;
use support\Db;
/**
* 构建查询工具类
* | 方法 | 说明 |
* | ------------------------------- | --------------------- |
* | `buildQuery($config)` | 构建动态查询(关联加载/字段/过滤/排序) |
* | `paginate($q,$per,$page)` | 分页返回 Paginator |
* | `getAll($q)` | 不分页直接 `get()` |
* | `findByPk($model,$id)` | 根据主键查询单条,找不到抛 404 |
* | `findOneBy($model,$where)` | 根据任意条件查询单条,找不到抛 404 |
* | `deleteByPk($model,$ids)` | 根据主键删除(可批量),返回删除条数 |
* | `deleteBy($model,$where)` | 根据任意条件删除,返回删除条数 |
* | `updateByPk($model,$id,$data)` | 根据主键更新,找不到抛 404 |
* | `updateBy($model,$where,$data)` | 根据任意条件更新,返回影响行数 |
*/
class CrudUtil_bak
{
/**
* 构建查询,支持:
* - 本表字段过滤 where
* - 关联加载 with
* - 关联表字段选择 withFields
* - 关联表字段过滤 where with dot-notation
* - 排序 order
* - 本表字段选择 fields
*
* $config = [
* 'model' => 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'],
* ],
* ];
*/
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
{
return $q->first();
}
/**
* 创建,支持 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 ==================
/**
* 应用数据权限过滤到查询
*
* @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 = '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);
}
// ================== 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';
if ($type === 'personal') {
$q->where($field, admin_id());
} elseif ($type === 'auth') {
$q->whereIn($field, Auth::getScopeAdminIds(true));
}
}
/**
* 应用数据权限到“创建/更新”操作
*
* - $target 可能是 array|Model 对象
* - $newData 仅在 updateByPk 场景时传入,用以校验变更归属
*/
protected static function applyDataLimitOnMutate(&$target, array $dataLimit, array $newData = []): void
{
if (Auth::isSuperAdmin()) {
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);
}
}
// 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)) {
throw new BusinessException("无权操作该记录", 403);
}
// 校验更新中指定的新归属
if (isset($newData[$field]) && !in_array($newData[$field], $scope)) {
throw new BusinessException("无权变更归属字段", 403);
}
}
}
}