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

303 lines
11 KiB

4 months ago
<?php
namespace App\Console\Commands;
use App\Models\Course;
use App\Models\Calendar;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class LinkCoursesToCalendar extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'link:courses-to-calendar';
/**
* The console command description.
*
* @var string
*/
protected $description = '将指定的课程列表关联到calendars日历表';
/**
* 课程列表
*
* @var array
*/
protected $courseList = [
'第三期:张平院士— 6G通信与AI融合',
'高研班|第四期高级科创人才研修班-第七模块',
'高研班|第五期高级科创人才研修班-第五模块',
'校友返校日AI+产业融合创新论坛',
'高研班|第六期高级科创人才研修班-第三模块',
'第二课堂|走进珂玛科技',
'人才培训|省科技厅高级技术经理人专班-开学模块',
'初创班|首期技术经理人领航班-第一模块',
'专题培训|苏州市科技企业资本运作公开课',
'初创班|首期高校科技成果转化班-第一模块',
'高研班|第五期高级科创人才研修班-毕业模块',
'高研班|第四期高级科创人才研修班-第八模块',
'第二课堂|走进世华科技',
'初创班|首期高校科技成果转化班-第二模块',
'高研班|第六期高级科创人才研修班-第四模块',
'人才培训|省科技厅高级技术经理人专班-实践模块',
'初创班|首期技术经理人领航班-第二模块',
'攀峰班|首期苏州科技企业资本运作研修班-开学模块',
'初创班|首期技术经理人领航班-结业模块',
'高研班|第七期高级科创人才研修班-开学模块',
'产业加速营|具身智能极客营-开学模块',
'产业加速营|苏州市人工智能潜在独角兽训练营-开学模块',
'初创班|首期高校科技成果转化班-结业模块',
'第二课堂|走进姑苏区',
'高研班|第六期高级科创人才研修班-第五模块',
'产业加速营|苏州市人工智能潜在独角兽训练营-第二模块',
'夏令营2025年度小科学家夏令营',
'攀峰班|首期苏州科技企业资本运作研修班-第二模块',
'产业加速营|苏州市人工智能潜在独角兽训练营-第三模块',
'人才培训|江苏青年科技人才"U35青创学院"培训',
'高研班|第七期高级科创人才研修班-第二模块',
'第二课堂|走进科沃斯',
'高研班|第四期高级科创人才研修班-开学模块',
'高研班|第三期高级科创人才研修班-结业模块',
'第二课堂|走进永鼎',
'第二课堂|走进旭创',
'高研班|第五期高级科创人才研修班-开学模块',
'高研班|第四期高级科创人才研修班-第二模块',
'第二课堂|走进亨通',
'第二课堂|走进企查查',
'科技大讲堂|第一期: 凯文凯利、丁文江院士领衔',
'高研班|第四期高级科创人才研修班-第三模块',
'第二课堂|走进华为苏研所',
'人才培训2024年姑苏领军人才培育营',
'专题培训|苏州市标杆孵化器培训',
'高研班|第五期高级科创人才研修班-第一模块',
'第二课堂|走进亚盛医药',
'第二课堂|新加坡海外游学',
'第二课堂|走进苏州市市场监督管理局',
'第二课堂|走进信达生物',
'人才培训|江苏省高层次人才专题培训',
'夏令营2024年度小科学家夏令营',
'高研班|第五期高级科创人才研修班-第二模块',
'高研班|第四期高级科创人才研修班-第四模块',
'专题培训|太仓市国资审计高质量发展专题培训',
'第二课堂|走进天准科技',
'高研班|第五期高级科创人才研修班-第三模块',
'科技金融沙龙|上海交通大学新能源沙龙',
'科技大讲堂|第二期:伊雷娜·克罗宁解析空间计算',
'人才培训|苏州市科技企业孵化器沙龙',
'高研班|第四期高级科创人才研修班-第五模块',
'人才培训|苏州乡镇党委书记专题研修班',
'科技金融沙龙|资本市场新机遇研讨沙龙',
'产业加速营|人工智能产业加速营',
'高研班|第六期高级科创人才研修班-开学模块',
'第二课堂|走进苏州市低空经济发展展示馆',
'高研班|第四期高级科创人才研修班-第六模块',
'人才培训2024姑苏领军人才创业营',
'产业加速营|集成电路产业专班',
'专题培训关税与出海应对2025美国新政',
'高研班|第五期高级科创人才研修班-第四模块',
'高研班|第六期高级科创人才研修班-第二模块',
];
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->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);
}
}