DedeCMS织梦网站数据批量迁移到Typecho的方法

DedeCMS织梦网站数据批量迁移到Typecho的方法

最近保哥在给一个老客户的网站数据进行检测排查时,发现网站被上传了加密的网页木马,并且篡改了网站内容,导致客户的网站收录从百度消失,并且在多个平台提示网址安全异常。这个网站是保哥很久以前用Dedecms织梦搭建的,有十几年了,由于近几年官方停止免费版本的更新,不能用于商业,并且Dedecms织梦网站程序漏洞实在太多,导致越来越多的人开始弃用Dedecms织梦了。以下是保哥把这次Dedecms织梦转Typecho的方法总结在下面,并提供批量数据迁移程序。

DedeCMS迁移数据到Typecho前置准备工作

  1. 数据库信息确认:准确获取DedeCMS和Typecho的数据库连接信息(主机地址、用户名、密码、数据库名、表前缀),并填入代码中的$dedeConfig$typechoConfig。表前缀(DedeCMS 默认dede_,Typecho 默认typecho_,若自定义过需对应修改)。数据库主机地址(通常是localhost,若用远程数据库则填 IP)。
  2. 环境依赖检查:确保服务器支持PHP(推荐7.0+)及PDO MySQL扩展,避免因环境缺失导致连接失败。
  3. 目标系统就绪:Typecho需提前安装完成,数据库表结构(如typecho_metastypecho_contents等)完整可用。
  4. 数据备份:迁移前备份DedeCMS和Typecho的数据库,防止操作失误导致数据丢失。可通过数据库工具(如 phpMyAdmin、Navicat)导出完整 SQL 文件。备份 Typecho 数据库:若 Typecho 已有数据,同样导出备份,避免迁移失败导致数据丢失。
  5. 内容预处理:若文章包含图片等媒体文件,需确认存储路径(如DedeCMS的/uploads/),并准备好Typecho对应的路径(如/usr/uploads/),以便后续替换(代码中已预留路径替换注释)。
  6. 权限检查:确保数据库用户对DedeCMS数据库有读取权限,对Typecho数据库有插入、查询权限。

DedeCMS转Typecho工具优点

  1. 调试友好:开启错误显示并输出详细迁移日志(如分类创建/复用情况、文章迁移进度、跳过原因等),迁移过程中显示当前文章的slug(即原 aid),迁移完成后统计成功数量和重复跳过数量,方便用户核对。便于实时监控迁移过程和排查问题。
  2. 数据库操作规范:使用PDO连接数据库,支持异常处理(ERRMODE_EXCEPTION),避免SQL注入风险,且禁用预处理模拟(EMULATE_PREPARES => false),提升执行稳定性。
  3. 分类迁移智能:自动检测Typecho中已存在的分类,若与DedeCMS分类名称一致则复用,否则创建新分类,减少重复数据。
  4. 分批处理数据:通过pageSize(默认100)和offset实现文章分批迁移,配合延长的执行时间(3600秒)和内存限制(512M),适合处理大量数据,避免超时或内存溢出。
  5. 冲突处理机制:该程序将DedeCMS文章的aid(即文章 ID)同步为Typecho文章的slug,并确保slug的唯一性和迁移准确性。将 DedeCMS 文章的aid(即archives表的id字段)直接作为Typecho文章的slug字段,通过$targetSlug = (string)$dedeAid实现,并在插入时绑定到slug参数。。
  6. 字符集兼容:数据库连接指定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后的工作

  1. 开启Typecho伪静态:伪静态代码如下(Nginx)
    if (!-e $request_filename) {
        rewrite ^(.*)$ /index.php$1 last;
    }
  1. 设置Typecho链接结构:为了保留原 URL 结构(如 DedeCMS 的/父分类/子分类/文章aid.html对应 Typecho 的/父分类/子分类/slug.html),需确保 Typecho 的固定链接设置中使用{slug}作为标识符。
    typecho-url.png
  2. 上传图片附件目录:将原Dedecms程序下的uploads目录复制上传到Typecho根目录下。

