#!/usr/bin/env python3 """ 步骤 5:验证迁移结果 在【迁移目标机】上执行本脚本 配置项请修改 migration_config.py """ import os import json import sys sys.path.insert(0, os.path.dirname(__file__)) from migration_config import ( OLD_IP, DST_CODE_DIR, DST_FILES_DIR, NEW_BASE_URL, NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD ) from neo4j import GraphDatabase URL_CHECK_SAMPLE = 5 PASS = "\033[92m[PASS]\033[0m" FAIL = "\033[91m[FAIL]\033[0m" WARN = "\033[93m[WARN]\033[0m" def check_files(): print("\n--- 1. 关键文件检查 ---") checks = [ (DST_FILES_DIR, "原始文档目录"), (f"{DST_FILES_DIR}/uploads_v1", "高亮 PDF 目录"), (f"{DST_CODE_DIR}/kg_output", "抽取结果 JSON 目录"), (f"{DST_CODE_DIR}/kg_snapshot.json", "全量快照 kg_snapshot.json"), (f"{DST_CODE_DIR}/entity_registry.json", "实体注册表"), (f"{DST_CODE_DIR}/app.py", "主程序 app.py"), (f"{DST_CODE_DIR}/config.py", "配置文件 config.py"), ] all_ok = True for path, desc in checks: if os.path.exists(path): if os.path.isdir(path): count = len(os.listdir(path)) print(f" {PASS} {desc}: {path} ({count} 个文件)") else: size = os.path.getsize(path) / 1024 / 1024 print(f" {PASS} {desc}: {path} ({size:.1f} MB)") else: print(f" {FAIL} 缺失: {desc}: {path}") all_ok = False return all_ok def check_neo4j(): print("\n--- 2. Neo4j 图谱检查 ---") try: driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD)) with driver.session() as session: node_count = session.run("MATCH (n) RETURN count(n) AS c").single()["c"] rel_count = session.run("MATCH ()-[r]->() RETURN count(r) AS c").single()["c"] ks_count = session.run( "MATCH (n) WHERE n.knowledge_source IS NOT NULL RETURN count(n) AS c" ).single()["c"] driver.close() print(f" {PASS} Neo4j 连接正常") print(f" {PASS} 节点数: {node_count} 关系数: {rel_count} 含溯源节点: {ks_count}") return True, ks_count except Exception as e: print(f" {FAIL} Neo4j 连接失败: {e}") return False, 0 def check_old_ip(neo4j_ok, ks_count): print(f"\n--- 3. 旧 IP 残留检查({OLD_IP})---") config_files = ["config.py", "batch_kg_build.py", "app.py"] residual_found = False for fname in config_files: fpath = os.path.join(DST_CODE_DIR, fname) if not os.path.exists(fpath): continue with open(fpath, "r", encoding="utf-8", errors="ignore") as f: content = f.read() lines = [i+1 for i, l in enumerate(content.splitlines()) if OLD_IP in l] if lines: print(f" {WARN} {fname} 第 {lines} 行仍含旧 IP") residual_found = True if neo4j_ok and ks_count > 0: try: driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD)) with driver.session() as session: result = session.run( f"MATCH (n) WHERE n.knowledge_source CONTAINS '{OLD_IP}' " "RETURN count(n) AS c" ) old_url_count = result.single()["c"] driver.close() if old_url_count > 0: print(f" {WARN} Neo4j 中仍有 {old_url_count} 个节点含旧 URL,请重新执行步骤 3") residual_found = True else: print(f" {PASS} Neo4j 中无旧 IP 残留") except Exception as e: print(f" {WARN} 无法检查 Neo4j 中的旧 URL: {e}") if not residual_found: print(f" {PASS} 配置文件中无旧 IP 残留") def check_urls(ks_count): print(f"\n--- 4. 溯源 URL 可访问性抽查(抽查 {URL_CHECK_SAMPLE} 个)---") if ks_count == 0: print(f" {WARN} Neo4j 不可用,跳过 URL 检查") return try: driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD)) with driver.session() as session: result = session.run( "MATCH (n) WHERE n.knowledge_source IS NOT NULL " f"RETURN n.名称 AS name, n.knowledge_source AS ks LIMIT {URL_CHECK_SAMPLE}" ) records = list(result) driver.close() except Exception as e: print(f" {WARN} 无法读取 Neo4j 节点: {e}") return for record in records: name = record["name"] ks_raw = record["ks"] try: ks_list = json.loads(ks_raw) if isinstance(ks_raw, str) else ks_raw url = ks_list[0].get("url", "") if ks_list else "" if not url: print(f" {WARN} 节点 {name}: knowledge_source 无 url 字段") continue filename = url.split("/upload/")[-1].split("#")[0] local_path = os.path.join(DST_FILES_DIR, "uploads_v1", filename) if os.path.exists(local_path): print(f" {PASS} {name}: 高亮 PDF 存在 → {filename}") else: print(f" {FAIL} {name}: 高亮 PDF 缺失 → {local_path}") except Exception as e: print(f" {WARN} 节点 {name} 检查失败: {e}") def main(): print("==========================================") print(" kgrag 迁移验证脚本 - 步骤 5") print(f" 代码目录: {DST_CODE_DIR}") print(f" 文件目录: {DST_FILES_DIR}") print(f" 新服务地址: {NEW_BASE_URL}") print("==========================================") files_ok = check_files() neo4j_ok, ks_cnt = check_neo4j() check_old_ip(neo4j_ok, ks_cnt) check_urls(ks_cnt) print("\n==========================================") if files_ok and neo4j_ok: print(" 总体结果:迁移基本完成,请检查上方 WARN/FAIL 项") else: print(" 总体结果:存在关键问题,请修复 FAIL 项后重新验证") print("==========================================") if __name__ == "__main__": main()