From 10b58a8c6dc1eca4fbdf7fa3ae895238aa9275bb Mon Sep 17 00:00:00 2001 From: Aether Date: Sun, 28 Sep 2025 14:51:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Handler/BusinessExceptionHandler.php | 2 +- config/autoload/exceptions.php | 5 +- .../Middleware/GatewayExceptionHandler.php | 96 +++++++++++++++++++ .../RpcException/ApiExceptionHandler.php | 54 +++++++++++ .../Hyperf/RpcException/BusinessException.php | 41 ++++++++ .../PHP/Hyperf/RpcException/ErrorCode.php | 47 +++++++++ .../RpcException/JsonRpcExceptionHandler.php | 47 +++++++++ .../Hyperf/RpcException/ValidateException.php | 38 ++++++++ 8 files changed, 326 insertions(+), 4 deletions(-) create mode 100644 extend/Aether/PHP/Hyperf/Middleware/GatewayExceptionHandler.php create mode 100644 extend/Aether/PHP/Hyperf/RpcException/ApiExceptionHandler.php create mode 100644 extend/Aether/PHP/Hyperf/RpcException/BusinessException.php create mode 100644 extend/Aether/PHP/Hyperf/RpcException/ErrorCode.php create mode 100644 extend/Aether/PHP/Hyperf/RpcException/JsonRpcExceptionHandler.php create mode 100644 extend/Aether/PHP/Hyperf/RpcException/ValidateException.php diff --git a/app/Exception/Handler/BusinessExceptionHandler.php b/app/Exception/Handler/BusinessExceptionHandler.php index dccf02f..b99b96c 100644 --- a/app/Exception/Handler/BusinessExceptionHandler.php +++ b/app/Exception/Handler/BusinessExceptionHandler.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace App\Exception\Handler; use Aether\AetherResponse; -use App\Exception\BusinessException; +use Aether\RpcException\BusinessException; use Hyperf\ExceptionHandler\ExceptionHandler; use Hyperf\HttpMessage\Stream\SwooleStream; use Psr\Http\Message\ResponseInterface; diff --git a/config/autoload/exceptions.php b/config/autoload/exceptions.php index 437cccd..515c607 100644 --- a/config/autoload/exceptions.php +++ b/config/autoload/exceptions.php @@ -1,13 +1,12 @@ [ 'jsonrpc-http' => [ - RpcExceptionHandler::class, + JsonRpcExceptionHandler::class, ], ], ]; diff --git a/extend/Aether/PHP/Hyperf/Middleware/GatewayExceptionHandler.php b/extend/Aether/PHP/Hyperf/Middleware/GatewayExceptionHandler.php new file mode 100644 index 0000000..7205012 --- /dev/null +++ b/extend/Aether/PHP/Hyperf/Middleware/GatewayExceptionHandler.php @@ -0,0 +1,96 @@ +handle($request); + } catch (Throwable $e) { + // 尝试解析异常信息,判断是否为RPC服务返回的业务异常 + $parsed = $this->parseRpcException($e); + + if ($parsed) { + // 转换为业务异常 + throw new BusinessException( + $parsed['code'], + $parsed['message'], + $parsed['data'] ?? [] + ); + } + + // 记录非业务异常日志 + $logger = ApplicationContext::getContainer()->get(StdoutLoggerInterface::class); + $logger->error(sprintf('服务调用异常: %s', $e->getMessage()), [ + 'trace' => $e->getTraceAsString(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + ]); + + // 非业务异常,使用系统错误码 + if ($e instanceof BusinessException) { + throw $e; + } + + // 统一转换为系统错误 + throw new BusinessException( + env('APP_ENV') === 'dev' ? $e->getMessage() : ErrorCode::getMessage(ErrorCode::RPC_CALL_ERROR), + ErrorCode::RPC_CALL_ERROR, + ); + } + } + + /** + * 解析RPC异常信息. + */ + private function parseRpcException(Throwable $e): ?array + { + try { + // 从异常消息中解析JSON数据 + $message = $e->getMessage(); + $data = json_decode($message, true); + + // 检查是否为有效的JSON-RPC错误响应 + if (json_last_error() === JSON_ERROR_NONE && isset($data['jsonrpc']) && $data['jsonrpc'] === '2.0') { + $error = $data['error'] ?? []; + + // 检查是否包含业务异常标识 + return [ + 'code' => $error['code'] ?? ErrorCode::RPC_CALL_ERROR, + 'message' => $error['message'] ?? '服务调用异常', + 'data' => $error['data'] ?? [], + ]; + + // 普通RPC错误 + } + + // 检查是否为直接返回的错误数组 + if (is_array($data) && isset($data['code'], $data['message'])) { + return $data; + } + } catch (Throwable $parseError) { + // 解析失败不影响主流程 + } + + return null; + } +} diff --git a/extend/Aether/PHP/Hyperf/RpcException/ApiExceptionHandler.php b/extend/Aether/PHP/Hyperf/RpcException/ApiExceptionHandler.php new file mode 100644 index 0000000..3394dc6 --- /dev/null +++ b/extend/Aether/PHP/Hyperf/RpcException/ApiExceptionHandler.php @@ -0,0 +1,54 @@ +getCode(); + $message = $throwable->getMessage(); + $data = $throwable->getData(); + } else { + // 其他异常 + $code = $throwable->getCode(); // ErrorCode::SYSTEM_ERROR; + $data = env('APP_ENV') === 'dev' ? [ + 'trace' => $throwable->getTraceAsString(), + 'file' => $throwable->getFile(), + 'line' => $throwable->getLine(), + ] : []; + $message = env('APP_ENV') === 'dev' ? $throwable->getMessage() : ErrorCode::getMessage($code); + } + + $result = [ + Config::RESPONSE_FIELD_KEY_CODE => $code, + Config::RESPONSE_FIELD_KEY_DATA => $data, + Config::RESPONSE_FIELD_KEY_MESSAGE => $message, + ]; + + $body = new SwooleStream(json_encode($result, JSON_UNESCAPED_UNICODE)); + + return $response->withHeader('Content-Type', 'application/json') + ->withStatus(200) + ->withBody($body); + } + + public function isValid(Throwable $throwable): bool + { + return true; + } +} diff --git a/extend/Aether/PHP/Hyperf/RpcException/BusinessException.php b/extend/Aether/PHP/Hyperf/RpcException/BusinessException.php new file mode 100644 index 0000000..6c18bd3 --- /dev/null +++ b/extend/Aether/PHP/Hyperf/RpcException/BusinessException.php @@ -0,0 +1,41 @@ +code = $code; + $this->data = $data; + + parent::__construct($message, $code, $previous); + } + + /** + * 获取额外数据. + */ + public function getData(): array + { + return $this->data; + } +} diff --git a/extend/Aether/PHP/Hyperf/RpcException/ErrorCode.php b/extend/Aether/PHP/Hyperf/RpcException/ErrorCode.php new file mode 100644 index 0000000..caba42d --- /dev/null +++ b/extend/Aether/PHP/Hyperf/RpcException/ErrorCode.php @@ -0,0 +1,47 @@ + '系统错误', + self::PARAM_ERROR => '参数错误', + self::AUTH_ERROR => '未授权', + self::FORBIDDEN_ERROR => '权限不足', + self::NOT_FOUND => '资源不存在', + self::DATA_NOT_FOUND => '数据不存在', + self::ARTICLE_NOT_FOUND => '文章不存在', + self::NOTICE_NOT_FOUND => '公告不存在', + ]; + + return $messages[$code] ?? '未知错误'; + } +} diff --git a/extend/Aether/PHP/Hyperf/RpcException/JsonRpcExceptionHandler.php b/extend/Aether/PHP/Hyperf/RpcException/JsonRpcExceptionHandler.php new file mode 100644 index 0000000..f865c32 --- /dev/null +++ b/extend/Aether/PHP/Hyperf/RpcException/JsonRpcExceptionHandler.php @@ -0,0 +1,47 @@ +getBody()->getContents(); + $responseContents = json_decode($responseContents, true); + if (! empty($responseContents['error'])) { + $port = null; + $config = ApplicationContext::getContainer()->get(ConfigInterface::class); + $servers = $config->get('server.servers'); + foreach ($servers as $k => $server) { + if ($server['name'] == 'jsonrpc-http') { + $port = $server['port']; + break; + } + } + $responseContents['error']['message'] .= " - {$config->get('app_name')}:{$port}"; + } + $data = json_encode($responseContents, JSON_UNESCAPED_UNICODE); + return $response->withStatus(200)->withBody(new SwooleStream($data)); + } + + public function isValid(Throwable $throwable): bool + { + return true; + } +} diff --git a/extend/Aether/PHP/Hyperf/RpcException/ValidateException.php b/extend/Aether/PHP/Hyperf/RpcException/ValidateException.php new file mode 100644 index 0000000..2bf3f93 --- /dev/null +++ b/extend/Aether/PHP/Hyperf/RpcException/ValidateException.php @@ -0,0 +1,38 @@ +code = $code; + $this->data = $data; + + parent::__construct($message, $code, $previous); + } + + public function getData(): array + { + return $this->data; + } +}