From 0a6ba2c56621bbc0b39cade9b2f92ef3cad8dd04 Mon Sep 17 00:00:00 2001 From: Aether Date: Fri, 19 Sep 2025 17:10:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=99=E5=B8=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- app/Controller/TeacherController.php | 162 ++++++++++++++++++ app/Exception/BusinessException.php | 3 + app/JsonRpc/Service/TeacherService.php | 88 ++++++++++ app/Model/Campus.php | 2 +- app/Model/Teacher.php | 106 ++++++++++++ app/Validator/TeacherValidator.php | 77 +++++++++ config/autoload/services.php | 14 ++ .../src/Contract/TeacherServiceInterface.php | 38 +++- 9 files changed, 489 insertions(+), 3 deletions(-) create mode 100644 app/Controller/TeacherController.php create mode 100644 app/JsonRpc/Service/TeacherService.php create mode 100644 app/Model/Teacher.php create mode 100644 app/Validator/TeacherValidator.php diff --git a/.env b/.env index 1e8e6bd..69af120 100644 --- a/.env +++ b/.env @@ -9,7 +9,7 @@ DB_USERNAME=hyperf_data DB_PASSWORD=4cfDRXZSksn7npiP DB_CHARSET=utf8mb4 DB_COLLATION=utf8mb4_unicode_ci -DB_PREFIX=da_ +DB_PREFIX= REDIS_HOST=localhost REDIS_AUTH=(null) diff --git a/app/Controller/TeacherController.php b/app/Controller/TeacherController.php new file mode 100644 index 0000000..2c96385 --- /dev/null +++ b/app/Controller/TeacherController.php @@ -0,0 +1,162 @@ +all(); + $validator = $this->validator->validateCreate($data); + if ($validator->fails()) { + throw new BusinessException(400, $validator->errors()->first()); + } + + $teacher = new Teacher(); + $teacher->fill($data); + $teacher->save(); + + return AetherResponse::success($teacher->toArray(), '教师创建成功'); + } + + /** + * 获取教师详情. + * @GetMapping(path="/{id}") + */ + public function get(int $id): array + { + $teacher = Teacher::find($id); + if (! $teacher) { + throw new BusinessException(10101); + } + + return AetherResponse::success($teacher->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()); + } + + $teacher = Teacher::find($id); + if (! $teacher) { + throw new BusinessException(10101); + } + + $teacher->fill($data); + $teacher->save(); + + return AetherResponse::success($teacher->toArray(), '教师信息更新成功'); + } + + /** + * 删除教师. + * @DeleteMapping(path="/{id}") + */ + public function delete(int $id): array + { + $teacher = Teacher::find($id); + if (! $teacher) { + throw new BusinessException(10101); + } + + $teacher->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 = Teacher::query(); + + if (! empty($data['id'])) { + $query->where('id', $data['id']); + } + if (! empty($data['name'])) { + $query->where('name', 'like', "%{$data['name']}%"); + } + if (isset($data['campus_id'])) { + $query->where('campus_id', $data['campus_id']); + } + if (! empty($data['major_subject'])) { + $query->where('major_subject', 'like', "%{$data['major_subject']}%"); + } + 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="/search") + */ + public function search(RequestInterface $request): array + { + $keyword = $request->input('keyword', ''); + $page = (int) $request->input('page', 1); + $size = (int) $request->input('size', 20); + + $query = Teacher::search($keyword)->enabled(); + + $total = $query->count(); + $list = $query->forPage($page, $size)->get()->toArray(); + + return AetherResponse::success([ + 'total' => $total, + 'page' => $page, + 'size' => $size, + 'list' => $list, + ]); + } +} diff --git a/app/Exception/BusinessException.php b/app/Exception/BusinessException.php index b1a8cb4..5d754a8 100644 --- a/app/Exception/BusinessException.php +++ b/app/Exception/BusinessException.php @@ -38,6 +38,9 @@ class BusinessException extends ServerException 10002 => '校区已存在', 10003 => '父级校区不存在', 10004 => '层级参数错误', + 10101 => '教师不存在', + 10102 => '教师已存在', + 10103 => '该教师已有关联课程,无法删除', ]; return $messages[$code] ?? '业务处理失败'; diff --git a/app/JsonRpc/Service/TeacherService.php b/app/JsonRpc/Service/TeacherService.php new file mode 100644 index 0000000..34bfaff --- /dev/null +++ b/app/JsonRpc/Service/TeacherService.php @@ -0,0 +1,88 @@ +status != 1) { + throw new BusinessException('教师不存在或已禁用', 10101); + } + return $teacher->toArray(); + } + + public function getTeachersByIds(array $ids): array + { + return Teacher::whereIn('id', $ids) + ->enabled() + ->orderBy('sort_order', 'asc') + ->get() + ->toArray(); + } + + public function getTeachersByCampusId(int $campusId, int $page = 1, int $size = 20): array + { + $query = Teacher::campusId($campusId) + ->enabled() + ->orderBy('sort_order', 'asc'); + + $total = $query->count(); + $list = $query->forPage($page, $size)->get()->toArray(); + + return [ + 'total' => $total, + 'page' => $page, + 'size' => $size, + 'list' => $list, + ]; + } + + public function getTeachersBySubject(string $subject, int $page = 1, int $size = 20): array + { + $query = Teacher::subject($subject) + ->enabled() + ->orderBy('sort_order', 'asc'); + + $total = $query->count(); + $list = $query->forPage($page, $size)->get()->toArray(); + + return [ + 'total' => $total, + 'page' => $page, + 'size' => $size, + 'list' => $list, + ]; + } + + public function searchTeachers(string $keyword, int $page = 1, int $size = 20): array + { + $query = Teacher::search($keyword) + ->enabled() + ->orderBy('sort_order', 'asc'); + + $total = $query->count(); + $list = $query->forPage($page, $size)->get()->toArray(); + + return [ + 'total' => $total, + 'page' => $page, + 'size' => $size, + 'list' => $list, + ]; + } +} diff --git a/app/Model/Campus.php b/app/Model/Campus.php index 62396b4..45cd79b 100644 --- a/app/Model/Campus.php +++ b/app/Model/Campus.php @@ -28,7 +28,7 @@ use Hyperf\Database\Model\Relations\HasMany; */ class Campus extends AetherModel { - protected ?string $table = 'campus'; + protected ?string $table = 'da_campus'; protected array $fillable = [ 'name', diff --git a/app/Model/Teacher.php b/app/Model/Teacher.php new file mode 100644 index 0000000..0fd5bb8 --- /dev/null +++ b/app/Model/Teacher.php @@ -0,0 +1,106 @@ + 'integer', + 'age' => 'integer', + 'gender' => 'integer', + 'campus_id' => 'integer', + 'status' => 'integer', + 'sort_order' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + ]; + + /** + * 所属校区. + */ + public function campus(): BelongsTo + { + return $this->belongsTo(Campus::class, 'campus_id', 'id') + ->where('status', 1) + ->whereNull('deleted_at'); + } + + /** + * 按校区查询. + */ + public function scopeCampusId(Builder $query, int $campusId): Builder + { + return $query->where('campus_id', $campusId); + } + + /** + * 按科目查询. + */ + public function scopeSubject(Builder $query, string $subject): Builder + { + return $query->where('major_subject', 'like', "%{$subject}%"); + } + + /** + * 搜索教师. + */ + public function scopeSearch(Builder $query, string $keyword): Builder + { + return $query->where(function ($q) use ($keyword) { + $q->where('name', 'like', "%{$keyword}%") + ->orWhere('title', 'like', "%{$keyword}%") + ->orWhere('major_subject', 'like', "%{$keyword}%") + ->orWhere('introduction', 'like', "%{$keyword}%"); + }); + } + + /** + * 查询启用的教师. + */ + public function scopeEnabled(Builder $query): Builder + { + return $query->where('status', 1); + } +} diff --git a/app/Validator/TeacherValidator.php b/app/Validator/TeacherValidator.php new file mode 100644 index 0000000..e9895e9 --- /dev/null +++ b/app/Validator/TeacherValidator.php @@ -0,0 +1,77 @@ +validationFactory->make($data, [ + 'name' => 'required|string|max:50', + 'age' => 'nullable|integer|min:18|max:65', + 'gender' => 'nullable|integer|in:1,2', + 'avatar' => 'nullable|url|max:255', + 'title' => 'nullable|string|max:100', + 'major_subject' => 'required|string|max:100', + 'teaching_style' => 'nullable|string', + 'introduction' => 'nullable|string', + 'campus_id' => 'nullable|integer|min:0', + 'status' => 'nullable|integer|in:0,1', + 'sort_order' => 'nullable|integer|min:0', + ], [ + 'name.required' => '教师姓名不能为空', + 'major_subject.required' => '主讲科目不能为空', + 'age.min' => '年龄不能小于18岁', + 'age.max' => '年龄不能大于65岁', + 'gender.in' => '性别只能是1(男)或2(女)', + 'avatar.url' => '头像必须是有效的URL', + ]); + } + + public function validateUpdate(array $data): Validator + { + return $this->validationFactory->make($data, [ + 'id' => 'required|integer|min:1', + 'name' => 'nullable|string|max:50', + 'age' => 'nullable|integer|min:18|max:65', + 'gender' => 'nullable|integer|in:1,2', + 'avatar' => 'nullable|url|max:255', + 'title' => 'nullable|string|max:100', + 'major_subject' => 'nullable|string|max:100', + 'teaching_style' => 'nullable|string', + 'introduction' => 'nullable|string', + 'campus_id' => 'nullable|integer|min:0', + 'status' => 'nullable|integer|in:0,1', + 'sort_order' => 'nullable|integer|min:0', + ], [ + 'id.required' => '教师ID不能为空', + 'age.min' => '年龄不能小于18岁', + 'age.max' => '年龄不能大于65岁', + 'gender.in' => '性别只能是1(男)或2(女)', + 'avatar.url' => '头像必须是有效的URL', + ]); + } + + public function validateQuery(array $data): Validator + { + return $this->validationFactory->make($data, [ + 'id' => 'nullable|integer|min:1', + 'name' => 'nullable|string|max:50', + 'campus_id' => 'nullable|integer|min:0', + 'major_subject' => 'nullable|string|max:100', + 'status' => 'nullable|integer|in:0,1', + 'page' => 'nullable|integer|min:1', + 'size' => 'nullable|integer|min:1|max:100', + 'keyword' => 'nullable|string|max:100', + ]); + } +} \ No newline at end of file diff --git a/config/autoload/services.php b/config/autoload/services.php index 54e06b5..e62535e 100644 --- a/config/autoload/services.php +++ b/config/autoload/services.php @@ -48,5 +48,19 @@ return [ // 'heartbeat' => 5, // 'heartbeat_timeout' => 15, // ], + [ + 'name' => 'DataTeacher', + 'host' => env('NACOS_HOST', '192.168.28.199'), + 'port' => env('NACOS_PORT', 8848), + 'username' => env('NACOS_USERNAME', 'nacos'), + 'password' => env('NACOS_PASSWORD', 'nacos'), + 'namespace' => env('NACOS_NAMESPACE', 'e42b853c-5195-478b-b5e3-6d49f6a45053'), + 'group_name' => env('NACOS_GROUP', 'DEFAULT_GROUP'), + 'service_name' => 'DataTeacher', + 'metadata' => [ + 'protocol' => 'jsonrpc-http', + 'server' => 'jsonrpc-http', + ], + ], // ], ]; diff --git a/extend/MicroService/src/Contract/TeacherServiceInterface.php b/extend/MicroService/src/Contract/TeacherServiceInterface.php index 8161275..bad085b 100644 --- a/extend/MicroService/src/Contract/TeacherServiceInterface.php +++ b/extend/MicroService/src/Contract/TeacherServiceInterface.php @@ -1,8 +1,44 @@