上一篇我整理了:
《每天批量处理 20 篇 WordPress 历史文章:代码块迁移、摘要补全与英文覆盖翻译完整 SOP》
那一阶段主要处理的是已经属于 Gutenberg 格式、但正文中仍然存在 SyntaxHighlighter 的历史文章。
完成这一阶段以后,我发现站内仍然有不少中文历史文章包含 SyntaxHighlighter。
进一步检查后确认,这些文章并不是此前漏扫,而是属于另一类历史格式:
Gutenberg 区块
+
Classic / 经典编辑器内容
+
SyntaxHighlighter
分析器将它们识别为:
editor_format = mixed
此前 Gutenberg + SyntaxHighlighter 阶段只处理 editor_format=gutenberg 的文章,因此这些 Mixed 文章被留到了下一阶段。
在历史快照中,共有 717 篇 Mixed 候选,其中 712 篇的主要问题就是:
editor-format-mixed
另外 5 篇还同时存在其他代码格式或结构异常,暂时单独保留。
因此我开始第二阶段:
Mixed Gutenberg + SyntaxHighlighter
→ Classic / Mixed 内容规范化为 Gutenberg
→ SyntaxHighlighter 转 Code Block Pro
→ 核对代码语言
→ 生产只读验证
→ 自动生成中文摘要
→ 自动覆盖英文译文
→ completed
整体流程继续沿用上一篇已经熟悉的操作方式,只增加 Mixed 阶段真正需要的步骤。
一、两个仓库分别负责什么
整个流程仍然涉及两个仓库。
1. WordPress 翻译管线
/home/wangqiang/code/wordpress-ai-translation-pipeline
主要负责:
- SlyTranslate 与 GLM 翻译定制;
- Gutenberg 结构保护;
- HTML、代码和短代码保护;
- 占位符校验;
- 英文整篇覆盖翻译;
- WordPress 生产环境相关 MU 插件。
2. 历史文章迁移仓库
/home/wangqiang/code/wordpress-ai-excerpt-backfill
主要负责:
- 管理历史候选;
- 创建固定批次;
- 保存中英文文章关系;
- 管理人工转换状态;
- 执行生产只读验证;
- 生成中文摘要;
- 调用现有英文覆盖翻译入口;
- 保存执行证据;
- 对偶发错误进行有限重试;
- 汇总整个批次状态。
整体职责仍然保持:
人工处理文章格式
→ wordpress-ai-excerpt-backfill 验证和调度
→ wordpress-ai-translation-pipeline 完成英文覆盖翻译
二、人工和仓库的职责边界
仓库负责
Mixed 阶段由仓库负责:
- 从历史数据中筛选符合条件的 Mixed + SyntaxHighlighter 文章;
- 排除已经进入旧批次或已经完成的文章;
- 按发布时间从新到旧排序;
- 每批最多固定 20 篇;
- 保存中英文文章 ID;
- 保存原 SyntaxHighlighter 数量;
- 管理人工转换状态;
- 验证最终文章是否已经成为规范 Gutenberg;
- 验证 SyntaxHighlighter 是否归零;
- 验证 Code Block Pro;
- 自动生成中文摘要;
- 自动覆盖已有英文译文;
- 单篇失败有限重试;
- 汇总整个批次最终状态。
人工负责
人工负责:
- 打开仓库指定的 20 篇文章;
- 将 Classic / Mixed 内容转换为 Gutenberg;
- 检查自动转换后的正文排版;
- 必要时重新拆分段落、标题、编号等;
- 将 SyntaxHighlighter 转换成 Code Block Pro;
- 核对 Code Block Pro 的语言;
- 检查图片、caption、链接和正文结构;
- 保存中文文章;
- 最后确认这批人工处理已经完成。
人工不需要:
- 自己寻找下一批文章;
- 自己按发布时间排序;
- 手工生成中文摘要;
- 手工逐篇点击英文覆盖翻译。
三、完整状态流转
正常主流程仍然与上一篇一致:
awaiting_manual_conversion
→ mark-converted
→ awaiting_readonly_validation
→ validate-live
→ ready_for_execution
→ run-ready --execute
→ completed
区别主要集中在人工处理和生产验证两个阶段。
Mixed 阶段除了代码块转换和语言核对,还必须确认:
整篇 Gutenberg normalization 已完成
生产只读验证则额外要求:
editor_format = gutenberg
classic_outside_blocks = false
因此:
Classic 区块已经消失
并不能直接等价于:
Gutenberg normalization 已经完成
还必须人工确认转换后的正文结构和排版。
四、每天开始前检查仓库状态
进入仓库:
cd ~/code/wordpress-ai-excerpt-backfill
执行:
python3 bin/history-migration.py status
python3 bin/history-migration.py summary
重点查看:
最新未完成批次
下一步
建议创建下一批
如果显示:
最新未完成批次: mixed-syntaxhighlighter-XXXXXXXX-XX
建议创建下一批: False
就继续完成当前批次。
不要提前创建新的 20 篇。
只有出现:
最新未完成批次: 无
建议创建下一批: True
建议: all batches complete
再创建下一批。
五、创建新的 20 篇 Mixed 固定批次
Mixed 阶段使用:
bin/build-mixed-syntaxhighlighter-batch.py
候选排序固定为:
published_at DESC
chinese_post_id DESC
也就是优先处理发布时间更晚的历史文章。
不会优先选择:
SyntaxHighlighter 最少
内容最短
HTML 最简单
人工最好处理
如果最后不足 20 篇,则直接处理全部剩余候选。
确认上一批已经完成以后:
cd ~/code/wordpress-ai-excerpt-backfill
summary_output="$(
python3 bin/history-migration.py summary
)"
printf '%s\n' "$summary_output"
if ! grep -q '^建议创建下一批: True$' <<<"$summary_output"; then
echo
echo "当前不允许创建下一批,请先完成最新未完成批次。"
else
date_tag="$(date +%Y%m%d)"
batch_id=""
batch_file=""
for number in $(seq -w 1 99)
do
candidate_id="mixed-syntaxhighlighter-${date_tag}-${number}"
candidate_file="data/analysis/mixed-syntaxhighlighter-migration-batch-${date_tag}-${number}.csv"
candidate_state_dir="data/state/history-migration/${candidate_id}"
if [ ! -e "$candidate_file" ] && [ ! -d "$candidate_state_dir" ]; then
batch_id="$candidate_id"
batch_file="$candidate_file"
break
fi
done
if [ -z "$batch_id" ]; then
echo "无法生成可用的新批次编号。"
else
preview_file="data/analysis/gutenberg-syntaxhighlighter-empty-excerpt-preview.csv"
translations_file="data/raw/wordpress-zh-translation-links-20260721.jsonl"
mapfile -t raw_files < <(
find data/raw \
-maxdepth 1 \
-type f \
\( \
-name 'wordpress-zh-posts-20260720*.jsonl' \
-o \
-name 'wordpress-zh-posts-20260721*.jsonl' \
\) \
| sort
)
if [ "${#raw_files[@]}" -eq 0 ]; then
echo "没有找到 WordPress 原始历史快照。"
elif [ ! -f "$preview_file" ]; then
echo "找不到 preview:$preview_file"
elif [ ! -f "$translations_file" ]; then
echo "找不到翻译关系:$translations_file"
else
echo
echo "新批次 ID:$batch_id"
echo "新批次文件:$batch_file"
python3 bin/build-mixed-syntaxhighlighter-batch.py \
"${raw_files[@]}" \
--preview "$preview_file" \
--translations "$translations_file" \
--output "$batch_file" \
--batch-id "$batch_id" \
--maximum 20 \
&& python3 bin/history-migration.py init-state --apply \
&& echo \
&& echo "新批次已经创建:" \
&& echo "batch_id=\"$batch_id\"" \
&& echo "csv_file=\"$batch_file\""
fi
fi
fi
正常最后应看到类似:
batch_id="mixed-syntaxhighlighter-20260730-01"
csv_file="data/analysis/mixed-syntaxhighlighter-migration-batch-20260730-01.csv"
六、设置批次变量,并查看后台编辑地址
为了后面的操作更方便,每天需要变化的两个变量单独设置。
例如:
batch_id="mixed-syntaxhighlighter-20260730-01"
csv_file="data/analysis/mixed-syntaxhighlighter-migration-batch-20260730-01.csv"
后面的命令统一复用:
$batch_id
$csv_file
不再到长命令中寻找日期和批次 ID。
然后一次性输出当前批次的后台编辑地址:
python3 - "$batch_id" "$csv_file" <<'PY'
import csv
import json
import sys
from pathlib import Path
batch_id = sys.argv[1]
csv_path = Path(sys.argv[2])
state_dir = Path("data/state/history-migration") / batch_id
with csv_path.open(encoding="utf-8-sig", newline="") as f:
rows = list(csv.DictReader(f))
items = []
for row in rows:
post_id = int(row["chinese_post_id"])
state_path = state_dir / f"chinese-{post_id}.json"
state = {}
if state_path.is_file():
state = json.loads(state_path.read_text(encoding="utf-8"))
status = state.get("workflow_status", "uninitialized")
if status == "completed":
continue
edit_url = (
"https://admin.shuijingwanwq.com/wp-admin/"
f"post.php?post={post_id}&action=edit"
)
items.append({
"post_id": post_id,
"english_id": row["english_post_id"],
"title": row["chinese_title"],
"syntax": row["before_syntaxhighlighter_count"],
"status": status,
"edit_url": edit_url,
})
print(f"批次:{batch_id}")
print(f"当前待处理:{len(items)}")
print()
for index, item in enumerate(items, 1):
print(
f"{index:02d}. "
f"zh={item['post_id']} "
f"en={item['english_id']} "
f"SH={item['syntax']} "
f"status={item['status']}"
)
print(f" 标题:{item['title']}")
print(f" 编辑:{item['edit_url']}")
print()
PY
这样可以一次拿到当天需要处理的所有后台链接。
七、人工规范化 Gutenberg,并迁移 SyntaxHighlighter
Mixed 阶段最主要的变化,就在这一节。
每篇文章需要处理两个部分:
Classic / Mixed → Gutenberg
SyntaxHighlighter → Code Block Pro
1. 将 Classic 转换为 Gutenberg
选中 Classic 区块后,可以使用 WordPress 提供的:
转换为区块
简单情况下,例如 Classic 中只有:
3. 最终成功运行日志
转换后可以直接得到:
<!-- wp:paragraph -->
<p>3. 最终成功运行日志</p>
<!-- /wp:paragraph -->
这种简单内容基本不需要继续处理。

