新增学校、校区、班级和学生管理功能,包括控制器、请求验证、服务层和模型,完善CRUD操作及统计功能,更新路由以支持新功能。

This commit is contained in:
zijing 2025-07-15 17:35:13 +08:00
parent a621de91ff
commit b43bf56e8e
58 changed files with 10540 additions and 26 deletions

View File

@ -59,7 +59,7 @@ class AuthController extends BaseController
->first(); ->first();
if (!$user || !Hash::check($credentials['password'], $user->password)) { if (!$user || !Hash::check($credentials['password'], $user->password)) {
return $this->Field('用户名或密码错误,或账户已被停用', 401); return $this->Field('用户名或密码错误,或账户已被停用', 404);
} }
// 设备名称默认为APP // 设备名称默认为APP
@ -84,6 +84,10 @@ class AuthController extends BaseController
'mobile' => $user->mobile, 'mobile' => $user->mobile,
'avatar' => $user->avatar, 'avatar' => $user->avatar,
'dept_id' => $user->dept_id, 'dept_id' => $user->dept_id,
'sex' => $user->sex,
'status' => $user->status,
'login_ip' => $user->login_ip,
'login_date' => $user->login_date,
], ],
'token' => [ 'token' => [
'access_token' => $token->plainTextToken, 'access_token' => $token->plainTextToken,

View File

@ -0,0 +1,119 @@
<?php
namespace App\Http\Controllers\Admin\Schools;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Admin\Schools\SchoolCampusRequest;
use App\Services\Schools\SchoolCampusService;
use Illuminate\Http\JsonResponse;
/**
* 校区控制器
*/
class SchoolCampusController extends BaseController
{
public function __construct(
private SchoolCampusService $schoolCampusService
) {}
/**
* 获取校区列表
*/
public function list(SchoolCampusRequest $request): JsonResponse
{
$params = $request->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);
}
}

View File

@ -0,0 +1,132 @@
<?php
namespace App\Http\Controllers\Admin\Schools;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Admin\Schools\SchoolClassRequest;
use App\Services\Schools\SchoolClassService;
use Illuminate\Http\JsonResponse;
/**
* 班级控制器
*/
class SchoolClassController extends BaseController
{
public function __construct(
private SchoolClassService $schoolClassService
) {}
/**
* 获取班级列表
*/
public function list(SchoolClassRequest $request): JsonResponse
{
$params = $request->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);
}
}

View File

@ -0,0 +1,128 @@
<?php
namespace App\Http\Controllers\Admin\Schools;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Admin\Schools\SchoolRequest;
use App\Services\Schools\SchoolService;
use Illuminate\Http\JsonResponse;
/**
* 学校控制器
*/
class SchoolController extends BaseController
{
public function __construct(
private SchoolService $schoolService
) {}
/**
* 获取学校列表
*/
public function list(SchoolRequest $request): JsonResponse
{
$params = $request->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);
}
}

View File

@ -0,0 +1,159 @@
<?php
namespace App\Http\Controllers\Admin\Students;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Admin\Students\StudentClassRequest;
use App\Services\Students\StudentClassService;
use Illuminate\Http\JsonResponse;
/**
* 学生班级关联控制器
*/
class StudentClassController extends BaseController
{
public function __construct(
private StudentClassService $studentClassService
) {}
/**
* 获取学生班级关联列表
*/
public function list(StudentClassRequest $request): JsonResponse
{
$params = $request->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);
}
}

View File

@ -0,0 +1,273 @@
<?php
namespace App\Http\Controllers\Admin\Students;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Admin\Students\StudentRequest;
use App\Services\Students\StudentService;
use Illuminate\Http\Request;
/**
* 学生管理控制器
*/
class StudentController extends BaseController
{
protected $studentService;
public function __construct(StudentService $studentService)
{
$this->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([], '导出功能待实现');
}
}

View File

@ -0,0 +1,188 @@
<?php
namespace App\Http\Controllers\Admin\System;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Admin\System\SystemDictDataRequest;
use App\Services\System\SystemDictDataService;
use Illuminate\Http\JsonResponse;
/**
* 字典数据控制器
*/
class SystemDictDataController extends BaseController
{
public function __construct(
private SystemDictDataService $systemDictDataService
) {}
/**
* 获取字典数据列表
*/
public function list(SystemDictDataRequest $request): JsonResponse
{
$params = $request->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);
}
}

View File

@ -0,0 +1,136 @@
<?php
namespace App\Http\Controllers\Admin\System;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Admin\System\SystemDictTypeRequest;
use App\Services\System\SystemDictTypeService;
use Illuminate\Http\JsonResponse;
/**
* 字典类型控制器
*/
class SystemDictTypeController extends BaseController
{
public function __construct(
private SystemDictTypeService $systemDictTypeService
) {}
/**
* 获取字典类型列表
*/
public function list(SystemDictTypeRequest $request): JsonResponse
{
$params = $request->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);
}
}