DedeCMS数据迁移到Typecho后的其它注意事项

  1. 数据备份优先:迁移过程可能因网络中断、配置错误等导致中断,必须先备份数据库,必要时可恢复后重新迁移。
  2. 调试模式管理:代码开启了error_reporting(E_ALL),迁移完成后需关闭(生产环境),避免暴露敏感信息。
  3. slug冲突处理:文章slug默认使用DedeCMS的aid(文章ID),若Typecho中已有相同slug的内容,会被标记为“跳过”,需在迁移后手动处理这些文章(如修改slug后重新导入)。
  4. 分类属性局限:仅迁移分类名称,DedeCMS分类的描述、排序等属性未迁移,若需保留需修改代码补充字段映射。
  5. 文章状态映射简单:仅通过arcrank(0为发布,其他为草稿)判断状态,DedeCMS的“定时发布”“回收站”等状态未处理,需手动核对。
  6. 作者固定限制:文章作者ID硬编码为1(authorId = 1),若DedeCMS有多个作者,需提前建立作者映射关系并修改代码。
  7. 大数量迁移优化:若文章总数超过10万级,建议进一步减小pageSize,或分多次执行(通过调整offset),避免单次迁移压力过大。
  8. 迁移后校验:完成后需检查:文章数量是否与原数据一致、分类关联是否正确、内容格式(如HTML标签、图片路径)是否正常、特殊字符是否显示正确。
  9. 禁止写入操作:迁移期间建议暂停DedeCMS和Typecho的内容发布/编辑,避免数据同步不一致。

常见问题解答

  1. 迁移后原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,加速搜索引擎收录更新。
  2. 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表的writermid字段,若字段名不同需替换);③若DedeCMS作者ID未在映射中,默认使用ID=1,避免插入失败。修改后需测试1-2篇不同作者的文章,确认作者归属正确。
  3. 迁移后文章中的内部链接(如链接到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篇文章,确认内部链接能正常跳转。
  4. 服务器安装的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+的废弃警告,不影响迁移功能。
  5. DedeCMS的文章有自定义字段(如“文章摘要”存储在dede_addonarticledescription字段,“缩略图”存储在dede_archiveslitpic字段),怎么修改代码迁移这些字段到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。
  6. 迁移后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为评论时间),避免字段不匹配。
  7. 运行迁移工具时提示“PDO MySQL扩展未启用”,但服务器已安装PHP,怎么快速启用这个扩展?
    分两种服务器环境处理:①Linux服务器(如CentOS):通过命令启用——若用PHP7.4,执行 yum install php74-php-pdo php74-php-mysqlnd,安装后重启Apache/Nginx(systemctl restart httpdsystemctl restart nginx);②Windows服务器(IIS):打开php.ini文件,找到 ;extension=pdo_mysql,删除前面的分号(取消注释),保存后重启IIS服务。启用后可通过phpinfo()函数(创建含<?php phpinfo(); ?>的文件访问)确认“PDO MySQL”模块是否显示,显示则表示启用成功。
  8. 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_arctypereid为父分类ID,0表示一级分类);③创建分类时关联父ID:若DedeCMS分类的reid != 0,则在Typecho创建分类时,将parent字段设为父分类在Typecho的mid(需先通过$cateMigrationMap[$reid]获取父分类ID),插入SQL中添加 parent = :parent。修改后二级分类会自动归属到对应父分类下,层级与DedeCMS一致。
  9. 迁移后图片能正常访问(直接访问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字段。
  10. 迁移完成后,DedeCMS的数据库和文件还需要保留吗?如果保留,怎么防止DedeCMS网站再次被入侵(毕竟漏洞多)?
    建议保留数据库3个月(用于核对数据),文件可删除或禁用访问;若保留需做3项安全处理:①禁用DedeCMS网站访问:在DedeCMS根目录添加.htaccess(Apache)或修改Nginx配置,拒绝所有访问(如Nginx location / { return 403; });②删除敏感文件:删除DedeCMS的后台管理文件(如dede/login.php)、安装文件(如install.php),避免被利用漏洞登录;③清空数据库写入权限:将DedeCMS数据库的用户权限从“读写”改为“只读”,即使被入侵也无法修改数据。3个月后确认Typecho数据无问题,再删除DedeCMS数据库和文件。
  11. 运行迁移工具时提示“数据库用户 '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”为实际名称)。
  12. 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
版权声明:本文原创,转载请注明出处和链接。许可协议:CC BY-NC-SA 4.0
发表新评论