|
|
|
@ -0,0 +1,405 @@
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Services\TestData;
|
|
|
|
|
|
|
|
|
|
use App\Models\Course;
|
|
|
|
|
use App\Models\CourseContent;
|
|
|
|
|
use App\Models\CourseContentEvaluation;
|
|
|
|
|
use App\Models\CourseContentEvaluationAsk;
|
|
|
|
|
use App\Models\CourseContentEvaluationForm;
|
|
|
|
|
use App\Models\User;
|
|
|
|
|
use Faker\Factory as FakerFactory;
|
|
|
|
|
use Illuminate\Support\Carbon;
|
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 课程内容评价模块测试数据生成器
|
|
|
|
|
*/
|
|
|
|
|
class CourseContentEvaluationTestDataGenerator
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* 生成课程内容评价相关的全量测试数据
|
|
|
|
|
*
|
|
|
|
|
* @param int $evaluationCount 生成评价问卷数量
|
|
|
|
|
* @param int $minUsers 最少用户数量,不足会自动补充
|
|
|
|
|
* @param callable|null $logger 可选日志输出函数 function(string $message): void
|
|
|
|
|
*/
|
|
|
|
|
public function generate(int $evaluationCount = 20, int $minUsers = 50, ?callable $logger = null): void
|
|
|
|
|
{
|
|
|
|
|
$log = $logger ?? static function (string $message): void {
|
|
|
|
|
// no-op
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$faker = FakerFactory::create('zh_CN');
|
|
|
|
|
|
|
|
|
|
DB::transaction(function () use ($evaluationCount, $minUsers, $faker, $log) {
|
|
|
|
|
// 1) 确保用户数量
|
|
|
|
|
$currentUserCount = User::count();
|
|
|
|
|
if ($currentUserCount < $minUsers) {
|
|
|
|
|
$need = $minUsers - $currentUserCount;
|
|
|
|
|
$log("创建用户: {$need} 个");
|
|
|
|
|
User::factory($need)->create();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$allUsers = User::all();
|
|
|
|
|
if ($allUsers->count() < 5) {
|
|
|
|
|
throw new \RuntimeException('生成评价数据至少需要5个用户');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2) 确保课程和课程内容存在
|
|
|
|
|
$courses = Course::with('courseContents')->get();
|
|
|
|
|
if ($courses->isEmpty()) {
|
|
|
|
|
$log("创建示例课程数据");
|
|
|
|
|
$this->createSampleCoursesAndContents($faker);
|
|
|
|
|
$courses = Course::with('courseContents')->get();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$allCourseContents = CourseContent::all();
|
|
|
|
|
if ($allCourseContents->isEmpty()) {
|
|
|
|
|
throw new \RuntimeException('无课程内容数据,无法生成评价');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3) 生成评价问卷主数据
|
|
|
|
|
for ($i = 0; $i < $evaluationCount; $i++) {
|
|
|
|
|
$courseContent = $allCourseContents->random();
|
|
|
|
|
$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);
|
|
|
|
|
|
|
|
|
|
// 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份用户提交',
|
|
|
|
|
$evaluation->id,
|
|
|
|
|
$evaluation->title,
|
|
|
|
|
$askCount,
|
|
|
|
|
$formCount
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建评价问卷主数据
|
|
|
|
|
*/
|
|
|
|
|
private function createEvaluation(CourseContent $courseContent, Course $course, $faker): CourseContentEvaluation
|
|
|
|
|
{
|
|
|
|
|
$evaluation = new CourseContentEvaluation();
|
|
|
|
|
$evaluation->course_id = $course->id;
|
|
|
|
|
$evaluation->course_content_id = $courseContent->id;
|
|
|
|
|
$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已发布
|
|
|
|
|
$evaluation->save();
|
|
|
|
|
|
|
|
|
|
return $evaluation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建评价问题字段
|
|
|
|
|
*/
|
|
|
|
|
private function createEvaluationAsks(CourseContentEvaluation $evaluation, CourseContent $courseContent, Course $course, int $count, $faker): array
|
|
|
|
|
{
|
|
|
|
|
$asks = [];
|
|
|
|
|
$fieldTemplates = $this->getEvaluationFieldTemplates();
|
|
|
|
|
|
|
|
|
|
for ($i = 0; $i < $count; $i++) {
|
|
|
|
|
$template = $faker->randomElement($fieldTemplates);
|
|
|
|
|
|
|
|
|
|
$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'] . '_' . ($i + 1);
|
|
|
|
|
$ask->edit_input = $template['edit_input'];
|
|
|
|
|
$ask->rule = $template['rule'];
|
|
|
|
|
$ask->sort = $i + 1;
|
|
|
|
|
$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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建用户提交的评价表单
|
|
|
|
|
*/
|
|
|
|
|
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;
|
|
|
|
|
$form->time_total = $faker->numberBetween(180, 1800); // 3分钟到30分钟
|
|
|
|
|
$form->data = $formData;
|
|
|
|
|
$form->created_at = $faker->dateTimeBetween($evaluation->start_time, $evaluation->end_time ?: 'now');
|
|
|
|
|
$form->save();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成评价标题
|
|
|
|
|
*/
|
|
|
|
|
private function generateEvaluationTitle(Course $course, CourseContent $courseContent, $faker): string
|
|
|
|
|
{
|
|
|
|
|
$templates = [
|
|
|
|
|
'《%s》课程满意度调查',
|
|
|
|
|
'%s 教学效果评价',
|
|
|
|
|
'%s 学习体验反馈',
|
|
|
|
|
'关于 %s 的教学质量评估',
|
|
|
|
|
'%s 课程内容评价问卷',
|
|
|
|
|
'%s 授课情况调研',
|
|
|
|
|
'%s 学员反馈调查'
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$courseName = $course->title ?? '课程';
|
|
|
|
|
return sprintf($faker->randomElement($templates), $courseName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成评价描述
|
|
|
|
|
*/
|
|
|
|
|
private function generateEvaluationDesc($faker): string
|
|
|
|
|
{
|
|
|
|
|
$descriptions = [
|
|
|
|
|
'为了提升教学质量,改进课程内容,请您根据实际学习体验,客观填写本次评价问卷。您的宝贵意见将帮助我们持续优化课程设计。',
|
|
|
|
|
'此次评价旨在了解您对本课程的学习感受和建议。问卷采用匿名形式,请放心填写真实想法,感谢您的配合!',
|
|
|
|
|
'请根据您的实际学习情况,对本次课程的各个方面进行客观评价。您的反馈对我们改进教学方法具有重要意义。',
|
|
|
|
|
'为持续提升课程品质,特设立本次学员满意度调查。请您花费几分钟时间,帮助我们了解课程的优点与不足。',
|
|
|
|
|
'感谢您参与本次课程学习!为了给后续学员提供更好的学习体验,恳请您如实填写这份评价问卷。'
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return $faker->randomElement($descriptions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取评价字段模板
|
|
|
|
|
*/
|
|
|
|
|
private function getEvaluationFieldTemplates(): array
|
|
|
|
|
{
|
|
|
|
|
return [
|
|
|
|
|
[
|
|
|
|
|
'name' => '课程内容满意度',
|
|
|
|
|
'field' => 'content_satisfaction',
|
|
|
|
|
'edit_input' => 'radio',
|
|
|
|
|
'rule' => 'required',
|
|
|
|
|
'help' => '请选择您对课程内容的满意程度',
|
|
|
|
|
'select_item' => ['非常满意', '满意', '一般', '不满意', '非常不满意'],
|
|
|
|
|
'need_fill' => true,
|
|
|
|
|
'belong_user' => false,
|
|
|
|
|
'allow_input' => false
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'name' => '授课方式评价',
|
|
|
|
|
'field' => 'teaching_method',
|
|
|
|
|
'edit_input' => 'radio',
|
|
|
|
|
'rule' => 'required',
|
|
|
|
|
'help' => '请评价老师的授课方式',
|
|
|
|
|
'select_item' => ['很好', '好', '一般', '较差', '很差'],
|
|
|
|
|
'need_fill' => true,
|
|
|
|
|
'belong_user' => false,
|
|
|
|
|
'allow_input' => false
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'name' => '课程难度评价',
|
|
|
|
|
'field' => 'difficulty_level',
|
|
|
|
|
'edit_input' => 'radio',
|
|
|
|
|
'rule' => 'required',
|
|
|
|
|
'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',
|
|
|
|
|
'edit_input' => 'radio',
|
|
|
|
|
'rule' => 'required',
|
|
|
|
|
'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,
|
|
|
|
|
'belong_user' => false,
|
|
|
|
|
'allow_input' => false
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'name' => '意见建议',
|
|
|
|
|
'field' => 'suggestions',
|
|
|
|
|
'edit_input' => 'textarea',
|
|
|
|
|
'rule' => '',
|
|
|
|
|
'help' => '请提出您的宝贵意见和建议',
|
|
|
|
|
'select_item' => null,
|
|
|
|
|
'need_fill' => false,
|
|
|
|
|
'belong_user' => false,
|
|
|
|
|
'allow_input' => true
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'name' => '您的姓名',
|
|
|
|
|
'field' => 'student_name',
|
|
|
|
|
'edit_input' => 'text',
|
|
|
|
|
'rule' => '',
|
|
|
|
|
'help' => '请填写您的真实姓名(可选)',
|
|
|
|
|
'select_item' => null,
|
|
|
|
|
'need_fill' => false,
|
|
|
|
|
'belong_user' => true,
|
|
|
|
|
'allow_input' => false
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'name' => '联系方式',
|
|
|
|
|
'field' => 'contact_info',
|
|
|
|
|
'edit_input' => 'text',
|
|
|
|
|
'rule' => '',
|
|
|
|
|
'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
|
|
|
|
|
]
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据字段类型生成对应的值
|
|
|
|
|
*/
|
|
|
|
|
private function generateFieldValue(CourseContentEvaluationAsk $ask, $faker)
|
|
|
|
|
{
|
|
|
|
|
switch ($ask->edit_input) {
|
|
|
|
|
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 === 'contact_info') {
|
|
|
|
|
return $faker->optional(0.3)->phoneNumber ?: '';
|
|
|
|
|
}
|
|
|
|
|
return $faker->optional(0.5)->words(3, true) ?: '';
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建示例课程和课程内容数据(如果不存在)
|
|
|
|
|
*/
|
|
|
|
|
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 框架', '项目实战']]
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
foreach ($courseData as $data) {
|
|
|
|
|
$course = Course::create([
|
|
|
|
|
'title' => $data['title'],
|
|
|
|
|
'description' => $data['title'] . '课程',
|
|
|
|
|
'status' => 1,
|
|
|
|
|
'created_at' => now(),
|
|
|
|
|
'updated_at' => now()
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
foreach ($data['contents'] as $index => $contentTitle) {
|
|
|
|
|
CourseContent::create([
|
|
|
|
|
'course_id' => $course->id,
|
|
|
|
|
'title' => $contentTitle,
|
|
|
|
|
'description' => $contentTitle . '相关内容',
|
|
|
|
|
'sort' => $index + 1,
|
|
|
|
|
'status' => 1,
|
|
|
|
|
'created_at' => now(),
|
|
|
|
|
'updated_at' => now()
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|