generate_frontend_removal_inventory.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #!/usr/bin/env python3
  2. """Generate the auditable frontend deletion inventory from the working tree."""
  3. from __future__ import annotations
  4. import subprocess
  5. from collections import Counter, defaultdict
  6. from pathlib import Path
  7. ROOT = Path(__file__).resolve().parents[1]
  8. OUTPUT = ROOT / "docs" / "architecture" / "FRONTEND_REMOVAL_INVENTORY.md"
  9. def deleted_files() -> list[str]:
  10. result = subprocess.run(
  11. ["git", "diff", "--name-only", "--diff-filter=D", "--", "frontend"],
  12. cwd=ROOT,
  13. check=True,
  14. capture_output=True,
  15. text=True,
  16. )
  17. return sorted(line for line in result.stdout.splitlines() if line)
  18. def group_name(path: str) -> str:
  19. parts = Path(path).parts
  20. if len(parts) >= 5 and parts[:3] == ("frontend", "src", "views"):
  21. return f"views/{parts[3]}"
  22. if len(parts) >= 4 and parts[:3] == ("frontend", "src", "api"):
  23. return "api"
  24. if len(parts) >= 4 and parts[:3] == ("frontend", "src", "components"):
  25. return "components"
  26. if len(parts) >= 4 and parts[:3] == ("frontend", "src", "layout"):
  27. return "layout"
  28. return "/".join(parts[1:3])
  29. def render(files: list[str]) -> str:
  30. groups: dict[str, list[str]] = defaultdict(list)
  31. for path in files:
  32. groups[group_name(path)].append(path)
  33. counts = Counter({name: len(paths) for name, paths in groups.items()})
  34. lines = [
  35. "# 前端历史代码删除清单",
  36. "",
  37. "> 生成方式:`python scripts/generate_frontend_removal_inventory.py`。本文件基于当前未提交删除集生成;合并前应再次生成并复核。",
  38. "",
  39. "## 1. 基线与恢复方式",
  40. "",
  41. "- 原解析报告识别出 348 个前端未匹配调用,产品确认其主体为已下线后端服务遗留;本轮不重建这些旧接口。",
  42. f"- 当前 Git 删除集包含 {len(files)} 个前端文件;一个文件可包含多个调用,因此文件数不等于 348 个调用数。",
  43. "- 原始代码备份:`/Users/maxiaolong/Documents/Codex/2026-07-06/g-i/DataOps-platform-backup-before-cleanup-20260716-170321.tar.gz`。",
  44. "- SHA-256:`866c6c4aff127f88497a5ddf39ac8d8da93008cc784ae6f276f80a7cd17d4ef0`。",
  45. "- 恢复优先级:先从 Git 历史恢复单文件;若原始工作区未入库内容缺失,再从上述只读备份提取。禁止将整份备份直接覆盖当前工作区。",
  46. "",
  47. "## 2. 删除批次",
  48. "",
  49. "| 批次 | 范围 | 处置依据 |",
  50. "|---|---|---|",
  51. "| C1 | Vanna、Airflow、模型训练、问卷薪酬、图表定制 | 产品确认整体下线 |",
  52. "| C2 | 数据资源、数据模型、数据指标和旧数据资产原型 | 静态引用与路由复核后确认没有保留链路 |",
  53. "| C3 | 旧数据工厂非 n8n 页面、执行/调度/节点管理 | 执行能力统一由 n8n 提供 |",
  54. "| C4 | 旧系统管理、自助注册、租户、升级管理 | 后续以管理员/编辑者/查看者重建 |",
  55. "| C5 | 不可达公共组件、API 封装和演示页 | 无活跃路由或 import 消费方 |",
  56. "",
  57. "## 3. 明确保留例外",
  58. "",
  59. "- n8n 适配页面和 `/api/datafactory/*` 后端代理接口;Workflow 保留为 n8n 原生概念。",
  60. "- DataFlow 治理定义页面;后续新增一个 DataFlow 对多个 n8n Workflow 版本的映射。",
  61. "- 数据治理知识库入口和工作台;当前为验收占位,下一阶段补齐真实能力。",
  62. "- 数据安全入口;当前不宣称已实现安全治理能力。",
  63. "- HOPMS 数据导入工具及其验收样例。",
  64. "",
  65. "## 4. 按目录统计",
  66. "",
  67. "| 分组 | 删除文件数 |",
  68. "|---|---:|",
  69. ]
  70. for name, count in sorted(counts.items(), key=lambda item: (-item[1], item[0])):
  71. lines.append(f"| `{name}` | {count} |")
  72. lines.extend(
  73. [
  74. "",
  75. "## 5. 合并门禁",
  76. "",
  77. "1. `npm --prefix frontend run build` 成功。",
  78. "2. 路由表中不再出现已下线模块,活跃页面无失效 import。",
  79. "3. `tests/test_cleanup_contract.py`、Docker 静态隔离测试与本地冒烟测试全部通过。",
  80. "4. 数据安全、治理知识库、工作台、DataFlow 和 n8n 保留入口可访问。",
  81. "5. 生产数据库不执行删表;仅提交代码和建库脚本清理。",
  82. "",
  83. "## 6. 完整删除文件清单",
  84. "",
  85. ]
  86. )
  87. for name in sorted(groups):
  88. lines.append(f"### {name}")
  89. lines.append("")
  90. lines.extend(f"- `{path}`" for path in groups[name])
  91. lines.append("")
  92. return "\n".join(lines).rstrip() + "\n"
  93. def main() -> None:
  94. files = deleted_files()
  95. if not files:
  96. raise SystemExit("No deleted frontend files found in the working tree")
  97. OUTPUT.parent.mkdir(parents=True, exist_ok=True)
  98. OUTPUT.write_text(render(files), encoding="utf-8")
  99. print(f"Generated {OUTPUT.relative_to(ROOT)} with {len(files)} deleted files")
  100. if __name__ == "__main__":
  101. main()