interfaceAlias = $interfaceAlias; $this->websiteInfo = $websiteInfo; $this->websiteId = $this->websiteInfo['id']; // 获取接口信息 $interfaceInfo = DB::table('dep_interface')->where('is_use', 1)->where('is_delete', 0)->where('website_id', $this->websiteId)->where('en_alias', $this->interfaceAlias)->first(); if ($interfaceInfo) { $this->interfaceInfo = get_object_vars($interfaceInfo); // 请求参数配置 $this->interfaceSettingInfo = $this->interfaceInfo['request_settings'] ? json_decode($this->interfaceInfo['request_settings'], true) : []; // 条件配置 $this->conditionSettingsInfo = $this->interfaceInfo['condition_settings'] ? json_decode($this->interfaceInfo['condition_settings'], true) : []; // 请求结果配置 $this->interfaceResultInfo = $this->interfaceInfo['result_settings'] ? json_decode($this->interfaceInfo['result_settings'], true) : []; // 系统接口使用 if (1 == $this->interfaceInfo['category_type']) { $this->getAssembleInfo(); $this->getAssembleColumn(); } } else { abort(404, '接口不存在,请联系管理人员!'); } } /** * 验证数据,如果有错误,则返回错误信息. */ public function validator(): ?string { // 判断是否需要登录 if (isset($this->interfaceInfo['is_login']) && $this->interfaceInfo['is_login']) { if (!$this->isLogin()) { return '对不起,请先登录!'; } } // 如果没有配置参数,则默认验证成功 if (empty($this->interfaceSettingInfo)) { return null; } $validatorAttributes = []; $ruleArr = []; // 设置的参数都要全部循环一遍,因为可能会设置固定参数,这样即使不传入参数,也可以使用 foreach ($this->interfaceSettingInfo as $item) { $columnType = $item['column_type'] ?? 'input'; // 获取参数方式 Input Header Cookies Session File userId $columnType = strtolower($columnType); $column = $item['column']; $tableColumn = $item['true_column'] ?? ''; // 真实使用的表字段 $ruleArr[$tableColumn ?: $column] = $item['rules']; if ($item['is_fixed']) { // 固定值 $requestValue = trim($item['default']); } else { // 如果设置is_delete,则必须为固定值,防止被非法使用 if ('is_delete' == $column) { return '请求参数【' . $item['title'] . '(' . $item['column'] . ')】必须是固定值,不允许动态设置!!'; } // 获取请求的参数 if ('input' == $columnType) { $requestValue = Request::input($column, $item['default']); } elseif ('header' == $columnType) { $requestValue = Request::header($column, $item['default']); } elseif ('cookies' == $columnType) { $requestValue = Request::cookie($column, $item['default']); } elseif ('session' == $columnType) { $requestValue = Request::session()->get($column, $item['default']); } elseif ('file' == $columnType) { $requestValue = Request::file($column, $item['default']); } elseif ('userid' == $columnType) { // 必须登录才能获取用户信息 if (!$this->isLogin()) { return '对不起,请先登录!'; } $requestValue = $this->userId; } else { $requestValue = Request::input($column, $item['default']); } } $this->requestData[$tableColumn ?: $column] = $requestValue; $validatorAttributes[$tableColumn ?: $column] = $item['title']; // 使用函数 if (isset($item['type']) && $item['type'] && isset($item['conditions']) && $item['conditions']) { $allData = array_merge($this->requestData, ['interfaceInfo' => $this->interfaceInfo], ['websiteInfo' => $this->websiteInfo]); $this->execResult($item['conditions'], $allData); // 如果有执行失败的返回错误 foreach ($this->execResultArr as $exeItem) { // 1 错误继续 2 错误退出 3 直接退出 $back_type = $exeItem['back_type']; if (2 == $back_type) { if ($exeItem['error']) { return $exeItem['result']['msg'] ?? "系统错误"; } } elseif ($back_type == 3) { return $exeItem['result']; } } // 处理参数返回的结果 if ($backR = $this->exeRequestManage($item, $tableColumn ?: $column)) { return $backR; } } } // 如果没有设置is_delete,默认设置0,只获取没有被删除的数据 if (!isset($this->requestData['is_delete'])) { $this->requestData['is_delete'] = 0; } // 获取请求的参数的验证规则 list($validatorRules, $validatorMsgArr) = $this->getRequestRules($ruleArr); $validator = Validator::make($this->requestData, $validatorRules, $validatorMsgArr, $validatorAttributes); if ($validator->fails()) { return $validator->errors()->first(); } else { return null; } } // 处理参数返回的结果 private function exeRequestManage($item, $column) { // 获取返回的结果设置 if (empty($item['return_settings']['resultIndexes'])) { return ''; } $return_settings = $item['return_settings']; $resultIndexes = $return_settings['resultIndexes']; if (empty($resultIndexes)) { return '请求参数【' . $item['title'] . '(' . $item['column'] . ')】设置错误! - 请进入管理后台,重新设置!'; } if (count($resultIndexes) > 1) { $exeData = []; foreach ($resultIndexes as $indexes) { // 允许设置请求别名 $paramArr = explode(':', $indexes); $index = $paramArr[0]; $indexAlias = $paramArr[1] ?? ''; // 别名 if ($indexAlias) { $exeData[$indexAlias] = Arr::get($this->requestData, $index); } else { $exeData[] = Arr::get($this->requestData, $index); } } $this->requestData[$column] = $exeData; } else { // 允许设置请求别名 $paramArr = explode(':', $resultIndexes[0]); $index = $paramArr[0]; $this->requestData[$column] = Arr::get($this->requestData, $index); } } /** * 获取请求的参数的验证规则. * * @return array */ private function getRequestRules(array $ruleArr) { $validatorRules = []; $validatorMsgArr = []; foreach ($ruleArr as $column => $rules) { $typeArr = []; foreach ($rules as $ruleValue) { if (isset($ruleValue['type']) && 2 === $ruleValue['type']) { // 自定义规则 $typeArr[] = $this->getRuleClass($ruleValue); } else { // laravel验证规则 if (isset($ruleValue['params']) && ($ruleValue['params'] || $ruleValue['params'] == 0)) { $typeArr[] = $ruleValue['rule'] . ':' . $ruleValue['params']; } else { $typeArr[] = $ruleValue['rule']; } if (!empty($ruleValue['error_msg'])) { $validatorMsgArr[$column . '.' . $ruleValue['rule']] = $ruleValue['error_msg']; } } } $validatorRules[$column] = implode('|', $typeArr); } return [$validatorRules, $validatorMsgArr]; } /** * 判断是否已经登录. */ public function isLogin(): bool { $loginToken = new LoginTokenService($this->websiteInfo['token_table']); $this->tokenInfo = $loginToken->checkLogin(); if ($this->tokenInfo) { $this->userId = $this->tokenInfo->user_id; return true; } else { return false; } } /** * 获取当前接口信息. * * @return array */ public function getInterfaceInfo(): array { return $this->interfaceInfo; } /** * 获取请求的数据. */ public function getRequestData(string $key = ''): array|string { if ($key) { return $this->requestData[$key] ?? ''; } return $this->requestData; } /** * 设置请求的参数 可以对请求参数进行修改. * * @return $this */ public function setRequestData(array $data): self { Request::merge($data); return $this; } /** * 获取集合(表)信息. */ public function getAssembleInfo(): void { $assembleInfo = DB::table('sys_assemble')->find($this->interfaceInfo['assemble_id']); $this->assembleInfo = get_object_vars($assembleInfo); $this->table = $this->assembleInfo['schema']; } /** * 获取表对应的字段列表,支持扩展表. * * @return void */ public function getAssembleColumn(): void { $assembleInfo = DB::table('sys_assemble_column')->where('assemble_id', $this->interfaceInfo['assemble_id'])->where('is_delete', 0)->get(); $this->assembleColumns = array_map('get_object_vars', $assembleInfo->toArray()); // id 和 字段 组成的数组 $idArr = array_column($this->assembleColumns, 'id'); $codeArr = array_column($this->assembleColumns, 'code'); $this->columnIdToCodes = array_combine($idArr, $codeArr); } /** * @throws \Exception */ public function exec(): JsonResponse|null { if (1 == $this->interfaceInfo['category_type']) { return $this->interfaceSysExec(); } else { return $this->interfaceCustomExec(); } } /** * 系统接口. * * @throws \Exception */ public function interfaceSysExec(): JsonResponse { $manager = new DbManager($this->interfaceInfo, $this->assembleInfo, $this->assembleColumns, $this->requestData, $this->columnIdToCodes); return $this->manageResult($manager->exec()); } // 混合接口 public function interfaceCustomExec(): JsonResponse|null { $conditionsArr = $this->interfaceInfo['conditions']; if ($conditionsArr) { $allData = $this->requestData; $this->execResult($conditionsArr, $allData); // 如果有执行失败的返回错误 foreach ($this->execResultArr as $exeItem) { // 1 错误继续 2 错误退出 3 直接退出 $back_type = $exeItem['back_type']; if (2 == $back_type) { if ($exeItem['error']) { return responseMessage($exeItem['result']['code'], $exeItem['result']['msg'] ?? "系统错误"); } } elseif ($back_type == 3) { return responseMessage($exeItem['result']['code'], $exeItem['result']['msg'] ?? "系统错误", $exeItem['result']); } } // 处理参数返回的结果 if ($backR = $this->exeRequestManage($exeItem, 'backRe')) { return responseMessage(3001, $backR); } } // 如果设置了返回值,则使用返回值 if ($this->requestData['backRe']) { $result = $this->requestData['backRe']; return responseMessage(1001, '', $result); } else { return null; } } /** * 结果集,操作并返回. */ public function manageResult(mixed $result): JsonResponse { if ($result instanceof JsonResponse) { return $result; } $resultInfo = $this->interfaceResultInfo; if (empty($resultInfo)) { return responseMessage(1001, '恭喜你,操作成功!', $result); } $operateType = $this->interfaceInfo['operate_type']; if (1 === $operateType) { // 关联查询 if ($resultInfo['is_use_relate'] && $resultInfo['relate_interfaces']) { foreach ($resultInfo['relate_interfaces'] as $relate) { $search_type = $this->interfaceInfo['search_type']; if (1 === $search_type) { // 单条信息查询 $result = $this->getChildrenInterfaceResult($result, $relate); } elseif (2 === $search_type) { // 分页查询 foreach ($result['list'] as $key => $row) { $row = is_array($row) ? $row : get_object_vars($row); $result['list'][$key] = $this->getChildrenInterfaceResult($row, $relate); } } elseif (3 === $search_type) { // 列表 foreach ($result as $key => $row) { $row = is_array($row) ? $row : get_object_vars($row); $result[$key] = $this->getChildrenInterfaceResult($row, $relate); } } } } } if ($result || 0 === $result || [] === $result) { // 成功的操作 if (2 == $resultInfo['success']['type']) { // 有操作 $conditionsArr = $resultInfo['success']['conditions']; $allData = array_merge($this->requestData, ['&result' => $result]); if ($conditionsArr) { $this->execResult($conditionsArr, $allData); // 如果有执行失败的返回错误 foreach ($this->execResultArr as $exeItem) { // 1 错误继续 2 错误退出 3 直接退出 $back_type = $exeItem['back_type']; if (2 == $back_type) { if ($exeItem['error']) { return responseMessage($exeItem['result']['code'], $exeItem['result']['msg'] ?? "系统错误"); } } elseif ($back_type == 3) { return responseMessage($exeItem['result']['code'], $exeItem['result']['msg'] ?? "系统错误", $exeItem['result']['result']); } } // 处理参数返回的结果 if ($backR = $this->exeRequestManage($resultInfo['success'], 'backRe')) { return responseMessage(3001, $backR); } } } /* * 如果查询详情,直接返回字段值 * 为了方便设置参数的时候,可以直接使用返回值 */ if (1 == $operateType && 1 == $this->interfaceInfo['search_type']) { if (isset($resultInfo['column_only_one']) && $resultInfo['column_only_one']) { $column_id = $resultInfo['columns']; $columnName = $this->columnIdToCodes[$column_id]; $result = $result[$columnName] ?? null; } } // 如果设置了返回值,则使用返回值, 不能判断返回值是否为空 $result = $this->requestData['backRe'] ?? $result; return responseMessage(1001, $resultInfo['success']['msg'], $result); } else { // 失败的操作 if (2 == $resultInfo['failed']['type']) { // 有操作 $conditionsArr = $resultInfo['failed']['conditions']; $allData = array_merge($this->requestData, ['&result' => $result]); if ($conditionsArr) { $this->execResult($conditionsArr, $allData); // 如果有执行失败的返回错误 foreach ($this->execResultArr as $exeItem) { // 1 错误继续 2 错误退出 3 直接退出 $back_type = $exeItem['back_type']; if (2 == $back_type) { if ($exeItem['error']) { return responseMessage($exeItem['result']['code'], $exeItem['result']['msg'] ?? "系统错误"); } } elseif ($back_type == 3) { return responseMessage($exeItem['result']['code'], $exeItem['result']['msg'] ?? "系统错误", $exeItem['result']['result']); } } // 处理参数返回的结果 if ($backR = $this->exeRequestManage($resultInfo['failed'], 'backReFailed')) { return responseMessage(3001, $backR); } } } return responseMessage(2001, $resultInfo['failed']['msg'], $this->requestData['backReFailed'] ?? ''); } } /** * @param array $conditionsArr * @param array $allData * @return void * @throws \GuzzleHttp\Exception\GuzzleException */ private function execResult(array $conditionsArr, array $allData) { foreach ($conditionsArr as $item) { if (1 == $item['type']) { // 内容处理 $expressionResult = $this->execManageCon($allData, $item); $flag_uuid = $expressionResult['flag_uuid'] ?? ''; $this->execResultArr[$flag_uuid] = $expressionResult; $this->requestData[$flag_uuid] = $expressionResult['result']['result'] ?? []; // 返回的的是 backJson() [error=>0,code=>0,result=>[]] // 1 错误继续 2 错误退出 3 直接退出 $back_type = $expressionResult['back_type']; if (2 == $back_type) { if ($expressionResult['error']) { break; } } elseif ($back_type == 3) { break; } } elseif (2 == $item['type']) { // 判断条件 $this->checkExpression('result_type', $allData, $item['condition']); } } } // ###################################################################### /** * 执行参数,判断参数的真假. * * @param string $searchOrResultType 判断是搜索内容处理,还是结果集内容管理 * * @throws \GuzzleHttp\Exception\GuzzleException */ private function checkExpression(string $searchOrResultType, array $data, array $condition, $find = null) { // if else 转换为代码执行 foreach ($condition as $judge) { // 参数 $paramsArr = $judge['params']; $resultBool = $parentCurrent = true; foreach ($paramsArr as $key => $row) { if (0 == $key) { $resultBool = $parentCurrent = $this->execExpression($data, $row); } else { if ('and' == $row['type']) { if (true === $parentCurrent) { $resultBool = $parentCurrent = $this->execExpression($data, $row); } else { $resultBool = false; break; } } elseif ('or' == $row['type']) { if (true === $parentCurrent) { $resultBool = true; break; } else { $resultBool = $parentCurrent = $this->execExpression($data, $row); } } } } if ($resultBool) { // 为真,执行内容 $manageCon = $judge['manageCon']; foreach ($manageCon as $con) { $expressionResult = $this->execManageCon($data, $con); // 根据返回类型判断,有的需要直接返回,不再继续向下执行 // 1 错误继续 2 错误退出 3 直接退出 $back_type = $expressionResult['back_type']; $backResult = $expressionResult['result'] ?? []; $flag_uuid = $expressionResult['flag_uuid'] ?? ''; $this->execResultArr[$flag_uuid] = $expressionResult; $this->requestData[$flag_uuid] = $backResult['result']; if (2 == $back_type) { if ($expressionResult['error']) { // 如果出现错误,则返回错误信息 return $backResult; } } elseif (3 == $back_type) { break; } } // 停止条件语句 break; } } } /** * 执行函数或表达式. */ private function execExpression(array $data, array $row): bool { $operate = $row['operate']; if ($row['isUseFn']) { // 使用函数 $fnName = $row['fnName']; $fnParams = $row['fnParams']; // 如果存在! 则先截取出来 preg_match('/^(!*)(.*)/', $fnName, $fnArr); $flag = $fnArr[1]; $fnName = $fnArr[2]; foreach ($fnParams as $requestColumn) { $val = Arr::get($data, $requestColumn); if ($val) { $fParams[] = $val; } else { $fParams[] = $requestColumn; } } $result = $this->execFunc($fnName, $fParams); if ($result) { $current = true; } else { $current = false; } // ! 非操作符 if ($flag) { if (1 == \strlen($flag) % 2) { $current = !$current; } } } else { // 表达式 $requestVal = Arr::get($data, $row['params']); if ($requestVal || 0 === $requestVal || true === $requestVal || false === $requestVal) { $current = $this->getOperateResult($requestVal, $operate, $row['value']); } else { $current = false; } } return $current; } /** * 操作符的操作结果. */ private function getOperateResult(mixed $requestVal, string $operate, mixed $value): bool { return match ($operate) { 'true' => true == $requestVal, 'false' => false == $requestVal, '==' => $requestVal == $value, '>' => $requestVal > $value, '<' => $requestVal < $value, '>=' => $requestVal >= $value, '<=' => $requestVal <= $value, '!=' => $requestVal != $value, '===' => $requestVal === $value, '!==' => $requestVal !== $value, 'in' => \in_array($value, $requestVal), default => false, }; } /** * 普通查询处理. * * @throws \GuzzleHttp\Exception\GuzzleException */ private function execManageCon(array $data, array $con): array|null { $success_code = (isset($con['success_code']) && $con['success_code']) ? $con['success_code'] : 1001; $success_msg = (isset($con['success_msg']) && $con['success_msg']) ? $con['success_msg'] : '恭喜你,操作成功!'; $failed_code = (isset($con['failed_code']) && $con['failed_code']) ? $con['failed_code'] : 2001; $failed_msg = (isset($con['failed_msg']) && $con['failed_msg']) ? $con['failed_msg'] : '操作失败,请稍后再试!'; // 返回类型为5,直接返回,终止执行 if (5 == $con['back_type']) { $backResult = backJson($failed_code, $failed_msg); return ['error' => false, 'back_type' => $con['back_type'], 'result' => $backResult]; } $requestParams = []; if (isset($con['column']) && $con['column']) { foreach ($con['column'] as $requestColumn) { // 允许设置请求别名 $paramArr = explode(':', $requestColumn); $column = $paramArr[0]; $requestColumn = $paramArr[1] ?? $column; $val = Arr::get($data, $column); $requestParams[$requestColumn] = $val; } } $param_type = $con['param_type']; // 1 接口 2 函数 3 自定义接口 if (1 == $param_type) { // 接口 if (empty($con['interface_id'])) { // 不允许参数为空 abort(508, '结果集设置错误!- 接口地址不存在!'); } $result = $this->execInterface($con['interface_id'], $requestParams); } elseif (2 == $param_type) { // 函数 if (empty($con['fnName'])) { // 不允许参数为空 abort(508, '结果集设置错误!- 系统函数或方法不存在!'); } $result = $this->execFunc($con['fnName'], array_values($requestParams)); } elseif (3 == $param_type) { // 自定义接口 if (empty($con['url'])) { // 不允许参数为空 abort(508, '结果集设置错误!- 系统函数或方法不存在!'); } // todo 可以在前端添加一个client请求设置菜单,可以设置微信支付接口,支付宝接口,百度网盘接口等等 $url = $con['url']; $method = 'POST'; $options = [ 'json' => $requestParams, 'timeout' => 60, 'verify' => false, ]; if (!Str::startsWith($url, 'http')) { if (Str::contains($url, '/')) { $url = secure_url($url); } else { // 路由名生成一个 URL $url = route($url); } } $client = new Client(); $result = $client->request($method, $url, $options); } if ($result instanceof JsonResponse) { $backResult = json_decode($result->getContent(), true); return ['error' => $backResult['error'], 'back_type' => $con['back_type'], 'flag_uuid' => $con['flag_uuid'] ?? '', 'result' => $backResult]; } elseif (empty($result) || (isset($result['error']) && $result['error'])) { $backResult = backJson($result['code'] ?? $failed_code, $result['msg'] ?? $failed_msg); return ['error' => true, 'back_type' => $con['back_type'], 'result' => $backResult]; } else { $backResult = backJson($success_code, $success_msg, isset($result['error']) ? $result['result'] : $result); return ['error' => false, 'back_type' => $con['back_type'], 'flag_uuid' => $con['flag_uuid'] ?? '', 'result' => $backResult]; } } /** * 执行函数. */ private function execFunc(string $function, array $params = []): mixed { if (empty($function)) { abort(508, '结果集设置错误!- 函数为空'); } if (($flag1 = Str::contains($function, '@')) || Str::contains($function, '::')) { $arr = explode($flag1 ? '@' : '::', $function); $class = $arr[0] ?? ''; $method = $arr[1] ?? 'handler'; if ($class && class_exists($class)) { if ($flag1) { return \call_user_func_array([new $class($this->requestData, $this->interfaceInfo, $this->websiteInfo, $this->assembleInfo, $this->assembleColumns), $method], $params); } else { return \call_user_func_array([$class, $method], $params); } } else { abort(508, '接口参数设置错误!- 类【' . $class . '】不存在'); } } elseif (\function_exists($function)) { // 如果是函数,则使用函数处理,参数:当前字段信息,接口信息,站点信息 return \call_user_func_array($function, $params); } return ''; } /** * 请求系统接口. * * @throws \Exception */ private function execInterface(int $interfaceId, $requestParams = []): mixed { if (empty($interfaceId)) { abort(508, '结果集设置错误!- 设置的接口为空!'); } // 获取接口信息 $otherInterfaceInfo = DB::table('dep_interface')->find($interfaceId); if ($otherInterfaceInfo) { $interfaceService = new static($otherInterfaceInfo->en_alias, $this->websiteInfo); $interfaceService->setRequestData($requestParams); // 设置参数 $errorMsg = $interfaceService->validator(); if ($errorMsg) { abort(508, '结果集设置错误!- ' . $otherInterfaceInfo->en_alias . $errorMsg); } else { $response = $interfaceService->exec(); return json_decode($response->getContent(), true); } } else { abort(508, '结果集设置错误!- 设置的接口不存在'); } } /** * 获取字段关联的接口返回的数据. * * @throws \Exception */ private function getChildrenInterfaceResult(array $row, array $relateInfo): mixed { $data = array_merge($this->requestData, ['&result' => $row]); $relateInterfaceId = $relateInfo['interface_id'] ?? ''; if ($relateInterfaceId) { // 获取接口信息 $interfaceInfo = DB::table('dep_interface')->find($relateInterfaceId); $interfaceAlias = $interfaceInfo->en_alias; // 参数设置 $requestParams = []; foreach ($relateInfo['params'] as $item) { // 允许设置请求别名 $paramArr = explode(':', $item); $column = $paramArr[0]; $requestColumn = $paramArr[1] ?? $column; $val = Arr::get($data, $column); //如果没有设置别名,则正则替换 result 为 result_ $requestColumn = preg_replace('/&result\./', 'result_', $requestColumn); $requestParams[$requestColumn] = $val; } // 请求接口 $interfaceService = new static($interfaceAlias, $this->websiteInfo); $interfaceService->setRequestData($requestParams); // 优点,不会干扰现有的数据,除了固定参数外,其他参数允许接收,如果和固定参数有相同的key,则被覆盖 $errorMsg = $interfaceService->validator(); if ($errorMsg) { // 为了不干扰原有字段 $row[$relateInfo['column']] = "error_" . $errorMsg; } else { $response = $interfaceService->exec(); $data = json_decode($response->getContent(), true); $row[$relateInfo['column']] = $data['result'] ?? []; } } return $row; } /** * @param $ruleValue * * @return mixed */ private function getRuleClass($ruleValue) { $class = ucfirst(Str::camel($ruleValue['rule'])); $class = '\\App\\Rules\\' . $class . 'Rule'; $ruleObj = new $class($this->table, $ruleValue, $this->websiteInfo['en_alias']); // todo 可以在前端页面,设置自定义规则 if ($ruleObj instanceof EsUniqueRule) { $operateType = $this->interfaceInfo['operate_type']; if (2 === $operateType || 3 === $operateType) { return (new EsUniqueRule($this->table, $ruleValue))->ignore($this->getRequestData('id')); } } elseif ($ruleObj instanceof EsExistsRule) { return new EsExistsRule($this->table, $ruleValue); } return $ruleObj; } }