feat: 集成 XMind 思维导图导出到 QE Fleet 工作流

- fleet_runner.py: _run_export() 在 Excel 导出后自动生成 XMind
- fleet_runner.py: 新增 --skip-xmind 参数(run/export 命令均支持)
- export_XMind.py: 按模块分组 → 用例编号/标题/优先级/前置条件/步骤/预期/备注树形结构
- XMind 输出目录: output/xmind_reports/

用法:
  python scripts/fleet_runner.py export --requirement <需求>       # Excel + XMind
  python scripts/fleet_runner.py export --requirement <需求> --skip-xmind  # 仅 Excel
  python scripts/export_XMind.py --base-name <名称>               # 独立 XMind 导出
This commit is contained in:
xst
2026-07-13 09:56:50 +08:00
parent acdb441507
commit d4c3d66efc
4 changed files with 295 additions and 8 deletions
+24 -8
View File
@@ -51,6 +51,7 @@ from case_pipeline import (
PROJECT_PROFILE_FILE,
)
from export_excel import export_markdown_to_excel
from export_XMind import export_markdown_to_xmind
# Fleet 自有模块
from fleet_manifest import (
@@ -1494,8 +1495,8 @@ def _run_monitor_zone(
# ── Export ─────────────────────────────────────────────────────────────────
def _run_export(requirement_path: Path) -> dict[str, Any]:
"""导出 Excel 并创建版本快照。"""
def _run_export(requirement_path: Path, skip_xmind: bool = False) -> dict[str, Any]:
"""导出 Excel + XMind 并创建版本快照。"""
from case_pipeline import command_export
base_name = requirement_path.stem
@@ -1504,16 +1505,29 @@ def _run_export(requirement_path: Path) -> dict[str, Any]:
if not test_cases_path.exists():
raise FileNotFoundError(f"测试用例文件不存在: {test_cases_path}")
# 调用现有 export 逻辑
# Excel 导出 + 版本快照
command_export(str(requirement_path))
# XMind 思维导图导出
result: dict[str, Any] = {"export": "success", "base_name": base_name, "excel": True}
if not skip_xmind:
try:
xmind_dir = REPO_ROOT / "output" / "xmind_reports"
xmind_path = export_markdown_to_xmind(test_cases_path, xmind_dir, base_name)
result["xmind"] = str(xmind_path)
print(f"🧠 XMind 已生成: {to_repo_relative(xmind_path)}")
except Exception as exc:
print(f"⚠️ XMind 导出失败(不影响 Excel: {exc}")
result["xmind"] = None
result["xmind_error"] = str(exc)
# 更新 monitor manifest
manifest = load_manifest_safe(base_name, "monitor") or {}
manifest["export_completed"] = True
manifest["export_timestamp"] = datetime.now(timezone.utc).isoformat()
save_manifest(base_name, "monitor", manifest)
return {"export": "success", "base_name": base_name}
return result
# ── Status ─────────────────────────────────────────────────────────────────
@@ -1566,6 +1580,7 @@ def main() -> None:
run_parser.add_argument("--requirement", required=True, help="需求文档路径")
run_parser.add_argument("--zone", choices=ZONE_ORDER, help="仅运行到指定战区")
run_parser.add_argument("--skip-export", action="store_true", help="跳过 Excel 导出")
run_parser.add_argument("--skip-xmind", action="store_true", help="跳过 XMind 导出")
# prepare
prepare_parser = subparsers.add_parser("prepare", help="仅准备战区")
@@ -1584,8 +1599,9 @@ def main() -> None:
review_parser.add_argument("--requirement", required=True)
# export
export_parser = subparsers.add_parser("export", help=" Excel 导出")
export_parser = subparsers.add_parser("export", help="导出 Excel + XMind 思维导图")
export_parser.add_argument("--requirement", required=True)
export_parser.add_argument("--skip-xmind", action="store_true", help="跳过 XMind 导出")
# monitor
monitor_parser = subparsers.add_parser("monitor", help="执行结果分析 + 知识沉淀")
@@ -1631,7 +1647,7 @@ def main() -> None:
return
if args.command == "export":
_run_export(requirement_path)
_run_export(requirement_path, skip_xmind=getattr(args, "skip_xmind", False))
return
# 确定要运行的战区
@@ -1685,8 +1701,8 @@ def main() -> None:
if review_manifest:
verdict = review_manifest.get("quality_verdict", {}).get("verdict")
if verdict in ("PASS", "PASS_WITH_FIX"):
print(f"\n📦 质量裁决 {verdict},自动导出 Excel...")
_run_export(requirement_path)
print(f"\n📦 质量裁决 {verdict},自动导出 Excel + XMind...")
_run_export(requirement_path, skip_xmind=getattr(args, "skip_xmind", False))
else:
print(f"\n🛑 质量裁决 {verdict},跳过导出。请先解决阻断项。")