all(); } /** * 获取当前用户ID * * @return int|null 当前用户ID,如果未登录则返回null */ protected function getUserId(): ?int { return request()->user->user_id ?? null; } /** * 注入字段 * 如果数据中不存在指定字段,则注入当前用户ID * * @param array $data 数据数组 * @param string $field 字段名,默认为 'user_id' * @return array 注入字段后的数据数组 */ protected function injectAdd(array $data, string $field = 'user_id'): array { return !isset($data[$field]) ? $data + [$field => $this->getUserId()] : $data; } /** * 参数预处理入口,按场景分发 * * @param Request $request * @param string $scene * @return array */ protected function prepareParams(Request $request, string $scene): array { // 默认所有场景都注入 user_id return $this->injectAdd($request->all()); } /** * 后置处理单条记录:默认不变 * * 子类可以重写这个方法来做额外字段加工, * 比如加 computed 字段、日期格式化、脱敏等等。 * * @param array $item 单条记录(pruned 后的数组) * @return array 处理后的数组 */ protected function postProcessItem(array $item, string $scene): array { return $item; } /** * 根据 queryScenes 配置构造 fieldsMap * * @param array $config 必须包含 keys: fields, relations * @return array */ // protected function buildFieldsMap2(array $config): array // { // $relations = $config['relations'] ?? []; // $fieldsMap = []; // // 根层:主表字段 + 所有一级关联名 // $rootFields = $config['fields'] ?? []; // foreach (array_keys($relations) as $path) { // $rootFields[] = explode('.', $path, 2)[0]; // } // $fieldsMap[''] = array_values(array_unique($rootFields)); // // 每个关联层:自己字段 + 直接子关联名 // foreach ($relations as $path => $ownFields) { // $fieldsMap[$path] = $ownFields; // $segments = explode('.', $path); // $depth = count($segments); // if ($depth > 1) { // for ($i = 1; $i < $depth; $i++) { // $parent = implode('.', array_slice($segments, 0, $i)); // $child = $segments[$i]; // $fieldsMap[$parent][] = $child; // } // } // } // // 去重每一层 // foreach ($fieldsMap as $k => $arr) { // $fieldsMap[$k] = array_values(array_unique($arr)); // } // return $fieldsMap; // } // protected function buildFieldsMap(array $config): array // { // $relations = $config['relations'] ?? []; // // 1) 处理主表层字段:把 "col as alias" -> alias // $rawFields = $config['fields'] ?? []; // $root = []; // foreach ($rawFields as $f) { // if (preg_match('/\s+as\s+/i', $f)) { // // alias 是 as 之后的部分 // [$_, $alias] = preg_split('/\s+as\s+/i', $f); // $root[] = $alias; // } else { // $root[] = $f; // } // } // // 再把顶级关联名也加进来 // foreach (array_keys($relations) as $path) { // $root[] = explode('.', $path, 2)[0]; // } // $fieldsMap = ['' => array_values(array_unique($root))]; // // 2) 每个关联路径,自身字段 & 子关联名 // foreach ($relations as $path => $rawCols) { // $arr = []; // // 同样把 alias 拆出来 // foreach ($rawCols as $col) { // if (preg_match('/\s+as\s+/i', $col)) { // [, $alias] = preg_split('/\s+as\s+/i', $col); // $arr[] = $alias; // } else { // $arr[] = $col; // } // } // // 再把它的直接子关联名也加上 // $segments = explode('.', $path); // $depth = count($segments); // if ($depth > 1) { // for ($i = 1; $i < $depth; $i++) { // $parent = implode('.', array_slice($segments, 0, $i)); // $child = $segments[$i]; // $fieldsMap[$parent][] = $child; // } // } // $fieldsMap[$path] = array_values(array_unique($arr)); // } // return $fieldsMap; // } /** * 验证请求 * * @param array $data 请求数据 * @param string $scene 验证场景 * @return array 验证后的数据 * @throws BusinessException 验证失败时抛出业务异常 */ protected function validateRequest(array $data, string $scene = ''): array { if (!$this->validator) { return $data; } $validator = is_callable($this->validator) ? call_user_func($this->validator) : (is_string($this->validator) ? new $this->validator : $this->validator); if ($scene && method_exists($validator, 'scene')) { $validator->scene($scene); } if (!$validator->check($data)) { throw new BusinessException($validator->getError()); } return $data; } /** * 列表 * * @param Request $request 请求对象 * @param string $scene 场景名称,默认为 'default' * @param callable|null $postProcessor 额外字段处理回调函数 * @return Response 响应对象 */ protected function listWithTransform( array $params, string $scene = 'default', bool $paginate = true ): Response { $collection = $paginate ? $this->getService()->list($params, $scene) : $this->getService()->all($params, $scene); $config = $this->getService()->queryScenes()[$scene]($params); // $fieldsMap = $this->buildFieldsMap($config); // —— 如果 fields 为空,就不做任何裁剪,直接原样输出 —— if (empty($config['fields'])) { $data = $this->transformData( $collection, fn(array $item) => $this->postProcessItem($item, $scene) ); return $this->successResponse($data); } $fieldsMap = SimpleQueryBuilder::buildFieldsMap($config); $result = $this->transformAndPrune( $collection, $fieldsMap, null, fn(array $item) => $this->postProcessItem($item, $scene) ); return $this->successResponse($result); } /** * 获取资源列表 * * @param Request $request 请求对象 * @param string $scene 场景名称,默认为 'default' * @return Response 响应对象 */ public function index(Request $request, string $scene = 'default'): Response { return $this->listWithTransform( $this->prepareParams($request, $scene), $scene ); } /** * 获取所有资源(不分页) * * @param Request $request 请求对象 * @param string $scene 场景名称,默认为 'default' * @return Response 响应对象 */ public function all(Request $request, string $scene = 'default'): Response { return $this->listWithTransform( $this->prepareParams($request, $scene), $scene, false ); } public function oneWithTransform(array $params, string $scene = 'default', int $id = 0): Response { $model = $id ? $this->getService()->getById($id, $params, $scene) : $this->getService()->getOne($params, $scene); if (!$model) { return $this->errorResponse('资源不存在', 1, 404); } $config = $this->getService()->queryScenes()[$scene]($params); // $fieldsMap = $this->buildFieldsMap($config); // —— 如果 fields 为空,就不做任何裁剪,直接原样输出 —— if (empty($config['fields'])) { $data = $this->transformData( $model, fn(array $item) => $this->postProcessItem($item, $scene) ); return $this->successResponse($data); } $fieldsMap = SimpleQueryBuilder::buildFieldsMap($config); $data = $this->transformAndPrune( $model, $fieldsMap, null, fn(array $item) => $this->postProcessItem($item, $scene) ); return $this->successResponse($data); } /** * GET /resource/{id} */ public function show(Request $request, int $id, string $scene = 'default'): Response { return $this->oneWithTransform( $this->prepareParams($request, $scene), $scene, $id ); } /** * GET /resource/one (按任意条件取单条) */ public function one(Request $request, string $scene = 'default'): Response { return $this->oneWithTransform( $this->prepareParams($request, $scene), $scene ); } /** * 创建新资源 * * @param Request $request 请求对象 * @return Response 响应对象 */ public function store(Request $request): Response { $data = $this->validateRequest($request->all(), 'store'); $model = $this->getService()->create($data); return $this->successResponse($this->transformData($model)); } /** * 更新现有资源 * * @param Request $request 请求对象 * @param int $id 资源ID * @return Response 响应对象 */ public function update(Request $request, int $id): Response { $data = $this->validateRequest($request->all(), 'update'); $result = $this->getService()->update($id, $data); return $result ? $this->successResponse(['id' => $id]) : $this->errorResponse('更新失败'); } /** * 删除资源 * * @param int $id 资源ID * @return Response 响应对象 */ public function destroy(int $id): Response { $result = $this->getService()->delete($id); return $result ? $this->successResponse(['id' => $id]) : $this->errorResponse('删除失败'); } }