514 lines
16 KiB
PHP
514 lines
16 KiB
PHP
<?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(),
|
|
]
|
|
];
|
|
}
|
|
}
|