feat(all): 物理正确性五重硬门槛、输入文件结构化与 fort.55 错位修复、conv 诊断 DB 化与阶段归因修复、ORELAX 收敛修复与导入工具下线

物理正确性校验体系(common/conv_check.rs +494 行)
- 新增 5 类硬门槛:能量守恒(.6)、温度结构(.7)、emflux 积分校验(.emflux,含全 NaN 判失败)、假收敛排查(itek 轨迹首末比)、b 因子合理性(.bfac)
- runner 在 TLUSTY 阶段结束后执行全部校验,任一失败判 final_converged=false
- GridConfig 新增 8 个可配阈值,经 scheduler→executor→runner 全链路透传

输入文件配置结构化重构(config.rs +1453 行)
- TlustyInput 拆为 dot5/nst 分层结构,字段名严格映射 tlusty208.f READ 语句;SynspecInput 重构为 9 个 Fort55Line 子结构体
- 移除 ChainStep.metals 字段,元素集改由 dot5.atoms/ions 显式声明(gen_input5/nst_writer 同步重写为三源融合 / 分层覆盖)
- fort.55 修复行结构 bug:补全分子表行(7→9 行),IDSTD 50→0 错位修正(影响全部光谱线强归一化,需重算 SYNSPEC 阶段)

conv 诊断 DB 化与阶段归因修复(server)
- 单点详情 conv 面板从磁盘 conv.json 改读 DB grid_points.summary_json;grid_points 新增 summary_json/last_elapsed_sec 两列(旧库幂等 ALTER)
- record_task_report 阶段归因列加 CASE 守卫 + clear_synspec 对称处理,修复 synspec-only/TLUSTY-only 重跑污染统计
- 新增 summary_merge.rs 点级增量合并,避免重跑覆盖诊断字段

收敛性 ORELAX 修复与 seed_chain 可配(sdB_cno.yaml + node)
- nl 阶段加 orelax=0.5、seed_nc 加 orelax=0.3,阻尼中温区 relc 振荡发散
- seed_chain 块可配,executor 优先采用用户配置而非内置默认链

导入工具下线
- 删除 import_results 客户端工具及 Windows 推送脚本;移除 /admin/import_seed 端点
- 改为服务端临时 migrate_conv 端点(扫 conv.json 增量合并入库,迁移后可删)