View File

@ -0,0 +1,183 @@
<?php
namespace App\Http\Controllers\Admin\System;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Admin\System\SystemUserSchoolCampusRequest;
use App\Services\System\SystemUserSchoolCampusService;
use Illuminate\Http\JsonResponse;
/**
* 系统用户校区关联控制器
*/
class SystemUserSchoolCampusController extends BaseController
{
public function __construct(
private SystemUserSchoolCampusService $systemUserSchoolCampusService
) {}
/**
* 获取用户校区关联列表
*/
public function list(SystemUserSchoolCampusRequest $request): JsonResponse
{
$params = $request->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);
}
}

View File

@ -0,0 +1,202 @@
<?php
namespace App\Http\Controllers\Admin\Teachers;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Admin\Teachers\TeacherClassRequest;
use App\Services\Teachers\TeacherClassService;
use Illuminate\Http\JsonResponse;
/**
* 老师班级关联控制器
*/
class TeacherClassController extends BaseController
{
public function __construct(
private TeacherClassService $teacherClassService
) {}
/**
* 获取老师班级关联列表
*/
public function list(TeacherClassRequest $request): JsonResponse
{
$params = $request->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);
}
}

View File

@ -0,0 +1,169 @@
<?php
namespace App\Http\Requests\Admin\Schools;
use App\Http\Requests\BaseRequest;
/**
* 校区请求验证
*/
class SchoolCampusRequest extends BaseRequest
{
/**
* 获取特定的验证规则
*/
protected function getSpecificRules(): array
{
$action = $this->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' => '至少选择一条记录进行删除',
];
}
}

View File

@ -0,0 +1,193 @@
<?php
namespace App\Http\Requests\Admin\Schools;
use App\Http\Requests\BaseRequest;
/**
* 班级请求验证
*/
class SchoolClassRequest extends BaseRequest
{
/**
* 获取特定的验证规则
*/
protected function getSpecificRules(): array
{
$action = $this->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' => '至少选择一条记录进行删除',
];
}
}

View File

@ -0,0 +1,193 @@
<?php
namespace App\Http\Requests\Admin\Schools;
use App\Http\Requests\BaseRequest;
/**
* 学校请求验证
*/
class SchoolRequest extends BaseRequest
{
/**
* 获取特定的验证规则
*/
protected function getSpecificRules(): array
{
$action = $this->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' => '指定的校区不存在',
];
}
}

View File

@ -0,0 +1,213 @@
<?php
namespace App\Http\Requests\Admin\Students;
use App\Http\Requests\BaseRequest;
/**
* 学生班级关联请求验证
*/
class StudentClassRequest extends BaseRequest
{
/**
* 获取特定的验证规则
*/
protected function getSpecificRules(): array
{
$action = $this->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' => '至少选择一条记录进行删除',
];
}
}

View File

@ -0,0 +1,175 @@
<?php
namespace App\Http\Requests\Admin\Students;
use App\Http\Requests\BaseRequest;
use App\Models\Students\Student;
/**
* 学生请求验证类
*/
class StudentRequest extends BaseRequest
{
/**
* 获取特定的验证规则
* @return array
*/
protected function getSpecificRules(): array
{
$rules = [];
switch ($this->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', '不能指定自己为家长');
}
}
});
}
}

View File

@ -0,0 +1,424 @@
<?php
namespace App\Http\Requests\Admin\System;
use App\Http\Requests\BaseRequest;
use App\Models\System\SystemDictData;
use App\Models\System\SystemDictType;
use Illuminate\Validation\Rule;
/**
* 字典数据请求验证类
*/
class SystemDictDataRequest extends BaseRequest
{
/**
* 获取验证规则
*/
public function rules(): array
{
$action = $this->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();
}
}

View File

@ -0,0 +1,266 @@
<?php
namespace App\Http\Requests\Admin\System;
use App\Http\Requests\BaseRequest;
use App\Models\System\SystemDictType;
use Illuminate\Validation\Rule;
/**
* 字典类型请求验证类
*/
class SystemDictTypeRequest extends BaseRequest
{
/**
* 获取验证规则
*/
public function rules(): array
{
$action = $this->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();
}
}

View File

@ -0,0 +1,220 @@
<?php
namespace App\Http\Requests\Admin\System;
use App\Http\Requests\BaseRequest;
/**
* 系统用户校区关联请求验证
*/
class SystemUserSchoolCampusRequest extends BaseRequest
{
/**
* 获取特定的验证规则
*/
protected function getSpecificRules(): array
{
$action = $this->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' => '至少选择一条记录进行删除',
];
}
}

View File

