diff --git a/app/Controller/CampusController.php b/app/Controller/CampusController.php index ae930ad..0f607fe 100644 --- a/app/Controller/CampusController.php +++ b/app/Controller/CampusController.php @@ -7,8 +7,8 @@ namespace App\Controller; use Aether\AetherController; use Aether\AetherResponse; use App\Exception\BusinessException; -use App\Model\Campus; -use App\Validator\CampusValidator; +use Aether\Campus; +use Aether\CampusValidator; use Exception; use Hyperf\Di\Annotation\Inject; use Hyperf\HttpServer\Annotation\Controller; diff --git a/app/Controller/TeacherController.php b/app/Controller/TeacherController.php index b4fa403..60be093 100644 --- a/app/Controller/TeacherController.php +++ b/app/Controller/TeacherController.php @@ -7,8 +7,8 @@ namespace App\Controller; use Aether\AetherController; use Aether\AetherResponse; use App\Exception\BusinessException; -use App\Model\Teacher; -use App\Validator\TeacherValidator; +use Aether\Teacher; +use Aether\TeacherValidator; use Hyperf\Di\Annotation\Inject; use Hyperf\HttpServer\Annotation\Controller; use Hyperf\HttpServer\Annotation\DeleteMapping; diff --git a/app/JsonRpc/Service/CampusService.php b/app/JsonRpc/Service/CampusService.php index dc40ac2..f59a056 100644 --- a/app/JsonRpc/Service/CampusService.php +++ b/app/JsonRpc/Service/CampusService.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace App\JsonRpc\Service; use Aether\Exception\BusinessException; -use App\Model\Campus; +use Aether\Campus; use Hyperf\RpcServer\Annotation\RpcService; use MicroService\Contract\CampusServiceInterface; diff --git a/app/JsonRpc/Service/TeacherService.php b/app/JsonRpc/Service/TeacherService.php index 34bfaff..62697c7 100644 --- a/app/JsonRpc/Service/TeacherService.php +++ b/app/JsonRpc/Service/TeacherService.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace App\JsonRpc\Service; use Aether\Exception\BusinessException; -use App\Model\Teacher; +use Aether\Teacher; use Hyperf\RpcServer\Annotation\RpcService; use MicroService\Contract\TeacherServiceInterface; diff --git a/config/autoload/exceptions.php b/config/autoload/exceptions.php index d123d35..6fc068d 100644 --- a/config/autoload/exceptions.php +++ b/config/autoload/exceptions.php @@ -13,12 +13,12 @@ return [ 'handler' => [ 'http' => [ Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class, - App\Exception\Handler\AppExceptionHandler::class, + Aether\AppExceptionHandler::class, ], 'jsonrpc-http' => [ //Aether\Exception\AppExceptionHandler::class, Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class, - App\Exception\Handler\AppExceptionHandler::class, + Aether\AppExceptionHandler::class, ], ], ]; diff --git a/extend/Aether/PHP/Hyperf/AbstractModel.php b/extend/Aether/PHP/Hyperf/AbstractModel.php new file mode 100644 index 0000000..f361260 --- /dev/null +++ b/extend/Aether/PHP/Hyperf/AbstractModel.php @@ -0,0 +1,101 @@ + 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + ]; + + /** + * 启用状态查询作用域 + */ + public function scopeEnabled(Builder $query): Builder + { + return $query->where('status', 1); + } + + /** + * 按ID查询并检查存在性 + * @param int $id + * @param array $columns + * @return AbstractModel + */ + public static function findOrFail(int $id, $columns = []): self + { + $model = self::find($id); + if (!$model) { + throw new ModelNotFoundException(); + } + return $model; + } + + /** + * 创建记录并返回实例 + */ + public static function createOne(array $data): self + { + $model = new static(); + $model->fill($data); + $model->save(); + return $model; + } + + /** + * 更新记录 + */ + public static function updateById(int $id, array $data): bool + { + $model = self::findOrFail($id); + return $model->update($data); + } + + /** + * 删除记录 + * @throws Exception + */ + public static function deleteById(int $id): bool + { + $model = self::findOrFail($id); + return $model->delete(); + } +} \ No newline at end of file diff --git a/extend/Aether/PHP/Hyperf/AbstractService.php b/extend/Aether/PHP/Hyperf/AbstractService.php new file mode 100644 index 0000000..a8ed6e6 --- /dev/null +++ b/extend/Aether/PHP/Hyperf/AbstractService.php @@ -0,0 +1,147 @@ +logger = $this->loggerFactory->get($this->getLoggerName()); + } + + /** + * 获取资源列表 + */ + public function list(array $params = []): array + { + $this->validateQuery($params); + return $this->getModel()::query() + ->when(isset($params['page']), function ($query) use ($params) { + $page = (int)($params['page'] ?? 1); + $size = (int)($params['size'] ?? 20); + return $query->forPage($page, $size); + }) + ->get() + ->toArray(); + } + + /** + * 获取资源详情 + */ + public function detail(int $id): array + { + $model = $this->getModel()::find($id); + if (!$model) { + throw new BusinessException(404, '资源不存在'); + } + return $model->toArray(); + } + + /** + * 创建资源 + */ + public function create(array $data): int + { + $this->validateCreate($data); + $model = $this->getModel()::create($data); + $this->logger->info('资源创建成功', ['id' => $model->id, 'data' => $data]); + return $model->id; + } + + /** + * 更新资源 + */ + public function update(int $id, array $data): bool + { + $this->validateUpdate($data); + $model = $this->getModel()::find($id); + if (!$model) { + throw new BusinessException(404, '资源不存在'); + } + $result = $model->update($data); + $this->logger->info('资源更新成功', ['id' => $id, 'data' => $data]); + return $result; + } + + /** + * 删除资源 + */ + public function delete(int $id): bool + { + $model = $this->getModel()::find($id); + if (!$model) { + throw new BusinessException(404, '资源不存在'); + } + $result = $model->delete(); + $this->logger->info('资源删除成功', ['id' => $id]); + return $result; + } + + /** + * 验证查询参数 + */ + protected function validateQuery(array $params): void + { + $validator = $this->getValidator()->scene('query', $params); + if ($validator->fails()) { + throw new BusinessException(400, $validator->errors()->first()); + } + } + + /** + * 验证创建参数 + */ + protected function validateCreate(array $data): void + { + $validator = $this->getValidator()->scene('create', $data); + if ($validator->fails()) { + throw new BusinessException(400, $validator->errors()->first()); + } + } + + /** + * 验证更新参数 + */ + protected function validateUpdate(array $data): void + { + $validator = $this->getValidator()->scene('update', $data); + if ($validator->fails()) { + throw new BusinessException(400, $validator->errors()->first()); + } + } + + /** + * 获取日志名称 + */ + protected function getLoggerName(): string + { + return strtolower((new \ReflectionClass($this))->getShortName()); + } + + /** + * 获取对应的模型类 + * @return AbstractModel + */ + abstract protected function getModel(): AbstractModel; + + /** + * 获取对应的验证器类 + * @return AbstractValidator + */ + abstract protected function getValidator(): AbstractValidator; +} \ No newline at end of file diff --git a/extend/Aether/PHP/Hyperf/AbstractValidator.php b/extend/Aether/PHP/Hyperf/AbstractValidator.php new file mode 100644 index 0000000..7f90e7d --- /dev/null +++ b/extend/Aether/PHP/Hyperf/AbstractValidator.php @@ -0,0 +1,67 @@ +getSceneRules($scene); + return $this->validationFactory->make($data, $rules, $this->messages, $this->attributes); + } + + /** + * 获取场景验证规则 + */ + protected function getSceneRules(string $scene): array + { + if (empty($this->scenes[$scene])) { + return $this->rules; + } + + $sceneRules = []; + foreach ($this->scenes[$scene] as $field) { + if (isset($this->rules[$field])) { + $sceneRules[$field] = $this->rules[$field]; + } + } + + return $sceneRules; + } +} \ No newline at end of file diff --git a/extend/Aether/PHP/Hyperf/AetherController.php b/extend/Aether/PHP/Hyperf/AetherController.php index e253ab8..446097c 100644 --- a/extend/Aether/PHP/Hyperf/AetherController.php +++ b/extend/Aether/PHP/Hyperf/AetherController.php @@ -20,30 +20,59 @@ abstract class AetherController #[Inject] protected ResponseInterface $response; + /** - * 获取请求参数. - * @param string $key - * @param mixed|null $default - * @return mixed + * 获取资源列表 (RESTFul: GET /resources) */ - public function requestParam(string $key, mixed $default = null): mixed + public function index(): array { - return $this->request->input($key, $default); + $params = $this->request->all(); + $result = $this->getService()->list($params); + return AetherResponse::success($result); } /** - * 获取所有请求参数. + * 获取单个资源 (RESTFul: GET /resources/{id}) */ - public function requestParams(): array + public function show(int $id): array { - return $this->request->all(); + $result = $this->getService()->detail($id); + return AetherResponse::success($result); } /** - * 返回JSON响应. + * 创建资源 (RESTFul: POST /resources) */ - public function json(array $data): \Psr\Http\Message\ResponseInterface + public function store() { - return $this->response->json($data); + $data = $this->request->all(); + $id = $this->getService()->create($data); + return AetherResponse::success(['id' => $id], '创建成功'); } + + /** + * 更新资源 (RESTFul: PUT /resources/{id}) + */ + public function update(int $id): array + { + $data = $this->request->all(); + $this->getService()->update($id, $data); + return AetherResponse::success(null, '更新成功'); + } + + /** + * 删除资源 (RESTFul: DELETE /resources/{id}) + */ + public function destroy(int $id): array + { + $this->getService()->delete($id); + return AetherResponse::success(null, '删除成功'); + } + + /** + * 获取对应的服务类 + * @return \Aether\AbstractService + */ + abstract protected function getService(); + } diff --git a/extend/Aether/PHP/Hyperf/ApiExceptionHandler.php b/extend/Aether/PHP/Hyperf/ApiExceptionHandler.php new file mode 100644 index 0000000..5fb5e1e --- /dev/null +++ b/extend/Aether/PHP/Hyperf/ApiExceptionHandler.php @@ -0,0 +1,42 @@ + $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; + } +} \ No newline at end of file diff --git a/extend/Aether/PHP/Hyperf/Middleware/Cors.php b/extend/Aether/PHP/Hyperf/Middleware/Cors.php new file mode 100644 index 0000000..a5d64c9 --- /dev/null +++ b/extend/Aether/PHP/Hyperf/Middleware/Cors.php @@ -0,0 +1,29 @@ +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/RequestId.php b/extend/Aether/PHP/Hyperf/Middleware/RequestId.php index e1b0cd2..20c642b 100644 --- a/extend/Aether/PHP/Hyperf/Middleware/RequestId.php +++ b/extend/Aether/PHP/Hyperf/Middleware/RequestId.php @@ -16,7 +16,7 @@ class RequestId implements MiddlewareInterface { $requestId = $request->getHeaderLine('X-Request-Id') ?: uniqid(); Context::set('request_id', $requestId); - var_dump('requestId: ' . $requestId); + $response = $handler->handle($request); return $response->withHeader('X-Request-Id', $requestId); } diff --git a/extend/Aether/PHP/Hyperf/Middleware/Trace.php b/extend/Aether/PHP/Hyperf/Middleware/Trace.php new file mode 100644 index 0000000..1d858b9 --- /dev/null +++ b/extend/Aether/PHP/Hyperf/Middleware/Trace.php @@ -0,0 +1,23 @@ +getHeaderLine('X-Request-Id') ?: uniqid(); + Context::set('request_id', $requestId); + + $response = $handler->handle($request); + return $response->withHeader('X-Request-Id', $requestId); + } +} \ No newline at end of file diff --git a/extend/Aether/PHP/Hyperf/RpcExceptionHandler.php b/extend/Aether/PHP/Hyperf/RpcExceptionHandler.php new file mode 100644 index 0000000..fb55e20 --- /dev/null +++ b/extend/Aether/PHP/Hyperf/RpcExceptionHandler.php @@ -0,0 +1,34 @@ + $throwable->getCode() ?: 500, + 'message' => $throwable->getMessage() ?: '服务调用失败', + 'request_id' => Context::get('request_id', ''), + ]; + + $protocol = make(Protocol::class); + $response->getBody()->write($protocol->pack($data)); + + return $response; + } + + public function isValid(Throwable $throwable): bool + { + return true; + } +} \ No newline at end of file