From b69f14ded2da9622dc76c6a1f62438fc9c0a3e33 Mon Sep 17 00:00:00 2001 From: Aether Date: Fri, 19 Sep 2025 15:14:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E7=A1=80=E6=95=B0=E6=8D=AE-=E6=A0=A1?= =?UTF-8?q?=E5=8C=BA=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controller/CampusController.php | 183 +++++++++++ app/Exception/BusinessException.php | 45 +++ .../Handler/BusinessExceptionHandler.php | 65 ++++ .../Contract/CampusServiceInterface.php | 40 --- app/JsonRpc/Service/CampusService.php | 72 ++--- app/Model/Campus.php | 24 ++ app/Validator/CampusValidator.php | 69 ++++ composer.json | 3 +- config/autoload/exceptions.php | 2 +- extend/Aether/PHP/Hyperf/AetherController.php | 45 +++ .../Aether/PHP/Hyperf/AetherCrudInterface.php | 25 +- .../Aether/PHP/Hyperf/AetherCrudService.php | 294 +++++++++--------- extend/Aether/PHP/Hyperf/AetherResponse.php | 56 ++++ extend/Aether/PHP/Hyperf/AetherService.php | 12 +- extend/Aether/PHP/Hyperf/AetherValidator.php | 85 +++-- .../src/Contract/CampusServiceInterface.php | 37 +++ 16 files changed, 757 insertions(+), 300 deletions(-) create mode 100644 app/Controller/CampusController.php create mode 100644 app/Exception/BusinessException.php create mode 100644 app/Exception/Handler/BusinessExceptionHandler.php delete mode 100644 app/JsonRpc/Contract/CampusServiceInterface.php create mode 100644 app/Validator/CampusValidator.php create mode 100644 extend/Aether/PHP/Hyperf/AetherController.php create mode 100644 extend/Aether/PHP/Hyperf/AetherResponse.php create mode 100644 extend/MicroService/src/Contract/CampusServiceInterface.php diff --git a/app/Controller/CampusController.php b/app/Controller/CampusController.php new file mode 100644 index 0000000..2e3c7d1 --- /dev/null +++ b/app/Controller/CampusController.php @@ -0,0 +1,183 @@ +all(); + $validator = $this->validator->validateCreate($data); + if ($validator->fails()) { + throw new BusinessException(400, $validator->errors()->first()); + } + + $campus = new Campus(); + $campus->fill($data); + $campus->save(); + + return AetherResponse::success($campus->toArray(), '校区创建成功'); + } + + /** + * 获取校区详情. + * @GetMapping(path="/{id}") + */ + public function get(int $id): array + { + $campus = Campus::find($id); + if (! $campus) { + throw new BusinessException(10001); + } + + return AetherResponse::success($campus->toArray()); + } + + /** + * 更新校区. + * @PutMapping(path="/{id}") + */ + public function update(int $id, RequestInterface $request): array + { + $data = $request->all(); + $data['id'] = $id; + + $validator = $this->validator->validateUpdate($data); + if ($validator->fails()) { + throw new BusinessException(400, $validator->errors()->first()); + } + + $campus = Campus::find($id); + if (! $campus) { + throw new BusinessException(10001); + } + + $campus->fill($data); + $campus->save(); + + return AetherResponse::success($campus->toArray(), '校区更新成功'); + } + + /** + * 删除校区. + * @DeleteMapping(path="/{id}") + * @throws Exception + */ + public function delete(int $id): array + { + $campus = Campus::find($id); + if (! $campus) { + throw new BusinessException(10001); + } + + $campus->delete(); + return AetherResponse::success(null, '校区删除成功'); + } + + /** + * 校区列表. + * @GetMapping(path="/list") + */ + public function list(RequestInterface $request): array + { + $data = $request->all(); + $validator = $this->validator->validateQuery($data); + if ($validator->fails()) { + throw new BusinessException(400, $validator->errors()->first()); + } + + $query = Campus::query(); + + if (! empty($data['id'])) { + $query->where('id', $data['id']); + } + if (! empty($data['name'])) { + $query->where('name', 'like', "%{$data['name']}%"); + } + if (isset($data['parent_id'])) { + $query->where('parent_id', $data['parent_id']); + } + if (isset($data['level'])) { + $query->where('level', $data['level']); + } + if (! empty($data['province'])) { + $query->where('province', $data['province']); + } + if (! empty($data['city'])) { + $query->where('city', $data['city']); + } + if (isset($data['status'])) { + $query->where('status', $data['status']); + } + + $page = $data['page'] ?? 1; + $size = $data['size'] ?? 20; + + $total = $query->count(); + $list = $query->forPage($page, $size)->get()->toArray(); + + return AetherResponse::success([ + 'total' => $total, + 'page' => $page, + 'size' => $size, + 'list' => $list, + ]); + } + + /** + * 获取省份列表. + * @GetMapping(path="/provinces") + */ + public function provinces(): array + { + $provinces = Campus::level(1) + ->enabled() + ->orderBy('name') + ->get(['id', 'name', 'province']) + ->toArray(); + + return AetherResponse::success($provinces); + } + + /** + * 获取城市列表. + * @GetMapping(path="/cities") + */ + public function cities(RequestInterface $request): array + { + $province = $request->input('province'); + if (empty($province)) { + throw new BusinessException(400, '省份不能为空'); + } + + $cities = Campus::level(2) + ->province($province) + ->enabled() + ->orderBy('name') + ->get(['id', 'name', 'city']) + ->toArray(); + + return AetherResponse::success($cities); + } +} diff --git a/app/Exception/BusinessException.php b/app/Exception/BusinessException.php new file mode 100644 index 0000000..b1a8cb4 --- /dev/null +++ b/app/Exception/BusinessException.php @@ -0,0 +1,45 @@ +getDefaultMessage($code); + } + + parent::__construct($message, $code, $previous); + } + + /** + * 获取默认错误消息. + */ + private function getDefaultMessage(int $code): string + { + $messages = [ + 400 => '请求参数错误', + 401 => '未授权', + 403 => '禁止访问', + 404 => '资源不存在', + 500 => '服务器内部错误', + 10001 => '校区不存在', + 10002 => '校区已存在', + 10003 => '父级校区不存在', + 10004 => '层级参数错误', + ]; + + return $messages[$code] ?? '业务处理失败'; + } +} diff --git a/app/Exception/Handler/BusinessExceptionHandler.php b/app/Exception/Handler/BusinessExceptionHandler.php new file mode 100644 index 0000000..dccf02f --- /dev/null +++ b/app/Exception/Handler/BusinessExceptionHandler.php @@ -0,0 +1,65 @@ +getCode(), + $throwable->getMessage() ?: '业务处理异常' + ); + + $body = json_encode($result, JSON_UNESCAPED_UNICODE); + + return $response + ->withHeader('Content-Type', 'application/json') + ->withStatus(200) + ->withBody(new SwooleStream((string) $body)); + } + + // 安全处理下一个处理器 + if ($this->next !== null) { + return $this->next->handle($throwable, $response); + } + + // 如果没有下一个处理器,直接返回原始响应 + return $response; + } + + /** + * 判断是否需要处理该异常. + */ + public function isValid(Throwable $throwable): bool + { + return $throwable instanceof BusinessException; + } + + /** + * 显式实现setNext方法. + */ + public function setNext(self $handler): void + { + $this->next = $handler; + } +} diff --git a/app/JsonRpc/Contract/CampusServiceInterface.php b/app/JsonRpc/Contract/CampusServiceInterface.php deleted file mode 100644 index 08c2835..0000000 --- a/app/JsonRpc/Contract/CampusServiceInterface.php +++ /dev/null @@ -1,40 +0,0 @@ -status != 1) { + throw new BusinessException('校区不存在或已禁用', 10001); } return $campus->toArray(); } - public function batchGetCampus(array $ids): array + public function getCampusesByIds(array $ids): array { - if (empty($ids)) { - return []; - } - $campuses = Campus::whereIn('id', $ids)->get()->toArray(); - return array_column($campuses, null, 'id'); - } - - public function getCampusByProvince(string $province): array - { - return Campus::province($province) - ->level(3) + return Campus::whereIn('id', $ids) + ->enabled() ->get() ->toArray(); } - public function getCampusByCity(string $province, string $city): array + public function getCampusesByParentId(int $parentId): array { - return Campus::province($province) - ->city($city) - ->level(3) + return Campus::where('parent_id', $parentId) + ->enabled() ->get() ->toArray(); } - public function getCampusHierarchy(int $campusId): array + public function getProvinces(): array { - $hierarchy = []; - $current = Campus::find($campusId); + return Campus::level(1) + ->enabled() + ->orderBy('name') + ->get(['id', 'name', 'province']) + ->toArray(); + } - if (! $current) { - return $hierarchy; - } - - // 从校区向上追溯层级 - while ($current) { - array_unshift($hierarchy, [ - 'id' => $current->id, - 'name' => $current->name, - 'level' => $current->level, - 'province' => $current->province, - 'city' => $current->city, - ]); - - if ($current->parent_id === 0) { - break; - } - - $current = Campus::find($current->parent_id); - } - - return $hierarchy; + public function getCitiesByProvince(string $province): array + { + return Campus::level(2) + ->province($province) + ->enabled() + ->orderBy('name') + ->get(['id', 'name', 'city']) + ->toArray(); } } diff --git a/app/Model/Campus.php b/app/Model/Campus.php index 34822b3..62396b4 100644 --- a/app/Model/Campus.php +++ b/app/Model/Campus.php @@ -7,6 +7,7 @@ namespace App\Model; use Aether\AetherModel; use Carbon\Carbon; use Hyperf\Database\Model\Builder; +use Hyperf\Database\Model\Relations\BelongsTo; use Hyperf\Database\Model\Relations\HasMany; /** @@ -41,11 +42,26 @@ class Campus extends AetherModel ]; protected array $casts = [ + 'id' => 'integer', 'parent_id' => 'integer', 'level' => 'integer', 'status' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', ]; + /** + * 获取父级校区. + * @return BelongsTo + */ + public function parent(): BelongsTo + { + return $this->belongsTo(Campus::class, 'parent_id', 'id') + ->where('status', 1) + ->whereNull('deleted_at'); + } + /** * 获取子校区. */ @@ -77,4 +93,12 @@ class Campus extends AetherModel { return $query->where('level', $level); } + + /** + * 查询启用的校区. + */ + public function scopeEnabled(Builder $query): Builder + { + return $query->where('status', 1); + } } diff --git a/app/Validator/CampusValidator.php b/app/Validator/CampusValidator.php new file mode 100644 index 0000000..885a424 --- /dev/null +++ b/app/Validator/CampusValidator.php @@ -0,0 +1,69 @@ +validationFactory->make($data, [ + 'name' => 'required|string|max:255', + 'parent_id' => 'required|integer|min:0', + 'level' => 'required|integer|in:1,2,3', + 'province' => 'nullable|string|max:100', + 'city' => 'nullable|string|max:100', + 'address' => 'nullable|string|max:500', + 'contact_phone' => 'nullable|string|max:20', + 'contact_person' => 'nullable|string|max:20', + 'status' => 'nullable|integer|in:0,1', + ], [ + 'name.required' => '校区名称不能为空', + 'parent_id.required' => '父级ID不能为空', + 'level.required' => '层级不能为空', + 'level.in' => '层级只能是1、2、3', + ]); + } + + public function validateUpdate(array $data): Validator + { + return $this->validationFactory->make($data, [ + 'id' => 'required|integer|min:1', + 'name' => 'nullable|string|max:255', + 'parent_id' => 'nullable|integer|min:0', + 'level' => 'nullable|integer|in:1,2,3', + 'province' => 'nullable|string|max:100', + 'city' => 'nullable|string|max:100', + 'address' => 'nullable|string|max:500', + 'contact_phone' => 'nullable|string|max:20', + 'contact_person' => 'nullable|string|max:20', + 'status' => 'nullable|integer|in:0,1', + ], [ + 'id.required' => '校区ID不能为空', + 'level.in' => '层级只能是1、2、3', + ]); + } + + public function validateQuery(array $data): Validator + { + return $this->validationFactory->make($data, [ + 'id' => 'nullable|integer|min:1', + 'name' => 'nullable|string|max:255', + 'parent_id' => 'nullable|integer|min:0', + 'level' => 'nullable|integer|in:1,2,3', + 'province' => 'nullable|string|max:100', + 'city' => 'nullable|string|max:100', + 'status' => 'nullable|integer|in:0,1', + 'page' => 'nullable|integer|min:1', + 'size' => 'nullable|integer|min:1|max:100', + ]); + } +} diff --git a/composer.json b/composer.json index 9e5cb8f..6b6129a 100755 --- a/composer.json +++ b/composer.json @@ -57,7 +57,8 @@ "autoload": { "psr-4": { "App\\": "app/", - "Aether\\": "extend/Aether/PHP/Hyperf" + "Aether\\": "extend/Aether/PHP/Hyperf/", + "MicroService\\": "extend/MicroService/src/" }, "files": [] }, diff --git a/config/autoload/exceptions.php b/config/autoload/exceptions.php index fbe9f49..2b0ed5e 100644 --- a/config/autoload/exceptions.php +++ b/config/autoload/exceptions.php @@ -13,7 +13,7 @@ return [ 'handler' => [ 'http' => [ Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class, - App\Exception\Handler\AppExceptionHandler::class, + Aether\Exception\Handler\AppExceptionHandler::class, ], 'jsonrpc-http' => [ Aether\Exception\AppExceptionHandler::class, diff --git a/extend/Aether/PHP/Hyperf/AetherController.php b/extend/Aether/PHP/Hyperf/AetherController.php new file mode 100644 index 0000000..2af9dc7 --- /dev/null +++ b/extend/Aether/PHP/Hyperf/AetherController.php @@ -0,0 +1,45 @@ +request->input($key, $default); + } + + /** + * 获取所有请求参数. + */ + public function requestParams(): array + { + return $this->request->all(); + } + + /** + * 返回JSON响应. + */ + public function json(array $data): \Psr\Http\Message\ResponseInterface + { + return $this->response->json($data); + } +} diff --git a/extend/Aether/PHP/Hyperf/AetherCrudInterface.php b/extend/Aether/PHP/Hyperf/AetherCrudInterface.php index 109ccd4..dbac646 100644 --- a/extend/Aether/PHP/Hyperf/AetherCrudInterface.php +++ b/extend/Aether/PHP/Hyperf/AetherCrudInterface.php @@ -1,40 +1,33 @@ search; - } - protected array $ignoreSearchFields = []; - protected function getIgnoreSearchFields(): array - { - return $this->ignoreSearchFields; - } - /** - * 获取当前服务对应的模型实例(由子类实现) - */ - protected abstract function getModel(): AetherModel; - - /** - * 获取当前服务对应的验证器实例(由子类实现) - */ - protected abstract function getValidator(): AetherValidator; - - - /** - * 通用列表查询(支持分页和树形结构) + * 通用列表查询(支持分页和树形结构). */ public function list(array $params = []): array { @@ -63,8 +42,8 @@ abstract class AetherCrudService extends AetherService implements AetherCrudInte // 存在分页参数(page或size)则进行分页查询 if (isset($params['page']) || isset($params['size'])) { - $page = (int)($params['page'] ?? 1); - $size = (int)($params['size'] ?? 10); + $page = (int) ($params['page'] ?? 1); + $size = (int) ($params['size'] ?? 10); // 确保分页参数合法性 $page = max(1, $page); $size = max(1, min(100, $size)); // 限制最大页大小为100 @@ -72,7 +51,7 @@ abstract class AetherCrudService extends AetherService implements AetherCrudInte $result = $query->paginate($size, ['*'], 'page', $page); return [ 'total' => $result->total(), - 'list' => $result->items() + 'list' => $result->items(), ]; } @@ -81,22 +60,23 @@ abstract class AetherCrudService extends AetherService implements AetherCrudInte // 若模型支持树形结构则构建树形,否则返回普通数组 if ($model instanceof TreeableInterface) { - return $model::buildTree($items, (int)($params['parent_id'] ?? 0)); + return $model::buildTree($items, (int) ($params['parent_id'] ?? 0)); } return $items; } /** - * 通用详情查询 + * 通用详情查询. */ public function detail(int $id): object - {var_dump('detail'); + { + var_dump('detail'); return $this->getModel()->findOrFailById($id); } /** - * 通用创建逻辑 + * 通用创建逻辑. * @throws BusinessException|Throwable */ public function create(array $data): int @@ -108,14 +88,14 @@ abstract class AetherCrudService extends AetherService implements AetherCrudInte $model = $this->getModel()->createOne($data); $this->logger()->info('创建资源', [ 'id' => $model->id, - 'code' => $data['code'] ?? $model->code + 'code' => $data['code'] ?? $model->code, ]); return $model->id; }); } /** - * 通用更新逻辑 + * 通用更新逻辑. * @throws BusinessException|Throwable */ public function update(int $id, array $data): bool @@ -134,14 +114,14 @@ abstract class AetherCrudService extends AetherService implements AetherCrudInte $result = $this->getModel()->updateById($id, $data); $this->logger()->info('更新资源', [ 'id' => $id, - 'data' => $data + 'data' => $data, ]); return $result; }); } /** - * 通用删除逻辑 + * 通用删除逻辑. * @throws BusinessException|Throwable */ public function delete(int $id): bool @@ -162,7 +142,134 @@ abstract class AetherCrudService extends AetherService implements AetherCrudInte } /** - * 钩子方法:更新时的特殊逻辑(子类可重写) + * 根据模型的$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 { @@ -173,7 +280,7 @@ abstract class AetherCrudService extends AetherService implements AetherCrudInte } /** - * 钩子方法:删除前检查子级(子类可重写) + * 钩子方法:删除前检查子级(子类可重写). */ protected function checkChildrenBeforeDelete(int $id): void { @@ -181,23 +288,9 @@ abstract class AetherCrudService extends AetherService implements AetherCrudInte } /** - * 根据模型的$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); - } - } - - /** - * 应用单个搜索规则 + * 应用单个搜索规则. + * @param mixed $value + * @param mixed $rule */ protected function applySearchRule(Builder $query, string $field, $value, $rule): void { @@ -221,98 +314,7 @@ abstract class AetherCrudService extends AetherService implements AetherCrudInte call_user_func($config['handler'], $query, $value); } break; - // 可扩展其他类型:in、>、< 等 + // 可扩展其他类型:in、>、< 等 } } - - /** - * 软删除恢复 - * @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; - }); - } -} \ No newline at end of file +} diff --git a/extend/Aether/PHP/Hyperf/AetherResponse.php b/extend/Aether/PHP/Hyperf/AetherResponse.php new file mode 100644 index 0000000..be14309 --- /dev/null +++ b/extend/Aether/PHP/Hyperf/AetherResponse.php @@ -0,0 +1,56 @@ + 0, + 'message' => $message, + 'data' => $data, + 'timestamp' => time(), + ]; + } + + /** + * 错误响应. + * @param int $code 错误码 + * @param string $message 错误消息 + * @param null|mixed $data 附加数据 + */ + public static function error(int $code, string $message = '', mixed $data = null): array + { + return [ + 'code' => $code, + 'message' => $message ?: self::getDefaultMessage($code), + 'data' => $data, + 'timestamp' => time(), + ]; + } + + /** + * 获取默认错误消息. + */ + 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 index c2edfa1..4342634 100644 --- a/extend/Aether/PHP/Hyperf/AetherService.php +++ b/extend/Aether/PHP/Hyperf/AetherService.php @@ -5,9 +5,9 @@ declare(strict_types=1); namespace Aether; use Aether\Exception\BusinessException; +use Hyperf\DbConnection\Db; use Hyperf\Di\Annotation\Inject; use Hyperf\Logger\LoggerFactory; -use Hyperf\DbConnection\Db; use Psr\Log\LoggerInterface; use Throwable; @@ -17,18 +17,16 @@ abstract class AetherService protected LoggerFactory $loggerFactory; /** - * 获取当前服务日志器 + * 获取当前服务日志器. */ protected function logger(): LoggerInterface { - // return $this->loggerFactory->get(substr(strrchr(static::class, '\\'), 1)); - // 提取类名(如ExamTypeService)作为日志通道名 $className = substr(strrchr(static::class, '\\'), 1); return $this->loggerFactory->get($className); } /** - * 事务处理 + * 事务处理. * @throws Throwable */ protected function transaction(callable $callback) @@ -37,7 +35,7 @@ abstract class AetherService } /** - * 检查资源是否存在(不存在则抛出异常) + * 检查资源是否存在(不存在则抛出异常). * @throws BusinessException */ protected function checkResourceExists(?object $resource, string $message = '资源不存在'): void @@ -46,4 +44,4 @@ abstract class AetherService throw new BusinessException($message, BusinessException::RESOURCE_NOT_FOUND); } } -} \ No newline at end of file +} diff --git a/extend/Aether/PHP/Hyperf/AetherValidator.php b/extend/Aether/PHP/Hyperf/AetherValidator.php index 9f159f2..c863d8e 100644 --- a/extend/Aether/PHP/Hyperf/AetherValidator.php +++ b/extend/Aether/PHP/Hyperf/AetherValidator.php @@ -5,45 +5,44 @@ declare(strict_types=1); namespace Aether; use Aether\Exception\ValidationFailedException; +use Hyperf\Context\ApplicationContext; use Hyperf\Di\Annotation\Inject; use Hyperf\Validation\Contract\ValidatorFactoryInterface; use Hyperf\Validation\Validator; -use Hyperf\Context\ApplicationContext; +use RuntimeException; abstract class AetherValidator { + /** + * 当前场景名. + */ + public ?string $currentScene = null; + #[Inject] protected ValidatorFactoryInterface $validationFactory; /** - * 当前场景名 - */ - public ?string $currentScene = null; - - /** - * 待验证数据 + * 待验证数据. */ protected array $data = []; /** * 自定义验证规则(子类可通过该属性注册,无需重写registerRules) - * 格式:['规则名' => 闭包/类方法] + * 格式:['规则名' => 闭包/类方法]. */ protected array $customRules = []; /** - * 静态快捷验证方法(简化调用) + * 静态快捷验证方法(简化调用). */ public static function validate(string $scene, array $data = []): array { - // return (new static())->scene($scene, $data)->check(); - // 从容器中获取当前类的实例(确保依赖注入生效) $instance = ApplicationContext::getContainer()->get(static::class); return $instance->scene($scene, $data)->check(); } /** - * 设置验证场景和数据(支持链式调用) + * 设置验证场景和数据(支持链式调用). */ public function scene(string $scene, array $data = []): self { @@ -53,17 +52,17 @@ abstract class AetherValidator } /** - * 执行验证(失败抛出异常) + * 执行验证(失败抛出异常). */ public function check(): array { if (empty($this->currentScene)) { - throw new \RuntimeException('请先设置验证场景'); + throw new RuntimeException('请先设置验证场景'); } $scenes = $this->scenes(); - if (!isset($scenes[$this->currentScene])) { - throw new \RuntimeException("验证场景不存在:{$this->currentScene}"); + if (! isset($scenes[$this->currentScene])) { + throw new RuntimeException("验证场景不存在:{$this->currentScene}"); } $sceneConfig = $scenes[$this->currentScene]; @@ -76,7 +75,30 @@ abstract class AetherValidator } /** - * 实际执行验证的逻辑(重命名方法名更清晰) + * 格式化验证错误信息(统一格式,供异常处理器复用). + */ + 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 { @@ -95,30 +117,7 @@ abstract class AetherValidator } /** - * 格式化验证错误信息(统一格式,供异常处理器复用) - */ - 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; - } - - /** - * 自动注册自定义规则(优先使用$customRules属性) + * 自动注册自定义规则(优先使用$customRules属性). */ protected function registerRules(Validator $validator): void { @@ -128,7 +127,7 @@ abstract class AetherValidator } /** - * 定义场景验证规则(子类实现) + * 定义场景验证规则(子类实现). */ abstract protected function scenes(): array; -} \ No newline at end of file +} diff --git a/extend/MicroService/src/Contract/CampusServiceInterface.php b/extend/MicroService/src/Contract/CampusServiceInterface.php new file mode 100644 index 0000000..c98e72a --- /dev/null +++ b/extend/MicroService/src/Contract/CampusServiceInterface.php @@ -0,0 +1,37 @@ +