From b326b0781abb08b9e4f94603b8e712168a9483a1 Mon Sep 17 00:00:00 2001 From: zeyan <258785420@qq.com> Date: Mon, 4 Aug 2025 00:27:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apiController/CustomerResources.php | 37 ++ .../apiController/StudentManager.php | 9 +- niucloud/app/api/controller/pay/Pay.php | 2 - .../student/AttendanceController.php | 75 +++ niucloud/app/api/route/route.php | 17 +- niucloud/app/api/route/student.php | 10 + .../school/SchoolPersonCourseSchedule.php | 20 + niucloud/app/model/school/SchoolStudent.php | 20 + .../model/school/SchoolStudentCourseUsage.php | 14 + .../app/model/school/SchoolStudentCourses.php | 14 + .../service/api/apiService/CourseService.php | 184 +++++- .../apiService/CustomerResourcesService.php | 312 +++++++++- .../api/apiService/OrderTableService.php | 153 ++++- .../service/api/apiService/StudentService.php | 161 ++++- .../service/api/student/AttendanceService.php | 411 +++++++++++++ uniapp/api/apiRoute.js | 59 +- uniapp/common/util.js | 30 - uniapp/common/utils-index.js | 2 - uniapp/components/course-info-card/index.vue | 274 +++++---- uniapp/components/schedule/ScheduleDetail.vue | 125 +++- .../coach/schedule/add_schedule.vue | 1 + .../coach/student/student_list.vue | 39 +- .../clue/class_arrangement_detail.vue | 151 +++-- 学员端开发计划-前端任务.md | 482 --------------- 学员端开发计划-后端任务.md | 410 ------------- 学员端开发需求整合确认文档.md | 575 ------------------ 学员端订单接口实现完成报告.md | 290 --------- 27 files changed, 1807 insertions(+), 2070 deletions(-) create mode 100644 niucloud/app/api/controller/student/AttendanceController.php create mode 100644 niucloud/app/model/school/SchoolPersonCourseSchedule.php create mode 100644 niucloud/app/model/school/SchoolStudent.php create mode 100644 niucloud/app/model/school/SchoolStudentCourseUsage.php create mode 100644 niucloud/app/model/school/SchoolStudentCourses.php create mode 100644 niucloud/app/service/api/student/AttendanceService.php delete mode 100644 学员端开发计划-前端任务.md delete mode 100644 学员端开发计划-后端任务.md delete mode 100644 学员端开发需求整合确认文档.md delete mode 100644 学员端订单接口实现完成报告.md diff --git a/niucloud/app/api/controller/apiController/CustomerResources.php b/niucloud/app/api/controller/apiController/CustomerResources.php index 81fe3c3a..100f20ad 100644 --- a/niucloud/app/api/controller/apiController/CustomerResources.php +++ b/niucloud/app/api/controller/apiController/CustomerResources.php @@ -293,4 +293,41 @@ class CustomerResources extends BaseApiService return success($res['data']); } + //搜索学员(用于课程安排) + public function searchStudents(Request $request) + { + $name = $request->param('name', '');//学员姓名 + $phone_number = $request->param('phone_number', '');//学员手机号 + $where = [ + 'name' => $name, + 'phone_number' => $phone_number + ]; + $res = (new CustomerResourcesService())->searchStudents($where); + if (!$res['code']) { + return fail($res['msg']); + } + return success($res['data']); + } + + //获取预设学员信息(不受状态限制) + public function getPresetStudentInfo(Request $request) + { + $resource_id = $request->param('resource_id', ''); + $student_id = $request->param('student_id', ''); + + if (empty($resource_id) && empty($student_id)) { + return fail('缺少必要参数'); + } + + $where = [ + 'resource_id' => $resource_id, + 'student_id' => $student_id + ]; + $res = (new CustomerResourcesService())->getPresetStudentInfo($where); + if (!$res['code']) { + return fail($res['msg']); + } + return success($res['data']); + } + } diff --git a/niucloud/app/api/controller/apiController/StudentManager.php b/niucloud/app/api/controller/apiController/StudentManager.php index b21bac3c..8a695d3f 100644 --- a/niucloud/app/api/controller/apiController/StudentManager.php +++ b/niucloud/app/api/controller/apiController/StudentManager.php @@ -82,7 +82,14 @@ class StudentManager extends BaseApiService ["parent_resource_id", 0], ["user_id", 0], ["campus_id", 0], - ["status", 0] + ["status", 0], + ["name", ""], // 学员姓名搜索 + ["phone", ""], // 联系电话搜索 + ["lessonCount", ""], // 课时数量搜索 + ["leaveCount", ""], // 请假次数搜索 + ["courseId", 0], // 课程ID搜索 + ["classId", 0], // 班级ID搜索 + ["type", ""] // 查询类型 ]); $result = (new StudentService())->getList($data); diff --git a/niucloud/app/api/controller/pay/Pay.php b/niucloud/app/api/controller/pay/Pay.php index c3b08d02..ae80612b 100644 --- a/niucloud/app/api/controller/pay/Pay.php +++ b/niucloud/app/api/controller/pay/Pay.php @@ -108,8 +108,6 @@ class Pay extends BaseApiController public function qrcodeNotify(int $order_id) { - // todo - $data = $this->request->param(); (new PayService())->qrcodeNotify($data,$order_id); diff --git a/niucloud/app/api/controller/student/AttendanceController.php b/niucloud/app/api/controller/student/AttendanceController.php new file mode 100644 index 00000000..335a8fee --- /dev/null +++ b/niucloud/app/api/controller/student/AttendanceController.php @@ -0,0 +1,75 @@ +request->params([ + ['schedule_id', 0], + ['student_id', 0], + ['resources_id', 0], + ['person_id', 0], + ]); + + try { + $result = (new AttendanceService())->checkinStudent($data); + return success($result, '签到成功'); + } catch (\Exception $e) { + return fail($e->getMessage()); + } + } + + /** + * 学员请假 + */ + public function leave() + { + $data = $this->request->params([ + ['schedule_id', 0], + ['student_id', 0], + ['resources_id', 0], + ['person_id', 0], + ['remark', ''], + ]); + + try { + $result = (new AttendanceService())->leaveStudent($data); + return success($result, '请假成功'); + } catch (\Exception $e) { + return fail($e->getMessage()); + } + } + + /** + * 学员取消 + */ + public function cancel() + { + $data = $this->request->params([ + ['schedule_id', 0], + ['student_id', 0], + ['resources_id', 0], + ['person_id', 0], + ['cancel_scope', 'single'], // single: 单节课, all: 全部课程 + ['cancel_reason', ''], + ]); + + try { + $result = (new AttendanceService())->cancelStudent($data); + return success($result, '操作成功'); + } catch (\Exception $e) { + return fail($e->getMessage()); + } + } +} \ No newline at end of file diff --git a/niucloud/app/api/route/route.php b/niucloud/app/api/route/route.php index 1d0d87a6..aa3da701 100644 --- a/niucloud/app/api/route/route.php +++ b/niucloud/app/api/route/route.php @@ -195,9 +195,7 @@ Route::group(function () { //公共端-获取支付类型字典(员工端) Route::get('common/getPaymentTypes', 'apiController.Common/getPaymentTypes'); - })->middleware(ApiChannel::class) - ->middleware(ApiPersonnelCheckToken::class) ->middleware(ApiLog::class); @@ -245,6 +243,10 @@ Route::group(function () { Route::get('customerResources/getStudentLabel', 'apiController.CustomerResources/getStudentLabel'); //客户资源-获取所有学生标签列表 Route::get('customerResources/getAllStudentLabels', 'apiController.CustomerResources/getAllStudentLabels'); + //客户资源-搜索学员(用于课程安排) + Route::get('customerResources/searchStudents', 'apiController.CustomerResources/searchStudents'); + //客户资源-获取预设学员信息(不受状态限制) + Route::get('customerResources/getPresetStudentInfo', 'apiController.CustomerResources/getPresetStudentInfo'); //资源共享-列表 @@ -570,6 +572,17 @@ Route::group(function () { })->middleware(ApiChannel::class) ->middleware(ApiLog::class); +// 学员出勤管理(测试) +Route::group('student/attendance', function () { + // 学员签到 + Route::post('checkin', 'student.AttendanceController/checkin'); + // 学员请假 + Route::post('leave', 'student.AttendanceController/leave'); + // 学员取消 + Route::post('cancel', 'student.AttendanceController/cancel'); +})->middleware(ApiChannel::class) + ->middleware(ApiLog::class); + //学员端路由 include_once __DIR__ . '/student.php'; diff --git a/niucloud/app/api/route/student.php b/niucloud/app/api/route/student.php index 72523e8c..af0b2315 100644 --- a/niucloud/app/api/route/student.php +++ b/niucloud/app/api/route/student.php @@ -173,6 +173,16 @@ Route::group('message', function () { Route::get('search/:student_id', 'app\api\controller\student\MessageController@searchMessages'); })->middleware(['ApiCheckToken']); +// 学员出勤管理 +Route::group('attendance', function () { + // 学员签到 + Route::post('checkin', 'student.AttendanceController/checkin'); + // 学员请假 + Route::post('leave', 'student.AttendanceController/leave'); + // 学员取消 + Route::post('cancel', 'student.AttendanceController/cancel'); +}); + // 学员登录相关(无需token验证) Route::group('auth', function () { Route::post('login/wechat', 'login.WechatLogin/login'); diff --git a/niucloud/app/model/school/SchoolPersonCourseSchedule.php b/niucloud/app/model/school/SchoolPersonCourseSchedule.php new file mode 100644 index 00000000..c0d6702a --- /dev/null +++ b/niucloud/app/model/school/SchoolPersonCourseSchedule.php @@ -0,0 +1,20 @@ +where([ - 'resources_id' => $data['resources_id'], - 'schedule_id' => $data['schedule_id'] - ])->find()){ + + // 根据person_type确定正确的student_id和resources_id + $student_id = 0; + $resources_id = 0; + + if ($data['person_type'] == 'student') { + // 如果是学员类型,从传入的数据中获取student_id,然后查询对应的resources_id + $student_id = $data['resources_id']; // 前端传来的是student.id + + // 通过student表查询对应的user_id(即customer_resources.id) + $Student = new Student(); + $student = $Student->where('id', $student_id)->find(); + if (!$student) { + return fail("学员不存在"); + } + $resources_id = $student['user_id']; // student.user_id = customer_resources.id + + } else if ($data['person_type'] == 'customer_resource') { + // 如果是客户资源类型,直接使用传入的resources_id + $resources_id = $data['resources_id']; + + // 验证客户资源是否存在 + $customerResource = Db::name('customer_resources') + ->where('id', $resources_id) + ->find(); + + if (!$customerResource) { + return fail("客户资源不存在"); + } + + // 通过customer_resources.id查找对应的学生记录 + // school_student.user_id = school_customer_resources.id + $Student = new Student(); + $student = $Student->where('user_id', $resources_id)->find(); + + if (!$student) { + return fail("该客户资源没有关联的学生记录"); + } + + $student_id = $student['id']; + } else { + return fail("无效的人员类型"); + } + + // 检查重复添加 - 根据person_type使用不同的检查逻辑 + $checkWhere = ['schedule_id' => $data['schedule_id']]; + if ($data['person_type'] == 'student') { + $checkWhere['student_id'] = $student_id; + } else { + $checkWhere['resources_id'] = $resources_id; + } + + if($personCourseSchedule->where($checkWhere)->find()){ return fail("重复添加"); } - $personCourseSchedule->insert([ - 'resources_id' => $data['resources_id'], + // 调试:插入前记录变量值 + error_log("Debug: Before insert - student_id = " . $student_id . ", resources_id = " . $resources_id); + + $insertData = [ + 'student_id' => $student_id, // 正确设置student_id + 'resources_id' => $resources_id, // 正确设置resources_id 'person_id' => $this->member_id, 'person_type' => $data['person_type'], 'schedule_id' => $data['schedule_id'], @@ -448,7 +501,11 @@ class CourseService extends BaseApiService 'schedule_type' => $data['schedule_type'] ?? 1, // 1=正式位, 2=等待位 'course_type' => $data['course_type'] ?? 1, // 1=正式课, 2=体验课, 3=等待位 'remark' => $data['remark'] ?? '' // 备注 - ]); + ]; + + error_log("Debug: Insert data = " . json_encode($insertData)); + + $personCourseSchedule->insert($insertData); $CourseSchedule->where(['id' => $data['schedule_id']])->dec("available_capacity")->update(); return success("添加成功"); @@ -458,10 +515,21 @@ class CourseService extends BaseApiService $personCourseSchedule = new PersonCourseSchedule(); $list = $personCourseSchedule ->alias('a') - ->join(['school_customer_resources' => 'b'],'a.resources_id = b.id','left') - ->join(['school_course_schedule' => 'c'],'c.id = a.schedule_id','left') - ->where('a.schedule_id',$data['schedule_id']) - ->field("b.name,a.status,a.person_type,c.campus_id,b.id as resources_id,a.schedule_type,a.course_type") + ->leftJoin(['school_student' => 'st'], 'a.student_id = st.id AND a.person_type = "student"') + ->leftJoin(['school_customer_resources' => 'b'], 'a.resources_id = b.id') + ->leftJoin(['school_course_schedule' => 'c'], 'c.id = a.schedule_id') + ->where('a.schedule_id', $data['schedule_id']) + ->field([ + 'COALESCE(st.name, b.name) as name', // 优先显示学员姓名,否则客户资源姓名 + 'a.status', + 'a.person_type', + 'c.campus_id', + 'a.student_id', + 'a.resources_id', + 'a.schedule_type', + 'a.course_type', + 'a.id as person_schedule_id' + ]) ->select() ->toArray(); @@ -532,6 +600,19 @@ class CourseService extends BaseApiService $mainCoachId = $data['main_coach_id'] ?? null; $educationId = $data['education_id'] ?? null; $assistantIds = $data['assistant_ids'] ?? ''; + $classId = $data['class_id'] ?? null; + + // 获取学员课程信息(需要 student_id 和 resource_id 来处理班级关联) + $studentCourse = Db::name('student_courses') + ->where('id', $studentCourseId) + ->field('student_id, resource_id') + ->find(); + + if (!$studentCourse) { + Db::rollback(); + $res['msg'] = '学员课程记录不存在'; + return $res; + } // 更新学员课程表 $updateData = [ @@ -561,6 +642,36 @@ class CourseService extends BaseApiService return $res; } + // 处理班级关联逻辑 + if ($classId !== null && $studentCourse['resource_id']) { + // 先删除现有的班级关联 + Db::name('class_resources_rel') + ->where('resource_id', $studentCourse['resource_id']) + ->delete(); + + // 如果选择了新的班级,创建新的关联记录 + if ($classId > 0) { + $classRelData = [ + 'class_id' => $classId, + 'resource_id' => $studentCourse['resource_id'], + 'campus_id' => 0, // 可根据实际需求设置 + 'source_id' => $studentCourse['student_id'], + 'source_type' => 'student', + 'join_time' => time(), + 'status' => 1, + 'create_time' => date('Y-m-d H:i:s'), + 'update_time' => date('Y-m-d H:i:s') + ]; + + $classRelResult = Db::name('class_resources_rel')->insert($classRelData); + if (!$classRelResult) { + Db::rollback(); + $res['msg'] = '更新班级关联失败'; + return $res; + } + } + } + // 提交事务 Db::commit(); @@ -571,7 +682,8 @@ class CourseService extends BaseApiService 'student_course_id' => $studentCourseId, 'main_coach_id' => $mainCoachId, 'education_id' => $educationId, - 'assistant_ids' => $assistantIds + 'assistant_ids' => $assistantIds, + 'class_id' => $classId ] ]; @@ -697,11 +809,31 @@ class CourseService extends BaseApiService ->select() ->toArray(); } - } elseif ($student['person_type'] == 'customer_resource' && !empty($student['resources'])) { - // 客户资源 - $name = $student['resources']['name'] ?: ''; - $age = $student['resources']['age'] ?: 0; - $phone = $student['resources']['phone_number'] ?: ''; + } elseif ($student['person_type'] == 'customer_resource') { + // 客户资源 - 现在我们有正确的student_id,应该从学生表获取信息 + if ($student['student_id'] > 0) { + // 从学生表获取学员信息 + $studentInfo = Db::name('student') + ->where('id', $student['student_id']) + ->find(); + + if ($studentInfo) { + $name = $studentInfo['name'] ?: ''; + $age = $studentInfo['age'] ?: 0; + $phone = $studentInfo['contact_phone'] ?: ''; + $trialClassCount = $studentInfo['trial_class_count'] ?: 0; + } else { + // 如果学生信息不存在,使用客户资源信息作为后备 + $name = !empty($student['resources']) ? $student['resources']['name'] : ''; + $age = !empty($student['resources']) ? $student['resources']['age'] : 0; + $phone = !empty($student['resources']) ? $student['resources']['phone_number'] : ''; + } + } else { + // 如果没有student_id,使用客户资源信息 + $name = !empty($student['resources']) ? $student['resources']['name'] : ''; + $age = !empty($student['resources']) ? $student['resources']['age'] : 0; + $phone = !empty($student['resources']) ? $student['resources']['phone_number'] : ''; + } } // 计算剩余课时和续费状态 @@ -751,7 +883,7 @@ class CourseService extends BaseApiService 'name' => $name, 'age' => $age, 'phone' => $phone, - 'courseStatus' => $student['person_type'] == 'student' ? '正式课' : '体验课', + 'courseStatus' => $this->getCourseTypeText($student['course_type']), 'courseType' => $student['schedule_type'] == 2 ? 'fixed' : 'temporary', 'remainingHours' => $remainingHours, 'totalHours' => $totalHours, @@ -1462,5 +1594,21 @@ class CourseService extends BaseApiService } } + /** + * 获取课程类型文本 + * @param int $courseType + * @return string + */ + private function getCourseTypeText($courseType) + { + $courseTypeMap = [ + 1 => '临时课', + 2 => '固定课', + 3 => '等待位', + 4 => '试听课' + ]; + return $courseTypeMap[$courseType] ?? '未知'; + } + } diff --git a/niucloud/app/service/api/apiService/CustomerResourcesService.php b/niucloud/app/service/api/apiService/CustomerResourcesService.php index 565aa8d9..f4465124 100644 --- a/niucloud/app/service/api/apiService/CustomerResourcesService.php +++ b/niucloud/app/service/api/apiService/CustomerResourcesService.php @@ -885,14 +885,6 @@ class CustomerResourcesService extends BaseApiService ->where('label_id', $label_id) ->field('label_id, label_name, memo') ->find(); - - if (!$label_info) { - return [ - 'code' => false, - 'msg' => '标签不存在' - ]; - } - return [ 'code' => true, 'msg' => '获取成功', @@ -948,4 +940,308 @@ class CustomerResourcesService extends BaseApiService ]; } } + + /** + * 搜索学员(用于课程安排) + * @param array $where 搜索条件 + * @param string $field 返回字段 + * @return array + */ + public function searchStudents(array $where, string $field = '*') + { + $res = [ + 'code' => 0, + 'msg' => '操作失败', + 'data' => [] + ]; + + try { + // 构建查询条件 + $query = Db::table('school_student') + ->alias('s') + ->leftJoin('school_student_courses sc', 's.id = sc.student_id') + ->where('s.deleted_at', 0) + ->where('s.status', 1); // 只查询状态正常的学员 + + // 按姓名搜索 + if (!empty($where['name'])) { + $query->where('s.name', 'like', "%{$where['name']}%"); + } + + // 按手机号搜索 + if (!empty($where['phone_number'])) { + $query->where('s.contact_phone', 'like', "%{$where['phone_number']}%"); + } + + // 获取学员基本信息和课程信息 + $students = $query + ->field([ + 's.id as student_id', + 's.name', + 's.age', + 's.contact_phone', + 's.gender', + 's.birthday', + 's.campus_id', + 's.class_id', + 's.trial_class_count', + 's.headimg', + 's.first_come', + 's.second_come', + 'sc.id as student_course_id', + 'sc.total_hours', + 'sc.use_total_hours', + 'sc.gift_hours', + 'sc.use_gift_hours', + 'sc.start_date', + 'sc.end_date', + 'sc.course_id' + ]) + ->group('s.id') + ->order('s.created_at DESC') + ->select(); + + if (!$students) { + $res['msg'] = '暂无学员数据'; + return $res; + } + + // 处理数据格式,计算课程进度 + $result = []; + foreach ($students as $student) { + $totalHours = intval($student['total_hours'] ?: 0) + intval($student['gift_hours'] ?: 0); + $usedHours = intval($student['use_total_hours'] ?: 0) + intval($student['use_gift_hours'] ?: 0); + $remainingHours = $totalHours - $usedHours; + + // 判断是否为体验课学员 + $isTrialStudent = empty($student['student_course_id']); + + // 判断是否需要续费 + $needsRenewal = false; + if (!$isTrialStudent) { + // 检查到期时间 + if ($student['end_date']) { + $daysUntilExpiry = (strtotime($student['end_date']) - time()) / (24 * 3600); + if ($daysUntilExpiry <= 10) { + $needsRenewal = true; + } + } + // 检查剩余课时 + if ($remainingHours < 4) { + $needsRenewal = true; + } + } + + $result[] = [ + 'id' => $student['student_id'], + 'student_id' => $student['student_id'], + 'name' => $student['name'], + 'age' => floatval($student['age'] ?: 0), + 'phone_number' => $student['contact_phone'], + 'contact_phone' => $student['contact_phone'], + 'gender' => $student['gender'], + 'birthday' => $student['birthday'], + 'campus_id' => $student['campus_id'], + 'class_id' => $student['class_id'], + 'trial_class_count' => intval($student['trial_class_count'] ?: 0), + 'headimg' => $student['headimg'], + 'first_come' => $student['first_come'], + 'second_come' => $student['second_come'], + 'person_type' => 'student', // 标识为学员类型 + 'courseStatus' => $isTrialStudent ? '体验课' : '正式课', + 'isTrialStudent' => $isTrialStudent, + 'is_formal_student' => !$isTrialStudent, // 有付费课程记录的为正式学员,可以选固定课 + 'needsRenewal' => $needsRenewal, + 'student_course_info' => [ + 'id' => $student['student_course_id'], + 'total_hours' => $student['total_hours'], + 'use_total_hours' => $student['use_total_hours'], + 'gift_hours' => $student['gift_hours'], + 'use_gift_hours' => $student['use_gift_hours'], + 'start_date' => $student['start_date'], + 'end_date' => $student['end_date'], + 'course_id' => $student['course_id'] + ], + 'course_progress' => [ + 'total' => $totalHours, + 'used' => $usedHours, + 'remaining' => $remainingHours, + 'percentage' => $totalHours > 0 ? round(($usedHours / $totalHours) * 100, 1) : 0 + ], + 'remainingHours' => $remainingHours, + 'totalHours' => $totalHours, + 'usedHours' => $usedHours, + 'expiryDate' => $student['end_date'] + ]; + } + + $res['code'] = 1; + $res['msg'] = '操作成功'; + $res['data'] = $result; + + } catch (\Exception $e) { + Log::error('搜索学员失败:' . $e->getMessage()); + $res['msg'] = '搜索学员失败:' . $e->getMessage(); + } + + return $res; + } + + /** + * 根据资源ID和学员ID获取预设学员信息(不受状态限制) + * @param array $where 查询条件 + * @return array + */ + public function getPresetStudentInfo(array $where) + { + $res = [ + 'code' => 0, + 'msg' => '操作失败', + 'data' => [] + ]; + + try { + $resourceId = $where['resource_id'] ?? 0; + $studentId = $where['student_id'] ?? 0; + + if (!$resourceId && !$studentId) { + $res['msg'] = '缺少必要参数'; + return $res; + } + + // 构建查询条件 - 不限制status状态 + $query = Db::table('school_student') + ->alias('s') + ->leftJoin('school_student_courses sc', 's.id = sc.student_id') + ->leftJoin('school_customer_resources cr', 's.user_id = cr.id') + ->where('s.deleted_at', 0); + + // 根据传入的参数查询 - 优先使用student_id,如果都提供则使用OR条件 + if ($studentId && $resourceId) { + // 如果同时提供student_id和resource_id,使用OR条件 + $query->where(function($query) use ($studentId, $resourceId) { + $query->where('s.id', $studentId)->whereOr('s.user_id', $resourceId); + }); + } elseif ($studentId) { + // 仅提供student_id + $query->where('s.id', $studentId); + } elseif ($resourceId) { + // 仅提供resource_id + $query->where('s.user_id', $resourceId); + } + + // 获取学员基本信息和课程信息 + $student = $query + ->field([ + 's.id as student_id', + 's.name', + 's.age', + 's.contact_phone', + 's.gender', + 's.birthday', + 's.campus_id', + 's.class_id', + 's.trial_class_count', + 's.headimg', + 's.first_come', + 's.second_come', + 's.user_id as resource_id', + 's.status as student_status', + 'cr.phone_number', + 'cr.member_id', + 'sc.id as student_course_id', + 'sc.total_hours', + 'sc.use_total_hours', + 'sc.gift_hours', + 'sc.use_gift_hours', + 'sc.start_date', + 'sc.end_date', + 'sc.course_id' + ]) + ->find(); + + if (!$student) { + $res['msg'] = '学员不存在'; + return $res; + } + + // 计算课程进度和状态 + $totalHours = intval($student['total_hours'] ?: 0) + intval($student['gift_hours'] ?: 0); + $usedHours = intval($student['use_total_hours'] ?: 0) + intval($student['use_gift_hours'] ?: 0); + $remainingHours = $totalHours - $usedHours; + + // 判断是否为体验课学员 + $isTrialStudent = empty($student['student_course_id']); + + // 判断是否需要续费 + $needsRenewal = false; + if (!$isTrialStudent) { + // 检查到期时间 + if ($student['end_date']) { + $daysUntilExpiry = (strtotime($student['end_date']) - time()) / (24 * 3600); + if ($daysUntilExpiry <= 10) { + $needsRenewal = true; + } + } + // 检查剩余课时 + if ($remainingHours < 4) { + $needsRenewal = true; + } + } + + $result = [ + 'id' => $student['student_id'], + 'student_id' => $student['student_id'], + 'name' => $student['name'], + 'age' => floatval($student['age'] ?: 0), + 'phone_number' => $student['contact_phone'] ?: $student['phone_number'], + 'contact_phone' => $student['contact_phone'] ?: $student['phone_number'], + 'gender' => $student['gender'], + 'birthday' => $student['birthday'], + 'campus_id' => $student['campus_id'], + 'class_id' => $student['class_id'], + 'trial_class_count' => intval($student['trial_class_count'] ?: 0), + 'headimg' => $student['headimg'], + 'first_come' => $student['first_come'], + 'second_come' => $student['second_come'], + 'resource_id' => $student['resource_id'], + 'member_id' => $student['member_id'], + 'student_status' => $student['student_status'], + 'person_type' => 'student', + 'courseStatus' => $isTrialStudent ? '体验课' : '正式课', + 'isTrialStudent' => $isTrialStudent, + 'is_formal_student' => !$isTrialStudent, // 有付费课程记录的为正式学员,可以选固定课 + 'needsRenewal' => $needsRenewal, + 'student_course_info' => [ + 'id' => $student['student_course_id'], + 'total_hours' => $student['total_hours'], + 'use_total_hours' => $student['use_total_hours'], + 'gift_hours' => $student['gift_hours'], + 'use_gift_hours' => $student['use_gift_hours'], + 'start_date' => $student['start_date'], + 'end_date' => $student['end_date'], + 'course_id' => $student['course_id'] + ], + 'course_progress' => [ + 'total' => $totalHours, + 'used' => $usedHours, + 'remaining' => $remainingHours, + 'percentage' => $totalHours > 0 ? round(($usedHours / $totalHours) * 100, 1) : 0 + ], + 'remainingHours' => $remainingHours, + 'totalHours' => $totalHours, + 'usedHours' => $usedHours, + 'expiryDate' => $student['end_date'] + ]; + + $res['code'] = 1; + $res['msg'] = '操作成功'; + $res['data'] = $result; + + } catch (\Exception $exception) { + $res['msg'] = '获取预设学员信息失败:' . $exception->getMessage(); + } + + return $res; + } } diff --git a/niucloud/app/service/api/apiService/OrderTableService.php b/niucloud/app/service/api/apiService/OrderTableService.php index 1826ded6..77d83898 100644 --- a/niucloud/app/service/api/apiService/OrderTableService.php +++ b/niucloud/app/service/api/apiService/OrderTableService.php @@ -17,6 +17,7 @@ use app\model\chat_friends\ChatFriends; use app\model\chat_messages\ChatMessages; use app\model\dict\Dict; use app\model\order_table\OrderTable; +use app\model\student\Student; use core\base\BaseApiService; use think\facade\Db; @@ -165,9 +166,18 @@ class OrderTableService extends BaseApiService $success = $order->save($updateData); if ($success) { - // 如果订单状态变更为已支付,则自动为学员分配课程 + // 如果订单状态变更为已支付,则执行完整的支付后处理流程 if ($data['order_status'] === 'paid') { - $this->assignCourseToStudent($order->toArray()); + $orderArray = $order->toArray(); + + // 1. 为学员分配课程 + $this->assignCourseToStudent($orderArray); + + // 2. 创建合同签署记录 + $this->createContractSign($orderArray); + + // 3. 创建支付记录 + $this->createPaymentRecord($orderArray); } return [ @@ -215,7 +225,7 @@ class OrderTableService extends BaseApiService return false; } $course = $course->toArray(); - + Student::where('id', $student_id)->update(['status' => 1]); // 检查学员是否已有该课程记录 $existingCourse = Db::table('school_student_courses') ->where('student_id', $student_id) @@ -295,4 +305,141 @@ class OrderTableService extends BaseApiService return false; } } + + /** + * 支付成功后创建合同签署记录 + * @param array $orderData 订单数据 + * @return bool + */ + private function createContractSign(array $orderData) + { + try { + $student_id = $orderData['student_id']; + $course_id = $orderData['course_id']; + + if (empty($student_id) || empty($course_id)) { + \think\facade\Log::warning('创建合同签署记录失败:缺少学员ID或课程ID', $orderData); + return false; + } + + // 获取课程的合同模板ID + $course = \app\model\course\Course::where('id', $course_id)->find(); + if (!$course || empty($course['contract_id'])) { + \think\facade\Log::warning('创建合同签署记录失败:课程无合同模板', ['course_id' => $course_id]); + return false; + } + + // 获取学员的user_id + $student = \app\model\student\Student::where('id', $student_id)->find(); + if (!$student || empty($student['user_id'])) { + \think\facade\Log::warning('创建合同签署记录失败:学员无user_id', ['student_id' => $student_id]); + return false; + } + + $now = date('Y-m-d H:i:s'); + $insertData = [ + 'contract_id' => $course['contract_id'], + 'personnel_id' => $student['user_id'], // 用户合同场景下存储学员的user_id + 'student_id' => $student_id, + 'status' => 1, // 默认状态 + 'source_type' => 'auto_course', // 来源类型:自动课程购买 + 'source_id' => $orderData['id'], // 订单ID + 'type' => 2, // 标识为用户购买课程合同 + 'created_at' => $now, + 'updated_at' => $now, + 'deleted_at' => 0 + ]; + + $result = Db::table('school_contract_sign')->insert($insertData); + + if ($result) { + \think\facade\Log::info('合同签署记录创建成功', [ + 'student_id' => $student_id, + 'contract_id' => $course['contract_id'], + 'order_id' => $orderData['id'] + ]); + } + + return $result ? true : false; + } catch (\Exception $e) { + \think\facade\Log::error('创建合同签署记录异常', [ + 'order_data' => $orderData, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + return false; + } + } + + /** + * 支付成功后创建支付记录 + * @param array $orderData 订单数据 + * @return bool + */ + private function createPaymentRecord(array $orderData) + { + try { + $order_id = $orderData['id']; + $payment_id = $orderData['payment_id'] ?? ''; + $payment_type = $orderData['payment_type'] ?? ''; + $order_amount = $orderData['order_amount'] ?? 0; + + if (empty($order_id) || empty($payment_id)) { + \think\facade\Log::warning('创建支付记录失败:缺少订单ID或支付单号', $orderData); + return false; + } + + // 获取课程名称用于支付描述 + $course_name = '未知课程'; + if (!empty($orderData['course_id'])) { + $course = \app\model\course\Course::where('id', $orderData['course_id'])->find(); + if ($course) { + $course_name = $course['course_name']; + } + } + + $now = time(); + $insertData = [ + 'main_id' => $order_id, // 订单ID + 'from_main_id' => 0, + 'out_trade_no' => $payment_id, // 支付单号 + 'trade_type' => 'order_payment', // 交易类型 + 'trade_id' => $order_id, + 'trade_no' => $payment_id, + 'body' => '课程购买-' . $course_name, // 支付描述 + 'money' => $order_amount, // 支付金额 + 'voucher' => '', + 'status' => 1, // 支付状态:已支付 + 'json' => '', + 'create_time' => $now, // 创建时间 + 'pay_time' => $now, // 支付时间 + 'cancel_time' => 0, + 'type' => $payment_type, // 支付方式:cash, scan_code等 + 'mch_id' => '', + 'main_type' => 'order', // 主类型 + 'channel' => '', + 'fail_reason' => '' + ]; + + $result = Db::table('school_pay')->insert($insertData); + + if ($result) { + \think\facade\Log::info('支付记录创建成功', [ + 'order_id' => $order_id, + 'payment_id' => $payment_id, + 'amount' => $order_amount, + 'type' => $payment_type + ]); + } + + return $result ? true : false; + } catch (\Exception $e) { + \think\facade\Log::error('创建支付记录异常', [ + 'order_data' => $orderData, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + return false; + } + } } diff --git a/niucloud/app/service/api/apiService/StudentService.php b/niucloud/app/service/api/apiService/StudentService.php index 8ee1f99c..fe9da03d 100644 --- a/niucloud/app/service/api/apiService/StudentService.php +++ b/niucloud/app/service/api/apiService/StudentService.php @@ -80,7 +80,7 @@ class StudentService extends BaseApiService } /** - * 获取学员列表 + * 获取学员列表(教练端专用) * @param array $data * @return array */ @@ -93,33 +93,64 @@ class StudentService extends BaseApiService ]; try { - $where = []; - $where[] = ['deleted_at', '=', 0]; + // 获取当前登录人员的ID + $currentUserId = $this->getUserId(); + if (empty($currentUserId)) { + // 用户未登录或不是有效用户,返回空数据 + $res['code'] = 1; + $res['data'] = []; + $res['msg'] = '获取成功'; + return $res; + } - if (!empty($data['user_id'])) { - $where[] = ['user_id', '=', $data['user_id']]; + // 查询符合条件的学生ID集合 + $studentIds = $this->getCoachStudentIds($currentUserId, $data); + + if (empty($studentIds)) { + // 当前教练没有负责的学员,返回空数据 + $res['code'] = 1; + $res['data'] = []; + $res['msg'] = '获取成功'; + return $res; } - if (!empty($data['parent_resource_id'])) { - $where[] = ['user_id', '=', $data['parent_resource_id']]; + // 构建学员基础查询条件 + $where = []; + $where[] = ['s.deleted_at', '=', 0]; + $where[] = ['s.id', 'in', $studentIds]; + + // 支持搜索参数 + if (!empty($data['name'])) { + $where[] = ['s.name', 'like', '%' . $data['name'] . '%']; + } + + if (!empty($data['phone'])) { + $where[] = ['s.contact_phone', 'like', '%' . $data['phone'] . '%']; } if (!empty($data['campus_id'])) { - $where[] = ['campus_id', '=', $data['campus_id']]; + $where[] = ['s.campus_id', '=', $data['campus_id']]; } if (!empty($data['status'])) { - $where[] = ['status', '=', $data['status']]; + $where[] = ['s.status', '=', $data['status']]; } - $list = Db::table('school_student') + // 查询学员基础信息 + $list = Db::table('school_student s') + ->leftJoin('school_campus c', 's.campus_id = c.id') ->where($where) - ->order('created_at', 'desc') + ->field('s.*, c.campus_name as campus') + ->order('s.created_at', 'desc') ->select() ->toArray(); - // 为每个学员添加到访情况信息 + // 为每个学员添加课程信息和到访情况 foreach ($list as &$student) { + // 获取学员最新有效的课程信息 + $courseInfo = $this->getStudentLatestCourse($student['id']); + $student = array_merge($student, $courseInfo); + // 查询该学员的课程安排记录,按日期排序获取一访和二访信息 $visitRecords = Db::table('school_person_course_schedule') ->where([ @@ -160,4 +191,110 @@ class StudentService extends BaseApiService return $res; } + + /** + * 获取教练负责的学生ID集合 + * @param int $coachId 教练ID + * @param array $data 查询条件 + * @return array + */ + private function getCoachStudentIds($coachId, $data = []) + { + // 1. 从 school_student_courses 表中查询 main_coach_id 或 education_id 是当前教练的学生 + $courseStudentIds = Db::table('school_student_courses') + ->where(function($query) use ($coachId) { + $query->where('main_coach_id', $coachId) + ->whereOr('education_id', $coachId); + }) + ->column('student_id'); + + // 调试日志 + error_log("Coach ID: " . $coachId); + error_log("Course Student IDs: " . json_encode($courseStudentIds)); + + // 2. 从 school_person_course_schedule 和 school_course_schedule 表联查,获取教练负责的学生 + $scheduleStudentIds = Db::table('school_person_course_schedule pcs') + ->leftJoin('school_course_schedule cs', 'pcs.schedule_id = cs.id') + ->where(function($query) use ($coachId) { + $query->where('cs.education_id', $coachId) + ->whereOr('cs.coach_id', $coachId); + }) + ->where('pcs.person_type', 'student') + ->where('pcs.deleted_at', 0) + ->where('cs.deleted_at', 0) + ->column('pcs.student_id'); + + error_log("Schedule Student IDs: " . json_encode($scheduleStudentIds)); + + // 3. 合并并去重学生ID + $allStudentIds = array_merge($courseStudentIds, $scheduleStudentIds); + $uniqueStudentIds = array_unique($allStudentIds); + + error_log("Final Student IDs: " . json_encode($uniqueStudentIds)); + + return array_values($uniqueStudentIds); + } + + /** + * 获取学员最新有效的课程信息 + * @param int $studentId 学员ID + * @return array + */ + private function getStudentLatestCourse($studentId) + { + $courseInfo = Db::table('school_student_courses sc') + ->leftJoin('school_course c', 'sc.course_id = c.id') + ->leftJoin('school_customer_resources cr', 'sc.resource_id = cr.id') + ->where([ + ['sc.student_id', '=', $studentId] + ]) + ->field('sc.total_hours, sc.gift_hours, sc.use_total_hours, sc.use_gift_hours, sc.end_date, sc.resource_id as resource_sharing_id, c.course_name') + ->order('sc.created_at', 'desc') + ->find(); + + if (empty($courseInfo)) { + return [ + 'total_hours' => 0, + 'gift_hours' => 0, + 'use_total_hours' => 0, + 'use_gift_hours' => 0, + 'end_date' => '', + 'resource_sharing_id' => 0, + 'course_name' => '' + ]; + } + + return $courseInfo; + } + + /** + * 获取当前登录用户ID + * @return int + */ + private function getUserId() + { + // 员工端通过JWT token获取用户信息,支持token和Authorization两种header + $token = request()->header('token') ?: request()->header('Authorization'); + if (empty($token)) { + return 0; // 返回0而不是抛异常,让上层处理 + } + + // 去掉Bearer前缀 + $token = str_replace('Bearer ', '', $token); + + try { + // 解析JWT token + $decoded = \Firebase\JWT\JWT::decode($token, new \Firebase\JWT\Key(config('app.app_key'), 'HS256')); + + // 检查token是否过期 + if ($decoded->exp < time()) { + return 0; // token过期返回0 + } + + // 返回用户ID + return $decoded->user_id ?? 0; + } catch (\Exception $e) { + return 0; // token解析失败返回0 + } + } } \ No newline at end of file diff --git a/niucloud/app/service/api/student/AttendanceService.php b/niucloud/app/service/api/student/AttendanceService.php new file mode 100644 index 00000000..130d46a7 --- /dev/null +++ b/niucloud/app/service/api/student/AttendanceService.php @@ -0,0 +1,411 @@ + 0) { + // 正式学员 + $where[] = ['student_id', '=', $student_id]; + $where[] = ['person_type', '=', 'student']; + } elseif (!empty($resources_id) && $resources_id > 0) { + // 客户资源 + $where[] = ['resources_id', '=', $resources_id]; + $where[] = ['person_type', '=', 'customer_resource']; + } else { + throw new \Exception('参数错误:必须提供学员ID或资源ID'); + } + + $schedule = (new SchoolPersonCourseSchedule()) + ->where($where) + ->find(); + + if (!$schedule) { + throw new \Exception('课程安排记录不存在'); + } + + if ($schedule['status'] == 1) { + throw new \Exception('该学员已经签到过了'); + } + + // 2. 更新课程安排状态为已上课 + $schedule->save([ + 'status' => 1, + 'updated_at' => time() + ]); + + $student_course_id = $schedule['student_course_id']; + $person_type = $schedule['person_type']; + + if (!empty($student_course_id)) { + // 正式学员处理逻辑 + $this->handleFormalStudentCheckin($student_course_id, $student_id, $schedule); + } elseif ($person_type == 'student' && !empty($student_id)) { + // 体验课学员处理逻辑 + $this->handleTrialStudentCheckin($student_id, $schedule); + } elseif ($person_type == 'customer_resource') { + // 客户资源处理逻辑 - 只更新状态,不扣除课时 + $this->handleCustomerResourceCheckin($schedule); + } else { + throw new \Exception('无法确定人员类型,签到失败'); + } + + Db::commit(); + + return [ + 'schedule_id' => $schedule_id, + 'student_id' => $student_id, + 'status' => 1, + 'message' => '签到成功' + ]; + + } catch (\Exception $e) { + Db::rollback(); + throw new \Exception('签到失败:' . $e->getMessage()); + } + } + + /** + * 处理正式学员签到 + * @param int $student_course_id + * @param int $student_id + * @param object $schedule + */ + private function handleFormalStudentCheckin($student_course_id, $student_id, $schedule) + { + // 查询学员课程信息 + $studentCourse = (new SchoolStudentCourses()) + ->where('id', $student_course_id) + ->find(); + + if (!$studentCourse) { + throw new \Exception('学员课程记录不存在'); + } + + $single_session_count = $studentCourse['single_session_count'] ?: 1; + $total_hours = $studentCourse['total_hours']; + $gift_hours = $studentCourse['gift_hours']; + $use_total_hours = $studentCourse['use_total_hours']; + $use_gift_hours = $studentCourse['use_gift_hours']; + + // 计算课时扣减逻辑 + $remaining_total_hours = $total_hours - $use_total_hours; + $remaining_gift_hours = $gift_hours - $use_gift_hours; + + $new_use_total_hours = $use_total_hours; + $new_use_gift_hours = $use_gift_hours; + + if ($remaining_total_hours > 0) { + // 优先扣减正式课时 + $deduct_from_total = min($remaining_total_hours, $single_session_count); + $new_use_total_hours += $deduct_from_total; + + $remaining_to_deduct = $single_session_count - $deduct_from_total; + + if ($remaining_to_deduct > 0 && $remaining_gift_hours > 0) { + // 剩余部分从赠送课时扣减 + $deduct_from_gift = min($remaining_gift_hours, $remaining_to_deduct); + $new_use_gift_hours += $deduct_from_gift; + } + } else if ($remaining_gift_hours > 0) { + // 没有正式课时,直接扣减赠送课时 + $deduct_from_gift = min($remaining_gift_hours, $single_session_count); + $new_use_gift_hours += $deduct_from_gift; + } + + // 更新学员课程表 + $studentCourse->save([ + 'use_total_hours' => $new_use_total_hours, + 'use_gift_hours' => $new_use_gift_hours, + 'updated_at' => time() + ]); + + // 插入消课记录 + $this->insertUsageRecord($student_course_id, $student_id, $single_session_count, $schedule); + } + + /** + * 处理体验课学员签到 + * @param int $student_id + * @param object $schedule + */ + private function handleTrialStudentCheckin($student_id, $schedule) + { + // 查询学员信息 + $student = (new SchoolStudent()) + ->where('id', $student_id) + ->find(); + + if (!$student) { + throw new \Exception('学员信息不存在'); + } + + $updateData = []; + $current_time = time(); + + // 更新到校时间逻辑 + if (empty($student['first_come'])) { + $updateData['first_come'] = $current_time; + } else if (empty($student['second_come'])) { + $updateData['second_come'] = $current_time; + } + + // 扣减体验课次数 + $trial_class_count = $student['trial_class_count'] ?: 0; + if ($trial_class_count > 0) { + $updateData['trial_class_count'] = $trial_class_count - 1; + } + + if (!empty($updateData)) { + $updateData['updated_at'] = date('Y-m-d H:i:s'); + $student->save($updateData); + } + + // 插入消课记录(体验课也需要记录) + $this->insertUsageRecord(0, $student_id, 1, $schedule); + } + + /** + * 插入消课记录 + * @param int $student_course_id + * @param int $student_id + * @param float $used_hours + * @param object $schedule + */ + private function insertUsageRecord($student_course_id, $student_id, $used_hours, $schedule) + { + // 获取资源ID + $resource_id = $schedule['resources_id']; + + (new SchoolStudentCourseUsage())->save([ + 'student_course_id' => $student_course_id, + 'used_hours' => $used_hours, + 'usage_date' => date('Y-m-d'), + 'student_id' => $student_id, + 'resource_id' => $resource_id, + 'created_at' => time(), + 'updated_at' => time() + ]); + } + + /** + * 学员请假 + * @param array $data + * @return array + */ + public function leaveStudent($data) + { + $schedule_id = $data['schedule_id']; + $student_id = $data['student_id']; + $resources_id = $data['resources_id'] ?? 0; + $person_id = $data['person_id']; + $remark = $data['remark'] ?? ''; + + if (empty($schedule_id)) { + throw new \Exception('参数错误:缺少课程安排ID'); + } + + // 查询课程安排记录 - 支持学员和客户资源两种类型 + $where = [ + ['schedule_id', '=', $schedule_id], + ['person_id', '=', $person_id], + ['deleted_at', '=', 0] + ]; + + // 根据传入的参数决定查询条件 + if (!empty($student_id) && $student_id > 0) { + // 正式学员 + $where[] = ['student_id', '=', $student_id]; + $where[] = ['person_type', '=', 'student']; + } elseif (!empty($resources_id) && $resources_id > 0) { + // 客户资源 + $where[] = ['resources_id', '=', $resources_id]; + $where[] = ['person_type', '=', 'customer_resource']; + } else { + throw new \Exception('参数错误:必须提供学员ID或资源ID'); + } + + $schedule = (new SchoolPersonCourseSchedule()) + ->where($where) + ->find(); + + if (!$schedule) { + throw new \Exception('课程安排记录不存在'); + } + + if ($schedule['status'] == 2) { + throw new \Exception('该学员已经请假了'); + } + + // 更新状态为请假 + $schedule->save([ + 'status' => 2, + 'remark' => $remark, + 'updated_at' => time() + ]); + + return [ + 'schedule_id' => $schedule_id, + 'student_id' => $student_id, + 'status' => 2, + 'message' => '请假成功' + ]; + } + + /** + * 学员取消 + * @param array $data + * @return array + */ + public function cancelStudent($data) + { + $schedule_id = $data['schedule_id']; + $student_id = $data['student_id']; + $resources_id = $data['resources_id'] ?? 0; + $person_id = $data['person_id']; + $cancel_scope = $data['cancel_scope'] ?? 'single'; // single: 单节课, all: 全部课程 + $cancel_reason = $data['cancel_reason'] ?? ''; + + if (empty($schedule_id)) { + throw new \Exception('参数错误:缺少课程安排ID'); + } + + // 开启事务 + Db::startTrans(); + try { + // 查询课程安排记录 - 支持学员和客户资源两种类型 + $where = [ + ['schedule_id', '=', $schedule_id], + ['person_id', '=', $person_id], + ['deleted_at', '=', 0] + ]; + + // 根据传入的参数决定查询条件 + if (!empty($student_id) && $student_id > 0) { + // 正式学员 + $where[] = ['student_id', '=', $student_id]; + $where[] = ['person_type', '=', 'student']; + } elseif (!empty($resources_id) && $resources_id > 0) { + // 客户资源 + $where[] = ['resources_id', '=', $resources_id]; + $where[] = ['person_type', '=', 'customer_resource']; + } else { + throw new \Exception('参数错误:必须提供学员ID或资源ID'); + } + + $schedule = (new SchoolPersonCourseSchedule()) + ->where($where) + ->find(); + + if (!$schedule) { + throw new \Exception('课程安排记录不存在'); + } + + $schedule_type = $schedule['schedule_type']; + $course_date = $schedule['course_date']; + $current_time = time(); + + if ($cancel_scope === 'all' && $schedule_type == 1) { + // 批量取消固定课程 + $cancelCount = (new SchoolPersonCourseSchedule()) + ->where([ + ['student_id', '=', $student_id], + ['schedule_id', '=', $schedule_id], + ['course_date', '>=', $course_date], + ['deleted_at', '=', 0] + ]) + ->update([ + 'deleted_at' => $current_time, + 'cancel_reason' => $cancel_reason, + 'updated_at' => $current_time + ]); + + $message = "成功取消{$cancelCount}节固定课程"; + } else { + // 单节课取消 + $schedule->save([ + 'deleted_at' => $current_time, + 'cancel_reason' => $cancel_reason, + 'updated_at' => $current_time + ]); + + $message = '成功取消单节课程'; + } + + Db::commit(); + + return [ + 'schedule_id' => $schedule_id, + 'student_id' => $student_id, + 'cancel_scope' => $cancel_scope, + 'message' => $message + ]; + + } catch (\Exception $e) { + Db::rollback(); + throw new \Exception('取消失败:' . $e->getMessage()); + } + } + + /** + * 处理客户资源签到 + * @param object $schedule + */ + private function handleCustomerResourceCheckin($schedule) + { + // 客户资源签到只需要更新状态,不涉及课时扣除 + // 状态已经在主流程中更新了,这里可以记录签到日志或其他业务逻辑 + + // 可以在这里添加客户资源的特殊处理逻辑 + // 比如记录体验课次数、更新客户跟进状态等 + + $current_time = time(); + + // 如果需要更新客户资源相关信息,可以在这里添加 + // 例如:更新体验课次数、最后上课时间等 + + return true; + } +} \ No newline at end of file diff --git a/uniapp/api/apiRoute.js b/uniapp/api/apiRoute.js index ce9cc247..428bc1eb 100644 --- a/uniapp/api/apiRoute.js +++ b/uniapp/api/apiRoute.js @@ -9,12 +9,6 @@ export default { console.log('统一登录响应:', response); return response; }, - //教师/销售端登陆(兼容旧接口) - async personnelLogin(data = {}) { - const response = await http.post('/personnelLogin', data); - console.log('登录响应:', response); - return response; - }, //教师/销售端详情 async getPersonnelInfo(data = {}) { return await http.get('/personnel/info', data); @@ -451,6 +445,16 @@ export default { return await http.get('/customerResources/getAll', data); }, + //搜索学员(用于课程安排) + async searchStudents(data = {}) { + return await http.get('/customerResources/searchStudents', data); + }, + + // 获取预设学员信息(不受状态限制) + async getPresetStudentInfo(data = {}) { + return await http.get('/customerResources/getPresetStudentInfo', data); + }, + //获取客户资源详情 async getCustomerResourcesInfo(data = {}) { return await http.get('/resourceSharing/info', data); @@ -779,6 +783,11 @@ export default { return await http.post('/updateStudentCoursePersonnel', data); }, + //检查学员班级关联 + async checkClassRelation(data = {}) { + return await http.get('/course/checkClassRelation', data); + }, + // 获取订单支付二维码 async getOrderPayQrcode(data = {}) { return await http.get('/getQrcode', data); @@ -1062,25 +1071,14 @@ export default { }, // 获取课程安排详情 async getCourseScheduleInfo(data = {}) { - // 开发阶段直接使用Mock数据,避免数据库表不存在的问题 - console.log('使用Mock数据获取课程安排详情:', data); - return this.getCourseScheduleInfoMock(data); - - // 未登录或测试模式使用模拟数据 - if (!uni.getStorageSync("token")) { - return this.getCourseScheduleInfoMock(data); - } - try { - const result = await http.get('/courseSchedule/info', data); - // 如果接口返回错误(数据库表不存在等问题),降级到Mock数据 - if (result.code === 0) { - console.warn('API返回错误,降级到Mock数据:', result.msg); - return this.getCourseScheduleInfoMock(data); - } + // 使用真实的API接口获取课程安排详情 + const result = await http.get('/course/scheduleDetail', data); + console.log('获取课程安排详情:', result); return result; } catch (error) { - console.warn('API调用失败,降级到Mock数据:', error); + console.error('获取课程安排详情失败:', error); + // 如果接口调用失败,降级到Mock数据 return this.getCourseScheduleInfoMock(data); } }, @@ -1287,6 +1285,23 @@ export default { return await http.post('/course/updateStudentStatus', data); }, + //↓↓↓↓↓↓↓↓↓↓↓↓-----学员出勤管理相关接口-----↓↓↓↓↓↓↓↓↓↓↓↓ + + // 学员签到 + async studentCheckin(data = {}) { + return await http.post('/student/attendance/checkin', data); + }, + + // 学员请假 + async studentLeave(data = {}) { + return await http.post('/student/attendance/leave', data); + }, + + // 学员取消 + async studentCancel(data = {}) { + return await http.post('/student/attendance/cancel', data); + }, + //↓↓↓↓↓↓↓↓↓↓↓↓-----学员合同管理相关接口-----↓↓↓↓↓↓↓↓↓↓↓↓ // 获取学员合同列表 diff --git a/uniapp/common/util.js b/uniapp/common/util.js index 4ce4e578..317a1748 100644 --- a/uniapp/common/util.js +++ b/uniapp/common/util.js @@ -204,35 +204,6 @@ function formatToDateTime(dateTime, fmt = 'Y-m-d H:i:s') { } - -//跳转首页 -function openHomeView() { - //获取用户类型缓存 - let userType = uni.getStorageSync('userType') - let url_path = '' - switch (String(userType)) { - case '1': //教练 - url_path = '/pages/coach/home/index' - break; - case '2': //销售 - url_path = '/pages/market/index/index' - break; - case '3': //学员 - url_path = '/pages/student/index/index' - break; - default: - uni.showToast({ - title: '用户类型错误', - icon: 'none' - }) - url_path = '/pages/student/login/login' - return; - } - uni.navigateTo({ - url: url_path - }) -} - //退出登陆-清空缓存数据 function loginOut() { //清除token @@ -578,7 +549,6 @@ function navigateToPage(url, params = {}) { module.exports = { loginOut, - openHomeView, formatTime, formatDateTime, formatLocation, diff --git a/uniapp/common/utils-index.js b/uniapp/common/utils-index.js index 5d28348d..b50a69d8 100644 --- a/uniapp/common/utils-index.js +++ b/uniapp/common/utils-index.js @@ -9,8 +9,6 @@ import util from './util.js' export const { // 登录退出相关 loginOut, - openHomeView, - // 时间格式化 formatTime, formatDateTime, diff --git a/uniapp/components/course-info-card/index.vue b/uniapp/components/course-info-card/index.vue index 0626d920..60da0bde 100644 --- a/uniapp/components/course-info-card/index.vue +++ b/uniapp/components/course-info-card/index.vue @@ -158,12 +158,13 @@ - - + + 班级配置 所属班级: - - - - - - 班级信息 - - 当前班级:{{ currentClassInfo.class_name }} - 如需更换班级,请联系管理员 + + + 当前班级:{{ currentClassInfo.class_name }} @@ -197,6 +193,8 @@