优化
This commit is contained in:
@@ -20,9 +20,8 @@ abstract class AetherController
|
|||||||
#[Inject]
|
#[Inject]
|
||||||
protected ResponseInterface $response;
|
protected ResponseInterface $response;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取资源列表 (RESTFul: GET /resources)
|
* 获取资源列表 (RESTFul: GET resources/list).
|
||||||
*/
|
*/
|
||||||
public function index(): array
|
public function index(): array
|
||||||
{
|
{
|
||||||
@@ -32,7 +31,7 @@ abstract class AetherController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取单个资源 (RESTFul: GET /resources/{id})
|
* 获取单个资源 (RESTFul: GET resources/{id}).
|
||||||
*/
|
*/
|
||||||
public function detail(int $id): array
|
public function detail(int $id): array
|
||||||
{
|
{
|
||||||
@@ -41,7 +40,7 @@ abstract class AetherController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建资源 (RESTFul: POST /resources)
|
* 创建资源 (RESTFul: POST resources).
|
||||||
*/
|
*/
|
||||||
public function create(): array
|
public function create(): array
|
||||||
{
|
{
|
||||||
@@ -51,7 +50,7 @@ abstract class AetherController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新资源 (RESTFul: PUT /resources/{id})
|
* 更新资源 (RESTFul: PUT resources/{id}).
|
||||||
*/
|
*/
|
||||||
public function update(int $id): array
|
public function update(int $id): array
|
||||||
{
|
{
|
||||||
@@ -61,7 +60,7 @@ abstract class AetherController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除资源 (RESTFul: DELETE /resources/{id})
|
* 删除资源 (RESTFul: DELETE resources/{id}).
|
||||||
*/
|
*/
|
||||||
public function delete(int $id): array
|
public function delete(int $id): array
|
||||||
{
|
{
|
||||||
@@ -70,8 +69,7 @@ abstract class AetherController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取对应的服务类
|
* 获取对应的服务类.
|
||||||
* @return AetherCrudService
|
|
||||||
*/
|
*/
|
||||||
abstract protected function getService(): AetherCrudService;
|
abstract protected function getService(): AetherCrudService;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,20 +18,10 @@ use Throwable;
|
|||||||
*/
|
*/
|
||||||
abstract class AetherCrudService extends AetherService implements AetherCrudInterface
|
abstract class AetherCrudService extends AetherService implements AetherCrudInterface
|
||||||
{
|
{
|
||||||
#[Inject]
|
|
||||||
protected LoggerFactory $loggerFactory;
|
|
||||||
|
|
||||||
protected LoggerInterface $logger;
|
|
||||||
|
|
||||||
protected array $search = [];
|
protected array $search = [];
|
||||||
|
|
||||||
protected array $ignoreSearchFields = [];
|
protected array $ignoreSearchFields = [];
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->logger = $this->loggerFactory->get($this->getLoggerName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用列表查询(支持分页和树形结构).
|
* 通用列表查询(支持分页和树形结构).
|
||||||
*/
|
*/
|
||||||
@@ -85,7 +75,6 @@ abstract class AetherCrudService extends AetherService implements AetherCrudInte
|
|||||||
*/
|
*/
|
||||||
public function detail(int $id): object
|
public function detail(int $id): object
|
||||||
{
|
{
|
||||||
$this->logger->info('获取资源详情', ['id' => $id]);
|
|
||||||
return $this->getModel()->findOrFailById($id);
|
return $this->getModel()->findOrFailById($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,14 +251,6 @@ abstract class AetherCrudService extends AetherService implements AetherCrudInte
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取日志名称.
|
|
||||||
*/
|
|
||||||
protected function getLoggerName(): string
|
|
||||||
{
|
|
||||||
return strtolower((new ReflectionClass($this))->getShortName());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getSearch(): array
|
protected function getSearch(): array
|
||||||
{
|
{
|
||||||
return $this->search;
|
return $this->search;
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ use Hyperf\Contract\LengthAwarePaginatorInterface;
|
|||||||
use Hyperf\Database\Model\Builder;
|
use Hyperf\Database\Model\Builder;
|
||||||
use Hyperf\Database\Model\Collection;
|
use Hyperf\Database\Model\Collection;
|
||||||
use Hyperf\Database\Model\ModelNotFoundException;
|
use Hyperf\Database\Model\ModelNotFoundException;
|
||||||
use Hyperf\DbConnection\Model\Model as HyperfModel;
|
|
||||||
use Hyperf\DbConnection\Db;
|
use Hyperf\DbConnection\Db;
|
||||||
|
use Hyperf\DbConnection\Model\Model as HyperfModel;
|
||||||
use Hyperf\HttpServer\Contract\RequestInterface;
|
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||||
use Hyperf\ModelCache\Cacheable;
|
use Hyperf\ModelCache\Cacheable;
|
||||||
use Hyperf\ModelCache\CacheableInterface;
|
use Hyperf\ModelCache\CacheableInterface;
|
||||||
@@ -60,13 +60,19 @@ abstract class AetherModel extends HyperfModel implements CacheableInterface
|
|||||||
* 排序配置:
|
* 排序配置:
|
||||||
* - false: 禁用排序
|
* - false: 禁用排序
|
||||||
* - 字符串: 排序字段(默认升序)
|
* - 字符串: 排序字段(默认升序)
|
||||||
* - 数组: ['field' => '字段名', 'direction' => 'asc/desc']
|
* - 数组: ['field' => '字段名', 'direction' => 'asc/desc'].
|
||||||
*/
|
*/
|
||||||
protected string|array|bool $sortable = 'sort'; // 默认按sort字段升序
|
protected array|bool|string $sortable = 'sort'; // 默认按sort字段升序
|
||||||
|
|
||||||
|
public function __construct(array $attributes = [])
|
||||||
|
{
|
||||||
|
parent::__construct($attributes);
|
||||||
|
$this->bootBaseModel();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取排序配置
|
* 获取排序配置.
|
||||||
* @return array|null [field, direction] 或 null(禁用排序)
|
* @return null|array [field, direction] 或 null(禁用排序)
|
||||||
*/
|
*/
|
||||||
public function getSortConfig(): ?array
|
public function getSortConfig(): ?array
|
||||||
{
|
{
|
||||||
@@ -78,19 +84,13 @@ abstract class AetherModel extends HyperfModel implements CacheableInterface
|
|||||||
if (is_string($this->sortable)) {
|
if (is_string($this->sortable)) {
|
||||||
return [
|
return [
|
||||||
'field' => $this->sortable,
|
'field' => $this->sortable,
|
||||||
'direction' => 'asc'
|
'direction' => 'asc',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(array $attributes = [])
|
|
||||||
{
|
|
||||||
parent::__construct($attributes);
|
|
||||||
$this->bootBaseModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 快捷创建.
|
* 快捷创建.
|
||||||
*/
|
*/
|
||||||
@@ -108,7 +108,7 @@ abstract class AetherModel extends HyperfModel implements CacheableInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 快捷删除指定ID的记录
|
* 快捷删除指定ID的记录.
|
||||||
*
|
*
|
||||||
* @param int $id 要删除的记录ID
|
* @param int $id 要删除的记录ID
|
||||||
* @return bool 成功删除返回true
|
* @return bool 成功删除返回true
|
||||||
@@ -117,7 +117,7 @@ abstract class AetherModel extends HyperfModel implements CacheableInterface
|
|||||||
*/
|
*/
|
||||||
public static function deleteById(int $id): bool
|
public static function deleteById(int $id): bool
|
||||||
{
|
{
|
||||||
if (!static::query()->where('id', $id)->exists()) {
|
if (! static::query()->where('id', $id)->exists()) {
|
||||||
throw new ModelNotFoundException(sprintf(
|
throw new ModelNotFoundException(sprintf(
|
||||||
'找不到ID为 %d 的 %s 记录',
|
'找不到ID为 %d 的 %s 记录',
|
||||||
$id,
|
$id,
|
||||||
@@ -129,7 +129,6 @@ abstract class AetherModel extends HyperfModel implements CacheableInterface
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 快捷查找.
|
* 快捷查找.
|
||||||
* @param int $id
|
|
||||||
* @return Builder|Builder[]|Collection|HyperfModel
|
* @return Builder|Builder[]|Collection|HyperfModel
|
||||||
* @throws Exception 当删除操作发生其他错误时抛出
|
* @throws Exception 当删除操作发生其他错误时抛出
|
||||||
* @throws ModelNotFoundException
|
* @throws ModelNotFoundException
|
||||||
@@ -154,14 +153,14 @@ abstract class AetherModel extends HyperfModel implements CacheableInterface
|
|||||||
public static function findOrFailById(int $id): static
|
public static function findOrFailById(int $id): static
|
||||||
{
|
{
|
||||||
$record = static::query()->find($id);
|
$record = static::query()->find($id);
|
||||||
if(is_null($record)) {
|
if (is_null($record)) {
|
||||||
throw new ModelNotFoundException(sprintf(
|
throw new ModelNotFoundException(sprintf(
|
||||||
'找不到ID为 %d 的 %s 记录',
|
'找不到ID为 %d 的 %s 记录',
|
||||||
$id,
|
$id,
|
||||||
static::class
|
static::class
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return $record;// static::query()->findOrFail($id);
|
return $record; // static::query()->findOrFail($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -406,7 +405,7 @@ abstract class AetherModel extends HyperfModel implements CacheableInterface
|
|||||||
{
|
{
|
||||||
foreach ($conditions as $field => $value) {
|
foreach ($conditions as $field => $value) {
|
||||||
// 跳过非字符串的字段名(防止索引数组键导致的类型错误)
|
// 跳过非字符串的字段名(防止索引数组键导致的类型错误)
|
||||||
if (!is_string($field)) {
|
if (! is_string($field)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Aether;
|
namespace Aether;
|
||||||
|
|
||||||
use function Hyperf\Support\env;
|
use Hyperf\Context\Context;
|
||||||
use Hyperf\ExceptionHandler\ExceptionHandler;
|
use Hyperf\ExceptionHandler\ExceptionHandler;
|
||||||
use Hyperf\HttpMessage\Stream\SwooleStream;
|
use Hyperf\HttpMessage\Stream\SwooleStream;
|
||||||
use Psr\Http\Message\MessageInterface;
|
use Psr\Http\Message\MessageInterface;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Hyperf\Context\Context;
|
|
||||||
|
use function Hyperf\Support\env;
|
||||||
|
|
||||||
class ApiExceptionHandler extends ExceptionHandler
|
class ApiExceptionHandler extends ExceptionHandler
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Aether\Contract;
|
namespace Aether\Contract;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 树形结构接口,标识模型支持树形功能
|
* 树形结构接口,标识模型支持树形功能.
|
||||||
*/
|
*/
|
||||||
interface TreeableInterface
|
interface TreeableInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* 构建树形结构
|
* 构建树形结构.
|
||||||
* @param array $items 原始数据
|
* @param array $items 原始数据
|
||||||
* @param int $parentId 根节点ID
|
* @param int $parentId 根节点ID
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public static function buildTree(array $items, int $parentId = 0): array;
|
public static function buildTree(array $items, int $parentId = 0): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取子节点ID集合
|
* 获取子节点ID集合.
|
||||||
* @param int $id 节点ID
|
* @param int $id 节点ID
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public function getChildIds(int $id): array;
|
public function getChildIds(int $id): array;
|
||||||
}
|
}
|
||||||
@@ -5,14 +5,15 @@ declare(strict_types=1);
|
|||||||
namespace Aether\Exception;
|
namespace Aether\Exception;
|
||||||
|
|
||||||
use Aether\AetherValidator;
|
use Aether\AetherValidator;
|
||||||
use Hyperf\Contract\StdoutLoggerInterface;
|
use Hyperf\Context\Context;
|
||||||
use Hyperf\Database\Model\ModelNotFoundException; // 引入模型未找到异常
|
use Hyperf\Contract\StdoutLoggerInterface; // 引入模型未找到异常
|
||||||
|
use Hyperf\Database\Model\ModelNotFoundException;
|
||||||
use Hyperf\ExceptionHandler\ExceptionHandler;
|
use Hyperf\ExceptionHandler\ExceptionHandler;
|
||||||
use Hyperf\HttpMessage\Stream\SwooleStream;
|
use Hyperf\HttpMessage\Stream\SwooleStream;
|
||||||
use Hyperf\Validation\ValidationException;
|
use Hyperf\Validation\ValidationException;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Hyperf\Context\Context;
|
|
||||||
use function Hyperf\Support\env;
|
use function Hyperf\Support\env;
|
||||||
|
|
||||||
class AetherExceptionHandler extends ExceptionHandler
|
class AetherExceptionHandler extends ExceptionHandler
|
||||||
@@ -52,6 +53,11 @@ class AetherExceptionHandler extends ExceptionHandler
|
|||||||
->withBody(new SwooleStream(json_encode($result, JSON_UNESCAPED_UNICODE)));
|
->withBody(new SwooleStream(json_encode($result, JSON_UNESCAPED_UNICODE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isValid(Throwable $throwable): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private function formatErrorResponse(Throwable $throwable, string $requestId): array
|
private function formatErrorResponse(Throwable $throwable, string $requestId): array
|
||||||
{
|
{
|
||||||
// 模型未找到异常
|
// 模型未找到异常
|
||||||
@@ -61,10 +67,10 @@ class AetherExceptionHandler extends ExceptionHandler
|
|||||||
'message' => $throwable->getMessage() ?: '请求的资源不存在',
|
'message' => $throwable->getMessage() ?: '请求的资源不存在',
|
||||||
'data' => env('APP_ENV') === 'dev' ? [
|
'data' => env('APP_ENV') === 'dev' ? [
|
||||||
'file' => $throwable->getFile(),
|
'file' => $throwable->getFile(),
|
||||||
'line' => $throwable->getLine()
|
'line' => $throwable->getLine(),
|
||||||
] : null,
|
] : null,
|
||||||
'request_id' => $requestId,
|
'request_id' => $requestId,
|
||||||
'timestamp' => time()
|
'timestamp' => time(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,17 +91,20 @@ class AetherExceptionHandler extends ExceptionHandler
|
|||||||
'data' => env('APP_ENV') === 'dev' ? [
|
'data' => env('APP_ENV') === 'dev' ? [
|
||||||
'file' => $throwable->getFile(),
|
'file' => $throwable->getFile(),
|
||||||
'line' => $throwable->getLine(),
|
'line' => $throwable->getLine(),
|
||||||
'trace' => explode("\n", $throwable->getTraceAsString())
|
'trace' => explode("\n", $throwable->getTraceAsString()),
|
||||||
] : null,
|
] : null,
|
||||||
'request_id' => $requestId,
|
'request_id' => $requestId,
|
||||||
'timestamp' => time()
|
'timestamp' => time(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function formatValidationError(ValidationFailedException $e, string $requestId): array
|
private function formatValidationError(ValidationFailedException $e, string $requestId): array
|
||||||
{
|
{
|
||||||
$validatorInstance = new class extends AetherValidator {
|
$validatorInstance = new class extends AetherValidator {
|
||||||
protected function scenes(): array { return []; }
|
protected function scenes(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -104,17 +113,20 @@ class AetherExceptionHandler extends ExceptionHandler
|
|||||||
'data' => [
|
'data' => [
|
||||||
'errors' => $validatorInstance->formatValidationErrors($e->validator),
|
'errors' => $validatorInstance->formatValidationErrors($e->validator),
|
||||||
'scene' => $e->getScene(),
|
'scene' => $e->getScene(),
|
||||||
'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null
|
'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null,
|
||||||
],
|
],
|
||||||
'request_id' => $requestId,
|
'request_id' => $requestId,
|
||||||
'timestamp' => time()
|
'timestamp' => time(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function formatNativeValidationError(ValidationException $e, string $requestId): array
|
private function formatNativeValidationError(ValidationException $e, string $requestId): array
|
||||||
{
|
{
|
||||||
$validatorInstance = new class extends AetherValidator {
|
$validatorInstance = new class extends AetherValidator {
|
||||||
protected function scenes(): array { return []; }
|
protected function scenes(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -122,15 +134,10 @@ class AetherExceptionHandler extends ExceptionHandler
|
|||||||
'message' => '参数验证失败',
|
'message' => '参数验证失败',
|
||||||
'data' => [
|
'data' => [
|
||||||
'errors' => $validatorInstance->formatValidationErrors($e->validator),
|
'errors' => $validatorInstance->formatValidationErrors($e->validator),
|
||||||
'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null
|
'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null,
|
||||||
],
|
],
|
||||||
'request_id' => $requestId,
|
'request_id' => $requestId,
|
||||||
'timestamp' => time()
|
'timestamp' => time(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isValid(Throwable $throwable): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,13 +5,14 @@ declare(strict_types=1);
|
|||||||
namespace Aether\Exception;
|
namespace Aether\Exception;
|
||||||
|
|
||||||
use Aether\AetherValidator;
|
use Aether\AetherValidator;
|
||||||
|
use Hyperf\Context\Context;
|
||||||
use Hyperf\Contract\StdoutLoggerInterface;
|
use Hyperf\Contract\StdoutLoggerInterface;
|
||||||
use Hyperf\ExceptionHandler\ExceptionHandler;
|
use Hyperf\ExceptionHandler\ExceptionHandler;
|
||||||
use Hyperf\HttpMessage\Stream\SwooleStream;
|
use Hyperf\HttpMessage\Stream\SwooleStream;
|
||||||
use Hyperf\Validation\ValidationException;
|
use Hyperf\Validation\ValidationException;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Hyperf\Context\Context;
|
|
||||||
use function Hyperf\Support\env;
|
use function Hyperf\Support\env;
|
||||||
|
|
||||||
class AppExceptionHandler extends ExceptionHandler
|
class AppExceptionHandler extends ExceptionHandler
|
||||||
@@ -39,8 +40,13 @@ class AppExceptionHandler extends ExceptionHandler
|
|||||||
->withBody(new SwooleStream(json_encode($result, JSON_UNESCAPED_UNICODE)));
|
->withBody(new SwooleStream(json_encode($result, JSON_UNESCAPED_UNICODE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isValid(Throwable $throwable): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统一错误响应格式
|
* 统一错误响应格式.
|
||||||
*/
|
*/
|
||||||
private function formatErrorResponse(Throwable $throwable, string $requestId): array
|
private function formatErrorResponse(Throwable $throwable, string $requestId): array
|
||||||
{
|
{
|
||||||
@@ -61,21 +67,24 @@ class AppExceptionHandler extends ExceptionHandler
|
|||||||
'data' => env('APP_ENV') === 'dev' ? [
|
'data' => env('APP_ENV') === 'dev' ? [
|
||||||
'file' => $throwable->getFile(),
|
'file' => $throwable->getFile(),
|
||||||
'line' => $throwable->getLine(),
|
'line' => $throwable->getLine(),
|
||||||
'trace' => explode("\n", $throwable->getTraceAsString())
|
'trace' => explode("\n", $throwable->getTraceAsString()),
|
||||||
] : null,
|
] : null,
|
||||||
'request_id' => $requestId,
|
'request_id' => $requestId,
|
||||||
'timestamp' => time()
|
'timestamp' => time(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化自定义验证异常
|
* 格式化自定义验证异常.
|
||||||
*/
|
*/
|
||||||
private function formatValidationError(ValidationFailedException $e, string $requestId): array
|
private function formatValidationError(ValidationFailedException $e, string $requestId): array
|
||||||
{
|
{
|
||||||
// 复用AetherValidator的错误格式化方法
|
// 复用AetherValidator的错误格式化方法
|
||||||
$validatorInstance = new class extends AetherValidator {
|
$validatorInstance = new class extends AetherValidator {
|
||||||
protected function scenes(): array { return []; }
|
protected function scenes(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -84,20 +93,23 @@ class AppExceptionHandler extends ExceptionHandler
|
|||||||
'data' => [
|
'data' => [
|
||||||
'errors' => $validatorInstance->formatValidationErrors($e->validator),
|
'errors' => $validatorInstance->formatValidationErrors($e->validator),
|
||||||
'scene' => $e->getScene(), // 直接从异常获取场景
|
'scene' => $e->getScene(), // 直接从异常获取场景
|
||||||
'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null
|
'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null,
|
||||||
],
|
],
|
||||||
'request_id' => $requestId,
|
'request_id' => $requestId,
|
||||||
'timestamp' => time()
|
'timestamp' => time(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化原生验证异常(保持格式一致)
|
* 格式化原生验证异常(保持格式一致).
|
||||||
*/
|
*/
|
||||||
private function formatNativeValidationError(ValidationException $e, string $requestId): array
|
private function formatNativeValidationError(ValidationException $e, string $requestId): array
|
||||||
{
|
{
|
||||||
$validatorInstance = new class extends AetherValidator {
|
$validatorInstance = new class extends AetherValidator {
|
||||||
protected function scenes(): array { return []; }
|
protected function scenes(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -105,15 +117,10 @@ class AppExceptionHandler extends ExceptionHandler
|
|||||||
'message' => '参数验证失败',
|
'message' => '参数验证失败',
|
||||||
'data' => [
|
'data' => [
|
||||||
'errors' => $validatorInstance->formatValidationErrors($e->validator),
|
'errors' => $validatorInstance->formatValidationErrors($e->validator),
|
||||||
'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null
|
'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null,
|
||||||
],
|
],
|
||||||
'request_id' => $requestId,
|
'request_id' => $requestId,
|
||||||
'timestamp' => time()
|
'timestamp' => time(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isValid(Throwable $throwable): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,25 +5,30 @@ declare(strict_types=1);
|
|||||||
namespace Aether\Exception;
|
namespace Aether\Exception;
|
||||||
|
|
||||||
use Hyperf\Server\Exception\ServerException;
|
use Hyperf\Server\Exception\ServerException;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
class BusinessException extends ServerException
|
class BusinessException extends ServerException
|
||||||
{
|
{
|
||||||
// 错误码常量(按业务模块划分)
|
// 错误码常量(按业务模块划分)
|
||||||
public const VALIDATION_ERROR = 400; // 参数验证失败
|
public const VALIDATION_ERROR = 400; // 参数验证失败
|
||||||
|
|
||||||
public const AUTH_ERROR = 401; // 认证失败
|
public const AUTH_ERROR = 401; // 认证失败
|
||||||
|
|
||||||
public const PERMISSION_DENY = 403; // 权限不足
|
public const PERMISSION_DENY = 403; // 权限不足
|
||||||
|
|
||||||
public const RESOURCE_NOT_FOUND = 404; // 资源不存在
|
public const RESOURCE_NOT_FOUND = 404; // 资源不存在
|
||||||
|
|
||||||
public const SCENE_NOT_FOUND = 400;
|
public const SCENE_NOT_FOUND = 400;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 额外错误数据(如验证详情)
|
* 额外错误数据(如验证详情).
|
||||||
*/
|
*/
|
||||||
protected ?array $errorData = null;
|
protected ?array $errorData = null;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $message,
|
string $message,
|
||||||
int $code = 500,
|
int $code = 500,
|
||||||
?\Throwable $previous = null,
|
?Throwable $previous = null,
|
||||||
?array $errorData = null
|
?array $errorData = null
|
||||||
) {
|
) {
|
||||||
parent::__construct($message, $code, $previous);
|
parent::__construct($message, $code, $previous);
|
||||||
@@ -31,7 +36,7 @@ class BusinessException extends ServerException
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取额外错误数据
|
* 获取额外错误数据.
|
||||||
*/
|
*/
|
||||||
public function getErrorData(): ?array
|
public function getErrorData(): ?array
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Aether\Exception;
|
namespace Aether\Exception;
|
||||||
|
|
||||||
use Hyperf\Validation\ValidationException;
|
use Hyperf\Validation\ValidationException;
|
||||||
@@ -9,7 +11,7 @@ use Psr\Http\Message\ResponseInterface;
|
|||||||
class ValidationFailedException extends ValidationException
|
class ValidationFailedException extends ValidationException
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* 验证场景
|
* 验证场景.
|
||||||
*/
|
*/
|
||||||
protected string $scene;
|
protected string $scene;
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ declare(strict_types=1);
|
|||||||
namespace Aether;
|
namespace Aether;
|
||||||
|
|
||||||
use Hyperf\Context\ApplicationContext;
|
use Hyperf\Context\ApplicationContext;
|
||||||
|
use Hyperf\Context\Context;
|
||||||
use Hyperf\ExceptionHandler\ExceptionHandler;
|
use Hyperf\ExceptionHandler\ExceptionHandler;
|
||||||
use Hyperf\Rpc\Protocol;
|
use Hyperf\Rpc\Protocol;
|
||||||
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 Hyperf\Context\Context;
|
|
||||||
|
|
||||||
class RpcExceptionHandler extends ExceptionHandler
|
class RpcExceptionHandler extends ExceptionHandler
|
||||||
{
|
{
|
||||||
@@ -29,7 +29,6 @@ class RpcExceptionHandler extends ExceptionHandler
|
|||||||
];
|
];
|
||||||
|
|
||||||
$protocol = ApplicationContext::getContainer()->get(Protocol::class);
|
$protocol = ApplicationContext::getContainer()->get(Protocol::class);
|
||||||
// $response->getBody()->write($protocol->pack($data));
|
|
||||||
$response->getBody()->write($protocol->pack($data));
|
$response->getBody()->write($protocol->pack($data));
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ trait AetherEnum
|
|||||||
/**
|
/**
|
||||||
* 根据值获取枚举实例(严格模式).
|
* 根据值获取枚举实例(严格模式).
|
||||||
* @param int|string $value 枚举值
|
* @param int|string $value 枚举值
|
||||||
* @return AetherEnum|NoticeStatsModel|NoticeStatusEnum 枚举实例
|
* @return AetherEnum 枚举实例
|
||||||
*/
|
*/
|
||||||
public static function fromValue(int|string $value): self
|
public static function fromValue(int|string $value): self
|
||||||
{
|
{
|
||||||
@@ -95,7 +95,7 @@ trait AetherEnum
|
|||||||
/**
|
/**
|
||||||
* 根据描述获取枚举实例(精确匹配).
|
* 根据描述获取枚举实例(精确匹配).
|
||||||
* @param string $description 描述文本
|
* @param string $description 描述文本
|
||||||
* @return null|AetherEnum|NoticeStatsModel|NoticeStatusEnum 匹配的枚举实例,无匹配时返回null
|
* @return null|AetherEnum 匹配的枚举实例,无匹配时返回null
|
||||||
*/
|
*/
|
||||||
public static function fromDescription(string $description): ?self
|
public static function fromDescription(string $description): ?self
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Aether\Traits;
|
namespace Aether\Traits;
|
||||||
|
|
||||||
use Hyperf\Database\Model\SoftDeletes;
|
use Hyperf\Database\Model\SoftDeletes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用软删除Trait,供需要软删除的模型使用
|
* 通用软删除Trait,供需要软删除的模型使用.
|
||||||
*/
|
*/
|
||||||
trait AetherSoftDelete
|
trait AetherSoftDelete
|
||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化软删除相关配置(自动隐藏deleted_at字段)
|
* 初始化软删除相关配置(自动隐藏deleted_at字段).
|
||||||
*/
|
*/
|
||||||
protected function initializeAetherSoftDeletes(): void
|
protected function initializeAetherSoftDeletes(): void
|
||||||
{
|
{
|
||||||
// 自动将deleted_at添加到隐藏字段,避免序列化时暴露
|
// 自动将deleted_at添加到隐藏字段,避免序列化时暴露
|
||||||
if (!in_array('deleted_at', $this->hidden, true)) {
|
if (! in_array('deleted_at', $this->hidden, true)) {
|
||||||
$this->hidden[] = 'deleted_at';
|
$this->hidden[] = 'deleted_at';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Aether\Traits;
|
namespace Aether\Traits;
|
||||||
|
|
||||||
use Aether\AetherModel;
|
use Aether\AetherModel;
|
||||||
@@ -13,25 +15,16 @@ trait AetherTree
|
|||||||
{
|
{
|
||||||
parent::__construct($attributes);
|
parent::__construct($attributes);
|
||||||
|
|
||||||
if (!$this instanceof AetherModel) {
|
if (! $this instanceof AetherModel) {
|
||||||
throw new LogicException(
|
throw new LogicException(
|
||||||
"使用AetherTree trait的类必须继承AetherModel,当前类: " . get_class($this)
|
'使用AetherTree trait的类必须继承AetherModel,当前类: ' . get_class($this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 抽象方法:获取父ID字段名(由子类实现)
|
* 构建树形结构.
|
||||||
*/
|
* @param mixed $items
|
||||||
abstract protected function getParentIdField(): string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 抽象方法:获取排序字段名(由子类实现)
|
|
||||||
*/
|
|
||||||
abstract protected function getSortField(): string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建树形结构
|
|
||||||
*/
|
*/
|
||||||
public static function buildTree($items, int $parentId = 0): array
|
public static function buildTree($items, int $parentId = 0): array
|
||||||
{
|
{
|
||||||
@@ -45,7 +38,7 @@ trait AetherTree
|
|||||||
foreach ($items as $item) {
|
foreach ($items as $item) {
|
||||||
if ($item[$parentField] == $parentId) {
|
if ($item[$parentField] == $parentId) {
|
||||||
$children = static::buildTree($items, $item['id']);
|
$children = static::buildTree($items, $item['id']);
|
||||||
if (!empty($children)) {
|
if (! empty($children)) {
|
||||||
$item['children'] = $children;
|
$item['children'] = $children;
|
||||||
}
|
}
|
||||||
$tree[] = $item;
|
$tree[] = $item;
|
||||||
@@ -57,20 +50,7 @@ trait AetherTree
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 树形节点排序
|
* 获取指定节点的所有子节点ID.
|
||||||
*/
|
|
||||||
protected function sortTreeItems(array &$items, string $sortField): void
|
|
||||||
{
|
|
||||||
usort($items, function ($a, $b) use ($sortField) {
|
|
||||||
$direction = $this->treeSortDirection ?? 'asc';
|
|
||||||
return $direction === 'desc'
|
|
||||||
? $b[$sortField] <=> $a[$sortField]
|
|
||||||
: $a[$sortField] <=> $b[$sortField];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取指定节点的所有子节点ID
|
|
||||||
*/
|
*/
|
||||||
public function getChildIds(int $id): array
|
public function getChildIds(int $id): array
|
||||||
{
|
{
|
||||||
@@ -84,7 +64,57 @@ trait AetherTree
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 递归收集子节点ID
|
* 获取节点的完整路径.
|
||||||
|
*/
|
||||||
|
public function getPath(int $id): array
|
||||||
|
{
|
||||||
|
$parentField = $this->getParentIdField();
|
||||||
|
// 安全调用newQuery()
|
||||||
|
$node = $this->newQuery()->find($id);
|
||||||
|
if (! $node) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = [$node->toArray()];
|
||||||
|
$parentId = $node[$parentField];
|
||||||
|
|
||||||
|
while ($parentId > 0) {
|
||||||
|
$parent = $this->newQuery()->find($parentId);
|
||||||
|
if (! $parent) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
array_unshift($path, $parent->toArray());
|
||||||
|
$parentId = $parent[$parentField];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象方法:获取父ID字段名(由子类实现).
|
||||||
|
*/
|
||||||
|
abstract protected function getParentIdField(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象方法:获取排序字段名(由子类实现).
|
||||||
|
*/
|
||||||
|
abstract protected function getSortField(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 树形节点排序.
|
||||||
|
*/
|
||||||
|
protected function sortTreeItems(array &$items, string $sortField): void
|
||||||
|
{
|
||||||
|
usort($items, function ($a, $b) use ($sortField) {
|
||||||
|
$direction = $this->treeSortDirection ?? 'asc';
|
||||||
|
return $direction === 'desc'
|
||||||
|
? $b[$sortField] <=> $a[$sortField]
|
||||||
|
: $a[$sortField] <=> $b[$sortField];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归收集子节点ID.
|
||||||
*/
|
*/
|
||||||
private function collectChildIds(array $items, int $parentId, string $parentField, array &$ids): void
|
private function collectChildIds(array $items, int $parentId, string $parentField, array &$ids): void
|
||||||
{
|
{
|
||||||
@@ -95,31 +125,4 @@ trait AetherTree
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取节点的完整路径
|
|
||||||
*/
|
|
||||||
public function getPath(int $id): array
|
|
||||||
{
|
|
||||||
$parentField = $this->getParentIdField();
|
|
||||||
// 安全调用newQuery()
|
|
||||||
$node = $this->newQuery()->find($id);
|
|
||||||
if (!$node) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$path = [$node->toArray()];
|
|
||||||
$parentId = $node[$parentField];
|
|
||||||
|
|
||||||
while ($parentId > 0) {
|
|
||||||
$parent = $this->newQuery()->find($parentId);
|
|
||||||
if (!$parent) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
array_unshift($path, $parent->toArray());
|
|
||||||
$parentId = $parent[$parentField];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user