master
cody 3 months ago
parent d42da8724f
commit 14237668e0

@ -0,0 +1,302 @@
<?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);
}
}

@ -0,0 +1,313 @@
<?php
namespace App\Console\Commands;
use App\Models\Course;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
use PhpOffice\PhpSpreadsheet\IOFactory;
class UpdateCourseUrls extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'update:course-urls {file=课程台账.xlsx}';
/**
* The console command description.
*
* @var string
*/
protected $description = '从Excel文件读取课程信息匹配phome_ecms_news表的titleurl并更新courses表的url字段';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$fileName = $this->argument('file');
$filePath = base_path($fileName);
if (!file_exists($filePath)) {
$this->error("文件不存在: {$filePath}");
return;
}
$this->info("开始处理文件: {$fileName}");
try {
// 读取Excel文件
$spreadsheet = IOFactory::load($filePath);
$sheetCount = $spreadsheet->getSheetCount();
$this->info("Excel文件包含 {$sheetCount} 个工作表");
$totalUpdated = 0;
// 处理每个工作表
for ($sheetIndex = 0; $sheetIndex < $sheetCount; $sheetIndex++) {
$worksheet = $spreadsheet->getSheet($sheetIndex);
$sheetName = $worksheet->getTitle();
$this->info("正在处理工作表: {$sheetName}");
$updated = $this->processWorksheet($worksheet, $sheetName);
$totalUpdated += $updated;
}
$this->info("处理完成,总共更新了 {$totalUpdated} 条记录");
} catch (\Exception $e) {
$this->error("处理Excel文件时发生错误: " . $e->getMessage());
return;
}
}
/**
* 处理单个工作表
*/
private function processWorksheet($worksheet, $sheetName)
{
$highestRow = $worksheet->getHighestRow();
$highestColumn = $worksheet->getHighestColumn();
$this->info("工作表 {$sheetName} 有 {$highestRow} 行,最高列为 {$highestColumn}");
// 读取第一行作为表头
$headers = [];
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn);
for ($col = 1; $col <= $highestColumnIndex; $col++) {
$cellValue = $worksheet->getCellByColumnAndRow($col, 1)->getCalculatedValue();
$headers[$col] = trim($cellValue);
}
$this->info("表头: " . implode(', ', $headers));
// 找到"课程"和"跳转链接"列的位置
$courseColumn = null;
$linkColumn = null;
foreach ($headers as $colIndex => $header) {
if (strpos($header, '课程') !== false) {
$courseColumn = $colIndex;
}
if (strpos($header, '跳转链接') !== false) {
$linkColumn = $colIndex;
}
}
if (!$courseColumn || !$linkColumn) {
$this->warn("工作表 {$sheetName} 中未找到'课程'或'跳转链接'列");
return 0;
}
$this->info("找到课程列: {$courseColumn},跳转链接列: {$linkColumn}");
$updated = 0;
$failedCourses = [];
// 处理数据行
for ($row = 2; $row <= $highestRow; $row++) {
$courseName = trim($worksheet->getCellByColumnAndRow($courseColumn, $row)->getCalculatedValue());
$jumpLink = trim($worksheet->getCellByColumnAndRow($linkColumn, $row)->getCalculatedValue());
if (empty($courseName) || empty($jumpLink)) {
continue;
}
$this->info("处理行 {$row}: 课程='{$courseName}', 跳转链接='{$jumpLink}'");
// 从phome_ecms_news表获取titleurl
$titleUrl = $this->getTitleUrlFromNews($jumpLink);
if ($titleUrl) {
// 更新courses表
$updateCount = $this->updateCourseUrl($courseName, $titleUrl);
$updated += $updateCount;
if ($updateCount > 0) {
$this->info("✓ 成功更新课程 '{$courseName}' 的URL为: {$titleUrl}");
} else {
$this->warn("✗ 未找到匹配的课程: '{$courseName}'");
$failedCourses[] = $courseName;
}
} else {
$this->warn("✗ 未找到匹配的新闻标题: '{$jumpLink}'");
}
}
// 显示匹配失败的课程
if (!empty($failedCourses)) {
$this->warn("工作表 {$sheetName} 中匹配失败的课程:");
foreach ($failedCourses as $failedCourse) {
$this->warn(" - {$failedCourse}");
}
}
return $updated;
}
/**
* 从phome_ecms_news表获取titleurl
*/
private function getTitleUrlFromNews($title)
{
try {
// 直接匹配
$news = DB::table('phome_ecms_news')
->where('title', $title)
->first();
if ($news && !empty($news->titleurl)) {
return $news->titleurl;
}
// 模糊匹配
$news = DB::table('phome_ecms_news')
->where('title', 'like', "%{$title}%")
->first();
if ($news && !empty($news->titleurl)) {
$this->info("通过模糊匹配找到: '{$news->title}' -> '{$news->titleurl}'");
return $news->titleurl;
}
// 使用相似度匹配
$allNews = DB::table('phome_ecms_news')
->whereNotNull('title')
->whereNotNull('titleurl')
->where('title', '!=', '')
->where('titleurl', '!=', '')
->get();
$bestMatch = null;
$highestSimilarity = 0;
foreach ($allNews as $news) {
$similarity = $this->calculateSimilarity($title, $news->title);
if ($similarity > $highestSimilarity) {
$highestSimilarity = $similarity;
$bestMatch = $news;
}
}
if ($bestMatch && $highestSimilarity > 0) {
$this->info("通过相似度匹配找到 (相似度: " . round($highestSimilarity * 100, 2) . "%): '{$bestMatch->title}' -> '{$bestMatch->titleurl}'");
return $bestMatch->titleurl;
}
} catch (\Exception $e) {
$this->error("查询phome_ecms_news表时发生错误: " . $e->getMessage());
}
return null;
}
/**
* 更新courses表的url字段
*/
private function updateCourseUrl($courseName, $titleUrl)
{
try {
// 直接匹配
$updateCount = Course::where('name', $courseName)
->whereNull('deleted_at')
->update(['url' => $titleUrl]);
if ($updateCount > 0) {
return $updateCount;
}
// 模糊匹配
$updateCount = Course::where('name', 'like', "%{$courseName}%")
->whereNull('deleted_at')
->update(['url' => $titleUrl]);
if ($updateCount > 0) {
$this->info("通过模糊匹配更新了课程");
return $updateCount;
}
// 使用相似度匹配
$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) {
$bestMatch->url = $titleUrl;
$bestMatch->save();
$this->info("通过相似度匹配更新了课程 (相似度: " . round($highestSimilarity * 100, 2) . "%): '{$bestMatch->name}'");
return 1;
}
} catch (\Exception $e) {
$this->error("更新courses表时发生错误: " . $e->getMessage());
}
return 0;
}
/**
* 计算字符串相似度
*/
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);
}
}