对于比较复杂的 Classic,一个 Classic 区块也可以一次拆成多个 Gutenberg 区块。
例如转换后可能得到:
Paragraph
Paragraph
Image
Paragraph
Code Block Pro
Paragraph
……
不需要对 Classic 中的每一段内容手工重新创建区块。

2. 自动转换以后必须检查排版
这是实际操作中很重要的一点。
WordPress 能够成功转换成 Gutenberg,不代表正文结构一定正确。
复杂 Classic 转换后,原本独立的:
章节标题
普通段落
1. 第一项
2. 第二项
结论
下一节标题
可能被合并成一个很大的 Paragraph。
也可能出现:
原来的换行消失
编号和正文连在一起
小标题变成普通段落
多个逻辑段落合并
因此:
已经点击“转换为区块”
并不代表人工工作已经完成。

我最终会继续人工整理:
- 段落;
- 标题和小标题;
- 编号;
- 列表;
- 换行;
- 图片位置;
- caption;
- 链接;
- 正文顺序。
直到恢复正常阅读结构。

所以最终判断条件应该是:
Classic 已转换
+
正文 Gutenberg 结构正常
+
排版正常
+
内容没有遗漏
3. SyntaxHighlighter 转换成 Code Block Pro
Gutenberg 正文整理好以后,再处理 SyntaxHighlighter。
正常代码、配置、终端输出等迁移为:
Code Block Pro
最终必须满足:
SyntaxHighlighter = 0
4. 核对 Code Block Pro 语言
原 SyntaxHighlighter 已经明确语言时,使用对应语言,例如:
PHP
JavaScript
Bash
JSON
HTML
CSS
SQL
Python
Nginx
YAML
原区块没有声明语言时:
Plaintext
Code Block Pro 有可能继承上一个代码块使用的语言,因此不能只看转换是否成功,还要逐块核对语言。
5. 保存前检查
每篇文章保存以前至少确认:
Classic / Mixed 内容已经规范化为 Gutenberg
自动转换后的排版已经人工检查
SyntaxHighlighter = 0
Code Block Pro 内容完整
Code Block Pro language 正确
图片、caption、链接没有遗漏
正文内容没有重复
内容顺序没有发生异常
不存在 Gutenberg 损坏区块
这里不要手工生成摘要,也不要修改英文译文。
八、记录人工转换已经完成
全部 20 篇人工处理完成以后,需要从:
awaiting_manual_conversion
进入:
awaiting_readonly_validation
先确认批次变量:
echo "$batch_id"
echo "$csv_file"
然后执行:
read -r -p \
"确认当前批次文章均已完成 Gutenberg 规范化、SyntaxHighlighter 迁移和 Code Block Pro 语言核对,输入 YES 继续:" \
answer
if [ "$answer" != "YES" ]; then
echo "未确认,已停止,没有修改状态。"
else
python3 - "$batch_id" "$csv_file" <<'PY'
import csv
import json
import subprocess
import sys
from pathlib import Path
batch_id = sys.argv[1]
csv_path = Path(sys.argv[2])
state_dir = Path("data/state/history-migration") / batch_id
with csv_path.open(encoding="utf-8-sig", newline="") as f:
rows = list(csv.DictReader(f))
def status_of(post_id):
path = state_dir / f"chinese-{post_id}.json"
if not path.is_file():
raise RuntimeError(f"缺少状态文件:{path}")
return json.loads(path.read_text(encoding="utf-8"))["workflow_status"]
targets = [
row for row in rows
if status_of(int(row["chinese_post_id"]))
== "awaiting_manual_conversion"
]
print(f"批次:{batch_id}")
print(f"等待记录人工转换:{len(targets)}")
print()
success = []
failed = []
for index, row in enumerate(targets, 1):
post_id = row["chinese_post_id"]
syntax_before = row["before_syntaxhighlighter_count"]
cbp_after = row["expected_code_block_pro_count_after"]
result = subprocess.run(
[
sys.executable,
"bin/history-migration.py",
"mark-converted",
"--post-id", post_id,
"--syntax-count-before", syntax_before,
"--cbp-count-after", cbp_after,
"--language-review-confirmed",
"--gutenberg-normalization-confirmed",
],
text=True,
capture_output=True,
check=False,
)
final_status = status_of(int(post_id))
if (
result.returncode == 0
and final_status == "awaiting_readonly_validation"
):
success.append(post_id)
print(f"[{index}/{len(targets)}] 已记录:zh={post_id}")
else:
failed.append(post_id)
output = (result.stderr or result.stdout or "").strip().splitlines()
error = output[-1] if output else "没有错误摘要"
print(
f"[{index}/{len(targets)}] "
f"记录失败:zh={post_id} "
f"error={error}"
)
print()
print("========== 人工转换记录汇总 ==========")
print(f"待记录:{len(targets)}")
print(f"成功:{len(success)}")
print(f"失败:{len(failed)}")
if failed:
print("失败文章:" + ", ".join(failed))
raise SystemExit(1)
PY
fi
正常主要观察:
========== 人工转换记录汇总 ==========
待记录:20
成功:20
失败:0
不再额外输出所有历史批次和完整 show-current,避免真正重要的结果被大量信息淹没。
九、执行生产只读验证
这一阶段负责:
awaiting_readonly_validation
→ ready_for_execution
1. 检查环境变量
for name in WP_ADMIN_COOKIE WP_REST_NONCE
do
if [ -z "${!name-}" ]; then
echo "缺少环境变量:$name"
else
echo "已设置:$name"
fi
done
如果缺少:
read -rsp "粘贴 WP_ADMIN_COOKIE,然后回车:" WP_ADMIN_COOKIE
echo
export WP_ADMIN_COOKIE
read -rsp "粘贴 WP_REST_NONCE,然后回车:" WP_REST_NONCE
echo
export WP_REST_NONCE
2. 批量执行只读验证
第二批实际执行时,20 篇中第一次有 18 篇成功,另外两篇出现:
batch read-only SSH query timed out
它们仍然保持:
awaiting_readonly_validation
随后重新验证,两篇都在第一次重试成功。
因此这里直接加入有限次数自动重试。
missing=0
for name in WP_ADMIN_COOKIE WP_REST_NONCE
do
if [ -z "${!name-}" ]; then
echo "缺少环境变量:$name"
missing=1
fi
done
if [ "$missing" -ne 0 ]; then
echo "已停止,未执行生产只读验证。"
else
python3 - "$batch_id" "$csv_file" <<'PY'
import csv
import json
import subprocess
import sys
import time
from pathlib import Path
batch_id = sys.argv[1]
csv_path = Path(sys.argv[2])
state_dir = Path("data/state/history-migration") / batch_id
with csv_path.open(encoding="utf-8-sig", newline="") as f:
rows = list(csv.DictReader(f))
def status_of(post_id):
path = state_dir / f"chinese-{post_id}.json"
if not path.is_file():
raise RuntimeError(f"缺少状态文件:{path}")
return json.loads(
path.read_text(encoding="utf-8")
)["workflow_status"]
targets = []
unexpected = []
for row in rows:
post_id = int(row["chinese_post_id"])
status = status_of(post_id)
if status == "awaiting_readonly_validation":
targets.append(post_id)
elif status not in ("ready_for_execution", "completed"):
unexpected.append((post_id, status))
print(f"批次:{batch_id}")
print(f"本次待验证:{len(targets)}")
print()
success = []
failed = []
for index, post_id in enumerate(targets, 1):
passed = False
for attempt in range(1, 4):
result = subprocess.run(
[
sys.executable,
"bin/history-migration.py",
"validate-live",
"--post-id",
str(post_id),
],
text=True,
capture_output=True,
check=False,
)
final_status = status_of(post_id)
if (
result.returncode == 0
and final_status == "ready_for_execution"
):
success.append(post_id)
if attempt == 1:
print(
f"[{index}/{len(targets)}] "
f"验证通过:zh={post_id}"
)
else:
print(
f"[{index}/{len(targets)}] "
f"重试通过:zh={post_id} attempts={attempt}"
)
passed = True
break
output = (
result.stderr
or result.stdout
or ""
).strip().splitlines()
error = output[-1] if output else "没有错误摘要"
print(
f"[{index}/{len(targets)}] "
f"第 {attempt}/3 次失败:zh={post_id} "
f"status={final_status} "
f"error={error}"
)
if final_status != "awaiting_readonly_validation":
break
if attempt < 3:
time.sleep(5)
if not passed:
failed.append(post_id)
print()
print("========== 只读验证最终汇总 ==========")
print(f"本次待验证:{len(targets)}")
print(f"本次通过:{len(success)}")
print(f"本次失败:{len(failed)}")
if unexpected:
print(
"非预期状态:"
+ ", ".join(
f"{post_id}:{status}"
for post_id, status in unexpected
)
)
if failed:
print(
"验证失败文章:"
+ ", ".join(map(str, failed))
)
if failed or unexpected:
raise SystemExit(1)
PY
result=$?
echo
if [ "$result" -eq 0 ]; then
echo "========== 验证后的待执行文章 =========="
python3 bin/history-migration.py run-ready \
--batch-id "$batch_id"
else
echo "存在验证失败或非预期状态,暂不进入正式执行。"
fi
fi
正常应看到:
========== 只读验证最终汇总 ==========
本次待验证:20
本次通过:20
本次失败:0
随后:
模式: preview
完整性: ok
selected_count: 20
allowed_count: 20
Mixed 阶段除了原有代码块检查之外,还会确认:
editor_format = gutenberg
classic_outside_blocks = false
SSH 超时和 validation_failed 是两种不同情况
例如第二批遇到:
batch read-only SSH query timed out
而状态仍然是:
awaiting_readonly_validation
这属于生产只读查询偶发超时,可以有限次数重试。
如果真正进入:
validation_failed
则不要继续盲目重试,应检查文章并修复。
修复后再执行:
python3 bin/history-migration.py validate-live \
--post-id 文章ID \
--refresh
十、批量生成摘要并覆盖英文译文
正式执行需要:
WP_ADMIN_COOKIE
WP_REST_NONCE
ZHIPU_API_KEY
检查:
for name in WP_ADMIN_COOKIE WP_REST_NONCE ZHIPU_API_KEY
do
if [ -z "${!name-}" ]; then
echo "缺少环境变量:$name"
else
echo "已设置:$name"
fi
done
如果只缺 API Key:
read -rsp "粘贴 ZHIPU_API_KEY,然后回车:" ZHIPU_API_KEY
echo
export ZHIPU_API_KEY
全部准备好以后:
python3 bin/history-migration.py run-ready \
--batch-id "$batch_id" \
--execute
执行期间会实时显示:
[1/20] 开始处理:zh=...
[1/20] 处理完成:zh=...
[2/20] 开始处理:zh=...
……
单篇出现偶发错误时,系统会有限次数重试,而不是立即停止整个批次。
第一批实际遇到过:
GLM HTTP 400
英文覆盖翻译 HTTP 500
Polylang SSH timeout
均在后续有限重试中恢复。
这里不需要人工看到一次 HTTP 错误就立即中止整个批次。
先让整个批次执行结束,再根据最终状态判断。
十一、批次结束后的完整检查
正式执行完成以后:
python3 bin/history-migration.py run-ready \
--batch-id "$batch_id"
python3 bin/history-migration.py status
python3 bin/history-migration.py summary
真正完成时应该看到:
selected_count: 0
allowed_count: 0
同时批次状态必须满足:
completed = 批次总数
remaining = 0
translation_failed = 0
validation_failed = 0
blocked = 0
integrity = ok
next_action = 当前批次已完成
最终全局状态可以进一步出现:
最新未完成批次: 无
建议创建下一批: True
建议: all batches complete
selected_count=0 不等于批次完成
第二批执行完成后,我实际看到:
模式: preview
完整性: ok
selected_count: 0
allowed_count: 0
但 status 同时显示:
completed=19
translation_failed=1
remaining=1
下一步: 可以 resume
也就是说:
selected_count=0
只说明当前没有可以直接由 run-ready 执行的文章。
它并不能证明:
整个批次已经完成
最终必须同时检查:
completed
remaining
失败状态
next_action
十二、异常状态与恢复
日常不要把所有异常都统一理解成“再跑一次”。
不同状态应该走不同恢复路径。
1. awaiting_readonly_validation + SSH timeout
例如:
batch read-only SSH query timed out
并且状态仍然是:
awaiting_readonly_validation
说明只是只读查询没有成功完成。
第九节已经加入有限重试逻辑,一般无需额外人工处理。
2. validation_failed
说明已经形成明确验证失败。
先检查并修复 WordPress 中文文章。
然后:
python3 bin/history-migration.py validate-live \
--post-id 文章ID \
--refresh
3. translation_failed
第二批最终有一篇:
zh=9147
status=translation_failed
当时整个批次:
completed=19
translation_failed=1
remaining=1
next_action=可以 resume
先查看当前批次:
python3 bin/history-migration.py show-current
定位失败文章以后:
python3 bin/history-migration.py resume \
--post-id 9147 \
--execute
实际结果:
模式: execute
完整性: ok
selected_count: 1
- zh=9147 result=completed category=completed returncode=0 error=
随后再次检查,最终:
最新未完成批次: 无
建议创建下一批: True
建议: all batches complete
因此:
translation_failed
并不需要重新跑整个批次。
只需要针对失败文章 resume。
4. ready_for_execution
如果文章仍然是:
ready_for_execution
先预览:
python3 bin/history-migration.py run-ready \
--batch-id "$batch_id"
确认以后:
python3 bin/history-migration.py run-ready \
--batch-id "$batch_id" \
--execute
5. blocked
如果出现:
blocked
先检查:
last_failure
execution evidence
生产实际状态
不要直接重复执行。
十三、每天真正需要做的固定流程
经过两批实际验证以后,整个日常操作已经可以压缩成下面这套固定流程。
第一步:检查状态
cd ~/code/wordpress-ai-excerpt-backfill
python3 bin/history-migration.py status
python3 bin/history-migration.py summary
确认:
最新未完成批次: 无
建议创建下一批: True
第二步:创建新的 Mixed 固定批次
最多:
20 篇
排序:
published_at DESC
chinese_post_id DESC
第三步:设置当天两个变量
batch_id="mixed-syntaxhighlighter-YYYYMMDD-01"
csv_file="data/analysis/mixed-syntaxhighlighter-migration-batch-YYYYMMDD-01.csv"
第四步:输出后台地址
一次获得当天 20 篇中文文章的后台编辑地址。
第五步:逐篇人工处理
Classic / Mixed → Gutenberg
↓
检查并修复自动转换后的排版
↓
SyntaxHighlighter → Code Block Pro
↓
核对 Code Block Pro language
↓
保存
第六步:批量记录人工完成
目标:
待记录:20
成功:20
失败:0
第七步:生产只读验证
程序自动对仍然处于:
awaiting_readonly_validation
的文章进行有限重试。
最终目标:
本次通过:20
本次失败:0
以及:
selected_count: 20
allowed_count: 20
第八步:正式执行
检查:
WP_ADMIN_COOKIE
WP_REST_NONCE
ZHIPU_API_KEY
然后:
python3 bin/history-migration.py run-ready \
--batch-id "$batch_id" \
--execute
第九步:最终检查
python3 bin/history-migration.py run-ready \
--batch-id "$batch_id"
python3 bin/history-migration.py status
python3 bin/history-migration.py summary
最终必须确认:
completed = 批次总数
remaining = 0
无失败状态
integrity = ok
只有达到这些条件,才能开始下一批。
十四、连续两批实际验证结果
为了确认这套流程不是只在理论上可行,我连续处理了两批真实历史文章。
第一批
批次:
mixed-syntaxhighlighter-20260729-01
共:
20 篇
结果:
人工 Gutenberg 规范化:20/20
mark-converted:20/20
生产只读验证:20/20
run-ready:
selected_count=20
allowed_count=20
最终:
completed=20
正式执行期间出现过:
GLM HTTP 400
英文覆盖翻译 HTTP 500
Polylang SSH timeout
但均被有限重试恢复。
第二批
批次:
mixed-syntaxhighlighter-20260730-01
同样:
20 篇
生产只读验证第一次:
通过:18
失败:2
两篇失败原因都是:
batch read-only SSH query timed out
并且仍然停留在:
awaiting_readonly_validation
有限重试以后:
待重试:2
成功:2
失败:0
随后:
selected_count: 20
allowed_count: 20
正式执行结束以后:
completed=19
translation_failed=1
remaining=1
针对唯一失败文章执行:
resume
成功以后最终:
最新未完成批次: 无
建议创建下一批: True
建议: all batches complete
因此两批共:
40 篇
最终全部完成。
总结
此前 Gutenberg + SyntaxHighlighter 阶段完成以后,站内仍然存在大量 SyntaxHighlighter,并不是此前流程漏掉了文章。
真正原因是这些文章属于:
Mixed Gutenberg / Classic + SyntaxHighlighter
因此不能直接套用只处理 Gutenberg 的候选规则。
但进入实际迁移以后,我并没有重新设计整套系统。
原来的:
fixed batch
→ manual conversion
→ mark-converted
→ readonly validation
→ excerpt
→ English overwrite translation
→ completed
仍然可以继续使用。
Mixed 阶段真正新增的核心内容只有:
Classic / Mixed → Gutenberg
以及人工确认:
--gutenberg-normalization-confirmed
生产验证再额外确认:
editor_format = gutenberg
classic_outside_blocks = false
连续两批、40 篇真实历史文章运行以后,除了正常主流程,也实际验证了:
只读 SSH 超时后的有限重试
HTTP 400 / 500 等偶发错误自动重试
translation_failed 单篇 resume
selected_count=0 时继续检查 remaining 和失败状态
因此现在这套流程已经从一次性的迁移测试,变成了可以重复执行的日常 SOP。
后续只需要每天继续:
创建 20 篇固定批次
→ 人工 Gutenberg 规范化
→ SyntaxHighlighter 转 Code Block Pro
→ 生产验证
→ 中文摘要
→ 英文覆盖翻译
→ 检查 completed
直到这一批 Mixed 历史文章全部迁移完成。
需要长期技术维护或远程问题排查?
我是拥有 15+ 年经验的 PHP / Go 后端工程师,长期关注已有系统维护、Bug 修复、性能优化、服务器排查、WordPress 网站维护和小功能迭代。
如果你的项目遇到以下情况,可以先从一次小问题排查开始合作:
- ✅ PHP / Laravel / Yii2 老项目无人维护
- ✅ Go / Gin 后端接口需要排查或优化
- ✅ WordPress 网站访问慢、报错或插件冲突
- ✅ Nginx / MySQL / Redis / Linux 服务器异常
- ✅ CDN / Cloudflare / DNS / HTTPS 配置问题
- ✅ 需要长期远程技术支持或兼职维护
更多介绍请查看:关于我 & 合作
微信:13980074657
邮箱:shuijingwanwq@gmail.com
Telegram:@shuijingwan
GitHub:https://github.com/shuijingwan


发表回复