diff --git a/app/Exception/CampusNotFound.php b/app/Exception/CampusNotFound.php new file mode 100644 index 0000000..2472bc9 --- /dev/null +++ b/app/Exception/CampusNotFound.php @@ -0,0 +1,11 @@ +campusModel->find($id); if (! $campus || $campus->status != 1) { - throw new BusinessException('校区不存在或已禁用', 10001); + throw new CampusNotFound('校区不存在或已禁用'); } return $campus->toArray(); } @@ -122,7 +124,7 @@ class DataService implements DataServiceInterface { $teacher = $this->teacherModel->find($id); if (! $teacher || $teacher->status != 1) { - throw new BusinessException('教师不存在或已禁用', 10001); + throw new TeacherNotFound(); } return $teacher->toArray(); } diff --git a/composer.json b/composer.json index 6b6129a..be2648e 100755 --- a/composer.json +++ b/composer.json @@ -13,6 +13,7 @@ "license": "Apache-2.0", "require": { "php": ">=8.1", + "aether/hyperf": "dev-master", "hyperf/cache": "~3.1.0", "hyperf/command": "~3.1.0", "hyperf/config": "~3.1.0", @@ -47,6 +48,12 @@ "phpstan/phpstan": "^1.0", "swoole/ide-helper": "^5.0" }, + "repositories": [ + { + "type": "git", + "url": "https://gitee.com/devAether666/aether-hyperf.git" + } + ], "suggest": { "ext-openssl": "Required to use HTTPS.", "ext-json": "Required to use JSON.", @@ -57,7 +64,6 @@ "autoload": { "psr-4": { "App\\": "app/", - "Aether\\": "extend/Aether/PHP/Hyperf/", "MicroService\\": "extend/MicroService/src/" }, "files": [] diff --git a/composer.lock b/composer.lock index 3c78c2e..ce99a5b 100755 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,56 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c30923a4e3d3314c7a9c295b398631d8", + "content-hash": "8e6c542d87ccd4c4654a4adb71436e17", "packages": [ + { + "name": "aether/hyperf", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://gitee.com/devAether666/aether-hyperf.git", + "reference": "11a16d4cb0693ebdb5beb291777395132c001ef4" + }, + "require": { + "hyperf/db-connection": "~3.1.0", + "hyperf/di": "~3.1.0", + "hyperf/logger": "~3.1.0", + "hyperf/model-cache": "^3.1", + "hyperf/validation": "^3.1", + "php": ">=8.1" + }, + "require-dev": { + "hyperf/testing": "~3.1.0" + }, + "default-branch": true, + "type": "library", + "extra": { + "hyperf": { + "config": [] + } + }, + "autoload": { + "psr-4": { + "Aether\\": "src/PHP/Hyperf/" + } + }, + "autoload-dev": { + "psr-4": { + "Aether\\Tests\\": "tests/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aether", + "email": "aether.dev.666@gmail.com" + } + ], + "description": "Aether Hyperf Common Components", + "time": "2025-09-30T07:14:33+00:00" + }, { "name": "carbonphp/carbon-doctrine-types", "version": "3.2.0", @@ -10648,12 +10696,14 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": {}, + "stability-flags": { + "aether/hyperf": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { "php": ">=8.1" }, - "platform-dev": {}, - "plugin-api-version": "2.6.0" + "platform-dev": [], + "plugin-api-version": "2.0.0" } diff --git a/config/autoload/exceptions.php b/config/autoload/exceptions.php index 515c607..f93254e 100644 --- a/config/autoload/exceptions.php +++ b/config/autoload/exceptions.php @@ -1,7 +1,8 @@ [ diff --git a/extend/Aether/PHP/Hyperf/AetherController.php b/extend/Aether/PHP/Hyperf/AetherController.php deleted file mode 100644 index 7809aac..0000000 --- a/extend/Aether/PHP/Hyperf/AetherController.php +++ /dev/null @@ -1,79 +0,0 @@ -request->all(); - $result = $this->getService()->list($params); - return AetherResponse::success($result); - } - - /** - * 获取单个资源 (RESTFul: GET resources/{id}). - */ - public function detail(int $id): array - { - $result = $this->getService()->detail($id); - return AetherResponse::success($result); - } - - /** - * 创建资源 (RESTFul: POST resources). - * @throws Throwable - */ - public function create(): array - { - $data = $this->request->all(); - $id = $this->getService()->create($data); - return AetherResponse::success(['id' => $id], '创建成功'); - } - - /** - * 更新资源 (RESTFul: PUT resources/{id}). - * @throws Throwable - */ - public function update(int $id): array - { - $data = $this->request->all(); - $this->getService()->update($id, $data); - return AetherResponse::success(null, '更新成功'); - } - - /** - * 删除资源 (RESTFul: DELETE resources/{id}). - * @throws Throwable - */ - public function delete(int $id): array - { - $this->getService()->delete($id); - return AetherResponse::success(null, '删除成功'); - } - - /** - * 获取对应的服务类. - */ - abstract protected function getService(): AetherCrudService; -} diff --git a/extend/Aether/PHP/Hyperf/AetherCrudInterface.php b/extend/Aether/PHP/Hyperf/AetherCrudInterface.php deleted file mode 100644 index dbac646..0000000 --- a/extend/Aether/PHP/Hyperf/AetherCrudInterface.php +++ /dev/null @@ -1,33 +0,0 @@ -getModel(); - $query = $model->newQuery(); - - // 通过模型配置自动应用所有搜索条件 - $this->applySearch($query, $params); - - // 动态应用排序 - $sortConfig = $model->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)); // 限制最大页大小为100 - - $result = $query->paginate($size, ['*'], 'page', $page); - return [ - 'total' => $result->total(), - 'list' => $result->items(), - ]; - } - - // 无分页参数时返回完整数据集合 - $items = $query->get()->toArray(); - - // 若模型支持树形结构则构建树形,否则返回普通数组 - if ($model instanceof TreeableInterface) { - return $model::buildTree($items, (int) ($params['parent_id'] ?? 0)); - } - - return $items; - } - - /** - * 通用详情查询. - */ - public function detail(int $id): object - { - return $this->getModel()->findOrFailById($id); - } - - /** - * 通用创建逻辑. - * @throws BusinessException|Throwable - */ - public function create(array $data): int - { - // 数据验证(使用子类指定的验证器) - $this->getValidator()->scene('create', $data)->check(); - - return $this->transaction(function () use ($data) { - $model = $this->getModel()->createOne($data); - $this->logger()->info('创建资源', [ - 'id' => $model->id, - 'code' => $data['code'] ?? $model->code, - ]); - return $model->id; - }); - } - - /** - * 通用更新逻辑. - * @throws BusinessException|Throwable - */ - public function update(int $id, array $data): bool - { - $model = $this->getModel(); - $resource = $model->findById($id); - $this->checkResourceExists($resource); - - // 数据验证 - $this->getValidator()->scene('update', $data)->check(); - - // 钩子:处理更新时的特殊逻辑(如禁止自身为父级) - $this->handleUpdateSpecialLogic($id, $data); - - return $this->transaction(function () use ($id, $data) { - $result = $this->getModel()->updateById($id, $data); - $this->logger()->info('更新资源', [ - 'id' => $id, - 'data' => $data, - ]); - return $result; - }); - } - - /** - * 通用删除逻辑. - * @throws BusinessException|Throwable - */ - public function delete(int $id): bool - { - $model = $this->getModel(); - $resource = $model->findById($id); - - $this->checkResourceExists($resource); - - // 钩子:删除前检查(如子级存在性) - $this->checkChildrenBeforeDelete($id); - - return $this->transaction(function () use ($id) { - $result = $this->getModel()->deleteById($id); - $this->logger()->info('删除资源', ['id' => $id]); - return $result; - }); - } - - /** - * 根据模型的$search配置,自动应用搜索条件到查询构建器. - */ - public function applySearch(Builder $query, array $params): void - { - foreach ($this->search as $field => $rule) { - // 跳过未传递的参数 - if (! isset($params[$field])) { - continue; - } - - $value = $params[$field]; - $this->applySearchRule($query, $field, $value, $rule); - } - } - - /** - * 软删除恢复. - * @throws BusinessException|Throwable - */ - public function restore(int $id): bool - { - $model = $this->getModel(); - // 必须使用withTrashed()才能查询到已删除记录 - $resource = $model->newQuery()->withTrashed()->find($id); - $this->checkResourceExists($resource, '恢复的资源不存在'); - - return $this->transaction(function () use ($id) { - $result = $this->getModel()->newQuery()->withTrashed() - ->where('id', $id)->restore(); - - $this->logger()->info('恢复软删除资源', ['id' => $id]); - return $result; - }); - } - - /** - * 批量软删除. - * @throws BusinessException|Throwable - */ - public function batchDelete(array $ids): bool - { - if (empty($ids)) { - throw new BusinessException('请选择要删除的记录', 400); - } - - $model = $this->getModel(); - $exists = $model->whereIn('id', $ids)->exists(); - if (! $exists) { - throw new BusinessException('部分记录不存在', 404); - } - - return $this->transaction(function () use ($ids) { - $result = $this->getModel()->whereIn('id', $ids)->delete(); - $this->logger()->info('批量软删除资源', ['ids' => $ids]); - return $result > 0; - }); - } - - /** - * 批量恢复软删除. - * @throws BusinessException|Throwable - */ - public function batchRestore(array $ids): bool - { - if (empty($ids)) { - throw new BusinessException('请选择要恢复的记录', 400); - } - - $model = $this->getModel(); - $exists = $model->newQuery()->withTrashed() - ->whereIn('id', $ids) - ->whereNotNull('deleted_at') - ->exists(); - - if (! $exists) { - throw new BusinessException('部分记录不存在或未被删除', 404); - } - - return $this->transaction(function () use ($ids) { - $result = $this->getModel()->newQuery()->withTrashed() - ->whereIn('id', $ids)->restore(); - - $this->logger()->info('批量恢复资源', ['ids' => $ids]); - return $result > 0; - }); - } - - /** - * 永久删除(物理删除). - * @throws BusinessException|Throwable - */ - public function forceDelete(int $id): bool - { - $model = $this->getModel(); - $resource = $model->newQuery()->withTrashed()->find($id); - $this->checkResourceExists($resource, '要删除的资源不存在'); - - return $this->transaction(function () use ($id) { - $result = $this->getModel()->newQuery()->withTrashed() - ->where('id', $id)->forceDelete(); - - $this->logger()->info('永久删除资源', ['id' => $id]); - return $result; - }); - } - - protected function getSearch(): array - { - return $this->search; - } - - protected function getIgnoreSearchFields(): array - { - return $this->ignoreSearchFields; - } - - /** - * 获取当前服务对应的模型实例(由子类实现). - */ - abstract protected function getModel(): AetherModel; - - /** - * 获取当前服务对应的验证器实例(由子类实现). - */ - abstract protected function getValidator(): AetherValidator; - - /** - * 钩子方法:更新时的特殊逻辑(子类可重写). - */ - protected function handleUpdateSpecialLogic(int $id, array &$data): void - { - // 通用逻辑:禁止将自身设为父级(适用于有parent_id的场景) - if (isset($data['parent_id']) && $data['parent_id'] == $id) { - throw new BusinessException('不能将自身设为父级', 400); - } - } - - /** - * 钩子方法:删除前检查子级(子类可重写). - */ - protected function checkChildrenBeforeDelete(int $id): void - { - // 默认不检查,需要的子类重写 - } - - /** - * 应用单个搜索规则. - * @param mixed $value - * @param mixed $rule - */ - protected function applySearchRule(Builder $query, string $field, $value, $rule): void - { - // 处理规则格式(支持字符串简写或数组配置) - $config = is_array($rule) ? $rule : ['type' => $rule]; - $type = $config['type']; - - switch ($type) { - case '=': // 精确匹配 - $query->where($field, $value); - break; - case 'like': // 模糊匹配 - $query->where($field, 'like', "%{$value}%"); - break; - case 'between': // 范围查询(支持数组或两个参数) - $values = is_array($value) ? $value : [$value, $params[$field . '_end'] ?? $value]; - $query->whereBetween($field, $values); - break; - case 'callback': // 自定义回调 - if (isset($config['handler']) && is_callable($config['handler'])) { - call_user_func($config['handler'], $query, $value); - } - break; - // 可扩展其他类型:in、>、< 等 - } - } -} diff --git a/extend/Aether/PHP/Hyperf/AetherModel.php b/extend/Aether/PHP/Hyperf/AetherModel.php deleted file mode 100644 index 62b2635..0000000 --- a/extend/Aether/PHP/Hyperf/AetherModel.php +++ /dev/null @@ -1,476 +0,0 @@ - '字段名', 'direction' => 'asc/desc']. - */ - protected array|bool|string $sortable = false; // 'sort'; // 默认按sort字段升序 - - public function __construct(array $attributes = []) - { - parent::__construct($attributes); - $this->bootBaseModel(); - } - - /** - * 获取排序配置. - * @return null|array [field, direction] 或 null(禁用排序) - */ - public function getSortConfig(): ?array - { - if ($this->sortable === false) { - return null; - } - - // 处理字符串配置(如 'sort' 或 'create_time') - if (is_string($this->sortable)) { - return [ - 'field' => $this->sortable, - 'direction' => 'asc', - ]; - } - - return null; - } - - public function list(array $params = []): array - { - $query = static::query(); - - // 通过模型配置自动应用所有搜索条件 - $this->applySearch($query, $params); - - // 动态应用排序 - if ($this->sortable) { - $sortConfig = $this->getSortConfig(); - $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)); // 限制最大页大小为100 - - $result = $query->paginate($size, ['*'], 'page', $page); - return [ - 'list' => $result->items(), - 'total' => $result->total(), - ]; - } - - // 无分页参数时返回完整数据集合 - $items = $query->get()->toArray(); - - // 若模型支持树形结构则构建树形,否则返回普通数组 - if ($this instanceof TreeableInterface) { - return $this::buildTree($items, (int) ($params['parent_id'] ?? 0)); - } - - return $items; - } - - /** - * 快捷创建. - */ - public static function createOne(array $data): AetherModel|Builder|HyperfModel - { - return static::query()->create($data); - } - - /** - * 快捷更新. - */ - public static function updateById(int $id, array $data): int - { - return static::query()->where('id', $id)->update($data); - } - - /** - * 快捷删除指定ID的记录. - * - * @param int $id 要删除的记录ID - * @return bool 成功删除返回true - * @throws ModelNotFoundException 当记录不存在时抛出 - * @throws Exception 当删除操作发生其他错误时抛出 - */ - public static function deleteById(int $id): bool - { - if (! static::query()->where('id', $id)->exists()) { - throw new ModelNotFoundException(sprintf( - '找不到 ID 为 %d 的记录', - $id, - )); - } - return static::query()->where('id', $id)->delete() > 0; - } - - /** - * 快捷查找. - * @return Builder|Builder[]|Collection|HyperfModel - * @throws Exception 当删除操作发生其他错误时抛出 - * @throws ModelNotFoundException - */ - public static function findById(int $id): array|Builder|Collection|HyperfModel - { - $record = static::query()->find($id); - if (is_null($record)) { - throw new ModelNotFoundException(sprintf('找不到 ID 为 %d 的记录', $id)); - } - return $record; - } - - /** - * 快捷查找或失败. - * @param int $id 要查找的记录ID - * @return AetherModel 根据ID查找记录,不存在则抛出异常 - * @throws Exception 当删除操作发生其他错误时抛出 - * @throws ModelNotFoundException 当记录不存在时抛出 - */ - public static function findOrFailById(int $id): static - { - $record = static::query()->find($id); - if (is_null($record)) { - throw new ModelNotFoundException(sprintf( - '找不到 ID 为 %d 的记录', - $id - )); - } - return $record; // static::query()->findOrFail($id); - } - - /** - * 获取器处理. - */ - public function getAttribute(string $key): mixed - { - $value = parent::getAttribute($key); - - // 检查是否有自定义获取器 - $getterMethod = 'get' . ucfirst($key) . 'Attr'; - if (method_exists($this, $getterMethod)) { - return $this->{$getterMethod}($value); - } - - // 应用获取器规则 - if (isset($this->append[$key])) { - return $this->applyGetterRule($value, $this->append[$key]); - } - - return $value; - } - - /** - * 批量更新. - */ - public static function batchUpdate(array $conditions, array $data): int - { - return static::query()->where($conditions)->update($data); - } - - /** - * 事务处理. - * @throws Throwable - */ - public static function transaction(Closure $closure): mixed - { - return Db::transaction($closure); - } - - /** - * 初始化模型. - */ - protected function bootBaseModel(): void - { - // 自动注册搜索器和获取器 - $this->registerSearchers(); - $this->registerGetters(); - } - - /** - * 注册搜索器. - */ - protected function registerSearchers(): void - { - // 为模型查询添加全局作用域,自动应用搜索规则 - static::addGlobalScope('auto_search', function (Builder $query) { - $searchConditions = $this->getSearchConditions(); - if (empty($searchConditions)) { - return; - } - // 应用搜索条件到查询 - $this->applySearch($query, $searchConditions); - }); - } - - /** - * 获取搜索条件. - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ - protected function getSearchConditions(): array - { - $request = ApplicationContext::getContainer()->get(RequestInterface::class); - $allParams = $request->all(); - - if (is_array($allParams) && count($allParams) === 1 && is_array($allParams[0])) { - $allParams = $allParams[0]; - } - - $allowedFields = array_keys($this->search); - return array_intersect_key($allParams, array_flip($allowedFields)); - } - - protected function registerGetters(): void - { - foreach ($this->append as $field => $rule) { - // 为字段注册获取器回调 - $this->registerGetter($field, $rule); - } - } - - /** - * 为单个字段注册获取器. - * @param string $field 字段名 - * @param array|Closure|string $rule 规则(类型/带参数的类型/闭包) - */ - protected function registerGetter(string $field, array|Closure|string $rule): void - { - // 生成获取器方法名(遵循 Hyperf 模型获取器规范:get{Field}Attr) - $getterMethod = 'get' . ucfirst($field) . 'Attr'; - - // 若已存在自定义获取器方法,则不覆盖 - if (method_exists($this, $getterMethod)) { - return; - } - - // 动态定义获取器方法 - $this->{$getterMethod} = function ($value) use ($rule) { - // 闭包规则直接执行 - if ($rule instanceof Closure) { - return $rule($value, $this); // 传入当前模型实例方便关联字段处理 - } - - // 解析规则类型和参数 - if (is_array($rule)) { - $type = $rule[0]; - $params = $rule[1] ?? []; - } else { - $type = $rule; - $params = []; - } - - // 应用规则(复用/扩展 applyGetterRule 方法) - return $this->applyGetterRule($value, $type, $params); - }; - } - - /** - * 应用获取器规则(支持参数). - * @param mixed $value 字段原始值 - * @param string $type 规则类型 - * @param array $params 规则参数 - */ - protected function applyGetterRule(mixed $value, string $type, array $params = []): mixed - { - return match ($type) { - // 基础规则 - 'date' => $value instanceof DateTime ? $value->format('Y-m-d') : $value, - 'datetime' => $value instanceof DateTime ? $value->format('Y-m-d H:i:s') : $value, - 'timestamp' => $value instanceof DateTime ? $value->getTimestamp() : $value, - 'boolean' => (bool) $value, - 'json' => is_string($value) ? json_decode($value, true) : $value, - - // 参数化规则 - 'number' => $this->formatNumber($value, $params), // 数字格式化 - 'truncate' => $this->truncateString($value, $params), // 字符串截断 - 'enum' => $this->mapEnum($value, $params), // 枚举映射 - default => $value, - }; - } - - // 数字格式化 - protected function formatNumber($value, array $params): string - { - $precision = $params['precision'] ?? 2; // 默认保留2位小数 - return number_format((float) $value, $precision); - } - - // 字符串截断 - protected function truncateString($value, array $params): string - { - $length = $params['length'] ?? 20; // 默认截断到20字符 - $suffix = $params['suffix'] ?? '...'; // 省略符 - if (! is_string($value)) { - $value = (string) $value; - } - return mb_strlen($value) > $length - ? mb_substr($value, 0, $length) . $suffix - : $value; - } - - // 枚举映射 - protected function mapEnum($value, array $params): mixed - { - $map = $params['map'] ?? []; // 枚举映射表,如 [1 => '男', 2 => '女'] - return $map[$value] ?? $value; - } - - /** - * 应用搜索条件. - */ - protected function applySearch(Builder $query, array $conditions): void - { - foreach ($conditions as $field => $value) { - // 基础过滤:非字符串字段名或未设置值的参数直接跳过 - if (! is_string($field) || ! isset($value)) { - continue; - } - - // 核心限制:只处理$search数组中定义的字段 - if (! isset($this->search[$field])) { - continue; - } - - // 处理嵌套关系查询(如:user.name,需在$search中配置完整键名) - if (str_contains($field, '.')) { - [$relation, $relationField] = explode('.', $field, 2); - $query->whereHas($relation, function ($q) use ($relationField, $value) { - // 嵌套查询默认使用精确匹配,如需特殊规则可在$search中自定义处理 - $q->where($relationField, $value); - }); - continue; - } - - // 优先使用自定义搜索器方法(仅对$search中存在的字段生效) - $searchMethod = 'search' . ucfirst($field); - if (method_exists($this, $searchMethod)) { - $this->{$searchMethod}($query, $value); - continue; - } - - // 应用$search中定义的搜索规则(如=、like等) - $this->applySearchRule($query, $field, $value, $this->search[$field]); - } - } - - /** - * 应用搜索规则. - */ - protected function applySearchRule(Builder $query, string $field, mixed $value, array|string $rule): void - { - if (is_array($rule)) { - $type = $rule[0]; - $params = $rule[1] ?? []; - } else { - $type = $rule; - $params = []; - } - - switch ($type) { - case 'like': - $query->where($field, 'like', "%{$value}%"); - break; - case 'like_left': - $query->where($field, 'like', "%{$value}"); - break; - case 'like_right': - $query->where($field, 'like', "{$value}%"); - break; - case 'in': - $query->whereIn($field, (array) $value); - break; - case 'not_in': - $query->whereNotIn($field, (array) $value); - break; - case 'gt': - $query->where($field, '>', $value); - break; - case 'lt': - $query->where($field, '<', $value); - break; - case 'gte': - $query->where($field, '>=', $value); - break; - case 'lte': - $query->where($field, '<=', $value); - break; - case 'between': - $query->whereBetween($field, (array) $value); - break; - case 'null': - $query->whereNull($field); - break; - case 'not_null': - $query->whereNotNull($field); - break; - default: - $query->where($field, $type, $value); - } - } -} diff --git a/extend/Aether/PHP/Hyperf/AetherResponse.php b/extend/Aether/PHP/Hyperf/AetherResponse.php deleted file mode 100644 index 6759033..0000000 --- a/extend/Aether/PHP/Hyperf/AetherResponse.php +++ /dev/null @@ -1,69 +0,0 @@ - $data, - Config::RESPONSE_FIELD_KEY_CODE => Config::RESPONSE_SUCCESS_CODE, - Config::RESPONSE_FIELD_KEY_MESSAGE => $message ?: Config::RESPONSE_SUCCESS_MESSAGE, - ]; - } - - /** - * 错误响应. - * @param int $code 错误码 - * @param string $message 错误消息 - * @param null|mixed $data 附加数据 - */ - public static function error(string $message = '', int $code = Config::RESPONSE_FAIL_CODE, mixed $data = null): array - { - return [ - Config::RESPONSE_FIELD_KEY_CODE => $code, - Config::RESPONSE_FIELD_KEY_DATA => $data, - Config::RESPONSE_FIELD_KEY_MESSAGE => $message ?: self::getDefaultMessage($code), - ]; - } - - public static function page($list, int $total, int $page, int $size): array - { - return [ - Config::RESPONSE_FIELD_KEY_CODE => Config::RESPONSE_SUCCESS_CODE, - Config::RESPONSE_FIELD_KEY_MESSAGE => 'success', - Config::RESPONSE_FIELD_KEY_LIST => [ - 'list' => $list, - 'page' => $page, - 'size' => $size, - 'total' => $total, - 'pages' => (int) ceil($total / $size), - ], - ]; - } - - /** - * 获取默认错误消息. - */ - private static function getDefaultMessage(int $code): string - { - $messages = [ - 400 => '请求参数错误', - 401 => '未授权', - 403 => '禁止访问', - 404 => '资源不存在', - 500 => '服务器内部错误', - 10001 => '校区不存在', - ]; - - return $messages[$code] ?? '未知错误'; - } -} diff --git a/extend/Aether/PHP/Hyperf/AetherService.php b/extend/Aether/PHP/Hyperf/AetherService.php deleted file mode 100644 index 4342634..0000000 --- a/extend/Aether/PHP/Hyperf/AetherService.php +++ /dev/null @@ -1,47 +0,0 @@ -loggerFactory->get($className); - } - - /** - * 事务处理. - * @throws Throwable - */ - protected function transaction(callable $callback) - { - return Db::transaction($callback); - } - - /** - * 检查资源是否存在(不存在则抛出异常). - * @throws BusinessException - */ - protected function checkResourceExists(?object $resource, string $message = '资源不存在'): void - { - if (empty($resource)) { - throw new BusinessException($message, BusinessException::RESOURCE_NOT_FOUND); - } - } -} diff --git a/extend/Aether/PHP/Hyperf/AetherValidator.php b/extend/Aether/PHP/Hyperf/AetherValidator.php deleted file mode 100644 index c863d8e..0000000 --- a/extend/Aether/PHP/Hyperf/AetherValidator.php +++ /dev/null @@ -1,133 +0,0 @@ - 闭包/类方法]. - */ - protected array $customRules = []; - - /** - * 静态快捷验证方法(简化调用). - */ - public static function validate(string $scene, array $data = []): array - { - $instance = ApplicationContext::getContainer()->get(static::class); - return $instance->scene($scene, $data)->check(); - } - - /** - * 设置验证场景和数据(支持链式调用). - */ - public function scene(string $scene, array $data = []): self - { - $this->currentScene = $scene; - $this->data = $data; - return $this; - } - - /** - * 执行验证(失败抛出异常). - */ - public function check(): array - { - if (empty($this->currentScene)) { - throw new RuntimeException('请先设置验证场景'); - } - - $scenes = $this->scenes(); - if (! isset($scenes[$this->currentScene])) { - throw new RuntimeException("验证场景不存在:{$this->currentScene}"); - } - - $sceneConfig = $scenes[$this->currentScene]; - return $this->validateData( - $this->data, - $sceneConfig['rules'], - $sceneConfig['messages'] ?? [], - $sceneConfig['attributes'] ?? [] - ); - } - - /** - * 格式化验证错误信息(统一格式,供异常处理器复用). - */ - public function formatValidationErrors(Validator $validator): array - { - $errors = []; - $failedRules = $validator->failed(); - $errorMessages = $validator->errors()->getMessages(); - $attributes = $validator->attributes(); - - foreach ($failedRules as $field => $rules) { - $errors[] = [ - 'field' => $field, - 'field_label' => $attributes[$field] ?? $field, - 'message' => $errorMessages[$field][0] ?? '', - 'rules' => array_keys($rules), - 'value' => $validator->getValue($field), - ]; - } - - return $errors; - } - - /** - * 实际执行验证的逻辑(重命名方法名更清晰). - */ - protected function validateData(array $data, array $rules, array $messages = [], array $attributes = []): array - { - $validator = $this->validationFactory->make($data, $rules, $messages, $attributes); - $this->registerRules($validator); - - if ($validator->fails()) { - throw new ValidationFailedException( - $validator, - $this->currentScene ?? '', - $validator->errors()->first() - ); - } - - return $validator->validated(); - } - - /** - * 自动注册自定义规则(优先使用$customRules属性). - */ - protected function registerRules(Validator $validator): void - { - foreach ($this->customRules as $ruleName => $rule) { - $validator->extend($ruleName, $rule); - } - } - - /** - * 定义场景验证规则(子类实现). - */ - abstract protected function scenes(): array; -} diff --git a/extend/Aether/PHP/Hyperf/ApiExceptionHandler.php b/extend/Aether/PHP/Hyperf/ApiExceptionHandler.php deleted file mode 100644 index 9a7a70a..0000000 --- a/extend/Aether/PHP/Hyperf/ApiExceptionHandler.php +++ /dev/null @@ -1,43 +0,0 @@ - $throwable->getCode() ?: 500, - 'message' => $throwable->getMessage() ?: '服务器内部错误', - 'request_id' => Context::get('request_id', ''), - 'timestamp' => time(), - ]; - - // 开发环境显示堆栈信息 - if (env('APP_ENV') === 'dev') { - $data['trace'] = $throwable->getTraceAsString(); - } - - $body = json_encode($data, JSON_UNESCAPED_UNICODE); - return $response->withHeader('Content-Type', 'application/json') - ->withStatus($data['code'] >= 400 && $data['code'] < 500 ? $data['code'] : 500) - ->withBody(new SwooleStream($body)); - } - - public function isValid(Throwable $throwable): bool - { - return true; - } -} diff --git a/extend/Aether/PHP/Hyperf/Config.php b/extend/Aether/PHP/Hyperf/Config.php deleted file mode 100644 index e3cbe31..0000000 --- a/extend/Aether/PHP/Hyperf/Config.php +++ /dev/null @@ -1,24 +0,0 @@ -formatErrorResponse($throwable, $requestId); - - // 记录错误日志(包含完整堆栈) - $this->logger->error(sprintf( - 'Exception [%s] | RequestId: %s | Message: %s in %s:%d', - get_class($throwable), - $requestId, - $throwable->getMessage(), - $throwable->getFile(), - $throwable->getLine() - )); - if (env('APP_ENV') === 'dev') { - $this->logger->error($throwable->getTraceAsString()); - } - - // 确保状态码合法(100-599) - $statusCode = $result['code'] ?? 500; - if ($statusCode < 100 || $statusCode >= 600) { - $statusCode = 500; - } - - // 构建响应 - return $response - ->withHeader('Content-Type', 'application/json') - ->withStatus($statusCode) - ->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 - { - // 模型未找到异常 - if ($throwable instanceof ModelNotFoundException) { - return [ - 'code' => 404, // 资源不存在标准状态码 - 'message' => $throwable->getMessage() ?: '请求的资源不存在', - 'data' => env('APP_ENV') === 'dev' ? [ - 'file' => $throwable->getFile(), - 'line' => $throwable->getLine(), - ] : null, - 'request_id' => $requestId, - 'timestamp' => time(), - ]; - } - - // 自定义验证异常 - if ($throwable instanceof ValidationFailedException) { - return $this->formatValidationError($throwable, $requestId); - } - - // 原生验证异常 - if ($throwable instanceof ValidationException) { - return $this->formatNativeValidationError($throwable, $requestId); - } - - // 其他异常(如RuntimeException等) - return [ - 'code' => 600, - 'message' => env('APP_ENV') === 'dev' ? $throwable->getMessage() : '服务暂时不可用', - 'data' => env('APP_ENV') === 'dev' ? [ - 'file' => $throwable->getFile(), - 'line' => $throwable->getLine(), - 'trace' => explode("\n", $throwable->getTraceAsString()), - ] : null, - 'request_id' => $requestId, - 'timestamp' => time(), - ]; - } - - private function formatValidationError(ValidationFailedException $e, string $requestId): array - { - $validatorInstance = new class extends AetherValidator { - protected function scenes(): array - { - return []; - } - }; - - return [ - 'code' => 422, - 'message' => $e->getMessage(), - 'data' => [ - 'errors' => $validatorInstance->formatValidationErrors($e->validator), - 'scene' => $e->getScene(), - 'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null, - ], - 'request_id' => $requestId, - 'timestamp' => time(), - ]; - } - - private function formatNativeValidationError(ValidationException $e, string $requestId): array - { - $validatorInstance = new class extends AetherValidator { - protected function scenes(): array - { - return []; - } - }; - - return [ - 'code' => 422, - 'message' => '参数验证失败', - 'data' => [ - 'errors' => $validatorInstance->formatValidationErrors($e->validator), - 'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null, - ], - 'request_id' => $requestId, - 'timestamp' => time(), - ]; - } -} diff --git a/extend/Aether/PHP/Hyperf/Exception/AppExceptionHandler.php b/extend/Aether/PHP/Hyperf/Exception/AppExceptionHandler.php deleted file mode 100644 index 7dfd136..0000000 --- a/extend/Aether/PHP/Hyperf/Exception/AppExceptionHandler.php +++ /dev/null @@ -1,126 +0,0 @@ -formatErrorResponse($throwable, $requestId); - - $this->logger->error(sprintf( - 'Exception: %s[%s] in %s:%d', - get_class($throwable), - $throwable->getMessage(), - $throwable->getFile(), - $throwable->getLine() - )); - - return $response - ->withHeader('Content-Type', 'application/json') - ->withStatus($result['code'] ?? 500) - ->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 - { - // 处理自定义验证异常 - if ($throwable instanceof ValidationFailedException) { - return $this->formatValidationError($throwable, $requestId); - } - - // 处理原生验证异常 - if ($throwable instanceof ValidationException) { - return $this->formatNativeValidationError($throwable, $requestId); - } - - // 处理其他异常 - return [ - 'code' => 500, - 'message' => env('APP_ENV') === 'dev' ? $throwable->getMessage() : '服务暂时不可用', - 'data' => env('APP_ENV') === 'dev' ? [ - 'file' => $throwable->getFile(), - 'line' => $throwable->getLine(), - 'trace' => explode("\n", $throwable->getTraceAsString()), - ] : null, - 'request_id' => $requestId, - 'timestamp' => time(), - ]; - } - - /** - * 格式化自定义验证异常. - */ - private function formatValidationError(ValidationFailedException $e, string $requestId): array - { - // 复用AetherValidator的错误格式化方法 - $validatorInstance = new class extends AetherValidator { - protected function scenes(): array - { - return []; - } - }; - - return [ - 'code' => 422, - 'message' => $e->getMessage(), - 'data' => [ - 'errors' => $validatorInstance->formatValidationErrors($e->validator), - 'scene' => $e->getScene(), // 直接从异常获取场景 - 'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null, - ], - 'request_id' => $requestId, - 'timestamp' => time(), - ]; - } - - /** - * 格式化原生验证异常(保持格式一致). - */ - private function formatNativeValidationError(ValidationException $e, string $requestId): array - { - $validatorInstance = new class extends AetherValidator { - protected function scenes(): array - { - return []; - } - }; - - return [ - 'code' => 422, - 'message' => '参数验证失败', - 'data' => [ - 'errors' => $validatorInstance->formatValidationErrors($e->validator), - 'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null, - ], - 'request_id' => $requestId, - 'timestamp' => time(), - ]; - } -} diff --git a/extend/Aether/PHP/Hyperf/Exception/BusinessException.php b/extend/Aether/PHP/Hyperf/Exception/BusinessException.php deleted file mode 100644 index 195e645..0000000 --- a/extend/Aether/PHP/Hyperf/Exception/BusinessException.php +++ /dev/null @@ -1,45 +0,0 @@ -errorData = $errorData; - } - - /** - * 获取额外错误数据. - */ - public function getErrorData(): ?array - { - return $this->errorData; - } -} diff --git a/extend/Aether/PHP/Hyperf/Exception/ValidationFailedException.php b/extend/Aether/PHP/Hyperf/Exception/ValidationFailedException.php deleted file mode 100644 index c752fba..0000000 --- a/extend/Aether/PHP/Hyperf/Exception/ValidationFailedException.php +++ /dev/null @@ -1,35 +0,0 @@ -message = $message; - $this->scene = $scene; - } - - public function getScene(): string - { - return $this->scene; - } -} diff --git a/extend/Aether/PHP/Hyperf/GlobalExceptionHandler.php b/extend/Aether/PHP/Hyperf/GlobalExceptionHandler.php deleted file mode 100644 index dcd5cb0..0000000 --- a/extend/Aether/PHP/Hyperf/GlobalExceptionHandler.php +++ /dev/null @@ -1,79 +0,0 @@ -logger = $logger; - } - - public function handle(Throwable $throwable, ResponseInterface $response): MessageInterface|ResponseInterface - { - if ($throwable instanceof ValidationFailedException) { - return $response->withBody(new SwooleStream($throwable->getMessage())); - } - // 处理业务异常 - if ($throwable instanceof BusinessException) { - $data = [ - Config::RESPONSE_FIELD_KEY_CODE => $throwable->getCode(), - Config::RESPONSE_FIELD_KEY_MESSAGE => $throwable->getMessage(), - Config::RESPONSE_FIELD_KEY_DATA => null, - ]; - } - // 数据库无记录异常 - if ($throwable instanceof ModelNotFoundException) { - $data = [ - Config::RESPONSE_FIELD_KEY_CODE => 404, - Config::RESPONSE_FIELD_KEY_MESSAGE => $throwable->getMessage() ?: '没有对应记录', - Config::RESPONSE_FIELD_KEY_DATA => null, - ]; - } else { - // 记录未知错误日志 - $this->logger->error(sprintf( - 'Unknown error: %s %s in %s:%d', - $throwable->getMessage(), - $throwable->getCode(), - $throwable->getFile(), - $throwable->getLine() - )); - - $data = [ - Config::RESPONSE_FIELD_KEY_CODE => 500, - Config::RESPONSE_FIELD_KEY_MESSAGE => 'Server internal error', - Config::RESPONSE_FIELD_KEY_DATA => env('APP_ENV') === 'dev' ? [ - 'message' => $throwable->getMessage(), - 'file' => $throwable->getFile(), - 'line' => $throwable->getLine(), - 'trace' => $throwable->getTraceAsString(), - ] : null, - ]; - } - - $body = json_encode($data, JSON_UNESCAPED_UNICODE); - return $response->withHeader('Content-Type', 'application/json') - ->withBody(new SwooleStream($body)); - } - - public function isValid(Throwable $throwable): bool - { - return true; - } -} diff --git a/extend/Aether/PHP/Hyperf/Middleware/Cors.php b/extend/Aether/PHP/Hyperf/Middleware/Cors.php deleted file mode 100644 index a5d64c9..0000000 --- a/extend/Aether/PHP/Hyperf/Middleware/Cors.php +++ /dev/null @@ -1,29 +0,0 @@ -handle($request); - - return $response - ->withHeader('Access-Control-Allow-Origin', '*') - ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') - ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With') - ->withHeader('Access-Control-Max-Age', '86400'); - } -} \ No newline at end of file diff --git a/extend/Aether/PHP/Hyperf/Middleware/GatewayExceptionHandler.php b/extend/Aether/PHP/Hyperf/Middleware/GatewayExceptionHandler.php deleted file mode 100644 index 7205012..0000000 --- a/extend/Aether/PHP/Hyperf/Middleware/GatewayExceptionHandler.php +++ /dev/null @@ -1,96 +0,0 @@ -handle($request); - } catch (Throwable $e) { - // 尝试解析异常信息,判断是否为RPC服务返回的业务异常 - $parsed = $this->parseRpcException($e); - - if ($parsed) { - // 转换为业务异常 - throw new BusinessException( - $parsed['code'], - $parsed['message'], - $parsed['data'] ?? [] - ); - } - - // 记录非业务异常日志 - $logger = ApplicationContext::getContainer()->get(StdoutLoggerInterface::class); - $logger->error(sprintf('服务调用异常: %s', $e->getMessage()), [ - 'trace' => $e->getTraceAsString(), - 'file' => $e->getFile(), - 'line' => $e->getLine(), - ]); - - // 非业务异常,使用系统错误码 - if ($e instanceof BusinessException) { - throw $e; - } - - // 统一转换为系统错误 - throw new BusinessException( - env('APP_ENV') === 'dev' ? $e->getMessage() : ErrorCode::getMessage(ErrorCode::RPC_CALL_ERROR), - ErrorCode::RPC_CALL_ERROR, - ); - } - } - - /** - * 解析RPC异常信息. - */ - private function parseRpcException(Throwable $e): ?array - { - try { - // 从异常消息中解析JSON数据 - $message = $e->getMessage(); - $data = json_decode($message, true); - - // 检查是否为有效的JSON-RPC错误响应 - if (json_last_error() === JSON_ERROR_NONE && isset($data['jsonrpc']) && $data['jsonrpc'] === '2.0') { - $error = $data['error'] ?? []; - - // 检查是否包含业务异常标识 - return [ - 'code' => $error['code'] ?? ErrorCode::RPC_CALL_ERROR, - 'message' => $error['message'] ?? '服务调用异常', - 'data' => $error['data'] ?? [], - ]; - - // 普通RPC错误 - } - - // 检查是否为直接返回的错误数组 - if (is_array($data) && isset($data['code'], $data['message'])) { - return $data; - } - } catch (Throwable $parseError) { - // 解析失败不影响主流程 - } - - return null; - } -} diff --git a/extend/Aether/PHP/Hyperf/Middleware/RequestId.php b/extend/Aether/PHP/Hyperf/Middleware/RequestId.php deleted file mode 100644 index 20c642b..0000000 --- a/extend/Aether/PHP/Hyperf/Middleware/RequestId.php +++ /dev/null @@ -1,23 +0,0 @@ -getHeaderLine('X-Request-Id') ?: uniqid(); - Context::set('request_id', $requestId); - - $response = $handler->handle($request); - return $response->withHeader('X-Request-Id', $requestId); - } -} diff --git a/extend/Aether/PHP/Hyperf/RpcException/ApiExceptionHandler.php b/extend/Aether/PHP/Hyperf/RpcException/ApiExceptionHandler.php deleted file mode 100644 index 4fb0b84..0000000 --- a/extend/Aether/PHP/Hyperf/RpcException/ApiExceptionHandler.php +++ /dev/null @@ -1,64 +0,0 @@ -logger = $logger; - } - - public function handle(Throwable $throwable, ResponseInterface $response): MessageInterface|ResponseInterface - { - $this->logger->error($throwable->getMessage()); - // 业务异常 - if ($throwable instanceof BusinessException) { - $code = $throwable->getCode(); - $message = $throwable->getMessage(); - $data = $throwable->getData(); - } else { - // 其他异常 - $code = $throwable->getCode(); // ErrorCode::SYSTEM_ERROR; - $data = env('APP_ENV') === 'dev' ? [ - 'trace' => $throwable->getTraceAsString(), - 'file' => $throwable->getFile(), - 'line' => $throwable->getLine(), - ] : []; - $message = env('APP_ENV') === 'dev' ? $throwable->getMessage() : ErrorCode::getMessage($code); - } - - $result = [ - Config::RESPONSE_FIELD_KEY_CODE => $code, - Config::RESPONSE_FIELD_KEY_DATA => $data, - Config::RESPONSE_FIELD_KEY_MESSAGE => $message, - ]; - - $body = new SwooleStream(json_encode($result, JSON_UNESCAPED_UNICODE)); - - return $response->withHeader('Content-Type', 'application/json') - ->withStatus(200) - ->withBody($body); - } - - public function isValid(Throwable $throwable): bool - { - return true; - } -} diff --git a/extend/Aether/PHP/Hyperf/RpcException/BusinessException.php b/extend/Aether/PHP/Hyperf/RpcException/BusinessException.php deleted file mode 100644 index 6c18bd3..0000000 --- a/extend/Aether/PHP/Hyperf/RpcException/BusinessException.php +++ /dev/null @@ -1,41 +0,0 @@ -code = $code; - $this->data = $data; - - parent::__construct($message, $code, $previous); - } - - /** - * 获取额外数据. - */ - public function getData(): array - { - return $this->data; - } -} diff --git a/extend/Aether/PHP/Hyperf/RpcException/ErrorCode.php b/extend/Aether/PHP/Hyperf/RpcException/ErrorCode.php deleted file mode 100644 index caba42d..0000000 --- a/extend/Aether/PHP/Hyperf/RpcException/ErrorCode.php +++ /dev/null @@ -1,47 +0,0 @@ - '系统错误', - self::PARAM_ERROR => '参数错误', - self::AUTH_ERROR => '未授权', - self::FORBIDDEN_ERROR => '权限不足', - self::NOT_FOUND => '资源不存在', - self::DATA_NOT_FOUND => '数据不存在', - self::ARTICLE_NOT_FOUND => '文章不存在', - self::NOTICE_NOT_FOUND => '公告不存在', - ]; - - return $messages[$code] ?? '未知错误'; - } -} diff --git a/extend/Aether/PHP/Hyperf/RpcException/JsonRpcExceptionHandler.php b/extend/Aether/PHP/Hyperf/RpcException/JsonRpcExceptionHandler.php deleted file mode 100644 index c10c513..0000000 --- a/extend/Aether/PHP/Hyperf/RpcException/JsonRpcExceptionHandler.php +++ /dev/null @@ -1,57 +0,0 @@ -logger = $logger; - } - - /** - * JSON-RPC 异常处理. - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ - public function handle(Throwable $throwable, ResponseInterface $response): MessageInterface|\Psr\Http\Message\ResponseInterface|ResponseInterface - { - $responseContents = $response->getBody()->getContents(); - $responseContents = json_decode($responseContents, true); - if (! empty($responseContents['error'])) { - $this->logger->error($responseContents['error']); - - $port = null; - $config = ApplicationContext::getContainer()->get(ConfigInterface::class); - $servers = $config->get('server.servers'); - foreach ($servers as $k => $server) { - if ($server['name'] == 'jsonrpc-http') { - $port = $server['port']; - break; - } - } - $responseContents['error']['message'] .= " - {$config->get('app_name')}:{$port}"; - } - $data = json_encode($responseContents, JSON_UNESCAPED_UNICODE); - return $response->withStatus(200)->withBody(new SwooleStream($data)); - } - - public function isValid(Throwable $throwable): bool - { - return true; - } -} diff --git a/extend/Aether/PHP/Hyperf/RpcException/ValidateException.php b/extend/Aether/PHP/Hyperf/RpcException/ValidateException.php deleted file mode 100644 index 2bf3f93..0000000 --- a/extend/Aether/PHP/Hyperf/RpcException/ValidateException.php +++ /dev/null @@ -1,38 +0,0 @@ -code = $code; - $this->data = $data; - - parent::__construct($message, $code, $previous); - } - - public function getData(): array - { - return $this->data; - } -} diff --git a/extend/Aether/PHP/Hyperf/RpcExceptionHandler.php b/extend/Aether/PHP/Hyperf/RpcExceptionHandler.php deleted file mode 100644 index 169f6df..0000000 --- a/extend/Aether/PHP/Hyperf/RpcExceptionHandler.php +++ /dev/null @@ -1,104 +0,0 @@ -getRpcIdFromRequest(); - - // 构建符合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/Aether/PHP/Hyperf/Traits/AetherEnum.php b/extend/Aether/PHP/Hyperf/Traits/AetherEnum.php deleted file mode 100644 index 858b291..0000000 --- a/extend/Aether/PHP/Hyperf/Traits/AetherEnum.php +++ /dev/null @@ -1,159 +0,0 @@ - 枚举值集合 - */ - public static function values(): array - { - self::validateEnumStructure(); - - $values = []; - foreach (self::cases() as $case) { - $values[] = $case->value; - } - return $values; - } - - /** - * 获取所有枚举描述数组(与values()顺序一一对应). - * @return array 描述文本集合 - */ - public static function descriptions(): array - { - self::validateEnumStructure(); - - $descriptions = []; - foreach (self::cases() as $case) { - $descriptions[] = $case->description(); - } - return $descriptions; - } - - /** - * 获取值-描述映射数组(用于下拉选择等场景). - * @return array 键为枚举值,值为描述文本 - */ - public static function valueMap(): array - { - self::validateEnumStructure(); - - $map = []; - foreach (self::cases() as $case) { - $map[$case->value] = $case->description(); - } - return $map; - } - - /** - * 根据值获取枚举实例(严格模式). - * @param int|string $value 枚举值 - * @return AetherEnum 枚举实例 - */ - public static function fromValue(int|string $value): self - { - self::validateEnumStructure(); - - // 检查值类型是否与枚举类型匹配(基于第一个case的类型) - $firstCase = self::cases()[0] ?? null; - if ($firstCase) { - $expectedType = gettype($firstCase->value); - $actualType = gettype($value); - if ($expectedType !== $actualType) { - throw new InvalidArgumentException(sprintf( - '枚举值类型不匹配,%s期望%s类型,实际为%s', - self::class, - $expectedType, - $actualType - )); - } - } - - $enum = self::tryFrom($value); - if (! $enum) { - throw new InvalidArgumentException(sprintf( - '无效的%s值: %s,允许值: %s', - self::class, - $value, - implode(', ', self::values()) - )); - } - return $enum; - } - - /** - * 根据描述获取枚举实例(精确匹配). - * @param string $description 描述文本 - * @return null|AetherEnum 匹配的枚举实例,无匹配时返回null - */ - public static function fromDescription(string $description): ?self - { - self::validateEnumStructure(); - - foreach (self::cases() as $case) { - if ($case->description() === $description) { - return $case; - } - } - return null; - } - - /** - * 检查值是否为有效的枚举值(严格类型检查). - * @param int|string $value 待检查的值 - * @return bool 是否有效 - */ - public static function isValidValue(int|string $value): bool - { - self::validateEnumStructure(); - - foreach (self::cases() as $case) { - if ($case->value === $value) { // 严格相等,避免类型松散匹配 - return true; - } - } - return false; - } - - /** - * 校验枚举结构合法性(替代__init,私有静态方法). - */ - private static function validateEnumStructure(): void - { - // 检查当前类是否为枚举 - if (! (new ReflectionClass(self::class))->isEnum()) { - throw new InvalidArgumentException(sprintf( - 'AetherEnum trait仅允许枚举类使用,%s不是枚举', - self::class - )); - } - - // 检查枚举是否实现了description()方法 - if (! method_exists(self::class, 'description')) { - throw new InvalidArgumentException(sprintf( - '枚举类%s必须实现description()方法(返回字符串描述)', - self::class - )); - } - - // 检查description()方法返回值是否为字符串 - $sampleCase = self::cases()[0] ?? null; - if ($sampleCase && ! is_string($sampleCase->description())) { - throw new InvalidArgumentException(sprintf( - '枚举类%s的description()方法必须返回字符串', - self::class - )); - } - } -} diff --git a/extend/Aether/PHP/Hyperf/Traits/AetherSearchable.php b/extend/Aether/PHP/Hyperf/Traits/AetherSearchable.php deleted file mode 100644 index fa47aaa..0000000 --- a/extend/Aether/PHP/Hyperf/Traits/AetherSearchable.php +++ /dev/null @@ -1,10 +0,0 @@ -hidden, true)) { - $this->hidden[] = 'deleted_at'; - } - } -} diff --git a/extend/Aether/PHP/Hyperf/Traits/AetherTree.php b/extend/Aether/PHP/Hyperf/Traits/AetherTree.php deleted file mode 100644 index 0776572..0000000 --- a/extend/Aether/PHP/Hyperf/Traits/AetherTree.php +++ /dev/null @@ -1,128 +0,0 @@ -getParentIdField(); - $sortField = $self->getSortField(); - - $items = $items instanceof Collection ? $items->toArray() : $items; - $tree = []; - - foreach ($items as $item) { - if ($item[$parentField] == $parentId) { - $children = static::buildTree($items, $item['id']); - if (! empty($children)) { - $item['children'] = $children; - } - $tree[] = $item; - } - } - - $self->sortTreeItems($tree, $sortField); - return $tree; - } - - /** - * 获取指定节点的所有子节点ID. - */ - public function getChildIds(int $id): array - { - $parentField = $this->getParentIdField(); - // 现在可以安全调用newQuery(),因为已通过继承检查 - $allItems = $this->newQuery()->get(['id', $parentField])->toArray(); - $ids = [$id]; - - $this->collectChildIds($allItems, $id, $parentField, $ids); - return $ids; - } - - /** - * 获取节点的完整路径. - */ - 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 - { - foreach ($items as $item) { - if ($item[$parentField] == $parentId) { - $ids[] = $item['id']; - $this->collectChildIds($items, $item['id'], $parentField, $ids); - } - } - } -}