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(); // 1. eager load if (!empty($config['with'])) { $q->with($config['with']); } // 2. 关联表字段选择 if (!empty($config['withFields'])) { foreach ($config['withFields'] as $rel => $fields) { $q->with([$rel => function ($qr) use ($fields) { $fk = $qr->getModel()->getForeignKey(); $qr->select(array_unique(array_merge($fields, [$fk]))); }]); } } // 3. 本表字段选择 if (!empty($config['fields'])) { $q->select($config['fields']); } // 4. where 条件(支持 dot-notation 关联过滤) if (!empty($config['where'])) { foreach ($config['where'] as $col => $val) { if (strpos($col, '.') !== false) { [$rel, $relCol] = explode('.', $col, 2); $q->whereHas($rel, function ($qr) use ($relCol, $val) { self::applyWhere($qr, $relCol, $val); }); } else { self::applyWhere($q, $col, $val); } } } // 5. 排序 if (!empty($config['order'])) { foreach ($config['order'] as $col => $dir) { $q->orderBy($col, strtolower($dir) === 'asc' ? 'asc' : 'desc'); } } // 6. 数据权限过滤 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 string $modelClass * @param array $where * @param array $with * @param array $dataLimit * @return Model * @throws BusinessException */ // public static function findOneBy( // string $modelClass, // array $where, // array $with = [], // array $dataLimit = [] // ): Model { // $config = compact('modelClass'); // $config['model'] = $modelClass; // $config['where'] = $where; // $config['with'] = $with; // $config['dataLimit'] = $dataLimit; // $m = self::buildQuery($config)->first(); // if (!$m) { // throw new BusinessException("未找到符合条件的记录", 404); // } // return $m; // } 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 string $modelClass * @param array $where * @param array $data * @param array $dataLimit * @return int 受影响行数 */ // public static function updateBy( // string $modelClass, // array $where, // array $data, // array $dataLimit = [] // ): int { // $config = ['model'=>$modelClass, 'where'=>$where, 'dataLimit'=>$dataLimit]; // return self::buildQuery($config)->update($data); // } 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 string $modelClass * @param array $where * @param array $dataLimit * @return int 删除条数 */ // public static function deleteBy( // string $modelClass, // array $where, // array $dataLimit = [] // ): int { // $config = ['model'=>$modelClass, 'where'=>$where, 'dataLimit'=>$dataLimit]; // return self::buildQuery($config)->delete(); // } public static function deleteBy(Builder $q): int { return $q->delete(); } //=================== Internal Methods ===================// /** * 应用单字段 where 过滤 * * @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) && count($val) === 2) { [$op, $v] = $val; 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); } } else { $q->where($col, $val); } } /** * 应用数据权限过滤到查询(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); } } } }