From b43bf56e8ea5144242d0cfd78699a5911bef4cb4 Mon Sep 17 00:00:00 2001 From: zijing <903204312@qq.com> Date: Tue, 15 Jul 2025 17:35:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AD=A6=E6=A0=A1=E3=80=81?= =?UTF-8?q?=E6=A0=A1=E5=8C=BA=E3=80=81=E7=8F=AD=E7=BA=A7=E5=92=8C=E5=AD=A6?= =?UTF-8?q?=E7=94=9F=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8C=85?= =?UTF-8?q?=E6=8B=AC=E6=8E=A7=E5=88=B6=E5=99=A8=E3=80=81=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E3=80=81=E6=9C=8D=E5=8A=A1=E5=B1=82=E5=92=8C?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=EF=BC=8C=E5=AE=8C=E5=96=84CRUD=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E5=8F=8A=E7=BB=9F=E8=AE=A1=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=B7=AF=E7=94=B1=E4=BB=A5=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=96=B0=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Admin/AuthController.php | 6 +- .../Admin/Schools/SchoolCampusController.php | 119 ++++ .../Admin/Schools/SchoolClassController.php | 132 +++++ .../Admin/Schools/SchoolController.php | 128 +++++ .../Admin/Students/StudentClassController.php | 159 ++++++ .../Admin/Students/StudentController.php | 273 ++++++++++ .../Admin/System/SystemDictDataController.php | 188 +++++++ .../Admin/System/SystemDictTypeController.php | 136 +++++ .../SystemUserSchoolCampusController.php | 183 +++++++ .../Admin/Teachers/TeacherClassController.php | 202 +++++++ .../Admin/Schools/SchoolCampusRequest.php | 169 ++++++ .../Admin/Schools/SchoolClassRequest.php | 193 +++++++ .../Requests/Admin/Schools/SchoolRequest.php | 193 +++++++ .../Admin/Students/StudentClassRequest.php | 213 ++++++++ .../Admin/Students/StudentRequest.php | 175 ++++++ .../Admin/System/SystemDictDataRequest.php | 424 +++++++++++++++ .../Admin/System/SystemDictTypeRequest.php | 266 +++++++++ .../System/SystemUserSchoolCampusRequest.php | 220 ++++++++ .../Admin/Teachers/TeacherClassRequest.php | 242 +++++++++ app/Models/BaseModel.php | 69 ++- app/Models/Schools/School.php | 118 ++++ app/Models/Schools/SchoolCampus.php | 88 +++ app/Models/Schools/SchoolClass.php | 107 ++++ app/Models/Students/Student.php | 416 ++++++++++++++ app/Models/Students/StudentClass.php | 173 ++++++ app/Models/System/SystemDictData.php | 217 ++++++++ app/Models/System/SystemDictType.php | 165 ++++++ app/Models/System/SystemUserSchoolCampus.php | 85 +++ app/Models/Teachers/TeacherClass.php | 99 ++++ app/Models/User.php | 66 +++ app/Services/Schools/SchoolCampusService.php | 150 +++++ app/Services/Schools/SchoolClassService.php | 208 +++++++ app/Services/Schools/SchoolService.php | 160 ++++++ app/Services/Students/StudentClassService.php | 206 +++++++ app/Services/Students/StudentService.php | 513 ++++++++++++++++++ app/Services/System/SystemDictDataService.php | 421 ++++++++++++++ app/Services/System/SystemDictTypeService.php | 329 +++++++++++ .../System/SystemUserSchoolCampusService.php | 271 +++++++++ app/Services/Teachers/TeacherClassService.php | 262 +++++++++ bootstrap/app.php | 9 + config/cors.php | 34 ++ ...2024_01_01_000001_create_student_table.php | 57 ++ ...5_100000_create_system_dict_type_table.php | 46 ++ ...5_100001_create_system_dict_data_table.php | 58 ++ database/seeders/DatabaseSeeder.php | 6 + database/seeders/StudentSeeder.php | 111 ++++ database/seeders/SystemDictDataSeeder.php | 481 ++++++++++++++++ database/seeders/SystemDictTypeSeeder.php | 168 ++++++ docs/BaseModel系统字段配置使用指南.md | 307 +++++++++++ docs/CRUD代码生成模板.md | 62 ++- docs/字典系统使用指南.md | 459 ++++++++++++++++ docs/学生管理系统使用指南.md | 365 +++++++++++++ docs/模型配置总结.md | 246 +++++++++ routes/admin.php | 3 + routes/admin/schools_route.php | 106 ++++ routes/admin/students_route.php | 96 ++++ routes/admin/system_route.php | 149 ++++- routes/admin/teachers_route.php | 59 ++ 58 files changed, 10540 insertions(+), 26 deletions(-) create mode 100644 app/Http/Controllers/Admin/Schools/SchoolCampusController.php create mode 100644 app/Http/Controllers/Admin/Schools/SchoolClassController.php create mode 100644 app/Http/Controllers/Admin/Schools/SchoolController.php create mode 100644 app/Http/Controllers/Admin/Students/StudentClassController.php create mode 100644 app/Http/Controllers/Admin/Students/StudentController.php create mode 100644 app/Http/Controllers/Admin/System/SystemDictDataController.php create mode 100644 app/Http/Controllers/Admin/System/SystemDictTypeController.php create mode 100644 app/Http/Controllers/Admin/System/SystemUserSchoolCampusController.php create mode 100644 app/Http/Controllers/Admin/Teachers/TeacherClassController.php create mode 100644 app/Http/Requests/Admin/Schools/SchoolCampusRequest.php create mode 100644 app/Http/Requests/Admin/Schools/SchoolClassRequest.php create mode 100644 app/Http/Requests/Admin/Schools/SchoolRequest.php create mode 100644 app/Http/Requests/Admin/Students/StudentClassRequest.php create mode 100644 app/Http/Requests/Admin/Students/StudentRequest.php create mode 100644 app/Http/Requests/Admin/System/SystemDictDataRequest.php create mode 100644 app/Http/Requests/Admin/System/SystemDictTypeRequest.php create mode 100644 app/Http/Requests/Admin/System/SystemUserSchoolCampusRequest.php create mode 100644 app/Http/Requests/Admin/Teachers/TeacherClassRequest.php create mode 100644 app/Models/Schools/School.php create mode 100644 app/Models/Schools/SchoolCampus.php create mode 100644 app/Models/Schools/SchoolClass.php create mode 100644 app/Models/Students/Student.php create mode 100644 app/Models/Students/StudentClass.php create mode 100644 app/Models/System/SystemDictData.php create mode 100644 app/Models/System/SystemDictType.php create mode 100644 app/Models/System/SystemUserSchoolCampus.php create mode 100644 app/Models/Teachers/TeacherClass.php create mode 100644 app/Services/Schools/SchoolCampusService.php create mode 100644 app/Services/Schools/SchoolClassService.php create mode 100644 app/Services/Schools/SchoolService.php create mode 100644 app/Services/Students/StudentClassService.php create mode 100644 app/Services/Students/StudentService.php create mode 100644 app/Services/System/SystemDictDataService.php create mode 100644 app/Services/System/SystemDictTypeService.php create mode 100644 app/Services/System/SystemUserSchoolCampusService.php create mode 100644 app/Services/Teachers/TeacherClassService.php create mode 100644 config/cors.php create mode 100644 database/migrations/2024_01_01_000001_create_student_table.php create mode 100644 database/migrations/2024_01_15_100000_create_system_dict_type_table.php create mode 100644 database/migrations/2024_01_15_100001_create_system_dict_data_table.php create mode 100644 database/seeders/StudentSeeder.php create mode 100644 database/seeders/SystemDictDataSeeder.php create mode 100644 database/seeders/SystemDictTypeSeeder.php create mode 100644 docs/BaseModel系统字段配置使用指南.md create mode 100644 docs/字典系统使用指南.md create mode 100644 docs/学生管理系统使用指南.md create mode 100644 docs/模型配置总结.md create mode 100644 routes/admin/schools_route.php create mode 100644 routes/admin/students_route.php create mode 100644 routes/admin/teachers_route.php diff --git a/app/Http/Controllers/Admin/AuthController.php b/app/Http/Controllers/Admin/AuthController.php index 9470b3c..96f5c9d 100644 --- a/app/Http/Controllers/Admin/AuthController.php +++ b/app/Http/Controllers/Admin/AuthController.php @@ -59,7 +59,7 @@ class AuthController extends BaseController ->first(); if (!$user || !Hash::check($credentials['password'], $user->password)) { - return $this->Field('用户名或密码错误,或账户已被停用', 401); + return $this->Field('用户名或密码错误,或账户已被停用', 404); } // 设备名称,默认为APP @@ -84,6 +84,10 @@ class AuthController extends BaseController 'mobile' => $user->mobile, 'avatar' => $user->avatar, 'dept_id' => $user->dept_id, + 'sex' => $user->sex, + 'status' => $user->status, + 'login_ip' => $user->login_ip, + 'login_date' => $user->login_date, ], 'token' => [ 'access_token' => $token->plainTextToken, diff --git a/app/Http/Controllers/Admin/Schools/SchoolCampusController.php b/app/Http/Controllers/Admin/Schools/SchoolCampusController.php new file mode 100644 index 0000000..3997017 --- /dev/null +++ b/app/Http/Controllers/Admin/Schools/SchoolCampusController.php @@ -0,0 +1,119 @@ +validated(); + $result = $this->schoolCampusService->getList($params); + + return $this->SuccessPage($result->items(), $result->total()); + } + + /** + * 获取简单列表 + */ + public function simpleList(SchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolCampusService->getSimpleList($params['school_id'] ?? null); + return $this->Success($result); + } + + /** + * 获取校区详情 + */ + public function detail(SchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolCampusService->detail($params['id']); + return $this->Success($result); + } + + /** + * 创建校区 + */ + public function create(SchoolCampusRequest $request): JsonResponse + { + $data = $request->validated(); + $result = $this->schoolCampusService->create($data); + return $this->Success($result); + } + + /** + * 更新校区 + */ + public function update(SchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolCampusService->update($params['id'], $params); + return $this->Success($result); + } + + /** + * 删除校区 + */ + public function delete(SchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $this->schoolCampusService->delete($params['id']); + return $this->Success(); + } + + /** + * 批量删除校区 + */ + public function batchDelete(SchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $this->schoolCampusService->batchDelete($params['ids']); + return $this->Success(); + } + + /** + * 获取用户管理的校区列表 + */ + public function userCampuses(SchoolCampusRequest $request): JsonResponse + { + $userId = $request->user()->id; + $result = $this->schoolCampusService->getUserCampuses($userId); + return $this->Success($result); + } + + /** + * 获取校区的班级列表 + */ + public function classes(SchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolCampusService->getCampusClasses($params['id']); + return $this->Success($result); + } + + /** + * 获取校区统计信息 + */ + public function stats(SchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolCampusService->getCampusStats($params['id']); + return $this->Success($result); + } +} diff --git a/app/Http/Controllers/Admin/Schools/SchoolClassController.php b/app/Http/Controllers/Admin/Schools/SchoolClassController.php new file mode 100644 index 0000000..ce3e24b --- /dev/null +++ b/app/Http/Controllers/Admin/Schools/SchoolClassController.php @@ -0,0 +1,132 @@ +validated(); + $result = $this->schoolClassService->getList($params); + + return $this->SuccessPage($result->items(), $result->total()); + } + + /** + * 获取简单列表 + */ + public function simpleList(SchoolClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolClassService->getSimpleList( + $params['school_id'] ?? null, + $params['campus_id'] ?? null + ); + return $this->Success($result); + } + + /** + * 获取班级详情 + */ + public function detail(SchoolClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolClassService->detail($params['id']); + return $this->Success($result); + } + + /** + * 创建班级 + */ + public function create(SchoolClassRequest $request): JsonResponse + { + $data = $request->validated(); + $result = $this->schoolClassService->create($data); + return $this->Success($result); + } + + /** + * 更新班级 + */ + public function update(SchoolClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolClassService->update($params['id'], $params); + return $this->Success($result); + } + + /** + * 删除班级 + */ + public function delete(SchoolClassRequest $request): JsonResponse + { + $params = $request->validated(); + $this->schoolClassService->delete($params['id']); + return $this->Success(); + } + + /** + * 批量删除班级 + */ + public function batchDelete(SchoolClassRequest $request): JsonResponse + { + $params = $request->validated(); + $this->schoolClassService->batchDelete($params['ids']); + return $this->Success(); + } + + /** + * 获取用户管理的班级列表 + */ + public function userClasses(SchoolClassRequest $request): JsonResponse + { + $userId = $request->user()->id; + $result = $this->schoolClassService->getUserClasses($userId); + return $this->Success($result); + } + + /** + * 获取班级的学生列表 + */ + public function students(SchoolClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolClassService->getClassStudents($params['id']); + return $this->Success($result); + } + + /** + * 获取班级的老师列表 + */ + public function teachers(SchoolClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolClassService->getClassTeachers($params['id']); + return $this->Success($result); + } + + /** + * 获取班级统计信息 + */ + public function stats(SchoolClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolClassService->getClassStats($params['id']); + return $this->Success($result); + } +} diff --git a/app/Http/Controllers/Admin/Schools/SchoolController.php b/app/Http/Controllers/Admin/Schools/SchoolController.php new file mode 100644 index 0000000..349ea46 --- /dev/null +++ b/app/Http/Controllers/Admin/Schools/SchoolController.php @@ -0,0 +1,128 @@ +validated(); + $result = $this->schoolService->getList($params); + + return $this->SuccessPage($result->items(), $result->total()); + } + + /** + * 获取简单列表 + */ + public function simpleList(): JsonResponse + { + $result = $this->schoolService->getSimpleList(); + return $this->Success($result); + } + + /** + * 获取学校详情 + */ + public function detail(SchoolRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolService->detail($params['id']); + return $this->Success($result); + } + + /** + * 创建学校 + */ + public function create(SchoolRequest $request): JsonResponse + { + $data = $request->validated(); + $result = $this->schoolService->create($data); + return $this->Success($result); + } + + /** + * 更新学校 + */ + public function update(SchoolRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolService->update($params['id'], $params); + return $this->Success($result); + } + + /** + * 删除学校 + */ + public function delete(SchoolRequest $request): JsonResponse + { + $params = $request->validated(); + $this->schoolService->delete($params['id']); + return $this->Success(); + } + + /** + * 批量删除学校 + */ + public function batchDelete(SchoolRequest $request): JsonResponse + { + $params = $request->validated(); + $this->schoolService->batchDelete($params['ids']); + return $this->Success(); + } + + /** + * 获取用户管理的学校列表 + */ + public function userSchools(SchoolRequest $request): JsonResponse + { + $userId = $request->user()->id; + $result = $this->schoolService->getUserSchools($userId); + return $this->Success($result); + } + + /** + * 获取学校的校区列表 + */ + public function campuses(SchoolRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolService->getSchoolCampuses($params['id']); + return $this->Success($result); + } + + /** + * 获取学校的班级列表 + */ + public function classes(SchoolRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolService->getSchoolClasses($params['id'], $params['campus_id'] ?? null); + return $this->Success($result); + } + + /** + * 获取学校统计信息 + */ + public function stats(SchoolRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->schoolService->getSchoolStats($params['id']); + return $this->Success($result); + } +} diff --git a/app/Http/Controllers/Admin/Students/StudentClassController.php b/app/Http/Controllers/Admin/Students/StudentClassController.php new file mode 100644 index 0000000..95e14c6 --- /dev/null +++ b/app/Http/Controllers/Admin/Students/StudentClassController.php @@ -0,0 +1,159 @@ +validated(); + $result = $this->studentClassService->getList($params); + + return $this->SuccessPage($result->items(), $result->total()); + } + + /** + * 获取学生班级关联详情 + */ + public function detail(StudentClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->studentClassService->detail($params['id']); + return $this->Success($result); + } + + /** + * 创建学生班级关联 + */ + public function create(StudentClassRequest $request): JsonResponse + { + $data = $request->validated(); + $result = $this->studentClassService->create($data); + return $this->Success($result); + } + + /** + * 更新学生班级关联 + */ + public function update(StudentClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->studentClassService->update($params['id'], $params); + return $this->Success($result); + } + + /** + * 删除学生班级关联 + */ + public function delete(StudentClassRequest $request): JsonResponse + { + $params = $request->validated(); + $this->studentClassService->delete($params['id']); + return $this->Success(); + } + + /** + * 批量删除学生班级关联 + */ + public function batchDelete(StudentClassRequest $request): JsonResponse + { + $params = $request->validated(); + $this->studentClassService->batchDelete($params['ids']); + return $this->Success(); + } + + /** + * 获取学生的班级列表 + */ + public function studentClasses(StudentClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->studentClassService->getStudentClasses($params['student_id']); + return $this->Success($result); + } + + /** + * 获取班级的学生列表 + */ + public function classStudents(StudentClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->studentClassService->getClassStudents($params['class_id']); + return $this->Success($result); + } + + /** + * 获取学校的学生列表 + */ + public function schoolStudents(StudentClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->studentClassService->getSchoolStudents($params['school_id']); + return $this->Success($result); + } + + /** + * 批量分配学生到班级 + */ + public function batchAssign(StudentClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->studentClassService->batchAssignStudentsToClass( + $params['student_ids'], + $params['class_id'] + ); + return $this->Success($result); + } + + /** + * 批量移除学生班级关联 + */ + public function batchRemove(StudentClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->studentClassService->batchRemoveStudentsFromClass( + $params['student_ids'], + $params['class_id'] + ); + return $this->Success(['removed_count' => $result]); + } + + /** + * 转移学生到新班级 + */ + public function transfer(StudentClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->studentClassService->transferStudentToClass( + $params['student_id'], + $params['old_class_id'], + $params['new_class_id'] + ); + return $this->Success(['transferred' => $result]); + } + + /** + * 获取学生班级统计信息 + */ + public function stats(StudentClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->studentClassService->getStudentClassStats($params['student_id']); + return $this->Success($result); + } +} diff --git a/app/Http/Controllers/Admin/Students/StudentController.php b/app/Http/Controllers/Admin/Students/StudentController.php new file mode 100644 index 0000000..5806b6f --- /dev/null +++ b/app/Http/Controllers/Admin/Students/StudentController.php @@ -0,0 +1,273 @@ +studentService = $studentService; + } + + /** + * 获取学生列表 + * @param Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function index(Request $request) + { + $params = $request->all(); + $result = $this->studentService->getStudentList($params); + + return $this->success($result, '获取学生列表成功'); + } + + /** + * 创建学生 + * @param StudentRequest $request + * @return \Illuminate\Http\JsonResponse + */ + public function store(StudentRequest $request) + { + $data = $request->validated(); + $result = $this->studentService->createStudent($data); + + return $this->success($result, '创建学生成功'); + } + + /** + * 获取学生详情 + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function show(int $id) + { + $result = $this->studentService->getStudentDetail($id); + + return $this->success($result, '获取学生详情成功'); + } + + /** + * 更新学生 + * @param StudentRequest $request + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function update(StudentRequest $request, int $id) + { + $data = $request->validated(); + $result = $this->studentService->updateStudent($id, $data); + + return $this->success($result, '更新学生成功'); + } + + /** + * 删除学生 + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function destroy(int $id) + { + $this->studentService->deleteStudent($id); + + return $this->success([], '删除学生成功'); + } + + /** + * 重置学生密码 + * @param Request $request + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function resetPassword(Request $request, int $id) + { + $request->validate([ + 'password' => 'required|string|min:6|max:20' + ], [ + 'password.required' => '密码不能为空', + 'password.min' => '密码长度至少6位', + 'password.max' => '密码长度最多20位' + ]); + + $this->studentService->resetPassword($id, $request->password); + + return $this->success([], '重置密码成功'); + } + + /** + * 更新学生状态 + * @param Request $request + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function updateStatus(Request $request, int $id) + { + $request->validate([ + 'status' => 'required|integer|in:0,1,2,3' + ], [ + 'status.required' => '状态不能为空', + 'status.in' => '状态值无效' + ]); + + $result = $this->studentService->updateStatus($id, $request->status); + + return $this->success($result, '更新状态成功'); + } + + /** + * 获取学生班级历史 + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function classHistory(int $id) + { + $result = $this->studentService->getStudentClassHistory($id); + + return $this->success($result, '获取班级历史成功'); + } + + /** + * 分配学生到班级 + * @param Request $request + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function assignToClass(Request $request, int $id) + { + $request->validate([ + 'class_id' => 'required|integer|exists:school_class,id', + 'join_time' => 'nullable|date', + 'remark' => 'nullable|string|max:500' + ], [ + 'class_id.required' => '班级ID不能为空', + 'class_id.exists' => '班级不存在', + 'join_time.date' => '入班时间格式错误', + 'remark.max' => '备注最多500字符' + ]); + + $result = $this->studentService->assignToClass($id, $request->class_id, $request->only(['join_time', 'remark'])); + + return $this->success($result, '分配班级成功'); + } + + /** + * 从班级移除学生 + * @param Request $request + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function removeFromClass(Request $request, int $id) + { + $request->validate([ + 'class_id' => 'required|integer|exists:school_class,id', + 'status' => 'nullable|integer|in:2,3,4', + 'leave_time' => 'nullable|date', + 'remark' => 'nullable|string|max:500' + ], [ + 'class_id.required' => '班级ID不能为空', + 'class_id.exists' => '班级不存在', + 'status.in' => '状态值无效', + 'leave_time.date' => '离班时间格式错误', + 'remark.max' => '备注最多500字符' + ]); + + $this->studentService->removeFromClass($id, $request->class_id, $request->only(['status', 'leave_time', 'remark'])); + + return $this->success([], '移除班级成功'); + } + + /** + * 获取家长的子女列表 + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function getChildren(int $id) + { + $result = $this->studentService->getParentChildren($id); + + return $this->success($result, '获取子女列表成功'); + } + + /** + * 绑定家长和子女关系 + * @param Request $request + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function bindChild(Request $request, int $id) + { + $request->validate([ + 'child_id' => 'required|integer|exists:student,id' + ], [ + 'child_id.required' => '子女ID不能为空', + 'child_id.exists' => '子女不存在' + ]); + + $this->studentService->bindParentChild($id, $request->child_id); + + return $this->success([], '绑定子女成功'); + } + + /** + * 解除家长和子女关系 + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function unbindChild(int $id) + { + $this->studentService->unbindParentChild($id); + + return $this->success([], '解除绑定成功'); + } + + /** + * 获取学生统计信息 + * @return \Illuminate\Http\JsonResponse + */ + public function statistics() + { + $result = $this->studentService->getStudentStatistics(); + + return $this->success($result, '获取统计信息成功'); + } + + /** + * 批量导入学生 + * @param Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function batchImport(Request $request) + { + $request->validate([ + 'file' => 'required|file|mimes:xlsx,xls,csv|max:10240' + ], [ + 'file.required' => '请选择文件', + 'file.mimes' => '文件格式不正确', + 'file.max' => '文件大小不能超过10M' + ]); + + // TODO: 实现批量导入逻辑 + return $this->success([], '批量导入功能待实现'); + } + + /** + * 导出学生数据 + * @param Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function export(Request $request) + { + // TODO: 实现导出逻辑 + return $this->success([], '导出功能待实现'); + } +} diff --git a/app/Http/Controllers/Admin/System/SystemDictDataController.php b/app/Http/Controllers/Admin/System/SystemDictDataController.php new file mode 100644 index 0000000..c2b10af --- /dev/null +++ b/app/Http/Controllers/Admin/System/SystemDictDataController.php @@ -0,0 +1,188 @@ +validated(); + $result = $this->systemDictDataService->getList($params); + + return $this->SuccessPage($result->items(), $result->total()); + } + + /** + * 获取简单列表 + */ + public function simpleList(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemDictDataService->getByType($params['dict_type'], true); + return $this->Success($result); + } + + /** + * 获取字典数据详情 + */ + public function detail(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemDictDataService->getById($params['id']); + return $this->Success($result); + } + + /** + * 创建字典数据 + */ + public function create(SystemDictDataRequest $request): JsonResponse + { + $data = $request->validated(); + $result = $this->systemDictDataService->create($data); + return $this->Success($result); + } + + /** + * 更新字典数据 + */ + public function update(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemDictDataService->update($params['id'], $params); + return $this->Success($result); + } + + /** + * 删除字典数据 + */ + public function delete(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $this->systemDictDataService->delete($params['id']); + return $this->Success(); + } + + /** + * 批量删除字典数据 + */ + public function batchDelete(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $this->systemDictDataService->batchDelete($params['ids']); + return $this->Success(); + } + + /** + * 更新字典数据状态 + */ + public function updateStatus(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $this->systemDictDataService->updateStatus($params['id'], $params['status']); + return $this->Success(); + } + + /** + * 批量更新状态 + */ + public function batchUpdateStatus(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $this->systemDictDataService->batchUpdateStatus($params['ids'], $params['status']); + return $this->Success(); + } + + /** + * 更新排序 + */ + public function updateSort(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $this->systemDictDataService->updateSort($params['id'], $params['sort']); + return $this->Success(); + } + + /** + * 批量更新排序 + */ + public function batchUpdateSort(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $this->systemDictDataService->batchUpdateSort($params['sort_data']); + return $this->Success(); + } + + /** + * 根据字典类型获取数据列表 + */ + public function getByType(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemDictDataService->getByType($params['dict_type'], $params['only_active'] ?? true); + return $this->Success($result); + } + + /** + * 根据字典类型获取选项列表 + */ + public function options(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemDictDataService->getOptions($params['dict_type'], $params['only_active'] ?? true); + return $this->Success($result); + } + + /** + * 根据字典类型和值获取标签 + */ + public function getLabel(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemDictDataService->getLabel($params['dict_type'], $params['value']); + return $this->Success($result); + } + + /** + * 根据字典类型和值获取字典数据 + */ + public function getByTypeAndValue(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemDictDataService->getByTypeAndValue($params['dict_type'], $params['value']); + return $this->Success($result); + } + + /** + * 获取字典数据统计信息 + */ + public function statistics(SystemDictDataRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemDictDataService->getStatistics($params['dict_type'] ?? null); + return $this->Success($result); + } + + /** + * 获取分组统计信息 + */ + public function groupStatistics(): JsonResponse + { + $result = $this->systemDictDataService->getGroupStatistics(); + return $this->Success($result); + } +} diff --git a/app/Http/Controllers/Admin/System/SystemDictTypeController.php b/app/Http/Controllers/Admin/System/SystemDictTypeController.php new file mode 100644 index 0000000..54b6516 --- /dev/null +++ b/app/Http/Controllers/Admin/System/SystemDictTypeController.php @@ -0,0 +1,136 @@ +validated(); + $result = $this->systemDictTypeService->getList($params); + + return $this->SuccessPage($result->items(), $result->total()); + } + + /** + * 获取简单列表 + */ + public function simpleList(): JsonResponse + { + $result = $this->systemDictTypeService->getSimpleList(); + return $this->Success($result); + } + + /** + * 获取字典类型详情 + */ + public function detail(SystemDictTypeRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemDictTypeService->getById($params['id']); + return $this->Success($result); + } + + /** + * 创建字典类型 + */ + public function create(SystemDictTypeRequest $request): JsonResponse + { + $data = $request->validated(); + $result = $this->systemDictTypeService->create($data); + return $this->Success($result); + } + + /** + * 更新字典类型 + */ + public function update(SystemDictTypeRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemDictTypeService->update($params['id'], $params); + return $this->Success($result); + } + + /** + * 删除字典类型 + */ + public function delete(SystemDictTypeRequest $request): JsonResponse + { + $params = $request->validated(); + $this->systemDictTypeService->delete($params['id']); + return $this->Success(); + } + + /** + * 批量删除字典类型 + */ + public function batchDelete(SystemDictTypeRequest $request): JsonResponse + { + $params = $request->validated(); + $this->systemDictTypeService->batchDelete($params['ids']); + return $this->Success(); + } + + /** + * 更新字典类型状态 + */ + public function updateStatus(SystemDictTypeRequest $request): JsonResponse + { + $params = $request->validated(); + $this->systemDictTypeService->updateStatus($params['id'], $params['status']); + return $this->Success(); + } + + /** + * 批量更新状态 + */ + public function batchUpdateStatus(SystemDictTypeRequest $request): JsonResponse + { + $params = $request->validated(); + $this->systemDictTypeService->batchUpdateStatus($params['ids'], $params['status']); + return $this->Success(); + } + + /** + * 获取字典类型统计信息 + */ + public function statistics(): JsonResponse + { + $result = $this->systemDictTypeService->getStatistics(); + return $this->Success($result); + } + + /** + * 获取字典类型选项列表 + */ + public function options(): JsonResponse + { + $result = $this->systemDictTypeService->getOptions(); + return $this->Success($result); + } + + /** + * 根据类型获取字典类型详情 + */ + public function getByType(SystemDictTypeRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemDictTypeService->getByType($params['type']); + return $this->Success($result); + } +} diff --git a/app/Http/Controllers/Admin/System/SystemUserSchoolCampusController.php b/app/Http/Controllers/Admin/System/SystemUserSchoolCampusController.php new file mode 100644 index 0000000..e1f2425 --- /dev/null +++ b/app/Http/Controllers/Admin/System/SystemUserSchoolCampusController.php @@ -0,0 +1,183 @@ +validated(); + $result = $this->systemUserSchoolCampusService->getList($params); + + return $this->SuccessPage($result->items(), $result->total()); + } + + /** + * 获取用户校区关联详情 + */ + public function detail(SystemUserSchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemUserSchoolCampusService->detail($params['id']); + return $this->Success($result); + } + + /** + * 创建用户校区关联 + */ + public function create(SystemUserSchoolCampusRequest $request): JsonResponse + { + $data = $request->validated(); + $result = $this->systemUserSchoolCampusService->create($data); + return $this->Success($result); + } + + /** + * 更新用户校区关联 + */ + public function update(SystemUserSchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemUserSchoolCampusService->update($params['id'], $params); + return $this->Success($result); + } + + /** + * 删除用户校区关联 + */ + public function delete(SystemUserSchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $this->systemUserSchoolCampusService->delete($params['id']); + return $this->Success(); + } + + /** + * 批量删除用户校区关联 + */ + public function batchDelete(SystemUserSchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $this->systemUserSchoolCampusService->batchDelete($params['ids']); + return $this->Success(); + } + + /** + * 获取用户的学校校区列表 + */ + public function userSchoolCampuses(SystemUserSchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemUserSchoolCampusService->getUserSchoolCampuses($params['userid']); + return $this->Success($result); + } + + /** + * 获取当前用户的学校校区列表 + */ + public function mySchoolCampuses(SystemUserSchoolCampusRequest $request): JsonResponse + { + $userId = $request->user()->id; + $result = $this->systemUserSchoolCampusService->getUserSchoolCampuses($userId); + return $this->Success($result); + } + + /** + * 获取学校的用户列表 + */ + public function schoolUsers(SystemUserSchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemUserSchoolCampusService->getSchoolUsers($params['schoolid']); + return $this->Success($result); + } + + /** + * 获取校区的用户列表 + */ + public function campusUsers(SystemUserSchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemUserSchoolCampusService->getCampusUsers($params['campusid']); + return $this->Success($result); + } + + /** + * 批量分配用户到学校校区 + */ + public function batchAssign(SystemUserSchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemUserSchoolCampusService->batchAssignUsersToSchoolCampus( + $params['userids'], + $params['schoolid'], + $params['campusid'] ?? null + ); + return $this->Success($result); + } + + /** + * 批量移除用户学校校区关联 + */ + public function batchRemove(SystemUserSchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemUserSchoolCampusService->batchRemoveUsersFromSchoolCampus( + $params['userids'], + $params['schoolid'], + $params['campusid'] ?? null + ); + return $this->Success(['removed_count' => $result]); + } + + /** + * 转移用户到新的学校校区 + */ + public function transfer(SystemUserSchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemUserSchoolCampusService->transferUserToSchoolCampus( + $params['userid'], + $params['old_schoolid'], + $params['new_schoolid'], + $params['old_campusid'] ?? null, + $params['new_campusid'] ?? null + ); + return $this->Success(['transferred' => $result]); + } + + /** + * 获取用户校区统计信息 + */ + public function stats(SystemUserSchoolCampusRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->systemUserSchoolCampusService->getUserSchoolCampusStats($params['userid']); + return $this->Success($result); + } + + /** + * 获取当前用户校区统计信息 + */ + public function myStats(SystemUserSchoolCampusRequest $request): JsonResponse + { + $userId = $request->user()->id; + $result = $this->systemUserSchoolCampusService->getUserSchoolCampusStats($userId); + return $this->Success($result); + } +} diff --git a/app/Http/Controllers/Admin/Teachers/TeacherClassController.php b/app/Http/Controllers/Admin/Teachers/TeacherClassController.php new file mode 100644 index 0000000..0374106 --- /dev/null +++ b/app/Http/Controllers/Admin/Teachers/TeacherClassController.php @@ -0,0 +1,202 @@ +validated(); + $result = $this->teacherClassService->getList($params); + + return $this->SuccessPage($result->items(), $result->total()); + } + + /** + * 获取老师班级关联详情 + */ + public function detail(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->teacherClassService->detail($params['id']); + return $this->Success($result); + } + + /** + * 创建老师班级关联 + */ + public function create(TeacherClassRequest $request): JsonResponse + { + $data = $request->validated(); + $result = $this->teacherClassService->create($data); + return $this->Success($result); + } + + /** + * 更新老师班级关联 + */ + public function update(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->teacherClassService->update($params['id'], $params); + return $this->Success($result); + } + + /** + * 删除老师班级关联 + */ + public function delete(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $this->teacherClassService->delete($params['id']); + return $this->Success(); + } + + /** + * 批量删除老师班级关联 + */ + public function batchDelete(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $this->teacherClassService->batchDelete($params['ids']); + return $this->Success(); + } + + /** + * 获取老师的班级列表 + */ + public function teacherClasses(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->teacherClassService->getTeacherClasses($params['teacher_id']); + return $this->Success($result); + } + + /** + * 获取当前用户的班级列表 + */ + public function myClasses(TeacherClassRequest $request): JsonResponse + { + $teacherId = $request->user()->id; + $result = $this->teacherClassService->getTeacherClasses($teacherId); + return $this->Success($result); + } + + /** + * 获取班级的老师列表 + */ + public function classTeachers(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->teacherClassService->getClassTeachers($params['class_id']); + return $this->Success($result); + } + + /** + * 获取学校的老师列表 + */ + public function schoolTeachers(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->teacherClassService->getSchoolTeachers($params['school_id']); + return $this->Success($result); + } + + /** + * 获取班主任列表 + */ + public function headmans(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->teacherClassService->getHeadmans( + $params['school_id'] ?? null, + $params['grade_id'] ?? null + ); + return $this->Success($result); + } + + /** + * 批量分配老师到班级 + */ + public function batchAssign(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->teacherClassService->batchAssignTeachersToClass( + $params['teacher_ids'], + $params['class_id'], + $params['grade_id'] ?? null + ); + return $this->Success($result); + } + + /** + * 批量移除老师班级关联 + */ + public function batchRemove(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->teacherClassService->batchRemoveTeachersFromClass( + $params['teacher_ids'], + $params['class_id'] + ); + return $this->Success(['removed_count' => $result]); + } + + /** + * 设置班主任 + */ + public function setHeadman(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->teacherClassService->setHeadman( + $params['teacher_id'], + $params['class_id'] + ); + return $this->Success(['success' => $result]); + } + + /** + * 取消班主任 + */ + public function removeHeadman(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->teacherClassService->removeHeadman($params['class_id']); + return $this->Success(['success' => $result]); + } + + /** + * 获取老师班级统计信息 + */ + public function stats(TeacherClassRequest $request): JsonResponse + { + $params = $request->validated(); + $result = $this->teacherClassService->getTeacherClassStats($params['teacher_id']); + return $this->Success($result); + } + + /** + * 获取当前用户班级统计信息 + */ + public function myStats(TeacherClassRequest $request): JsonResponse + { + $teacherId = $request->user()->id; + $result = $this->teacherClassService->getTeacherClassStats($teacherId); + return $this->Success($result); + } +} diff --git a/app/Http/Requests/Admin/Schools/SchoolCampusRequest.php b/app/Http/Requests/Admin/Schools/SchoolCampusRequest.php new file mode 100644 index 0000000..b465c94 --- /dev/null +++ b/app/Http/Requests/Admin/Schools/SchoolCampusRequest.php @@ -0,0 +1,169 @@ +route()->getActionMethod(); + + return match($action) { + 'list' => $this->listRules(), + 'detail' => $this->detailRules(), + 'create' => $this->createRules(), + 'update' => $this->updateRules(), + 'delete' => $this->deleteRules(), + 'batchDelete' => $this->batchDeleteRules(), + 'simpleList' => $this->simpleListRules(), + 'classes' => $this->classesRules(), + 'stats' => $this->statsRules(), + 'userCampuses' => [], + default => [] + }; + } + + /** + * 获取分页验证规则 + */ + private function getPaginationRules(): array + { + return [ + 'page' => ['sometimes', 'integer', 'min:1'], + 'page_size' => ['sometimes', 'integer', 'min:1', 'max:100'], + ]; + } + + /** + * 列表查询验证规则 + */ + private function listRules(): array + { + return array_merge($this->getPaginationRules(), [ + 'keyword' => ['sometimes', 'string', 'max:100'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'school_id' => ['sometimes', 'integer', 'exists:school,id'], + ]); + } + + /** + * 简单列表验证规则 + */ + private function simpleListRules(): array + { + return [ + 'school_id' => ['sometimes', 'integer', 'exists:school,id'], + ]; + } + + /** + * 详情查询验证规则 + */ + private function detailRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school_campus,id'], + ]; + } + + /** + * 创建验证规则 + */ + private function createRules(): array + { + return [ + 'name' => ['required', 'string', 'max:300'], + 'alias' => ['sometimes', 'string', 'max:300'], + 'school_id' => ['required', 'integer', 'exists:school,id'], + 'code' => ['sometimes', 'string', 'max:16'], + 'self_support' => ['sometimes', 'boolean'], + 'is_open_user_login' => ['sometimes', 'integer', 'in:0,1'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + ]; + } + + /** + * 更新验证规则 + */ + private function updateRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school_campus,id'], + 'name' => ['required', 'string', 'max:300'], + 'alias' => ['sometimes', 'string', 'max:300'], + 'school_id' => ['required', 'integer', 'exists:school,id'], + 'code' => ['sometimes', 'string', 'max:16'], + 'self_support' => ['sometimes', 'boolean'], + 'is_open_user_login' => ['sometimes', 'integer', 'in:0,1'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + ]; + } + + /** + * 删除验证规则 + */ + private function deleteRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school_campus,id'], + ]; + } + + /** + * 批量删除验证规则 + */ + private function batchDeleteRules(): array + { + return [ + 'ids' => ['required', 'array', 'min:1'], + 'ids.*' => ['integer', 'exists:school_campus,id'], + ]; + } + + /** + * 获取班级列表验证规则 + */ + private function classesRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school_campus,id'], + ]; + } + + /** + * 获取统计信息验证规则 + */ + private function statsRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school_campus,id'], + ]; + } + + /** + * 获取特定的验证错误消息 + */ + protected function getSpecificMessages(): array + { + return [ + 'name.required' => '校区名称不能为空', + 'name.max' => '校区名称不能超过300个字符', + 'alias.max' => '校区别名不能超过300个字符', + 'school_id.required' => '所属学校不能为空', + 'school_id.exists' => '指定的学校不存在', + 'code.max' => '校区编码不能超过16个字符', + 'status.in' => '校区状态值无效', + 'is_open_user_login.in' => '开放用户登录状态值无效', + 'ids.required' => '请选择要删除的校区', + 'ids.min' => '至少选择一条记录进行删除', + ]; + } +} diff --git a/app/Http/Requests/Admin/Schools/SchoolClassRequest.php b/app/Http/Requests/Admin/Schools/SchoolClassRequest.php new file mode 100644 index 0000000..5c61a74 --- /dev/null +++ b/app/Http/Requests/Admin/Schools/SchoolClassRequest.php @@ -0,0 +1,193 @@ +route()->getActionMethod(); + + return match($action) { + 'list' => $this->listRules(), + 'detail' => $this->detailRules(), + 'create' => $this->createRules(), + 'update' => $this->updateRules(), + 'delete' => $this->deleteRules(), + 'batchDelete' => $this->batchDeleteRules(), + 'simpleList' => $this->simpleListRules(), + 'students' => $this->studentsRules(), + 'teachers' => $this->teachersRules(), + 'stats' => $this->statsRules(), + 'userClasses' => [], + default => [] + }; + } + + /** + * 获取分页验证规则 + */ + private function getPaginationRules(): array + { + return [ + 'page' => ['sometimes', 'integer', 'min:1'], + 'page_size' => ['sometimes', 'integer', 'min:1', 'max:100'], + ]; + } + + /** + * 列表查询验证规则 + */ + private function listRules(): array + { + return array_merge($this->getPaginationRules(), [ + 'keyword' => ['sometimes', 'string', 'max:100'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'school_id' => ['sometimes', 'integer', 'exists:school,id'], + 'campus_id' => ['sometimes', 'integer', 'exists:school_campus,id'], + 'grade_id' => ['sometimes', 'integer'], + 'type' => ['sometimes', 'integer'], + ]); + } + + /** + * 简单列表验证规则 + */ + private function simpleListRules(): array + { + return [ + 'school_id' => ['sometimes', 'integer', 'exists:school,id'], + 'campus_id' => ['sometimes', 'integer', 'exists:school_campus,id'], + ]; + } + + /** + * 详情查询验证规则 + */ + private function detailRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 创建验证规则 + */ + private function createRules(): array + { + return [ + 'name' => ['required', 'string', 'max:100'], + 'school_id' => ['required', 'integer', 'exists:school,id'], + 'campus_id' => ['required', 'integer', 'exists:school_campus,id'], + 'grade_id' => ['required', 'integer'], + 'type' => ['sometimes', 'integer'], + 'number' => ['sometimes', 'integer', 'min:0'], + 'code' => ['sometimes', 'string', 'max:16'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'level' => ['sometimes', 'integer'], + 'is_open_user_login' => ['sometimes', 'integer', 'in:0,1'], + ]; + } + + /** + * 更新验证规则 + */ + private function updateRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school_class,id'], + 'name' => ['required', 'string', 'max:100'], + 'school_id' => ['required', 'integer', 'exists:school,id'], + 'campus_id' => ['required', 'integer', 'exists:school_campus,id'], + 'grade_id' => ['required', 'integer'], + 'type' => ['sometimes', 'integer'], + 'number' => ['sometimes', 'integer', 'min:0'], + 'code' => ['sometimes', 'string', 'max:16'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'level' => ['sometimes', 'integer'], + 'is_open_user_login' => ['sometimes', 'integer', 'in:0,1'], + ]; + } + + /** + * 删除验证规则 + */ + private function deleteRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 批量删除验证规则 + */ + private function batchDeleteRules(): array + { + return [ + 'ids' => ['required', 'array', 'min:1'], + 'ids.*' => ['integer', 'exists:school_class,id'], + ]; + } + + /** + * 获取学生列表验证规则 + */ + private function studentsRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 获取老师列表验证规则 + */ + private function teachersRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 获取统计信息验证规则 + */ + private function statsRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 获取特定的验证错误消息 + */ + protected function getSpecificMessages(): array + { + return [ + 'name.required' => '班级名称不能为空', + 'name.max' => '班级名称不能超过100个字符', + 'school_id.required' => '所属学校不能为空', + 'school_id.exists' => '指定的学校不存在', + 'campus_id.required' => '所属校区不能为空', + 'campus_id.exists' => '指定的校区不存在', + 'grade_id.required' => '年级不能为空', + 'number.min' => '班级编号必须大于等于0', + 'code.max' => '班级编码不能超过16个字符', + 'status.in' => '班级状态值无效', + 'is_open_user_login.in' => '开放用户登录状态值无效', + 'ids.required' => '请选择要删除的班级', + 'ids.min' => '至少选择一条记录进行删除', + ]; + } +} diff --git a/app/Http/Requests/Admin/Schools/SchoolRequest.php b/app/Http/Requests/Admin/Schools/SchoolRequest.php new file mode 100644 index 0000000..78e9431 --- /dev/null +++ b/app/Http/Requests/Admin/Schools/SchoolRequest.php @@ -0,0 +1,193 @@ +route()->getActionMethod(); + + return match($action) { + 'list' => $this->listRules(), + 'detail' => $this->detailRules(), + 'create' => $this->createRules(), + 'update' => $this->updateRules(), + 'delete' => $this->deleteRules(), + 'batchDelete' => $this->batchDeleteRules(), + 'campuses' => $this->campusesRules(), + 'classes' => $this->classesRules(), + 'stats' => $this->statsRules(), + 'simpleList', 'userSchools' => [], + default => [] + }; + } + + /** + * 获取分页验证规则 + */ + private function getPaginationRules(): array + { + return [ + 'page' => ['sometimes', 'integer', 'min:1'], + 'page_size' => ['sometimes', 'integer', 'min:1', 'max:100'], + ]; + } + + /** + * 列表查询验证规则 + */ + private function listRules(): array + { + return array_merge($this->getPaginationRules(), [ + 'keyword' => ['sometimes', 'string', 'max:100'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'type' => ['sometimes', 'integer'], + 'province_id' => ['sometimes', 'integer'], + 'city_id' => ['sometimes', 'integer'], + 'district_id' => ['sometimes', 'integer'], + 'grade_period' => ['sometimes', 'string', 'max:16'], + ]); + } + + /** + * 详情查询验证规则 + */ + private function detailRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school,id'], + ]; + } + + /** + * 创建验证规则 + */ + private function createRules(): array + { + return [ + 'name' => ['required', 'string', 'max:100'], + 'alias' => ['sometimes', 'string', 'max:255'], + 'type' => ['sometimes', 'integer'], + 'province_id' => ['sometimes', 'integer'], + 'city_id' => ['sometimes', 'integer'], + 'district_id' => ['sometimes', 'integer'], + 'grade_period' => ['sometimes', 'string', 'max:16'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'code' => ['sometimes', 'string', 'max:10'], + 'sno_automatic' => ['sometimes', 'integer'], + 'show_order' => ['sometimes', 'integer', 'min:0'], + 'is_open_user_login' => ['sometimes', 'integer', 'in:0,1'], + 'max_sno' => ['sometimes', 'integer', 'min:1'], + 'default_password' => ['sometimes', 'string', 'max:50'], + 'school_district_id' => ['sometimes', 'integer'], + ]; + } + + /** + * 更新验证规则 + */ + private function updateRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school,id'], + 'name' => ['required', 'string', 'max:100'], + 'alias' => ['sometimes', 'string', 'max:255'], + 'type' => ['sometimes', 'integer'], + 'province_id' => ['sometimes', 'integer'], + 'city_id' => ['sometimes', 'integer'], + 'district_id' => ['sometimes', 'integer'], + 'grade_period' => ['sometimes', 'string', 'max:16'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'code' => ['sometimes', 'string', 'max:10'], + 'sno_automatic' => ['sometimes', 'integer'], + 'show_order' => ['sometimes', 'integer', 'min:0'], + 'is_open_user_login' => ['sometimes', 'integer', 'in:0,1'], + 'max_sno' => ['sometimes', 'integer', 'min:1'], + 'default_password' => ['sometimes', 'string', 'max:50'], + 'school_district_id' => ['sometimes', 'integer'], + ]; + } + + /** + * 删除验证规则 + */ + private function deleteRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school,id'], + ]; + } + + /** + * 批量删除验证规则 + */ + private function batchDeleteRules(): array + { + return [ + 'ids' => ['required', 'array', 'min:1'], + 'ids.*' => ['integer', 'exists:school,id'], + ]; + } + + /** + * 获取校区列表验证规则 + */ + private function campusesRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school,id'], + ]; + } + + /** + * 获取班级列表验证规则 + */ + private function classesRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school,id'], + 'campus_id' => ['sometimes', 'integer', 'exists:school_campus,id'], + ]; + } + + /** + * 获取统计信息验证规则 + */ + private function statsRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:school,id'], + ]; + } + + /** + * 获取特定的验证错误消息 + */ + protected function getSpecificMessages(): array + { + return [ + 'name.required' => '学校名称不能为空', + 'name.max' => '学校名称不能超过100个字符', + 'alias.max' => '学校别名不能超过255个字符', + 'code.max' => '学校编码不能超过10个字符', + 'status.in' => '学校状态值无效', + 'is_open_user_login.in' => '开放用户登录状态值无效', + 'max_sno.min' => '最大学号必须大于0', + 'show_order.min' => '显示顺序必须大于等于0', + 'grade_period.max' => '学段不能超过16个字符', + 'default_password.max' => '默认密码不能超过50个字符', + 'ids.required' => '请选择要删除的学校', + 'ids.min' => '至少选择一条记录进行删除', + 'campus_id.exists' => '指定的校区不存在', + ]; + } +} diff --git a/app/Http/Requests/Admin/Students/StudentClassRequest.php b/app/Http/Requests/Admin/Students/StudentClassRequest.php new file mode 100644 index 0000000..c2739c7 --- /dev/null +++ b/app/Http/Requests/Admin/Students/StudentClassRequest.php @@ -0,0 +1,213 @@ +route()->getActionMethod(); + + return match($action) { + 'list' => $this->listRules(), + 'detail' => $this->detailRules(), + 'create' => $this->createRules(), + 'update' => $this->updateRules(), + 'delete' => $this->deleteRules(), + 'batchDelete' => $this->batchDeleteRules(), + 'studentClasses' => $this->studentClassesRules(), + 'classStudents' => $this->classStudentsRules(), + 'schoolStudents' => $this->schoolStudentsRules(), + 'batchAssign' => $this->batchAssignRules(), + 'batchRemove' => $this->batchRemoveRules(), + 'transfer' => $this->transferRules(), + 'stats' => $this->statsRules(), + default => [] + }; + } + + /** + * 获取分页验证规则 + */ + private function getPaginationRules(): array + { + return [ + 'page' => ['sometimes', 'integer', 'min:1'], + 'page_size' => ['sometimes', 'integer', 'min:1', 'max:100'], + ]; + } + + /** + * 列表查询验证规则 + */ + private function listRules(): array + { + return array_merge($this->getPaginationRules(), [ + 'student_id' => ['sometimes', 'integer'], + 'school_id' => ['sometimes', 'integer', 'exists:school,id'], + 'class_id' => ['sometimes', 'integer', 'exists:school_class,id'], + ]); + } + + /** + * 详情查询验证规则 + */ + private function detailRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:student_class,id'], + ]; + } + + /** + * 创建验证规则 + */ + private function createRules(): array + { + return [ + 'student_id' => ['required', 'integer'], + 'school_id' => ['required', 'integer', 'exists:school,id'], + 'class_id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 更新验证规则 + */ + private function updateRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:student_class,id'], + 'student_id' => ['required', 'integer'], + 'school_id' => ['required', 'integer', 'exists:school,id'], + 'class_id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 删除验证规则 + */ + private function deleteRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:student_class,id'], + ]; + } + + /** + * 批量删除验证规则 + */ + private function batchDeleteRules(): array + { + return [ + 'ids' => ['required', 'array', 'min:1'], + 'ids.*' => ['integer', 'exists:student_class,id'], + ]; + } + + /** + * 学生班级列表验证规则 + */ + private function studentClassesRules(): array + { + return [ + 'student_id' => ['required', 'integer'], + ]; + } + + /** + * 班级学生列表验证规则 + */ + private function classStudentsRules(): array + { + return [ + 'class_id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 学校学生列表验证规则 + */ + private function schoolStudentsRules(): array + { + return [ + 'school_id' => ['required', 'integer', 'exists:school,id'], + ]; + } + + /** + * 批量分配验证规则 + */ + private function batchAssignRules(): array + { + return [ + 'student_ids' => ['required', 'array', 'min:1'], + 'student_ids.*' => ['integer'], + 'class_id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 批量移除验证规则 + */ + private function batchRemoveRules(): array + { + return [ + 'student_ids' => ['required', 'array', 'min:1'], + 'student_ids.*' => ['integer'], + 'class_id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 转移验证规则 + */ + private function transferRules(): array + { + return [ + 'student_id' => ['required', 'integer'], + 'old_class_id' => ['required', 'integer', 'exists:school_class,id'], + 'new_class_id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 统计信息验证规则 + */ + private function statsRules(): array + { + return [ + 'student_id' => ['required', 'integer'], + ]; + } + + /** + * 获取特定的验证错误消息 + */ + protected function getSpecificMessages(): array + { + return [ + 'student_id.required' => '学生ID不能为空', + 'school_id.required' => '学校不能为空', + 'school_id.exists' => '指定的学校不存在', + 'class_id.required' => '班级不能为空', + 'class_id.exists' => '指定的班级不存在', + 'student_ids.required' => '学生ID列表不能为空', + 'student_ids.min' => '至少选择一个学生', + 'old_class_id.required' => '原班级不能为空', + 'old_class_id.exists' => '指定的原班级不存在', + 'new_class_id.required' => '新班级不能为空', + 'new_class_id.exists' => '指定的新班级不存在', + 'ids.required' => '请选择要删除的记录', + 'ids.min' => '至少选择一条记录进行删除', + ]; + } +} diff --git a/app/Http/Requests/Admin/Students/StudentRequest.php b/app/Http/Requests/Admin/Students/StudentRequest.php new file mode 100644 index 0000000..2051e54 --- /dev/null +++ b/app/Http/Requests/Admin/Students/StudentRequest.php @@ -0,0 +1,175 @@ +method()) { + case 'GET': + // 列表查询参数 + $rules = [ + 'username' => 'nullable|string|max:200', + 'real_name' => 'nullable|string|max:30', + 'email' => 'nullable|string|max:200', + 'phone_number' => 'nullable|string|max:11', + 'role' => 'nullable|integer|in:2,3', + 'status' => 'nullable|integer|in:0,1,2,3', + 'sex' => 'nullable|integer|in:0,1,2', + 'grade_id' => 'nullable|integer|min:0', + 'parent_id' => 'nullable|integer|min:0', + 'class_id' => 'nullable|integer|min:0', + ]; + break; + + case 'POST': + // 创建学生 + $rules = [ + 'username' => 'required|string|max:200|unique:student,username', + 'real_name' => 'nullable|string|max:30', + 'password' => 'required|string|min:6|max:20', + 'role' => 'required|integer|in:2,3', + 'grade_id' => 'nullable|integer|min:0', + 'parent_id' => 'nullable|integer|exists:student,id', + 'email' => 'nullable|email|max:200|unique:student,email', + 'phone_number' => 'nullable|string|max:11|unique:student,phone_number', + 'title' => 'nullable|string|max:200', + 'avatar' => 'nullable|string|max:200', + 'status' => 'nullable|integer|in:0,1,2,3', + 'vip_level' => 'nullable|integer|min:0', + 'sex' => 'nullable|integer|in:0,1,2', + 'qq' => 'nullable|string|max:20', + 'examing_number' => 'nullable|string|max:50', + ]; + break; + + case 'PUT': + case 'PATCH': + // 更新学生 + $studentId = $this->route('id'); + $rules = [ + 'username' => 'nullable|string|max:200|unique:student,username,' . $studentId, + 'real_name' => 'nullable|string|max:30', + 'password' => 'nullable|string|min:6|max:20', + 'role' => 'nullable|integer|in:2,3', + 'grade_id' => 'nullable|integer|min:0', + 'parent_id' => 'nullable|integer|exists:student,id', + 'email' => 'nullable|email|max:200|unique:student,email,' . $studentId, + 'phone_number' => 'nullable|string|max:11|unique:student,phone_number,' . $studentId, + 'title' => 'nullable|string|max:200', + 'avatar' => 'nullable|string|max:200', + 'status' => 'nullable|integer|in:0,1,2,3', + 'vip_level' => 'nullable|integer|min:0', + 'sex' => 'nullable|integer|in:0,1,2', + 'qq' => 'nullable|string|max:20', + 'examing_number' => 'nullable|string|max:50', + ]; + break; + } + + return $rules; + } + + /** + * 获取特定的验证错误消息 + * @return array + */ + protected function getSpecificMessages(): array + { + return [ + 'username.required' => '用户名不能为空', + 'username.max' => '用户名最多200个字符', + 'username.unique' => '用户名已存在', + 'real_name.max' => '真实姓名最多30个字符', + 'password.required' => '密码不能为空', + 'password.min' => '密码长度至少6位', + 'password.max' => '密码长度最多20位', + 'role.required' => '角色不能为空', + 'role.in' => '角色值无效', + 'grade_id.integer' => '年级ID必须是整数', + 'grade_id.min' => '年级ID不能小于0', + 'parent_id.integer' => '家长ID必须是整数', + 'parent_id.exists' => '家长不存在', + 'email.email' => '邮箱格式不正确', + 'email.max' => '邮箱最多200个字符', + 'email.unique' => '邮箱已存在', + 'phone_number.max' => '手机号最多11个字符', + 'phone_number.unique' => '手机号已存在', + 'title.max' => '头衔最多200个字符', + 'avatar.max' => '头像URL最多200个字符', + 'status.in' => '状态值无效', + 'vip_level.integer' => 'VIP等级必须是整数', + 'vip_level.min' => 'VIP等级不能小于0', + 'sex.in' => '性别值无效', + 'qq.max' => 'QQ号最多20个字符', + 'examing_number.max' => '考号最多50个字符', + ]; + } + + + + /** + * 获取字段名称 + * @return array + */ + public function attributes(): array + { + return [ + 'username' => '用户名', + 'real_name' => '真实姓名', + 'password' => '密码', + 'role' => '角色', + 'grade_id' => '年级ID', + 'parent_id' => '家长ID', + 'email' => '邮箱', + 'phone_number' => '手机号', + 'title' => '头衔', + 'avatar' => '头像', + 'status' => '状态', + 'vip_level' => 'VIP等级', + 'sex' => '性别', + 'qq' => 'QQ号', + 'examing_number' => '考号', + 'class_id' => '班级ID', + ]; + } + + /** + * 配置验证器 + * @param \Illuminate\Validation\Validator $validator + * @return void + */ + public function withValidator($validator): void + { + $validator->after(function ($validator) { + // 如果是创建学生并且指定了家长,验证家长必须是家长角色 + if ($this->method() === 'POST' && $this->parent_id) { + $parent = Student::find($this->parent_id); + if ($parent && $parent->role !== Student::ROLE_PARENT) { + $validator->errors()->add('parent_id', '指定的家长用户角色不正确'); + } + } + + // 如果是学生角色,不能指定自己为家长 + if ($this->role === Student::ROLE_STUDENT && $this->parent_id) { + $studentId = $this->method() === 'POST' ? 0 : $this->route('id'); + if ($this->parent_id == $studentId) { + $validator->errors()->add('parent_id', '不能指定自己为家长'); + } + } + }); + } +} diff --git a/app/Http/Requests/Admin/System/SystemDictDataRequest.php b/app/Http/Requests/Admin/System/SystemDictDataRequest.php new file mode 100644 index 0000000..7c4e398 --- /dev/null +++ b/app/Http/Requests/Admin/System/SystemDictDataRequest.php @@ -0,0 +1,424 @@ +route()->getActionMethod(); + + return match($action) { + 'list' => $this->listRules(), + 'detail' => $this->detailRules(), + 'create' => $this->createRules(), + 'update' => $this->updateRules(), + 'delete' => $this->deleteRules(), + 'batchDelete' => $this->batchDeleteRules(), + 'updateStatus' => $this->updateStatusRules(), + 'batchUpdateStatus' => $this->batchUpdateStatusRules(), + 'updateSort' => $this->updateSortRules(), + 'batchUpdateSort' => $this->batchUpdateSortRules(), + 'getByType' => $this->getByTypeRules(), + 'options' => $this->optionsRules(), + 'getLabel' => $this->getLabelRules(), + 'getByTypeAndValue' => $this->getByTypeAndValueRules(), + 'statistics' => $this->statisticsRules(), + 'simpleList' => $this->simpleListRules(), + 'groupStatistics' => [], + default => [] + }; + } + + /** + * 列表查询验证规则 + */ + private function listRules(): array + { + return array_merge($this->getPaginationRules(), [ + 'label' => ['sometimes', 'string', 'max:100'], + 'value' => ['sometimes', 'string', 'max:100'], + 'dict_type' => ['sometimes', 'string', 'max:100'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'color_type' => ['sometimes', 'string', 'max:100'], + 'create_time_start' => ['sometimes', 'date'], + 'create_time_end' => ['sometimes', 'date'], + ]); + } + + /** + * 详情查询验证规则 + */ + private function detailRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:system_dict_data,id'], + ]; + } + + /** + * 创建验证规则 + */ + private function createRules(): array + { + return [ + 'sort' => ['sometimes', 'integer', 'min:0'], + 'label' => ['required', 'string', 'max:100'], + 'value' => ['required', 'string', 'max:100'], + 'dict_type' => ['required', 'string', 'max:100', 'exists:system_dict_type,type'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'color_type' => ['sometimes', 'nullable', 'string', 'max:100'], + 'css_class' => ['sometimes', 'nullable', 'string', 'max:100'], + 'remark' => ['sometimes', 'nullable', 'string', 'max:500'], + ]; + } + + /** + * 更新验证规则 + */ + private function updateRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:system_dict_data,id'], + 'sort' => ['sometimes', 'integer', 'min:0'], + 'label' => ['required', 'string', 'max:100'], + 'value' => ['required', 'string', 'max:100'], + 'dict_type' => ['required', 'string', 'max:100', 'exists:system_dict_type,type'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'color_type' => ['sometimes', 'nullable', 'string', 'max:100'], + 'css_class' => ['sometimes', 'nullable', 'string', 'max:100'], + 'remark' => ['sometimes', 'nullable', 'string', 'max:500'], + ]; + } + + /** + * 删除验证规则 + */ + private function deleteRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:system_dict_data,id'], + ]; + } + + /** + * 批量删除验证规则 + */ + private function batchDeleteRules(): array + { + return [ + 'ids' => ['required', 'array', 'min:1'], + 'ids.*' => ['integer', 'exists:system_dict_data,id'], + ]; + } + + /** + * 更新状态验证规则 + */ + private function updateStatusRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:system_dict_data,id'], + 'status' => ['required', 'integer', 'in:0,1'], + ]; + } + + /** + * 批量更新状态验证规则 + */ + private function batchUpdateStatusRules(): array + { + return [ + 'ids' => ['required', 'array', 'min:1'], + 'ids.*' => ['integer', 'exists:system_dict_data,id'], + 'status' => ['required', 'integer', 'in:0,1'], + ]; + } + + /** + * 更新排序验证规则 + */ + private function updateSortRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:system_dict_data,id'], + 'sort' => ['required', 'integer', 'min:0'], + ]; + } + + /** + * 批量更新排序验证规则 + */ + private function batchUpdateSortRules(): array + { + return [ + 'sort_data' => ['required', 'array', 'min:1'], + 'sort_data.*.id' => ['required', 'integer', 'exists:system_dict_data,id'], + 'sort_data.*.sort' => ['required', 'integer', 'min:0'], + ]; + } + + /** + * 根据类型获取数据验证规则 + */ + private function getByTypeRules(): array + { + return [ + 'dict_type' => ['required', 'string', 'max:100', 'exists:system_dict_type,type'], + 'only_active' => ['sometimes', 'boolean'], + ]; + } + + /** + * 获取选项验证规则 + */ + private function optionsRules(): array + { + return [ + 'dict_type' => ['required', 'string', 'max:100', 'exists:system_dict_type,type'], + 'only_active' => ['sometimes', 'boolean'], + ]; + } + + /** + * 获取标签验证规则 + */ + private function getLabelRules(): array + { + return [ + 'dict_type' => ['required', 'string', 'max:100', 'exists:system_dict_type,type'], + 'value' => ['required', 'string', 'max:100'], + ]; + } + + /** + * 根据类型和值获取数据验证规则 + */ + private function getByTypeAndValueRules(): array + { + return [ + 'dict_type' => ['required', 'string', 'max:100', 'exists:system_dict_type,type'], + 'value' => ['required', 'string', 'max:100'], + ]; + } + + /** + * 统计信息验证规则 + */ + private function statisticsRules(): array + { + return [ + 'dict_type' => ['sometimes', 'string', 'max:100', 'exists:system_dict_type,type'], + ]; + } + + /** + * 简单列表验证规则 + */ + private function simpleListRules(): array + { + return [ + 'dict_type' => ['required', 'string', 'max:100', 'exists:system_dict_type,type'], + ]; + } + + /** + * 获取验证错误消息 + */ + public function messages(): array + { + return [ + 'sort.integer' => '排序必须是整数', + 'sort.min' => '排序不能小于0', + 'sort.required' => '排序不能为空', + + 'label.required' => '字典标签不能为空', + 'label.string' => '字典标签必须是字符串', + 'label.max' => '字典标签最大长度为100个字符', + + 'value.required' => '字典键值不能为空', + 'value.string' => '字典键值必须是字符串', + 'value.max' => '字典键值最大长度为100个字符', + + 'dict_type.required' => '字典类型不能为空', + 'dict_type.string' => '字典类型必须是字符串', + 'dict_type.max' => '字典类型最大长度为100个字符', + 'dict_type.exists' => '字典类型不存在', + + 'status.integer' => '状态必须是整数', + 'status.in' => '状态值无效,只能是0或1', + 'status.required' => '状态不能为空', + + 'color_type.string' => '颜色类型必须是字符串', + 'color_type.max' => '颜色类型最大长度为100个字符', + + 'css_class.string' => 'CSS样式必须是字符串', + 'css_class.max' => 'CSS样式最大长度为100个字符', + + 'remark.string' => '备注必须是字符串', + 'remark.max' => '备注最大长度为500个字符', + + 'id.required' => 'ID不能为空', + 'id.integer' => 'ID必须是整数', + 'id.exists' => '字典数据不存在', + + 'ids.required' => '请选择要操作的字典数据', + 'ids.array' => 'IDs必须是数组', + 'ids.min' => '至少选择一条记录进行操作', + 'ids.*.integer' => 'ID必须是整数', + 'ids.*.exists' => '选中的字典数据不存在', + + 'sort_data.required' => '排序数据不能为空', + 'sort_data.array' => '排序数据必须是数组', + 'sort_data.min' => '排序数据不能为空', + 'sort_data.*.id.required' => '排序数据ID不能为空', + 'sort_data.*.id.integer' => '排序数据ID必须是整数', + 'sort_data.*.id.exists' => '排序数据ID不存在', + 'sort_data.*.sort.required' => '排序值不能为空', + 'sort_data.*.sort.integer' => '排序值必须是整数', + 'sort_data.*.sort.min' => '排序值不能小于0', + + 'only_active.boolean' => '只获取启用状态参数必须是布尔值', + + 'create_time_start.date' => '开始时间格式不正确', + 'create_time_end.date' => '结束时间格式不正确', + ]; + } + + /** + * 字段名称 + * + * @return array + */ + public function attributes(): array + { + return [ + 'sort' => '排序', + 'label' => '字典标签', + 'value' => '字典键值', + 'dict_type' => '字典类型', + 'status' => '状态', + 'color_type' => '颜色类型', + 'css_class' => 'CSS样式', + 'remark' => '备注', + 'id' => 'ID', + 'ids' => 'IDs', + 'sort_data' => '排序数据', + 'only_active' => '只获取启用状态', + ]; + } + + /** + * 验证数据预处理 + * + * @return void + */ + protected function prepareForValidation(): void + { + // 设置默认状态 + if (!$this->has('status') && $this->route()->getActionMethod() === 'create') { + $this->merge(['status' => SystemDictData::STATUS_NORMAL]); + } + + // 设置默认排序 + if (!$this->has('sort') && $this->has('dict_type') && $this->route()->getActionMethod() === 'create') { + $nextSort = SystemDictData::getNextSort($this->input('dict_type')); + $this->merge(['sort' => $nextSort]); + } + + // 移除空白字符 + $trimFields = ['label', 'value', 'dict_type', 'color_type', 'css_class', 'remark']; + foreach ($trimFields as $field) { + if ($this->has($field)) { + $this->merge([$field => trim($this->input($field))]); + } + } + } + + /** + * 自定义验证规则 + * + * @param \Illuminate\Validation\Validator $validator + */ + public function withValidator($validator): void + { + $validator->after(function ($validator) { + // 验证字典类型是否启用 + if ($this->has('dict_type')) { + $dictType = SystemDictType::findByType($this->input('dict_type')); + if ($dictType && $dictType->isDisabled()) { + $validator->errors()->add('dict_type', '该字典类型已被停用'); + } + } + + // 验证字典值唯一性(创建或更新时) + $action = $this->route()->getActionMethod(); + if (in_array($action, ['create', 'update']) && $this->has('value') && $this->has('dict_type')) { + $query = SystemDictData::where('value', $this->input('value')) + ->where('dict_type', $this->input('dict_type')); + + if ($action === 'update' && $this->has('id')) { + $query->where('id', '!=', $this->input('id')); + } + + if ($query->exists()) { + $validator->errors()->add('value', '该字典类型下的字典键值已存在'); + } + } + }); + } + + /** + * 获取特定的验证规则(BaseRequest需要) + * + * @return array + */ + protected function getSpecificRules(): array + { + return []; + } + + /** + * 获取特定的验证消息(BaseRequest需要) + * + * @return array + */ + protected function getSpecificMessages(): array + { + return []; + } + + /** + * 获取分页验证规则 + * + * @return array + */ + private function getPaginationRules(): array + { + return [ + 'page' => ['sometimes', 'integer', 'min:1'], + 'page_size' => ['sometimes', 'integer', 'min:1', 'max:100'], + ]; + } + + /** + * 获取验证失败后的错误重定向URL + * + * @return string + */ + protected function getRedirectUrl(): string + { + return $this->ajax() ? '' : url()->previous(); + } +} diff --git a/app/Http/Requests/Admin/System/SystemDictTypeRequest.php b/app/Http/Requests/Admin/System/SystemDictTypeRequest.php new file mode 100644 index 0000000..f9972d4 --- /dev/null +++ b/app/Http/Requests/Admin/System/SystemDictTypeRequest.php @@ -0,0 +1,266 @@ +route()->getActionMethod(); + + return match($action) { + 'list' => $this->listRules(), + 'detail' => $this->detailRules(), + 'create' => $this->createRules(), + 'update' => $this->updateRules(), + 'delete' => $this->deleteRules(), + 'batchDelete' => $this->batchDeleteRules(), + 'updateStatus' => $this->updateStatusRules(), + 'batchUpdateStatus' => $this->batchUpdateStatusRules(), + 'getByType' => $this->getByTypeRules(), + 'simpleList' => [], + 'statistics' => [], + 'options' => [], + default => [] + }; + } + + /** + * 列表查询验证规则 + */ + private function listRules(): array + { + return array_merge($this->getPaginationRules(), [ + 'name' => ['sometimes', 'string', 'max:100'], + 'type' => ['sometimes', 'string', 'max:100'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'create_time_start' => ['sometimes', 'date'], + 'create_time_end' => ['sometimes', 'date'], + ]); + } + + /** + * 详情查询验证规则 + */ + private function detailRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:system_dict_type,id'], + ]; + } + + /** + * 创建验证规则 + */ + private function createRules(): array + { + return [ + 'name' => ['required', 'string', 'max:100'], + 'type' => ['required', 'string', 'max:100', 'unique:system_dict_type,type'], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'remark' => ['sometimes', 'nullable', 'string', 'max:500'], + ]; + } + + /** + * 更新验证规则 + */ + private function updateRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:system_dict_type,id'], + 'name' => ['required', 'string', 'max:100'], + 'type' => ['required', 'string', 'max:100', Rule::unique('system_dict_type', 'type')->ignore($this->input('id'))], + 'status' => ['sometimes', 'integer', 'in:0,1'], + 'remark' => ['sometimes', 'nullable', 'string', 'max:500'], + ]; + } + + /** + * 删除验证规则 + */ + private function deleteRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:system_dict_type,id'], + ]; + } + + /** + * 批量删除验证规则 + */ + private function batchDeleteRules(): array + { + return [ + 'ids' => ['required', 'array', 'min:1'], + 'ids.*' => ['integer', 'exists:system_dict_type,id'], + ]; + } + + /** + * 更新状态验证规则 + */ + private function updateStatusRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:system_dict_type,id'], + 'status' => ['required', 'integer', 'in:0,1'], + ]; + } + + /** + * 批量更新状态验证规则 + */ + private function batchUpdateStatusRules(): array + { + return [ + 'ids' => ['required', 'array', 'min:1'], + 'ids.*' => ['integer', 'exists:system_dict_type,id'], + 'status' => ['required', 'integer', 'in:0,1'], + ]; + } + + /** + * 根据类型获取验证规则 + */ + private function getByTypeRules(): array + { + return [ + 'type' => ['required', 'string', 'max:100', 'exists:system_dict_type,type'], + ]; + } + + /** + * 获取验证错误消息 + */ + public function messages(): array + { + return [ + 'name.required' => '字典名称不能为空', + 'name.string' => '字典名称必须是字符串', + 'name.max' => '字典名称最大长度为100个字符', + + 'type.required' => '字典类型不能为空', + 'type.string' => '字典类型必须是字符串', + 'type.max' => '字典类型最大长度为100个字符', + 'type.unique' => '字典类型已存在', + 'type.exists' => '字典类型不存在', + + 'status.integer' => '状态必须是整数', + 'status.in' => '状态值无效,只能是0或1', + 'status.required' => '状态不能为空', + + 'remark.string' => '备注必须是字符串', + 'remark.max' => '备注最大长度为500个字符', + + 'id.required' => 'ID不能为空', + 'id.integer' => 'ID必须是整数', + 'id.exists' => '字典类型不存在', + + 'ids.required' => '请选择要操作的字典类型', + 'ids.array' => 'IDs必须是数组', + 'ids.min' => '至少选择一条记录进行操作', + 'ids.*.integer' => 'ID必须是整数', + 'ids.*.exists' => '选中的字典类型不存在', + + 'create_time_start.date' => '开始时间格式不正确', + 'create_time_end.date' => '结束时间格式不正确', + ]; + } + + /** + * 字段名称 + * + * @return array + */ + public function attributes(): array + { + return [ + 'name' => '字典名称', + 'type' => '字典类型', + 'status' => '状态', + 'remark' => '备注', + 'id' => 'ID', + 'ids' => 'IDs', + ]; + } + + /** + * 验证数据预处理 + * + * @return void + */ + protected function prepareForValidation(): void + { + // 设置默认状态 + if (!$this->has('status') && $this->route()->getActionMethod() === 'create') { + $this->merge(['status' => SystemDictType::STATUS_NORMAL]); + } + + // 移除空白字符 + if ($this->has('name')) { + $this->merge(['name' => trim($this->input('name'))]); + } + + if ($this->has('type')) { + $this->merge(['type' => trim($this->input('type'))]); + } + + if ($this->has('remark')) { + $this->merge(['remark' => trim($this->input('remark'))]); + } + } + + /** + * 获取分页验证规则 + * + * @return array + */ + private function getPaginationRules(): array + { + return [ + 'page' => ['sometimes', 'integer', 'min:1'], + 'page_size' => ['sometimes', 'integer', 'min:1', 'max:100'], + ]; + } + + /** + * 获取特定的验证规则(BaseRequest需要) + * + * @return array + */ + protected function getSpecificRules(): array + { + return []; + } + + /** + * 获取特定的验证消息(BaseRequest需要) + * + * @return array + */ + protected function getSpecificMessages(): array + { + return []; + } + + /** + * 获取验证失败后的错误重定向URL + * + * @return string + */ + protected function getRedirectUrl(): string + { + return $this->ajax() ? '' : url()->previous(); + } +} diff --git a/app/Http/Requests/Admin/System/SystemUserSchoolCampusRequest.php b/app/Http/Requests/Admin/System/SystemUserSchoolCampusRequest.php new file mode 100644 index 0000000..34d3b03 --- /dev/null +++ b/app/Http/Requests/Admin/System/SystemUserSchoolCampusRequest.php @@ -0,0 +1,220 @@ +route()->getActionMethod(); + + return match($action) { + 'list' => $this->listRules(), + 'detail' => $this->detailRules(), + 'create' => $this->createRules(), + 'update' => $this->updateRules(), + 'delete' => $this->deleteRules(), + 'batchDelete' => $this->batchDeleteRules(), + 'userSchoolCampuses' => $this->userSchoolCampusesRules(), + 'schoolUsers' => $this->schoolUsersRules(), + 'campusUsers' => $this->campusUsersRules(), + 'batchAssign' => $this->batchAssignRules(), + 'batchRemove' => $this->batchRemoveRules(), + 'transfer' => $this->transferRules(), + 'stats' => $this->statsRules(), + 'mySchoolCampuses', 'myStats' => [], + default => [] + }; + } + + /** + * 获取分页验证规则 + */ + private function getPaginationRules(): array + { + return [ + 'page' => ['sometimes', 'integer', 'min:1'], + 'page_size' => ['sometimes', 'integer', 'min:1', 'max:100'], + ]; + } + + /** + * 列表查询验证规则 + */ + private function listRules(): array + { + return array_merge($this->getPaginationRules(), [ + 'userid' => ['sometimes', 'integer', 'exists:system_users,id'], + 'schoolid' => ['sometimes', 'integer', 'exists:school,id'], + 'campusid' => ['sometimes', 'integer', 'exists:school_campus,id'], + ]); + } + + /** + * 详情查询验证规则 + */ + private function detailRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:system_user_school_campus,id'], + ]; + } + + /** + * 创建验证规则 + */ + private function createRules(): array + { + return [ + 'userid' => ['required', 'integer', 'exists:system_users,id'], + 'schoolid' => ['required', 'integer', 'exists:school,id'], + 'campusid' => ['sometimes', 'integer', 'exists:school_campus,id'], + ]; + } + + /** + * 更新验证规则 + */ + private function updateRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:system_user_school_campus,id'], + 'userid' => ['required', 'integer', 'exists:system_users,id'], + 'schoolid' => ['required', 'integer', 'exists:school,id'], + 'campusid' => ['sometimes', 'integer', 'exists:school_campus,id'], + ]; + } + + /** + * 删除验证规则 + */ + private function deleteRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:system_user_school_campus,id'], + ]; + } + + /** + * 批量删除验证规则 + */ + private function batchDeleteRules(): array + { + return [ + 'ids' => ['required', 'array', 'min:1'], + 'ids.*' => ['integer', 'exists:system_user_school_campus,id'], + ]; + } + + /** + * 用户校区列表验证规则 + */ + private function userSchoolCampusesRules(): array + { + return [ + 'userid' => ['required', 'integer', 'exists:system_users,id'], + ]; + } + + /** + * 学校用户列表验证规则 + */ + private function schoolUsersRules(): array + { + return [ + 'schoolid' => ['required', 'integer', 'exists:school,id'], + ]; + } + + /** + * 校区用户列表验证规则 + */ + private function campusUsersRules(): array + { + return [ + 'campusid' => ['required', 'integer', 'exists:school_campus,id'], + ]; + } + + /** + * 批量分配验证规则 + */ + private function batchAssignRules(): array + { + return [ + 'userids' => ['required', 'array', 'min:1'], + 'userids.*' => ['integer', 'exists:system_users,id'], + 'schoolid' => ['required', 'integer', 'exists:school,id'], + 'campusid' => ['sometimes', 'integer', 'exists:school_campus,id'], + ]; + } + + /** + * 批量移除验证规则 + */ + private function batchRemoveRules(): array + { + return [ + 'userids' => ['required', 'array', 'min:1'], + 'userids.*' => ['integer', 'exists:system_users,id'], + 'schoolid' => ['required', 'integer', 'exists:school,id'], + 'campusid' => ['sometimes', 'integer', 'exists:school_campus,id'], + ]; + } + + /** + * 转移验证规则 + */ + private function transferRules(): array + { + return [ + 'userid' => ['required', 'integer', 'exists:system_users,id'], + 'old_schoolid' => ['required', 'integer', 'exists:school,id'], + 'new_schoolid' => ['required', 'integer', 'exists:school,id'], + 'old_campusid' => ['sometimes', 'integer', 'exists:school_campus,id'], + 'new_campusid' => ['sometimes', 'integer', 'exists:school_campus,id'], + ]; + } + + /** + * 统计信息验证规则 + */ + private function statsRules(): array + { + return [ + 'userid' => ['required', 'integer', 'exists:system_users,id'], + ]; + } + + /** + * 获取特定的验证错误消息 + */ + protected function getSpecificMessages(): array + { + return [ + 'userid.required' => '用户ID不能为空', + 'userid.exists' => '指定的用户不存在', + 'schoolid.required' => '学校不能为空', + 'schoolid.exists' => '指定的学校不存在', + 'campusid.exists' => '指定的校区不存在', + 'userids.required' => '用户ID列表不能为空', + 'userids.min' => '至少选择一个用户', + 'old_schoolid.required' => '原学校不能为空', + 'old_schoolid.exists' => '指定的原学校不存在', + 'new_schoolid.required' => '新学校不能为空', + 'new_schoolid.exists' => '指定的新学校不存在', + 'old_campusid.exists' => '指定的原校区不存在', + 'new_campusid.exists' => '指定的新校区不存在', + 'ids.required' => '请选择要删除的记录', + 'ids.min' => '至少选择一条记录进行删除', + ]; + } +} diff --git a/app/Http/Requests/Admin/Teachers/TeacherClassRequest.php b/app/Http/Requests/Admin/Teachers/TeacherClassRequest.php new file mode 100644 index 0000000..a84f4ca --- /dev/null +++ b/app/Http/Requests/Admin/Teachers/TeacherClassRequest.php @@ -0,0 +1,242 @@ +route()->getActionMethod(); + + return match($action) { + 'list' => $this->listRules(), + 'detail' => $this->detailRules(), + 'create' => $this->createRules(), + 'update' => $this->updateRules(), + 'delete' => $this->deleteRules(), + 'batchDelete' => $this->batchDeleteRules(), + 'teacherClasses' => $this->teacherClassesRules(), + 'classTeachers' => $this->classTeachersRules(), + 'schoolTeachers' => $this->schoolTeachersRules(), + 'headmans' => $this->headmansRules(), + 'batchAssign' => $this->batchAssignRules(), + 'batchRemove' => $this->batchRemoveRules(), + 'setHeadman' => $this->setHeadmanRules(), + 'removeHeadman' => $this->removeHeadmanRules(), + 'stats' => $this->statsRules(), + 'myClasses', 'myStats' => [], + default => [] + }; + } + + /** + * 获取分页验证规则 + */ + private function getPaginationRules(): array + { + return [ + 'page' => ['sometimes', 'integer', 'min:1'], + 'page_size' => ['sometimes', 'integer', 'min:1', 'max:100'], + ]; + } + + /** + * 列表查询验证规则 + */ + private function listRules(): array + { + return array_merge($this->getPaginationRules(), [ + 'teacher_id' => ['sometimes', 'integer', 'exists:system_users,id'], + 'school_id' => ['sometimes', 'integer', 'exists:school,id'], + 'class_id' => ['sometimes', 'integer', 'exists:school_class,id'], + 'grade_id' => ['sometimes', 'integer'], + 'headman' => ['sometimes', 'integer', 'in:0,1'], + ]); + } + + /** + * 详情查询验证规则 + */ + private function detailRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:teacher_class,id'], + ]; + } + + /** + * 创建验证规则 + */ + private function createRules(): array + { + return [ + 'teacher_id' => ['required', 'integer', 'exists:system_users,id'], + 'school_id' => ['required', 'integer', 'exists:school,id'], + 'grade_id' => ['required', 'integer'], + 'class_id' => ['required', 'integer', 'exists:school_class,id'], + 'headman' => ['sometimes', 'integer', 'in:0,1'], + ]; + } + + /** + * 更新验证规则 + */ + private function updateRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:teacher_class,id'], + 'teacher_id' => ['required', 'integer', 'exists:system_users,id'], + 'school_id' => ['required', 'integer', 'exists:school,id'], + 'grade_id' => ['required', 'integer'], + 'class_id' => ['required', 'integer', 'exists:school_class,id'], + 'headman' => ['sometimes', 'integer', 'in:0,1'], + ]; + } + + /** + * 删除验证规则 + */ + private function deleteRules(): array + { + return [ + 'id' => ['required', 'integer', 'exists:teacher_class,id'], + ]; + } + + /** + * 批量删除验证规则 + */ + private function batchDeleteRules(): array + { + return [ + 'ids' => ['required', 'array', 'min:1'], + 'ids.*' => ['integer', 'exists:teacher_class,id'], + ]; + } + + /** + * 老师班级列表验证规则 + */ + private function teacherClassesRules(): array + { + return [ + 'teacher_id' => ['required', 'integer', 'exists:system_users,id'], + ]; + } + + /** + * 班级老师列表验证规则 + */ + private function classTeachersRules(): array + { + return [ + 'class_id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 学校老师列表验证规则 + */ + private function schoolTeachersRules(): array + { + return [ + 'school_id' => ['required', 'integer', 'exists:school,id'], + ]; + } + + /** + * 班主任列表验证规则 + */ + private function headmansRules(): array + { + return [ + 'school_id' => ['sometimes', 'integer', 'exists:school,id'], + 'grade_id' => ['sometimes', 'integer'], + ]; + } + + /** + * 批量分配验证规则 + */ + private function batchAssignRules(): array + { + return [ + 'teacher_ids' => ['required', 'array', 'min:1'], + 'teacher_ids.*' => ['integer', 'exists:system_users,id'], + 'class_id' => ['required', 'integer', 'exists:school_class,id'], + 'grade_id' => ['sometimes', 'integer'], + ]; + } + + /** + * 批量移除验证规则 + */ + private function batchRemoveRules(): array + { + return [ + 'teacher_ids' => ['required', 'array', 'min:1'], + 'teacher_ids.*' => ['integer', 'exists:system_users,id'], + 'class_id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 设置班主任验证规则 + */ + private function setHeadmanRules(): array + { + return [ + 'teacher_id' => ['required', 'integer', 'exists:system_users,id'], + 'class_id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 取消班主任验证规则 + */ + private function removeHeadmanRules(): array + { + return [ + 'class_id' => ['required', 'integer', 'exists:school_class,id'], + ]; + } + + /** + * 统计信息验证规则 + */ + private function statsRules(): array + { + return [ + 'teacher_id' => ['required', 'integer', 'exists:system_users,id'], + ]; + } + + /** + * 获取特定的验证错误消息 + */ + protected function getSpecificMessages(): array + { + return [ + 'teacher_id.required' => '老师ID不能为空', + 'teacher_id.exists' => '指定的老师不存在', + 'school_id.required' => '学校不能为空', + 'school_id.exists' => '指定的学校不存在', + 'class_id.required' => '班级不能为空', + 'class_id.exists' => '指定的班级不存在', + 'grade_id.required' => '年级不能为空', + 'headman.in' => '班主任状态值无效', + 'teacher_ids.required' => '老师ID列表不能为空', + 'teacher_ids.min' => '至少选择一个老师', + 'ids.required' => '请选择要删除的记录', + 'ids.min' => '至少选择一条记录进行删除', + ]; + } +} diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index 6701c20..590232c 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -14,6 +14,19 @@ class BaseModel extends Model const UPDATED_AT = 'update_time'; const DELETED_AT = 'deleted'; + /** + * 是否启用系统字段自动维护 + * 包括:tenant_id、creator、create_time、updater、update_time、deleted + * 默认关闭,需要的模型可以设置为true + */ + protected $enableSystemFields = false; + + /** + * 是否启用租户隔离 + * 默认关闭,需要的模型可以设置为true + */ + protected $enableTenantScope = false; + /** * 序列化日期为指定格式 */ @@ -80,6 +93,10 @@ class BaseModel extends Model */ public function setCreateFields(): void { + if (!$this->enableSystemFields) { + return; + } + $userId = auth('admin')->id() ?? 0; $tenantId = $this->getCurrentTenantId() ?? 0; @@ -94,6 +111,10 @@ class BaseModel extends Model */ public function setUpdateFields(): void { + if (!$this->enableSystemFields) { + return; + } + $userId = auth('admin')->id() ?? 0; $this->updater = $userId; @@ -126,28 +147,36 @@ class BaseModel extends Model */ protected static function booted() { - // 创建时自动设置系统字段 - static::creating(function ($model) { - $model->setCreateFields(); - }); + $model = new static; - // 更新时自动设置系统字段 - static::updating(function ($model) { - $model->setUpdateFields(); - }); + // 只有启用系统字段维护的模型才自动设置系统字段 + if ($model->enableSystemFields) { + // 创建时自动设置系统字段 + static::creating(function ($model) { + $model->setCreateFields(); + }); - // 全局作用域:自动过滤租户数据 - static::addGlobalScope('tenant', function ($builder) { - $model = new static; - $tenantId = $model->getCurrentTenantId(); - if ($tenantId !== null) { - $builder->where('tenant_id', $tenantId); - } - }); + // 更新时自动设置系统字段 + static::updating(function ($model) { + $model->setUpdateFields(); + }); - // 全局作用域:自动过滤已删除数据 - static::addGlobalScope('not_deleted', function ($builder) { - $builder->where('deleted', 0); - }); + // 全局作用域:自动过滤已删除数据 + static::addGlobalScope('not_deleted', function ($builder) { + $builder->where('deleted', 0); + }); + } + + // 只有启用租户隔离的模型才添加租户作用域 + if ($model->enableTenantScope) { + // 全局作用域:自动过滤租户数据 + static::addGlobalScope('tenant', function ($builder) { + $model = new static; + $tenantId = $model->getCurrentTenantId(); + if ($tenantId !== null) { + $builder->where('tenant_id', $tenantId); + } + }); + } } } diff --git a/app/Models/Schools/School.php b/app/Models/Schools/School.php new file mode 100644 index 0000000..d15f639 --- /dev/null +++ b/app/Models/Schools/School.php @@ -0,0 +1,118 @@ + 'integer', + 'province_id' => 'integer', + 'city_id' => 'integer', + 'district_id' => 'integer', + 'status' => 'integer', + 'sno_automatic' => 'integer', + 'show_order' => 'integer', + 'is_open_user_login' => 'integer', + 'max_sno' => 'integer', + 'school_district_id' => 'integer', + ]; + + /** + * 学校的校区关联 + */ + public function campuses() + { + return $this->hasMany(SchoolCampus::class, 'school_id'); + } + + /** + * 学校的班级关联 + */ + public function classes() + { + return $this->hasMany(SchoolClass::class, 'school_id'); + } + + /** + * 学校的用户关联 + */ + public function userSchoolCampuses() + { + return $this->hasMany(SystemUserSchoolCampus::class, 'schoolid'); + } + + /** + * 激活状态查询作用域 + */ + public function scopeActive($query) + { + return $query->where('status', 1); + } + + /** + * 按显示顺序排序 + */ + public function scopeOrdered($query) + { + return $query->orderBy('show_order', 'asc')->orderBy('id', 'desc'); + } +} diff --git a/app/Models/Schools/SchoolCampus.php b/app/Models/Schools/SchoolCampus.php new file mode 100644 index 0000000..d0c14fd --- /dev/null +++ b/app/Models/Schools/SchoolCampus.php @@ -0,0 +1,88 @@ + 'integer', + 'self_support' => 'boolean', + 'is_open_user_login' => 'integer', + 'status' => 'integer', + ]; + + /** + * 校区所属学校 + */ + public function school() + { + return $this->belongsTo(School::class, 'school_id'); + } + + /** + * 校区的班级关联 + */ + public function classes() + { + return $this->hasMany(SchoolClass::class, 'campus_id'); + } + + /** + * 校区的用户关联 + */ + public function userSchoolCampuses() + { + return $this->hasMany(SystemUserSchoolCampus::class, 'campusid'); + } + + /** + * 激活状态查询作用域 + */ + public function scopeActive($query) + { + return $query->where('status', 1); + } +} diff --git a/app/Models/Schools/SchoolClass.php b/app/Models/Schools/SchoolClass.php new file mode 100644 index 0000000..5c38c1e --- /dev/null +++ b/app/Models/Schools/SchoolClass.php @@ -0,0 +1,107 @@ + 'integer', + 'school_id' => 'integer', + 'campus_id' => 'integer', + 'grade_id' => 'integer', + 'number' => 'integer', + 'status' => 'integer', + 'level' => 'integer', + 'is_open_user_login' => 'integer', + ]; + + /** + * 班级所属学校 + */ + public function school() + { + return $this->belongsTo(School::class, 'school_id'); + } + + /** + * 班级所属校区 + */ + public function campus() + { + return $this->belongsTo(SchoolCampus::class, 'campus_id'); + } + + /** + * 班级的学生关联 + */ + public function studentClasses() + { + return $this->hasMany(StudentClass::class, 'class_id'); + } + + /** + * 班级的老师关联 + */ + public function teacherClasses() + { + return $this->hasMany(TeacherClass::class, 'class_id'); + } + + /** + * 激活状态查询作用域 + */ + public function scopeActive($query) + { + return $query->where('status', 1); + } +} diff --git a/app/Models/Students/Student.php b/app/Models/Students/Student.php new file mode 100644 index 0000000..ebec881 --- /dev/null +++ b/app/Models/Students/Student.php @@ -0,0 +1,416 @@ + 'datetime', + 'role' => 'integer', + 'grade_id' => 'integer', + 'parent_id' => 'integer', + 'status' => 'integer', + 'vip_level' => 'integer', + 'sex' => 'integer', + ]; + + /** + * 角色常量 + */ + const ROLE_STUDENT = 2; // 学生 + const ROLE_PARENT = 3; // 家长 + + /** + * 状态常量 + */ + const STATUS_DISABLED = 0; // 禁用 + const STATUS_NORMAL = 1; // 正常 + const STATUS_ARREARS = 2; // 欠费 + const STATUS_INACTIVE = 3; // 未激活 + + /** + * 性别常量 + */ + const SEX_UNKNOWN = 0; // 不填 + const SEX_MALE = 1; // 男 + const SEX_FEMALE = 2; // 女 + + /** + * 角色映射 + * @var array + */ + public static $roleMap = [ + self::ROLE_STUDENT => '学生', + self::ROLE_PARENT => '家长', + ]; + + /** + * 状态映射 + * @var array + */ + public static $statusMap = [ + self::STATUS_DISABLED => '禁用', + self::STATUS_NORMAL => '正常', + self::STATUS_ARREARS => '欠费', + self::STATUS_INACTIVE => '未激活', + ]; + + /** + * 性别映射 + * @var array + */ + public static $sexMap = [ + self::SEX_UNKNOWN => '不填', + self::SEX_MALE => '男', + self::SEX_FEMALE => '女', + ]; + + /** + * 获取学生班级关联 + * @return HasMany + */ + public function studentClasses(): HasMany + { + return $this->hasMany(StudentClass::class, 'student_id', 'id'); + } + + /** + * 获取学生所属班级(多对多) + * @return BelongsToMany + */ + public function classes(): BelongsToMany + { + return $this->belongsToMany(SchoolClass::class, 'student_class', 'student_id', 'class_id') + ->withPivot(['join_time', 'leave_time', 'status', 'remark']) + ->withTimestamps(); + } + + /** + * 获取家长关联(如果是学生) + * @return BelongsTo + */ + public function parent(): BelongsTo + { + return $this->belongsTo(Student::class, 'parent_id', 'id'); + } + + /** + * 获取子女关联(如果是家长) + * @return HasMany + */ + public function children(): HasMany + { + return $this->hasMany(Student::class, 'parent_id', 'id'); + } + + /** + * 只查询学生 + * @param $query + * @return mixed + */ + public function scopeStudents($query) + { + return $query->where('role', self::ROLE_STUDENT); + } + + /** + * 只查询家长 + * @param $query + * @return mixed + */ + public function scopeParents($query) + { + return $query->where('role', self::ROLE_PARENT); + } + + /** + * 只查询正常状态 + * @param $query + * @return mixed + */ + public function scopeNormal($query) + { + return $query->where('status', self::STATUS_NORMAL); + } + + /** + * 按年级过滤 + * @param $query + * @param int $gradeId + * @return mixed + */ + public function scopeByGrade($query, int $gradeId) + { + return $query->where('grade_id', $gradeId); + } + + /** + * 按性别过滤 + * @param $query + * @param int $sex + * @return mixed + */ + public function scopeBySex($query, int $sex) + { + return $query->where('sex', $sex); + } + + /** + * 获取角色名称 + * @return string + */ + public function getRoleNameAttribute(): string + { + return self::$roleMap[$this->role] ?? '未知'; + } + + /** + * 获取状态名称 + * @return string + */ + public function getStatusNameAttribute(): string + { + return self::$statusMap[$this->status] ?? '未知'; + } + + /** + * 获取性别名称 + * @return string + */ + public function getSexNameAttribute(): string + { + return self::$sexMap[$this->sex] ?? '未知'; + } + + /** + * 获取头像URL(如果为空则返回默认头像) + * @return string + */ + public function getAvatarUrlAttribute(): string + { + return $this->avatar ?: '/images/default-avatar.png'; + } + + /** + * 检查是否为学生 + * @return bool + */ + public function isStudent(): bool + { + return $this->role === self::ROLE_STUDENT; + } + + /** + * 检查是否为家长 + * @return bool + */ + public function isParent(): bool + { + return $this->role === self::ROLE_PARENT; + } + + /** + * 检查是否为正常状态 + * @return bool + */ + public function isNormal(): bool + { + return $this->status === self::STATUS_NORMAL; + } + + /** + * 检查是否为男性 + * @return bool + */ + public function isMale(): bool + { + return $this->sex === self::SEX_MALE; + } + + /** + * 检查是否为女性 + * @return bool + */ + public function isFemale(): bool + { + return $this->sex === self::SEX_FEMALE; + } + + /** + * 获取当前所在班级 + * @return SchoolClass|null + */ + public function getCurrentClass(): ?SchoolClass + { + $studentClass = $this->studentClasses() + ->where('status', StudentClass::STATUS_NORMAL) + ->whereNull('leave_time') + ->first(); + + return $studentClass ? $studentClass->schoolClass : null; + } + + /** + * 获取所有历史班级 + * @return \Illuminate\Database\Eloquent\Collection + */ + public function getHistoryClasses() + { + return $this->classes() + ->orderBy('student_class.join_time', 'desc') + ->get(); + } + + /** + * 密码加密 + * @param string $password + * @param string $salt + * @return string + */ + public static function encryptPassword(string $password, string $salt): string + { + return md5($password . $salt); + } + + /** + * 验证密码 + * @param string $password + * @return bool + */ + public function verifyPassword(string $password): bool + { + return $this->password === self::encryptPassword($password, $this->salt); + } + + /** + * 生成随机盐值 + * @return string + */ + public static function generateSalt(): string + { + return substr(md5(time() . mt_rand()), 0, 6); + } + + /** + * 获取完整信息(包含班级、家长等) + * @return array + */ + public function getFullInfo(): array + { + $info = $this->toArray(); + + // 添加角色名称 + $info['role_name'] = $this->role_name; + $info['status_name'] = $this->status_name; + $info['sex_name'] = $this->sex_name; + $info['avatar_url'] = $this->avatar_url; + + // 添加当前班级信息 + $currentClass = $this->getCurrentClass(); + $info['current_class'] = $currentClass ? $currentClass->toArray() : null; + + // 添加家长信息(如果是学生) + if ($this->isStudent() && $this->parent_id) { + $info['parent'] = $this->parent ? $this->parent->toArray() : null; + } + + // 添加子女信息(如果是家长) + if ($this->isParent()) { + $info['children'] = $this->children->toArray(); + } + + return $info; + } +} diff --git a/app/Models/Students/StudentClass.php b/app/Models/Students/StudentClass.php new file mode 100644 index 0000000..df6f7ef --- /dev/null +++ b/app/Models/Students/StudentClass.php @@ -0,0 +1,173 @@ + 'integer', + 'school_id' => 'integer', + 'class_id' => 'integer', + 'join_time' => 'datetime', + 'leave_time' => 'datetime', + 'status' => 'integer', + ]; + + /** + * 状态常量 + */ + const STATUS_NORMAL = 1; // 正常在读 + const STATUS_TRANSFERRED = 2; // 已转班 + const STATUS_GRADUATED = 3; // 已毕业 + const STATUS_DROPOUT = 4; // 退学 + + /** + * 状态映射 + */ + public static $statusMap = [ + self::STATUS_NORMAL => '正常在读', + self::STATUS_TRANSFERRED => '已转班', + self::STATUS_GRADUATED => '已毕业', + self::STATUS_DROPOUT => '退学', + ]; + + /** + * 关联学生 + */ + public function student() + { + return $this->belongsTo(Student::class, 'student_id'); + } + + /** + * 关联学校 + */ + public function school() + { + return $this->belongsTo(School::class, 'school_id'); + } + + /** + * 关联班级 + */ + public function schoolClass() + { + return $this->belongsTo(SchoolClass::class, 'class_id'); + } + + /** + * 根据学生ID查询 + */ + public function scopeByStudent($query, $studentId) + { + return $query->where('student_id', $studentId); + } + + /** + * 根据学校ID查询 + */ + public function scopeBySchool($query, $schoolId) + { + return $query->where('school_id', $schoolId); + } + + /** + * 根据班级ID查询 + */ + public function scopeByClass($query, $classId) + { + return $query->where('class_id', $classId); + } + + /** + * 根据状态查询 + */ + public function scopeByStatus($query, $status) + { + return $query->where('status', $status); + } + + /** + * 查询正常在读的学生 + */ + public function scopeNormal($query) + { + return $query->where('status', self::STATUS_NORMAL); + } + + /** + * 查询未离开的学生 + */ + public function scopeNotLeft($query) + { + return $query->whereNull('leave_time'); + } + + /** + * 获取状态名称 + */ + public function getStatusNameAttribute() + { + return self::$statusMap[$this->status] ?? '未知状态'; + } + + /** + * 检查是否正常在读 + */ + public function isNormal() + { + return $this->status === self::STATUS_NORMAL; + } + + /** + * 检查是否已离开 + */ + public function hasLeft() + { + return !is_null($this->leave_time); + } + + /** + * 获取在读时长(天数) + */ + public function getStudyDays(): int + { + if (!$this->join_time) { + return 0; + } + + $endTime = $this->leave_time ?? now(); + return $this->join_time->diffInDays($endTime); + } +} diff --git a/app/Models/System/SystemDictData.php b/app/Models/System/SystemDictData.php new file mode 100644 index 0000000..a2618bd --- /dev/null +++ b/app/Models/System/SystemDictData.php @@ -0,0 +1,217 @@ + 'integer', + 'sort' => 'integer', + 'status' => 'integer', + 'deleted' => 'boolean', + 'create_time' => 'datetime', + 'update_time' => 'datetime', + ]; + + // 状态常量 + const STATUS_NORMAL = 0; // 正常 + const STATUS_DISABLED = 1; // 停用 + + // 状态映射 + const STATUS_MAP = [ + self::STATUS_NORMAL => '正常', + self::STATUS_DISABLED => '停用', + ]; + + /** + * 获取状态文本 + * + * @return string + */ + public function getStatusTextAttribute(): string + { + return self::STATUS_MAP[$this->status] ?? '未知'; + } + + /** + * 获取字典类型 + * + * @return BelongsTo + */ + public function dictType(): BelongsTo + { + return $this->belongsTo(SystemDictType::class, 'dict_type', 'type'); + } + + /** + * 检查是否为正常状态 + * + * @return bool + */ + public function isNormal(): bool + { + return $this->status == self::STATUS_NORMAL; + } + + /** + * 检查是否为停用状态 + * + * @return bool + */ + public function isDisabled(): bool + { + return $this->status == self::STATUS_DISABLED; + } + + /** + * 启用字典数据 + * + * @return bool + */ + public function enable(): bool + { + return $this->update(['status' => self::STATUS_NORMAL]); + } + + /** + * 停用字典数据 + * + * @return bool + */ + public function disable(): bool + { + return $this->update(['status' => self::STATUS_DISABLED]); + } + + /** + * 检查字典值是否已存在 + * + * @param string $value + * @param string $dictType + * @param int|null $excludeId + * @return bool + */ + public static function valueExists(string $value, string $dictType, ?int $excludeId = null): bool + { + $query = static::where('value', $value)->where('dict_type', $dictType); + + if ($excludeId) { + $query->where('id', '!=', $excludeId); + } + + return $query->exists(); + } + + /** + * 根据字典类型和值获取字典数据 + * + * @param string $dictType + * @param string $value + * @return static|null + */ + public static function findByTypeAndValue(string $dictType, string $value): ?static + { + return static::where('dict_type', $dictType) + ->where('value', $value) + ->first(); + } + + /** + * 根据字典类型获取所有数据 + * + * @param string $dictType + * @param bool $onlyActive + * @return \Illuminate\Database\Eloquent\Collection + */ + public static function getByType(string $dictType, bool $onlyActive = false) + { + $query = static::where('dict_type', $dictType)->orderBy('sort'); + + if ($onlyActive) { + $query->where('status', self::STATUS_NORMAL); + } + + return $query->get(); + } + + /** + * 根据字典类型获取键值对数组 + * + * @param string $dictType + * @param bool $onlyActive + * @return array + */ + public static function getOptionsByType(string $dictType, bool $onlyActive = true): array + { + $query = static::where('dict_type', $dictType)->orderBy('sort'); + + if ($onlyActive) { + $query->where('status', self::STATUS_NORMAL); + } + + return $query->pluck('label', 'value')->toArray(); + } + + /** + * 根据字典类型和值获取标签 + * + * @param string $dictType + * @param string $value + * @return string|null + */ + public static function getLabelByTypeAndValue(string $dictType, string $value): ?string + { + $dictData = static::findByTypeAndValue($dictType, $value); + return $dictData ? $dictData->label : null; + } + + /** + * 获取字典数据的下一个排序值 + * + * @param string $dictType + * @return int + */ + public static function getNextSort(string $dictType): int + { + $maxSort = static::where('dict_type', $dictType)->max('sort'); + return $maxSort ? $maxSort + 1 : 1; + } +} diff --git a/app/Models/System/SystemDictType.php b/app/Models/System/SystemDictType.php new file mode 100644 index 0000000..0ef3b4a --- /dev/null +++ b/app/Models/System/SystemDictType.php @@ -0,0 +1,165 @@ + 'integer', + 'status' => 'integer', + 'deleted' => 'boolean', + 'create_time' => 'datetime', + 'update_time' => 'datetime', + 'deleted_time' => 'datetime', + ]; + + // 状态常量 + const STATUS_NORMAL = 0; // 正常 + const STATUS_DISABLED = 1; // 停用 + + // 状态映射 + const STATUS_MAP = [ + self::STATUS_NORMAL => '正常', + self::STATUS_DISABLED => '停用', + ]; + + /** + * 获取状态文本 + * + * @return string + */ + public function getStatusTextAttribute(): string + { + return self::STATUS_MAP[$this->status] ?? '未知'; + } + + /** + * 获取字典数据 + * + * @return HasMany + */ + public function dictData(): HasMany + { + return $this->hasMany(SystemDictData::class, 'dict_type', 'type'); + } + + /** + * 获取启用的字典数据 + * + * @return HasMany + */ + public function activeDictData(): HasMany + { + return $this->dictData()->where('status', SystemDictData::STATUS_NORMAL); + } + + /** + * 检查是否为正常状态 + * + * @return bool + */ + public function isNormal(): bool + { + return $this->status == self::STATUS_NORMAL; + } + + /** + * 检查是否为停用状态 + * + * @return bool + */ + public function isDisabled(): bool + { + return $this->status == self::STATUS_DISABLED; + } + + /** + * 启用字典类型 + * + * @return bool + */ + public function enable(): bool + { + return $this->update(['status' => self::STATUS_NORMAL]); + } + + /** + * 停用字典类型 + * + * @return bool + */ + public function disable(): bool + { + return $this->update(['status' => self::STATUS_DISABLED]); + } + + /** + * 检查字典类型是否已存在 + * + * @param string $type + * @param int|null $excludeId + * @return bool + */ + public static function typeExists(string $type, ?int $excludeId = null): bool + { + $query = static::where('type', $type); + + if ($excludeId) { + $query->where('id', '!=', $excludeId); + } + + return $query->exists(); + } + + /** + * 根据类型获取字典类型 + * + * @param string $type + * @return static|null + */ + public static function findByType(string $type): ?static + { + return static::where('type', $type)->first(); + } + + /** + * 获取所有正常状态的字典类型 + * + * @return \Illuminate\Database\Eloquent\Collection + */ + public static function getNormalTypes() + { + return static::where('status', self::STATUS_NORMAL)->get(); + } +} diff --git a/app/Models/System/SystemUserSchoolCampus.php b/app/Models/System/SystemUserSchoolCampus.php new file mode 100644 index 0000000..7a770d4 --- /dev/null +++ b/app/Models/System/SystemUserSchoolCampus.php @@ -0,0 +1,85 @@ + 'integer', + 'schoolid' => 'integer', + 'campusid' => 'integer', + ]; + + /** + * 关联用户 + */ + public function user() + { + return $this->belongsTo(User::class, 'userid'); + } + + /** + * 关联学校 + */ + public function school() + { + return $this->belongsTo(School::class, 'schoolid'); + } + + /** + * 关联校区 + */ + public function campus() + { + return $this->belongsTo(SchoolCampus::class, 'campusid'); + } + + /** + * 根据用户ID查询 + */ + public function scopeByUser($query, $userId) + { + return $query->where('userid', $userId); + } + + /** + * 根据学校ID查询 + */ + public function scopeBySchool($query, $schoolId) + { + return $query->where('schoolid', $schoolId); + } + + /** + * 根据校区ID查询 + */ + public function scopeByCampus($query, $campusId) + { + return $query->where('campusid', $campusId); + } +} diff --git a/app/Models/Teachers/TeacherClass.php b/app/Models/Teachers/TeacherClass.php new file mode 100644 index 0000000..6db948f --- /dev/null +++ b/app/Models/Teachers/TeacherClass.php @@ -0,0 +1,99 @@ + 'integer', + 'school_id' => 'integer', + 'grade_id' => 'integer', + 'class_id' => 'integer', + 'headman' => 'integer', + ]; + + /** + * 关联老师(系统用户) + */ + public function teacher() + { + return $this->belongsTo(User::class, 'teacher_id'); + } + + /** + * 关联学校 + */ + public function school() + { + return $this->belongsTo(School::class, 'school_id'); + } + + /** + * 关联班级 + */ + public function schoolClass() + { + return $this->belongsTo(SchoolClass::class, 'class_id'); + } + + /** + * 根据老师ID查询 + */ + public function scopeByTeacher($query, $teacherId) + { + return $query->where('teacher_id', $teacherId); + } + + /** + * 根据学校ID查询 + */ + public function scopeBySchool($query, $schoolId) + { + return $query->where('school_id', $schoolId); + } + + /** + * 根据班级ID查询 + */ + public function scopeByClass($query, $classId) + { + return $query->where('class_id', $classId); + } + + /** + * 查询班主任 + */ + public function scopeHeadman($query) + { + return $query->where('headman', 1); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 96685b1..63f44a4 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -5,6 +5,8 @@ namespace App\Models; use App\Models\BaseModel; use App\Models\System\SystemRole; use App\Models\System\SystemUserRole; +use App\Models\System\SystemUserSchoolCampus; +use App\Models\Teachers\TeacherClass; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -172,4 +174,68 @@ class User extends Authenticatable { return $this->tenant_id; } + + /** + * 用户的学校校区关联 + */ + public function schoolCampuses() + { + return $this->hasMany(SystemUserSchoolCampus::class, 'userid'); + } + + /** + * 用户的班级关联(作为老师) + */ + public function teacherClasses() + { + return $this->hasMany(TeacherClass::class, 'teacher_id'); + } + + /** + * 获取用户管理的学校ID列表 + */ + public function getSchoolIds(): array + { + return $this->schoolCampuses()->pluck('schoolid')->unique()->toArray(); + } + + /** + * 获取用户管理的校区ID列表 + */ + public function getCampusIds(): array + { + return $this->schoolCampuses()->pluck('campusid')->toArray(); + } + + /** + * 获取用户管理的班级ID列表 + */ + public function getClassIds(): array + { + return $this->teacherClasses()->pluck('class_id')->toArray(); + } + + /** + * 检查用户是否管理指定学校 + */ + public function managesSchool(int $schoolId): bool + { + return $this->schoolCampuses()->where('schoolid', $schoolId)->exists(); + } + + /** + * 检查用户是否管理指定校区 + */ + public function managesCampus(int $campusId): bool + { + return $this->schoolCampuses()->where('campusid', $campusId)->exists(); + } + + /** + * 检查用户是否管理指定班级 + */ + public function managesClass(int $classId): bool + { + return $this->teacherClasses()->where('class_id', $classId)->exists(); + } } diff --git a/app/Services/Schools/SchoolCampusService.php b/app/Services/Schools/SchoolCampusService.php new file mode 100644 index 0000000..7f453cd --- /dev/null +++ b/app/Services/Schools/SchoolCampusService.php @@ -0,0 +1,150 @@ +where(function ($q) use ($params) { + $q->where('name', 'like', '%' . $params['keyword'] . '%') + ->orWhere('alias', 'like', '%' . $params['keyword'] . '%') + ->orWhere('code', 'like', '%' . $params['keyword'] . '%'); + }); + } + + // 状态筛选 + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + + // 学校筛选 + if (!empty($params['school_id'])) { + $query->where('school_id', $params['school_id']); + } + + // 包含学校信息 + $query->with(['school:id,name,alias']); + + // 排序 + $query->orderBy('school_id', 'asc')->orderBy('id', 'desc'); + + return $query->paginate($params['page_size'] ?? 15); + } + + /** + * 获取简单列表(用于下拉选择等) + */ + public function getSimpleList(?int $schoolId = null): array + { + $query = SchoolCampus::select('id', 'name', 'alias', 'code', 'school_id') + ->active() + ->with(['school:id,name']); + + if ($schoolId) { + $query->where('school_id', $schoolId); + } + + return $query->get()->toArray(); + } + + /** + * 获取指定用户管理的校区列表 + */ + public function getUserCampuses(int $userId): array + { + $campusIds = \App\Models\User::find($userId)->getCampusIds(); + + return SchoolCampus::whereIn('id', $campusIds) + ->active() + ->with(['school:id,name']) + ->get() + ->toArray(); + } + + /** + * 获取校区的班级列表 + */ + public function getCampusClasses(int $campusId): array + { + $campus = SchoolCampus::findOrFail($campusId); + + return $campus->classes() + ->active() + ->select('id', 'name', 'code', 'grade_id', 'number') + ->orderBy('grade_id', 'asc') + ->orderBy('number', 'asc') + ->get() + ->toArray(); + } + + /** + * 获取校区统计信息 + */ + public function getCampusStats(int $campusId): array + { + $campus = SchoolCampus::findOrFail($campusId); + + return [ + 'class_count' => $campus->classes()->active()->count(), + 'student_count' => \App\Models\Students\StudentClass::whereHas('schoolClass', function ($query) use ($campusId) { + $query->where('campus_id', $campusId); + })->count(), + 'teacher_count' => \App\Models\Teachers\TeacherClass::whereHas('schoolClass', function ($query) use ($campusId) { + $query->where('campus_id', $campusId); + })->distinct('teacher_id')->count(), + ]; + } + + /** + * 创建前的特殊处理 + */ + protected function beforeCreate(array &$data): void + { + // 设置默认值 + $data['status'] = $data['status'] ?? 1; + $data['self_support'] = $data['self_support'] ?? 0; + $data['is_open_user_login'] = $data['is_open_user_login'] ?? 0; + + // 验证学校是否存在 + if (!empty($data['school_id'])) { + $school = \App\Models\Schools\School::find($data['school_id']); + if (!$school) { + throw new \InvalidArgumentException('指定的学校不存在'); + } + } + } + + /** + * 更新前的特殊处理 + */ + protected function beforeUpdate(array &$data): void + { + // 移除不允许更新的字段 + unset($data['id']); + + // 验证学校是否存在 + if (!empty($data['school_id'])) { + $school = \App\Models\Schools\School::find($data['school_id']); + if (!$school) { + throw new \InvalidArgumentException('指定的学校不存在'); + } + } + } +} diff --git a/app/Services/Schools/SchoolClassService.php b/app/Services/Schools/SchoolClassService.php new file mode 100644 index 0000000..e25a618 --- /dev/null +++ b/app/Services/Schools/SchoolClassService.php @@ -0,0 +1,208 @@ +where(function ($q) use ($params) { + $q->where('name', 'like', '%' . $params['keyword'] . '%') + ->orWhere('code', 'like', '%' . $params['keyword'] . '%'); + }); + } + + // 状态筛选 + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + + // 学校筛选 + if (!empty($params['school_id'])) { + $query->where('school_id', $params['school_id']); + } + + // 校区筛选 + if (!empty($params['campus_id'])) { + $query->where('campus_id', $params['campus_id']); + } + + // 年级筛选 + if (!empty($params['grade_id'])) { + $query->where('grade_id', $params['grade_id']); + } + + // 班级类型筛选 + if (isset($params['type'])) { + $query->where('type', $params['type']); + } + + // 包含学校和校区信息 + $query->with(['school:id,name,alias', 'campus:id,name,alias']); + + // 排序 + $query->orderBy('school_id', 'asc') + ->orderBy('campus_id', 'asc') + ->orderBy('grade_id', 'asc') + ->orderBy('number', 'asc'); + + return $query->paginate($params['page_size'] ?? 15); + } + + /** + * 获取简单列表(用于下拉选择等) + */ + public function getSimpleList(?int $schoolId = null, ?int $campusId = null): array + { + $query = SchoolClass::select('id', 'name', 'code', 'school_id', 'campus_id', 'grade_id', 'number') + ->active() + ->with(['school:id,name', 'campus:id,name']); + + if ($schoolId) { + $query->where('school_id', $schoolId); + } + + if ($campusId) { + $query->where('campus_id', $campusId); + } + + return $query->orderBy('grade_id', 'asc') + ->orderBy('number', 'asc') + ->get() + ->toArray(); + } + + /** + * 获取指定用户管理的班级列表 + */ + public function getUserClasses(int $userId): array + { + $classIds = \App\Models\User::find($userId)->getClassIds(); + + return SchoolClass::whereIn('id', $classIds) + ->active() + ->with(['school:id,name', 'campus:id,name']) + ->get() + ->toArray(); + } + + /** + * 获取班级的学生列表 + */ + public function getClassStudents(int $classId): array + { + $class = SchoolClass::findOrFail($classId); + + return $class->studentClasses() + ->with(['student:id,name,sno']) // 假设有学生模型 + ->get() + ->toArray(); + } + + /** + * 获取班级的老师列表 + */ + public function getClassTeachers(int $classId): array + { + $class = SchoolClass::findOrFail($classId); + + return $class->teacherClasses() + ->with(['teacher:id,username,nickname']) + ->get() + ->toArray(); + } + + /** + * 获取班级统计信息 + */ + public function getClassStats(int $classId): array + { + $class = SchoolClass::findOrFail($classId); + + return [ + 'student_count' => $class->studentClasses()->count(), + 'teacher_count' => $class->teacherClasses()->count(), + 'headman_count' => $class->teacherClasses()->headman()->count(), + ]; + } + + /** + * 创建前的特殊处理 + */ + protected function beforeCreate(array &$data): void + { + // 设置默认值 + $data['status'] = $data['status'] ?? 1; + $data['type'] = $data['type'] ?? 0; + $data['level'] = $data['level'] ?? 0; + $data['number'] = $data['number'] ?? 0; + $data['is_open_user_login'] = $data['is_open_user_login'] ?? 0; + + // 验证学校是否存在 + if (!empty($data['school_id'])) { + $school = \App\Models\Schools\School::find($data['school_id']); + if (!$school) { + throw new \InvalidArgumentException('指定的学校不存在'); + } + } + + // 验证校区是否存在 + if (!empty($data['campus_id'])) { + $campus = \App\Models\Schools\SchoolCampus::find($data['campus_id']); + if (!$campus) { + throw new \InvalidArgumentException('指定的校区不存在'); + } + + // 验证校区是否属于指定学校 + if ($campus->school_id !== $data['school_id']) { + throw new \InvalidArgumentException('指定的校区不属于该学校'); + } + } + } + + /** + * 更新前的特殊处理 + */ + protected function beforeUpdate(array &$data): void + { + // 移除不允许更新的字段 + unset($data['id']); + + // 验证学校是否存在 + if (!empty($data['school_id'])) { + $school = \App\Models\Schools\School::find($data['school_id']); + if (!$school) { + throw new \InvalidArgumentException('指定的学校不存在'); + } + } + + // 验证校区是否存在 + if (!empty($data['campus_id'])) { + $campus = \App\Models\Schools\SchoolCampus::find($data['campus_id']); + if (!$campus) { + throw new \InvalidArgumentException('指定的校区不存在'); + } + + // 验证校区是否属于指定学校 + if (isset($data['school_id']) && $campus->school_id !== $data['school_id']) { + throw new \InvalidArgumentException('指定的校区不属于该学校'); + } + } + } +} diff --git a/app/Services/Schools/SchoolService.php b/app/Services/Schools/SchoolService.php new file mode 100644 index 0000000..5d73108 --- /dev/null +++ b/app/Services/Schools/SchoolService.php @@ -0,0 +1,160 @@ +where(function ($q) use ($params) { + $q->where('name', 'like', '%' . $params['keyword'] . '%') + ->orWhere('alias', 'like', '%' . $params['keyword'] . '%') + ->orWhere('code', 'like', '%' . $params['keyword'] . '%'); + }); + } + + // 状态筛选 + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + + // 学校类型筛选 + if (isset($params['type'])) { + $query->where('type', $params['type']); + } + + // 省市区筛选 + if (!empty($params['province_id'])) { + $query->where('province_id', $params['province_id']); + } + if (!empty($params['city_id'])) { + $query->where('city_id', $params['city_id']); + } + if (!empty($params['district_id'])) { + $query->where('district_id', $params['district_id']); + } + + // 学段筛选 + if (!empty($params['grade_period'])) { + $query->where('grade_period', $params['grade_period']); + } + + // 排序 + $query->ordered(); + + return $query->paginate($params['page_size'] ?? 15); + } + + /** + * 获取简单列表(用于下拉选择等) + */ + public function getSimpleList(): array + { + return School::select('id', 'name', 'alias', 'code') + ->active() + ->ordered() + ->get() + ->toArray(); + } + + /** + * 获取指定用户管理的学校列表 + */ + public function getUserSchools(int $userId): array + { + $schoolIds = \App\Models\User::find($userId)->getSchoolIds(); + + return School::whereIn('id', $schoolIds) + ->active() + ->ordered() + ->get() + ->toArray(); + } + + /** + * 获取学校的校区列表 + */ + public function getSchoolCampuses(int $schoolId): array + { + $school = School::findOrFail($schoolId); + + return $school->campuses() + ->active() + ->select('id', 'name', 'alias', 'code') + ->get() + ->toArray(); + } + + /** + * 获取学校的班级列表 + */ + public function getSchoolClasses(int $schoolId, ?int $campusId = null): array + { + $school = School::findOrFail($schoolId); + + $query = $school->classes()->active(); + + if ($campusId) { + $query->where('campus_id', $campusId); + } + + return $query->select('id', 'name', 'code', 'campus_id', 'grade_id', 'number') + ->orderBy('grade_id', 'asc') + ->orderBy('number', 'asc') + ->get() + ->toArray(); + } + + /** + * 获取学校统计信息 + */ + public function getSchoolStats(int $schoolId): array + { + $school = School::findOrFail($schoolId); + + return [ + 'campus_count' => $school->campuses()->active()->count(), + 'class_count' => $school->classes()->active()->count(), + 'student_count' => \App\Models\Students\StudentClass::where('school_id', $schoolId)->count(), + 'teacher_count' => \App\Models\Teachers\TeacherClass::where('school_id', $schoolId)->distinct('teacher_id')->count(), + ]; + } + + /** + * 创建前的特殊处理 + */ + protected function beforeCreate(array &$data): void + { + // 设置默认值 + $data['status'] = $data['status'] ?? 1; + $data['show_order'] = $data['show_order'] ?? 999; + $data['sno_automatic'] = $data['sno_automatic'] ?? 0; + $data['max_sno'] = $data['max_sno'] ?? 2; + $data['is_open_user_login'] = $data['is_open_user_login'] ?? 0; + } + + /** + * 更新前的特殊处理 + */ + protected function beforeUpdate(array &$data): void + { + // 移除不允许更新的字段 + unset($data['id']); + } +} diff --git a/app/Services/Students/StudentClassService.php b/app/Services/Students/StudentClassService.php new file mode 100644 index 0000000..672216d --- /dev/null +++ b/app/Services/Students/StudentClassService.php @@ -0,0 +1,206 @@ +where('student_id', $params['student_id']); + } + + // 学校筛选 + if (!empty($params['school_id'])) { + $query->where('school_id', $params['school_id']); + } + + // 班级筛选 + if (!empty($params['class_id'])) { + $query->where('class_id', $params['class_id']); + } + + // 包含关联信息 + $query->with([ + 'school:id,name,alias', + 'schoolClass:id,name,code,campus_id,grade_id,number' + ]); + + // 排序 + $query->orderBy('school_id', 'asc') + ->orderBy('class_id', 'asc') + ->orderBy('id', 'desc'); + + return $query->paginate($params['page_size'] ?? 15); + } + + /** + * 获取学生的班级列表 + */ + public function getStudentClasses(int $studentId): array + { + return StudentClass::where('student_id', $studentId) + ->with([ + 'school:id,name,alias', + 'schoolClass:id,name,code,campus_id,grade_id,number' + ]) + ->get() + ->toArray(); + } + + /** + * 获取班级的学生列表 + */ + public function getClassStudents(int $classId): array + { + return StudentClass::where('class_id', $classId) + ->with([ + 'school:id,name,alias' + ]) + ->get() + ->toArray(); + } + + /** + * 获取学校的学生列表 + */ + public function getSchoolStudents(int $schoolId): array + { + return StudentClass::where('school_id', $schoolId) + ->with([ + 'schoolClass:id,name,code,campus_id,grade_id,number' + ]) + ->get() + ->toArray(); + } + + /** + * 批量分配学生到班级 + */ + public function batchAssignStudentsToClass(array $studentIds, int $classId): array + { + $class = \App\Models\Schools\SchoolClass::findOrFail($classId); + $results = []; + + foreach ($studentIds as $studentId) { + // 检查是否已经存在关联 + $exists = StudentClass::where('student_id', $studentId) + ->where('class_id', $classId) + ->exists(); + + if (!$exists) { + $data = [ + 'student_id' => $studentId, + 'school_id' => $class->school_id, + 'class_id' => $classId, + ]; + + $result = $this->create($data); + $results[] = $result; + } + } + + return $results; + } + + /** + * 批量移除学生班级关联 + */ + public function batchRemoveStudentsFromClass(array $studentIds, int $classId): int + { + return StudentClass::whereIn('student_id', $studentIds) + ->where('class_id', $classId) + ->delete(); + } + + /** + * 转移学生到新班级 + */ + public function transferStudentToClass(int $studentId, int $oldClassId, int $newClassId): bool + { + $newClass = \App\Models\Schools\SchoolClass::findOrFail($newClassId); + + // 删除旧的关联 + StudentClass::where('student_id', $studentId) + ->where('class_id', $oldClassId) + ->delete(); + + // 创建新的关联 + $data = [ + 'student_id' => $studentId, + 'school_id' => $newClass->school_id, + 'class_id' => $newClassId, + ]; + + $this->create($data); + + return true; + } + + /** + * 获取学生班级统计信息 + */ + public function getStudentClassStats(int $studentId): array + { + $classes = StudentClass::where('student_id', $studentId) + ->with(['school:id,name', 'schoolClass:id,name']) + ->get(); + + return [ + 'total_classes' => $classes->count(), + 'schools' => $classes->pluck('school')->unique('id')->values()->toArray(), + 'classes' => $classes->pluck('schoolClass')->toArray(), + ]; + } + + /** + * 创建前的特殊处理 + */ + protected function beforeCreate(array &$data): void + { + // 验证学校是否存在 + if (!empty($data['school_id'])) { + $school = \App\Models\Schools\School::find($data['school_id']); + if (!$school) { + throw new \InvalidArgumentException('指定的学校不存在'); + } + } + + // 验证班级是否存在 + if (!empty($data['class_id'])) { + $class = \App\Models\Schools\SchoolClass::find($data['class_id']); + if (!$class) { + throw new \InvalidArgumentException('指定的班级不存在'); + } + + // 验证班级是否属于指定学校 + if ($class->school_id !== $data['school_id']) { + throw new \InvalidArgumentException('指定的班级不属于该学校'); + } + } + + // 检查是否已经存在关联 + $exists = StudentClass::where('student_id', $data['student_id']) + ->where('class_id', $data['class_id']) + ->exists(); + + if ($exists) { + throw new \InvalidArgumentException('该学生已经在此班级中'); + } + } +} diff --git a/app/Services/Students/StudentService.php b/app/Services/Students/StudentService.php new file mode 100644 index 0000000..af84c7c --- /dev/null +++ b/app/Services/Students/StudentService.php @@ -0,0 +1,513 @@ +where('username', 'like', '%' . $params['username'] . '%'); + } + + if (!empty($params['real_name'])) { + $query->where('real_name', 'like', '%' . $params['real_name'] . '%'); + } + + if (!empty($params['email'])) { + $query->where('email', 'like', '%' . $params['email'] . '%'); + } + + if (!empty($params['phone_number'])) { + $query->where('phone_number', 'like', '%' . $params['phone_number'] . '%'); + } + + if (isset($params['role'])) { + $query->where('role', $params['role']); + } + + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + + if (isset($params['sex'])) { + $query->where('sex', $params['sex']); + } + + if (!empty($params['grade_id'])) { + $query->where('grade_id', $params['grade_id']); + } + + if (!empty($params['parent_id'])) { + $query->where('parent_id', $params['parent_id']); + } + + if (!empty($params['class_id'])) { + $query->whereHas('studentClasses', function ($q) use ($params) { + $q->where('class_id', $params['class_id']); + $q->where('status', StudentClass::STATUS_NORMAL); + }); + } + + // 排序 + $query->orderBy('id', 'desc'); + + return $this->paginate($query, $params); + } + + /** + * 获取学生详情 + * @param int $id + * @return array + */ + public function getStudentDetail(int $id): array + { + $student = Student::find($id); + if (!$student) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '学生不存在'); + } + + return $student->getFullInfo(); + } + + /** + * 创建学生 + * @param array $data + * @return array + */ + public function createStudent(array $data): array + { + // 检查用户名是否已存在 + if (Student::where('username', $data['username'])->exists()) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '用户名已存在'); + } + + // 检查邮箱是否已存在 + if (!empty($data['email']) && Student::where('email', $data['email'])->exists()) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '邮箱已存在'); + } + + // 检查手机号是否已存在 + if (!empty($data['phone_number']) && Student::where('phone_number', $data['phone_number'])->exists()) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '手机号已存在'); + } + + // 生成密码盐值 + $salt = Student::generateSalt(); + $data['salt'] = $salt; + $data['password'] = Student::encryptPassword($data['password'], $salt); + $data['reg_time'] = now(); + $data['reg_ip'] = request()->ip(); + + $student = Student::create($data); + + Log::info('创建学生', [ + 'student_id' => $student->id, + 'username' => $student->username, + 'operator' => $this->getCurrentUser()['id'] ?? 0 + ]); + + return $student->getFullInfo(); + } + + /** + * 更新学生 + * @param int $id + * @param array $data + * @return array + */ + public function updateStudent(int $id, array $data): array + { + $student = Student::find($id); + if (!$student) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '学生不存在'); + } + + // 检查用户名是否已存在(排除自己) + if (!empty($data['username']) && Student::where('username', $data['username'])->where('id', '!=', $id)->exists()) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '用户名已存在'); + } + + // 检查邮箱是否已存在(排除自己) + if (!empty($data['email']) && Student::where('email', $data['email'])->where('id', '!=', $id)->exists()) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '邮箱已存在'); + } + + // 检查手机号是否已存在(排除自己) + if (!empty($data['phone_number']) && Student::where('phone_number', $data['phone_number'])->where('id', '!=', $id)->exists()) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '手机号已存在'); + } + + // 如果修改密码,需要重新加密 + if (!empty($data['password'])) { + $salt = Student::generateSalt(); + $data['salt'] = $salt; + $data['password'] = Student::encryptPassword($data['password'], $salt); + } + + $student->update($data); + + Log::info('更新学生', [ + 'student_id' => $student->id, + 'username' => $student->username, + 'operator' => $this->getCurrentUser()['id'] ?? 0 + ]); + + return $student->refresh()->getFullInfo(); + } + + /** + * 删除学生 + * @param int $id + * @return bool + */ + public function deleteStudent(int $id): bool + { + $student = Student::find($id); + if (!$student) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '学生不存在'); + } + + // 检查是否有关联的班级记录 + if ($student->studentClasses()->exists()) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '学生有关联的班级记录,无法删除'); + } + + // 检查是否有子女关联 + if ($student->children()->exists()) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '该家长有关联的子女,无法删除'); + } + + $student->delete(); + + Log::info('删除学生', [ + 'student_id' => $id, + 'username' => $student->username, + 'operator' => $this->getCurrentUser()['id'] ?? 0 + ]); + + return true; + } + + /** + * 重置学生密码 + * @param int $id + * @param string $newPassword + * @return bool + */ + public function resetPassword(int $id, string $newPassword): bool + { + $student = Student::find($id); + if (!$student) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '学生不存在'); + } + + $salt = Student::generateSalt(); + $student->update([ + 'password' => Student::encryptPassword($newPassword, $salt), + 'salt' => $salt + ]); + + Log::info('重置学生密码', [ + 'student_id' => $id, + 'username' => $student->username, + 'operator' => $this->getCurrentUser()['id'] ?? 0 + ]); + + return true; + } + + /** + * 更新学生状态 + * @param int $id + * @param int $status + * @return array + */ + public function updateStatus(int $id, int $status): array + { + $student = Student::find($id); + if (!$student) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '学生不存在'); + } + + $student->update(['status' => $status]); + + Log::info('更新学生状态', [ + 'student_id' => $id, + 'username' => $student->username, + 'old_status' => $student->status, + 'new_status' => $status, + 'operator' => $this->getCurrentUser()['id'] ?? 0 + ]); + + return $student->refresh()->getFullInfo(); + } + + /** + * 获取学生的班级历史 + * @param int $id + * @return array + */ + public function getStudentClassHistory(int $id): array + { + $student = Student::find($id); + if (!$student) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '学生不存在'); + } + + $classes = $student->studentClasses() + ->with(['schoolClass.school', 'schoolClass.campus']) + ->orderBy('join_time', 'desc') + ->get(); + + return $classes->map(function ($studentClass) { + return [ + 'id' => $studentClass->id, + 'school_name' => $studentClass->schoolClass->school->name ?? '', + 'campus_name' => $studentClass->schoolClass->campus->name ?? '', + 'class_name' => $studentClass->schoolClass->name ?? '', + 'join_time' => $studentClass->join_time, + 'leave_time' => $studentClass->leave_time, + 'status' => $studentClass->status, + 'status_name' => $studentClass->status_name, + 'study_days' => $studentClass->getStudyDays(), + 'remark' => $studentClass->remark + ]; + })->toArray(); + } + + /** + * 将学生分配到班级 + * @param int $studentId + * @param int $classId + * @param array $data + * @return array + */ + public function assignToClass(int $studentId, int $classId, array $data = []): array + { + $student = Student::find($studentId); + if (!$student) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '学生不存在'); + } + + $class = SchoolClass::find($classId); + if (!$class) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '班级不存在'); + } + + // 检查是否已经在该班级中 + $existingRecord = StudentClass::where('student_id', $studentId) + ->where('class_id', $classId) + ->where('status', StudentClass::STATUS_NORMAL) + ->first(); + + if ($existingRecord) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '学生已在该班级中'); + } + + // 将学生在其他班级的记录设为转班状态 + StudentClass::where('student_id', $studentId) + ->where('status', StudentClass::STATUS_NORMAL) + ->update([ + 'status' => StudentClass::STATUS_TRANSFERRED, + 'leave_time' => now(), + 'remark' => '转班' + ]); + + // 创建新的班级记录 + $studentClass = StudentClass::create([ + 'student_id' => $studentId, + 'school_id' => $class->school_id, + 'class_id' => $classId, + 'join_time' => $data['join_time'] ?? now(), + 'status' => StudentClass::STATUS_NORMAL, + 'remark' => $data['remark'] ?? '' + ]); + + Log::info('学生分配到班级', [ + 'student_id' => $studentId, + 'class_id' => $classId, + 'operator' => $this->getCurrentUser()['id'] ?? 0 + ]); + + return $studentClass->load(['student', 'schoolClass'])->toArray(); + } + + /** + * 从班级中移除学生 + * @param int $studentId + * @param int $classId + * @param array $data + * @return bool + */ + public function removeFromClass(int $studentId, int $classId, array $data = []): bool + { + $studentClass = StudentClass::where('student_id', $studentId) + ->where('class_id', $classId) + ->where('status', StudentClass::STATUS_NORMAL) + ->first(); + + if (!$studentClass) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '学生不在该班级中'); + } + + $studentClass->update([ + 'status' => $data['status'] ?? StudentClass::STATUS_TRANSFERRED, + 'leave_time' => $data['leave_time'] ?? now(), + 'remark' => $data['remark'] ?? '移除' + ]); + + Log::info('从班级移除学生', [ + 'student_id' => $studentId, + 'class_id' => $classId, + 'operator' => $this->getCurrentUser()['id'] ?? 0 + ]); + + return true; + } + + /** + * 获取家长的子女列表 + * @param int $parentId + * @return array + */ + public function getParentChildren(int $parentId): array + { + $parent = Student::find($parentId); + if (!$parent) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '家长不存在'); + } + + if (!$parent->isParent()) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '用户不是家长'); + } + + return $parent->children()->with(['studentClasses.schoolClass'])->get()->map(function ($child) { + return $child->getFullInfo(); + })->toArray(); + } + + /** + * 绑定家长和子女关系 + * @param int $parentId + * @param int $childId + * @return bool + */ + public function bindParentChild(int $parentId, int $childId): bool + { + $parent = Student::find($parentId); + if (!$parent) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '家长不存在'); + } + + if (!$parent->isParent()) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '用户不是家长'); + } + + $child = Student::find($childId); + if (!$child) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '学生不存在'); + } + + if (!$child->isStudent()) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '用户不是学生'); + } + + if ($child->parent_id) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '学生已有家长'); + } + + $child->update(['parent_id' => $parentId]); + + Log::info('绑定家长和子女关系', [ + 'parent_id' => $parentId, + 'child_id' => $childId, + 'operator' => $this->getCurrentUser()['id'] ?? 0 + ]); + + return true; + } + + /** + * 解除家长和子女关系 + * @param int $childId + * @return bool + */ + public function unbindParentChild(int $childId): bool + { + $child = Student::find($childId); + if (!$child) { + throw new BusinessException(ResponseEnum::CLIENT_NOT_FOUND, '学生不存在'); + } + + if (!$child->parent_id) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '学生没有家长'); + } + + $parentId = $child->parent_id; + $child->update(['parent_id' => null]); + + Log::info('解除家长和子女关系', [ + 'parent_id' => $parentId, + 'child_id' => $childId, + 'operator' => $this->getCurrentUser()['id'] ?? 0 + ]); + + return true; + } + + /** + * 获取学生统计信息 + * @return array + */ + public function getStudentStatistics(): array + { + $totalStudents = Student::students()->count(); + $totalParents = Student::parents()->count(); + $normalStudents = Student::students()->where('status', Student::STATUS_NORMAL)->count(); + $maleStudents = Student::students()->where('sex', Student::SEX_MALE)->count(); + $femaleStudents = Student::students()->where('sex', Student::SEX_FEMALE)->count(); + + return [ + 'total_students' => $totalStudents, + 'total_parents' => $totalParents, + 'normal_students' => $normalStudents, + 'male_students' => $maleStudents, + 'female_students' => $femaleStudents, + 'status_distribution' => [ + 'normal' => Student::where('status', Student::STATUS_NORMAL)->count(), + 'disabled' => Student::where('status', Student::STATUS_DISABLED)->count(), + 'arrears' => Student::where('status', Student::STATUS_ARREARS)->count(), + 'inactive' => Student::where('status', Student::STATUS_INACTIVE)->count(), + ] + ]; + } +} diff --git a/app/Services/System/SystemDictDataService.php b/app/Services/System/SystemDictDataService.php new file mode 100644 index 0000000..89cafc4 --- /dev/null +++ b/app/Services/System/SystemDictDataService.php @@ -0,0 +1,421 @@ +where('label', 'like', '%' . $params['label'] . '%'); + } + + // 字典值搜索 + if (!empty($params['value'])) { + $query->where('value', 'like', '%' . $params['value'] . '%'); + } + + // 字典类型筛选 + if (!empty($params['dict_type'])) { + $query->where('dict_type', $params['dict_type']); + } + + // 状态筛选 + if (isset($params['status']) && $params['status'] !== '') { + $query->where('status', $params['status']); + } + + // 颜色类型筛选 + if (!empty($params['color_type'])) { + $query->where('color_type', $params['color_type']); + } + + // 创建时间范围 + if (!empty($params['create_time_start'])) { + $query->where('create_time', '>=', $params['create_time_start']); + } + if (!empty($params['create_time_end'])) { + $query->where('create_time', '<=', $params['create_time_end']); + } + + return $query->with('dictType') + ->orderBy('sort') + ->orderBy('id', 'desc') + ->paginate($params['page_size'] ?? 15); + } + + /** + * 根据字典类型获取数据列表 + * + * @param string $dictType + * @param bool $onlyActive + * @return \Illuminate\Database\Eloquent\Collection + */ + public function getByType(string $dictType, bool $onlyActive = true) + { + $query = SystemDictData::where('dict_type', $dictType); + + if ($onlyActive) { + $query->where('status', SystemDictData::STATUS_NORMAL); + } + + return $query->orderBy('sort')->get(); + } + + /** + * 根据ID获取字典数据详情 + * + * @param int $id + * @return SystemDictData + * @throws BusinessException + */ + public function getById(int $id): SystemDictData + { + $dictData = SystemDictData::with('dictType')->find($id); + + if (!$dictData) { + throw new BusinessException(ResponseEnum::DATA_NOT_FOUND_ERROR, '字典数据不存在'); + } + + return $dictData; + } + + /** + * 根据字典类型和值获取字典数据 + * + * @param string $dictType + * @param string $value + * @return SystemDictData + * @throws BusinessException + */ + public function getByTypeAndValue(string $dictType, string $value): SystemDictData + { + $dictData = SystemDictData::findByTypeAndValue($dictType, $value); + + if (!$dictData) { + throw new BusinessException(ResponseEnum::DATA_NOT_FOUND_ERROR, '字典数据不存在'); + } + + return $dictData; + } + + /** + * 创建字典数据 + * + * @param array $data + * @return SystemDictData + * @throws BusinessException + */ + public function create(array $data): SystemDictData + { + // 检查字典类型是否存在 + $dictType = SystemDictType::findByType($data['dict_type']); + if (!$dictType) { + throw new BusinessException(ResponseEnum::DATA_NOT_FOUND_ERROR, '字典类型不存在'); + } + + // 检查字典值是否已存在 + if (SystemDictData::valueExists($data['value'], $data['dict_type'])) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '字典值已存在'); + } + + // 如果没有设置排序,自动获取下一个排序值 + if (!isset($data['sort']) || $data['sort'] === '') { + $data['sort'] = SystemDictData::getNextSort($data['dict_type']); + } + + try { + $dictData = SystemDictData::create($data); + + Log::info('字典数据创建成功', ['id' => $dictData->id, 'dict_type' => $dictData->dict_type, 'value' => $dictData->value]); + + return $dictData; + } catch (\Exception $e) { + Log::error('字典数据创建失败', ['error' => $e->getMessage(), 'data' => $data]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '创建字典数据失败'); + } + } + + /** + * 更新字典数据 + * + * @param int $id + * @param array $data + * @return SystemDictData + * @throws BusinessException + */ + public function update(int $id, array $data): SystemDictData + { + $dictData = $this->getById($id); + + // 如果更新了字典类型,检查新类型是否存在 + if (isset($data['dict_type']) && $data['dict_type'] !== $dictData->dict_type) { + $dictType = SystemDictType::findByType($data['dict_type']); + if (!$dictType) { + throw new BusinessException(ResponseEnum::DATA_NOT_FOUND_ERROR, '字典类型不存在'); + } + } + + // 检查字典值是否已存在(排除当前记录) + if (isset($data['value']) || isset($data['dict_type'])) { + $checkType = $data['dict_type'] ?? $dictData->dict_type; + $checkValue = $data['value'] ?? $dictData->value; + + if (SystemDictData::valueExists($checkValue, $checkType, $id)) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '字典值已存在'); + } + } + + try { + $dictData->update($data); + + Log::info('字典数据更新成功', ['id' => $id, 'data' => $data]); + + return $dictData->refresh(); + } catch (\Exception $e) { + Log::error('字典数据更新失败', ['id' => $id, 'error' => $e->getMessage(), 'data' => $data]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '更新字典数据失败'); + } + } + + /** + * 删除字典数据 + * + * @param int $id + * @return bool + * @throws BusinessException + */ + public function delete(int $id): bool + { + $dictData = $this->getById($id); + + try { + DB::transaction(function () use ($dictData) { + $dictData->delete(); + }); + + Log::info('字典数据删除成功', ['id' => $id, 'dict_type' => $dictData->dict_type, 'value' => $dictData->value]); + + return true; + } catch (\Exception $e) { + Log::error('字典数据删除失败', ['id' => $id, 'error' => $e->getMessage()]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '删除字典数据失败'); + } + } + + /** + * 批量删除字典数据 + * + * @param array $ids + * @return bool + * @throws BusinessException + */ + public function batchDelete(array $ids): bool + { + // 检查所有字典数据是否存在 + $dictDataList = SystemDictData::whereIn('id', $ids)->get(); + if ($dictDataList->count() !== count($ids)) { + throw new BusinessException(ResponseEnum::DATA_NOT_FOUND_ERROR, '部分字典数据不存在'); + } + + try { + DB::transaction(function () use ($ids) { + SystemDictData::whereIn('id', $ids)->delete(); + }); + + Log::info('批量删除字典数据成功', ['ids' => $ids]); + + return true; + } catch (\Exception $e) { + Log::error('批量删除字典数据失败', ['ids' => $ids, 'error' => $e->getMessage()]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '批量删除字典数据失败'); + } + } + + /** + * 更新字典数据状态 + * + * @param int $id + * @param int $status + * @return bool + * @throws BusinessException + */ + public function updateStatus(int $id, int $status): bool + { + $dictData = $this->getById($id); + + if (!in_array($status, [SystemDictData::STATUS_NORMAL, SystemDictData::STATUS_DISABLED])) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '无效的状态值'); + } + + try { + $dictData->update(['status' => $status]); + + Log::info('字典数据状态更新成功', ['id' => $id, 'status' => $status]); + + return true; + } catch (\Exception $e) { + Log::error('字典数据状态更新失败', ['id' => $id, 'status' => $status, 'error' => $e->getMessage()]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '更新状态失败'); + } + } + + /** + * 批量更新状态 + * + * @param array $ids + * @param int $status + * @return bool + * @throws BusinessException + */ + public function batchUpdateStatus(array $ids, int $status): bool + { + if (!in_array($status, [SystemDictData::STATUS_NORMAL, SystemDictData::STATUS_DISABLED])) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '无效的状态值'); + } + + try { + SystemDictData::whereIn('id', $ids)->update(['status' => $status]); + + Log::info('批量更新字典数据状态成功', ['ids' => $ids, 'status' => $status]); + + return true; + } catch (\Exception $e) { + Log::error('批量更新字典数据状态失败', ['ids' => $ids, 'status' => $status, 'error' => $e->getMessage()]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '批量更新状态失败'); + } + } + + /** + * 更新排序 + * + * @param int $id + * @param int $sort + * @return bool + * @throws BusinessException + */ + public function updateSort(int $id, int $sort): bool + { + $dictData = $this->getById($id); + + try { + $dictData->update(['sort' => $sort]); + + Log::info('字典数据排序更新成功', ['id' => $id, 'sort' => $sort]); + + return true; + } catch (\Exception $e) { + Log::error('字典数据排序更新失败', ['id' => $id, 'sort' => $sort, 'error' => $e->getMessage()]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '更新排序失败'); + } + } + + /** + * 批量更新排序 + * + * @param array $sortData 格式:[['id' => 1, 'sort' => 1], ['id' => 2, 'sort' => 2]] + * @return bool + * @throws BusinessException + */ + public function batchUpdateSort(array $sortData): bool + { + try { + DB::transaction(function () use ($sortData) { + foreach ($sortData as $item) { + SystemDictData::where('id', $item['id'])->update(['sort' => $item['sort']]); + } + }); + + Log::info('批量更新字典数据排序成功', ['sort_data' => $sortData]); + + return true; + } catch (\Exception $e) { + Log::error('批量更新字典数据排序失败', ['sort_data' => $sortData, 'error' => $e->getMessage()]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '批量更新排序失败'); + } + } + + /** + * 根据字典类型获取选项列表 + * + * @param string $dictType + * @param bool $onlyActive + * @return array + */ + public function getOptions(string $dictType, bool $onlyActive = true): array + { + return SystemDictData::getOptionsByType($dictType, $onlyActive); + } + + /** + * 根据字典类型和值获取标签 + * + * @param string $dictType + * @param string $value + * @return string|null + */ + public function getLabel(string $dictType, string $value): ?string + { + return SystemDictData::getLabelByTypeAndValue($dictType, $value); + } + + /** + * 获取字典数据的统计信息 + * + * @param string|null $dictType + * @return array + */ + public function getStatistics(?string $dictType = null): array + { + $query = SystemDictData::query(); + + if ($dictType) { + $query->where('dict_type', $dictType); + } + + $total = $query->count(); + $normal = $query->clone()->where('status', SystemDictData::STATUS_NORMAL)->count(); + $disabled = $query->clone()->where('status', SystemDictData::STATUS_DISABLED)->count(); + + return [ + 'total' => $total, + 'normal' => $normal, + 'disabled' => $disabled, + ]; + } + + /** + * 根据字典类型获取字典数据分组统计 + * + * @return array + */ + public function getGroupStatistics(): array + { + return SystemDictData::select('dict_type', DB::raw('count(*) as total')) + ->groupBy('dict_type') + ->with('dictType:type,name') + ->get() + ->toArray(); + } +} diff --git a/app/Services/System/SystemDictTypeService.php b/app/Services/System/SystemDictTypeService.php new file mode 100644 index 0000000..0edfb95 --- /dev/null +++ b/app/Services/System/SystemDictTypeService.php @@ -0,0 +1,329 @@ +where('name', 'like', '%' . $params['name'] . '%'); + } + + // 字典类型搜索 + if (!empty($params['type'])) { + $query->where('type', 'like', '%' . $params['type'] . '%'); + } + + // 状态筛选 + if (isset($params['status']) && $params['status'] !== '') { + $query->where('status', $params['status']); + } + + // 创建时间范围 + if (!empty($params['create_time_start'])) { + $query->where('create_time', '>=', $params['create_time_start']); + } + if (!empty($params['create_time_end'])) { + $query->where('create_time', '<=', $params['create_time_end']); + } + + return $query->orderBy('id', 'desc') + ->paginate($params['page_size'] ?? 15); + } + + /** + * 获取所有字典类型(不分页) + * + * @param bool $onlyActive + * @return \Illuminate\Database\Eloquent\Collection + */ + public function getAll(bool $onlyActive = false) + { + $query = SystemDictType::query(); + + if ($onlyActive) { + $query->where('status', SystemDictType::STATUS_NORMAL); + } + + return $query->orderBy('id', 'desc')->get(); + } + + /** + * 根据ID获取字典类型详情 + * + * @param int $id + * @return SystemDictType + * @throws BusinessException + */ + public function getById(int $id): SystemDictType + { + $dictType = SystemDictType::find($id); + + if (!$dictType) { + throw new BusinessException(ResponseEnum::DATA_NOT_FOUND_ERROR, '字典类型不存在'); + } + + return $dictType; + } + + /** + * 根据类型获取字典类型详情 + * + * @param string $type + * @return SystemDictType + * @throws BusinessException + */ + public function getByType(string $type): SystemDictType + { + $dictType = SystemDictType::findByType($type); + + if (!$dictType) { + throw new BusinessException(ResponseEnum::DATA_NOT_FOUND_ERROR, '字典类型不存在'); + } + + return $dictType; + } + + /** + * 创建字典类型 + * + * @param array $data + * @return SystemDictType + * @throws BusinessException + */ + public function create(array $data): SystemDictType + { + // 检查字典类型是否已存在 + if (SystemDictType::typeExists($data['type'])) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '字典类型已存在'); + } + + try { + $dictType = SystemDictType::create($data); + + Log::info('字典类型创建成功', ['id' => $dictType->id, 'type' => $dictType->type]); + + return $dictType; + } catch (\Exception $e) { + Log::error('字典类型创建失败', ['error' => $e->getMessage(), 'data' => $data]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '创建字典类型失败'); + } + } + + /** + * 更新字典类型 + * + * @param int $id + * @param array $data + * @return SystemDictType + * @throws BusinessException + */ + public function update(int $id, array $data): SystemDictType + { + $dictType = $this->getById($id); + + // 检查字典类型是否已存在(排除当前记录) + if (isset($data['type']) && SystemDictType::typeExists($data['type'], $id)) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '字典类型已存在'); + } + + try { + $dictType->update($data); + + Log::info('字典类型更新成功', ['id' => $id, 'data' => $data]); + + return $dictType->refresh(); + } catch (\Exception $e) { + Log::error('字典类型更新失败', ['id' => $id, 'error' => $e->getMessage(), 'data' => $data]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '更新字典类型失败'); + } + } + + /** + * 删除字典类型 + * + * @param int $id + * @return bool + * @throws BusinessException + */ + public function delete(int $id): bool + { + $dictType = $this->getById($id); + + // 检查是否有关联的字典数据 + $dataCount = SystemDictData::where('dict_type', $dictType->type)->count(); + if ($dataCount > 0) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '该字典类型下存在字典数据,无法删除'); + } + + try { + DB::transaction(function () use ($dictType) { + $dictType->delete(); + }); + + Log::info('字典类型删除成功', ['id' => $id, 'type' => $dictType->type]); + + return true; + } catch (\Exception $e) { + Log::error('字典类型删除失败', ['id' => $id, 'error' => $e->getMessage()]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '删除字典类型失败'); + } + } + + /** + * 批量删除字典类型 + * + * @param array $ids + * @return bool + * @throws BusinessException + */ + public function batchDelete(array $ids): bool + { + // 检查所有字典类型是否存在 + $dictTypes = SystemDictType::whereIn('id', $ids)->get(); + if ($dictTypes->count() !== count($ids)) { + throw new BusinessException(ResponseEnum::DATA_NOT_FOUND_ERROR, '部分字典类型不存在'); + } + + // 检查是否有关联的字典数据 + $types = $dictTypes->pluck('type')->toArray(); + $dataCount = SystemDictData::whereIn('dict_type', $types)->count(); + if ($dataCount > 0) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '选中的字典类型下存在字典数据,无法删除'); + } + + try { + DB::transaction(function () use ($ids) { + SystemDictType::whereIn('id', $ids)->delete(); + }); + + Log::info('批量删除字典类型成功', ['ids' => $ids]); + + return true; + } catch (\Exception $e) { + Log::error('批量删除字典类型失败', ['ids' => $ids, 'error' => $e->getMessage()]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '批量删除字典类型失败'); + } + } + + /** + * 更新字典类型状态 + * + * @param int $id + * @param int $status + * @return bool + * @throws BusinessException + */ + public function updateStatus(int $id, int $status): bool + { + $dictType = $this->getById($id); + + if (!in_array($status, [SystemDictType::STATUS_NORMAL, SystemDictType::STATUS_DISABLED])) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '无效的状态值'); + } + + try { + $dictType->update(['status' => $status]); + + Log::info('字典类型状态更新成功', ['id' => $id, 'status' => $status]); + + return true; + } catch (\Exception $e) { + Log::error('字典类型状态更新失败', ['id' => $id, 'status' => $status, 'error' => $e->getMessage()]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '更新状态失败'); + } + } + + /** + * 批量更新状态 + * + * @param array $ids + * @param int $status + * @return bool + * @throws BusinessException + */ + public function batchUpdateStatus(array $ids, int $status): bool + { + if (!in_array($status, [SystemDictType::STATUS_NORMAL, SystemDictType::STATUS_DISABLED])) { + throw new BusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '无效的状态值'); + } + + try { + SystemDictType::whereIn('id', $ids)->update(['status' => $status]); + + Log::info('批量更新字典类型状态成功', ['ids' => $ids, 'status' => $status]); + + return true; + } catch (\Exception $e) { + Log::error('批量更新字典类型状态失败', ['ids' => $ids, 'status' => $status, 'error' => $e->getMessage()]); + throw new BusinessException(ResponseEnum::SYSTEM_ERROR, '批量更新状态失败'); + } + } + + /** + * 获取字典类型的统计信息 + * + * @return array + */ + public function getStatistics(): array + { + $total = SystemDictType::count(); + $normal = SystemDictType::where('status', SystemDictType::STATUS_NORMAL)->count(); + $disabled = SystemDictType::where('status', SystemDictType::STATUS_DISABLED)->count(); + + return [ + 'total' => $total, + 'normal' => $normal, + 'disabled' => $disabled, + ]; + } + + /** + * 获取简单列表(用于下拉选择等) + * + * @return array + */ + public function getSimpleList(): array + { + return SystemDictType::select('id', 'name', 'type') + ->where('status', SystemDictType::STATUS_NORMAL) + ->orderBy('id', 'desc') + ->get() + ->toArray(); + } + + /** + * 获取字典类型选项列表 + * + * @return array + */ + public function getOptions(): array + { + return SystemDictType::where('status', SystemDictType::STATUS_NORMAL) + ->orderBy('id', 'desc') + ->pluck('name', 'type') + ->toArray(); + } +} diff --git a/app/Services/System/SystemUserSchoolCampusService.php b/app/Services/System/SystemUserSchoolCampusService.php new file mode 100644 index 0000000..0f2bfde --- /dev/null +++ b/app/Services/System/SystemUserSchoolCampusService.php @@ -0,0 +1,271 @@ +where('userid', $params['userid']); + } + + // 学校筛选 + if (!empty($params['schoolid'])) { + $query->where('schoolid', $params['schoolid']); + } + + // 校区筛选 + if (!empty($params['campusid'])) { + $query->where('campusid', $params['campusid']); + } + + // 包含关联信息 + $query->with([ + 'user:id,username,nickname', + 'school:id,name,alias', + 'campus:id,name,alias' + ]); + + // 排序 + $query->orderBy('schoolid', 'asc') + ->orderBy('campusid', 'asc') + ->orderBy('userid', 'asc'); + + return $query->paginate($params['page_size'] ?? 15); + } + + /** + * 获取用户的学校校区列表 + */ + public function getUserSchoolCampuses(int $userId): array + { + return SystemUserSchoolCampus::where('userid', $userId) + ->with([ + 'school:id,name,alias', + 'campus:id,name,alias' + ]) + ->get() + ->toArray(); + } + + /** + * 获取学校的用户列表 + */ + public function getSchoolUsers(int $schoolId): array + { + return SystemUserSchoolCampus::where('schoolid', $schoolId) + ->with([ + 'user:id,username,nickname', + 'campus:id,name,alias' + ]) + ->get() + ->toArray(); + } + + /** + * 获取校区的用户列表 + */ + public function getCampusUsers(int $campusId): array + { + return SystemUserSchoolCampus::where('campusid', $campusId) + ->with([ + 'user:id,username,nickname', + 'school:id,name,alias' + ]) + ->get() + ->toArray(); + } + + /** + * 批量分配用户到学校校区 + */ + public function batchAssignUsersToSchoolCampus(array $userIds, int $schoolId, ?int $campusId = null): array + { + $results = []; + + foreach ($userIds as $userId) { + // 检查是否已经存在关联 + $exists = SystemUserSchoolCampus::where('userid', $userId) + ->where('schoolid', $schoolId) + ->where('campusid', $campusId ?? 0) + ->exists(); + + if (!$exists) { + $data = [ + 'userid' => $userId, + 'schoolid' => $schoolId, + 'campusid' => $campusId ?? 0, + ]; + + $result = $this->create($data); + $results[] = $result; + } + } + + return $results; + } + + /** + * 批量移除用户学校校区关联 + */ + public function batchRemoveUsersFromSchoolCampus(array $userIds, int $schoolId, ?int $campusId = null): int + { + $query = SystemUserSchoolCampus::whereIn('userid', $userIds) + ->where('schoolid', $schoolId); + + if ($campusId !== null) { + $query->where('campusid', $campusId); + } + + return $query->delete(); + } + + /** + * 转移用户到新的学校校区 + */ + public function transferUserToSchoolCampus(int $userId, int $oldSchoolId, int $newSchoolId, ?int $oldCampusId = null, ?int $newCampusId = null): bool + { + // 删除旧的关联 + $deleteQuery = SystemUserSchoolCampus::where('userid', $userId) + ->where('schoolid', $oldSchoolId); + + if ($oldCampusId !== null) { + $deleteQuery->where('campusid', $oldCampusId); + } + + $deleteQuery->delete(); + + // 创建新的关联 + $data = [ + 'userid' => $userId, + 'schoolid' => $newSchoolId, + 'campusid' => $newCampusId ?? 0, + ]; + + $this->create($data); + + return true; + } + + /** + * 获取用户管理的学校ID列表 + */ + public function getUserSchoolIds(int $userId): array + { + return SystemUserSchoolCampus::where('userid', $userId) + ->pluck('schoolid') + ->unique() + ->toArray(); + } + + /** + * 获取用户管理的校区ID列表 + */ + public function getUserCampusIds(int $userId): array + { + return SystemUserSchoolCampus::where('userid', $userId) + ->pluck('campusid') + ->toArray(); + } + + /** + * 检查用户是否管理指定学校 + */ + public function checkUserManagesSchool(int $userId, int $schoolId): bool + { + return SystemUserSchoolCampus::where('userid', $userId) + ->where('schoolid', $schoolId) + ->exists(); + } + + /** + * 检查用户是否管理指定校区 + */ + public function checkUserManagesCampus(int $userId, int $campusId): bool + { + return SystemUserSchoolCampus::where('userid', $userId) + ->where('campusid', $campusId) + ->exists(); + } + + /** + * 获取用户校区统计信息 + */ + public function getUserSchoolCampusStats(int $userId): array + { + $relations = SystemUserSchoolCampus::where('userid', $userId) + ->with(['school:id,name', 'campus:id,name']) + ->get(); + + return [ + 'total_schools' => $relations->pluck('school')->unique('id')->count(), + 'total_campuses' => $relations->where('campusid', '>', 0)->count(), + 'schools' => $relations->pluck('school')->unique('id')->values()->toArray(), + 'campuses' => $relations->where('campusid', '>', 0)->pluck('campus')->toArray(), + ]; + } + + /** + * 创建前的特殊处理 + */ + protected function beforeCreate(array &$data): void + { + // 设置默认值 + $data['campusid'] = $data['campusid'] ?? 0; + + // 验证用户是否存在 + if (!empty($data['userid'])) { + $user = \App\Models\User::find($data['userid']); + if (!$user) { + throw new \InvalidArgumentException('指定的用户不存在'); + } + } + + // 验证学校是否存在 + if (!empty($data['schoolid'])) { + $school = \App\Models\Schools\School::find($data['schoolid']); + if (!$school) { + throw new \InvalidArgumentException('指定的学校不存在'); + } + } + + // 验证校区是否存在 + if (!empty($data['campusid']) && $data['campusid'] > 0) { + $campus = \App\Models\Schools\SchoolCampus::find($data['campusid']); + if (!$campus) { + throw new \InvalidArgumentException('指定的校区不存在'); + } + + // 验证校区是否属于指定学校 + if ($campus->school_id !== $data['schoolid']) { + throw new \InvalidArgumentException('指定的校区不属于该学校'); + } + } + + // 检查是否已经存在关联 + $exists = SystemUserSchoolCampus::where('userid', $data['userid']) + ->where('schoolid', $data['schoolid']) + ->where('campusid', $data['campusid']) + ->exists(); + + if ($exists) { + throw new \InvalidArgumentException('该用户已经关联了此学校和校区'); + } + } +} diff --git a/app/Services/Teachers/TeacherClassService.php b/app/Services/Teachers/TeacherClassService.php new file mode 100644 index 0000000..2f9dd04 --- /dev/null +++ b/app/Services/Teachers/TeacherClassService.php @@ -0,0 +1,262 @@ +where('teacher_id', $params['teacher_id']); + } + + // 学校筛选 + if (!empty($params['school_id'])) { + $query->where('school_id', $params['school_id']); + } + + // 班级筛选 + if (!empty($params['class_id'])) { + $query->where('class_id', $params['class_id']); + } + + // 年级筛选 + if (!empty($params['grade_id'])) { + $query->where('grade_id', $params['grade_id']); + } + + // 班主任筛选 + if (isset($params['headman'])) { + $query->where('headman', $params['headman']); + } + + // 包含关联信息 + $query->with([ + 'teacher:id,username,nickname', + 'school:id,name,alias', + 'schoolClass:id,name,code,campus_id,grade_id,number' + ]); + + // 排序 + $query->orderBy('school_id', 'asc') + ->orderBy('grade_id', 'asc') + ->orderBy('class_id', 'asc') + ->orderBy('headman', 'desc'); + + return $query->paginate($params['page_size'] ?? 15); + } + + /** + * 获取老师的班级列表 + */ + public function getTeacherClasses(int $teacherId): array + { + return TeacherClass::where('teacher_id', $teacherId) + ->with([ + 'school:id,name,alias', + 'schoolClass:id,name,code,campus_id,grade_id,number' + ]) + ->get() + ->toArray(); + } + + /** + * 获取班级的老师列表 + */ + public function getClassTeachers(int $classId): array + { + return TeacherClass::where('class_id', $classId) + ->with([ + 'teacher:id,username,nickname', + 'school:id,name,alias' + ]) + ->get() + ->toArray(); + } + + /** + * 获取学校的老师列表 + */ + public function getSchoolTeachers(int $schoolId): array + { + return TeacherClass::where('school_id', $schoolId) + ->with([ + 'teacher:id,username,nickname', + 'schoolClass:id,name,code,campus_id,grade_id,number' + ]) + ->get() + ->toArray(); + } + + /** + * 获取班主任列表 + */ + public function getHeadmans(?int $schoolId = null, ?int $gradeId = null): array + { + $query = TeacherClass::headman(); + + if ($schoolId) { + $query->where('school_id', $schoolId); + } + + if ($gradeId) { + $query->where('grade_id', $gradeId); + } + + return $query->with([ + 'teacher:id,username,nickname', + 'school:id,name,alias', + 'schoolClass:id,name,code,campus_id,grade_id,number' + ])->get()->toArray(); + } + + /** + * 批量分配老师到班级 + */ + public function batchAssignTeachersToClass(array $teacherIds, int $classId, ?int $gradeId = null): array + { + $class = \App\Models\Schools\SchoolClass::findOrFail($classId); + $results = []; + + foreach ($teacherIds as $teacherId) { + // 检查是否已经存在关联 + $exists = TeacherClass::where('teacher_id', $teacherId) + ->where('class_id', $classId) + ->exists(); + + if (!$exists) { + $data = [ + 'teacher_id' => $teacherId, + 'school_id' => $class->school_id, + 'grade_id' => $gradeId ?? $class->grade_id, + 'class_id' => $classId, + 'headman' => 0, + ]; + + $result = $this->create($data); + $results[] = $result; + } + } + + return $results; + } + + /** + * 批量移除老师班级关联 + */ + public function batchRemoveTeachersFromClass(array $teacherIds, int $classId): int + { + return TeacherClass::whereIn('teacher_id', $teacherIds) + ->where('class_id', $classId) + ->delete(); + } + + /** + * 设置班主任 + */ + public function setHeadman(int $teacherId, int $classId): bool + { + // 先清除该班级的所有班主任 + TeacherClass::where('class_id', $classId)->update(['headman' => 0]); + + // 设置新的班主任 + $teacherClass = TeacherClass::where('teacher_id', $teacherId) + ->where('class_id', $classId) + ->first(); + + if (!$teacherClass) { + throw new \InvalidArgumentException('该老师不在此班级中'); + } + + $teacherClass->headman = 1; + return $teacherClass->save(); + } + + /** + * 取消班主任 + */ + public function removeHeadman(int $classId): bool + { + return TeacherClass::where('class_id', $classId) + ->update(['headman' => 0]) > 0; + } + + /** + * 获取老师班级统计信息 + */ + public function getTeacherClassStats(int $teacherId): array + { + $classes = TeacherClass::where('teacher_id', $teacherId) + ->with(['school:id,name', 'schoolClass:id,name']) + ->get(); + + return [ + 'total_classes' => $classes->count(), + 'headman_classes' => $classes->where('headman', 1)->count(), + 'schools' => $classes->pluck('school')->unique('id')->values()->toArray(), + 'classes' => $classes->pluck('schoolClass')->toArray(), + ]; + } + + /** + * 创建前的特殊处理 + */ + protected function beforeCreate(array &$data): void + { + // 设置默认值 + $data['headman'] = $data['headman'] ?? 0; + + // 验证老师是否存在 + if (!empty($data['teacher_id'])) { + $teacher = \App\Models\User::find($data['teacher_id']); + if (!$teacher) { + throw new \InvalidArgumentException('指定的老师不存在'); + } + } + + // 验证学校是否存在 + if (!empty($data['school_id'])) { + $school = \App\Models\Schools\School::find($data['school_id']); + if (!$school) { + throw new \InvalidArgumentException('指定的学校不存在'); + } + } + + // 验证班级是否存在 + if (!empty($data['class_id'])) { + $class = \App\Models\Schools\SchoolClass::find($data['class_id']); + if (!$class) { + throw new \InvalidArgumentException('指定的班级不存在'); + } + + // 验证班级是否属于指定学校 + if (isset($data['school_id']) && $class->school_id !== $data['school_id']) { + throw new \InvalidArgumentException('指定的班级不属于该学校'); + } + } + + // 检查是否已经存在关联 + $exists = TeacherClass::where('teacher_id', $data['teacher_id']) + ->where('class_id', $data['class_id']) + ->exists(); + + if ($exists) { + throw new \InvalidArgumentException('该老师已经在此班级中'); + } + } +} diff --git a/bootstrap/app.php b/bootstrap/app.php index 858ba87..0cfdf1f 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -22,6 +22,15 @@ return Application::configure(basePath: dirname(__DIR__)) $middleware->alias([ 'admin.auth' => \App\Http\Middleware\AdminApiAuthenticate::class, ]); + + // 添加CORS中间件到全局中间件组 + $middleware->web(append: [ + \Illuminate\Http\Middleware\HandleCors::class, + ]); + + $middleware->api(append: [ + \Illuminate\Http\Middleware\HandleCors::class, + ]); }) ->withExceptions(function (Exceptions $exceptions): void { // 配置不需要报告的异常类型 diff --git a/config/cors.php b/config/cors.php new file mode 100644 index 0000000..ce60c9a --- /dev/null +++ b/config/cors.php @@ -0,0 +1,34 @@ + ['api/*', 'admin/*', 'sanctum/csrf-cookie'], + + 'allowed_methods' => ['*'], + + 'allowed_origins' => ['*'], + + 'allowed_origins_patterns' => [], + + 'allowed_headers' => ['*'], + + 'exposed_headers' => ['Authorization'], + + 'max_age' => 86400, // 24小时 + + 'supports_credentials' => true, + +]; diff --git a/database/migrations/2024_01_01_000001_create_student_table.php b/database/migrations/2024_01_01_000001_create_student_table.php new file mode 100644 index 0000000..5102d0c --- /dev/null +++ b/database/migrations/2024_01_01_000001_create_student_table.php @@ -0,0 +1,57 @@ +id(); + $table->string('username', 200)->unique()->comment('用户名'); + $table->string('real_name', 30)->nullable()->comment('真实姓名'); + $table->string('password', 100)->comment('密码'); + $table->char('salt', 6)->default('')->comment('随机串'); + $table->unsignedInteger('role')->default(0)->comment('所属角色; 2:学生 3:家长'); + $table->unsignedInteger('grade_id')->default(0)->comment('年级id'); + $table->unsignedInteger('parent_id')->default(0)->comment('家长ID'); + $table->string('email', 200)->nullable()->comment('电子邮件'); + $table->string('phone_number', 11)->nullable()->comment('手机码'); + $table->string('title', 200)->default('')->comment('头衔'); + $table->string('avatar', 200)->default('')->comment('头像URL'); + $table->dateTime('reg_time')->nullable()->comment('注册时间'); + $table->string('reg_ip', 20)->nullable()->comment('注册IP'); + $table->unsignedInteger('status')->default(1)->comment('用户状态:0禁用1正常2欠费3未激活'); + $table->unsignedInteger('vip_level')->default(0)->comment('常规VIP等级'); + $table->unsignedTinyInteger('sex')->default(0)->comment('性别;1是男;2是女;0是不填'); + $table->string('qq', 20)->nullable()->comment('QQ号'); + $table->string('examing_number', 50)->nullable()->comment('考号'); + + // 创建索引 + $table->index('email'); + $table->index('role'); + $table->index('parent_id'); + $table->index('status'); + $table->index('vip_level'); + $table->index('phone_number'); + $table->index('grade_id'); + $table->index('sex'); + $table->index('real_name'); + $table->index('examing_number'); + $table->index(['role', 'status', 'id'], 'idx_user_role_status_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('student'); + } +}; diff --git a/database/migrations/2024_01_15_100000_create_system_dict_type_table.php b/database/migrations/2024_01_15_100000_create_system_dict_type_table.php new file mode 100644 index 0000000..bef6bde --- /dev/null +++ b/database/migrations/2024_01_15_100000_create_system_dict_type_table.php @@ -0,0 +1,46 @@ +bigIncrements('id')->comment('字典主键'); + $table->string('name', 100)->default('')->comment('字典名称'); + $table->string('type', 100)->default('')->comment('字典类型'); + $table->tinyInteger('status')->default(0)->comment('状态(0正常 1停用)'); + $table->string('remark', 500)->nullable()->comment('备注'); + $table->string('creator', 64)->default('')->comment('创建者'); + $table->timestamp('create_time')->useCurrent()->comment('创建时间'); + $table->string('updater', 64)->default('')->comment('更新者'); + $table->timestamp('update_time')->useCurrent()->useCurrentOnUpdate()->comment('更新时间'); + $table->boolean('deleted')->default(false)->comment('是否删除'); + $table->timestamp('deleted_time')->nullable()->comment('删除时间'); + + // 添加索引 + $table->unique('type', 'uk_type'); + $table->index('status', 'idx_status'); + $table->index('create_time', 'idx_create_time'); + $table->index('deleted', 'idx_deleted'); + }); + + // 添加表注释 + DB::statement("ALTER TABLE `system_dict_type` COMMENT = '字典类型表'"); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_dict_type'); + } +}; diff --git a/database/migrations/2024_01_15_100001_create_system_dict_data_table.php b/database/migrations/2024_01_15_100001_create_system_dict_data_table.php new file mode 100644 index 0000000..f9bed72 --- /dev/null +++ b/database/migrations/2024_01_15_100001_create_system_dict_data_table.php @@ -0,0 +1,58 @@ +bigIncrements('id')->comment('字典编码'); + $table->integer('sort')->default(0)->comment('字典排序'); + $table->string('label', 100)->default('')->comment('字典标签'); + $table->string('value', 100)->default('')->comment('字典键值'); + $table->string('dict_type', 100)->default('')->comment('字典类型'); + $table->tinyInteger('status')->default(0)->comment('状态(0正常 1停用)'); + $table->string('color_type', 100)->default('')->nullable()->comment('颜色类型'); + $table->string('css_class', 100)->default('')->nullable()->comment('css 样式'); + $table->string('remark', 500)->nullable()->comment('备注'); + $table->string('creator', 64)->default('')->comment('创建者'); + $table->timestamp('create_time')->useCurrent()->comment('创建时间'); + $table->string('updater', 64)->default('')->comment('更新者'); + $table->timestamp('update_time')->useCurrent()->useCurrentOnUpdate()->comment('更新时间'); + $table->boolean('deleted')->default(false)->comment('是否删除'); + + // 添加索引 + $table->index('dict_type', 'idx_dict_type'); + $table->index('status', 'idx_status'); + $table->index('sort', 'idx_sort'); + $table->index('create_time', 'idx_create_time'); + $table->index('deleted', 'idx_deleted'); + $table->unique(['value', 'dict_type'], 'uk_value_dict_type'); + + // 添加外键约束 + $table->foreign('dict_type', 'fk_dict_data_type') + ->references('type') + ->on('system_dict_type') + ->onDelete('cascade') + ->onUpdate('cascade'); + }); + + // 添加表注释 + DB::statement("ALTER TABLE `system_dict_data` COMMENT = '字典数据表'"); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_dict_data'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index d01a0ef..cfa951e 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -19,5 +19,11 @@ class DatabaseSeeder extends Seeder 'name' => 'Test User', 'email' => 'test@example.com', ]); + + // 字典数据 + $this->call([ + SystemDictTypeSeeder::class, + SystemDictDataSeeder::class, + ]); } } diff --git a/database/seeders/StudentSeeder.php b/database/seeders/StudentSeeder.php new file mode 100644 index 0000000..dedaf45 --- /dev/null +++ b/database/seeders/StudentSeeder.php @@ -0,0 +1,111 @@ + 'student001', + 'real_name' => '张三', + 'password' => Student::encryptPassword('123456', 'abc123'), + 'salt' => 'abc123', + 'role' => Student::ROLE_STUDENT, + 'grade_id' => 1, + 'email' => 'student001@example.com', + 'phone_number' => '13800138001', + 'status' => Student::STATUS_NORMAL, + 'sex' => Student::SEX_MALE, + 'reg_time' => now(), + 'reg_ip' => '127.0.0.1', + ], + [ + 'username' => 'student002', + 'real_name' => '李四', + 'password' => Student::encryptPassword('123456', 'def456'), + 'salt' => 'def456', + 'role' => Student::ROLE_STUDENT, + 'grade_id' => 1, + 'email' => 'student002@example.com', + 'phone_number' => '13800138002', + 'status' => Student::STATUS_NORMAL, + 'sex' => Student::SEX_FEMALE, + 'reg_time' => now(), + 'reg_ip' => '127.0.0.1', + ], + [ + 'username' => 'student003', + 'real_name' => '王五', + 'password' => Student::encryptPassword('123456', 'ghi789'), + 'salt' => 'ghi789', + 'role' => Student::ROLE_STUDENT, + 'grade_id' => 2, + 'email' => 'student003@example.com', + 'phone_number' => '13800138003', + 'status' => Student::STATUS_NORMAL, + 'sex' => Student::SEX_MALE, + 'reg_time' => now(), + 'reg_ip' => '127.0.0.1', + ], + [ + 'username' => 'parent001', + 'real_name' => '张父', + 'password' => Student::encryptPassword('123456', 'jkl012'), + 'salt' => 'jkl012', + 'role' => Student::ROLE_PARENT, + 'email' => 'parent001@example.com', + 'phone_number' => '13800138101', + 'status' => Student::STATUS_NORMAL, + 'sex' => Student::SEX_MALE, + 'reg_time' => now(), + 'reg_ip' => '127.0.0.1', + ], + [ + 'username' => 'parent002', + 'real_name' => '李母', + 'password' => Student::encryptPassword('123456', 'mno345'), + 'salt' => 'mno345', + 'role' => Student::ROLE_PARENT, + 'email' => 'parent002@example.com', + 'phone_number' => '13800138102', + 'status' => Student::STATUS_NORMAL, + 'sex' => Student::SEX_FEMALE, + 'reg_time' => now(), + 'reg_ip' => '127.0.0.1', + ], + ]; + + foreach ($students as $studentData) { + Student::create($studentData); + } + + // 设置家长和子女关系 + $parent1 = Student::where('username', 'parent001')->first(); + $parent2 = Student::where('username', 'parent002')->first(); + $student1 = Student::where('username', 'student001')->first(); + $student2 = Student::where('username', 'student002')->first(); + + if ($parent1 && $student1) { + $student1->update(['parent_id' => $parent1->id]); + } + + if ($parent2 && $student2) { + $student2->update(['parent_id' => $parent2->id]); + } + + $this->command->info('学生测试数据创建完成!'); + $this->command->info('创建了3个学生和2个家长,密码都是123456'); + $this->command->info('学生用户名:student001, student002, student003'); + $this->command->info('家长用户名:parent001, parent002'); + } +} diff --git a/database/seeders/SystemDictDataSeeder.php b/database/seeders/SystemDictDataSeeder.php new file mode 100644 index 0000000..3383b37 --- /dev/null +++ b/database/seeders/SystemDictDataSeeder.php @@ -0,0 +1,481 @@ + 1, + 'label' => '男', + 'value' => '1', + 'dict_type' => 'sys_gender', + 'status' => 0, + 'color_type' => 'primary', + 'css_class' => '', + 'remark' => '男性', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 2, + 'label' => '女', + 'value' => '2', + 'dict_type' => 'sys_gender', + 'status' => 0, + 'color_type' => 'danger', + 'css_class' => '', + 'remark' => '女性', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 3, + 'label' => '未知', + 'value' => '0', + 'dict_type' => 'sys_gender', + 'status' => 0, + 'color_type' => 'info', + 'css_class' => '', + 'remark' => '未知性别', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + + // 状态字典数据 + [ + 'sort' => 1, + 'label' => '正常', + 'value' => '0', + 'dict_type' => 'sys_status', + 'status' => 0, + 'color_type' => 'success', + 'css_class' => '', + 'remark' => '正常状态', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 2, + 'label' => '停用', + 'value' => '1', + 'dict_type' => 'sys_status', + 'status' => 0, + 'color_type' => 'danger', + 'css_class' => '', + 'remark' => '停用状态', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + + // 是否字典数据 + [ + 'sort' => 1, + 'label' => '是', + 'value' => 'Y', + 'dict_type' => 'sys_yes_no', + 'status' => 0, + 'color_type' => 'success', + 'css_class' => '', + 'remark' => '是', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 2, + 'label' => '否', + 'value' => 'N', + 'dict_type' => 'sys_yes_no', + 'status' => 0, + 'color_type' => 'danger', + 'css_class' => '', + 'remark' => '否', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + + // 显示状态字典数据 + [ + 'sort' => 1, + 'label' => '显示', + 'value' => '0', + 'dict_type' => 'sys_show_hide', + 'status' => 0, + 'color_type' => 'success', + 'css_class' => '', + 'remark' => '显示', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 2, + 'label' => '隐藏', + 'value' => '1', + 'dict_type' => 'sys_show_hide', + 'status' => 0, + 'color_type' => 'danger', + 'css_class' => '', + 'remark' => '隐藏', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + + // 系统开关字典数据 + [ + 'sort' => 1, + 'label' => '开启', + 'value' => '1', + 'dict_type' => 'sys_switch', + 'status' => 0, + 'color_type' => 'success', + 'css_class' => '', + 'remark' => '开启', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 2, + 'label' => '关闭', + 'value' => '0', + 'dict_type' => 'sys_switch', + 'status' => 0, + 'color_type' => 'danger', + 'css_class' => '', + 'remark' => '关闭', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + + // 菜单类型字典数据 + [ + 'sort' => 1, + 'label' => '目录', + 'value' => 'M', + 'dict_type' => 'sys_menu_type', + 'status' => 0, + 'color_type' => 'warning', + 'css_class' => '', + 'remark' => '目录', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 2, + 'label' => '菜单', + 'value' => 'C', + 'dict_type' => 'sys_menu_type', + 'status' => 0, + 'color_type' => 'success', + 'css_class' => '', + 'remark' => '菜单', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 3, + 'label' => '按钮', + 'value' => 'F', + 'dict_type' => 'sys_menu_type', + 'status' => 0, + 'color_type' => 'info', + 'css_class' => '', + 'remark' => '按钮', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + + // 用户类型字典数据 + [ + 'sort' => 1, + 'label' => '系统用户', + 'value' => '1', + 'dict_type' => 'sys_user_type', + 'status' => 0, + 'color_type' => 'primary', + 'css_class' => '', + 'remark' => '系统管理员', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 2, + 'label' => '学生', + 'value' => '2', + 'dict_type' => 'sys_user_type', + 'status' => 0, + 'color_type' => 'success', + 'css_class' => '', + 'remark' => '学生用户', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 3, + 'label' => '家长', + 'value' => '3', + 'dict_type' => 'sys_user_type', + 'status' => 0, + 'color_type' => 'warning', + 'css_class' => '', + 'remark' => '家长用户', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + + // 操作类型字典数据 + [ + 'sort' => 1, + 'label' => '新增', + 'value' => '1', + 'dict_type' => 'sys_operation_type', + 'status' => 0, + 'color_type' => 'success', + 'css_class' => '', + 'remark' => '新增操作', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 2, + 'label' => '修改', + 'value' => '2', + 'dict_type' => 'sys_operation_type', + 'status' => 0, + 'color_type' => 'primary', + 'css_class' => '', + 'remark' => '修改操作', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 3, + 'label' => '删除', + 'value' => '3', + 'dict_type' => 'sys_operation_type', + 'status' => 0, + 'color_type' => 'danger', + 'css_class' => '', + 'remark' => '删除操作', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 4, + 'label' => '查询', + 'value' => '4', + 'dict_type' => 'sys_operation_type', + 'status' => 0, + 'color_type' => 'info', + 'css_class' => '', + 'remark' => '查询操作', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + + // 年级字典数据 + [ + 'sort' => 1, + 'label' => '一年级', + 'value' => '1', + 'dict_type' => 'edu_grade', + 'status' => 0, + 'color_type' => '', + 'css_class' => '', + 'remark' => '小学一年级', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 2, + 'label' => '二年级', + 'value' => '2', + 'dict_type' => 'edu_grade', + 'status' => 0, + 'color_type' => '', + 'css_class' => '', + 'remark' => '小学二年级', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 3, + 'label' => '三年级', + 'value' => '3', + 'dict_type' => 'edu_grade', + 'status' => 0, + 'color_type' => '', + 'css_class' => '', + 'remark' => '小学三年级', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 4, + 'label' => '四年级', + 'value' => '4', + 'dict_type' => 'edu_grade', + 'status' => 0, + 'color_type' => '', + 'css_class' => '', + 'remark' => '小学四年级', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 5, + 'label' => '五年级', + 'value' => '5', + 'dict_type' => 'edu_grade', + 'status' => 0, + 'color_type' => '', + 'css_class' => '', + 'remark' => '小学五年级', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 6, + 'label' => '六年级', + 'value' => '6', + 'dict_type' => 'edu_grade', + 'status' => 0, + 'color_type' => '', + 'css_class' => '', + 'remark' => '小学六年级', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + + // 学生状态字典数据 + [ + 'sort' => 1, + 'label' => '在读', + 'value' => '1', + 'dict_type' => 'edu_student_status', + 'status' => 0, + 'color_type' => 'success', + 'css_class' => '', + 'remark' => '正常在读', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 2, + 'label' => '转学', + 'value' => '2', + 'dict_type' => 'edu_student_status', + 'status' => 0, + 'color_type' => 'warning', + 'css_class' => '', + 'remark' => '转学离校', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 3, + 'label' => '毕业', + 'value' => '3', + 'dict_type' => 'edu_student_status', + 'status' => 0, + 'color_type' => 'primary', + 'css_class' => '', + 'remark' => '正常毕业', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'sort' => 4, + 'label' => '退学', + 'value' => '4', + 'dict_type' => 'edu_student_status', + 'status' => 0, + 'color_type' => 'danger', + 'css_class' => '', + 'remark' => '退学离校', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + ]; + + foreach ($dictData as $data) { + SystemDictData::create($data); + } + + $this->command->info('字典数据插入完成!'); + } +} diff --git a/database/seeders/SystemDictTypeSeeder.php b/database/seeders/SystemDictTypeSeeder.php new file mode 100644 index 0000000..48d9612 --- /dev/null +++ b/database/seeders/SystemDictTypeSeeder.php @@ -0,0 +1,168 @@ + '性别', + 'type' => 'sys_gender', + 'status' => 0, + 'remark' => '用户性别字典', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '状态', + 'type' => 'sys_status', + 'status' => 0, + 'remark' => '通用状态字典', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '是否', + 'type' => 'sys_yes_no', + 'status' => 0, + 'remark' => '是否选择字典', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '显示状态', + 'type' => 'sys_show_hide', + 'status' => 0, + 'remark' => '显示隐藏状态', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '系统开关', + 'type' => 'sys_switch', + 'status' => 0, + 'remark' => '系统开关状态', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '菜单类型', + 'type' => 'sys_menu_type', + 'status' => 0, + 'remark' => '系统菜单类型', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '用户类型', + 'type' => 'sys_user_type', + 'status' => 0, + 'remark' => '系统用户类型', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '操作类型', + 'type' => 'sys_operation_type', + 'status' => 0, + 'remark' => '系统操作类型', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '通知类型', + 'type' => 'sys_notice_type', + 'status' => 0, + 'remark' => '系统通知类型', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '公告状态', + 'type' => 'sys_notice_status', + 'status' => 0, + 'remark' => '系统公告状态', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '学期', + 'type' => 'edu_semester', + 'status' => 0, + 'remark' => '教育学期字典', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '年级', + 'type' => 'edu_grade', + 'status' => 0, + 'remark' => '教育年级字典', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '班级状态', + 'type' => 'edu_class_status', + 'status' => 0, + 'remark' => '班级状态字典', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + [ + 'name' => '学生状态', + 'type' => 'edu_student_status', + 'status' => 0, + 'remark' => '学生状态字典', + 'creator' => $creator, + 'updater' => $creator, + 'create_time' => $now, + 'update_time' => $now, + ], + ]; + + foreach ($dictTypes as $dictType) { + SystemDictType::create($dictType); + } + + $this->command->info('字典类型数据插入完成!'); + } +} diff --git a/docs/BaseModel系统字段配置使用指南.md b/docs/BaseModel系统字段配置使用指南.md new file mode 100644 index 0000000..7e48bb8 --- /dev/null +++ b/docs/BaseModel系统字段配置使用指南.md @@ -0,0 +1,307 @@ +# BaseModel系统字段配置使用指南 + +## 概述 + +BaseModel提供了灵活的系统字段自动维护功能,允许开发者根据不同表的需求选择性地启用或关闭系统字段的自动维护。 + +## 配置选项 + +### 1. `$enableSystemFields` +控制是否自动维护系统字段,默认为 `false`。 + +**维护的字段包括:** +- `tenant_id` - 租户ID +- `creator` - 创建者 +- `create_time` - 创建时间 +- `updater` - 更新者 +- `update_time` - 更新时间 +- `deleted` - 删除标识 + +### 2. `$enableTenantScope` +控制是否启用租户隔离查询,默认为 `false`。 + +**功能:** +- 自动在查询中添加 `tenant_id` 过滤条件 +- 确保用户只能访问自己租户的数据 + +## 使用示例 + +### 主要业务表(推荐启用) + +```php +get(); + +// 只查询已删除的记录 +$deletedSchools = School::onlyTrashed()->get(); +``` + +### Q2: 如何跨租户查询? +```php +// 跨租户查询 +$allSchools = School::withoutGlobalScope('tenant')->get(); +``` + +### Q3: 如何为现有表添加系统字段? +```php +// 创建迁移文件 +php artisan make:migration add_system_fields_to_schools_table + +// 在迁移文件中添加字段 +public function up() +{ + Schema::table('schools', function (Blueprint $table) { + $table->integer('tenant_id')->default(0)->comment('租户ID'); + $table->integer('creator')->default(0)->comment('创建者'); + $table->timestamp('create_time')->useCurrent()->comment('创建时间'); + $table->integer('updater')->default(0)->comment('更新者'); + $table->timestamp('update_time')->useCurrent()->on('update')->comment('更新时间'); + $table->tinyInteger('deleted')->default(0)->comment('删除标识'); + + // 添加索引 + $table->index('tenant_id'); + $table->index('deleted'); + }); +} +``` + +### Q4: 如何自定义系统字段的值? +```php +// 在模型中重写方法 +public function setCreateFields(): void +{ + if (!$this->enableSystemFields) { + return; + } + + // 自定义逻辑 + $this->creator = auth()->id() ?? 0; + $this->create_time = now(); + $this->tenant_id = $this->getCustomTenantId(); + $this->deleted = 0; +} +``` + +## 总结 + +通过合理配置BaseModel的系统字段维护功能,可以: + +1. **提高开发效率** - 自动维护常用的系统字段 +2. **增强数据安全** - 租户隔离确保数据安全 +3. **简化代码逻辑** - 减少重复的字段维护代码 +4. **提高灵活性** - 根据表的特点选择性启用功能 + +建议在项目开始时就规划好各个表的配置策略,确保系统的一致性和可维护性。 diff --git a/docs/CRUD代码生成模板.md b/docs/CRUD代码生成模板.md index 137c0cf..412acb8 100644 --- a/docs/CRUD代码生成模板.md +++ b/docs/CRUD代码生成模板.md @@ -53,6 +53,42 @@ app/ **文件路径**:`app/Models/[模块名]/[表名].php` +**字段类型注释说明**: +- `int` - 整数字段(不可为null) +- `int|null` - 整数字段(可为null) +- `string` - 字符串字段(不可为null) +- `string|null` - 字符串字段(可为null) +- `float` - 浮点数字段(不可为null) +- `float|null` - 浮点数字段(可为null) +- `bool` - 布尔字段(不可为null) +- `bool|null` - 布尔字段(可为null) +- `\Carbon\Carbon` - 日期时间字段(不可为null) +- `\Carbon\Carbon|null` - 日期时间字段(可为null) +- `array` - 数组字段(通过JSON存储) +- `array|null` - 数组字段(通过JSON存储,可为null) + +**注意**:根据数据库字段定义中的DEFAULT和NULL约束来确定是否需要添加`|null`。 + +### 系统字段配置说明 + +BaseModel提供了两个配置选项来控制系统字段的自动维护: + +1. **`$enableSystemFields`** - 控制是否自动维护系统字段 + - `tenant_id` - 租户ID + - `creator` - 创建者 + - `create_time` - 创建时间 + - `updater` - 更新者 + - `update_time` - 更新时间 + - `deleted` - 删除标识 + +2. **`$enableTenantScope`** - 控制是否启用租户隔离查询 + +**使用建议**: +- **主要业务表**(如School、User、Product等):启用这两个配置 +- **关联表**(如StudentClass、UserRole等):保持默认关闭 +- **日志表**:只启用系统字段,不启用租户隔离 +- **配置表**:根据实际情况决定 + ```php group(function () { +Route::middleware("admin.auth")->group(function () { // 获取[中文名称]详情 Route::match(['get', 'post'], "[路由前缀]/detail", [App\Http\Controllers\Admin\[模块名]\[表名Controller]::class, 'detail']); diff --git a/docs/字典系统使用指南.md b/docs/字典系统使用指南.md new file mode 100644 index 0000000..169828c --- /dev/null +++ b/docs/字典系统使用指南.md @@ -0,0 +1,459 @@ +# 字典系统使用指南 + +## 概述 + +字典系统提供了统一的配置数据管理功能,包括字典类型和字典数据两个核心模块。遵循CRUD代码生成模板规范,统一使用功能性命名。 + +## 数据表结构 + +### 字典类型表 (system_dict_type) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | bigint | 字典主键 | +| name | varchar(100) | 字典名称 | +| type | varchar(100) | 字典类型(唯一) | +| status | tinyint | 状态(0正常 1停用) | +| remark | varchar(500) | 备注 | +| creator | varchar(64) | 创建者 | +| create_time | datetime | 创建时间 | +| updater | varchar(64) | 更新者 | +| update_time | datetime | 更新时间 | +| deleted | bit(1) | 是否删除 | +| deleted_time | datetime | 删除时间 | + +### 字典数据表 (system_dict_data) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | bigint | 字典编码 | +| sort | int | 字典排序 | +| label | varchar(100) | 字典标签 | +| value | varchar(100) | 字典键值 | +| dict_type | varchar(100) | 字典类型 | +| status | tinyint | 状态(0正常 1停用) | +| color_type | varchar(100) | 颜色类型 | +| css_class | varchar(100) | CSS样式 | +| remark | varchar(500) | 备注 | +| creator | varchar(64) | 创建者 | +| create_time | datetime | 创建时间 | +| updater | varchar(64) | 更新者 | +| update_time | datetime | 更新时间 | +| deleted | bit(1) | 是否删除 | + +## API接口说明 + +### 字典类型接口 + +#### 基础CRUD接口 + +1. **获取字典类型详情** + ```http + POST /admin/system/dict/type/detail + Content-Type: application/json + + { + "id": 1 + } + ``` + +2. **创建字典类型** + ```http + POST /admin/system/dict/type/create + Content-Type: application/json + + { + "name": "性别", + "type": "sys_gender", + "status": 0, + "remark": "用户性别字典" + } + ``` + +3. **更新字典类型** + ```http + POST /admin/system/dict/type/update + Content-Type: application/json + + { + "id": 1, + "name": "性别", + "type": "sys_gender", + "status": 0, + "remark": "用户性别字典" + } + ``` + +4. **删除字典类型** + ```http + POST /admin/system/dict/type/delete + Content-Type: application/json + + { + "id": 1 + } + ``` + +5. **获取字典类型列表** + ```http + POST /admin/system/dict/type/list + Content-Type: application/json + + { + "page": 1, + "page_size": 15, + "name": "性别", + "type": "sys_gender", + "status": 0, + "create_time_start": "2024-01-01", + "create_time_end": "2024-12-31" + } + ``` + +6. **获取简单列表** + ```http + POST /admin/system/dict/type/simple/list + ``` + +#### 批量操作接口 + +7. **批量删除字典类型** + ```http + POST /admin/system/dict/type/batch/delete + Content-Type: application/json + + { + "ids": [1, 2, 3] + } + ``` + +8. **更新字典类型状态** + ```http + POST /admin/system/dict/type/status + Content-Type: application/json + + { + "id": 1, + "status": 1 + } + ``` + +9. **批量更新状态** + ```http + POST /admin/system/dict/type/batch/status + Content-Type: application/json + + { + "ids": [1, 2, 3], + "status": 1 + } + ``` + +#### 业务扩展接口 + +10. **获取统计信息** + ```http + POST /admin/system/dict/type/statistics + ``` + +11. **获取选项列表** + ```http + POST /admin/system/dict/type/options + ``` + +12. **根据类型获取详情** + ```http + POST /admin/system/dict/type/get-by-type + Content-Type: application/json + + { + "type": "sys_gender" + } + ``` + +### 字典数据接口 + +#### 基础CRUD接口 + +1. **获取字典数据详情** + ```http + POST /admin/system/dict/data/detail + Content-Type: application/json + + { + "id": 1 + } + ``` + +2. **创建字典数据** + ```http + POST /admin/system/dict/data/create + Content-Type: application/json + + { + "label": "男", + "value": "1", + "dict_type": "sys_gender", + "sort": 1, + "status": 0, + "color_type": "primary", + "css_class": "", + "remark": "男性" + } + ``` + +3. **更新字典数据** + ```http + POST /admin/system/dict/data/update + Content-Type: application/json + + { + "id": 1, + "label": "男", + "value": "1", + "dict_type": "sys_gender", + "sort": 1, + "status": 0, + "color_type": "primary", + "css_class": "", + "remark": "男性" + } + ``` + +4. **删除字典数据** + ```http + POST /admin/system/dict/data/delete + Content-Type: application/json + + { + "id": 1 + } + ``` + +5. **获取字典数据列表** + ```http + POST /admin/system/dict/data/list + Content-Type: application/json + + { + "page": 1, + "page_size": 15, + "label": "男", + "value": "1", + "dict_type": "sys_gender", + "status": 0, + "color_type": "primary" + } + ``` + +6. **获取简单列表** + ```http + POST /admin/system/dict/data/simple/list + Content-Type: application/json + + { + "dict_type": "sys_gender" + } + ``` + +#### 批量操作接口 + +7. **批量删除字典数据** + ```http + POST /admin/system/dict/data/batch/delete + Content-Type: application/json + + { + "ids": [1, 2, 3] + } + ``` + +8. **更新状态** + ```http + POST /admin/system/dict/data/status + Content-Type: application/json + + { + "id": 1, + "status": 1 + } + ``` + +9. **批量更新状态** + ```http + POST /admin/system/dict/data/batch/status + Content-Type: application/json + + { + "ids": [1, 2, 3], + "status": 1 + } + ``` + +10. **更新排序** + ```http + POST /admin/system/dict/data/sort + Content-Type: application/json + + { + "id": 1, + "sort": 10 + } + ``` + +11. **批量更新排序** + ```http + POST /admin/system/dict/data/batch/sort + Content-Type: application/json + + { + "sort_data": [ + {"id": 1, "sort": 1}, + {"id": 2, "sort": 2}, + {"id": 3, "sort": 3} + ] + } + ``` + +#### 业务查询接口 + +12. **根据字典类型获取数据列表** + ```http + POST /admin/system/dict/data/get-by-type + Content-Type: application/json + + { + "dict_type": "sys_gender", + "only_active": true + } + ``` + +13. **获取选项列表** + ```http + POST /admin/system/dict/data/options + Content-Type: application/json + + { + "dict_type": "sys_gender", + "only_active": true + } + ``` + +14. **根据类型和值获取标签** + ```http + POST /admin/system/dict/data/get-label + Content-Type: application/json + + { + "dict_type": "sys_gender", + "value": "1" + } + ``` + +15. **根据类型和值获取字典数据** + ```http + POST /admin/system/dict/data/get-by-type-value + Content-Type: application/json + + { + "dict_type": "sys_gender", + "value": "1" + } + ``` + +16. **获取统计信息** + ```http + POST /admin/system/dict/data/statistics + Content-Type: application/json + + { + "dict_type": "sys_gender" + } + ``` + +17. **获取分组统计信息** + ```http + POST /admin/system/dict/data/group-statistics + ``` + +## 预置字典数据 + +系统预置了以下常用字典类型: + +### 系统通用字典 +- `sys_gender` - 性别(男、女、未知) +- `sys_status` - 状态(正常、停用) +- `sys_yes_no` - 是否(是、否) +- `sys_show_hide` - 显示状态(显示、隐藏) +- `sys_switch` - 系统开关(开启、关闭) +- `sys_menu_type` - 菜单类型(目录、菜单、按钮) +- `sys_user_type` - 用户类型(系统用户、学生、家长) +- `sys_operation_type` - 操作类型(新增、修改、删除、查询) + +### 教育相关字典 +- `edu_grade` - 年级(一年级~六年级) +- `edu_student_status` - 学生状态(在读、转学、毕业、退学) + +## 数据库操作 + +### 运行迁移 +```bash +php artisan migrate +``` + +### 填充测试数据 +```bash +# 填充字典类型数据 +php artisan db:seed --class=SystemDictTypeSeeder + +# 填充字典数据 +php artisan db:seed --class=SystemDictDataSeeder + +# 或者运行所有填充器 +php artisan db:seed +``` + +## 使用示例 + +### 在代码中使用字典 + +1. **获取字典选项** + ```php + use App\Models\System\SystemDictData; + + // 获取性别选项 + $genderOptions = SystemDictData::getOptionsByType('sys_gender'); + // 返回:['1' => '男', '2' => '女', '0' => '未知'] + ``` + +2. **获取字典标签** + ```php + // 根据值获取标签 + $label = SystemDictData::getLabelByTypeAndValue('sys_gender', '1'); + // 返回:'男' + ``` + +3. **获取字典数据列表** + ```php + // 获取某类型的所有数据 + $genderList = SystemDictData::getByType('sys_gender', true); + ``` + +## 系统字段配置 + +字典表启用了系统字段自动维护: +- **tenant_id** - 租户ID(如果启用多租户) +- **creator** - 创建者 +- **create_time** - 创建时间 +- **updater** - 更新者 +- **update_time** - 更新时间 +- **deleted** - 删除标识 + +## 注意事项 + +1. 字典类型的`type`字段必须唯一 +2. 同一字典类型下的`value`字段必须唯一 +3. 删除字典类型时会级联删除对应的字典数据 +4. 字典数据按`sort`字段升序排列 +5. 所有接口都支持软删除功能 +6. 系统会自动维护创建者、更新者等系统字段 diff --git a/docs/学生管理系统使用指南.md b/docs/学生管理系统使用指南.md new file mode 100644 index 0000000..3e42868 --- /dev/null +++ b/docs/学生管理系统使用指南.md @@ -0,0 +1,365 @@ +# 学生管理系统使用指南 + +## 概述 + +学生管理系统是一个完整的学生信息管理解决方案,支持学生基本信息管理、家长关系管理、班级分配、状态管理等功能。 + +## 主要功能 + +### 1. 学生基本信息管理 +- 学生CRUD操作 +- 家长CRUD操作 +- 用户名、邮箱、手机号唯一性验证 +- 密码加密存储 + +### 2. 家长子女关系管理 +- 家长和子女关系绑定 +- 家长查看子女信息 +- 关系解除功能 + +### 3. 班级管理 +- 学生班级分配 +- 班级转移功能 +- 班级历史记录 +- 批量操作 + +### 4. 状态管理 +- 用户状态控制(正常/禁用/欠费/未激活) +- 密码重置功能 +- 统计信息查看 + +## 数据库设计 + +### 学生表 (student) +```sql +-- 主要字段 +id -- 主键 +username -- 用户名(唯一) +real_name -- 真实姓名 +password -- 加密密码 +salt -- 密码盐值 +role -- 角色(2:学生,3:家长) +grade_id -- 年级ID +parent_id -- 家长ID +email -- 邮箱(唯一) +phone_number -- 手机号(唯一) +status -- 状态(0:禁用,1:正常,2:欠费,3:未激活) +sex -- 性别(0:不填,1:男,2:女) +reg_time -- 注册时间 +reg_ip -- 注册IP +-- 其他字段... +``` + +### 学生班级关联表 (student_class) +```sql +-- 主要字段 +id -- 主键 +student_id -- 学生ID +school_id -- 学校ID +class_id -- 班级ID +join_time -- 入班时间 +leave_time -- 离班时间 +status -- 状态(1:正常在读,2:已转班,3:已毕业,4:退学) +remark -- 备注 +created_at -- 创建时间 +updated_at -- 更新时间 +``` + +## API接口 + +### 学生管理接口 + +#### 1. 获取学生列表 +``` +GET/POST /admin/student/list +``` + +**参数:** +- `page`: 页码 +- `page_size`: 每页数量 +- `username`: 用户名(模糊搜索) +- `real_name`: 真实姓名(模糊搜索) +- `email`: 邮箱(模糊搜索) +- `phone_number`: 手机号(模糊搜索) +- `role`: 角色(2:学生,3:家长) +- `status`: 状态 +- `sex`: 性别 +- `grade_id`: 年级ID +- `parent_id`: 家长ID +- `class_id`: 班级ID + +**返回示例:** +```json +{ + "success": true, + "data": { + "data": [ + { + "id": 1, + "username": "student001", + "real_name": "张三", + "role": 2, + "role_name": "学生", + "status": 1, + "status_name": "正常", + "sex": 1, + "sex_name": "男", + "email": "student001@example.com", + "phone_number": "13800138001", + "current_class": { + "id": 1, + "name": "一年级1班" + }, + "parent": { + "id": 2, + "username": "parent001", + "real_name": "张父" + } + } + ], + "total": 100, + "per_page": 15, + "current_page": 1 + }, + "msg": "获取学生列表成功" +} +``` + +#### 2. 创建学生 +``` +POST /admin/student/create +``` + +**参数:** +- `username`: 用户名(必填) +- `real_name`: 真实姓名 +- `password`: 密码(必填) +- `role`: 角色(必填,2:学生,3:家长) +- `grade_id`: 年级ID +- `parent_id`: 家长ID +- `email`: 邮箱 +- `phone_number`: 手机号 +- `status`: 状态 +- `sex`: 性别 + +#### 3. 更新学生 +``` +PUT/POST /admin/student/update +``` + +**参数:** +- `id`: 学生ID(必填) +- 其他参数同创建接口 + +#### 4. 删除学生 +``` +DELETE/POST /admin/student/delete +``` + +**参数:** +- `id`: 学生ID(必填) + +#### 5. 获取学生详情 +``` +GET/POST /admin/student/detail +``` + +**参数:** +- `id`: 学生ID(必填) + +#### 6. 重置密码 +``` +POST /admin/student/reset-password +``` + +**参数:** +- `id`: 学生ID(必填) +- `password`: 新密码(必填) + +#### 7. 更新状态 +``` +POST /admin/student/status +``` + +**参数:** +- `id`: 学生ID(必填) +- `status`: 状态(必填,0-3) + +#### 8. 获取班级历史 +``` +GET/POST /admin/student/class-history +``` + +**参数:** +- `id`: 学生ID(必填) + +#### 9. 分配到班级 +``` +POST /admin/student/assign-class +``` + +**参数:** +- `id`: 学生ID(必填) +- `class_id`: 班级ID(必填) +- `join_time`: 入班时间 +- `remark`: 备注 + +#### 10. 从班级移除 +``` +POST /admin/student/remove-class +``` + +**参数:** +- `id`: 学生ID(必填) +- `class_id`: 班级ID(必填) +- `status`: 离班状态 +- `leave_time`: 离班时间 +- `remark`: 备注 + +### 家长管理接口 + +#### 1. 获取家长的子女列表 +``` +GET/POST /admin/student/children +``` + +**参数:** +- `id`: 家长ID(必填) + +#### 2. 绑定家长和子女 +``` +POST /admin/student/bind-child +``` + +**参数:** +- `id`: 家长ID(必填) +- `child_id`: 子女ID(必填) + +#### 3. 解除家长和子女绑定 +``` +POST /admin/student/unbind-child +``` + +**参数:** +- `id`: 子女ID(必填) + +### 统计接口 + +#### 1. 获取学生统计信息 +``` +GET/POST /admin/student/statistics +``` + +**返回示例:** +```json +{ + "success": true, + "data": { + "total_students": 150, + "total_parents": 80, + "normal_students": 140, + "male_students": 75, + "female_students": 75, + "status_distribution": { + "normal": 140, + "disabled": 5, + "arrears": 3, + "inactive": 2 + } + }, + "msg": "获取统计信息成功" +} +``` + +## 使用示例 + +### 1. 创建学生 +```bash +curl -X POST http://localhost:8000/admin/student/create \ + -H "Content-Type: application/json" \ + -d '{ + "username": "student123", + "real_name": "测试学生", + "password": "123456", + "role": 2, + "email": "test@example.com", + "phone_number": "13800138000", + "sex": 1, + "status": 1 + }' +``` + +### 2. 查询学生列表 +```bash +curl -X GET "http://localhost:8000/admin/student/list?page=1&page_size=10&role=2&status=1" +``` + +### 3. 分配学生到班级 +```bash +curl -X POST http://localhost:8000/admin/student/assign-class \ + -H "Content-Type: application/json" \ + -d '{ + "id": 1, + "class_id": 5, + "join_time": "2024-01-01 08:00:00", + "remark": "新学期分班" + }' +``` + +### 4. 绑定家长和子女 +```bash +curl -X POST http://localhost:8000/admin/student/bind-child \ + -H "Content-Type: application/json" \ + -d '{ + "id": 2, + "child_id": 1 + }' +``` + +## 数据初始化 + +### 1. 运行迁移 +```bash +php artisan migrate +``` + +### 2. 运行测试数据填充 +```bash +php artisan db:seed --class=StudentSeeder +``` + +这会创建以下测试数据: +- 3个学生用户:student001, student002, student003 +- 2个家长用户:parent001, parent002 +- 密码都是:123456 +- 已设置好家长和子女关系 + +## 注意事项 + +1. **密码安全**: 系统使用MD5+盐值加密,建议在生产环境中升级到更安全的加密方式 +2. **数据验证**: 所有输入都经过严格验证,确保数据完整性 +3. **权限控制**: 需要配合认证中间件使用 +4. **日志记录**: 所有重要操作都会记录日志 +5. **事务处理**: 重要操作使用数据库事务确保数据一致性 + +## 扩展功能 + +系统预留了以下扩展接口: +- 批量导入学生数据 +- 导出学生数据 +- 更多统计报表 +- 图片上传功能 +- 消息通知功能 + +## 技术架构 + +- **框架**: Laravel 11 +- **数据库**: MySQL +- **认证**: Laravel Sanctum +- **验证**: Laravel Validation +- **日志**: Laravel Log +- **中间件**: 自定义认证中间件 + +## 联系支持 + +如有问题或需要技术支持,请联系开发团队。 diff --git a/docs/模型配置总结.md b/docs/模型配置总结.md new file mode 100644 index 0000000..458ea25 --- /dev/null +++ b/docs/模型配置总结.md @@ -0,0 +1,246 @@ +# 模型配置总结 + +## 系统字段维护配置状态 + +### 已启用系统字段维护的模型 + +#### 学校相关模型 +- ✅ **School** (`app/Models/Schools/School.php`) + - `$enableSystemFields = true` + - `$enableTenantScope = true` + - 原因:主要业务表,需要完整的审计功能 + +- ✅ **SchoolCampus** (`app/Models/Schools/SchoolCampus.php`) + - `$enableSystemFields = true` + - `$enableTenantScope = true` + - 原因:主要业务表,需要租户隔离 + +- ✅ **SchoolClass** (`app/Models/Schools/SchoolClass.php`) + - `$enableSystemFields = true` + - `$enableTenantScope = true` + - 原因:主要业务表,需要租户隔离 + +### 关闭系统字段维护的模型 + +#### 学生相关模型 +- ❌ **Student** (`app/Models/Students/Student.php`) + - 继承 `Authenticatable`,不是 `BaseModel` + - 使用自己的时间戳字段和认证机制 + +- ❌ **StudentClass** (`app/Models/Students/StudentClass.php`) + - `$enableSystemFields = false` (默认) + - `$enableTenantScope = false` (默认) + - 原因:关联表,不需要系统字段维护 + +#### 老师相关模型 +- ❌ **TeacherClass** (`app/Models/Teachers/TeacherClass.php`) + - `$enableSystemFields = false` (默认) + - `$enableTenantScope = false` (默认) + - 原因:关联表,不需要系统字段维护 + +#### 系统相关模型 +- ❌ **SystemUserSchoolCampus** (`app/Models/System/SystemUserSchoolCampus.php`) + - `$enableSystemFields = false` (默认) + - `$enableTenantScope = false` (默认) + - 原因:关联表,不需要系统字段维护 + +- ❌ **SystemUserRole** (`app/Models/System/SystemUserRole.php`) + - 已有完整的系统字段注释 + - 原因:系统核心表,已有自己的字段管理 + +- ❌ **SystemRole** (`app/Models/System/SystemRole.php`) + - 已有完整的系统字段注释 + - 原因:系统核心表,已有自己的字段管理 + +- ❌ **SystemMenu** (`app/Models/System/SystemMenu.php`) + - 已有完整的系统字段注释 + - 原因:系统核心表,已有自己的字段管理 + +- ❌ **SystemRoleMenu** (`app/Models/System/SystemRoleMenu.php`) + - 已有完整的系统字段注释 + - 原因:关联表,不需要系统字段维护 + +- ❌ **User** (`app/Models/User.php`) + - 继承 `Authenticatable`,不是 `BaseModel` + - 使用自己的时间戳字段和认证机制 + +## 配置原则 + +### 启用系统字段维护 (`$enableSystemFields = true`) + +**适用场景:** +1. 主要业务实体表 +2. 需要审计功能的表 +3. 需要软删除的表 +4. 需要跟踪创建者和更新者的表 + +**必要条件:** +- 表中必须包含系统字段:`tenant_id`、`creator`、`create_time`、`updater`、`update_time`、`deleted` +- 继承 `BaseModel` 类 + +### 启用租户隔离 (`$enableTenantScope = true`) + +**适用场景:** +1. 多租户系统的业务表 +2. 需要按租户隔离数据的表 +3. 用户相关的数据表 + +**必要条件:** +- 表中必须包含 `tenant_id` 字段 +- 继承 `BaseModel` 类 + +### 关闭配置 (默认状态) + +**适用场景:** +1. 关联表(如用户角色关联、学生班级关联) +2. 日志表(有自己的时间戳管理) +3. 全局共享的配置表 +4. 临时表或缓存表 +5. 非 `BaseModel` 的认证表 + +## 数据库字段要求 + +### 启用系统字段维护需要的字段 + +```sql +-- 租户ID +tenant_id INT DEFAULT 0 COMMENT '租户ID', + +-- 创建信息 +creator INT DEFAULT 0 COMMENT '创建者', +create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + +-- 更新信息 +updater INT DEFAULT 0 COMMENT '更新者', +update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + +-- 软删除 +deleted TINYINT DEFAULT 0 COMMENT '删除标识', + +-- 推荐的索引 +INDEX idx_tenant_id (tenant_id), +INDEX idx_deleted (deleted), +INDEX idx_create_time (create_time), +INDEX idx_update_time (update_time) +``` + +### 启用租户隔离需要的字段 + +```sql +-- 租户ID(必须) +tenant_id INT DEFAULT 0 COMMENT '租户ID', + +-- 推荐的索引 +INDEX idx_tenant_id (tenant_id) +``` + +## 使用建议 + +### 开发新模型时 + +1. **分析表的性质** + - 是否为主要业务表? + - 是否需要审计功能? + - 是否需要租户隔离? + +2. **设置合适的配置** + ```php + // 主要业务表 + protected $enableSystemFields = true; + protected $enableTenantScope = true; + + // 关联表 + // 保持默认关闭,不需要显式设置 + ``` + +3. **确保数据库结构匹配** + - 添加必要的系统字段 + - 创建合适的索引 + +### 升级现有项目 + +1. **检查现有模型** + - 确定哪些模型需要启用配置 + - 检查数据库字段是否完整 + +2. **逐步启用** + - 先启用主要业务表 + - 测试功能是否正常 + - 逐步扩展到其他表 + +3. **数据迁移** + - 为缺少系统字段的表添加字段 + - 填充历史数据的系统字段 + +## 性能考虑 + +### 查询性能 + +1. **为系统字段添加索引** + ```sql + ALTER TABLE table_name ADD INDEX idx_tenant_id (tenant_id); + ALTER TABLE table_name ADD INDEX idx_deleted (deleted); + ``` + +2. **合理使用全局作用域** + - 启用租户隔离会在所有查询中添加 `tenant_id` 过滤 + - 启用系统字段维护会在所有查询中添加 `deleted = 0` 过滤 + +### 写入性能 + +1. **每次保存都会更新系统字段** + - 创建时设置:`creator`、`create_time`、`tenant_id`、`deleted` + - 更新时设置:`updater`、`update_time` + +2. **批量操作优化** + - 使用 `DB::table()` 进行批量操作时,系统字段不会自动设置 + - 需要手动设置系统字段值 + +## 常见问题 + +### Q1: 如何临时跳过系统字段维护? + +```php +// 方法1:使用 DB::table() 直接操作 +DB::table('schools')->where('id', 1)->update(['name' => 'New Name']); + +// 方法2:临时禁用钩子(不推荐) +School::withoutEvents(function () { + School::where('id', 1)->update(['name' => 'New Name']); +}); +``` + +### Q2: 如何批量设置系统字段? + +```php +// 使用模型的批量操作 +$userId = auth()->id(); +$tenantId = auth()->user()->tenant_id; +$now = now(); + +School::whereIn('id', $ids)->update([ + 'updater' => $userId, + 'update_time' => $now, + 'tenant_id' => $tenantId, +]); +``` + +### Q3: 关联表是否需要 tenant_id? + +关联表通常不需要直接的 tenant_id,因为: +- 通过关联的主表来控制访问权限 +- 查询时通过关联关系自动过滤 +- 减少数据冗余 + +但在某些情况下,为了性能考虑,可能需要添加 tenant_id 字段。 + +## 总结 + +通过合理配置BaseModel的系统字段维护功能,可以实现: + +1. **灵活的字段管理** - 根据表的特点选择性启用功能 +2. **统一的开发体验** - 所有表遵循相同的配置规则 +3. **高效的数据维护** - 自动处理常见的系统字段 +4. **安全的数据隔离** - 租户隔离确保数据安全 + +建议在项目开始时就制定好配置策略,确保系统的一致性和可维护性。 diff --git a/routes/admin.php b/routes/admin.php index 63214dc..3dac5aa 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -6,6 +6,9 @@ use Illuminate\Support\Facades\Auth; use App\Exceptions\Handler; require_once __DIR__ . '/admin/system_route.php'; +require_once __DIR__ . '/admin/schools_route.php'; +require_once __DIR__ . '/admin/students_route.php'; +require_once __DIR__ . '/admin/teachers_route.php'; /* |-------------------------------------------------------------------------- | 后台管理 API 路由 diff --git a/routes/admin/schools_route.php b/routes/admin/schools_route.php new file mode 100644 index 0000000..718343c --- /dev/null +++ b/routes/admin/schools_route.php @@ -0,0 +1,106 @@ +group(function () { + + /** 学校路由 */ + // 获取学校详情 + Route::match(['get', 'post'], "schools/detail", [App\Http\Controllers\Admin\Schools\SchoolController::class, 'detail']); + + // 创建学校 + Route::match(['get', 'post'], "schools/create", [App\Http\Controllers\Admin\Schools\SchoolController::class, 'create']); + + // 更新学校 + Route::match(['put', 'post'], "schools/update", [App\Http\Controllers\Admin\Schools\SchoolController::class, 'update']); + + // 删除学校 + Route::match(['delete', 'post'], "schools/delete", [App\Http\Controllers\Admin\Schools\SchoolController::class, 'delete']); + + // 获取学校列表 + Route::match(['get', 'post'], "schools/list", [App\Http\Controllers\Admin\Schools\SchoolController::class, 'list']); + + // 获取简单列表 + Route::match(['get', 'post'], "schools/simple/list", [App\Http\Controllers\Admin\Schools\SchoolController::class, 'simpleList']); + + // 批量删除学校 + Route::match(['delete', 'post'], "schools/batch/delete", [App\Http\Controllers\Admin\Schools\SchoolController::class, 'batchDelete']); + + // 获取用户管理的学校列表 + Route::match(['get', 'post'], "schools/user/list", [App\Http\Controllers\Admin\Schools\SchoolController::class, 'userSchools']); + + // 获取学校的校区列表 + Route::match(['get', 'post'], "schools/campuses", [App\Http\Controllers\Admin\Schools\SchoolController::class, 'campuses']); + + // 获取学校的班级列表 + Route::match(['get', 'post'], "schools/classes", [App\Http\Controllers\Admin\Schools\SchoolController::class, 'classes']); + + // 获取学校统计信息 + Route::match(['get', 'post'], "schools/stats", [App\Http\Controllers\Admin\Schools\SchoolController::class, 'stats']); + + /** 校区路由 */ + // 获取校区详情 + Route::match(['get', 'post'], "school/campus/detail", [App\Http\Controllers\Admin\Schools\SchoolCampusController::class, 'detail']); + + // 创建校区 + Route::match(['get', 'post'], "school/campus/create", [App\Http\Controllers\Admin\Schools\SchoolCampusController::class, 'create']); + + // 更新校区 + Route::match(['put', 'post'], "school/campus/update", [App\Http\Controllers\Admin\Schools\SchoolCampusController::class, 'update']); + + // 删除校区 + Route::match(['delete', 'post'], "school/campus/delete", [App\Http\Controllers\Admin\Schools\SchoolCampusController::class, 'delete']); + + // 获取校区列表 + Route::match(['get', 'post'], "school/campus/list", [App\Http\Controllers\Admin\Schools\SchoolCampusController::class, 'list']); + + // 获取简单列表 + Route::match(['get', 'post'], "school/campus/simple/list", [App\Http\Controllers\Admin\Schools\SchoolCampusController::class, 'simpleList']); + + // 批量删除校区 + Route::match(['delete', 'post'], "school/campus/batch/delete", [App\Http\Controllers\Admin\Schools\SchoolCampusController::class, 'batchDelete']); + + // 获取用户管理的校区列表 + Route::match(['get', 'post'], "school/campus/user/list", [App\Http\Controllers\Admin\Schools\SchoolCampusController::class, 'userCampuses']); + + // 获取校区的班级列表 + Route::match(['get', 'post'], "school/campus/classes", [App\Http\Controllers\Admin\Schools\SchoolCampusController::class, 'classes']); + + // 获取校区统计信息 + Route::match(['get', 'post'], "school/campus/stats", [App\Http\Controllers\Admin\Schools\SchoolCampusController::class, 'stats']); + + /** 班级路由 */ + // 获取班级详情 + Route::match(['get', 'post'], "school/class/detail", [App\Http\Controllers\Admin\Schools\SchoolClassController::class, 'detail']); + + // 创建班级 + Route::match(['get', 'post'], "school/class/create", [App\Http\Controllers\Admin\Schools\SchoolClassController::class, 'create']); + + // 更新班级 + Route::match(['put', 'post'], "school/class/update", [App\Http\Controllers\Admin\Schools\SchoolClassController::class, 'update']); + + // 删除班级 + Route::match(['delete', 'post'], "school/class/delete", [App\Http\Controllers\Admin\Schools\SchoolClassController::class, 'delete']); + + // 获取班级列表 + Route::match(['get', 'post'], "school/class/list", [App\Http\Controllers\Admin\Schools\SchoolClassController::class, 'list']); + + // 获取简单列表 + Route::match(['get', 'post'], "school/class/simple/list", [App\Http\Controllers\Admin\Schools\SchoolClassController::class, 'simpleList']); + + // 批量删除班级 + Route::match(['delete', 'post'], "school/class/batch/delete", [App\Http\Controllers\Admin\Schools\SchoolClassController::class, 'batchDelete']); + + // 获取用户管理的班级列表 + Route::match(['get', 'post'], "school/class/user/list", [App\Http\Controllers\Admin\Schools\SchoolClassController::class, 'userClasses']); + + // 获取班级的学生列表 + Route::match(['get', 'post'], "school/class/students", [App\Http\Controllers\Admin\Schools\SchoolClassController::class, 'students']); + + // 获取班级的老师列表 + Route::match(['get', 'post'], "school/class/teachers", [App\Http\Controllers\Admin\Schools\SchoolClassController::class, 'teachers']); + + // 获取班级统计信息 + Route::match(['get', 'post'], "school/class/stats", [App\Http\Controllers\Admin\Schools\SchoolClassController::class, 'stats']); +}); diff --git a/routes/admin/students_route.php b/routes/admin/students_route.php new file mode 100644 index 0000000..c6985e2 --- /dev/null +++ b/routes/admin/students_route.php @@ -0,0 +1,96 @@ +group(function () { + + /** 学生管理路由 */ + // 获取学生详情 + Route::match(['get', 'post'], "student/detail", [App\Http\Controllers\Admin\Students\StudentController::class, 'show']); + + // 创建学生 + Route::match(['get', 'post'], "student/create", [App\Http\Controllers\Admin\Students\StudentController::class, 'store']); + + // 更新学生 + Route::match(['put', 'post'], "student/update", [App\Http\Controllers\Admin\Students\StudentController::class, 'update']); + + // 删除学生 + Route::match(['delete', 'post'], "student/delete", [App\Http\Controllers\Admin\Students\StudentController::class, 'destroy']); + + // 获取学生列表 + Route::match(['get', 'post'], "student/list", [App\Http\Controllers\Admin\Students\StudentController::class, 'index']); + + // 重置学生密码 + Route::match(['post'], "student/reset-password", [App\Http\Controllers\Admin\Students\StudentController::class, 'resetPassword']); + + // 更新学生状态 + Route::match(['post'], "student/status", [App\Http\Controllers\Admin\Students\StudentController::class, 'updateStatus']); + + // 获取学生班级历史 + Route::match(['get', 'post'], "student/class-history", [App\Http\Controllers\Admin\Students\StudentController::class, 'classHistory']); + + // 分配学生到班级 + Route::match(['post'], "student/assign-class", [App\Http\Controllers\Admin\Students\StudentController::class, 'assignToClass']); + + // 从班级移除学生 + Route::match(['post'], "student/remove-class", [App\Http\Controllers\Admin\Students\StudentController::class, 'removeFromClass']); + + // 获取家长的子女列表 + Route::match(['get', 'post'], "student/children", [App\Http\Controllers\Admin\Students\StudentController::class, 'getChildren']); + + // 绑定家长和子女 + Route::match(['post'], "student/bind-child", [App\Http\Controllers\Admin\Students\StudentController::class, 'bindChild']); + + // 解除家长和子女绑定 + Route::match(['post'], "student/unbind-child", [App\Http\Controllers\Admin\Students\StudentController::class, 'unbindChild']); + + // 获取学生统计信息 + Route::match(['get', 'post'], "student/statistics", [App\Http\Controllers\Admin\Students\StudentController::class, 'statistics']); + + // 批量导入学生 + Route::match(['post'], "student/batch-import", [App\Http\Controllers\Admin\Students\StudentController::class, 'batchImport']); + + // 导出学生数据 + Route::match(['get', 'post'], "student/export", [App\Http\Controllers\Admin\Students\StudentController::class, 'export']); + + /** 学生班级关联路由 */ + // 获取学生班级关联详情 + Route::match(['get', 'post'], "student/class/detail", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'detail']); + + // 创建学生班级关联 + Route::match(['get', 'post'], "student/class/create", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'create']); + + // 更新学生班级关联 + Route::match(['put', 'post'], "student/class/update", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'update']); + + // 删除学生班级关联 + Route::match(['delete', 'post'], "student/class/delete", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'delete']); + + // 获取学生班级关联列表 + Route::match(['get', 'post'], "student/class/list", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'list']); + + // 批量删除学生班级关联 + Route::match(['delete', 'post'], "student/class/batch/delete", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'batchDelete']); + + // 获取学生的班级列表 + Route::match(['get', 'post'], "student/classes", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'studentClasses']); + + // 获取班级的学生列表 + Route::match(['get', 'post'], "class/students", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'classStudents']); + + // 获取学校的学生列表 + Route::match(['get', 'post'], "school/students", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'schoolStudents']); + + // 批量分配学生到班级 + Route::match(['post'], "student/class/batch/assign", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'batchAssign']); + + // 批量移除学生班级关联 + Route::match(['post'], "student/class/batch/remove", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'batchRemove']); + + // 转移学生到新班级 + Route::match(['post'], "student/class/transfer", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'transfer']); + + // 获取学生班级统计信息 + Route::match(['get', 'post'], "student/class/stats", [App\Http\Controllers\Admin\Students\StudentClassController::class, 'stats']); +}); diff --git a/routes/admin/system_route.php b/routes/admin/system_route.php index f77cec5..5cf292c 100644 --- a/routes/admin/system_route.php +++ b/routes/admin/system_route.php @@ -3,7 +3,7 @@ use Illuminate\Support\Facades\Route; /** -------------------------- 系统角色 ----------------------- */ -Route::middleware("admin")->group(function () { +Route::middleware("admin.auth")->group(function () { // 获取系统角色详情 Route::match(['get', 'post'], "system/role/detail", [App\Http\Controllers\Admin\System\SystemRoleController::class, 'detail']); @@ -33,7 +33,7 @@ Route::middleware("admin")->group(function () { }); /** -------------------------- 系统菜单 ----------------------- */ -Route::middleware("admin")->group(function () { +Route::middleware("admin.auth")->group(function () { // 获取系统菜单详情 Route::match(['get', 'post'], "system/menu/detail", [App\Http\Controllers\Admin\System\SystemMenuController::class, 'detail']); @@ -63,7 +63,7 @@ Route::middleware("admin")->group(function () { }); /** -------------------------- 角色菜单关联 ----------------------- */ -Route::middleware("admin")->group(function () { +Route::middleware("admin.auth")->group(function () { // 获取角色菜单关联列表 Route::match(['get', 'post'], "system/role-menu/list", [App\Http\Controllers\Admin\System\SystemRoleMenuController::class, 'list']); @@ -90,7 +90,7 @@ Route::middleware("admin")->group(function () { }); /** -------------------------- 用户角色关联 ----------------------- */ -Route::middleware("admin")->group(function () { +Route::middleware("admin.auth")->group(function () { // 获取用户角色关联列表 Route::match(['get', 'post'], "system/user-role/list", [App\Http\Controllers\Admin\System\SystemUserRoleController::class, 'list']); @@ -124,3 +124,144 @@ Route::middleware("admin")->group(function () { // 清除角色的所有用户关联 Route::match(['delete', 'post'], "system/user-role/clear-role-users", [App\Http\Controllers\Admin\System\SystemUserRoleController::class, 'clearRoleUsers']); }); + +/** -------------------------- 系统用户校区关联 ----------------------- */ +Route::middleware("admin.auth")->group(function () { + // 获取系统用户校区关联详情 + Route::match(['get', 'post'], "system/user/school/campus/detail", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'detail']); + + // 创建系统用户校区关联 + Route::match(['get', 'post'], "system/user/school/campus/create", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'create']); + + // 更新系统用户校区关联 + Route::match(['put', 'post'], "system/user/school/campus/update", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'update']); + + // 删除系统用户校区关联 + Route::match(['delete', 'post'], "system/user/school/campus/delete", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'delete']); + + // 获取系统用户校区关联列表 + Route::match(['get', 'post'], "system/user/school/campus/list", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'list']); + + // 批量删除系统用户校区关联 + Route::match(['delete', 'post'], "system/user/school/campus/batch/delete", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'batchDelete']); + + // 获取用户的学校校区列表 + Route::match(['get', 'post'], "system/user/school/campuses", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'userSchoolCampuses']); + + // 获取当前用户的学校校区列表 + Route::match(['get', 'post'], "system/user/my/school/campuses", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'mySchoolCampuses']); + + // 获取学校的用户列表 + Route::match(['get', 'post'], "system/school/users", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'schoolUsers']); + + // 获取校区的用户列表 + Route::match(['get', 'post'], "system/campus/users", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'campusUsers']); + + // 批量分配用户到学校校区 + Route::match(['post'], "system/user/school/campus/batch/assign", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'batchAssign']); + + // 批量移除用户学校校区关联 + Route::match(['post'], "system/user/school/campus/batch/remove", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'batchRemove']); + + // 转移用户到新的学校校区 + Route::match(['post'], "system/user/school/campus/transfer", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'transfer']); + + // 获取用户校区统计信息 + Route::match(['get', 'post'], "system/user/school/campus/stats", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'stats']); + + // 获取当前用户校区统计信息 + Route::match(['get', 'post'], "system/user/my/school/campus/stats", [App\Http\Controllers\Admin\System\SystemUserSchoolCampusController::class, 'myStats']); +}); + +/** -------------------------- 字典类型 ----------------------- */ +Route::middleware("admin.auth")->group(function () { + // 获取字典类型详情 + Route::match(['get', 'post'], "system/dict/type/detail", [App\Http\Controllers\Admin\System\SystemDictTypeController::class, 'detail']); + + // 创建字典类型 + Route::match(['get', 'post'], "system/dict/type/create", [App\Http\Controllers\Admin\System\SystemDictTypeController::class, 'create']); + + // 更新字典类型 + Route::match(['put', 'post'], "system/dict/type/update", [App\Http\Controllers\Admin\System\SystemDictTypeController::class, 'update']); + + // 删除字典类型 + Route::match(['delete', 'post'], "system/dict/type/delete", [App\Http\Controllers\Admin\System\SystemDictTypeController::class, 'delete']); + + // 获取字典类型列表 + Route::match(['get', 'post'], "system/dict/type/list", [App\Http\Controllers\Admin\System\SystemDictTypeController::class, 'list']); + + // 获取简单列表 + Route::match(['get', 'post'], "system/dict/type/simple/list", [App\Http\Controllers\Admin\System\SystemDictTypeController::class, 'simpleList']); + + // 批量删除字典类型 + Route::match(['delete', 'post'], "system/dict/type/batch/delete", [App\Http\Controllers\Admin\System\SystemDictTypeController::class, 'batchDelete']); + + // 更新字典类型状态 + Route::match(['put', 'post'], "system/dict/type/status", [App\Http\Controllers\Admin\System\SystemDictTypeController::class, 'updateStatus']); + + // 批量更新字典类型状态 + Route::match(['put', 'post'], "system/dict/type/batch/status", [App\Http\Controllers\Admin\System\SystemDictTypeController::class, 'batchUpdateStatus']); + + // 获取字典类型统计信息 + Route::match(['get', 'post'], "system/dict/type/statistics", [App\Http\Controllers\Admin\System\SystemDictTypeController::class, 'statistics']); + + // 获取字典类型选项列表 + Route::match(['get', 'post'], "system/dict/type/options", [App\Http\Controllers\Admin\System\SystemDictTypeController::class, 'options']); + + // 根据类型获取字典类型详情 + Route::match(['get', 'post'], "system/dict/type/get-by-type", [App\Http\Controllers\Admin\System\SystemDictTypeController::class, 'getByType']); +}); + +/** -------------------------- 字典数据 ----------------------- */ +Route::middleware("admin.auth")->group(function () { + // 获取字典数据详情 + Route::match(['get', 'post'], "system/dict/data/detail", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'detail']); + + // 创建字典数据 + Route::match(['get', 'post'], "system/dict/data/create", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'create']); + + // 更新字典数据 + Route::match(['put', 'post'], "system/dict/data/update", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'update']); + + // 删除字典数据 + Route::match(['delete', 'post'], "system/dict/data/delete", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'delete']); + + // 获取字典数据列表 + Route::match(['get', 'post'], "system/dict/data/list", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'list']); + + // 获取简单列表 + Route::match(['get', 'post'], "system/dict/data/simple/list", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'simpleList']); + + // 批量删除字典数据 + Route::match(['delete', 'post'], "system/dict/data/batch/delete", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'batchDelete']); + + // 更新字典数据状态 + Route::match(['put', 'post'], "system/dict/data/status", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'updateStatus']); + + // 批量更新字典数据状态 + Route::match(['put', 'post'], "system/dict/data/batch/status", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'batchUpdateStatus']); + + // 更新字典数据排序 + Route::match(['put', 'post'], "system/dict/data/sort", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'updateSort']); + + // 批量更新字典数据排序 + Route::match(['put', 'post'], "system/dict/data/batch/sort", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'batchUpdateSort']); + + // 根据字典类型获取数据列表 + Route::match(['get', 'post'], "system/dict/data/get-by-type", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'getByType']); + + // 根据字典类型获取选项列表 + Route::match(['get', 'post'], "system/dict/data/options", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'options']); + + // 根据字典类型和值获取标签 + Route::match(['get', 'post'], "system/dict/data/get-label", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'getLabel']); + + // 根据字典类型和值获取字典数据 + Route::match(['get', 'post'], "system/dict/data/get-by-type-value", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'getByTypeAndValue']); + + // 获取字典数据统计信息 + Route::match(['get', 'post'], "system/dict/data/statistics", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'statistics']); + + // 获取字典数据分组统计信息 + Route::match(['get', 'post'], "system/dict/data/group-statistics", [App\Http\Controllers\Admin\System\SystemDictDataController::class, 'groupStatistics']); +}); diff --git a/routes/admin/teachers_route.php b/routes/admin/teachers_route.php new file mode 100644 index 0000000..eb6e1d2 --- /dev/null +++ b/routes/admin/teachers_route.php @@ -0,0 +1,59 @@ +group(function () { + + /** 老师班级关联路由 */ + // 获取老师班级关联详情 + Route::match(['get', 'post'], "teacher/class/detail", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'detail']); + + // 创建老师班级关联 + Route::match(['get', 'post'], "teacher/class/create", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'create']); + + // 更新老师班级关联 + Route::match(['put', 'post'], "teacher/class/update", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'update']); + + // 删除老师班级关联 + Route::match(['delete', 'post'], "teacher/class/delete", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'delete']); + + // 获取老师班级关联列表 + Route::match(['get', 'post'], "teacher/class/list", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'list']); + + // 批量删除老师班级关联 + Route::match(['delete', 'post'], "teacher/class/batch/delete", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'batchDelete']); + + // 获取老师的班级列表 + Route::match(['get', 'post'], "teacher/classes", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'teacherClasses']); + + // 获取当前用户的班级列表 + Route::match(['get', 'post'], "teacher/my/classes", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'myClasses']); + + // 获取班级的老师列表 + Route::match(['get', 'post'], "class/teachers", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'classTeachers']); + + // 获取学校的老师列表 + Route::match(['get', 'post'], "school/teachers", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'schoolTeachers']); + + // 获取班主任列表 + Route::match(['get', 'post'], "teacher/headmans", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'headmans']); + + // 批量分配老师到班级 + Route::match(['post'], "teacher/class/batch/assign", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'batchAssign']); + + // 批量移除老师班级关联 + Route::match(['post'], "teacher/class/batch/remove", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'batchRemove']); + + // 设置班主任 + Route::match(['post'], "teacher/headman/set", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'setHeadman']); + + // 取消班主任 + Route::match(['post'], "teacher/headman/remove", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'removeHeadman']); + + // 获取老师班级统计信息 + Route::match(['get', 'post'], "teacher/class/stats", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'stats']); + + // 获取当前用户班级统计信息 + Route::match(['get', 'post'], "teacher/my/stats", [App\Http\Controllers\Admin\Teachers\TeacherClassController::class, 'myStats']); +});