文档与分析
- 新增 1305 失败点根因分析、fort.14 全 NaN 物理含义分析两份深度文档
- spectrum_correctness_analysis 两次修订标注已修复项;fetch_results.sh 修 trap RETURN 的 set -u 报错
This commit is contained in:
fmq
2026-08-09 12:09:48 +08:00
parent d16b3d3cdc
commit 43b82b1ae2
45 changed files with 6059 additions and 3184 deletions
+2 -2
View File
@@ -254,12 +254,12 @@ batch_deploy_profiles() {
mkdir -p data
if [ "$need_server" = true ]; then
server_tar="data/.batch_dcts-server.tar.gz"
echo " -> [Docker Compose] 导出 server 镜像归档包: ${server_tar} (仅打包 1 次)..."
echo " -> [Docker Compose] 导出 server 镜像归档包: ${server_tar}..."
docker save dcts-server:latest | gzip > "${server_tar}"
fi
if [ "$need_node" = true ]; then
node_tar="data/.batch_dcts-node.tar.gz"
echo " -> [Docker Compose] 导出 node 镜像归档包: ${node_tar} (仅打包 1 次)..."
echo " -> [Docker Compose] 导出 node 镜像归档包: ${node_tar}..."
docker save dcts-node:latest | gzip > "${node_tar}"
fi
fi
+5 -3
View File
@@ -207,9 +207,11 @@ sync_dir() { # $1=user $2=ip $3=port $4=远端绝对目录 $5=本地目标
return 0
fi
# scp 原子写入:先落地到 .partial 临时目录,成功后同文件系统原子 mv 为最终目录名。
# 中途失败残留的 .partial 由下方 scp_dir 的 RETURN 时清理钩子统一兜底,
# 避免下次因"目录已存在"误判为已同步而跳过、留下不完整子目录
trap 'rm -rf "${dest}"/.*.partial 2>/dev/null || true' RETURN
# 开拷前先清理可能的历史 .partial 残留(上次中断留下的半成品),scp_dir 自身失败
# 时也会清理各自的 .partial;这样避免下次因"目录已存在"误判为已同步而漏传
# (原先用 trap RETURN 兜底,但 RETURN 陷阱会被外层 backup_node 继承,函数返回时
# dest 已出作用域,触发 set -u 的"未绑定变量"报错,故改为前置清理。)
rm -rf "${dest}"/.*.partial 2>/dev/null || true
while IFS= read -r d; do
[ -n "$d" ] || continue
echo -e " ${YELLOW}[→]${NC} scp 新增: ${d}"
-328
View File
@@ -1,328 +0,0 @@
#!/usr/bin/env bash
# ==============================================================================
# push_import_results.sh — 推送 import_results 工具到 Windows 计算节点并编译
# ==============================================================================
# 把 import_results 工具的源码推送到 deploy.env.d/<profile> 指定的 Windows 节点,
# 在该节点原生编译(Windows 上 cargo build),并打印确切的运行命令。
#
# 设计前提:
# - 旧版 Python run_grid.py 的结果在 Windows 机的 E:/fmq/grid(工具必须在该机运行才能读到)
# - 该机已部署 nodeDocker Compose),data 目录在 E:/fmq/dcts/data
# - DCTS 服务端地址来自 profile 的 DCTS_SERVER_URL
# - Windows 机需已装 Rust 工具链(cargo/rustc);脚本会自检并给出安装提示
#
# 用法:
# ./scripts/push_import_results.sh # 默认用 node-dckj-win-01.env
# ./scripts/push_import_results.sh -f deploy.env.d/other-node.env # 指定其他 profile
# ./scripts/push_import_results.sh --no-build # 只推源码不编译(远端已有缓存时加速)
# ./scripts/push_import_results.sh --archive-dir E:/fmq/dcts/data/result # 覆盖归档目录
#
# 运行(推完编译后,在 Windows 机上执行,或本脚本末尾会打印):
# E:\fmq\dcts-src\target\release\import_results.exe \
# -d E:/fmq/grid -c E:/fmq/dcts-src/workflows/sdB_cno.yaml \
# -r E:/fmq/dcts/data/result -s http://100.66.1.2:8090 \
# -w sdB_cno -t <admin_token>
# ==============================================================================
set -eo pipefail
GREEN='\033[0;32m'
BLUE='\033[0;34m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORK_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
cd "${WORK_DIR}"
# 默认值(可被 profile 覆盖)
PROFILE_FILE="deploy.env.d/node-dckj-win-01.env"
REMOTE_USER="fmq"
REMOTE_IP="192.168.6.102"
REMOTE_PORT="22"
REMOTE_SRC_DIR="E:/fmq/dcts-src"
DCTS_SERVER_URL="http://100.66.1.2:8090"
RESULT_DIR="" # 空 → 用 profile 推导(默认 E:/fmq/dcts/data/result
DO_BUILD=true
# SSH ControlMaster 复用目录(与 deploy.sh 一致,便于共享连接)
SSH_CONTROL_DIR="${SSH_CONTROL_DIR:-$HOME/.ssh/cm}"
# =============================================================================
# 解析命令行参数
# =============================================================================
while [[ $# -gt 0 ]]; do
case "$1" in
-f|--profile) PROFILE_FILE="$2"; shift 2 ;;
--no-build) DO_BUILD=false; shift ;;
--archive-dir) RESULT_DIR="$2"; shift 2 ;;
-h|--help)
sed -n '2,30p' "$0"
exit 0 ;;
*) echo -e "${RED}未知参数: $1${NC}"; exit 1 ;;
esac
done
# =============================================================================
# 加载 profile(SSH 连接信息 + 服务端地址)
# =============================================================================
if [ ! -f "$PROFILE_FILE" ]; then
echo -e "${RED}错误: 找不到 profile '${PROFILE_FILE}'${NC}"
exit 1
fi
echo -e "${CYAN}[i] 加载 profile: ${PROFILE_FILE}${NC}"
# shellcheck disable=SC1090
set -a; source "$PROFILE_FILE"; set +a
# profile 提供的变量优先(REMOTE_* / DCTS_SERVER_URL
[ -n "${REMOTE_USER:-}" ] && REMOTE_USER="${REMOTE_USER}"
[ -n "${REMOTE_IP:-}" ] && REMOTE_IP="${REMOTE_IP}"
[ -n "${REMOTE_PORT:-}" ] && REMOTE_PORT="${REMOTE_PORT}"
# 源码目录默认放到部署目录旁(E:/fmq/dcts-src),与运行中的 nodeE:/fmq/dcts)隔离
[ -n "${REMOTE_DIR:-}" ] && REMOTE_SRC_DIR="${REMOTE_DIR}-src"
# 结果目录:命令行 > 参数 > 默认(部署目录下的 data/resultnode 的 DCTS_RESULT_DIR 卷映射到此)
if [ -z "${RESULT_DIR}" ]; then
RESULT_DIR="${REMOTE_DIR}/data/result"
fi
echo -e "${CYAN}📢 目标节点: ${YELLOW}${REMOTE_USER}@${REMOTE_IP}:${REMOTE_PORT}${NC}"
echo -e "${CYAN}📢 源码推送: ${YELLOW}${REMOTE_SRC_DIR}${NC}"
echo -e "${CYAN}📢 结果目录: ${YELLOW}${RESULT_DIR}${NC}"
echo -e "${CYAN}📢 服务端: ${YELLOW}${DCTS_SERVER_URL}${NC}"
echo ""
# =============================================================================
# 建立 SSH ControlMaster 连接复用(一次性输密码,后续 scp/ssh 复用)
# =============================================================================
mkdir -p "${SSH_CONTROL_DIR}"
SOCK="${SSH_CONTROL_DIR}/${REMOTE_USER}@${REMOTE_IP}:${REMOTE_PORT}"
SSH_OPTS="-o ControlMaster=auto -o ControlPath=${SOCK} -o ControlPersist=1800 -o StrictHostKeyChecking=no"
if ssh -p "${REMOTE_PORT}" -o ControlPath="${SOCK}" -O check "${REMOTE_USER}@${REMOTE_IP}" >/dev/null 2>&1; then
echo -e "${GREEN}[√] 复用已有 SSH 连接${NC}"
else
echo -e "${CYAN}=== 建立 SSH 连接(请输入 ${REMOTE_USER}@${REMOTE_IP} 的密码)===${NC}"
if ! ssh -p "${REMOTE_PORT}" ${SSH_OPTS} -fN "${REMOTE_USER}@${REMOTE_IP}"; then
echo -e "${RED}SSH 连接失败${NC}"
exit 1
fi
echo -e "${GREEN}[√] SSH 连接已建立${NC}"
fi
trap 'ssh -p "${REMOTE_PORT}" -o ControlPath="${SOCK}" "${REMOTE_USER}@${REMOTE_IP}" -O exit 2>/dev/null || true' EXIT
# =============================================================================
# 自检远端 Rust 工具链
# =============================================================================
echo -e "\n${CYAN}=== 检查远端 Rust 工具链 ===${NC}"
HAS_CARGO=$(ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
"powershell -NoProfile -Command \"if (Get-Command cargo -ErrorAction SilentlyContinue) { 'yes' } else { 'no' }\"" 2>/dev/null | tr -d '[:space:]')
if [ "$HAS_CARGO" != "yes" ]; then
echo -e "${RED}[✗] 远端 ${REMOTE_IP} 未检测到 cargo/rustc${NC}"
echo -e "${YELLOW}请先在该 Windows 机上安装 Rust 工具链:${NC}"
echo -e " 方式1PowerShell,需联网): winget install Rustlang.Rustup"
echo -e " 方式2(浏览器): 访问 https://rustup.rs 下载 rustup-init.exe 并运行"
echo -e " 安装后重开 PowerShell 再跑本脚本。"
exit 1
fi
echo -e "${GREEN}[√] 远端已安装 Rust 工具链${NC}"
# =============================================================================
# 同步源码到远端(仅 git 跟踪文件,排除 target/data/assets 等大目录)
# =============================================================================
echo -e "\n${CYAN}=== 同步源码到 ${REMOTE_SRC_DIR} ===${NC}"
# 远端先建目录
ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
"powershell -NoProfile -Command \"New-Item -ItemType Directory -Force -Path '${REMOTE_SRC_DIR}' | Out-Null\""
# 单文件 scp 辅助:失败时打印诊断并退出(不静默吞错)。
# Windows OpenSSH scp 对带正斜杠的远端路径需用 ':' 分隔主机,路径部分正斜杠 OK。
scp_one() {
local src="$1" dst="$2"
if ! scp -P "${REMOTE_PORT}" ${SSH_OPTS} "$src" "${REMOTE_USER}@${REMOTE_IP}:${dst}"; then
echo -e "${RED}[✗] 传输失败: ${src} -> ${dst}${NC}"
exit 1
fi
}
# 递归目录 scp:先建/清远端目标目录再传,避免 Windows scp -r 创建子目录失败。
scp_dir() {
local sub="$1"
[ -d "$sub" ] || return 0
# 远端先删除旧目录并重建空目录(确保 scp -r 能写入)
ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
"powershell -NoProfile -Command \"Remove-Item -Recurse -Force '${REMOTE_SRC_DIR}/${sub}' -ErrorAction SilentlyContinue; New-Item -ItemType Directory -Force -Path '${REMOTE_SRC_DIR}/${sub}' | Out-Null\"" || true
echo -e " -> ${sub}/"
if ! scp -P "${REMOTE_PORT}" ${SSH_OPTS} -r "$sub" "${REMOTE_USER}@${REMOTE_IP}:${REMOTE_SRC_DIR}/"; then
echo -e "${RED}[✗] 传输目录失败: ${sub}${NC}"
echo -e "${YELLOW} 若反复失败,可在 Windows 机上手动 git clone 后仅同步增量,或检查磁盘空间/权限${NC}"
exit 1
fi
}
echo -e "${CYAN} -> 传输顶层文件 + crates/tools/workflows ...${NC}"
scp_one Cargo.toml "${REMOTE_SRC_DIR}/"
[ -f Cargo.lock ] && scp_one Cargo.lock "${REMOTE_SRC_DIR}/"
# import_results 依赖 common crate,二者都要同步;workflows 含配置(决定命名精度)。
scp_dir crates
scp_dir tools
scp_dir workflows
echo -e "${GREEN}[√] 源码同步完成${NC}"
# =============================================================================
# 远端编译 import_resultsrelease
# =============================================================================
if [ "$DO_BUILD" = true ]; then
echo -e "\n${CYAN}=== 远端编译 import_results (release) ===${NC}"
echo -e "${YELLOW} (首次编译会拉 crate 依赖,可能耗时数分钟,请耐心等待...)${NC}"
# 用 ssh 透传的退出码判断成败,不依赖输出最后一行文本:
# cargo 把 Compiling/Finished 进度写到 stderr,与 PowerShell 打印 $LASTEXITCODE 的 stdout
# 在 2>&1 合并后顺序会交错,tail -1 可能抓到 "Finished ..." 而非 "0",导致把成功误判成失败。
# 这里让 powershell `exit $LASTEXITCODE` → ssh 透传 → ${PIPESTATUS[0]} 取管道首段退出码。
# 暂时关掉 errexit/pipefail:否则 cargo 失败时管道整体非零会先触发 set -e 退出,丢失自定义提示。
set +e +o pipefail
ssh -p "${REMOTE_PORT}" ${SSH_OPTS} "${REMOTE_USER}@${REMOTE_IP}" \
"powershell -NoProfile -Command \"Set-Location '${REMOTE_SRC_DIR}'; cargo build --release -p import_results; exit \$LASTEXITCODE\"" 2>&1 | tee /tmp/import_build.log | tail -20
BUILD_RC=${PIPESTATUS[0]}
set -e -o pipefail
rm -f /tmp/import_build.log
if [ "$BUILD_RC" -ne 0 ]; then
echo -e "${RED}[✗] 远端编译失败 (exit=${BUILD_RC})${NC}"
exit 1
fi
echo -e "${GREEN}[√] 编译成功${NC}"
else
echo -e "${YELLOW}[i] 跳过编译(--no-build${NC}"
fi
# =============================================================================
# 在远端生成 run_import.batWindows 批处理),用户双击或在 cmd 里运行即可,无需手敲命令
# =============================================================================
# 交互式 .bat 各参数的默认值(来自 profile 推导,用户双击时可逐项覆盖,回车即用默认)
EXE_PATH_WIN=$(echo "${REMOTE_SRC_DIR}/target/release/import_results.exe" | sed 's|/|\\|g')
CONFIG_PATH="${REMOTE_SRC_DIR}/workflows/sdB_cno.yaml"
WORK_DIR_WIN=$(echo "${REMOTE_SRC_DIR}" | sed 's|/|\\|g')
GRID_DIR="E:/fmq/grid" # 旧版 run_grid.py 的 results 根目录
WORKFLOW_NAME="sdB_cno" # 工作流名,须匹配将来在 Dashboard 创建/启动的工作流
# .bat 内容:用 quoted heredoc 避免反引号/%/$ 被 bash 解释,占位符稍后替换。
# 每个参数都用 set /p 交互读取,空回车则采用 [方括号] 里的默认值;token 空回车则只迁移 result 不入库。
BAT_CONTENT=$(cat <<'BATEOF'
@echo off
chcp 65001 >nul
setlocal enabledelayedexpansion
cd /d "__WORK_DIR__"
echo ============================================================
echo DCTS 历史计算结果导入工具
echo ============================================================
echo 以下每项直接回车即采用 [方括号] 内的默认值
echo ============================================================
echo.
set /p DATA_DIR="旧数据目录 (run_grid.py results 根) [__DATA_DIR__]: "
if "!DATA_DIR!"=="" set "DATA_DIR=__DATA_DIR__"
set /p CONFIG="workflow 配置文件 [__CONFIG__]: "
if "!CONFIG!"=="" set "CONFIG=__CONFIG__"
set /p RESULT_DIR="结果目录 [__RESULT__]: "
if "!RESULT_DIR!"=="" set "RESULT_DIR=__RESULT__"
set /p SERVER="服务端地址 [__SERVER__]: "
if "!SERVER!"=="" set "SERVER=__SERVER__"
set /p WORKFLOW="工作流名 -w (须匹配将来启动的工作流) [__WORKFLOW__]: "
if "!WORKFLOW!"=="" set "WORKFLOW=__WORKFLOW__"
set /p ADMIN_TOKEN="Admin Token (直接回车则只迁移 result 不入库): "
echo.
echo ============================================================
echo 即将执行,请确认:
echo 旧数据目录 : !DATA_DIR!
echo workflow : !CONFIG!
echo 结果目录 : !RESULT_DIR!
echo 服务端 : !SERVER!
echo 工作流名 : !WORKFLOW!
echo Token : !ADMIN_TOKEN!
echo ============================================================
echo.
if "!ADMIN_TOKEN!"=="" (
echo [i] 未输入 token,仅迁移产物树到 result(入库会因无鉴权失败,但不阻断迁移)
"__EXE__" -d "!DATA_DIR!" -c "!CONFIG!" -r "!RESULT_DIR!" -s "!SERVER!" -w "!WORKFLOW!"
) else (
"__EXE__" -d "!DATA_DIR!" -c "!CONFIG!" -r "!RESULT_DIR!" -s "!SERVER!" -w "!WORKFLOW!" -t "!ADMIN_TOKEN!"
)
echo.
echo ============================================================
echo 执行结束。按任意键退出...
pause >nul
BATEOF
)
BAT_CONTENT="${BAT_CONTENT//__WORK_DIR__/${WORK_DIR_WIN}}"
BAT_CONTENT="${BAT_CONTENT//__EXE__/${EXE_PATH_WIN}}"
BAT_CONTENT="${BAT_CONTENT//__DATA_DIR__/${GRID_DIR}}"
BAT_CONTENT="${BAT_CONTENT//__CONFIG__/${CONFIG_PATH}}"
BAT_CONTENT="${BAT_CONTENT//__RESULT__/${RESULT_DIR}}"
BAT_CONTENT="${BAT_CONTENT//__SERVER__/${DCTS_SERVER_URL}}"
BAT_CONTENT="${BAT_CONTENT//__WORKFLOW__/${WORKFLOW_NAME}}"
# 本地写临时 .batCRLF 行尾 + UTF-8 with BOM),再用 scp 上传——
# 绕开 “ssh 管道 + powershell $input 自动变量 + stdin 双重消费” 导致的空文件问题。
BAT_REMOTE_PATH="${REMOTE_SRC_DIR}/run_import.bat"
# 用固定可预测的临时路径,便于在成功/失败两条出口都显式清理(脚本头部已有 SSH ControlMaster 的 EXIT trap
# 这里不再叠加 trap 以免覆盖它)。
TMP_BAT="${TMPDIR:-/tmp}/dcts_run_import.$$.bat"
# 关键:必须写成 UTF-8 **带 BOM**EF BB BF)。
# 原因:chcp 65001 只改控制台显示编码,不改 cmd.exe 解析 .bat 文件的编码。无 BOM 时 cmd 按系统 ANSI(中文
# Windows 为 GBK)逐字节解析 UTF-8 文件,中文多字节序列错位会导致 echo/if 解析错乱——
# 报 "'数据目录' is not recognized as an internal or external command" 且 if 块边界被破坏,
# 使 import_results.exe 根本不执行。带 BOM 后现代 cmdWin10 1903+)会按 UTF-8 正确解析整个文件。
# CRLF 行尾:命令替换 $(...) 会吞掉末尾换行,直接 printf '%s\r\n' 只在末尾加一个 \r
# 故先按 LF 展开再用 sed 给每行补 \r,确保每行都是 CRLF。
{
printf '\xEF\xBB\xBF' # UTF-8 BOM
printf '%s\n' "$BAT_CONTENT" | sed 's/$/\r/'
} > "$TMP_BAT"
echo -e "\n${CYAN}=== 上传 run_import.bat 到远端 ===${NC}"
if scp -P "${REMOTE_PORT}" ${SSH_OPTS} "$TMP_BAT" "${REMOTE_USER}@${REMOTE_IP}:${BAT_REMOTE_PATH}"; then
echo -e "${GREEN}[√] 已生成: ${BAT_REMOTE_PATH}${NC}"
else
echo -e "${RED}[✗] 上传 run_import.bat 失败${NC}"
rm -f "$TMP_BAT"
exit 1
fi
rm -f "$TMP_BAT"
# =============================================================================
# 输出最终提示
# =============================================================================
echo ""
echo -e "${BLUE}==============================================================${NC}"
echo -e "${GREEN}🎉 推送 + 编译完成!${NC}"
echo -e "${BLUE}==============================================================${NC}"
echo ""
echo -e "${CYAN}在 Windows 机 ${REMOTE_IP} 上启动导入(任选其一):${NC}"
echo ""
echo -e " 方式1(推荐):双击运行 ${YELLOW}${WORK_DIR_WIN}\\run_import.bat${NC}"
echo -e " 会逐项交互提示:旧数据目录 / workflow 配置 / 结果目录 / 服务端 / 工作流名 / Token"
echo -e " 每项直接回车即采用 [方括号] 内的默认值;Token 空回车则只迁移 result 不入库"
echo ""
echo -e " 方式2cmd): cd /d ${WORK_DIR_WIN} 然后 run_import.bat"
echo ""
echo -e "${YELLOW}参数默认值(双击时可逐项覆盖):${NC}"
echo -e " -d ${GRID_DIR} 旧版 Python run_grid.py 的 results 根目录"
echo -e " -c ${CONFIG_PATH}"
echo -e " -r ${RESULT_DIR} 结果写入目标(node 容器卷映射处)"
echo -e " -s ${DCTS_SERVER_URL} 服务端地址(来自 profile)"
echo -e " -w ${WORKFLOW_NAME} 工作流名(必须等于将来要启动的工作流名)"
echo ""
echo -e "${YELLOW}⚠️ 注意:${NC}"
echo -e " - -w sdB_cno 必须匹配将来在 Dashboard 创建/启动的工作流名,否则 converged 标记被隔离看不到、导致重算"
echo -e " - 工具可重复运行(DB 幂等 + result 原子覆盖),网络中断后重跑 run_import.bat 即可补齐"
echo ""