..
This commit is contained in:
@@ -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.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -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,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化模型.
|
* 初始化模型.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user