@ -0,0 +1,242 @@
<?php
namespace App\Http\Requests\Admin\Teachers;
use App\Http\Requests\BaseRequest;
/**
* 老师班级关联请求验证
*/
class TeacherClassRequest extends BaseRequest
{
/**
* 获取特定的验证规则
*/
protected function getSpecificRules(): array
{
$action = $this->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' => '至少选择一条记录进行删除',
];
}
}

View File

@ -14,6 +14,19 @@ class BaseModel extends Model
const UPDATED_AT = 'update_time'; const UPDATED_AT = 'update_time';
const DELETED_AT = 'deleted'; 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 public function setCreateFields(): void
{ {
if (!$this->enableSystemFields) {
return;
}
$userId = auth('admin')->id() ?? 0; $userId = auth('admin')->id() ?? 0;
$tenantId = $this->getCurrentTenantId() ?? 0; $tenantId = $this->getCurrentTenantId() ?? 0;
@ -94,6 +111,10 @@ class BaseModel extends Model
*/ */
public function setUpdateFields(): void public function setUpdateFields(): void
{ {
if (!$this->enableSystemFields) {
return;
}
$userId = auth('admin')->id() ?? 0; $userId = auth('admin')->id() ?? 0;
$this->updater = $userId; $this->updater = $userId;
@ -126,28 +147,36 @@ class BaseModel extends Model
*/ */
protected static function booted() protected static function booted()
{ {
// 创建时自动设置系统字段 $model = new static;
static::creating(function ($model) {
$model->setCreateFields();
});
// 更新时自动设置系统字段 // 只有启用系统字段维护的模型才自动设置系统字段
static::updating(function ($model) { if ($model->enableSystemFields) {
$model->setUpdateFields(); // 创建时自动设置系统字段
}); static::creating(function ($model) {
$model->setCreateFields();
});
// 全局作用域:自动过滤租户数据 // 更新时自动设置系统字段
static::addGlobalScope('tenant', function ($builder) { static::updating(function ($model) {
$model = new static; $model->setUpdateFields();
$tenantId = $model->getCurrentTenantId(); });
if ($tenantId !== null) {
$builder->where('tenant_id', $tenantId);
}
});
// 全局作用域:自动过滤已删除数据 // 全局作用域:自动过滤已删除数据
static::addGlobalScope('not_deleted', function ($builder) { static::addGlobalScope('not_deleted', function ($builder) {
$builder->where('deleted', 0); $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);
}
});
}
} }
} }

View File

@ -0,0 +1,118 @@
<?php
namespace App\Models\Schools;
use App\Models\BaseModel;
use App\Models\Schools\SchoolCampus;
use App\Models\Schools\SchoolClass;
use App\Models\System\SystemUserSchoolCampus;
/**
* 学校模型
* @package App\Models\Schools
* @property int $id 主键ID
* @property int $type 学校类型
* @property string $name 学校名称
* @property string|null $alias 学校别名
* @property int $province_id 省份ID
* @property int $city_id 城市ID
* @property int $district_id 区县ID
* @property string|null $grade_period 学段
* @property int $status 状态
* @property string|null $code 学校代码
* @property int $sno_automatic 学号自动生成
* @property int $show_order 显示顺序
* @property int $is_open_user_login 是否开放用户登录
* @property int $max_sno 最大学号
* @property string|null $default_password 默认密码
* @property int $school_district_id 学区ID
* @property \Carbon\Carbon $created_at 创建时间
* @property \Carbon\Carbon $updated_at 更新时间
* @property \Carbon\Carbon|null $deleted_at 删除时间
* @property int $tenant_id 租户ID
*/
class School extends BaseModel
{
protected $table = 'school';
/**
* 启用系统字段自动维护
*/
protected $enableSystemFields = true;
/**
* 启用租户隔离
*/
protected $enableTenantScope = true;
protected $fillable = [
'type',
'name',
'alias',
'province_id',
'city_id',
'district_id',
'grade_period',
'status',
'code',
'sno_automatic',
'show_order',
'is_open_user_login',
'max_sno',
'default_password',
'school_district_id',
];
protected $casts = [
'type' => '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');
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace App\Models\Schools;
use App\Models\BaseModel;
use App\Models\Schools\School;
use App\Models\Schools\SchoolClass;
use App\Models\System\SystemUserSchoolCampus;
/**
* 校区模型
* @package App\Models\Schools
* @property int $id 主键ID
* @property string $name 校区名称
* @property string|null $alias 校区别名
* @property int $school_id 所属学校ID
* @property string|null $code 校区代码
* @property bool $self_support 是否自营
* @property int $is_open_user_login 是否开放用户登录
* @property int $status 状态
* @property \Carbon\Carbon $created_at 创建时间
* @property \Carbon\Carbon $updated_at 更新时间
* @property \Carbon\Carbon|null $deleted_at 删除时间
* @property int $tenant_id 租户ID
*/
class SchoolCampus extends BaseModel
{
protected $table = 'school_campus';
/**
* 启用系统字段自动维护
*/
protected $enableSystemFields = true;
/**
* 启用租户隔离
*/
protected $enableTenantScope = true;
protected $fillable = [
'name',
'alias',
'school_id',
'code',
'self_support',
'is_open_user_login',
'status',
];
protected $casts = [
'school_id' => '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);
}
}

View File

@ -0,0 +1,107 @@
<?php
namespace App\Models\Schools;
use App\Models\BaseModel;
use App\Models\Schools\School;
use App\Models\Schools\SchoolCampus;
use App\Models\Students\StudentClass;
use App\Models\Teachers\TeacherClass;
/**
* 班级模型
* @package App\Models\Schools
* @property int $id 主键ID
* @property int $type 班级类型
* @property string $name 班级名称
* @property int $school_id 所属学校ID
* @property int $campus_id 所属校区ID
* @property int $grade_id 年级ID
* @property int $number 班级编号
* @property string|null $code 班级代码
* @property int $status 状态
* @property int $level 班级级别
* @property int $is_open_user_login 是否开放用户登录
* @property \Carbon\Carbon $created_at 创建时间
* @property \Carbon\Carbon $updated_at 更新时间
* @property \Carbon\Carbon|null $deleted_at 删除时间
* @property int $tenant_id 租户ID
*/
class SchoolClass extends BaseModel
{
protected $table = 'school_class';
/**
* 启用系统字段自动维护
*/
protected $enableSystemFields = true;
/**
* 启用租户隔离
*/
protected $enableTenantScope = true;
protected $fillable = [
'type',
'name',
'school_id',
'campus_id',
'grade_id',
'number',
'code',
'status',
'level',
'is_open_user_login',
];
protected $casts = [
'type' => '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);
}
}

View File

@ -0,0 +1,416 @@
<?php
namespace App\Models\Students;
use App\Models\BaseModel;
use App\Models\Students\StudentClass;
use App\Models\Schools\School;
use App\Models\Schools\SchoolClass;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
/**
* 学生模型
* @package App\Models\Students
* @property int $id 主键ID
* @property string $username 用户名
* @property string|null $real_name 真实姓名
* @property string $password 密码
* @property string $salt 随机串
* @property int $role 所属角色(2:学生 3:家长)
* @property int $grade_id 年级ID
* @property int $parent_id 家长ID
* @property string|null $email 电子邮件
* @property string|null $phone_number 手机号
* @property string $title 头衔
* @property string $avatar 头像URL
* @property \Carbon\Carbon|null $reg_time 注册时间
* @property string|null $reg_ip 注册IP
* @property int $status 用户状态(0禁用1正常2欠费3未激活)
* @property int $vip_level 常规VIP等级
* @property int $sex 性别(1男2女0不填)
* @property string|null $qq QQ号
* @property string|null $examing_number 考号
*/
class Student extends Authenticatable
{
use HasApiTokens, Notifiable;
/**
* 数据表名
* @var string
*/
protected $table = 'student';
/**
* 主键
* @var string
*/
protected $primaryKey = 'id';
/**
* 关闭自动时间戳
* @var bool
*/
public $timestamps = false;
/**
* 可批量赋值的字段
* @var array
*/
protected $fillable = [
'username',
'real_name',
'password',
'salt',
'role',
'grade_id',
'parent_id',
'email',
'phone_number',
'title',
'avatar',
'reg_time',
'reg_ip',
'status',
'vip_level',
'sex',
'qq',
'examing_number'
];
/**
* 隐藏的字段
* @var array
*/
protected $hidden = [
'password',
'salt',
];
/**
* 字段类型转换
* @var array
*/
protected $casts = [
'reg_time' => '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;
}
}

View File

@ -0,0 +1,173 @@
<?php
namespace App\Models\Students;
use App\Models\BaseModel;
use App\Models\Schools\School;
use App\Models\Schools\SchoolClass;
use App\Models\Students\Student;
/**
* 学生班级关联模型
* @package App\Models\Students
* @property int $id 主键ID
* @property int $student_id 学生ID
* @property int $school_id 学校ID
* @property int $class_id 班级ID
* @property \Carbon\Carbon|null $join_time 入班时间
* @property \Carbon\Carbon|null $leave_time 离班时间
* @property int $status 状态(1正常在读2已转班3已毕业4退学)
* @property string|null $remark 备注
* @property \Carbon\Carbon $created_at 创建时间
* @property \Carbon\Carbon $updated_at 更新时间
*/
class StudentClass extends BaseModel
{
protected $table = 'student_class';
protected $fillable = [
'student_id',
'school_id',
'class_id',
'join_time',
'leave_time',
'status',
'remark',
];
protected $casts = [
'student_id' => '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);
}
}

View File

@ -0,0 +1,217 @@
<?php
namespace App\Models\System;
use App\Models\BaseModel;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Carbon\Carbon;
/**
* 字典数据模型
*
* @property int $id 字典编码
* @property int $sort 字典排序
* @property string $label 字典标签
* @property string $value 字典键值
* @property string $dict_type 字典类型
* @property int $status 状态0正常 1停用)
* @property string|null $color_type 颜色类型
* @property string|null $css_class css样式
* @property string|null $remark 备注
* @property string $creator 创建者
* @property Carbon\Carbon $create_time 创建时间
* @property string $updater 更新者
* @property Carbon\Carbon $update_time 更新时间
* @property bool $deleted 是否删除
*/
class SystemDictData extends BaseModel
{
protected $table = 'system_dict_data';
// 启用系统字段配置
protected $enableSystemFields = true;
protected $fillable = [
'sort',
'label',
'value',
'dict_type',
'status',
'color_type',
'css_class',
'remark',
];
protected $casts = [
'id' => '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;
}
}

View File

@ -0,0 +1,165 @@
<?php
namespace App\Models\System;
use App\Models\BaseModel;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Carbon\Carbon;
/**
* 字典类型模型
*
* @property int $id 字典主键
* @property string $name 字典名称
* @property string $type 字典类型
* @property int $status 状态0正常 1停用)
* @property string|null $remark 备注
* @property string $creator 创建者
* @property Carbon\Carbon $create_time 创建时间
* @property string $updater 更新者
* @property Carbon\Carbon $update_time 更新时间
* @property bool $deleted 是否删除
* @property Carbon\Carbon|null $deleted_time 删除时间
*/
class SystemDictType extends BaseModel
{
protected $table = 'system_dict_type';
// 启用系统字段配置
protected $enableSystemFields = true;
protected $fillable = [
'name',
'type',
'status',
'remark',
];
protected $casts = [
'id' => '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();
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace App\Models\System;
use App\Models\BaseModel;
use App\Models\Schools\School;
use App\Models\Schools\SchoolCampus;
use App\Models\User;
/**
* 系统用户校区关联模型
* @package App\Models\System
* @property int $id 主键ID
* @property int $userid 用户ID
* @property int $schoolid 学校ID
* @property int $campusid 校区ID
* @property \Carbon\Carbon $created_at 创建时间
* @property \Carbon\Carbon $updated_at 更新时间
* @property \Carbon\Carbon|null $deleted_at 删除时间
* @property int $tenant_id 租户ID
*/
class SystemUserSchoolCampus extends BaseModel
{
protected $table = 'system_user_school_campus';
protected $fillable = [
'userid',
'schoolid',
'campusid',
];
protected $casts = [
'userid' => '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);
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace App\Models\Teachers;
use App\Models\BaseModel;
use App\Models\Schools\School;
use App\Models\Schools\SchoolClass;
use App\Models\User;
/**
* 老师班级关联模型
* @package App\Models\Teachers
* @property int $id 主键ID
* @property int $teacher_id 老师ID
* @property int $school_id 学校ID
* @property int $grade_id 年级ID
* @property int $class_id 班级ID
* @property int $headman 是否班主任(1是0否)
* @property \Carbon\Carbon $created_at 创建时间
* @property \Carbon\Carbon $updated_at 更新时间
* @property \Carbon\Carbon|null $deleted_at 删除时间
* @property int $tenant_id 租户ID
*/
class TeacherClass extends BaseModel
{
protected $table = 'teacher_class';
protected $fillable = [
'teacher_id',
'school_id',
'grade_id',
'class_id',
'headman',
];
protected $casts = [
'teacher_id' => '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);
}
}

View File

@ -5,6 +5,8 @@ namespace App\Models;
use App\Models\BaseModel; use App\Models\BaseModel;
use App\Models\System\SystemRole; use App\Models\System\SystemRole;
use App\Models\System\SystemUserRole; use App\Models\System\SystemUserRole;
use App\Models\System\SystemUserSchoolCampus;
use App\Models\Teachers\TeacherClass;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
@ -172,4 +174,68 @@ class User extends Authenticatable
{ {
return $this->tenant_id; 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();
}
} }

View File

@ -0,0 +1,150 @@
<?php
namespace App\Services\Schools;
use App\Models\Schools\SchoolCampus;
use App\Services\BaseService;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* 校区服务类
*/
class SchoolCampusService extends BaseService
{
protected string $modelClass = SchoolCampus::class;
/**
* 获取校区列表
*/
public function getList(array $params): LengthAwarePaginator
{
$query = SchoolCampus::query();
// 搜索条件
if (!empty($params['keyword'])) {
$query->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('指定的学校不存在');
}
}
}
}

View File

@ -0,0 +1,208 @@
<?php
namespace App\Services\Schools;
use App\Models\Schools\SchoolClass;
use App\Services\BaseService;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* 班级服务类
*/
class SchoolClassService extends BaseService
{
protected string $modelClass = SchoolClass::class;
/**
* 获取班级列表
*/
public function getList(array $params): LengthAwarePaginator
{
$query = SchoolClass::query();
// 搜索条件
if (!empty($params['keyword'])) {
$query->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('指定的校区不属于该学校');
}
}
}
}

View File

@ -0,0 +1,160 @@
<?php
namespace App\Services\Schools;
use App\Models\Schools\School;
use App\Services\BaseService;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* 学校服务类
*/
class SchoolService extends BaseService
{
protected string $modelClass = School::class;
/**
* 获取学校列表
*/
public function getList(array $params): LengthAwarePaginator
{
$query = School::query();
// 搜索条件
if (!empty($params['keyword'])) {
$query->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']);
}
}

View File

@ -0,0 +1,206 @@
<?php
namespace App\Services\Students;
use App\Models\Students\StudentClass;
use App\Services\BaseService;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* 学生班级关联服务类
*/
class StudentClassService extends BaseService
{
protected string $modelClass = StudentClass::class;
/**
* 获取学生班级关联列表
*/
public function getList(array $params): LengthAwarePaginator
{
$query = StudentClass::query();
// 学生ID筛选
if (!empty($params['student_id'])) {
$query->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('该学生已经在此班级中');
}
}
}

View File

@ -0,0 +1,513 @@
<?php
namespace App\Services\Students;
use App\Models\Students\Student;
use App\Models\Students\StudentClass;
use App\Models\Schools\SchoolClass;
use App\Services\BaseService;
use App\Exceptions\BusinessException;
use App\Helpers\ResponseEnum;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
/**
* 学生服务类
*/
class StudentService extends BaseService
{
/**
* 构造函数
*/
public function __construct()
{
parent::__construct();
}
/**
* 获取学生列表
* @param array $params
* @return array
*/
public function getStudentList(array $params = []): array
{
$query = Student::query();
// 基础筛选
if (!empty($params['username'])) {
$query->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(),
]
];
}
}

View File

@ -0,0 +1,421 @@
<?php
namespace App\Services\System;
use App\Models\System\SystemDictData;
use App\Models\System\SystemDictType;
use App\Services\BaseService;
use App\Exceptions\BusinessException;
use App\Helpers\ResponseEnum;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
/**
* 字典数据服务类
*/
class SystemDictDataService extends BaseService
{
/**
* 获取字典数据列表(分页)
*
* @param array $params
* @return LengthAwarePaginator
*/
public function getList(array $params): LengthAwarePaginator
{
$query = SystemDictData::query();
// 字典标签搜索
if (!empty($params['label'])) {
$query->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();
}
}

View File

@ -0,0 +1,329 @@
<?php
namespace App\Services\System;
use App\Models\System\SystemDictType;
use App\Models\System\SystemDictData;
use App\Services\BaseService;
use App\Exceptions\BusinessException;
use App\Helpers\ResponseEnum;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
/**
* 字典类型服务类
*/
class SystemDictTypeService extends BaseService
{
/**
* 获取字典类型列表(分页)
*
* @param array $params
* @return LengthAwarePaginator
*/
public function getList(array $params): LengthAwarePaginator
{
$query = SystemDictType::query();
// 字典名称搜索
if (!empty($params['name'])) {
$query->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();
}
}

View File

@ -0,0 +1,271 @@
<?php
namespace App\Services\System;
use App\Models\System\SystemUserSchoolCampus;
use App\Services\BaseService;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* 系统用户校区关联服务类
*/
class SystemUserSchoolCampusService extends BaseService
{
protected string $modelClass = SystemUserSchoolCampus::class;
/**
* 获取用户校区关联列表
*/
public function getList(array $params): LengthAwarePaginator
{
$query = SystemUserSchoolCampus::query();
// 用户ID筛选
if (!empty($params['userid'])) {
$query->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('该用户已经关联了此学校和校区');
}
}
}

View File

@ -0,0 +1,262 @@
<?php
namespace App\Services\Teachers;
use App\Models\Teachers\TeacherClass;
use App\Services\BaseService;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* 老师班级关联服务类
*/
class TeacherClassService extends BaseService
{
protected string $modelClass = TeacherClass::class;
/**
* 获取老师班级关联列表
*/
public function getList(array $params): LengthAwarePaginator
{
$query = TeacherClass::query();
// 老师ID筛选
if (!empty($params['teacher_id'])) {
$query->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('该老师已经在此班级中');
}
}
}

View File

@ -22,6 +22,15 @@ return Application::configure(basePath: dirname(__DIR__))
$middleware->alias([ $middleware->alias([
'admin.auth' => \App\Http\Middleware\AdminApiAuthenticate::class, '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 { ->withExceptions(function (Exceptions $exceptions): void {
// 配置不需要报告的异常类型 // 配置不需要报告的异常类型

34
config/cors.php Normal file
View File

@ -0,0 +1,34 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => ['api/*', 'admin/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => ['Authorization'],
'max_age' => 86400, // 24小时
'supports_credentials' => true,
];

View File

@ -0,0 +1,57 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('student', function (Blueprint $table) {
$table->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');
}
};

View File

@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('system_dict_type', function (Blueprint $table) {
$table->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');
}
};

View File

@ -0,0 +1,58 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('system_dict_data', function (Blueprint $table) {
$table->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');
}
};

View File

@ -19,5 +19,11 @@ class DatabaseSeeder extends Seeder
'name' => 'Test User', 'name' => 'Test User',
'email' => 'test@example.com', 'email' => 'test@example.com',
]); ]);
// 字典数据
$this->call([
SystemDictTypeSeeder::class,
SystemDictDataSeeder::class,
]);
} }
} }

View File

@ -0,0 +1,111 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Students\Student;
class StudentSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// 创建一些测试学生数据
$students = [
[
'username' => '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');
}
}

View File

@ -0,0 +1,481 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\System\SystemDictData;
use Carbon\Carbon;
class SystemDictDataSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$now = Carbon::now();
$creator = 'system';
$dictData = [
// 性别字典数据
[
'sort' => 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('字典数据插入完成!');
}
}

View File

@ -0,0 +1,168 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\System\SystemDictType;
use Carbon\Carbon;
class SystemDictTypeSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$now = Carbon::now();
$creator = 'system';
$dictTypes = [
[
'name' => '性别',
'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('字典类型数据插入完成!');
}
}

View File

@ -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
<?php
namespace App\Models\Schools;
use App\Models\BaseModel;
/**
* 学校模型
*/
class School extends BaseModel
{
protected $table = 'school';
/**
* 启用系统字段自动维护
*/
protected $enableSystemFields = true;
/**
* 启用租户隔离
*/
protected $enableTenantScope = true;
protected $fillable = [
'name',
'type',
'status',
// ... 其他业务字段
];
}
```
### 关联表(推荐关闭)
```php
<?php
namespace App\Models\Students;
use App\Models\BaseModel;
/**
* 学生班级关联模型
*/
class StudentClass extends BaseModel
{
protected $table = 'student_class';
// 默认关闭,不需要显式设置
// protected $enableSystemFields = false;
// protected $enableTenantScope = false;
protected $fillable = [
'student_id',
'class_id',
'join_time',
'leave_time',
'status',
'remark',
];
}
```
### 日志表(只启用系统字段)
```php
<?php
namespace App\Models\Logs;
use App\Models\BaseModel;
/**
* 操作日志模型
*/
class OperationLog extends BaseModel
{
protected $table = 'operation_log';
/**
* 启用系统字段自动维护
*/
protected $enableSystemFields = true;
/**
* 日志表不需要租户隔离
*/
protected $enableTenantScope = false;
protected $fillable = [
'user_id',
'action',
'description',
'ip_address',
// ... 其他日志字段
];
}
```
## 使用建议
### 启用系统字段维护的场景
1. **主要业务表**
- 学校、用户、产品等核心业务实体
- 需要跟踪创建者、更新者信息
- 需要软删除功能
2. **配置表**
- 系统配置、字典表等
- 需要审计功能的表
3. **内容表**
- 文章、新闻、公告等内容实体
- 需要发布状态管理
### 关闭系统字段维护的场景
1. **关联表**
- 用户角色关联、学生班级关联等
- 通常不需要创建者、更新者信息
- 不需要软删除(直接删除关联关系)
2. **日志表**
- 操作日志、访问日志等
- 已经有自己的时间戳字段
- 不需要软删除
3. **临时表**
- 导入导出的临时数据
- 缓存表
### 启用租户隔离的场景
1. **多租户系统的业务表**
- 需要按租户隔离数据
- 确保数据安全性
2. **用户相关的数据表**
- 用户创建的内容
- 用户的配置信息
### 关闭租户隔离的场景
1. **全局共享的表**
- 系统配置、字典表
- 所有租户共享的数据
2. **关联表**
- 通常通过关联的主表来控制访问权限
- 不需要直接的租户隔离
## 影响和注意事项
### 启用系统字段维护的影响
1. **数据库结构要求**
- 表必须包含相应的系统字段
- 字段类型必须匹配
2. **查询影响**
- 自动过滤 `deleted = 0` 的记录
- 需要使用 `withTrashed()` 查询已删除记录
3. **性能考虑**
- 每次保存都会更新系统字段
- 建议为系统字段添加索引
### 启用租户隔离的影响
1. **查询限制**
- 所有查询都会自动加上租户过滤
- 跨租户查询需要使用 `withoutGlobalScope('tenant')`
2. **数据完整性**
- 确保所有记录都有正确的租户ID
- 防止数据泄露
## 迁移指南
### 从旧版本升级
如果你的项目已经在使用BaseModel的自动维护功能升级后需要
1. **为需要系统字段维护的模型添加配置**
```php
protected $enableSystemFields = true;
```
2. **为需要租户隔离的模型添加配置**
```php
protected $enableTenantScope = true;
```
3. **检查关联表**
- 确保关联表不启用不需要的功能
- 移除不必要的系统字段
### 新项目开发
1. **分析表的性质**
- 确定是否需要系统字段维护
- 确定是否需要租户隔离
2. **设置合适的配置**
- 主要业务表:启用所有功能
- 关联表:保持默认关闭
- 特殊表:根据需求单独配置
## 常见问题
### Q1: 如何查询已删除的记录?
```php
// 包含已删除的记录
$schools = School::withTrashed()->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. **提高灵活性** - 根据表的特点选择性启用功能
建议在项目开始时就规划好各个表的配置策略,确保系统的一致性和可维护性。

View File

@ -53,6 +53,42 @@ app/
**文件路径**`app/Models/[模块名]/[表名].php` **文件路径**`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 ```php
<?php <?php
@ -62,11 +98,35 @@ use App\Models\BaseModel;
/** /**
* [中文名称]模型 * [中文名称]模型
* @package App\Models\[模块名]
* @property int $id 主键ID
* @property string $name 名称
* @property string $code 编码
* @property int $status 状态
* @property int $sort_order 排序
* @property string $remark 备注
* @property \Carbon\Carbon $created_at 创建时间
* @property \Carbon\Carbon $updated_at 更新时间
* @property \Carbon\Carbon|null $deleted_at 删除时间
* @property int $tenant_id 租户ID
*/ */
class [表名] extends BaseModel class [表名] extends BaseModel
{ {
protected $table = '[数据库表名]'; protected $table = '[数据库表名]';
/**
* 启用系统字段自动维护
* 包括tenant_id、creator、create_time、updater、update_time、deleted
* 默认关闭主要业务表可以设置为true
*/
protected $enableSystemFields = false;
/**
* 启用租户隔离
* 默认关闭需要租户隔离的表可以设置为true
*/
protected $enableTenantScope = false;
protected $fillable = [ protected $fillable = [
'[字段1]', '[字段1]',
'[字段2]', '[字段2]',
@ -393,7 +453,7 @@ class [表名Request] extends BaseRequest
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
/** -------------------------- [中文名称] ----------------------- */ /** -------------------------- [中文名称] ----------------------- */
Route::middleware("admin")->group(function () { Route::middleware("admin.auth")->group(function () {
// 获取[中文名称]详情 // 获取[中文名称]详情
Route::match(['get', 'post'], "[路由前缀]/detail", [App\Http\Controllers\Admin\[模块名]\[表名Controller]::class, 'detail']); Route::match(['get', 'post'], "[路由前缀]/detail", [App\Http\Controllers\Admin\[模块名]\[表名Controller]::class, 'detail']);

View File

@ -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. 系统会自动维护创建者、更新者等系统字段

View File

@ -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不填12
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
- **中间件**: 自定义认证中间件
## 联系支持
如有问题或需要技术支持,请联系开发团队。

246
docs/模型配置总结.md Normal file
View File

@ -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. **安全的数据隔离** - 租户隔离确保数据安全
建议在项目开始时就制定好配置策略,确保系统的一致性和可维护性。

View File

@ -6,6 +6,9 @@ use Illuminate\Support\Facades\Auth;
use App\Exceptions\Handler; use App\Exceptions\Handler;
require_once __DIR__ . '/admin/system_route.php'; 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 路由 | 后台管理 API 路由

View File

@ -0,0 +1,106 @@
<?php
use Illuminate\Support\Facades\Route;
/** -------------------------- 学校管理 ----------------------- */
Route::middleware("admin.auth")->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']);
});

View File

@ -0,0 +1,96 @@
<?php
use Illuminate\Support\Facades\Route;
/** -------------------------- 学生管理 ----------------------- */
Route::middleware("admin.auth")->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']);
});

View File

@ -3,7 +3,7 @@
use Illuminate\Support\Facades\Route; 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']); 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']); 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']); 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']); 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::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']);
});

View File

@ -0,0 +1,59 @@
<?php
use Illuminate\Support\Facades\Route;
/** -------------------------- 老师管理 ----------------------- */
Route::middleware("admin")->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']);
});