智慧教务系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

663 lines
22 KiB

<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
namespace app\service\api\student;
use app\model\customer_resources\CustomerResources;
use app\model\student\Student;
use app\model\member\Member;
use think\facade\Db;
use core\base\BaseService;
use core\exception\CommonException;
/**
* 学员信息管理服务类
*/
class StudentService extends BaseService
{
/**
* 获取当前用户的学员列表
* @return array
*/
public function getStudentList()
{
// 获取当前登录用户ID
$customerId = $this->getUserId();
// 通过客户资源表获取user_id
$customerResource = (new CustomerResources())->where('id', $customerId)->find();
if (!$customerResource) {
throw new CommonException('用户信息不存在');
}
// 获取该用户的所有学员
$studentList = (new Student())
->where('user_id', $customerId)
->where('deleted_at', 0)
->field('id,name,gender,birthday,headimg,created_at')
->order('id desc')
->select()
->toArray();
// 计算年龄和格式化数据
foreach ($studentList as &$student) {
$student['age'] = $this->calculateAge($student['birthday']);
$student['gender_text'] = $student['gender'] == 1 ? '男' : '女';
$student['headimg'] = $student['headimg'] ? get_image_url($student['headimg']) : '';
}
return [
'list' => $studentList,
'total' => count($studentList)
];
}
/**
* 获取学员概览信息(首页用)
* @param int $studentId
* @return array
*/
public function getStudentSummary($studentId)
{
// 验证学员权限
$this->checkStudentPermission($studentId);
$student = (new Student())
->where('id', $studentId)
->where('deleted_at', 0)
->find();
if (!$student) {
throw new CommonException('学员信息不存在');
}
// 获取用户基本信息
$member = (new CustomerResources())->where('id', $student['user_id'])->find();
return [
'student_id' => $student['id'],
'name' => $student['name'],
'age' => $this->calculateAge($student['birthday']),
'gender' => $student['gender'],
'gender_text' => $student['gender'] == 1 ? '男' : '女',
'headimg' => $student['headimg'] ? get_image_url($student['headimg']) : '',
'member_name' => $member['name'] ?? '',
'created_at' => $student['created_at'],
'create_year_month' => date('Y年m月', strtotime($student['created_at'])),
'week_day' => '星期' . ['日', '一', '二', '三', '四', '五', '六'][date('w')]
];
}
/**
* 获取学员详细信息
* @param int $studentId
* @return array
*/
public function getStudentInfo($studentId)
{
// 验证学员权限
$this->checkStudentPermission($studentId);
$student = (new Student())
->where('id', $studentId)
->where('deleted_at', 0)
->find();
if (!$student) {
throw new CommonException('学员信息不存在');
}
$studentData = $student->toArray();
// 处理图片URL
$studentData['headimg'] = $studentData['headimg'] ? get_image_url($studentData['headimg']) : '';
// 计算年龄
$studentData['age'] = $this->calculateAge($studentData['birthday']);
$studentData['gender_text'] = $studentData['gender'] == 1 ? '男' : '女';
return $studentData;
}
/**
* 获取学员详细信息(包含体测信息)
* @param int $studentId
* @return array
*/
public function getStudentInfoWithPhysicalTest($studentId)
{
// 验证学员权限
$this->checkStudentPermission($studentId);
// 获取学员基本信息
$student = Db::table('school_student')
->where('id', $studentId)
->where('deleted_at', 0)
->find();
if (!$student) {
throw new CommonException('学员信息不存在');
}
// 获取最新的体测信息
$physicalTest = Db::table('school_physical_test')
->where('student_id', $studentId)
->order('created_at desc')
->find();
// 处理学员信息
$studentInfo = [
'id' => $student['id'],
'name' => $student['name'],
'gender' => $student['gender'],
'gender_text' => $student['gender'] == 1 ? '男' : '女',
'birthday' => $student['birthday'],
'emergency_contact' => $student['emergency_contact'],
'contact_phone' => $student['contact_phone'],
'note' => $student['note'],
'headimg' => $student['headimg'] ? get_image_url($student['headimg']) : '',
];
// 处理体测信息
$physicalTestInfo = [];
if ($physicalTest) {
$physicalTestInfo = [
'height' => $physicalTest['height'] ? (string)$physicalTest['height'] : '',
'weight' => $physicalTest['weight'] ? (string)$physicalTest['weight'] : '',
'test_date' => date('Y-m-d', strtotime($physicalTest['created_at']))
];
}
return [
'student_info' => $studentInfo,
'physical_test_info' => $physicalTestInfo
];
}
/**
* 更新学员信息
* @param array $data
* @return bool
*/
public function updateStudentInfo($data)
{
$studentId = $data['student_id'];
// 验证学员权限
$this->checkStudentPermission($studentId);
// 验证学员是否存在
$student = Db::table('school_student')
->where('id', $studentId)
->where('deleted_at', 0)
->find();
if (!$student) {
throw new CommonException('学员信息不存在');
}
// 允许更新的字段
$allowedFields = ['name', 'gender', 'birthday', 'emergency_contact', 'contact_phone', 'note', 'headimg'];
$updateData = [];
foreach ($allowedFields as $field) {
if (isset($data[$field]) && $data[$field] !== '') {
$updateData[$field] = $data[$field];
}
}
if (empty($updateData)) {
throw new CommonException('没有需要更新的数据');
}
// 如果有生日更新,需要重新计算年龄
if (isset($updateData['birthday'])) {
$updateData['age'] = $this->calculateAgeFromBirthday($updateData['birthday']);
}
$result = Db::table('school_student')
->where('id', $studentId)
->update($updateData);
if ($result === false) {
throw new CommonException('更新学员信息失败');
}
return true;
}
/**
* 上传学员头像
* @param int $studentId
* @return array
*/
public function uploadAvatar($studentId)
{
// 验证学员权限
$this->checkStudentPermission($studentId);
// 处理文件上传
$uploadService = new \app\service\api\upload\UploadService();
$result = $uploadService->avatar(request()->file('image'));
if (!$result) {
throw new CommonException('头像上传失败');
}
// 更新学员头像
$student = (new Student())->where('id', $studentId)->find();
$student->headimg = $result['url'];
$student->save();
return [
'url' => get_image_url($result['url']),
'path' => $result['url']
];
}
/**
* 检查学员权限(确保只能操作自己的孩子)
* @param int $studentId
* @return bool
*/
private function checkStudentPermission($studentId)
{
$customerId = $this->getUserId();
// 获取客户资源信息
$customerResource = (new CustomerResources())->where('id', $customerId)->find();
if (!$customerResource) {
throw new CommonException('用户信息不存在');
}
// 检查学员是否属于当前用户
$student = (new Student())
->where('id', $studentId)
->where('user_id', $customerId)
->where('deleted_at', 0)
->find();
if (!$student) {
throw new CommonException('无权限访问该学员信息');
}
return true;
}
/**
* 计算年龄
* @param string $birthday
* @return int
*/
private function calculateAge($birthday)
{
if (!$birthday) return 0;
$birthTime = strtotime($birthday);
if (!$birthTime) return 0;
$age = date('Y') - date('Y', $birthTime);
// 如果还没过生日,年龄减1
if (date('md') < date('md', $birthTime)) {
$age--;
}
return max(0, $age);
}
/**
* 根据生日精确计算年龄(支持小数表示)
* @param string $birthday
* @return float
*/
private function calculateAgeFromBirthday($birthday)
{
if (!$birthday) return 0;
$birthTime = strtotime($birthday);
if (!$birthTime) return 0;
$today = new \DateTime();
$birthDate = new \DateTime($birthday);
$interval = $today->diff($birthDate);
$years = $interval->y;
$months = $interval->m;
// 将月份转换为小数,如3岁11个月 = 3.11
return $years + ($months / 100);
}
/**
* 添加孩子信息
* @param array $data
* @return array
*/
public function addChild($data)
{
$customerId = $this->getUserId();
// 创建学员数据
$studentData = [
'user_id' => $customerId,
'name' => $data['name'],
'gender' => (int)$data['gender'],
'birthday' => $data['birthday'],
'headimg' => $data['headimg'] ?? '',
'emergency_contact' => $data['emergency_contact'] ?? '',
'contact_phone' => $data['contact_phone'] ?? '',
'note' => $data['note'] ?? '',
'age' => $this->calculateAgeFromBirthday($data['birthday']),
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'deleted_at' => 0
];
try {
// 插入学员数据
$studentId = Db::table('school_student')->insertGetId($studentData);
if (!$studentId) {
throw new CommonException('添加孩子失败');
}
return [
'student_id' => $studentId,
'name' => $data['name'],
'gender_text' => $data['gender'] == 1 ? '男' : '女',
'age' => $this->calculateAge($data['birthday'])
];
} catch (\Exception $e) {
throw new CommonException('添加孩子失败:' . $e->getMessage());
}
}
/**
* 获取学员课程安排列表
* @param array $params
* @return array
*/
public function getCourseScheduleList($params)
{
$studentId = $params['student_id'];
// 构建查询条件
$where = [
['pcs.student_id', '=', $studentId],
['pcs.deleted_at', '=', 0],
['cs.deleted_at', '=', 0]
];
// 日期筛选
if (!empty($params['date'])) {
$where[] = ['cs.course_date', '=', $params['date']];
}
// 日期范围筛选
if (!empty($params['start_date']) && !empty($params['end_date'])) {
$where[] = ['cs.course_date', 'between', [$params['start_date'], $params['end_date']]];
}
// 状态筛选
if (isset($params['status']) && $params['status'] !== '') {
$where[] = ['pcs.status', '=', $params['status']];
}
// 查询课程安排数据,联合两个表
$scheduleList = Db::table('school_person_course_schedule pcs')
->leftJoin('school_course_schedule cs', 'pcs.schedule_id = cs.id')
->leftJoin('school_course c', 'cs.course_id = c.id')
->leftJoin('school_personnel p', 'cs.coach_id = p.id')
->leftJoin('school_venue v', 'cs.venue_id = v.id')
->where($where)
->field('pcs.id,
cs.course_date,
COALESCE(cs.start_time,
CASE
WHEN pcs.time_slot REGEXP "^[0-9]{2}:[0-9]{2}-[0-9]{2}:[0-9]{2}$"
THEN SUBSTRING_INDEX(pcs.time_slot, "-", 1)
ELSE "09:00"
END
) as start_time,
COALESCE(cs.end_time,
CASE
WHEN pcs.time_slot REGEXP "^[0-9]{2}:[0-9]{2}-[0-9]{2}:[0-9]{2}$"
THEN SUBSTRING_INDEX(pcs.time_slot, "-", -1)
ELSE "10:00"
END
) as end_time,
cs.time_slot,
pcs.status,
pcs.cancel_reason,
c.course_name,
c.remarks as course_description,
p.name as coach_name,
v.venue_name,
60 as duration')
->order('cs.course_date desc, cs.start_time desc')
->select()
->toArray();
// 处理数据格式
foreach ($scheduleList as &$schedule) {
// 状态处理
$schedule['status_text'] = $this->getScheduleStatusText($schedule['status']);
// 时间处理
if (!$schedule['start_time'] || !$schedule['end_time']) {
// 如果没有具体时间,从time_slot中解析
$timeSlot = $schedule['time_slot'] ?? '09:00-10:00';
$times = explode('-', $timeSlot);
$schedule['start_time'] = $times[0] ?? '09:00';
$schedule['end_time'] = $times[1] ?? '10:00';
}
$schedule['time_slot'] = $schedule['start_time'] . '-' . $schedule['end_time'];
$schedule['duration'] = $schedule['duration'] ?: 60;
// 准备事项(模拟数据,实际可从课程信息中获取)
$schedule['preparation_items'] = $this->getCoursePreparationItems($schedule['course_name']);
}
return [
'list' => $scheduleList,
'total' => count($scheduleList)
];
}
/**
* 获取课程安排详情
* @param int $scheduleId
* @return array
*/
public function getCourseScheduleDetail($scheduleId)
{
// 查询课程安排详情 - 通过schedule_id关联到course_schedule表获取详细信息
$schedule = Db::table('school_person_course_schedule pcs')
->leftJoin('school_course_schedule cs', 'pcs.schedule_id = cs.id')
->leftJoin('school_course c', 'cs.course_id = c.id')
->leftJoin('school_personnel p', 'cs.coach_id = p.id')
->leftJoin('school_venue v', 'cs.venue_id = v.id')
->leftJoin('school_student s', 'pcs.student_id = s.id')
->where('pcs.id', $scheduleId)
->where('pcs.deleted_at', 0)
->field('
pcs.id,
pcs.student_id,
pcs.course_date,
COALESCE(cs.start_time,
CASE
WHEN pcs.time_slot REGEXP "^[0-9]{2}:[0-9]{2}-[0-9]{2}:[0-9]{2}$"
THEN SUBSTRING_INDEX(pcs.time_slot, "-", 1)
ELSE "09:00"
END
) as start_time,
COALESCE(cs.end_time,
CASE
WHEN pcs.time_slot REGEXP "^[0-9]{2}:[0-9]{2}-[0-9]{2}:[0-9]{2}$"
THEN SUBSTRING_INDEX(pcs.time_slot, "-", -1)
ELSE "10:00"
END
) as end_time,
pcs.status,
pcs.cancel_reason,
c.course_name,
c.remarks as course_description,
p.name as coach_name,
v.venue_name,
s.user_id,
TIMESTAMPDIFF(MINUTE, cs.start_time, cs.end_time) as duration
')
->find();
if (!$schedule) {
throw new CommonException('课程安排不存在');
}
// 验证权限
$this->checkStudentPermission($schedule['student_id']);
// 处理数据格式
$schedule['status_text'] = $this->getScheduleStatusText($schedule['status']);
$schedule['time_slot'] = $schedule['start_time'] . '-' . $schedule['end_time'];
$schedule['duration'] = $schedule['duration'] ?: 60;
$schedule['preparation_items'] = $this->getCoursePreparationItems($schedule['course_name']);
return $schedule;
}
/**
* 申请课程请假
* @param array $data
* @return bool
*/
public function requestCourseLeave($data)
{
$scheduleId = $data['schedule_id'];
$reason = $data['reason'] ?? '';
// 查询课程安排
$schedule = Db::table('school_person_course_schedule')
->where('id', $scheduleId)
->where('deleted_at', 0)
->find();
if (!$schedule) {
throw new CommonException('课程安排不存在');
}
// 验证权限
$this->checkStudentPermission($schedule['student_id']);
// 检查课程状态
if ($schedule['status'] != 0) { // 0-待上课
throw new CommonException('当前课程状态不允许请假');
}
// 获取对应的课程安排信息来检查时间
$courseSchedule = Db::table('school_course_schedule')
->where('id', $schedule['schedule_id'])
->find();
if ($courseSchedule) {
// 检查请假时间限制(上课前6小时)
$courseDateTime = $courseSchedule['course_date'] . ' ' . $courseSchedule['start_time'];
$courseTimestamp = strtotime($courseDateTime);
$currentTimestamp = time();
if ($courseTimestamp - $currentTimestamp < 6 * 3600) {
throw new CommonException('上课前6小时内不允许请假');
}
}
// 更新状态为请假
$result = Db::table('school_person_course_schedule')
->where('id', $scheduleId)
->update([
'status' => 2, // 2-请假
'cancel_reason' => $reason,
'updated_at' => date('Y-m-d H:i:s')
]);
if ($result === false) {
throw new CommonException('请假申请失败');
}
// TODO: 发送消息通知相关人员
return true;
}
/**
* 获取课程状态文本
* @param int $status
* @return string
*/
private function getScheduleStatusText($status)
{
$statusMap = [
0 => '待上课',
1 => '已完成',
2 => '请假',
3 => '取消'
];
return $statusMap[$status] ?? '未知状态';
}
/**
* 获取课程准备事项
* @param string $courseName
* @return array
*/
private function getCoursePreparationItems($courseName)
{
// 根据课程名称返回相应的准备事项
$preparationMap = [
'基础体能训练' => ['运动服装', '运动鞋', '毛巾', '水杯'],
'专项技能训练' => ['专项器材', '护具', '运动服装'],
'体适能评估' => ['轻便服装', '测试表格'],
'少儿形体课' => ['舞蹈服装', '舞蹈鞋', '毛巾'],
'成人瑜伽' => ['瑜伽垫', '舒适服装', '毛巾'],
'私教训练' => ['运动服装', '运动鞋', '水杯'],
'儿童游泳' => ['泳衣', '泳帽', '游泳镜', '毛巾'],
'暑季特训营' => ['运动服装', '防晒用品', '充足水分', '能量补充食品']
];
return $preparationMap[$courseName] ?? ['运动服装', '运动鞋', '毛巾', '水杯'];
}
/**
* 获取当前登录用户ID
* @return int
*/
private function getUserId()
{
// 从request中获取memberId(由ApiCheckToken中间件设置)
$memberId = request()->memberId();
if ($memberId) {
return $memberId;
}
// 如果没有中间件设置,尝试解析token
$token = request()->header('token');
if ($token) {
try {
$loginService = new \app\service\api\login\LoginService();
$tokenInfo = $loginService->parseToken($token);
if (!empty($tokenInfo) && isset($tokenInfo['member_id'])) {
return $tokenInfo['member_id'];
}
} catch (\Exception $e) {
// token解析失败,抛出异常
throw new CommonException('用户未登录或token无效');
}
}
// 如果都没有,抛出异常
throw new CommonException('用户未登录');
}
}