This commit is contained in:
Aether
2025-09-26 15:36:09 +08:00
parent 0c7142ad46
commit f3a672b5c9
6 changed files with 295 additions and 46 deletions

View File

@@ -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;
}
/**
* 初始化模型.
*/

View File

@@ -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;
}
}

View File

@@ -1,10 +1,23 @@
<?php
declare(strict_types=1);
namespace MicroService\Contract;
interface DataServiceInterface
{
// ----------------- 校区服务 -----------------
public function getCampuses(array $data): array;
public function getCampusBy(int $id): array;
public function createCampus(array $data): int;
public function updateCampus(int $id, array $data): int;
public function deleteCampus(int $id): bool;
/**
* 获取校区详情.
* @param int $id 校区ID
@@ -36,6 +49,16 @@ interface DataServiceInterface
// ----------------- 教师服务 -----------------
public function getTeachers(): array;
public function getTeacherBy(int $id): array;
public function createTeacher(array $data): int;
public function updateTeacher(int $id, array $data): int;
public function deleteTeacher(int $id): bool;
/**
* 获取教师详情.
* @param int $id 教师ID
@@ -71,4 +94,4 @@ interface DataServiceInterface
* @param int $size 每页条数
*/
public function searchTeachers(string $keyword, int $page = 1, int $size = 20): array;
}
}