From f3a672b5c914cdb5a405511e68425eeeb38fab0a Mon Sep 17 00:00:00 2001 From: Aether Date: Fri, 26 Sep 2025 15:36:09 +0800 Subject: [PATCH] .. --- app/JsonRpc/Service/DataService.php | 58 ++++++++ config/autoload/nacos.php | 2 +- config/autoload/services.php | 38 +----- extend/Aether/PHP/Hyperf/AetherModel.php | 125 ++++++++++++++++++ .../Aether/PHP/Hyperf/RpcExceptionHandler.php | 93 +++++++++++-- .../src/Contract/DataServiceInterface.php | 25 +++- 6 files changed, 295 insertions(+), 46 deletions(-) diff --git a/app/JsonRpc/Service/DataService.php b/app/JsonRpc/Service/DataService.php index b66460d..150beeb 100644 --- a/app/JsonRpc/Service/DataService.php +++ b/app/JsonRpc/Service/DataService.php @@ -21,6 +21,39 @@ class DataService implements DataServiceInterface #[Inject] protected CampusValidator $campusValidator; + public function getCampuses(array $data): array + { + // TODO list 方法存在问题 + return $this->campusModel->list($data); + } + + public function getCampusBy(int $id): array + { + $campus = $this->campusModel->find($id); + if (! $campus || $campus->status != 1) { + throw new BusinessException('校区不存在或已禁用', 10001); + } + return $campus->toArray(); + } + + public function createCampus(array $data): int + { + $this->campusValidator->scene('create', $data)->check(); + $campus = $this->campusModel->create($data); + // $this->tran + return $campus->id; + } + + public function updateCampus(int $id, array $data): int + { + // TODO: Implement updateCampus() method. + } + + public function deleteCampus(int $id): bool + { + // TODO: Implement deleteCampus() method. + } + public function getCampusById(int $id): array { $campus = Campus::find($id); @@ -133,4 +166,29 @@ class DataService implements DataServiceInterface 'list' => $list, ]; } + + public function getTeachers(): array + { + // TODO: Implement getTeachers() method. + } + + public function getTeacherBy(int $id): array + { + // TODO: Implement getTeacherBy() method. + } + + public function createTeacher(array $data): int + { + // TODO: Implement createTeacher() method. + } + + public function updateTeacher(int $id, array $data): int + { + // TODO: Implement updateTeacher() method. + } + + public function deleteTeacher(int $id): bool + { + // TODO: Implement deleteTeacher() method. + } } diff --git a/config/autoload/nacos.php b/config/autoload/nacos.php index b02b825..abda78f 100644 --- a/config/autoload/nacos.php +++ b/config/autoload/nacos.php @@ -10,7 +10,7 @@ return [ 'port' => env('NACOS_PORT', 8848), // 'username' => env('NACOS_USERNAME', 'nacos'), // 'password' => env('NACOS_PASSWORD', 'nacos'), - 'namespace' => env('NACOS_NAMESPACE', 'dev'), + 'namespace' => env('NACOS_NAMESPACE_ID', 'dev'), 'timeout' => 5.0, ], ]; diff --git a/config/autoload/services.php b/config/autoload/services.php index 785609a..daeb701 100644 --- a/config/autoload/services.php +++ b/config/autoload/services.php @@ -1,6 +1,7 @@ true, 'register' => true, ], - 'consumers' => [ - // 数据服务消费者配置 - [ - 'name' => 'DataService', - 'service' => DataServiceInterface::class, - 'registry' => [ - 'protocol' => 'nacos', - 'address' => 'http://192.168.28.199:8848/', - ], - 'protocol' => 'jsonrpc-http', - 'options' => [ - 'timeout' => 0.5, - ], - ], - ], - 'providers' => [ - [ - 'id' => 'hyperf-gateway', - 'service' => 'hyperf-gateway', - 'protocol' => 'jsonrpc-http', - 'load_balancer' => 'random', - 'registry' => [ - 'protocol' => 'nacos', - 'address' => 'http://192.168.28.199:8848', - ], - ], - ], + 'consumers' => [], + 'providers' => [], 'drivers' => [ 'nacos' => [ // nacos server url like https://nacos.hyperf.io, Priority is higher than host:port // 'url' => '', // The nacos host info - 'host' => env('NACOS_HOST', '192.168.28.199'), + 'host' => env('NACOS_HOST', '127.0.0.1'), 'port' => env('NACOS_PORT', 8848), // The nacos account info // 'username' => null, @@ -48,8 +24,8 @@ return [ 'guzzle' => [ 'config' => null, ], - 'group_name' => env('NACOS_GROUP', 'api'), - 'namespace_id' => env('NACOS_NAMESPACE_ID', 'dev'), + 'group_name' => env('NACOS_GROUP', 'DEFAULT_GROUP'), + 'namespace_id' => env('NACOS_NAMESPACE', 'dev'), 'heartbeat' => 5, ], ], diff --git a/extend/Aether/PHP/Hyperf/AetherModel.php b/extend/Aether/PHP/Hyperf/AetherModel.php index a828f94..9bbeefc 100644 --- a/extend/Aether/PHP/Hyperf/AetherModel.php +++ b/extend/Aether/PHP/Hyperf/AetherModel.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Aether; +use Aether\Contract\TreeableInterface; use Closure; use DateTime; use Exception; @@ -91,6 +92,60 @@ abstract class AetherModel extends HyperfModel implements CacheableInterface return null; } + /** + * 列表查询. + */ + // public function list(array $params = []): array + // { + // $query = $this->newQuery(); + // + // // 通过模型配置自动应用所有搜索条件 + // $this->applySearch($query, $params); + // + // // 动态应用排序 + // $sortConfig = $this->getSortConfig(); + // if ($sortConfig) { + // $query->orderBy($sortConfig['field'], $sortConfig['direction']); + // } + // + // $withDeleted = filter_var($params['with_deleted'] ?? false, FILTER_VALIDATE_BOOLEAN); + // if ($withDeleted) { + // $query->withTrashed(); + // } + // + // // 存在分页参数(page或size)则进行分页查询 + // if (isset($params['page']) || isset($params['size'])) { + // $page = (int) ($params['page'] ?? 1); + // $size = (int) ($params['size'] ?? 10); + // $page = max(1, $page); + // $size = max(1, min(100, $size)); + // $result = $query->paginate($size, ['*'], 'page', $page); + // return [ + // 'total' => $result->total(), + // 'list' => $result->items(), + // ]; + // } + // + // // 无分页参数时返回完整数据集合 + // $items = $query->get()->toArray(); + // + // // 若模型支持树形结构则构建树形,否则返回普通数组 + // if ($this instanceof TreeableInterface) { + // return $this::buildTree($items, (int) ($params['parent_id'] ?? 0)); + // } + // + // return $items; + // } + + /** + * 列表查询. + */ + public function list(array $params = []): array + { + $query = $this->buildQueryFromParams($params); + return $this->listResult($query, $params); + } + /** * 快捷创建. */ @@ -238,6 +293,76 @@ abstract class AetherModel extends HyperfModel implements CacheableInterface return Db::transaction($closure); } + /** + * 根据参数构建查询. + */ + protected function buildQueryFromParams(array $params = []): Builder + { + // 创建查询构建器 + $query = static::query(); + + // 应用搜索条件 + // if (isset($params['search'])) { + // $this->applySearch($query, $params['search']); + // } + $this->applySearch($query, $params); + // 应用排序 + $this->applySorting($query, $params); + + // 处理软删除 + if (isset($params['withTrashed']) && $params['withTrashed']) { + $query->withTrashed(); + } + + return $query; + } + + /** + * 应用排序. + */ + protected function applySorting(Builder $query, array $params = []): void + { + // 优先使用传入的排序参数 + if (isset($params['sort_field'], $params['sort_direction'])) { + $query->orderBy($params['sort_field'], $params['sort_direction']); + return; + } + + // 使用模型配置的排序 + $sortConfig = $this->getSortConfig(); + if (! empty($sortConfig)) { + $query->orderBy($sortConfig['field'], $sortConfig['direction']); + } + } + + /** + * 根据分页参数获取结果. + */ + protected function listResult(Builder $query, array $params = []): array + { + // 分页处理 + if (isset($params['page'], $params['size'])) { + $page = max(1, (int) $params['page']); + $size = max(1, min(100, (int) $params['size'])); + $result = $query->paginate($size, ['*'], 'page', $page); + + return [ + 'list' => $result->items(), + 'total' => $result->total(), + ]; + } + + // 获取所有数据 + $result = $query->get()->toArray(); + + // 如果实现了树结构接口,构建树 + if ($this instanceof TreeableInterface) { + return $this->buildTree($result, $params['parent_id'] ?? 0); + } + + return $result; + } + /** * 初始化模型. */ diff --git a/extend/Aether/PHP/Hyperf/RpcExceptionHandler.php b/extend/Aether/PHP/Hyperf/RpcExceptionHandler.php index 783be37..ee3d7fb 100644 --- a/extend/Aether/PHP/Hyperf/RpcExceptionHandler.php +++ b/extend/Aether/PHP/Hyperf/RpcExceptionHandler.php @@ -4,38 +4,105 @@ declare(strict_types=1); namespace Aether; -use Hyperf\Context\ApplicationContext; use Hyperf\Context\Context; use Hyperf\ExceptionHandler\ExceptionHandler; -use Hyperf\Rpc\Protocol; +use Hyperf\HttpMessage\Stream\SwooleStream; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Psr\Http\Message\ResponseInterface; use Throwable; +use function Hyperf\Support\env; + class RpcExceptionHandler extends ExceptionHandler { /** - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface { - // 微服务间调用返回更精简的错误信息 - $data = [ - 'code' => $throwable->getCode() ?: 500, - 'message' => $throwable->getMessage() ?: '服务调用失败', - 'request_id' => Context::get('request_id', ''), - ]; + try { + // 获取请求ID(用于日志追踪) + $requestId = Context::get('request_id', ''); - $protocol = ApplicationContext::getContainer()->get(Protocol::class); - $response->getBody()->write($protocol->pack($data)); + // 从请求中获取可能的RPC ID + $rpcId = $this->getRpcIdFromRequest(); - return $response; + // 构建符合JSON-RPC 2.0规范的错误响应 + $errorResponse = [ + 'jsonrpc' => '2.0', + 'id' => $rpcId ?? $requestId, + 'error' => [ + 'code' => $throwable->getCode() ?: -32603, // 默认服务器错误码 + 'message' => $throwable->getMessage() ?: 'Internal error', + 'data' => [ + 'request_id' => $requestId, + 'exception_type' => get_class($throwable), + // 开发环境下可以添加更多调试信息 + 'debug' => env('APP_ENV') === 'dev' ? [ + 'file' => $throwable->getFile(), + 'line' => $throwable->getLine(), + ] : null, + ], + ], + ]; + + // JSON编码 + $jsonResponse = json_encode($errorResponse, JSON_UNESCAPED_UNICODE); + + // 检查JSON编码错误 + if (json_last_error() !== JSON_ERROR_NONE) { + $jsonResponse = json_encode([ + 'jsonrpc' => '2.0', + 'id' => $rpcId ?? $requestId, + 'error' => [ + 'code' => -32603, + 'message' => 'Failed to encode error response', + 'data' => ['request_id' => $requestId], + ], + ]); + } + + return $response + ->withHeader('Content-Type', 'application/json') + ->withBody(new SwooleStream((string) $jsonResponse)); + } catch (Throwable $e) { + $fallbackResponse = json_encode([ + 'jsonrpc' => '2.0', + 'id' => null, + 'error' => [ + 'code' => -32603, + 'message' => 'Fatal error occurred in exception handler', + 'data' => ['original_error' => $throwable->getMessage()], + ], + ]); + + return $response + ->withHeader('Content-Type', 'application/json') + ->withBody(new SwooleStream((string) $fallbackResponse)); + } } public function isValid(Throwable $throwable): bool { return true; } + + /** + * 尝试从请求中获取RPC ID. + */ + private function getRpcIdFromRequest(): mixed + { + try { + $request = Context::get('hyperf.request'); + if ($request) { + $body = $request->getParsedBody(); + if (is_array($body) && isset($body['id'])) { + return $body['id']; + } + } + } catch (Throwable $e) { + // 获取失败时静默处理 + } + return null; + } } diff --git a/extend/MicroService/src/Contract/DataServiceInterface.php b/extend/MicroService/src/Contract/DataServiceInterface.php index d6bd0aa..0091ca2 100644 --- a/extend/MicroService/src/Contract/DataServiceInterface.php +++ b/extend/MicroService/src/Contract/DataServiceInterface.php @@ -1,10 +1,23 @@