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

@@ -21,6 +21,39 @@ class DataService implements DataServiceInterface
#[Inject] #[Inject]
protected CampusValidator $campusValidator; 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 public function getCampusById(int $id): array
{ {
$campus = Campus::find($id); $campus = Campus::find($id);
@@ -133,4 +166,29 @@ class DataService implements DataServiceInterface
'list' => $list, '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.
}
} }

View File

@@ -10,7 +10,7 @@ return [
'port' => env('NACOS_PORT', 8848), 'port' => env('NACOS_PORT', 8848),
// 'username' => env('NACOS_USERNAME', 'nacos'), // 'username' => env('NACOS_USERNAME', 'nacos'),
// 'password' => env('NACOS_PASSWORD', 'nacos'), // 'password' => env('NACOS_PASSWORD', 'nacos'),
'namespace' => env('NACOS_NAMESPACE', 'dev'), 'namespace' => env('NACOS_NAMESPACE_ID', 'dev'),
'timeout' => 5.0, 'timeout' => 5.0,
], ],
]; ];

View File

@@ -1,6 +1,7 @@
<?php <?php
use MicroService\Contract\DataServiceInterface; declare(strict_types=1);
use function Hyperf\Support\env; use function Hyperf\Support\env;
return [ return [
@@ -8,39 +9,14 @@ return [
'discovery' => true, 'discovery' => true,
'register' => true, 'register' => true,
], ],
'consumers' => [ 'consumers' => [],
// 数据服务消费者配置 'providers' => [],
[
'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',
],
],
],
'drivers' => [ 'drivers' => [
'nacos' => [ 'nacos' => [
// nacos server url like https://nacos.hyperf.io, Priority is higher than host:port // nacos server url like https://nacos.hyperf.io, Priority is higher than host:port
// 'url' => '', // 'url' => '',
// The nacos host info // 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), 'port' => env('NACOS_PORT', 8848),
// The nacos account info // The nacos account info
// 'username' => null, // 'username' => null,
@@ -48,8 +24,8 @@ return [
'guzzle' => [ 'guzzle' => [
'config' => null, 'config' => null,
], ],
'group_name' => env('NACOS_GROUP', 'api'), 'group_name' => env('NACOS_GROUP', 'DEFAULT_GROUP'),
'namespace_id' => env('NACOS_NAMESPACE_ID', 'dev'), 'namespace_id' => env('NACOS_NAMESPACE', 'dev'),
'heartbeat' => 5, 'heartbeat' => 5,
], ],
], ],

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Aether; namespace Aether;
use Aether\Contract\TreeableInterface;
use Closure; use Closure;
use DateTime; use DateTime;
use Exception; use Exception;
@@ -91,6 +92,60 @@ abstract class AetherModel extends HyperfModel implements CacheableInterface
return null; 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); 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; namespace Aether;
use Hyperf\Context\ApplicationContext;
use Hyperf\Context\Context; use Hyperf\Context\Context;
use Hyperf\ExceptionHandler\ExceptionHandler; use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\Rpc\Protocol; use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface; use Psr\Container\NotFoundExceptionInterface;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Throwable; use Throwable;
use function Hyperf\Support\env;
class RpcExceptionHandler extends ExceptionHandler class RpcExceptionHandler extends ExceptionHandler
{ {
/** /**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/ */
public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface
{ {
// 微服务间调用返回更精简的错误信息 try {
$data = [ // 获取请求ID用于日志追踪
'code' => $throwable->getCode() ?: 500, $requestId = Context::get('request_id', '');
'message' => $throwable->getMessage() ?: '服务调用失败',
'request_id' => Context::get('request_id', ''),
];
$protocol = ApplicationContext::getContainer()->get(Protocol::class); // 从请求中获取可能的RPC ID
$response->getBody()->write($protocol->pack($data)); $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 public function isValid(Throwable $throwable): bool
{ {
return true; 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 <?php
declare(strict_types=1);
namespace MicroService\Contract; namespace MicroService\Contract;
interface DataServiceInterface 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 * @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 * @param int $id 教师ID
@@ -71,4 +94,4 @@ interface DataServiceInterface
* @param int $size 每页条数 * @param int $size 每页条数
*/ */
public function searchTeachers(string $keyword, int $page = 1, int $size = 20): array; public function searchTeachers(string $keyword, int $page = 1, int $size = 20): array;
} }