DedeCMS织梦网站数据批量迁移到Typecho的方法
最近保哥在给一个老客户的网站数据进行检测排查时,发现网站被上传了加密的网页木马,并且篡改了网站内容,导致客户的网站收录从百度消失,并且在多个平台提示网址安全异常。这个网站是保哥很久以前用Dedecms织梦搭建的,有十几年了,由于近几年官方停止免费版本的更新,不能用于商业,并且Dedecms织梦网站程序漏洞实在太多,导致越来越多的人开始弃用Dedecms织梦了。以下是保哥把这次Dedecms织梦转Typecho的方法总结在下面,并提供批量数据迁移程序。
DedeCMS迁移数据到Typecho前置准备工作
- 数据库信息确认:准确获取DedeCMS和Typecho的数据库连接信息(主机地址、用户名、密码、数据库名、表前缀),并填入代码中的
$dedeConfig和$typechoConfig。表前缀(DedeCMS 默认dede_,Typecho 默认typecho_,若自定义过需对应修改)。数据库主机地址(通常是localhost,若用远程数据库则填 IP)。 - 环境依赖检查:确保服务器支持PHP(推荐7.0+)及PDO MySQL扩展,避免因环境缺失导致连接失败。
- 目标系统就绪:Typecho需提前安装完成,数据库表结构(如
typecho_metas、typecho_contents等)完整可用。 - 数据备份:迁移前备份DedeCMS和Typecho的数据库,防止操作失误导致数据丢失。可通过数据库工具(如 phpMyAdmin、Navicat)导出完整 SQL 文件。备份 Typecho 数据库:若 Typecho 已有数据,同样导出备份,避免迁移失败导致数据丢失。
- 内容预处理:若文章包含图片等媒体文件,需确认存储路径(如DedeCMS的
/uploads/),并准备好Typecho对应的路径(如/usr/uploads/),以便后续替换(代码中已预留路径替换注释)。 - 权限检查:确保数据库用户对DedeCMS数据库有读取权限,对Typecho数据库有插入、查询权限。
DedeCMS转Typecho工具优点
- 调试友好:开启错误显示并输出详细迁移日志(如分类创建/复用情况、文章迁移进度、跳过原因等),迁移过程中显示当前文章的slug(即原 aid),迁移完成后统计成功数量和重复跳过数量,方便用户核对。便于实时监控迁移过程和排查问题。
- 数据库操作规范:使用PDO连接数据库,支持异常处理(
ERRMODE_EXCEPTION),避免SQL注入风险,且禁用预处理模拟(EMULATE_PREPARES => false),提升执行稳定性。 - 分类迁移智能:自动检测Typecho中已存在的分类,若与DedeCMS分类名称一致则复用,否则创建新分类,减少重复数据。
- 分批处理数据:通过
pageSize(默认100)和offset实现文章分批迁移,配合延长的执行时间(3600秒)和内存限制(512M),适合处理大量数据,避免超时或内存溢出。 - 冲突处理机制:该程序将DedeCMS文章的aid(即文章 ID)同步为Typecho文章的slug,并确保slug的唯一性和迁移准确性。将 DedeCMS 文章的aid(即archives表的id字段)直接作为Typecho文章的slug字段,通过$targetSlug = (string)$dedeAid实现,并在插入时绑定到slug参数。。
- 字符集兼容:数据库连接指定
utf8mb4字符集,支持 emoji 等特殊字符,减少迁移后的乱码问题。
DedeCMS迁移到Typecho工具完整代码
可以直接从Github链接下载文件:https://github.com/zhangwenbao-com/DedeCMS-to-Typecho
或者将以下代码保存为dedecms-to-typecho.php,配置好两边的数据库信息,并上传到Typecho网站根目录,打开浏览器访问https://网站域名/dedecms-to-typecho.php即可运行
<?php
/**
* DedeCMS 到 Typecho 文章数据迁移工具
*/
// 显示所有错误(调试用)
error_reporting(E_ALL);
ini_set('display_errors', 1);
// 延长执行时间
ini_set('max_execution_time', 3600);
ini_set('memory_limit', '512M');
// 配置数据库信息
$dedeConfig = [
'host' => 'localhost', // DedeCMS数据库主机
'user' => 'root', // DedeCMS数据库用户名
'pass' => '', // DedeCMS数据库密码
'name' => 'dedecms', // DedeCMS数据库名
'prefix' => 'dede_' // DedeCMS表前缀
];
$typechoConfig = [
'host' => 'localhost', // Typecho数据库主机
'user' => 'root', // Typecho数据库用户名
'pass' => '', // Typecho数据库密码
'name' => 'typecho', // Typecho数据库名
'prefix' => 'typecho_' // Typecho表前缀
];
// 建立数据库连接
try {
$dedeDb = new PDO(
"mysql:host={$dedeConfig['host']};dbname={$dedeConfig['name']};charset=utf8mb4",
$dedeConfig['user'],
$dedeConfig['pass'],
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
]
);
$typechoDb = new PDO(
"mysql:host={$typechoConfig['host']};dbname={$typechoConfig['name']};charset=utf8mb4",
$typechoConfig['user'],
$typechoConfig['pass'],
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
]
);
echo "数据库连接成功!\n<br>";
} catch (PDOException $e) {
die("数据库连接失败: " . $e->getMessage() . "\n<br>");
}
// 获取DedeCMS栏目
echo "开始获取DedeCMS栏目...\n<br>";
try {
$dedeCateStmt = $dedeDb->query("SELECT id, typename FROM {$dedeConfig['prefix']}arctype");
$categoryMap = [];
while ($cate = $dedeCateStmt->fetch(PDO::FETCH_ASSOC)) {
$categoryMap[$cate['id']] = $cate['typename'];
}
$cateCount = count($categoryMap);
echo "成功获取 {$cateCount} 个DedeCMS栏目\n<br>";
} catch (PDOException $e) {
die("获取DedeCMS栏目失败: " . $e->getMessage() . "\n<br>");
}
// 处理Typecho分类
echo "开始处理Typecho分类...\n<br>";
$existingCategories = [];
try {
$typechoCateStmt = $typechoDb->query("SELECT mid, name FROM {$typechoConfig['prefix']}metas WHERE type = 'category'");
while ($cate = $typechoCateStmt->fetch(PDO::FETCH_ASSOC)) {
$existingCategories[$cate['name']] = $cate['mid'];
}
echo "Typecho已存在 " . count($existingCategories) . " 个分类\n<br>";
} catch (PDOException $e) {
die("获取Typecho现有分类失败: " . $e->getMessage() . "\n<br>");
}
// 创建分类映射
$cateMigrationMap = [];
foreach ($categoryMap as $dedeCateId => $dedeCateName) {
if (isset($existingCategories[$dedeCateName])) {
$cateMigrationMap[$dedeCateId] = $existingCategories[$dedeCateName];
echo "分类已存在,复用:{$dedeCateName}(ID: {$cateMigrationMap[$dedeCateId]})\n<br>";
continue;
}
try {
$stmt = $typechoDb->prepare("INSERT INTO {$typechoConfig['prefix']}metas
(name, slug, type, description, count, `order`)
VALUES (:name, :slug, 'category', '', 0, 0)");
$slug = preg_replace('/[^\w]+/', '-', strtolower($dedeCateName));
$stmt->execute([
':name' => $dedeCateName,
':slug' => $slug
]);
$cateMigrationMap[$dedeCateId] = $typechoDb->lastInsertId();
echo "创建新分类:{$dedeCateName}(ID: {$cateMigrationMap[$dedeCateId]})\n<br>";
} catch (PDOException $e) {
die("创建分类 {$dedeCateName} 失败: " . $e->getMessage() . "\n<br>");
}
}
// 迁移文章数据
echo "开始迁移文章数据...\n<br>";
$pageSize = 100;
$offset = 0;
$totalMigrated = 0;
$duplicateSlugCount = 0;
try {
// 查询文章总数
$countStmt = $dedeDb->query("
SELECT COUNT(*) AS total
FROM {$dedeConfig['prefix']}archives a
LEFT JOIN {$dedeConfig['prefix']}addonarticle b ON a.id = b.aid
WHERE a.channel = 1
");
$totalArticles = $countStmt->fetchColumn();
echo "DedeCMS中符合条件的文章总数:{$totalArticles} 篇\n<br>";
} catch (PDOException $e) {
die("查询文章总数失败: " . $e->getMessage() . "\n<br>");
}
do {
try {
$stmt = $dedeDb->prepare("
SELECT a.id AS aid, a.title, a.senddate, a.typeid, a.arcrank, b.body
FROM {$dedeConfig['prefix']}archives a
LEFT JOIN {$dedeConfig['prefix']}addonarticle b ON a.id = b.aid
WHERE a.channel = 1
LIMIT :pageSize OFFSET :offset
");
$stmt->bindValue(':pageSize', $pageSize, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);
$articleCount = count($articles);
echo "第 " . (($offset / $pageSize) + 1) . " 批,获取到 {$articleCount} 篇文章\n<br>";
if ($articleCount == 0) {
break;
}
foreach ($articles as $article) {
$dedeAid = $article['aid'];
$targetSlug = (string)$dedeAid;
// 检查slug重复
$checkStmt = $typechoDb->prepare("SELECT cid FROM {$typechoConfig['prefix']}contents WHERE slug = :slug");
$checkStmt->execute([':slug' => $targetSlug]);
if ($checkStmt->fetch()) {
$duplicateSlugCount++;
echo "跳过:slug重复({$targetSlug}),文章标题:{$article['title']}\n<br>";
continue;
}
// 文章状态
$status = $article['arcrank'] == 0 ? 'publish' : 'draft';
// 文章内容
$content = $article['body'] ?? '';
// 如需替换图片路径,取消下面注释并修改路径
// $content = str_replace('/uploads/', '/usr/uploads/', $content);
// 插入文章(已移除viewsNum字段)
$insertStmt = $typechoDb->prepare("
INSERT INTO {$typechoConfig['prefix']}contents
(title, slug, created, modified, text, authorId, status, type,
commentsNum, parent, `order`)
VALUES (:title, :slug, :created, :modified, :text, 1, :status, 'post', 0, 0, 0)
");
$insertStmt->execute([
':title' => $article['title'],
':slug' => $targetSlug,
':created' => $article['senddate'],
':modified' => $article['senddate'],
':text' => $content,
':status' => $status
]);
$contentId = $typechoDb->lastInsertId();
// 关联分类
if (isset($cateMigrationMap[$article['typeid']])) {
$relationStmt = $typechoDb->prepare("
INSERT INTO {$typechoConfig['prefix']}relationships (cid, mid) VALUES (:cid, :mid)
");
$relationStmt->execute([
':cid' => $contentId,
':mid' => $cateMigrationMap[$article['typeid']]
]);
} else {
echo "警告:文章《{$article['title']}》无对应分类,未关联分类\n<br>";
}
$totalMigrated++;
echo "已迁移({$totalMigrated}):{$article['title']}(slug: {$targetSlug})\n<br>";
}
$offset += $pageSize;
} catch (PDOException $e) {
die("迁移第 " . (($offset / $pageSize) + 1) . " 批文章失败: " . $e->getMessage() . "\n<br>");
}
} while ($articleCount == $pageSize);
// 迁移完成
echo "\n<br>===== 迁移结束 =====\n<br>";
echo "成功迁移:{$totalMigrated} 篇文章\n<br>";
echo "因slug重复跳过:{$duplicateSlugCount} 篇文章\n<br>";
?>若文章包含图片,且图片路径需要从 DedeCMS 的/uploads/改为 Typecho 的/usr/uploads/,取消代码中这一行的注释并修改路径:
// $content = str_replace('/uploads/', '/usr/uploads/', $content);如果要同步迁移dedecms的tag标签,可以在以上代码中的关联分类后加入:
// 处理标签
if (!empty($article['keywords'])) {
$tags = explode(',', $article['keywords']);
foreach ($tags as $tag) {
$tag = trim($tag);
if (empty($tag)) continue;
// 检查标签是否已存在
$tagStmt = $typechoDb->prepare("
SELECT mid FROM {$typechoConfig['prefix']}metas
WHERE name = :name AND type = 'tag'
");
$tagStmt->execute([':name' => $tag]);
$tagData = $tagStmt->fetch();
if ($tagData) {
$tagId = $tagData['mid'];
} else {
// 创建新标签
$tagInsertStmt = $typechoDb->prepare("
INSERT INTO {$typechoConfig['prefix']}metas
(name, slug, type, count)
VALUES (:name, :slug, 'tag', 0)
");
$tagSlug = preg_replace('/[^\w]+/', '-', strtolower($tag));
$tagInsertStmt->execute([
':name' => $tag,
':slug' => $tagSlug
]);
$tagId = $typechoDb->lastInsertId();
}
// 关联标签
$tagRelationStmt = $typechoDb->prepare("
INSERT INTO {$typechoConfig['prefix']}relationships
(cid, mid) VALUES (:cid, :mid)
");
$tagRelationStmt->execute([
':cid' => $contentId,
':mid' => $tagId
]);
}
}DedeCMS迁移到Typecho后的工作
- 开启Typecho伪静态:伪静态代码如下(Nginx)
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php$1 last;
}- 设置Typecho链接结构:为了保留原 URL 结构(如
DedeCMS 的/父分类/子分类/文章aid.html对应Typecho 的/父分类/子分类/slug.html),需确保 Typecho 的固定链接设置中使用{slug}作为标识符。
- 上传图片附件目录:将原Dedecms程序下的uploads目录复制上传到Typecho根目录下。
DedeCMS数据迁移到Typecho后的其它注意事项
- 数据备份优先:迁移过程可能因网络中断、配置错误等导致中断,必须先备份数据库,必要时可恢复后重新迁移。
- 调试模式管理:代码开启了
error_reporting(E_ALL),迁移完成后需关闭(生产环境),避免暴露敏感信息。 - slug冲突处理:文章slug默认使用DedeCMS的
aid(文章ID),若Typecho中已有相同slug的内容,会被标记为“跳过”,需在迁移后手动处理这些文章(如修改slug后重新导入)。 - 分类属性局限:仅迁移分类名称,DedeCMS分类的描述、排序等属性未迁移,若需保留需修改代码补充字段映射。
- 文章状态映射简单:仅通过
arcrank(0为发布,其他为草稿)判断状态,DedeCMS的“定时发布”“回收站”等状态未处理,需手动核对。 - 作者固定限制:文章作者ID硬编码为1(
authorId = 1),若DedeCMS有多个作者,需提前建立作者映射关系并修改代码。 - 大数量迁移优化:若文章总数超过10万级,建议进一步减小
pageSize,或分多次执行(通过调整offset),避免单次迁移压力过大。 - 迁移后校验:完成后需检查:文章数量是否与原数据一致、分类关联是否正确、内容格式(如HTML标签、图片路径)是否正常、特殊字符是否显示正确。
- 禁止写入操作:迁移期间建议暂停DedeCMS和Typecho的内容发布/编辑,避免数据同步不一致。
常见问题解答
- 迁移后原DedeCMS的URL(如
/news/123.html)如何301重定向到Typecho的新URL(如/news/123.html,slug为123)?需要修改服务器配置吗?
需通过服务器配置实现301重定向,不同服务器配置方式不同:①Nginx:在DedeCMS站点的配置文件中添加规则rewrite ^/news/(\d+)\.html$ https://新域名/news/$1.html permanent;(需替换“news”为实际分类路径,“新域名”为Typecho站点域名);②Apache:在DedeCMS根目录的.htaccess文件中添加Redirect 301 /news/123.html https://新域名/news/123.html(若需批量匹配,可使用正则RewriteRule ^news/(\d+)\.html$ https://新域名/news/$1.html [R=301,L])。重定向后需通过Google Search Console或百度资源平台提交新URL,加速搜索引擎收录更新。 - DedeCMS有多个作者(如作者A、作者B,对应不同ID),迁移工具默认作者ID为1,怎么修改代码实现作者ID的精准映射?
需先建立“DedeCMS作者ID与Typecho作者ID”的映射关系,再修改代码:①在代码顶部的配置区添加作者映射数组$authorMap = [1 => 2, 3 => 1];(左侧为DedeCMS作者ID,右侧为Typecho作者ID,需根据实际情况调整);②找到文章插入代码中的authorId,字段,将原1改为$authorMap[$article['writer_id']] ?? 1(需先确认DedeCMS存储作者ID的字段名,通常在dede_archives表的writer或mid字段,若字段名不同需替换);③若DedeCMS作者ID未在映射中,默认使用ID=1,避免插入失败。修改后需测试1-2篇不同作者的文章,确认作者归属正确。 - 迁移后文章中的内部链接(如链接到DedeCMS的
/about/456.html)失效,怎么批量修复这些内部链接?
有两种批量修复方式:①数据库直接替换:在Typecho数据库中执行SQL语句UPDATE typecho_contents SET text = REPLACE(text, 'https://旧域名/about/', 'https://新域名/about/')(需替换“旧域名”“新域名”“about”为实际路径,若链接是相对路径则去掉域名部分);②代码补充:在迁移工具的“文章内容处理”环节($content = $article['body'] ?? '';后)添加$content = str_replace('https://旧域名/about/', 'https://新域名/about/', $content);,重新执行迁移即可自动替换。修复后需随机抽查10-20篇文章,确认内部链接能正常跳转。 - 服务器安装的PHP版本是8.2,迁移工具推荐PHP7.0+,是否兼容?若出现“语法错误”怎么处理?
PHP8.2兼容迁移工具,但需处理2处PHP8+的语法差异:①若出现“Deprecated: strtolower(): Passing null to parameter #1”错误,需在$tag = trim($tag);后添加if (empty($tag)) continue;(过滤空标签,避免strtolower处理null);②若出现“PDO::__construct(): Argument #4 ($options) must be an array”错误,确认代码中PDO连接的第4个参数是数组(工具代码已正确设置,无需修改)。若仍有错误,可在代码顶部添加error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);,屏蔽PHP8+的废弃警告,不影响迁移功能。 - DedeCMS的文章有自定义字段(如“文章摘要”存储在
dede_addonarticle的description字段,“缩略图”存储在dede_archives的litpic字段),怎么修改代码迁移这些字段到Typecho?
需分两步修改代码:①查询文章时增加自定义字段:在DedeCMS文章查询SQL中添加字段,如SELECT a.id AS aid, a.title, a.senddate, a.typeid, a.arcrank, a.litpic, b.body, b.description FROM ...(新增a.litpic“缩略图”、b.description“摘要”);②插入Typecho时存储自定义字段:Typecho需先安装“自定义字段插件”(如Custom Fields),再在文章插入代码后添加字段插入逻辑,如$insertMetaStmt = $typechoDb->prepare("INSERT INTO typecho_fields (cid, name, type, str_value) VALUES (:cid, 'summary', 'str', :summary), (:cid, 'thumbnail', 'str', :thumbnail)"); $insertMetaStmt->execute([':cid' => $contentId, ':summary' => $article['description'], ':thumbnail' => $article['litpic']]);,即可将自定义字段同步到Typecho。 - 迁移后Typecho的评论数据是空的,原DedeCMS的评论(存储在
dede_feedback表)能迁移到Typecho吗?需要怎么修改代码?
能迁移,需在文章迁移代码后添加评论迁移逻辑:①先查询DedeCMS评论:$commentStmt = $dedeDb->prepare("SELECT * FROM dede_feedback WHERE aid = :aid AND check = 1");(check=1表示已审核评论);②映射Typecho评论字段:Typecho评论表typecho_comments需匹配字段——cid(对应文章ID)、author(评论者姓名)、mail(评论者邮箱)、url(评论者网址)、text(评论内容)、created(评论时间)、status(状态,1为通过);③插入评论:$insertCommentStmt = $typechoDb->prepare("INSERT INTO typecho_comments (cid, author, mail, url, text, created, status) VALUES (:cid, :author, :mail, :url, :text, :created, 1)"); $insertCommentStmt->execute([':cid' => $contentId, ':author' => $comment['username'], ':mail' => $comment['email'], ':url' => $comment['homepage'], ':text' => $comment['msg'], ':created' => $comment['dtime']]);。迁移前需确认DedeCMS评论表字段名(如dtime为评论时间),避免字段不匹配。 - 运行迁移工具时提示“PDO MySQL扩展未启用”,但服务器已安装PHP,怎么快速启用这个扩展?
分两种服务器环境处理:①Linux服务器(如CentOS):通过命令启用——若用PHP7.4,执行yum install php74-php-pdo php74-php-mysqlnd,安装后重启Apache/Nginx(systemctl restart httpd或systemctl restart nginx);②Windows服务器(IIS):打开php.ini文件,找到;extension=pdo_mysql,删除前面的分号(取消注释),保存后重启IIS服务。启用后可通过phpinfo()函数(创建含<?php phpinfo(); ?>的文件访问)确认“PDO MySQL”模块是否显示,显示则表示启用成功。 - DedeCMS有二级分类(如“新闻>科技新闻”),迁移后Typecho的分类层级是否会混乱?需要提前在Typecho创建分类层级吗?
无需提前创建,需修改代码确保分类层级正确:原工具仅迁移一级分类,二级分类需补充“父分类ID映射”;①在“处理Typecho分类”环节,查询时保留父分类ID:SELECT mid, name, parent FROM typecho_metas WHERE type = 'category'(新增parent字段);②在“获取DedeCMS栏目”时,查询DedeCMS的分类层级:SELECT id, typename, reid FROM dede_arctype(reid为父分类ID,0表示一级分类);③创建分类时关联父ID:若DedeCMS分类的reid != 0,则在Typecho创建分类时,将parent字段设为父分类在Typecho的mid(需先通过$cateMigrationMap[$reid]获取父分类ID),插入SQL中添加parent = :parent。修改后二级分类会自动归属到对应父分类下,层级与DedeCMS一致。 - 迁移后图片能正常访问(直接访问URL能打开),但Typecho后台“媒体管理”中看不到这些图片,怎么让图片显示在后台?
原因是Typecho媒体库仅显示“通过后台上传”的图片(需在typecho_attachments表中记录),迁移的图片未写入该表。解决方法:①批量插入附件记录:执行SQL语句INSERT INTO typecho_attachments (cid, title, slug, type, path, size, created, authorId) SELECT c.id AS cid, SUBSTRING_INDEX(c.path, '/', -1) AS title, SUBSTRING_INDEX(c.path, '/', -1) AS slug, 'image/jpeg' AS type, c.path AS path, 0 AS size, c.created AS created, 1 AS authorId FROM (SELECT DISTINCT SUBSTRING_INDEX(text, 'src=\"', -1) AS path, cid, created FROM typecho_contents WHERE text LIKE '%src=\"/usr/uploads/%'") c(需替换/usr/uploads/为实际图片路径,image/jpeg为图片类型,若有PNG需补充);②执行后刷新后台媒体管理,图片即可显示。若需精准匹配图片大小,可先用脚本遍历图片目录获取大小,再更新size字段。 - 迁移完成后,DedeCMS的数据库和文件还需要保留吗?如果保留,怎么防止DedeCMS网站再次被入侵(毕竟漏洞多)?
建议保留数据库3个月(用于核对数据),文件可删除或禁用访问;若保留需做3项安全处理:①禁用DedeCMS网站访问:在DedeCMS根目录添加.htaccess(Apache)或修改Nginx配置,拒绝所有访问(如Nginxlocation / { return 403; });②删除敏感文件:删除DedeCMS的后台管理文件(如dede/login.php)、安装文件(如install.php),避免被利用漏洞登录;③清空数据库写入权限:将DedeCMS数据库的用户权限从“读写”改为“只读”,即使被入侵也无法修改数据。3个月后确认Typecho数据无问题,再删除DedeCMS数据库和文件。 - 运行迁移工具时提示“数据库用户 'root'@'localhost' 对 typecho 数据库无 INSERT 权限”,具体需要给数据库用户哪些权限才能完成迁移?
需给数据库用户“DedeCMS库只读权限”和“Typecho库读写权限”,具体权限清单:①对DedeCMS数据库:SELECT(读取文章、分类数据)、SHOW VIEW(查看表结构);②对Typecho数据库:SELECT(检查分类、slug是否存在)、INSERT(插入文章、分类、关联数据)、UPDATE(若后续需补充数据)、CREATE TEMPORARY TABLES(批量处理时临时表需求)。权限设置方式:在phpMyAdmin中,编辑用户→“数据库”标签→选择对应数据库→勾选所需权限;或通过SQL命令GRANT SELECT, SHOW VIEW ON dedecms_db.* TO 'user'@'localhost'; GRANT SELECT, INSERT, UPDATE, CREATE TEMPORARY TABLES ON typecho_db.* TO 'user'@'localhost';(替换dedecms_db“Typecho_db”“user”为实际名称)。 - Typecho已存在100篇文章,迁移DedeCMS时部分文章slug(如123)与现有文章冲突,除了工具默认的“跳过”,能否让工具自动在冲突slug后加后缀(如123-1)避免跳过?
能,需修改工具的“slug冲突处理”逻辑:找到代码中“检查slug重复”的部分,将原“跳过”逻辑改为“生成新slug”;①原代码:if ($checkStmt->fetch()) { $duplicateSlugCount++; echo "跳过:slug重复..."; continue; };②修改为:$newSlug = $targetSlug; $i = 1; while ($checkStmt->fetch()) { $newSlug = $targetSlug . "-" . $i; $checkStmt->execute([':slug' => $newSlug]); $i++; }(循环检查slug,若重复则加“-1”“-2”直到无冲突);③插入文章时用$newSlug替代$targetSlug:':slug' => $newSlug。修改后冲突slug会自动生成新后缀,无需手动处理,同时需在迁移日志中增加“slug冲突,自动生成新slug:XXX”的提示,方便后续核对。
TAG
本文标题:《DedeCMS织梦网站数据批量迁移到Typecho的方法》
本文链接:https://zhangwenbao.com/dedecms-to-typecho.html
