From 14237668e0de43e7a08c1a0c51de9d18d1954a90 Mon Sep 17 00:00:00 2001 From: cody <648753004@qq.com> Date: Sat, 9 Aug 2025 17:29:30 +0800 Subject: [PATCH] update --- .../Commands/LinkCoursesToCalendar.php | 302 +++++++++++++++++ app/Console/Commands/UpdateCourseUrls.php | 313 ++++++++++++++++++ .../Controllers/Mobile/UserController.php | 2 +- composer.json | 1 + course_url_update_readme.md | 104 ++++++ link_courses_to_calendar_readme.md | 143 ++++++++ 课程台账.xlsx | Bin 0 -> 14754 bytes 7 files changed, 864 insertions(+), 1 deletion(-) create mode 100644 app/Console/Commands/LinkCoursesToCalendar.php create mode 100644 app/Console/Commands/UpdateCourseUrls.php create mode 100644 course_url_update_readme.md create mode 100644 link_courses_to_calendar_readme.md create mode 100644 课程台账.xlsx diff --git a/app/Console/Commands/LinkCoursesToCalendar.php b/app/Console/Commands/LinkCoursesToCalendar.php new file mode 100644 index 0000000..e661984 --- /dev/null +++ b/app/Console/Commands/LinkCoursesToCalendar.php @@ -0,0 +1,302 @@ +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 new file mode 100644 index 0000000..d238d21 --- /dev/null +++ b/app/Console/Commands/UpdateCourseUrls.php @@ -0,0 +1,313 @@ +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); + } +} diff --git a/app/Http/Controllers/Mobile/UserController.php b/app/Http/Controllers/Mobile/UserController.php index e182f5b..ce4ad43 100755 --- a/app/Http/Controllers/Mobile/UserController.php +++ b/app/Http/Controllers/Mobile/UserController.php @@ -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}"); } diff --git a/composer.json b/composer.json index 3e505c5..8efdc80 100755 --- a/composer.json +++ b/composer.json @@ -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", diff --git a/course_url_update_readme.md b/course_url_update_readme.md new file mode 100644 index 0000000..fb41718 --- /dev/null +++ b/course_url_update_readme.md @@ -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`: 软删除时间戳字段 diff --git a/link_courses_to_calendar_readme.md b/link_courses_to_calendar_readme.md new file mode 100644 index 0000000..e08439f --- /dev/null +++ b/link_courses_to_calendar_readme.md @@ -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`: 链接 diff --git a/课程台账.xlsx b/课程台账.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3bc5dde1b46d29ae16a036196038ad8770a720db GIT binary patch literal 14754 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 literal 0 HcmV?d00001