From 045d88f726ba844b193ab9c55521de8cede3bf8e Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Fri, 22 Aug 2025 13:14:59 +0800 Subject: [PATCH 01/19] update --- .../Controllers/Admin/TeacherController.php | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Admin/TeacherController.php b/app/Http/Controllers/Admin/TeacherController.php index 7e83616..3dd2953 100755 --- a/app/Http/Controllers/Admin/TeacherController.php +++ b/app/Http/Controllers/Admin/TeacherController.php @@ -3,8 +3,12 @@ namespace App\Http\Controllers\Admin; use App\Exports\BaseExport; +use App\Helpers\ResponseCode; use App\Models\CustomForm; +use App\Models\CustomFormField; use App\Models\Teacher; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Validator; use Maatwebsite\Excel\Facades\Excel; class TeacherController extends BaseController @@ -44,7 +48,7 @@ class TeacherController extends BaseController public function index() { $all = request()->all(); - $list = $this->model->with('courseContents.course','courseContents.directionDetail')->where(function ($query) use ($all) { + $list = $this->model->with('courseContents.course', 'courseContents.directionDetail')->where(function ($query) use ($all) { if (isset($all['filter']) && !empty($all['filter'])) { foreach ($all['filter'] as $condition) { $key = $condition['key'] ?? null; @@ -224,7 +228,46 @@ class TeacherController extends BaseController */ public function import() { - return parent::import(); + $all = \request()->all(); + $messages = [ + 'data.required' => '数据必填', + ]; + $validator = Validator::make($all, [ + 'data' => 'required', + ], $messages); + if ($validator->fails()) { + return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]); + } + $records = $all['data']; + DB::beginTransaction(); + try { + // 获取数据表的所有字段 + $tableName = $this->model->getTable(); + $existingColumns = (new CustomFormField)->getRowTableFieldsByComment($tableName); + // 过滤掉不存在的字段 + $filteredRecords = array_map(function ($record) use ($existingColumns) { + return array_intersect_key($record, $existingColumns); + }, $records); + // 去除空数据 + $filteredRecords = array_filter($filteredRecords); + // 分段导入 + foreach ($filteredRecords as $item) { + $where = ['name'=>$item['name']]; + $data = [ + 'name' => $item['name'], + 'sex' => $item['sex'], + 'remark' => $item['remark'], + 'introduce' => $item['introduce'], + 'mobile'=>$item['mobile'], + ]; + $this->model->firstOrCreate($where, $data); + } + DB::commit(); + return $this->success(['total' => count($records), 'filter_total' => count($filteredRecords)]); + } catch (\Exception $exception) { + DB::rollBack(); + return $this->fail([$exception->getCode(), $exception->getMessage()]); + } } From b938942e9f1cc9c653ba22142ca212058df5ca23 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Fri, 22 Aug 2025 13:20:55 +0800 Subject: [PATCH 02/19] update --- app/Http/Controllers/Admin/TeacherController.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/Admin/TeacherController.php b/app/Http/Controllers/Admin/TeacherController.php index 3dd2953..3108569 100755 --- a/app/Http/Controllers/Admin/TeacherController.php +++ b/app/Http/Controllers/Admin/TeacherController.php @@ -252,13 +252,13 @@ class TeacherController extends BaseController $filteredRecords = array_filter($filteredRecords); // 分段导入 foreach ($filteredRecords as $item) { - $where = ['name'=>$item['name']]; + $where = ['name' => $item['name']]; $data = [ 'name' => $item['name'], - 'sex' => $item['sex'], - 'remark' => $item['remark'], - 'introduce' => $item['introduce'], - 'mobile'=>$item['mobile'], + 'sex' => $item['sex'] ?? '', + 'remark' => $item['remark'] ?? '', + 'introduce' => $item['introduce'] ?? '', + 'mobile' => $item['mobile'] ?? '', ]; $this->model->firstOrCreate($where, $data); } From 0ac953fe78e1f8379e1f36d684d14569820f8975 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Fri, 22 Aug 2025 13:27:35 +0800 Subject: [PATCH 03/19] update --- app/Http/Controllers/Admin/TeacherController.php | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/app/Http/Controllers/Admin/TeacherController.php b/app/Http/Controllers/Admin/TeacherController.php index 3108569..89725e6 100755 --- a/app/Http/Controllers/Admin/TeacherController.php +++ b/app/Http/Controllers/Admin/TeacherController.php @@ -242,16 +242,8 @@ class TeacherController extends BaseController DB::beginTransaction(); try { // 获取数据表的所有字段 - $tableName = $this->model->getTable(); - $existingColumns = (new CustomFormField)->getRowTableFieldsByComment($tableName); - // 过滤掉不存在的字段 - $filteredRecords = array_map(function ($record) use ($existingColumns) { - return array_intersect_key($record, $existingColumns); - }, $records); - // 去除空数据 - $filteredRecords = array_filter($filteredRecords); // 分段导入 - foreach ($filteredRecords as $item) { + foreach ($records as $item) { $where = ['name' => $item['name']]; $data = [ 'name' => $item['name'], @@ -263,7 +255,7 @@ class TeacherController extends BaseController $this->model->firstOrCreate($where, $data); } DB::commit(); - return $this->success(['total' => count($records), 'filter_total' => count($filteredRecords)]); + return $this->success(['total' => count($records), 'filter_total' => count($records)]); } catch (\Exception $exception) { DB::rollBack(); return $this->fail([$exception->getCode(), $exception->getMessage()]); From d133b6de60408b5ec0a4924da110324f98b1330a Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Fri, 22 Aug 2025 13:28:52 +0800 Subject: [PATCH 04/19] update --- app/Http/Controllers/Admin/TeacherController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Admin/TeacherController.php b/app/Http/Controllers/Admin/TeacherController.php index 89725e6..f2e547d 100755 --- a/app/Http/Controllers/Admin/TeacherController.php +++ b/app/Http/Controllers/Admin/TeacherController.php @@ -252,7 +252,7 @@ class TeacherController extends BaseController 'introduce' => $item['introduce'] ?? '', 'mobile' => $item['mobile'] ?? '', ]; - $this->model->firstOrCreate($where, $data); + $this->model->updateOrCreate($where, $data); } DB::commit(); return $this->success(['total' => count($records), 'filter_total' => count($records)]); From 85ab80acdbb472f5f91dfce11f0644ba0749200f Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Fri, 22 Aug 2025 16:19:51 +0800 Subject: [PATCH 05/19] update --- .../Controllers/Admin/OtherController.php | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/app/Http/Controllers/Admin/OtherController.php b/app/Http/Controllers/Admin/OtherController.php index d2c045c..5db50f3 100755 --- a/app/Http/Controllers/Admin/OtherController.php +++ b/app/Http/Controllers/Admin/OtherController.php @@ -6,6 +6,7 @@ use App\Helpers\ResponseCode; use App\Models\Admin; use App\Models\Appointment; use App\Models\AppointmentConfig; +use App\Models\Calendar; use App\Models\CarparkLog; use App\Models\Company; use App\Models\CourseSign; @@ -125,28 +126,22 @@ class OtherController extends CommonController $query->whereIn('course_id', $courses->pluck('id')); })->count(); // 审核通过人数 - $list['course_signs_pass'] = CourseSign::where('status', 1) + $courseSign = CourseSign::where('status', 1) ->whereDate('created_at', '>=', $start_date) ->whereDate('created_at', '<=', $end_date) ->where(function ($query) use ($courses) { $query->whereIn('course_id', $courses->pluck('id')); - }) - ->count(); + })->get(); + $list['course_signs_pass'] = $courseSign->count(); // 审核通过人数去重 - $list['course_signs_pass_unique'] = CourseSign::where('status', 1) - ->whereDate('created_at', '>=', $start_date) - ->whereDate('created_at', '<=', $end_date) - ->where(function ($query) use ($courses) { - $query->whereIn('course_id', $courses->pluck('id')); - })->select('user_id') - ->distinct() - ->count(); + $list['course_signs_pass_unique'] = User::whereIn('id', $courseSign->pluck('user_id'))->groupBy('mobile')->count(); // 开课场次 - $list['course_total'] = $courses->count(); + $calendar = Calendar::whereIn('course_id', $courses->pluck('id'))->whereBetween('date', [$start_date, $end_date])->get(); + $list['course_total'] = $calendar->count(); // 开课天数 - $list['course_day_total'] = $courses->sum(function ($course) { - $start = Carbon::parse($course->start_date); - $end = Carbon::parse($course->end_date); + $list['course_day_total'] = $calendar->sum(function ($course) { + $start = Carbon::parse($course->start_time); + $end = Carbon::parse($course->end_time); return $end->diffInDays($start) + 1; // 包含起始和结束日期 }); // 返回所有sql语句 @@ -160,20 +155,15 @@ class OtherController extends CommonController ->where('start_date', '<=', $end_date) ->where('type', $courseType->id) ->get(); - // 培养人数 - $courseTypeSignsPass = CourseSign::where('status', 1) + $courseSignByType = CourseSign::where('status', 1) ->whereIn('course_id', $courses2->pluck('id')) ->whereDate('created_at', '>=', $start_date) ->whereDate('created_at', '<=', $end_date) - ->count(); + ->get(); + // 培养人数 + $courseTypeSignsPass = $courseSignByType->count(); // 去重培养人数 - $courseTypeSignsPassUnique = CourseSign::where('status', 1) - ->whereIn('course_id', $courses2->pluck('id')) - ->whereDate('created_at', '>=', $start_date) - ->whereDate('created_at', '<=', $end_date) - ->select('user_id') - ->distinct() - ->count(); + $courseTypeSignsPassUnique = User::whereIn('id', $courseSignByType->pluck('user_id'))->groupBy('mobile')->count(); foreach ($courses2 as $course) { $courseTypesSum[] = [ 'course_type' => $courseType->name, From a26decca9c05a906d28018afb4b4cf21a1dfacd2 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Fri, 22 Aug 2025 16:25:22 +0800 Subject: [PATCH 06/19] update --- app/Http/Controllers/Admin/OtherController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Admin/OtherController.php b/app/Http/Controllers/Admin/OtherController.php index 5db50f3..035a7fd 100755 --- a/app/Http/Controllers/Admin/OtherController.php +++ b/app/Http/Controllers/Admin/OtherController.php @@ -134,7 +134,7 @@ class OtherController extends CommonController })->get(); $list['course_signs_pass'] = $courseSign->count(); // 审核通过人数去重 - $list['course_signs_pass_unique'] = User::whereIn('id', $courseSign->pluck('user_id'))->groupBy('mobile')->count(); + $list['course_signs_pass_unique'] = User::whereIn('id', $courseSign->pluck('user_id'))->count(); // 开课场次 $calendar = Calendar::whereIn('course_id', $courses->pluck('id'))->whereBetween('date', [$start_date, $end_date])->get(); $list['course_total'] = $calendar->count(); From 626afe5484dfcae2ef6bcc1630ba4da4cc07e0bb Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Fri, 22 Aug 2025 17:54:41 +0800 Subject: [PATCH 07/19] update --- .../Commands/LinkCoursesToCalendar.php | 302 ----------------- app/Console/Commands/UpdateCourseUrls.php | 316 ++++++++++++++---- .../Controllers/Admin/OtherController.php | 2 +- 课程台账.xlsx | Bin 14754 -> 15669 bytes 4 files changed, 246 insertions(+), 374 deletions(-) delete mode 100644 app/Console/Commands/LinkCoursesToCalendar.php diff --git a/app/Console/Commands/LinkCoursesToCalendar.php b/app/Console/Commands/LinkCoursesToCalendar.php deleted file mode 100644 index e661984..0000000 --- a/app/Console/Commands/LinkCoursesToCalendar.php +++ /dev/null @@ -1,302 +0,0 @@ -info("开始将课程关联到calendars日历表..."); - $this->info("总共需要处理 " . count($this->courseList) . " 个课程"); - - $linkedCount = 0; - $notFoundCourses = []; - $alreadyLinkedCourses = []; - - DB::beginTransaction(); - - try { - foreach ($this->courseList as $courseName) { - $this->info("正在处理课程: {$courseName}"); - - // 查找匹配的课程 - $course = $this->findCourse($courseName); - - if (!$course) { - $this->warn("✗ 未找到匹配的课程: {$courseName}"); - $notFoundCourses[] = $courseName; - continue; - } - - $this->info("✓ 找到匹配课程: {$course->name} (ID: {$course->id})"); - - // 检查是否已经存在日历记录 - $existingCalendar = Calendar::where('course_id', $course->id) - ->where('type', 1) // 类型1为课程 - ->first(); - - if ($existingCalendar) { - $this->warn("⚠ 课程已存在日历记录: {$course->name}"); - $alreadyLinkedCourses[] = $course->name; - continue; - } - - // 创建日历记录 - $calendarData = $this->createCalendarData($course); - $calendar = Calendar::create($calendarData); - - $this->info("✓ 成功创建日历记录 (ID: {$calendar->id}) 关联课程: {$course->name}"); - $linkedCount++; - } - - DB::commit(); - - $this->info("\n" . str_repeat('=', 60)); - $this->info("处理完成!"); - $this->info("成功关联课程数量: {$linkedCount}"); - $this->info("已存在日历记录: " . count($alreadyLinkedCourses)); - $this->info("未找到匹配课程: " . count($notFoundCourses)); - - // 显示未找到的课程 - if (!empty($notFoundCourses)) { - $this->warn("\n未找到匹配的课程列表:"); - foreach ($notFoundCourses as $course) { - $this->warn(" - {$course}"); - } - } - - // 显示已存在日历记录的课程 - if (!empty($alreadyLinkedCourses)) { - $this->warn("\n已存在日历记录的课程列表:"); - foreach ($alreadyLinkedCourses as $course) { - $this->warn(" - {$course}"); - } - } - - } catch (\Exception $e) { - DB::rollback(); - $this->error("处理过程中发生错误: " . $e->getMessage()); - $this->error("已回滚所有更改"); - return; - } - - $this->info("\n所有操作已完成!"); - } - - /** - * 查找匹配的课程 - */ - private function findCourse($courseName) - { - // 1. 精确匹配 - $course = Course::where('name', $courseName) - ->whereNull('deleted_at') - ->first(); - - if ($course) { - return $course; - } - - // 2. 模糊匹配 - $course = Course::where('name', 'like', "%{$courseName}%") - ->whereNull('deleted_at') - ->first(); - - if ($course) { - $this->info("通过模糊匹配找到课程: '{$course->name}'"); - return $course; - } - - // 3. 相似度匹配 - $courses = Course::whereNull('deleted_at') - ->whereNotNull('name') - ->where('name', '!=', '') - ->get(); - - $bestMatch = null; - $highestSimilarity = 0; - - foreach ($courses as $course) { - $similarity = $this->calculateSimilarity($courseName, $course->name); - if ($similarity > $highestSimilarity) { - $highestSimilarity = $similarity; - $bestMatch = $course; - } - } - - if ($bestMatch && $highestSimilarity > 0.3) { // 设置最低相似度阈值 - $this->info("通过相似度匹配找到课程 (相似度: " . round($highestSimilarity * 100, 2) . "%): '{$bestMatch->name}'"); - return $bestMatch; - } - - return null; - } - - /** - * 创建日历数据 - */ - private function createCalendarData($course) - { - return [ - 'type' => 1, // 类型1为课程 - 'course_id' => $course->id, - 'date' => $course->start_date ?? now()->format('Y-m-d'), - 'title' => $course->name, - 'content' => $course->content ?? '', - 'start_time' => $course->start_date ? $course->start_date . ' 09:00:00' : null, - 'end_time' => $course->end_date ? $course->end_date . ' 17:00:00' : null, - 'url' => $course->url ?? '', - 'created_at' => now(), - 'updated_at' => now(), - ]; - } - - /** - * 计算字符串相似度 - */ - private function calculateSimilarity($str1, $str2) - { - // 移除空格并转换为小写 - $str1 = strtolower(preg_replace('/\s+/', '', $str1)); - $str2 = strtolower(preg_replace('/\s+/', '', $str2)); - - if ($str1 === $str2) { - return 1.0; - } - - if (empty($str1) || empty($str2)) { - return 0.0; - } - - // 使用Levenshtein距离计算相似度 - $maxLen = max(strlen($str1), strlen($str2)); - if ($maxLen == 0) { - return 1.0; - } - - $distance = levenshtein($str1, $str2); - $similarity = 1 - ($distance / $maxLen); - - // 如果其中一个字符串包含另一个,提高相似度 - if (strpos($str1, $str2) !== false || strpos($str2, $str1) !== false) { - $containsSimilarity = min(strlen($str1), strlen($str2)) / $maxLen; - $similarity = max($similarity, $containsSimilarity); - } - - return max(0, $similarity); - } -} diff --git a/app/Console/Commands/UpdateCourseUrls.php b/app/Console/Commands/UpdateCourseUrls.php index 1c276a7..80363c8 100644 --- a/app/Console/Commands/UpdateCourseUrls.php +++ b/app/Console/Commands/UpdateCourseUrls.php @@ -2,6 +2,7 @@ namespace App\Console\Commands; +use App\Models\Calendar; use App\Models\Course; use Illuminate\Console\Command; use Illuminate\Support\Facades\DB; @@ -22,7 +23,7 @@ class UpdateCourseUrls extends Command * * @var string */ - protected $description = '从Excel文件读取课程信息,匹配phome_ecms_news表的titleurl并更新courses表的url字段'; + protected $description = '从Excel文件读取课程信息,匹配courses表,获取新闻链接,并创建calendar记录'; /** * Create a new command instance. @@ -58,7 +59,9 @@ class UpdateCourseUrls extends Command $this->info("Excel文件包含 {$sheetCount} 个工作表"); - $totalUpdated = 0; + $totalCreated = 0; + $failedCourses = []; + $failedNews = []; // 处理每个工作表 for ($sheetIndex = 0; $sheetIndex < $sheetCount; $sheetIndex++) { @@ -67,11 +70,29 @@ class UpdateCourseUrls extends Command $this->info("正在处理工作表: {$sheetName}"); - $updated = $this->processWorksheet($worksheet, $sheetName); - $totalUpdated += $updated; + list($created, $sheetFailedCourses, $sheetFailedNews) = $this->processWorksheet($worksheet, $sheetName); + $totalCreated += $created; + $failedCourses = array_merge($failedCourses, $sheetFailedCourses); + $failedNews = array_merge($failedNews, $sheetFailedNews); } - $this->info("处理完成,总共更新了 {$totalUpdated} 条记录"); + $this->info("处理完成,总共创建了 {$totalCreated} 条日历记录"); + + // 显示匹配失败的课程 + if (!empty($failedCourses)) { + $this->warn("匹配失败的课程:"); + foreach (array_unique($failedCourses) as $failedCourse) { + $this->warn(" - {$failedCourse}"); + } + } + + // 显示匹配失败的新闻 + if (!empty($failedNews)) { + $this->warn("匹配失败的新闻:"); + foreach (array_unique($failedNews) as $failedNewsItem) { + $this->warn(" - {$failedNewsItem}"); + } + } } catch (\Exception $e) { $this->error("处理Excel文件时发生错误: " . $e->getMessage()); @@ -98,70 +119,150 @@ class UpdateCourseUrls extends Command $headers[$col] = trim($cellValue); } - $this->info("表头: " . implode(', ', $headers)); - - // 找到"课程"和"跳转链接"列的位置 + // 找到"课程"、"开始时间"、"结束时间"、"跳转链接"列的位置 $courseColumn = null; + $startTimeColumn = null; + $endTimeColumn = null; $linkColumn = null; foreach ($headers as $colIndex => $header) { if (strpos($header, '课程') !== false) { $courseColumn = $colIndex; } + if (strpos($header, '开始时间') !== false) { + $startTimeColumn = $colIndex; + } + if (strpos($header, '结束时间') !== false) { + $endTimeColumn = $colIndex; + } if (strpos($header, '跳转链接') !== false) { $linkColumn = $colIndex; } } - if (!$courseColumn || !$linkColumn) { - $this->warn("工作表 {$sheetName} 中未找到'课程'或'跳转链接'列"); - return 0; + if (!$courseColumn || !$startTimeColumn || !$endTimeColumn || !$linkColumn) { + $this->warn("工作表 {$sheetName} 中未找到必要的列(课程、开始时间、结束时间、跳转链接)"); + return [0, [], []]; } - $this->info("找到课程列: {$courseColumn},跳转链接列: {$linkColumn}"); - - $updated = 0; + $this->info("找到课程列: {$courseColumn},开始时间列: {$startTimeColumn},结束时间列: {$endTimeColumn},跳转链接列: {$linkColumn}"); + $created = 0; $failedCourses = []; + $failedNews = []; // 处理数据行 for ($row = 2; $row <= $highestRow; $row++) { $courseName = trim($worksheet->getCellByColumnAndRow($courseColumn, $row)->getCalculatedValue()); + + // 获取开始时间和结束时间的原始值,避免格式化问题 + $startTimeCell = $worksheet->getCellByColumnAndRow($startTimeColumn, $row); + $endTimeCell = $worksheet->getCellByColumnAndRow($endTimeColumn, $row); + + // 优先使用原始值,如果没有则使用计算值 + $startTime = $startTimeCell->getValue(); + if ($startTime === null) { + $startTime = trim($startTimeCell->getCalculatedValue()); + } + + $endTime = $endTimeCell->getValue(); + if ($endTime === null) { + $endTime = trim($endTimeCell->getCalculatedValue()); + } + $jumpLink = trim($worksheet->getCellByColumnAndRow($linkColumn, $row)->getCalculatedValue()); - if (empty($courseName) || empty($jumpLink)) { + if (empty($courseName) || empty($startTime) || empty($endTime)) { continue; } - $this->info("处理行 {$row}: 课程='{$courseName}', 跳转链接='{$jumpLink}'"); - - // 从phome_ecms_news表获取titleurl - list($title, $titleUrl) = $this->getTitleUrlFromNews($jumpLink); + $this->info("处理行 {$row}: 课程='{$courseName}', 开始时间='{$startTime}' (类型: " . gettype($startTime) . "), 结束时间='{$endTime}' (类型: " . gettype($endTime) . "), 跳转链接='{$jumpLink}'"); - if ($titleUrl) { - // 更新courses表 - $updateCount = $this->updateCourseUrl($courseName, $titleUrl, $title); - $updated += $updateCount; + // 1. 匹配courses表 + $courseId = $this->matchCourse($courseName); + if (!$courseId) { + $this->warn("✗ 未找到匹配的课程: '{$courseName}'"); + $failedCourses[] = $courseName; + continue; + } - if ($updateCount > 0) { - $this->info("✓ 成功更新课程 '{$courseName}' 的URL为: {$titleUrl}"); - } else { - $this->warn("✗ 未找到匹配的课程: '{$courseName}'"); - $failedCourses[] = $courseName; + // 2. 匹配phome_ecms_news表获取url和title + $url = null; + $title = null; + if (!empty($jumpLink)) { + list($title, $url) = $this->getTitleUrlFromNews($jumpLink); + if (!$url) { + $this->warn("✗ 未找到匹配的新闻标题: '{$jumpLink}'"); + $failedNews[] = $jumpLink; } + } + + // 3. 更新courses表的url字段 + if ($url && $title) { + $this->updateCourseUrl($courseId, $url, $title); + } + + // 4. 创建calendar记录 + $calendarCreated = $this->createCalendarRecord($courseId, $courseName, $startTime, $endTime, $url, $title, $courseName); + if ($calendarCreated) { + $created++; + $this->info("✓ 成功创建日历记录: '{$courseName}'"); } else { - $this->warn("✗ 未找到匹配的新闻标题: '{$jumpLink}'"); + $this->warn("✗ 创建日历记录失败: '{$courseName}'"); } } - // 显示匹配失败的课程 - if (!empty($failedCourses)) { - $this->warn("工作表 {$sheetName} 中匹配失败的课程:"); - foreach ($failedCourses as $failedCourse) { - $this->warn(" - {$failedCourse}"); + return [$created, $failedCourses, $failedNews]; + } + + /** + * 匹配courses表 + */ + private function matchCourse($courseName) + { + try { + // 直接匹配 + $course = Course::where('name', $courseName)->first(); + + if ($course) { + $this->info("通过直接匹配找到课程: '{$course->name}' (ID: {$course->id})"); + return $course->id; + } + + // 模糊匹配 + $course = Course::where('name', 'like', "%{$courseName}%") + ->whereNull('deleted_at') + ->first(); + + if ($course) { + $this->info("通过模糊匹配找到课程: '{$course->name}' (ID: {$course->id})"); + return $course->id; + } + + // 使用相似度匹配 + $courses = Course::whereNotNull('name')->where('name', '!=', '')->get(); + + $bestMatch = null; + $highestSimilarity = 0; + + foreach ($courses as $course) { + $similarity = $this->calculateSimilarity($courseName, $course->name); + if ($similarity > $highestSimilarity) { + $highestSimilarity = $similarity; + $bestMatch = $course; + } + } + + // 取相似度最高的作为结果,不设置阈值限制 + if ($bestMatch && $highestSimilarity > 0.3) { + $this->info("通过相似度匹配找到课程 (相似度: " . round($highestSimilarity * 100, 2) . "%): '{$bestMatch->name}' (ID: {$bestMatch->id})"); + return $bestMatch->id; } + + } catch (\Exception $e) { + $this->error("查询courses表时发生错误: " . $e->getMessage()); } - return $updated; + return null; } /** @@ -208,8 +309,9 @@ class UpdateCourseUrls extends Command } } - if ($bestMatch && $highestSimilarity > 0) { - $this->info("通过相似度匹配找到 (相似度: " . round($highestSimilarity * 100, 2) . "%): '{$bestMatch->title}' -> '{$bestMatch->titleurl}'"); + // 取相似度最高的作为结果,不设置阈值限制 + if ($bestMatch && $highestSimilarity > 0.3) { + $this->info("通过相似度匹配找到新闻 (相似度: " . round($highestSimilarity * 100, 2) . "%): '{$bestMatch->title}' -> '{$bestMatch->titleurl}'"); return [$bestMatch->title, $bestMatch->titleurl]; } @@ -223,58 +325,130 @@ class UpdateCourseUrls extends Command /** * 更新courses表的url字段 */ - private function updateCourseUrl($courseName, $titleUrl, $title) + private function updateCourseUrl($courseId, $titleUrl, $title) { try { - // 直接匹配 - $updateCount = Course::where('name', $courseName) - ->whereNull('deleted_at') - ->update(['url' => $titleUrl, 'url_title' => $title]); + $course = Course::find($courseId); + if ($course) { + $course->url = $titleUrl; + $course->url_title = $title; + $course->save(); + $this->info("✓ 成功更新课程URL: '{$course->name}' -> '{$titleUrl}'"); + return true; + } + } catch (\Exception $e) { + $this->error("更新courses表时发生错误: " . $e->getMessage()); + } + + return false; + } + + /** + * 创建calendar记录 + */ + private function createCalendarRecord($courseId, $courseName, $startTime, $endTime, $url = null, $title = null, $calendarTitle = null) + { + try { + // 转换时间格式 + $startDateTime = $this->parseDateTime($startTime); + $endDateTime = $this->parseDateTime($endTime); - if ($updateCount > 0) { - return $updateCount; + if (!$startDateTime || !$endDateTime) { + $this->warn("时间格式解析失败: 开始时间='{$startTime}', 结束时间='{$endTime}'"); + return false; } - // 模糊匹配 - $updateCount = Course::where('name', 'like', "%{$courseName}%") - ->whereNull('deleted_at') - ->update(['url' => $titleUrl, 'url_title' => $title]); + // 检查是否已存在相同的日历记录 + $existingCalendar = Calendar::where('course_id', $courseId) + ->where('start_time', $startDateTime) + ->where('end_time', $endDateTime) + ->first(); - if ($updateCount > 0) { - $this->info("通过模糊匹配更新了课程"); - return $updateCount; + if ($existingCalendar) { + $this->info("日历记录已存在,跳过创建: '{$courseName}'"); + return true; } - // 使用相似度匹配 - $courses = Course::whereNull('deleted_at') - ->whereNotNull('name') - ->where('name', '!=', '') - ->get(); + // 创建新的日历记录 + $calendar = new Calendar(); + $calendar->type = 1; // 课程类型 + $calendar->course_id = $courseId; + $calendar->title = $calendarTitle ?: $courseName; // 使用Excel中的课程名字作为title + $calendar->start_time = $startDateTime; + $calendar->end_time = $endDateTime; + $calendar->date = $startDateTime->format('Y-m-d'); + $calendar->url = $url; + $calendar->is_publish = 1; // 默认发布 + $calendar->save(); - $bestMatch = null; - $highestSimilarity = 0; + return true; - foreach ($courses as $course) { - $similarity = $this->calculateSimilarity($courseName, $course->name); - if ($similarity > $highestSimilarity) { - $highestSimilarity = $similarity; - $bestMatch = $course; + } catch (\Exception $e) { + $this->error("创建calendar记录时发生错误: " . $e->getMessage()); + } + + return false; + } + + /** + * 解析日期时间格式 + */ + private function parseDateTime($dateTimeString) + { + try { + // 处理Excel数字格式的日期时间 + if (is_numeric($dateTimeString)) { + $excelDate = (float) $dateTimeString; + + // Excel日期从1900年1月1日开始计算天数 + // 需要减去2是因为Excel错误地认为1900年是闰年 + $unixTimestamp = ($excelDate - 25569) * 86400; + + $dateTime = new \DateTime(); + $dateTime->setTimestamp($unixTimestamp); + + $this->info("Excel数字日期转换: {$dateTimeString} -> " . $dateTime->format('Y-m-d H:i:s')); + return $dateTime; + } + + // 尝试多种日期时间格式 + $formats = [ + 'Y-m-d H:i:s', + 'Y-m-d H:i', + 'Y/m/d H:i:s', + 'Y/m/d H:i', + 'Y-m-d', + 'Y/m/d', + 'd/m/Y H:i:s', + 'd/m/Y H:i', + 'd-m-Y H:i:s', + 'd-m-Y H:i' + ]; + + foreach ($formats as $format) { + $dateTime = \DateTime::createFromFormat($format, $dateTimeString); + if ($dateTime !== false) { + // 如果只有日期没有时间,设置默认时间 + if (strpos($format, 'H:i') === false) { + $dateTime->setTime(9, 0, 0); // 默认上午9点 + } + return $dateTime; } } - if ($bestMatch && $highestSimilarity > 0) { - $bestMatch->url = $titleUrl; - $bestMatch->url_title = $title; - $bestMatch->save(); - $this->info("通过相似度匹配更新了课程 (相似度: " . round($highestSimilarity * 100, 2) . "%): '{$bestMatch->name}'"); - return 1; + // 尝试使用strtotime + $timestamp = strtotime($dateTimeString); + if ($timestamp !== false) { + $dateTime = new \DateTime(); + $dateTime->setTimestamp($timestamp); + return $dateTime; } } catch (\Exception $e) { - $this->error("更新courses表时发生错误: " . $e->getMessage()); + $this->error("解析日期时间时发生错误: " . $e->getMessage()); } - return 0; + return null; } /** diff --git a/app/Http/Controllers/Admin/OtherController.php b/app/Http/Controllers/Admin/OtherController.php index 035a7fd..f133af0 100755 --- a/app/Http/Controllers/Admin/OtherController.php +++ b/app/Http/Controllers/Admin/OtherController.php @@ -163,7 +163,7 @@ class OtherController extends CommonController // 培养人数 $courseTypeSignsPass = $courseSignByType->count(); // 去重培养人数 - $courseTypeSignsPassUnique = User::whereIn('id', $courseSignByType->pluck('user_id'))->groupBy('mobile')->count(); + $courseTypeSignsPassUnique = User::whereIn('id', $courseSignByType->pluck('user_id'))->distinct('mobile')->count(); foreach ($courses2 as $course) { $courseTypesSum[] = [ 'course_type' => $courseType->name, diff --git a/课程台账.xlsx b/课程台账.xlsx index 3bc5dde1b46d29ae16a036196038ad8770a720db..4a2659a7261919437197f93c010761754a27b238 100644 GIT binary patch delta 11321 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 delta 10394 zcmZ8{WmFwOwPx8Gt^Jb=g zboJVM*X~-?-D}mUbDI3VyVvDG1Lg1u?qs2%pv+;Qps=8zpe&s&)ZCn1+}SOh-K^Mr z93A3Q)K$YcFh_nVzn~YL&%%T_qSZ-Ha0@JT#2S85x zv&}HOf+<|7ErLS25REuAv0zJj7o`2{4p~KOqw@P!tCz4-=ucmZbVIE|^&$}Sf2l}& z$RdkUK~#+rq53!%DJAs61}F!`^icG(#Sn+5Ss0liEl#BkXJFQca>3%nlI&2U6?h|n zoxnS0k0Zsm5$#t6w3eM<#~3A-ab6C{;rBCc0CVKR(&TD&$5joC>k0v#?MCD!m(s3w zHZ9Q`nQQOdyEOc^AdTNukB9N@bEI2XOq6uAYPzUis=9O`9^GCbM{70(sw&3SpG%6J z3RKN(76!e^C<1X#Ka=;E9TGJ~SU6Cy5OD)7Y$i1FmK9I90TL9H2NP%*jvSb)Z{)ly zfE^%a`W{wr-^4mb;fiVVV~?^G_AYp6^K_$OSO?p}VZw&_a$sTBNd&FsfGusgL3^=);v2ECkZF)o^sZDsdWmm0=V&t$S&jYKZ+~4k8pMKJb zwoL_YoO-_|)@_8SK{WGDZiTw@*mkF|^k~tTGC<8O6dB#Z0biayL2GA* z1h0pm4(>dSUi3xRtc+U4gI{b`jM>L#3cDYlYQ26rJ$aopYQ%LSbbD>^W4uHfWIyde zZ)x)71l|8!^Xf4n@B;2z-t?#qz8)egnTOvT6s#$#O`RXiH3vG1-gTYcy>B=^-+TTb z&RFUsTz)^S0Xlm-pLZ2D5DJ%jQM?9uI{@v)AYC1{Mq1sgC6JEEvtM&&2inGQ=u_US z(3VC%$?vPefip34^f$;SX+9!`dfRDd@65(}@t?ItjMy%I2?KGKGDlw%qsJ@AS1Nxd zO}KW(Chwy7>n&oWqIP(Ej+i&fvUU}tq01UdMAIjVI9R%Q@i?oY@tz0#(%cQq52(sT zJ!xRwPR+f7YtQlhxwlG{lpXhA8UJ-Aam+!KjC9?OljUDZ=#Sx_32_#( zNv@KJ9l;$cYF*ci!8BLRPCxk)~MeaI^IEyRQ@TRbL|9`#32_Mzqj>2`t!_7|Pa2A$)` zeF->8Q-rW$O>Rfsx<;lbE?4Z$t}FTnm>($75d~y^vX= zBmF(7xf>uri`Hu|-&C#<9CnttZ|b^9shIq0&~_eC1K>u;MN6o`aF7qN%z7#UcpwxW z^;H$o^G>p=`=MxBLr)`6;1;hIGDh0gk0_-g|4yg(bZ61@W+{&>i(G#>RstbMcnsM< zTmOdhm?@P-vUNzb?ddBMOTV05mQ+j^pD$5xHU*B$X_zPh5FpU%D;`Yp^Pj#j5fZaH zN1mnan?`N>L?!1CDhovY&>Iwp#!)?{*i}0UB1-SteR{S9YTE25q=5F&{Xd~rD!%v{ zZBR4U$u)jV;c_EH<=Lj>z~?UF&Q7)zL_D`_L#XK6>VG9MiY1L;aLr6!|AB!VQ8Xqf z@DbQ?z@fF}Gn=V^%R^|Dliq)m$q@MyOz%X<#`dC=c*jpWWYiXOgLHn8}@EYsBK z%VGzZ>thcfpyac8$FLat_)C^C-Xv?+@tq#pZ-PciWA{lg&q&h_n)(s! z1FeG8%}=ecSnSBQq{Jl-9?fZq`zbq@u2~>rHqD(`6g}x+r^6&=Zu`6u7t6>|>=~CKz6#HCIjxt!fQE+5EcmON=`b=l2v-&8R zGt0@aIZ%;O&-M5_iJeeFLgkz}M(2T`X4k7E${`l{{J4Il9%j@DDM++PBUu=W$QKd@ zTS9Dx(k&6zm3P!C^ma-wTaHnNq~>H8TGM|-zQHflDhvk8#7jb}$8gE&2r6|~^md}@ zOrjV*k~l7ONlu-CSvf((4K$l_66=4ezpGpo>f2f@w}Ea`PWBH1DhmRljP=mm6;s+N zrZvPS$#*%&1uV0_qN2Jhg3yb|PPy2NWZ@>sgkiiy8Vv>hWCSYq{6BWW-Els%r$b`TXMt^TH`7^hWtZy zq=HdoM1Ln(ez|V8W%jQ5l}T!f7_lnO?9OVr$HR;mgkbm8) zcNo+n_gaGw;UV0I=eorR$^eh2;7;nbYQ?~_y|{rt++z7ds}D;C=^7~YNfdEROSbgN z*oE*C5!44Wuu~3NaKaUoz|exz}*vD{kNoxPhYx+xq^B ziLJFUPa_*W|!j2nyQ$g;ebVt?nY za^Fmni}iRK|IcKpJhVt?+mqkNqT=zeBV(i6dFD^GZ~)3xZTGSxzZ-#QcSK{!$;Lzw z`$o_&qxa56h*XZftM@*+bLGdioZ@XDD-av$>Vn^H|Y$3XmBbaYH_q zGy)RV@^4riG$<%n&?x~ukh1ZRzHT4zr@8#J_rq*&2V2TnxO6<)V2VAX42q?M{BO+w zdFtCODQqkGBFE@d%dw(}q7RMlgIVr2*84rP=hFqFm-~}yZYUol0AT;Kwu8&ubFi>? z&(i_u8_auV0N=Hb-|^;ZDZg_(Jbh{Rx3}%f#l-vJ?&rH1{x=SMU}jcs=9Ae@=~4NZ zzr}ktnCSWV%^B$KZEejL+@#5UKTJL~E&A#f5b*o?6G)Zb+DSz$7u?Sd%n7=)G{=7r zxZ8c0T(AM*h)n{r!QjBFg$J7!rwt+?*QJjg%f;)>jAp~?{WbeAjF_s(+w%;_1S4;~ zNBH{TZR6*2Q86QMqg7bc|NX2rpkH(8 z;`7j*i}?c&@WPupn}bDjr{Q4x+wgA1RHKTVXpI?}wM-INy>c?_wJb-`eU4i|frmV+ zD!RxdYZd(+-e^IZCEEMO39WXc4&w)9g%wkEdB6182Vo7r4W_P_jX_W;u->bA>XoC& z6zI^aFI5wT$VgB!5JCvA}ci+sG^^ca8PYJ(W$FZ?4CEf+@{yyUM{c_d#x3_W~a^N~`1K%+Tr9=4l>g>Jyt==$1{2 zQ(AI6hkXao&(AD)aiNl`35#R1m*L%lm8kFGV(F?)b(~wE#jx)c{ar{JWm?BqnW7V%K6j|n>zYG_-<9Q$f1zwtK9sUltt`H`>vHa|54Tz zI9HaIOeKm5m>m#>!>dfyU1$ok@BO-ISZo<{T9~(nM-N6RYSR?9uAbPg@m8efL^Obo zsQ!@Ik4<(!tGg;usn8QNXout-Ht)1)o11MD%fTS^#|zYsGT(VA`6~c?O1d!s9apur z+wNR|AWi}9qQ>tp@;0PNuclMczo@AOvS)~4v1}8rFd!96aZq@h09g=L_@;)lI+h3C zu7*riZSaJaWk;*NR?Cfx0t&I0_+&g@GnkBN$j;4~O|EMIBoxug0X<47h%iS=G z?PxF+!kP{-=5$K*u)hTguMu2zlU+*>pAfb+?Zih9)!C+@J)_~G6sqAsuxzk^;=eWE zUOdK%QyG5L@Ds?+Qiv_M;%Os0Z*AxcK$XGV$jbz`uXT?G8?kS8`f3hEliSBS8}zYq z(~QxC6PMg&Lu)xqt2-jj1d=^GL#Pc()s{wCboX048pkKf>MW|A^>R@a*hi2iLo}@O zPN5VZk&ahwT0}dhw!}wV)BA~l6Lc7*95cui4BCsfh2x9EOy71tC)WmD^cOGd=oKC+ zcKJ$NDieH0{g#_1IVxBg6|5YiZE@LK#iCgTyJ^*kFZ*-Q;Rd;J6FJ)aPh#UUHgV={ zL{#lzE{`Ad$s+(NI>mk+{7MYQDNgmcbD9^3`^iE&2@h3k-(fnRkL+gv)F5068<02+ z>*p7+6XCEGq(U=|jNe2ZuRA6!g9uKfO9OMzHLc`plx&Y7ja)WEDvvflh@mA?sE|s+ zY9`N?U0CedLL*h$V$_l(LT9)=V?N6SBy!0Ua5MONHZ}KGnn?{3&)iROtw8`oDLX~} zxBc6nu-{gqENI&N6Ft*`)!#lr!gMHO~b4^HsWdvM;xs*aX)ndpp3ovC`O=ef|QZX!&ez=8LVj*kHSEzU5@-(Kz| zYvM?=bG_%@9yZN44QfnYp|V{yPN(mDHbr{uw{q?`)p+YVG7M`#jMg6nGS|!oq!nJ( z(ego^q&(@BanP`RO6{l3o*wPjor+9H#fcObf*6R2l7yX6>WINP(>e2^S#Yw%5A2i@ z$6qpny0Z4B<(twf+lJH^>`G<3j+a$AiLdzx?T#(j=eP&mH|8E|hC7dEoCRP-khZYLevo0C#Bgm{$l2 z|MDCGrEl-CPl}vhgNOYc3MEY@6Tv`(=Q}^|3zKpKr=@3UPr+_)Q&8WoHXdQ?jZRpo zV;+U+U@)wDSG+Z0Ryg2>C>E8kWmlmogqTvPI)#xGBRS>)=STi8;y+dPiiCKIp?Iu5 zbS8;Lj}zL*K#Jp6g8EGeK$Tozon&=FpC1ULk1Z1`CKWMOx3aFrXG>=Y2M!7O?(0%JJFcjrcWBndBf0zOlC?y;0xNZk zByK1mY{sHnp{a2)MMob@MXMnKsEfRC+nknOd%m&&*7^3-TOW5-H6b#d5h&M4l-v6j zs^v>hC_Sa1)f#SLdMKD>awpJ21F#8TLUoL}sRuxG~+~ z4Pag%{D90IWt}APoM%GW7Sy$AA;UpZ>vv2SU1B;i*Q8ixHN_ax2YjxU`KG8?F>FHd>#eoibI%@%3 zB=_DY)(cgIQe0UPTIyL6DIw*h3Q_Cw2xC3Jn`ex>=`*Lg2jdEm zJ!Vy3#}euD+>Yfy`)aWF10RVyYn^3WPLg#mg(1TS)`9b~5o<;_+{2=rQS@gL#4fTH zWV8zWJ_^ZLS(|vftM94~)^QoxOKUWC?LwB4IhMvQ2F0Q**;wH2yUOOH3 zsd2|O=_v=#$_Uk@JeipECE-(!Il-RE2MQD%%&+ z*+Us<5go6bSjf^eS>Kqb@!&lw{g_%!xyuUB(M1Pr*Naa{obFb)F9Lc-It)+JjyE@Q zU)43mzN*Xj+F*^!-c33z3K~E$ec-=U!|3V0{9MG<4SV^WghH;~Dk*RZ6ibT&Bq?Ni z=30OG`AcC>enR7w^sd9vu&7tca?+-w!g_jPy*r#rs8Fev+;9_gIq>$PwB=9d_>D^0 zsn`Fz2sE=6zuL~3LTX>m$WQrLf5JFnON{nBnB473&$r(Gx9{@C`kw7uvpqgTN8%{L zDzBo>nl}_^vf$S<-vO6}!kN}E#tzaA9~pQ{G`>b{PX+IfqPq{4H*Y}be}~t&f3pK0 zpdwXv#Cg8R;( z%B8OXU++I0oRfaD^l6&lK0%Qig1b*%s85Cm;HO?TC5p0rx5m&@z)_K>7kD^iX7J$k zdv|KZyLyz5vYCKty;866a&z%~xP(p$(Ly)GCG&nyIsgkb!ZA z)s+cH-3h7Xbx{kV_yt{zq-ZUrNuHUk!X*pM`nhzM@$`xq&g7f|&bchDq>mV_h|3b! z=y44Mkjd-!Nt=-nN=IX z8kDXzE*;(cNdZQx5^B?+R_e1Fw|O|O_&?rW@84rA+}5k}564-6(K3CL_!r9=BCBz+ zheOiwh-c*+N2=za?d7|)Tpvjc1K6wG%2DshAg38BW~I()H9Gb*S-`|@cw_cDNMMUEk)ZnZ{1Ie| z^3ag4i^I;}=p$IE>+so(){BnQmzzK^wpfjNu3C4ml{}h%VgJUZi6m-Mj=QnYOqnFr zuJNoodMq=_M}IB z@>%9CVJ%v%e3yzr*HR5*;4D1)p!WXSGnaDvb4+b7(Ar@pj@xV93ncLAUgp&DZKmgO z=GyXlXS+AK#vsqRV?ak`fQZApFePgBon$5==Y2V+|#q3`6LZn z`hDE3i;LsbEZSw2(BqneD05SFxX^8dEN-&*Y&C(5)XbRf%j|}aJMhrYdIVYZ$QyvFU#$OydxLOFc=WOa9%;~c-SC7JaSso3eN%%n}N&SJtl zmVx$Pv>$g%7_&? zq2u00Bez9Rc4+zVKrS9cd`3>exBcrbfdx(L*k7dm6#t%QP5t_S&Xu=diM41 zHR6@0nexmCJ#JDP&3qSWyOmo+Z5mM4rucy&LcpPk`p#K_hG1 zg3*r#IkSNnEY7yQD;f_0x3+MB`BZT{YH5{}<88(FBG+>2t~n`*z20OUk&Ua)4I=D^ zC-8j7OA`mK&k&@Z=17r~+0cYQvimlsxHVsGC559zcKLL>-d}E( zjw`dkzg^RbzpTHrU_BI3{QlS)WR4(yfTLwgs8PIi#C&Ibeb%$9d@*OLq((b!f1)-d zplfaMgsF^*ow%am?z2Qdp^p7BuF+DeOlh#%TgqhB9JnimgZt_;wzce=LVsL`UTp1@ z(n9C|g{|wl-Zi@a)4e)||lKZ%;c(NRc8EZDWk=A0t`9oGA+Y?>2h@BH$z z^$g$rM!`|91>N_kw@;~c{fM!?)fz5Pv95EI?gDZf>T?u!Kq#g%TLzks@XLsmAP^L8 z;?1$j z5IdyU{mT__O{i2MW}gtD>Sj9)TF$CggFT!#yz>0o@pp_(IaY~*+@{flYfV1Sb660# zk2kd+<9U)=%%Mj-w#t?Q+VAV*%tdmM{Z)O_7ood17gIDd!PP66j#zw9$Aw)o-qt`+ zXmv!!>A_{%*;3hRZ z{fEasrU=c;ywoO_iinoBs_+UWD;b4YX6ER3@8Ph7eIg%r+-hdqqp)t)0yj?L!nitP zVs0d?)KVm@x_C|Pn8Y@Z=HRbG54goOw^xixlr>qy90bkQ!~=~9Y8InJdae6z1Lm$M z+}QBI6-?fy@7eIt`IMqc3z+j#PG}hr7zQQD@JSgQrcTSUPF;rn^ihHNBygF=MZHjQ z=I>lhyW%`^eHXcyMa{p6YB6s}5*&9bS9<23)J8sxdQ){ROJYG6QZz>|uAh1@hjB-L z7?e5?YM9GXlok+(+Vd5-91#`9hZZ%gvQdDk#Vg{9a*&}#>}IpUYH&ZAHL~`A9%~!9 zWOC!4%UIKN_)>~}O>7Ze$?igZx6EHTmFEMho_ z0=aY1$8SE}cF|jHj$Ej(bHpIH{L6sT!yWxwqX?Q$&6=wI@@6!~tPYTYFM6xM=E&HG zaY!C=2feD!Vb+i@VFsPf9Q1d=Wq*w4+IEF^x0f+XyevUj-JNn~YbX3%KK{ae-=p5! zKi;NvsE)YekX8jje)K49g&k%t-*6_ng-BhxWx7W%l_;_#R>x%ZU^x^5N%6w3V?G<) zFvBVWyo6l@k}^Skd>Zk8#G*HW_%%LgQ<$N(YQ^vck6?ojQ$)7Eb-> ze;x$olPePxbp$#`_uk?6eC-Z)XoCF-X1|#>zg?2N?`%eXt+BUFqfeyx1_($T+s*xP z+KcZupBp@t!VTx`BntGixq4$)5qnaWM#u`LqVYD77RHbg{P^xC;QP`5jDqS*AMYEX z0l68!Y!iLRdSk^8^>a!^_;#x}uSB>J#PBYVQsSEW@SvNLQZ_Q1@5-ANF+)8y`|`Ye zv|+PgA`-Qiug+FXbze@q&hhxt0NQ0&JtymVIP3z^iVy=e`~u9W8Qq^|dcd1+9N1A2 zp{Y{t=WI_W4xJ<9Rwyo4L(WMoy7(e0SY8fm?DCs2DezaUypK|ukJLMAjA$3< z>u%Us?k^Rm4RFwljMBueBpyPHcu)jB8a$CwdB2dz>&WR*{JZqd1(<0FzD=2`{CKZ# zp%s9Er2Qd!>|n&zmmIAE6Rng#=;76@a=5Nz-CK zI_t1fdj`|dE=~E~BERr@d7jVxhUv$6;|O_#gP(_7z(WdPpI$UlSSnG?F3F!4nbpXg zIe+PhPfprV>sqyHd;m9IDmQrDXm0zo5f(!ShF*sX;AaLB@`k!TgI@Y2=2WyqLxFip zQ_yi*CR%FFZ=uiLGKbqy6_v-Sf8-4h#*HiR0@2->MZp*m z_ZxkTDvUKac3NB4M_wI1)rbV%tQ8eeVgSPCs%&#^*MEsxTWC3@&x5B&c7WbUuTnm= z9kQp@hd8!_)AEQnRF>!j`C6S!*t^`SU^N8yFQYhMp^!1D)FLlpRtX695R_w?-BU(6BA`0Ys*6yfo$JG_&L z0a+M}wMg5wajF`XI4Gpx4h_W`hS2h-N!vW;&!2R}U#Aevz#`X__;}NB)>J37Cw=|Q znLTEw1oP?Zy|L~I437s+UR-UKlDCyRhk)BV?ar@uXg3|O9z$Y4Q5A}r@&TU;o7<@$ zIEL{(4rzziRx_c`3zaL)9fw5#)SdqMjMEdRV7(@Vo2*ow-TA6f7jluX+Ygt6AN>b9 z^i2UUJrZ@C&1PwrrM!XOrJGui{p=EZD@SMV+D@a494kGi6MtSQK98m+wm7=7WPfx+ zbd1th*Z{F(2x1M;!tBTtjwon*&O-kY_F)cw;<@J8%aAltZy^Se%>l<$ahJ+X|ET-) zQ3B#Q4EsP9sYU|eQKfO2%)0V1Ow%Dpf%2?@%M+m z!hypm0D+4))4$|V{T=VzpH?Sfoma=aJ6)BKHFsBCBm4*H==Uem0?Ty?DS8*|}I8GyHy@GffzzKv%)(vsNNmKD)&^ZJu;q zHL#U}L1X@4ZbIzON1NQ08`0fiQ?_5yxxiwtU8%EdFWix&xhCAv5y4jJ_G|?8@yRhg zUP{Kl>6cjYZH!7#A^e>r<66XdZ*7r}*$x`cA>NZ-WVsJ30+#qQ|F&G_AP`Dr$j2|bcO)v%iKh=96H6bl%cu=vBIHD=sePCleIZzli={gNCB~driOvWeC%g{{I2n z{|IPkK|h5BVMNhExBy-lD|C<|fR6OvSpd!7+ClrjPsaGC{oDS_Z-ELF2M~r10gVEv gNdKqa4GN0zzf}J%tA+!O!x4ihL@*J;dH*s03+~(O(*OVf From 62442d83dea70d2c13082432af7b71949638729c Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Fri, 22 Aug 2025 17:58:07 +0800 Subject: [PATCH 08/19] update --- app/Http/Controllers/Admin/OtherController.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Admin/OtherController.php b/app/Http/Controllers/Admin/OtherController.php index f133af0..cb33c19 100755 --- a/app/Http/Controllers/Admin/OtherController.php +++ b/app/Http/Controllers/Admin/OtherController.php @@ -134,7 +134,7 @@ class OtherController extends CommonController })->get(); $list['course_signs_pass'] = $courseSign->count(); // 审核通过人数去重 - $list['course_signs_pass_unique'] = User::whereIn('id', $courseSign->pluck('user_id'))->count(); + $list['course_signs_pass_unique'] = User::whereIn('id', $courseSign->pluck('user_id'))->distinct('mobile')->count(); // 开课场次 $calendar = Calendar::whereIn('course_id', $courses->pluck('id'))->whereBetween('date', [$start_date, $end_date])->get(); $list['course_total'] = $calendar->count(); @@ -193,8 +193,7 @@ class OtherController extends CommonController $query->where('company_area', $area->value); })->whereDate('created_at', '>=', $start_date) ->whereDate('created_at', '<=', $end_date) - ->select('user_id') - ->distinct() + ->distinct('user_id') ->count(); } return $this->success(compact('list', 'courseTypesSum', 'areas', 'sql')); From ba08383ff616c6f15c29a94e54a899e5ad6a7764 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Fri, 22 Aug 2025 17:59:46 +0800 Subject: [PATCH 09/19] update --- app/Http/Controllers/Admin/OtherController.php | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/app/Http/Controllers/Admin/OtherController.php b/app/Http/Controllers/Admin/OtherController.php index cb33c19..1ff1832 100755 --- a/app/Http/Controllers/Admin/OtherController.php +++ b/app/Http/Controllers/Admin/OtherController.php @@ -182,19 +182,14 @@ class OtherController extends CommonController // 区域明细统计 $areas = ParameterDetail::where('parameter_id', 5)->get(); foreach ($areas as $area) { - $area->course_signs_pass = CourseSign::where('status', 1) + $courseSignByArea = CourseSign::where('status', 1) ->whereHas('user', function ($query) use ($area) { $query->where('company_area', $area->value); })->whereDate('created_at', '>=', $start_date) ->whereDate('created_at', '<=', $end_date) - ->count(); - $area->course_signs_pass_unique = CourseSign::where('status', 1) - ->whereHas('user', function ($query) use ($area) { - $query->where('company_area', $area->value); - })->whereDate('created_at', '>=', $start_date) - ->whereDate('created_at', '<=', $end_date) - ->distinct('user_id') - ->count(); + ->get(); + $area->course_signs_pass = $courseSignByArea->count(); + $area->course_signs_pass_unique = User::whereIn('id', $courseSignByArea->pluck('user_id'))->distinct('mobile')->count(); } return $this->success(compact('list', 'courseTypesSum', 'areas', 'sql')); } From 699593080646187464d2407da5e6da4dba1d78b5 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Sun, 24 Aug 2025 10:03:50 +0800 Subject: [PATCH 10/19] update --- app/Http/Controllers/Admin/SupplyDemandController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Admin/SupplyDemandController.php b/app/Http/Controllers/Admin/SupplyDemandController.php index 3e40c56..e29ba42 100755 --- a/app/Http/Controllers/Admin/SupplyDemandController.php +++ b/app/Http/Controllers/Admin/SupplyDemandController.php @@ -373,7 +373,7 @@ class SupplyDemandController extends BaseController $dialogue = Dialogue::where(function ($query) use ($all) { $query->where('user_id', $all['user_id'])->where('to_user_id', $all['to_user_id']); })->orWhere(function ($query) use ($all) { - $query->where('user_id', $all['to_user_id'])->where('to_user_id', $all['user_id']); + $query->where('user_id1', $all['to_user_id'])->where('to_user_id', $all['user_id']); })->first(); if (empty($dialogue)) { return $this->fail([ResponseCode::ERROR_BUSINESS, '会话不存在']); From 6219185dcd6313524c9e26c3ab255b046d8d439f Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Sun, 24 Aug 2025 10:05:00 +0800 Subject: [PATCH 11/19] update --- app/Http/Controllers/Admin/SupplyDemandController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Admin/SupplyDemandController.php b/app/Http/Controllers/Admin/SupplyDemandController.php index e29ba42..3e40c56 100755 --- a/app/Http/Controllers/Admin/SupplyDemandController.php +++ b/app/Http/Controllers/Admin/SupplyDemandController.php @@ -373,7 +373,7 @@ class SupplyDemandController extends BaseController $dialogue = Dialogue::where(function ($query) use ($all) { $query->where('user_id', $all['user_id'])->where('to_user_id', $all['to_user_id']); })->orWhere(function ($query) use ($all) { - $query->where('user_id1', $all['to_user_id'])->where('to_user_id', $all['user_id']); + $query->where('user_id', $all['to_user_id'])->where('to_user_id', $all['user_id']); })->first(); if (empty($dialogue)) { return $this->fail([ResponseCode::ERROR_BUSINESS, '会话不存在']); From 0063d66b9d848b8a48bbc52ad4abcdaac7eca9d1 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Sun, 24 Aug 2025 10:21:25 +0800 Subject: [PATCH 12/19] update --- app/Http/Controllers/Admin/SupplyDemandController.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Admin/SupplyDemandController.php b/app/Http/Controllers/Admin/SupplyDemandController.php index 3e40c56..273d806 100755 --- a/app/Http/Controllers/Admin/SupplyDemandController.php +++ b/app/Http/Controllers/Admin/SupplyDemandController.php @@ -278,7 +278,13 @@ class SupplyDemandController extends BaseController } })->whereBetween('created_at', [$startDate, $endDate]) ->paginate($all['page_size'] ?? 20); - + foreach ($list as $item) { + $item->dialogue = Dialogue::with('user', 'toUser')->where(function ($query) use ($item) { + $query->where('user_id', $item->user_id)->where('to_user_id', $item->to_user_id); + })->orWhere(function ($query) use ($item) { + $query->where('user_id', $item->to_user_id)->where('to_user_id', $item->user_id); + })->first(); + } return $this->success([ 'list' => $list, 'supply_demand_count' => $supplyDemandCount, From ccc75c79ea4bb811da4858565352a3ec73439da8 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Sun, 24 Aug 2025 10:25:05 +0800 Subject: [PATCH 13/19] update --- app/Http/Controllers/Admin/SupplyDemandController.php | 9 +-------- app/Models/SupplyDemand.php | 5 +++++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/Http/Controllers/Admin/SupplyDemandController.php b/app/Http/Controllers/Admin/SupplyDemandController.php index 273d806..9cd6795 100755 --- a/app/Http/Controllers/Admin/SupplyDemandController.php +++ b/app/Http/Controllers/Admin/SupplyDemandController.php @@ -270,7 +270,7 @@ class SupplyDemandController extends BaseController $interactionGrowthRate = $this->calculateGrowthRate($interactionCount, $prevInteractionCount); // 当期供需发布分页 - $list = SupplyDemand::with(['user', 'messages' => function ($query) { + $list = SupplyDemand::with(['user', 'dialogues' => function ($query) { $query->with('user', 'toUser')->limit(2)->orderBy('created_at', 'desc'); }])->where(function ($query) use ($type) { if ($type) { @@ -278,13 +278,6 @@ class SupplyDemandController extends BaseController } })->whereBetween('created_at', [$startDate, $endDate]) ->paginate($all['page_size'] ?? 20); - foreach ($list as $item) { - $item->dialogue = Dialogue::with('user', 'toUser')->where(function ($query) use ($item) { - $query->where('user_id', $item->user_id)->where('to_user_id', $item->to_user_id); - })->orWhere(function ($query) use ($item) { - $query->where('user_id', $item->to_user_id)->where('to_user_id', $item->user_id); - })->first(); - } return $this->success([ 'list' => $list, 'supply_demand_count' => $supplyDemandCount, diff --git a/app/Models/SupplyDemand.php b/app/Models/SupplyDemand.php index b9c0860..9fb41cd 100755 --- a/app/Models/SupplyDemand.php +++ b/app/Models/SupplyDemand.php @@ -34,5 +34,10 @@ class SupplyDemand extends SoftDeletesModel return $this->hasMany(Message::class, 'supply_demand_id', 'id'); } + public function dialogues() + { + return $this->hasMany(Dialogue::class, 'supply_demand_id', 'id'); + } + } From 3574e0efdab2a5687a060b4b5186472dc72c1c7e Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Sun, 24 Aug 2025 10:57:02 +0800 Subject: [PATCH 14/19] update --- app/Http/Controllers/Admin/SupplyDemandController.php | 4 +++- app/Models/Dialogue.php | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Admin/SupplyDemandController.php b/app/Http/Controllers/Admin/SupplyDemandController.php index 9cd6795..5c6a116 100755 --- a/app/Http/Controllers/Admin/SupplyDemandController.php +++ b/app/Http/Controllers/Admin/SupplyDemandController.php @@ -271,7 +271,9 @@ class SupplyDemandController extends BaseController // 当期供需发布分页 $list = SupplyDemand::with(['user', 'dialogues' => function ($query) { - $query->with('user', 'toUser')->limit(2)->orderBy('created_at', 'desc'); + $query->with(['user', 'toUser', 'messages' => function ($q) { + $q->orderBy('created_at', 'desc'); + }])->limit(2)->orderBy('created_at', 'desc'); }])->where(function ($query) use ($type) { if ($type) { $query->where('type', $type); diff --git a/app/Models/Dialogue.php b/app/Models/Dialogue.php index 19b3fd0..671b3ce 100644 --- a/app/Models/Dialogue.php +++ b/app/Models/Dialogue.php @@ -15,8 +15,14 @@ class Dialogue extends SoftDeletesModel return $this->hasOne(User::class, 'id', 'to_user_id'); } - public function supplyDemand(){ + public function supplyDemand() + { return $this->hasOne(SupplyDemand::class, 'id', 'supply_demand_id'); } + public function messages() + { + return $this->hasMany(Message::class, 'dialogue_id', 'id'); + } + } From 8aa5e4b7c1812c412980bfe75d11aa00ff38c6b0 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Sun, 24 Aug 2025 10:57:45 +0800 Subject: [PATCH 15/19] update --- app/Http/Controllers/Admin/SupplyDemandController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Admin/SupplyDemandController.php b/app/Http/Controllers/Admin/SupplyDemandController.php index 5c6a116..1bfeb9a 100755 --- a/app/Http/Controllers/Admin/SupplyDemandController.php +++ b/app/Http/Controllers/Admin/SupplyDemandController.php @@ -272,7 +272,7 @@ class SupplyDemandController extends BaseController // 当期供需发布分页 $list = SupplyDemand::with(['user', 'dialogues' => function ($query) { $query->with(['user', 'toUser', 'messages' => function ($q) { - $q->orderBy('created_at', 'desc'); + $q->orderBy('created_at', 'desc')->limit(10); }])->limit(2)->orderBy('created_at', 'desc'); }])->where(function ($query) use ($type) { if ($type) { From e99e5a84e083b68edce5db335dab1f27c9e7b412 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Sun, 24 Aug 2025 11:06:34 +0800 Subject: [PATCH 16/19] update --- app/Http/Controllers/Admin/OtherController.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Http/Controllers/Admin/OtherController.php b/app/Http/Controllers/Admin/OtherController.php index 1ff1832..7912f9e 100755 --- a/app/Http/Controllers/Admin/OtherController.php +++ b/app/Http/Controllers/Admin/OtherController.php @@ -122,6 +122,7 @@ class OtherController extends CommonController // 报名人数 $list['course_signs_total'] = CourseSign::whereDate('created_at', '>=', $start_date) ->whereDate('created_at', '<=', $end_date) + ->whereNotIn('status', [4, 5]) ->where(function ($query) use ($courses) { $query->whereIn('course_id', $courses->pluck('id')); })->count(); @@ -129,6 +130,7 @@ class OtherController extends CommonController $courseSign = CourseSign::where('status', 1) ->whereDate('created_at', '>=', $start_date) ->whereDate('created_at', '<=', $end_date) + ->whereNotIn('status', [4, 5]) ->where(function ($query) use ($courses) { $query->whereIn('course_id', $courses->pluck('id')); })->get(); @@ -157,6 +159,7 @@ class OtherController extends CommonController ->get(); $courseSignByType = CourseSign::where('status', 1) ->whereIn('course_id', $courses2->pluck('id')) + ->whereNotIn('status', [4, 5]) ->whereDate('created_at', '>=', $start_date) ->whereDate('created_at', '<=', $end_date) ->get(); From 60e2821bb40fb4d06a7753e72e2f16f77f7ebd7c Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Sun, 24 Aug 2025 17:42:28 +0800 Subject: [PATCH 17/19] update --- app/Http/Controllers/Mobile/UserController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Mobile/UserController.php b/app/Http/Controllers/Mobile/UserController.php index 4b58cd3..96353cb 100755 --- a/app/Http/Controllers/Mobile/UserController.php +++ b/app/Http/Controllers/Mobile/UserController.php @@ -254,7 +254,7 @@ class UserController extends CommonController })->where('id', $this->getUserId())->count(); // 是否生日 $is_birthday = 0; - if (isset($user->birthday) && $user->birthday == date('Y-m-d')) { + if (isset($user->birthday) && date('m-d', strtotime($user->birthday)) == date('m-d')) { $is_birthday = 1; } return $this->success(compact('user', 'door_appointments', 'course_signs', 'enter_schoolmate', 'is_birthday')); From 4e11516ce214b0ba1db7781a68fc013294a94f18 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Mon, 25 Aug 2025 11:07:11 +0800 Subject: [PATCH 18/19] update --- app/Console/Commands/CheckBirthday.php | 3 +- ...urseContentEvaluationTestDataGenerator.php | 358 ++++++++++++------ 2 files changed, 240 insertions(+), 121 deletions(-) diff --git a/app/Console/Commands/CheckBirthday.php b/app/Console/Commands/CheckBirthday.php index 26a6839..a87d5c8 100755 --- a/app/Console/Commands/CheckBirthday.php +++ b/app/Console/Commands/CheckBirthday.php @@ -44,7 +44,8 @@ class CheckBirthday extends Command */ public function handle() { - $users = User::where('is_schoolmate', 1)->where('birthday', date('Y-m-d'))->get(); + $day = date('Y-m'); + $users = User::where('is_schoolmate', 1)->where('birthday', 'like', '%' . $day . '%')->get(); foreach ($users as $user) { Notification::send($user, new BirthdayNotify(['user_id' => $user->id])); } diff --git a/app/Services/TestData/CourseContentEvaluationTestDataGenerator.php b/app/Services/TestData/CourseContentEvaluationTestDataGenerator.php index ac613c8..d911d0f 100644 --- a/app/Services/TestData/CourseContentEvaluationTestDataGenerator.php +++ b/app/Services/TestData/CourseContentEvaluationTestDataGenerator.php @@ -60,33 +60,34 @@ class CourseContentEvaluationTestDataGenerator } // 3) 生成评价问卷主数据 - for ($i = 0; $i < $evaluationCount; $i++) { - $courseContent = $allCourseContents->random(); + $selectedCourseContents = $allCourseContents->random(min($evaluationCount, $allCourseContents->count())); + + foreach ($selectedCourseContents as $courseContent) { $course = $courses->where('id', $courseContent->course_id)->first(); - + if (!$course) { // 如果找不到课程,跳过这个课程内容 continue; } - + $evaluation = $this->createEvaluation($courseContent, $course, $faker); - + // 4) 为每个问卷生成问题字段 - $askCount = $faker->numberBetween(5, 15); - $asks = $this->createEvaluationAsks($evaluation, $courseContent, $course, $askCount, $faker); - + $asks = $this->createEvaluationAsks($evaluation, $courseContent, $course, 0, $faker); + // 5) 生成用户提交的表单数据 $formCount = $faker->numberBetween(10, min(40, $allUsers->count())); $submittedUsers = $allUsers->random($formCount); - + foreach ($submittedUsers as $user) { $this->createEvaluationForm($evaluation, $user, $asks, $faker); } - $log(sprintf('评价问卷#%d "%s" 已生成:%d个问题字段,%d份用户提交', + $log(sprintf( + '评价问卷#%d "%s" 已生成:%d个问题字段,%d份用户提交', $evaluation->id, $evaluation->title, - $askCount, + count($asks), $formCount )); } @@ -104,11 +105,11 @@ class CourseContentEvaluationTestDataGenerator $evaluation->title = $this->generateEvaluationTitle($course, $courseContent, $faker); $evaluation->desc = $this->generateEvaluationDesc($faker); $evaluation->type_id = $faker->numberBetween(1, 5); // 问卷类型ID - + // 时间设置:开始时间在课程内容时间前后,截止时间在开始时间之后 $startTime = $faker->dateTimeBetween('-30 days', '+7 days'); $endTime = $faker->dateTimeBetween($startTime, $startTime->format('Y-m-d H:i:s') . ' +30 days'); - + $evaluation->start_time = $startTime->format('Y-m-d H:i:s'); $evaluation->end_time = $endTime->format('Y-m-d H:i:s'); $evaluation->status = $faker->randomElement([0, 1]); // 0未发布, 1已发布 @@ -124,10 +125,95 @@ class CourseContentEvaluationTestDataGenerator { $asks = []; $fieldTemplates = $this->getEvaluationFieldTemplates(); - - for ($i = 0; $i < $count; $i++) { - $template = $faker->randomElement($fieldTemplates); - + + // 为每个课程内容生成4个评价维度的问题(课程必要性、理论丰富程度、实践指导意义、讲授能力) + $evaluationDimensions = [ + '课程必要性', + '理论丰富程度', + '实践指导意义', + '讲授能力' + ]; + + $sort = 1; + + // 添加课程内容标题作为分组 + $ask = new CourseContentEvaluationAsk(); + $ask->admin_id = $faker->numberBetween(1, 10); + $ask->department_id = $faker->numberBetween(1, 5); + $ask->course_id = $course->id; + $ask->course_content_id = $courseContent->id; + $ask->course_content_evaluation_id = $evaluation->id; + $ask->name = $courseContent->theme; + $ask->field = 'course_content_title'; + $ask->edit_input = 'text'; + $ask->rule = ''; + $ask->sort = $sort++; + $ask->help = '课程内容标题'; + $ask->select_item = null; + $ask->need_fill = false; + $ask->belong_user = false; + $ask->allow_input = false; + $ask->save(); + + $asks[] = $ask; + + // 为每个评价维度创建问题 + foreach ($evaluationDimensions as $dimension) { + $ask = new CourseContentEvaluationAsk(); + $ask->admin_id = $faker->numberBetween(1, 10); + $ask->department_id = $faker->numberBetween(1, 5); + $ask->course_id = $course->id; + $ask->course_content_id = $courseContent->id; + $ask->course_content_evaluation_id = $evaluation->id; + $ask->name = $dimension; + $ask->field = strtolower(str_replace(' ', '_', $dimension)); + $ask->edit_input = 'radio'; + $ask->rule = 'required'; + $ask->sort = $sort++; + $ask->help = "请评价该课程的{$dimension}"; + $ask->select_item = ['很不满意', '不满意', '一般', '满意', '很满意']; + $ask->need_fill = true; + $ask->belong_user = false; + $ask->allow_input = false; + $ask->save(); + + $asks[] = $ask; + } + + // 添加建议类问题 + $suggestionTemplates = array_filter($fieldTemplates, function ($template) { + return $template['edit_input'] === 'textarea'; + }); + + foreach ($suggestionTemplates as $template) { + $ask = new CourseContentEvaluationAsk(); + $ask->admin_id = $faker->numberBetween(1, 10); + $ask->department_id = $faker->numberBetween(1, 5); + $ask->course_id = $course->id; + $ask->course_content_id = $courseContent->id; + $ask->course_content_evaluation_id = $evaluation->id; + $ask->name = $template['name']; + $ask->field = $template['field']; + $ask->edit_input = $template['edit_input']; + $ask->rule = $template['rule']; + $ask->sort = $sort++; + $ask->help = $template['help']; + $ask->select_item = $template['select_item']; + $ask->need_fill = $template['need_fill']; + $ask->belong_user = $template['belong_user']; + $ask->allow_input = $template['allow_input']; + $ask->save(); + + $asks[] = $ask; + } + + // 添加姓名字段 + $nameTemplate = array_filter($fieldTemplates, function ($template) { + return $template['field'] === 'student_name'; + }); + + if (!empty($nameTemplate)) { + $template = reset($nameTemplate); $ask = new CourseContentEvaluationAsk(); $ask->admin_id = $faker->numberBetween(1, 10); $ask->department_id = $faker->numberBetween(1, 5); @@ -135,20 +221,20 @@ class CourseContentEvaluationTestDataGenerator $ask->course_content_id = $courseContent->id; $ask->course_content_evaluation_id = $evaluation->id; $ask->name = $template['name']; - $ask->field = $template['field'] . '_' . ($i + 1); + $ask->field = $template['field']; $ask->edit_input = $template['edit_input']; $ask->rule = $template['rule']; - $ask->sort = $i + 1; + $ask->sort = $sort++; $ask->help = $template['help']; $ask->select_item = $template['select_item']; $ask->need_fill = $template['need_fill']; $ask->belong_user = $template['belong_user']; $ask->allow_input = $template['allow_input']; $ask->save(); - + $asks[] = $ask; } - + return $asks; } @@ -158,11 +244,11 @@ class CourseContentEvaluationTestDataGenerator private function createEvaluationForm(CourseContentEvaluation $evaluation, User $user, array $asks, $faker): void { $formData = []; - + foreach ($asks as $ask) { $formData[$ask->field] = $this->generateFieldValue($ask, $faker); } - + $form = new CourseContentEvaluationForm(); $form->course_content_evaluation_id = $evaluation->id; $form->user_id = $user->id; @@ -178,17 +264,20 @@ class CourseContentEvaluationTestDataGenerator private function generateEvaluationTitle(Course $course, CourseContent $courseContent, $faker): string { $templates = [ - '《%s》课程满意度调查', - '%s 教学效果评价', - '%s 学习体验反馈', - '关于 %s 的教学质量评估', - '%s 课程内容评价问卷', - '%s 授课情况调研', - '%s 学员反馈调查' + '首期%s课程满意度调查', + '%s(%s班)第一次课课程满意度调查', + '%s研修班课程满意度调查', + '%s培训课程满意度调查', + '%s课程满意度调查问卷', + '%s课程评价调查', + '%s学习效果评价调查' ]; - - $courseName = $course->title ?? '课程'; - return sprintf($faker->randomElement($templates), $courseName); + + $courseName = $course->name ?? '课程'; + $classNames = ['攀峰班', '精英班', '进阶班', '基础班', '高级班']; + $className = $faker->randomElement($classNames); + + return sprintf($faker->randomElement($templates), $courseName, $className); } /** @@ -197,13 +286,13 @@ class CourseContentEvaluationTestDataGenerator private function generateEvaluationDesc($faker): string { $descriptions = [ - '为了提升教学质量,改进课程内容,请您根据实际学习体验,客观填写本次评价问卷。您的宝贵意见将帮助我们持续优化课程设计。', - '此次评价旨在了解您对本课程的学习感受和建议。问卷采用匿名形式,请放心填写真实想法,感谢您的配合!', - '请根据您的实际学习情况,对本次课程的各个方面进行客观评价。您的反馈对我们改进教学方法具有重要意义。', - '为持续提升课程品质,特设立本次学员满意度调查。请您花费几分钟时间,帮助我们了解课程的优点与不足。', - '感谢您参与本次课程学习!为了给后续学员提供更好的学习体验,恳请您如实填写这份评价问卷。' + '为了提升研修班教学质量,改进课程内容,请您根据实际学习体验,客观填写本次评价问卷。您的宝贵意见将帮助我们持续优化课程设计。', + '此次评价旨在了解您对本研修班课程的学习感受和建议。问卷采用匿名形式,请放心填写真实想法,感谢您的配合!', + '请根据您的实际学习情况,对本次研修班课程的各个方面进行客观评价。您的反馈对我们改进教学方法具有重要意义。', + '为持续提升研修班课程品质,特设立本次学员满意度调查。请您花费几分钟时间,帮助我们了解课程的优点与不足。', + '感谢您参与本次研修班课程学习!为了给后续学员提供更好的学习体验,恳请您如实填写这份评价问卷。' ]; - + return $faker->randomElement($descriptions); } @@ -214,114 +303,92 @@ class CourseContentEvaluationTestDataGenerator { return [ [ - 'name' => '课程内容满意度', - 'field' => 'content_satisfaction', + 'name' => '课程必要性', + 'field' => 'course_necessity', 'edit_input' => 'radio', 'rule' => 'required', - 'help' => '请选择您对课程内容的满意程度', - 'select_item' => ['非常满意', '满意', '一般', '不满意', '非常不满意'], + 'help' => '请评价该课程的必要性', + 'select_item' => ['很不满意', '不满意', '一般', '满意', '很满意'], 'need_fill' => true, 'belong_user' => false, 'allow_input' => false ], [ - 'name' => '授课方式评价', - 'field' => 'teaching_method', + 'name' => '理论丰富程度', + 'field' => 'theory_richness', 'edit_input' => 'radio', 'rule' => 'required', - 'help' => '请评价老师的授课方式', - 'select_item' => ['很好', '好', '一般', '较差', '很差'], + 'help' => '请评价课程理论的丰富程度', + 'select_item' => ['很不满意', '不满意', '一般', '满意', '很满意'], 'need_fill' => true, 'belong_user' => false, 'allow_input' => false ], [ - 'name' => '课程难度评价', - 'field' => 'difficulty_level', + 'name' => '实践指导意义', + 'field' => 'practical_guidance', 'edit_input' => 'radio', 'rule' => 'required', - 'help' => '您认为课程难度如何', - 'select_item' => ['太简单', '偏简单', '适中', '偏难', '太难'], + 'help' => '请评价课程的实践指导意义', + 'select_item' => ['很不满意', '不满意', '一般', '满意', '很满意'], 'need_fill' => true, 'belong_user' => false, 'allow_input' => false ], [ - 'name' => '学习收获评价', - 'field' => 'learning_gain', - 'edit_input' => 'checkbox', - 'rule' => '', - 'help' => '您在本次学习中获得了哪些收获(可多选)', - 'select_item' => ['理论知识', '实践技能', '思维方法', '行业认知', '人际交往', '其他'], - 'need_fill' => false, - 'belong_user' => false, - 'allow_input' => true - ], - [ - 'name' => '课程推荐度', - 'field' => 'recommendation', + 'name' => '讲授能力', + 'field' => 'teaching_ability', 'edit_input' => 'radio', 'rule' => 'required', - 'help' => '您是否愿意向他人推荐此课程', - 'select_item' => ['非常愿意', '愿意', '无所谓', '不愿意', '绝对不会'], + 'help' => '请评价老师的讲授能力', + 'select_item' => ['很不满意', '不满意', '一般', '满意', '很满意'], 'need_fill' => true, 'belong_user' => false, 'allow_input' => false ], [ - 'name' => '整体评分', - 'field' => 'overall_rating', - 'edit_input' => 'select', - 'rule' => 'required', - 'help' => '请为本次课程打分(10分制)', - 'select_item' => ['10分', '9分', '8分', '7分', '6分', '5分', '4分', '3分', '2分', '1分'], - 'need_fill' => true, + 'name' => '您对本次培训的课程及老师的资料、专业性等方面有哪些建议?', + 'field' => 'course_teacher_suggestions', + 'edit_input' => 'textarea', + 'rule' => '', + 'help' => '请提出您对课程和老师的建议', + 'select_item' => null, + 'need_fill' => false, 'belong_user' => false, - 'allow_input' => false + 'allow_input' => true ], [ - 'name' => '意见建议', - 'field' => 'suggestions', + 'name' => '您对培训专题、课程内容、授课师资及形式等方面有哪些建议?', + 'field' => 'training_suggestions', 'edit_input' => 'textarea', 'rule' => '', - 'help' => '请提出您的宝贵意见和建议', + 'help' => '请提出您对培训各方面的建议', 'select_item' => null, 'need_fill' => false, 'belong_user' => false, 'allow_input' => true ], [ - 'name' => '您的姓名', - 'field' => 'student_name', - 'edit_input' => 'text', + 'name' => '其他建议', + 'field' => 'other_suggestions', + 'edit_input' => 'textarea', 'rule' => '', - 'help' => '请填写您的真实姓名(可选)', + 'help' => '请提出其他建议', 'select_item' => null, 'need_fill' => false, - 'belong_user' => true, - 'allow_input' => false + 'belong_user' => false, + 'allow_input' => true ], [ - 'name' => '联系方式', - 'field' => 'contact_info', + 'name' => '如果您愿意,请留下您的姓名(选填)', + 'field' => 'student_name', 'edit_input' => 'text', 'rule' => '', - 'help' => '如需回访,请留下联系方式', + 'help' => '这将帮助我们更好地整理反馈并与您沟通', 'select_item' => null, 'need_fill' => false, 'belong_user' => true, 'allow_input' => false - ], - [ - 'name' => '课堂互动评价', - 'field' => 'interaction_rating', - 'edit_input' => 'radio', - 'rule' => '', - 'help' => '您对课堂互动环节的评价', - 'select_item' => ['很活跃', '较活跃', '一般', '较沉闷', '很沉闷'], - 'need_fill' => false, - 'belong_user' => false, - 'allow_input' => false ] ]; } @@ -335,33 +402,42 @@ class CourseContentEvaluationTestDataGenerator case 'radio': case 'select': return $faker->randomElement($ask->select_item ?? []); - + case 'checkbox': $options = $ask->select_item ?? []; $selected = $faker->randomElements($options, $faker->numberBetween(1, min(3, count($options)))); return implode(',', $selected); - + case 'textarea': $suggestions = [ - '希望增加更多实践环节', - '课程进度可以适当放慢', - '案例分析很有帮助,建议增加', - '老师讲解很清晰,受益良多', - '课程资料很丰富,感谢分享', - '希望提供更多课后练习', - '建议增加小组讨论时间', - '整体非常满意,期待后续课程' + '希望增加更多实践环节和案例分析', + '课程进度可以适当放慢,便于消化吸收', + '案例分析很有帮助,建议增加更多实际案例', + '老师讲解很清晰,理论联系实际,受益良多', + '课程资料很丰富,感谢分享,希望提供更多参考资料', + '希望提供更多课后练习和讨论机会', + '建议增加小组讨论时间和互动环节', + '整体非常满意,期待后续课程,希望继续深入学习', + '课程内容很实用,对工作有很大帮助', + '老师专业水平很高,讲解深入浅出', + '希望增加更多行业前沿信息分享', + '课程安排合理,时间控制得当', + '建议增加更多实战演练环节', + '课程质量很高,值得推荐给同事', + '希望提供更多学习资源和交流平台' ]; return $faker->optional(0.7)->randomElement($suggestions) ?: ''; - + case 'text': if ($ask->field === 'student_name') { return $faker->optional(0.4)->name ?: ''; + } elseif ($ask->field === 'course_content_title') { + return $ask->name; // 返回课程内容标题 } elseif ($ask->field === 'contact_info') { return $faker->optional(0.3)->phoneNumber ?: ''; } return $faker->optional(0.5)->words(3, true) ?: ''; - + default: return ''; } @@ -373,18 +449,62 @@ class CourseContentEvaluationTestDataGenerator private function createSampleCoursesAndContents($faker): void { $courseData = [ - ['title' => 'Python 程序设计基础', 'contents' => ['Python 语言概述', '数据类型与变量', '控制结构', '函数与模块', '面向对象编程']], - ['title' => '数据库原理与应用', 'contents' => ['数据库基础概念', 'SQL 语言基础', '数据库设计', '事务处理', '性能优化']], - ['title' => '项目管理实务', 'contents' => ['项目管理概述', '项目计划制定', '风险管理', '团队管理', '项目收尾']], - ['title' => '市场营销学', 'contents' => ['市场营销概论', '消费者行为分析', '产品策略', '价格策略', '推广策略']], - ['title' => 'Web 前端开发', 'contents' => ['HTML 基础', 'CSS 样式设计', 'JavaScript 编程', 'Vue.js 框架', '项目实战']] + [ + 'title' => '苏州市科技企业资本运作研修班', + 'contents' => [ + '《资本市场深化改革背景下,私募股权基金的专业化发展路径与企业家资本战略选择》——王建平老师', + '《科技创新产业现状与未来趋势(深圳创新实践案例)》——梁永生老师', + '《IPO流程与估值——沙盘演练》——郭圣宇老师', + '《IPO流程与估值——沙盘演练》——张剑波老师', + '《IPO企业常见涉税问题——专题授课》——肖鑫老师', + '《IPO企业财务管理——沙盘演练》——王晓苗老师' + ] + ], + [ + 'title' => '企业数字化转型研修班', + 'contents' => [ + '《数字化转型战略规划》——李明老师', + '《人工智能在企业中的应用》——张华老师', + '《大数据分析与决策支持》——王强老师', + '《云计算与云原生架构》——刘伟老师' + ] + ], + [ + 'title' => '创新创业管理研修班', + 'contents' => [ + '《创业机会识别与商业模式设计》——陈刚老师', + '《创业团队建设与管理》——赵敏老师', + '《融资策略与风险投资》——孙涛老师', + '《知识产权保护与运用》——周琳老师' + ] + ], + [ + 'title' => '供应链管理研修班', + 'contents' => [ + '《供应链战略规划》——吴斌老师', + '《采购管理与供应商关系》——郑红老师', + '《物流配送优化》——马超老师', + '《供应链风险管理》——徐静老师' + ] + ], + [ + 'title' => '人力资源管理研修班', + 'contents' => [ + '《人力资源战略规划》——黄磊老师', + '《招聘与人才发展》——韩雪老师', + '《绩效管理与激励机制》——杨帆老师', + '《企业文化与组织发展》——林峰老师' + ] + ] ]; foreach ($courseData as $data) { $course = Course::create([ - 'title' => $data['title'], - 'description' => $data['title'] . '课程', + 'name' => $data['title'], + 'content' => $data['title'] . '课程', 'status' => 1, + 'course_status' => 10, // 进行中 + 'sign_status' => 10, // 进行中 'created_at' => now(), 'updated_at' => now() ]); @@ -392,10 +512,8 @@ class CourseContentEvaluationTestDataGenerator foreach ($data['contents'] as $index => $contentTitle) { CourseContent::create([ 'course_id' => $course->id, - 'title' => $contentTitle, - 'description' => $contentTitle . '相关内容', - 'sort' => $index + 1, - 'status' => 1, + 'theme' => $contentTitle, + 'period' => '第' . ($index + 1) . '期', 'created_at' => now(), 'updated_at' => now() ]); From 6408f2e6314e56fe910e4c511541ac7395765e99 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Mon, 25 Aug 2025 11:16:50 +0800 Subject: [PATCH 19/19] update --- ...urseContentEvaluationTestDataGenerator.php | 65 ++++++++----------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/app/Services/TestData/CourseContentEvaluationTestDataGenerator.php b/app/Services/TestData/CourseContentEvaluationTestDataGenerator.php index d911d0f..ddcfe07 100644 --- a/app/Services/TestData/CourseContentEvaluationTestDataGenerator.php +++ b/app/Services/TestData/CourseContentEvaluationTestDataGenerator.php @@ -60,34 +60,46 @@ class CourseContentEvaluationTestDataGenerator } // 3) 生成评价问卷主数据 - $selectedCourseContents = $allCourseContents->random(min($evaluationCount, $allCourseContents->count())); + // 按课程分组,每个课程生成一个问卷,包含该课程的所有内容 + $coursesWithContents = $courses->map(function ($course) use ($allCourseContents) { + $courseContents = $allCourseContents->where('course_id', $course->id); + return [ + 'course' => $course, + 'contents' => $courseContents + ]; + })->filter(function ($item) { + return $item['contents']->count() > 0; + }); - foreach ($selectedCourseContents as $courseContent) { - $course = $courses->where('id', $courseContent->course_id)->first(); + $selectedCourses = $coursesWithContents->random(min($evaluationCount, $coursesWithContents->count())); - if (!$course) { - // 如果找不到课程,跳过这个课程内容 - continue; - } + foreach ($selectedCourses as $courseData) { + $course = $courseData['course']; + $courseContents = $courseData['contents']; - $evaluation = $this->createEvaluation($courseContent, $course, $faker); + // 为每个课程创建一个评价问卷 + $evaluation = $this->createEvaluation($courseContents->first(), $course, $faker); - // 4) 为每个问卷生成问题字段 - $asks = $this->createEvaluationAsks($evaluation, $courseContent, $course, 0, $faker); + // 4) 为每个问卷生成问题字段(包含该课程的所有内容) + $allAsks = []; + foreach ($courseContents as $courseContent) { + $asks = $this->createEvaluationAsks($evaluation, $courseContent, $course, 0, $faker); + $allAsks = array_merge($allAsks, $asks); + } // 5) 生成用户提交的表单数据 $formCount = $faker->numberBetween(10, min(40, $allUsers->count())); $submittedUsers = $allUsers->random($formCount); foreach ($submittedUsers as $user) { - $this->createEvaluationForm($evaluation, $user, $asks, $faker); + $this->createEvaluationForm($evaluation, $user, $allAsks, $faker); } $log(sprintf( '评价问卷#%d "%s" 已生成:%d个问题字段,%d份用户提交', $evaluation->id, $evaluation->title, - count($asks), + count($allAsks), $formCount )); } @@ -136,28 +148,7 @@ class CourseContentEvaluationTestDataGenerator $sort = 1; - // 添加课程内容标题作为分组 - $ask = new CourseContentEvaluationAsk(); - $ask->admin_id = $faker->numberBetween(1, 10); - $ask->department_id = $faker->numberBetween(1, 5); - $ask->course_id = $course->id; - $ask->course_content_id = $courseContent->id; - $ask->course_content_evaluation_id = $evaluation->id; - $ask->name = $courseContent->theme; - $ask->field = 'course_content_title'; - $ask->edit_input = 'text'; - $ask->rule = ''; - $ask->sort = $sort++; - $ask->help = '课程内容标题'; - $ask->select_item = null; - $ask->need_fill = false; - $ask->belong_user = false; - $ask->allow_input = false; - $ask->save(); - - $asks[] = $ask; - - // 为每个评价维度创建问题 + // 为每个评价维度创建问题,每个维度都包含课程内容标题 foreach ($evaluationDimensions as $dimension) { $ask = new CourseContentEvaluationAsk(); $ask->admin_id = $faker->numberBetween(1, 10); @@ -165,12 +156,12 @@ class CourseContentEvaluationTestDataGenerator $ask->course_id = $course->id; $ask->course_content_id = $courseContent->id; $ask->course_content_evaluation_id = $evaluation->id; - $ask->name = $dimension; - $ask->field = strtolower(str_replace(' ', '_', $dimension)); + $ask->name = $courseContent->theme . '——' . $dimension; + $ask->field = strtolower(str_replace(' ', '_', $dimension)) . '_' . $courseContent->id; $ask->edit_input = 'radio'; $ask->rule = 'required'; $ask->sort = $sort++; - $ask->help = "请评价该课程的{$dimension}"; + $ask->help = "请评价《{$courseContent->theme}》的{$dimension}"; $ask->select_item = ['很不满意', '不满意', '一般', '满意', '很满意']; $ask->need_fill = true; $ask->belong_user = false;