@ -175,7 +175,7 @@ class UserController extends CommonController
$all['letter'] = strtoupper(Pinyin::abbr(mb_substr($all['name'], 0, 1))[0]);
}
// 如果有公司信息,就更新一下公司
if (!empty($all['company_name'] && $model->company_name != $all['company_name'])) {
if (isset($all['company_name']) && !empty($all['company_name']) && $model->company_name != $all['company_name']) {
// 调用命令行更新
Artisan::call("update_company --user_id={$model->id}");
}

@ -19,6 +19,7 @@
"overtrue/pinyin": "^5.0",
"overtrue/wechat": "~5.0",
"owen-it/laravel-auditing": "^13.6",
"phpoffice/phpspreadsheet": "^1.29",
"prettus/l5-repository": "^2.9",
"rap2hpoutre/fast-excel": "^5.2",
"simplesoftwareio/simple-qrcode": "^4.2",

@ -0,0 +1,104 @@
# 课程URL更新命令使用说明
## 功能描述
这个Laravel命令 `UpdateCourseUrls` 用于从Excel文件读取课程信息并自动更新数据库中课程的URL字段。
## 主要功能
1. **Excel文件解析**: 读取Excel文件中的课程数据和跳转链接信息
2. **数据匹配**: 从`phome_ecms_news`表中根据跳转链接匹配title字段获取对应的titleurl
3. **智能匹配**: 支持精确匹配、模糊匹配和相似度匹配三种方式
4. **批量更新**: 将获取到的titleurl批量更新到`courses`表的url字段
## 命令使用方法
### 基本用法
```bash
php artisan update:course-urls
```
### 指定Excel文件
```bash
php artisan update:course-urls "your_excel_file.xlsx"
```
## Excel文件格式要求
Excel文件需要包含以下列
- **课程**: 包含课程名称的列
- **跳转链接**: 包含需要匹配的标题的列
文件可以包含多个工作表,命令会自动处理所有工作表。
## 匹配逻辑
### 1. 新闻标题匹配从phome_ecms_news表
- **精确匹配**: 直接匹配title字段
- **模糊匹配**: 使用LIKE进行部分匹配
- **相似度匹配**: 使用Levenshtein算法计算字符串相似度自动选择相似度最高的记录
### 2. 课程名称匹配更新courses表
- **精确匹配**: 直接匹配name字段
- **模糊匹配**: 使用LIKE进行部分匹配
- **相似度匹配**: 使用Levenshtein算法计算字符串相似度自动选择相似度最高的记录
## 特殊处理
1. **软删除过滤**: 只处理未被软删除的课程记录
2. **空值检查**: 自动跳过空的课程名称或跳转链接
3. **重复更新**: 如果课程已有URL会被新的URL覆盖
4. **详细日志**: 提供详细的处理日志,包括匹配方式和相似度信息
5. **失败统计**: 自动显示匹配失败的课程名称列表
## 相似度算法说明
相似度计算使用多种策略:
- **Levenshtein距离**: 计算字符编辑距离
- **包含检查**: 如果一个字符串包含另一个,会提高相似度
- **预处理**: 移除空格并转换为小写进行比较
## 示例输出
```
开始处理文件: 课程台账.xlsx
Excel文件包含 2 个工作表
正在处理工作表: Sheet1
工作表 Sheet1 有 10 行,最高列为 C
表头: 序号, 课程, 跳转链接
找到课程列: 2跳转链接列: 3
处理行 2: 课程='Python基础课程', 跳转链接='Python编程入门教程'
通过相似度匹配找到 (相似度: 85.7%): 'Python编程入门教程完整版' -> '/news/python-tutorial-2024'
✓ 成功更新课程 'Python基础课程' 的URL为: /news/python-tutorial-2024
工作表 Sheet1 中匹配失败的课程:
- 高级数据分析课程
- 机器学习实战
处理完成,总共更新了 6 条记录
```
## 注意事项
1. 确保Excel文件位于项目根目录或提供正确的文件路径
2. 确保数据库连接正常,特别是`phome_ecms_news`表的访问权限
3. 建议在执行前备份`courses`表的数据
4. 相似度匹配会自动选择最高相似度的记录,无需设置阈值
5. 命令会显示所有匹配失败的课程名称,便于后续手动处理
## 错误处理
命令包含完善的错误处理机制:
- 文件不存在检查
- 数据库连接错误处理
- Excel格式错误处理
- 详细的错误日志输出
## 数据库表结构要求
### phome_ecms_news表
- `title`: 新闻标题字段
- `titleurl`: 新闻URL字段
### courses表
- `name`: 课程名称字段
- `url`: 课程URL字段会被更新
- `deleted_at`: 软删除时间戳字段

@ -0,0 +1,143 @@
# 课程关联日历命令使用说明
## 功能描述
这个Laravel命令 `LinkCoursesToCalendar` 用于将指定的课程列表自动关联到`calendars`日历表中。
## 主要功能
1. **批量课程关联**: 一次性处理69个指定的课程
2. **智能匹配**: 支持精确匹配、模糊匹配和相似度匹配三种策略
3. **重复检查**: 自动检查并跳过已存在日历记录的课程
4. **事务保护**: 使用数据库事务确保数据一致性
5. **详细统计**: 提供完整的处理结果统计
## 命令使用方法
### 基本用法
```bash
php artisan link:courses-to-calendar
```
## 课程列表
命令会处理以下69个课程
### 高研班系列
- 高研班|第四期高级科创人才研修班-第七模块
- 高研班|第五期高级科创人才研修班-第五模块
- 高研班|第六期高级科创人才研修班-第三模块
- 高研班|第五期高级科创人才研修班-毕业模块
- 高研班|第四期高级科创人才研修班-第八模块
- ... (共29个高研班课程)
### 第二课堂系列
- 第二课堂|走进珂玛科技
- 第二课堂|走进世华科技
- 第二课堂|走进姑苏区
- 第二课堂|走进科沃斯
- ... (共17个第二课堂课程)
### 其他培训类
- 人才培训、初创班、攀峰班、产业加速营、专题培训等 (共23个课程)
## 匹配策略
### 1. 精确匹配
直接匹配courses表中的name字段
### 2. 模糊匹配
使用LIKE查询进行部分匹配
### 3. 相似度匹配
- 使用Levenshtein算法计算字符串相似度
- 最低相似度阈值30%
- 自动选择相似度最高的课程
## 日历记录创建
为每个匹配的课程创建以下日历记录:
```php
[
'type' => 1, // 类型1为课程
'course_id' => $course->id, // 关联的课程ID
'date' => $course->start_date, // 课程开始日期
'title' => $course->name, // 课程标题
'content' => $course->content, // 课程内容
'start_time' => '09:00:00', // 默认开始时间
'end_time' => '17:00:00', // 默认结束时间
'url' => $course->url, // 课程链接
]
```
## 输出示例
```
开始将课程关联到calendars日历表...
总共需要处理 69 个课程
正在处理课程: 第三期:张平院士— 6G通信与AI融合
✓ 找到匹配课程: 第三期:张平院士— 6G通信与AI融合 (ID: 123)
✓ 成功创建日历记录 (ID: 456) 关联课程: 第三期:张平院士— 6G通信与AI融合
正在处理课程: 高研班|第四期高级科创人才研修班-第七模块
通过模糊匹配找到课程: '第四期高级科创人才研修班-第七模块'
✓ 成功创建日历记录 (ID: 457) 关联课程: 第四期高级科创人才研修班-第七模块
============================================================
处理完成!
成功关联课程数量: 65
已存在日历记录: 2
未找到匹配课程: 2
未找到匹配的课程列表:
- 某个未找到的课程1
- 某个未找到的课程2
已存在日历记录的课程列表:
- 某个已存在的课程1
- 某个已存在的课程2
```
## 安全特性
1. **数据库事务**: 所有操作在事务中执行,出错时自动回滚
2. **重复检查**: 防止创建重复的日历记录
3. **软删除过滤**: 只处理未被软删除的课程
4. **错误处理**: 完善的异常处理机制
## 注意事项
1. **备份数据**: 建议执行前备份`calendars`表数据
2. **权限检查**: 确保数据库连接正常,有足够的读写权限
3. **课程匹配**: 相似度匹配可能不是100%准确,建议执行后检查结果
4. **重复执行**: 命令可以安全地重复执行,已存在的记录会被跳过
## 错误处理
命令包含完善的错误处理机制:
- 数据库连接错误处理
- 事务回滚保护
- 详细的错误日志输出
- 匹配失败统计
## 数据库表要求
### courses表
- `id`: 主键
- `name`: 课程名称
- `start_date`: 开始日期
- `end_date`: 结束日期
- `content`: 课程内容
- `url`: 课程链接
- `deleted_at`: 软删除时间戳
### calendars表
- `type`: 类型1=课程2=课堂3=事件)
- `course_id`: 关联的课程ID
- `date`: 日期
- `title`: 标题
- `content`: 内容
- `start_time`: 开始时间
- `end_time`: 结束时间
- `url`: 链接

Binary file not shown.
Loading…
Cancel
Save