From 1d3fffba6af97814f2a7bda7224a8f63953e9eb6 Mon Sep 17 00:00:00 2001
From: zeyan <258785420@qq.com>
Date: Sat, 8 Nov 2025 10:28:17 +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
---
.../personnel/components/BaseInfoForm.vue | 380 ++++
.../personnel/components/DetailInfoForm.vue | 350 ++++
.../personnel/components/personnel-edit.vue | 844 +++------
.../apiController/PersonCourseSchedule.php | 32 +-
.../apiController/StudentCourse.php | 51 +-
niucloud/app/api/route/route.php | 2 +
niucloud/app/common.php | 40 +
.../api/apiService/CoachStudentService.php | 1 -
.../api/apiService/CourseScheduleService.php | 237 ++-
.../service/api/apiService/CourseService.php | 5 +-
.../PersonCourseScheduleService.php | 239 ++-
.../service/api/student/ContractService.php | 5 +-
uniapp/api/apiRoute.js | 5 +
uniapp/components/course-info-card/index.vue | 290 +--
.../contract/staff-contract-sign.vue | 24 +-
uniapp/pages-market/course/course_detail.vue | 1614 +++++++++++++++++
uniapp/pages.json | 10 +-
17 files changed, 3339 insertions(+), 790 deletions(-)
create mode 100644 admin/src/app/views/personnel/components/BaseInfoForm.vue
create mode 100644 admin/src/app/views/personnel/components/DetailInfoForm.vue
create mode 100644 uniapp/pages-market/course/course_detail.vue
diff --git a/admin/src/app/views/personnel/components/BaseInfoForm.vue b/admin/src/app/views/personnel/components/BaseInfoForm.vue
new file mode 100644
index 00000000..1ddbc290
--- /dev/null
+++ b/admin/src/app/views/personnel/components/BaseInfoForm.vue
@@ -0,0 +1,380 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/admin/src/app/views/personnel/components/DetailInfoForm.vue b/admin/src/app/views/personnel/components/DetailInfoForm.vue
new file mode 100644
index 00000000..f357a20a
--- /dev/null
+++ b/admin/src/app/views/personnel/components/DetailInfoForm.vue
@@ -0,0 +1,350 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/admin/src/app/views/personnel/components/personnel-edit.vue b/admin/src/app/views/personnel/components/personnel-edit.vue
index 31f9d70b..6dc3a4c8 100755
--- a/admin/src/app/views/personnel/components/personnel-edit.vue
+++ b/admin/src/app/views/personnel/components/personnel-edit.vue
@@ -1,656 +1,274 @@
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
- {{ item.name }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ item.name }}
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
+
+
-
\ No newline at end of file
diff --git a/niucloud/app/api/controller/apiController/PersonCourseSchedule.php b/niucloud/app/api/controller/apiController/PersonCourseSchedule.php
index 01203298..8e0551c3 100755
--- a/niucloud/app/api/controller/apiController/PersonCourseSchedule.php
+++ b/niucloud/app/api/controller/apiController/PersonCourseSchedule.php
@@ -161,17 +161,17 @@ class PersonCourseSchedule extends BaseApiService
$resource_id = $request->param('resource_id', '');//客户资源ID
$member_id = $request->param('member_id', '');//会员ID
$student_id = $request->param('student_id', '');//学生ID
-
+
if (empty($resource_id)) {
return fail('缺少参数resource_id');
}
-
+
$where = [
'resource_id' => $resource_id,
'member_id' => $member_id,
'student_id' => $student_id,
];
-
+
$res = (new PersonCourseScheduleService())->getStudentCourseInfo($where);
if(!$res['code']){
return fail($res['msg']);
@@ -194,21 +194,41 @@ class PersonCourseSchedule extends BaseApiService
$main_coach_id = $request->param('main_coach_id', '');//主教练ID
$education_id = $request->param('education_id', '');//教务ID
$assistant_ids = $request->param('assistant_ids', '');//助教IDs,逗号分隔
-
+
if (empty($student_course_id)) {
return fail('缺少参数student_course_id');
}
-
+
$data = [
'main_coach_id' => $main_coach_id,
'education_id' => $education_id,
'assistant_ids' => $assistant_ids,
];
-
+
$res = (new PersonCourseScheduleService())->updateStudentCoursePersonnel($student_course_id, $data);
if(!$res['code']){
return fail($res['msg']);
}
return success($res['data']);
}
+
+ //获取学生课程详情包含已上课的情况和订单情况
+ public function getStudentCourseDetail(Request $request)
+ {
+ $student_course_id = $request->param('student_course_id', '');
+
+ if (empty($student_course_id)) {
+ return fail('缺少参数student_course_id');
+ }
+
+ $where = [
+ 'student_course_id' => $student_course_id,
+ ];
+
+ $res = (new PersonCourseScheduleService())->getStudentCourseDetail($where);
+ if(!$res['code']){
+ return fail($res['msg']);
+ }
+ return success($res['data']);
+ }
}
diff --git a/niucloud/app/api/controller/apiController/StudentCourse.php b/niucloud/app/api/controller/apiController/StudentCourse.php
index f1db1bbd..3f4a6bfd 100755
--- a/niucloud/app/api/controller/apiController/StudentCourse.php
+++ b/niucloud/app/api/controller/apiController/StudentCourse.php
@@ -32,39 +32,39 @@ class StudentCourse extends BaseApiService
{
$course_id = $request->param('course_id', '');
$resource_id = $request->param('resource_id', '');
-
+
if (empty($course_id)) {
return fail('课程ID不能为空');
}
-
+
// 如果没有传resource_id,尝试从当前登录用户获取
if (empty($resource_id)) {
// 这里需要根据实际情况获取当前学员的resource_id
// 可能需要从member_id获取对应的resource_id
$resource_id = $this->getResourceIdByMemberId($this->member_id);
}
-
+
if (empty($resource_id)) {
return fail('资源ID不能为空');
}
-
+
$where = [
'course_id' => $course_id,
'resource_id' => $resource_id
];
-
+
try {
$res = (new StudentCourseService())->getCourseDetail($where);
if (!$res['code']) {
return fail($res['msg']);
}
-
+
return success($res['data']);
} catch (\Exception $e) {
return fail('获取课程详情失败:' . $e->getMessage());
}
}
-
+
/**
* 获取学员服务记录
* @param Request $request
@@ -73,28 +73,28 @@ class StudentCourse extends BaseApiService
public function getServiceList(Request $request)
{
$student_id = $request->param('student_id', '');
-
+
// 如果没有传student_id,尝试从当前登录用户获取
if (empty($student_id)) {
$student_id = $this->getResourceIdByMemberId($this->member_id);
}
-
+
if (empty($student_id)) {
return fail('学员ID不能为空');
}
-
+
try {
$res = (new ServiceService())->getStudentServiceList((int)$student_id);
if (!$res['code']) {
return fail($res['msg']);
}
-
+
return success($res['data']);
} catch (\Exception $e) {
return fail('获取服务记录失败:' . $e->getMessage());
}
}
-
+
/**
* 获取教练列表
* @param Request $request
@@ -108,13 +108,13 @@ class StudentCourse extends BaseApiService
if (!$res['code']) {
return fail($res['msg']);
}
-
+
return success($res['data']);
} catch (\Exception $e) {
return fail('获取教练列表失败:' . $e->getMessage());
}
}
-
+
/**
* 获取教务人员列表
* @param Request $request
@@ -128,13 +128,13 @@ class StudentCourse extends BaseApiService
if (!$res['code']) {
return fail($res['msg']);
}
-
+
return success($res['data']);
} catch (\Exception $e) {
return fail('获取教务人员列表失败:' . $e->getMessage());
}
}
-
+
/**
* 更新学员课程信息
* @param Request $request
@@ -150,22 +150,22 @@ class StudentCourse extends BaseApiService
["education_id", 0],
["class_id", 0] // 可选,如果需要更新班级关联
]);
-
+
if (empty($data['student_course_id'])) {
return fail('学员课程ID不能为空');
}
-
+
$res = (new StudentCourseService())->updateCourseInfo($data);
if (!$res['code']) {
return fail($res['msg']);
}
-
+
return success('更新成功', $res['data']);
} catch (\Exception $e) {
return fail('更新失败:' . $e->getMessage());
}
}
-
+
/**
* 检查学员班级关联情况
* @param Request $request
@@ -175,21 +175,21 @@ class StudentCourse extends BaseApiService
{
try {
$resource_id = $request->param('resource_id', '');
-
+
if (empty($resource_id)) {
// 尝试从当前登录用户获取
$resource_id = $this->getResourceIdByMemberId($this->member_id);
}
-
+
if (empty($resource_id)) {
return fail('资源ID不能为空');
}
-
+
$res = (new StudentCourseService())->checkClassRelation($resource_id);
if (!$res['code']) {
return fail($res['msg']);
}
-
+
return success($res['data']);
} catch (\Exception $e) {
return fail('检查班级关联失败:' . $e->getMessage());
@@ -208,4 +208,5 @@ class StudentCourse extends BaseApiService
$customerResource = \app\model\customer_resources\CustomerResources::where('member_id', $member_id)->find();
return $customerResource ? $customerResource->id : '';
}
-}
\ No newline at end of file
+
+}
diff --git a/niucloud/app/api/route/route.php b/niucloud/app/api/route/route.php
index 3f885643..5501e3e3 100755
--- a/niucloud/app/api/route/route.php
+++ b/niucloud/app/api/route/route.php
@@ -582,6 +582,8 @@ Route::group(function () {
//获取学生课程信息列表(包含教练配置)
Route::get('getStudentCourseInfo', 'apiController.PersonCourseSchedule/getStudentCourseInfo');
+ //获取学生课程详情包含已上课的情况和订单情况
+ Route::post('getStudentCourseDetail', 'apiController.PersonCourseSchedule/getStudentCourseDetail');
//获取人员列表(教练、教务、助教)
Route::get('getPersonnelList', 'apiController.PersonCourseSchedule/getPersonnelList');
//更新学生课程人员配置
diff --git a/niucloud/app/common.php b/niucloud/app/common.php
index 6a27a7fa..85c18fc0 100755
--- a/niucloud/app/common.php
+++ b/niucloud/app/common.php
@@ -2305,3 +2305,43 @@ function save_user_signature($data)
return false;
}
}
+
+// ==================== 合同占位符处理函数 ====================
+
+/**
+ * 将合同内容中的占位符替换为下划线输入框
+ * 使用正则表达式将 {{占位符}} 格式替换为指定长度的下划线
+ *
+ * @param string $contractContent 合同内容
+ * @param int $underlineLength 下划线长度(默认为10个字符)
+ * @return string 替换后的合同内容
+ */
+function replace_placeholders_with_underlines($contractContent, $underlineLength = 10)
+{
+ if (empty($contractContent)) {
+ return $contractContent;
+ }
+
+ // 使用正则表达式匹配 {{}} 格式的占位符
+ // \{\{ 匹配 {{
+ // [^}]+ 匹配一个或多个非 } 字符(占位符内容)
+ // \}\} 匹配 }}
+ $pattern = '/\{\{[^}]+\}\}/';
+
+ // 生成替换字符串 - 根据占位符内容长度动态调整下划线数量
+ $replacement = function($matches) use ($underlineLength) {
+ $placeholder = $matches[0];
+
+ // 提取占位符中的内容
+ $content = preg_replace('/^\{\{|\}\}$/', '', $placeholder);
+
+ // 根据内容长度动态决定下划线数量,最少8个,最多20个
+ $contentLength = mb_strlen($content, 'UTF-8');
+ $adjustedLength = max(8, min(20, $contentLength + 4));
+
+ return str_repeat('_', $adjustedLength); // 使用全角下划线,显示效果更好
+ };
+
+ // 执行替换
+ return preg_replace_callback($pattern, $replacement, $contractContent);
+}
diff --git a/niucloud/app/service/api/apiService/CoachStudentService.php b/niucloud/app/service/api/apiService/CoachStudentService.php
index 4c05bf3a..c47df828 100755
--- a/niucloud/app/service/api/apiService/CoachStudentService.php
+++ b/niucloud/app/service/api/apiService/CoachStudentService.php
@@ -285,7 +285,6 @@ class CoachStudentService extends BaseApiService
// resource_sharing_id 是 school_resource_assignment 表的 id
$resourceAssignment = Db::table('school_resource_assignment')
->where('resource_id', $customerResource['id'])
- ->where('assignee_type', 'user')
->field('id')
->order('assigned_at', 'desc')
->find();
diff --git a/niucloud/app/service/api/apiService/CourseScheduleService.php b/niucloud/app/service/api/apiService/CourseScheduleService.php
index e6a2bac6..0bb647d2 100755
--- a/niucloud/app/service/api/apiService/CourseScheduleService.php
+++ b/niucloud/app/service/api/apiService/CourseScheduleService.php
@@ -2771,6 +2771,231 @@ class CourseScheduleService extends BaseApiService
}
}
+ /**
+ * 处理正式学员课程消减逻辑
+ * @param array $enrollment 学员课程安排记录
+ * @param Student $student 学员信息
+ * @throws \Exception
+ */
+ private function handlePaidStudentCourseDeduction($enrollment, $student)
+ {
+ try {
+ $scheduleId = $enrollment['schedule_id'] ?? 0;
+
+ if (empty($scheduleId)) {
+ throw new \Exception('课程安排ID不能为空');
+ }
+
+ // 获取课程安排信息以确定课程日期
+ $schedule = Db::name('course_schedule')
+ ->where('id', $scheduleId)
+ ->find();
+
+ if (!$schedule) {
+ throw new \Exception('找不到课程安排信息');
+ }
+
+ $courseDate = $schedule['course_date'];
+
+ // 根据课程日期动态查找应该核销的课程包
+ $studentCourse = $this->findApplicableStudentCourse($student->id, $courseDate);
+
+ if (!$studentCourse) {
+ throw new \Exception('找不到该日期有效的学员课程包');
+ }
+
+ // 获取课程信息以计算消耗课时数
+ $course = Db::name('course')
+ ->where('id', $studentCourse['course_id'])
+ ->find();
+
+ if (!$course) {
+ throw new \Exception('找不到课程信息');
+ }
+
+ // 计算本次消耗的课时数
+ $deductHours = $this->calculateCourseDeduction($course, $scheduleId);
+
+ // 检查剩余课时是否足够
+ $remainingHours = $studentCourse['total_hours'] - $studentCourse['use_total_hours'];
+ if ($remainingHours < $deductHours) {
+ throw new \Exception('剩余课时不足,当前剩余:' . $remainingHours . ',需要消耗:' . $deductHours);
+ }
+
+ // 1. 更新学员课程的已使用课时数
+ $updateResult = Db::name('student_courses')
+ ->where('id', $studentCourse['id'])
+ ->inc('use_total_hours', $deductHours)
+ ->update(['updated_at' => date('Y-m-d H:i:s')]);
+
+ if (!$updateResult) {
+ throw new \Exception('更新学员课程课时失败');
+ }
+
+ // 2. 插入课程消减记录
+ $usageData = [
+ 'student_course_id' => $studentCourse['id'],
+ 'student_id' => $student->id,
+ 'resource_id' => $enrollment['resources_id'] ?? null,
+ 'used_hours' => $deductHours,
+ 'usage_date' => date('Y-m-d'),
+ 'created_at' => date('Y-m-d H:i:s'),
+ 'updated_at' => date('Y-m-d H:i:s')
+ ];
+
+ $usageResult = Db::name('student_course_usage')->insert($usageData);
+ if (!$usageResult) {
+ throw new \Exception('插入课程消减记录失败');
+ }
+
+ // 记录日志
+ trace('Paid student course deduction processed', 'info');
+ trace('Deduction details: ' . json_encode([
+ 'student_id' => $student->id,
+ 'student_course_id' => $studentCourse['id'],
+ 'course_start_date' => $studentCourse['start_date'],
+ 'course_end_date' => $studentCourse['end_date'],
+ 'schedule_date' => $courseDate,
+ 'deducted_hours' => $deductHours,
+ 'remaining_hours' => $remainingHours - $deductHours,
+ 'usage_date' => date('Y-m-d')
+ ]), 'info');
+
+ } catch (\Exception $e) {
+ // 抛出异常以便外层事务回滚
+ throw new \Exception('处理正式学员课程消减失败:' . $e->getMessage());
+ }
+ }
+
+ /**
+ * 根据课程日期查找适用的学员课程包
+ * 优先选择:
+ * 1. 在有效期内(开始日期 ≤ 课程日期 ≤ 结束日期)
+ * 2. 还有剩余课时
+ * 3. 最早开始的课程包
+ * @param int $studentId 学员ID
+ * @param string $courseDate 课程日期
+ * @return array|null 学员课程记录
+ */
+ private function findApplicableStudentCourse($studentId, $courseDate)
+ {
+ try {
+ // 查找在有效期内且有剩余课时的课程包
+ $applicableCourses = Db::name('student_courses')
+ ->where('student_id', $studentId)
+ ->where('status', 1) // 有效状态
+ ->where('start_date', '<=', $courseDate) // 开始日期 <= 课程日期
+ ->where('end_date', '>=', $courseDate) // 结束日期 >= 课程日期
+ ->whereRaw('total_hours > use_total_hours') // 有剩余课时
+ ->order('start_date ASC, created_at ASC') // 按开始日期升序,创建时间升序
+ ->select()
+ ->toArray();
+
+ if (empty($applicableCourses)) {
+ return null;
+ }
+
+ // 返回最早开始的有效课程包
+ return $applicableCourses[0];
+
+ } catch (\Exception $e) {
+ trace('Find applicable student course error: ' . $e->getMessage(), 'error');
+ return null;
+ }
+ }
+
+ /**
+ * 计算课程消耗课时数
+ * @param array $course 课程信息
+ * @param int $scheduleId 课程安排ID
+ * @return float 消耗课时数
+ */
+ private function calculateCourseDeduction($course, $scheduleId)
+ {
+ try {
+ // 获取课程安排信息
+ $schedule = Db::name('course_schedule')
+ ->where('id', $scheduleId)
+ ->find();
+
+ if (!$schedule) {
+ throw new \Exception('找不到课程安排信息');
+ }
+
+ // 根据课程类型计算消耗课时数
+ switch ($course['course_type']) {
+ case 1: // 按课时
+ // 解析时间段计算实际课时
+ $timeSlot = $schedule['time_slot'] ?? '';
+ $deductHours = $this->parseTimeSlotToHours($timeSlot);
+ break;
+
+ case 2: // 按次卡
+ // 按次卡每次消耗1次
+ $deductHours = 1;
+ break;
+
+ case 3: // 按周期
+ // 周期课程每次消耗1次
+ $deductHours = 1;
+ break;
+
+ case 4: // 按时长
+ // 按实际时长计算,转换为小时
+ $duration = $course['duration'] ?? 0; // 分钟
+ $deductHours = $duration / 60;
+ break;
+
+ default:
+ // 默认每次消耗1课时
+ $deductHours = 1;
+ break;
+ }
+
+ // 确保返回正数
+ return max(0.01, $deductHours);
+
+ } catch (\Exception $e) {
+ // 计算失败时默认消耗1课时
+ return 1;
+ }
+ }
+
+ /**
+ * 解析时间段为小时数
+ * @param string $timeSlot 时间段,格式:08:00-08:30
+ * @return float 小时数
+ */
+ private function parseTimeSlotToHours($timeSlot)
+ {
+ try {
+ if (empty($timeSlot) || strpos($timeSlot, '-') === false) {
+ return 1; // 默认1小时
+ }
+
+ $times = explode('-', $timeSlot);
+ if (count($times) !== 2) {
+ return 1;
+ }
+
+ $startTime = strtotime($times[0]);
+ $endTime = strtotime($times[1]);
+
+ if (!$startTime || !$endTime) {
+ return 1;
+ }
+
+ $minutes = ($endTime - $startTime) / 60;
+ $hours = $minutes / 60;
+
+ // 如果计算结果小于0.1小时,按0.1小时计算
+ return max(0.1, $hours);
+
+ } catch (\Exception $e) {
+ return 1; // 解析失败时默认1小时
+ }
+ }
+
/**
* 处理单个学员签到(内部方法,不管理事务)
* @param int $scheduleId 课程安排ID
@@ -2856,9 +3081,15 @@ class CourseScheduleService extends BaseApiService
return ['success' => false, 'error' => '更新签到状态失败'];
}
- // 处理试听课签到逻辑(仅在签到成功且学员未付费时处理)
- if ($status === 1 && $student->pay_status != 1) {
- $this->handleTrialClassCheckin($student);
+ // 处理签到后的课程消减逻辑
+ if ($status === 1) {
+ if ($student->pay_status != 1) {
+ // 处理试听课签到逻辑
+ $this->handleTrialClassCheckin($student);
+ } else {
+ // 处理正式学员的课程消减逻辑
+ $this->handlePaidStudentCourseDeduction($enrollment, $student);
+ }
}
return ['success' => true, 'error' => ''];
diff --git a/niucloud/app/service/api/apiService/CourseService.php b/niucloud/app/service/api/apiService/CourseService.php
index 096aa0f1..d97ebf34 100755
--- a/niucloud/app/service/api/apiService/CourseService.php
+++ b/niucloud/app/service/api/apiService/CourseService.php
@@ -793,9 +793,12 @@ class CourseService extends BaseApiService
$phone = $student['student']['contact_phone'] ?: '';
$trialClassCount = $student['student']['trial_class_count'] ?: 0;
- // 获取学员最新的付费课程信息
+ // 获取学员最新的有效课程信息
$studentCourseInfo = Db::name('student_courses')
->where('student_id', $student['student_id'])
+ ->where('status', 1) // 只获取状态为1的有效课程
+ ->where('start_date', '<=', date('Y-m-d')) // 开始时间小于等于当前时间
+ ->where('end_date', '>=', date('Y-m-d')) // 结束时间大于等于当前时间
->order('created_at DESC')
->find();
diff --git a/niucloud/app/service/api/apiService/PersonCourseScheduleService.php b/niucloud/app/service/api/apiService/PersonCourseScheduleService.php
index 97c734db..1c3db65b 100755
--- a/niucloud/app/service/api/apiService/PersonCourseScheduleService.php
+++ b/niucloud/app/service/api/apiService/PersonCourseScheduleService.php
@@ -406,6 +406,31 @@ class PersonCourseScheduleService extends BaseApiService
->where('status', 2) // 2表示请假
->count();
+ // 获取班级关联信息
+ $classInfo = null;
+ try {
+ $classRel = \app\model\class_resources_rel\ClassResourcesRel::alias('crr')
+ ->join(['school_class' => 'c'], 'crr.class_id = c.id', 'left')
+ ->where([
+ 'crr.resource_id' => $where['resource_id'],
+ 'crr.status' => 1
+ ])
+ ->field([
+ 'crr.class_id',
+ 'c.class_name',
+ 'c.head_coach',
+ 'c.educational_id'
+ ])
+ ->find();
+
+ if ($classRel) {
+ $classInfo = $classRel->toArray();
+ }
+ } catch (\Exception $e) {
+ // 班级查询失败,不影响整体功能
+ $classInfo = null;
+ }
+
// 获取教练配置信息
$mainCoach = null;
$education = null;
@@ -471,12 +496,19 @@ class PersonCourseScheduleService extends BaseApiService
'status' => $status, // 课程状态
'db_status' => $dbStatus, // 数据库原始状态
'single_session_count' => $course['single_session_count'] ?? 1, // 单次消课数量
+ 'resource_id' => $course['resource_id'] ?? null, // 添加资源ID
+ 'student_course_id' => $course['id'], // 添加学生课程ID
'main_coach_id' => $course['main_coach_id'] ?? null,
'main_coach_name' => $mainCoach['name'] ?? '未分配',
'education_id' => $course['education_id'] ?? null,
'education_name' => $education['name'] ?? '未分配',
'assistant_ids' => $course['assistant_ids'] ?? '',
- 'assistant_names' => implode(', ', array_column($assistants, 'name')) ?: '无'
+ 'assistant_names' => implode(', ', array_column($assistants, 'name')) ?: '无',
+ // 班级相关字段
+ 'class_id' => $classInfo['class_id'] ?? null,
+ 'class_name' => $classInfo['class_name'] ?? null,
+ 'has_class' => !empty($classInfo), // 是否有班级关联
+ 'class_info' => $classInfo // 完整的班级信息
];
}
@@ -553,6 +585,209 @@ class PersonCourseScheduleService extends BaseApiService
return $res;
}
+ //获取学生课程详情(包含课时使用记录和订单信息)
+ public function getStudentCourseDetail(array $where)
+ {
+ $res = [
+ 'code' => 0,
+ 'msg' => '获取失败',
+ 'data' => []
+ ];
+
+ try {
+ $studentCourseId = $where['student_course_id'];
+
+ // 1. 获取学员课程基础信息
+ $studentCourse = StudentCourses::where('id', $studentCourseId)
+ ->with([
+ 'course' => function($query) {
+ $query->field('id,course_name');
+ },
+ 'student' => function($query) {
+ $query->field('id,name');
+ }
+ ])
+ ->find();
+
+ if (!$studentCourse) {
+ $res['msg'] = '学员课程不存在';
+ return $res;
+ }
+
+ $studentCourse = $studentCourse->toArray();
+
+ // 2. 获取课时使用记录(一对多关联)
+ $usageRecords = StudentCourseUsage::where('student_course_id', $studentCourseId)
+ ->order('usage_date', 'desc')
+ ->select()
+ ->toArray();
+
+ // 3. 获取关联的订单信息(一对一关联,通过course_plan_id)
+ $orderInfo = \app\model\order_table\OrderTable::where('course_plan_id', $studentCourseId)
+ ->with([
+ 'course' => function($query) {
+ $query->field('id,course_name');
+ },
+ 'personnel' => function($query) {
+ $query->field('id,name');
+ },
+ 'campus' => function($query) {
+ $query->field('id,campus_name');
+ }
+ ])
+ ->find();
+
+ // 4. 计算课时统计信息
+ $totalHours = ($studentCourse['total_hours'] ?? 0) + ($studentCourse['gift_hours'] ?? 0);
+ $usedHours = ($studentCourse['use_total_hours'] ?? 0) + ($studentCourse['use_gift_hours'] ?? 0);
+ $remainingHours = $totalHours - $usedHours;
+
+ // 5. 获取教练配置信息
+ $mainCoach = null;
+ $education = null;
+ $assistants = [];
+
+ if (!empty($studentCourse['main_coach_id'])) {
+ $mainCoach = Personnel::where('id', $studentCourse['main_coach_id'])->field('id,name')->find();
+ }
+ if (!empty($studentCourse['education_id'])) {
+ $education = Personnel::where('id', $studentCourse['education_id'])->field('id,name')->find();
+ }
+ if (!empty($studentCourse['assistant_ids'])) {
+ $assistantIds = array_filter(explode(',', $studentCourse['assistant_ids']));
+ if (!empty($assistantIds)) {
+ $assistants = Personnel::whereIn('id', $assistantIds)->field('id,name')->select()->toArray();
+ }
+ }
+
+ // 6. 计算课程状态
+ $status = 'active';
+ $dbStatus = $studentCourse['status'] ?? 1;
+
+ switch ($dbStatus) {
+ case 1:
+ $status = 'active';
+ break;
+ case 2:
+ $status = 'expired';
+ break;
+ case 3:
+ $status = 'waiting';
+ break;
+ case 4:
+ $status = 'delayed';
+ break;
+ }
+
+ if ($status === 'active' && !empty($studentCourse['end_date'])) {
+ if (strtotime($studentCourse['end_date']) < time()) {
+ $status = 'expired';
+ }
+ }
+
+ if ($remainingHours <= 0) {
+ $status = 'completed';
+ }
+
+ // 7. 组装返回数据
+ $data = [
+ // 学员课程基础信息
+ 'student_course_info' => [
+ 'id' => $studentCourse['id'],
+ 'student_id' => $studentCourse['student_id'],
+ 'student_name' => $studentCourse['student']['name'] ?? '未知学员',
+ 'course_id' => $studentCourse['course_id'],
+ 'course_name' => $studentCourse['course']['course_name'] ?? '未知课程',
+ 'total_hours' => $studentCourse['total_hours'] ?? 0,
+ 'gift_hours' => $studentCourse['gift_hours'] ?? 0,
+ 'use_total_hours' => $studentCourse['use_total_hours'] ?? 0,
+ 'use_gift_hours' => $studentCourse['use_gift_hours'] ?? 0,
+ 'start_date' => $studentCourse['start_date'] ?? '',
+ 'end_date' => $studentCourse['end_date'] ?? '',
+ 'status' => $status,
+ 'db_status' => $dbStatus,
+ 'single_session_count' => $studentCourse['single_session_count'] ?? 1,
+ 'resource_id' => $studentCourse['resource_id'] ?? null,
+ 'main_coach_id' => $studentCourse['main_coach_id'] ?? null,
+ 'education_id' => $studentCourse['education_id'] ?? null,
+ 'assistant_ids' => $studentCourse['assistant_ids'] ?? '',
+ // 课时统计
+ 'total_class_hours' => $totalHours,
+ 'used_class_hours' => $usedHours,
+ 'remaining_class_hours' => $remainingHours,
+ // 教练信息
+ 'main_coach_name' => $mainCoach['name'] ?? '未分配',
+ 'education_name' => $education['name'] ?? '未分配',
+ 'assistant_names' => implode(', ', array_column($assistants, 'name')) ?: '无',
+ 'coach_details' => [
+ 'main_coach' => $mainCoach,
+ 'education' => $education,
+ 'assistants' => $assistants
+ ]
+ ],
+
+ // 课时使用记录列表
+ 'usage_records' => array_map(function($record) {
+ return [
+ 'id' => $record['id'],
+ 'used_hours' => $record['used_hours'],
+ 'usage_date' => $record['usage_date'],
+ 'created_at' => $record['created_at'],
+ 'updated_at' => $record['updated_at'],
+ 'student_id' => $record['student_id'],
+ 'resource_id' => $record['resource_id']
+ ];
+ }, $usageRecords),
+
+ // 订单信息(如果存在)
+ 'order_info' => $orderInfo ? [
+ 'id' => $orderInfo['id'],
+ 'payment_id' => $orderInfo['payment_id'] ?? '',
+ 'order_type' => $orderInfo['order_type'] ?? '',
+ 'order_status' => $orderInfo['order_status'] ?? '',
+ 'payment_type' => $orderInfo['payment_type'] ?? '',
+ 'order_amount' => $orderInfo['order_amount'] ?? 0,
+ 'course_id' => $orderInfo['course_id'] ?? 0,
+ 'class_id' => $orderInfo['class_id'] ?? null,
+ 'staff_id' => $orderInfo['staff_id'] ?? 0,
+ 'resource_id' => $orderInfo['resource_id'] ?? 0,
+ 'campus_id' => $orderInfo['campus_id'] ?? 0,
+ 'student_id' => $orderInfo['student_id'] ?? null,
+ 'discount_amount' => $orderInfo['discount_amount'] ?? 0,
+ 'remark' => $orderInfo['remark'] ?? '',
+ 'payment_time' => $orderInfo['payment_time'] ?? '',
+ 'created_at' => $orderInfo['created_at'] ?? '',
+ // 关联信息
+ 'course_name' => $orderInfo['course']['course_name'] ?? '',
+ 'staff_name' => $orderInfo['personnel']['name'] ?? '',
+ 'campus_name' => $orderInfo['campus']['campus_name'] ?? ''
+ ] : null,
+
+ // 统计信息
+ 'statistics' => [
+ 'total_usage_records' => count($usageRecords),
+ 'total_used_hours_from_records' => array_sum(array_column($usageRecords, 'used_hours')),
+ 'has_order' => !empty($orderInfo),
+ 'usage_date_range' => $usageRecords ? [
+ 'first_usage' => min(array_column($usageRecords, 'usage_date')),
+ 'last_usage' => max(array_column($usageRecords, 'usage_date'))
+ ] : null
+ ]
+ ];
+
+ $res = [
+ 'code' => 1,
+ 'msg' => '获取成功',
+ 'data' => $data
+ ];
+
+ } catch (\Exception $e) {
+ $res['msg'] = '获取异常:' . $e->getMessage();
+ }
+
+ return $res;
+ }
+
//更新学生课程人员配置
public function updateStudentCoursePersonnel($studentCourseId, array $data)
{
@@ -585,7 +820,7 @@ class PersonCourseScheduleService extends BaseApiService
// 执行更新
$result = StudentCourses::where('id', $studentCourseId)->update($updateData);
-
+
if ($result !== false) {
$res = [
'code' => 1,
diff --git a/niucloud/app/service/api/student/ContractService.php b/niucloud/app/service/api/student/ContractService.php
index 9938c87d..5282356c 100755
--- a/niucloud/app/service/api/student/ContractService.php
+++ b/niucloud/app/service/api/student/ContractService.php
@@ -1096,7 +1096,7 @@ class ContractService extends BaseService
/**
* 格式化字段值
* 对特定类型的字段值进行格式化处理
- *
+ *
* @param string $fieldName 字段名
* @param mixed $value 原始值
* @return string 格式化后的值
@@ -1133,4 +1133,5 @@ class ContractService extends BaseService
return (string)$value;
}
-}
\ No newline at end of file
+
+ }
\ No newline at end of file
diff --git a/uniapp/api/apiRoute.js b/uniapp/api/apiRoute.js
index d6d29b92..86c4ceeb 100755
--- a/uniapp/api/apiRoute.js
+++ b/uniapp/api/apiRoute.js
@@ -1083,6 +1083,11 @@ export default {
return response
},
+ // 获取学员课程详情
+ async getStudentCourseDetail(data = {}) {
+ return await http.post('/getStudentCourseDetail', data)
+ },
+
diff --git a/uniapp/components/course-info-card/index.vue b/uniapp/components/course-info-card/index.vue
index 15a9f7c2..8f89e418 100755
--- a/uniapp/components/course-info-card/index.vue
+++ b/uniapp/components/course-info-card/index.vue
@@ -3,11 +3,11 @@
-
-
+
-
@@ -33,7 +33,7 @@
{{ course.used_count || 0 }}/{{ course.total_count }}节
-
+
@@ -50,7 +50,7 @@
{{ getRemainingCount(course) }}节
-
+
@@ -62,7 +62,7 @@
{{ formatDate(course.end_date || course.expiry_date) }}
-
+
@@ -74,7 +74,7 @@
{{ formatTime(course.create_time) }}
-
+
-
+
📖
暂无课程信息
学生还未报名任何课程
-
+
@@ -100,17 +100,17 @@
编辑课程信息
×
-
+
人员配置
-
+
主教练:
-
-
+
助教:
@@ -139,14 +139,14 @@
-
+
教务:
@@ -157,7 +157,7 @@
-
+
班级配置
@@ -165,8 +165,8 @@
所属班级:
@@ -182,7 +182,7 @@
-
+