build_pages.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. # -*- coding: utf-8 -*-
  2. """pages.yaml 校验 + pages.json 生成脚本
  3. 用法: python build_pages.py (在本目录 references/ 下运行或任意目录均可)
  4. """
  5. import json
  6. import re
  7. import sys
  8. from pathlib import Path
  9. try:
  10. import yaml
  11. except ImportError:
  12. print("ERROR: 需要 PyYAML(pip install pyyaml)")
  13. sys.exit(1)
  14. HERE = Path(__file__).parent
  15. YAML_PATH = HERE / "pages.yaml"
  16. JSON_PATH = HERE / "pages.json"
  17. CONFIG_PATH = HERE.parent / "config.yaml"
  18. errors, warns = [], []
  19. # ---------- 读取 ----------
  20. data = yaml.safe_load(YAML_PATH.read_text(encoding="utf-8"))
  21. cfg = yaml.safe_load(CONFIG_PATH.read_text(encoding="utf-8"))
  22. base_url = (cfg.get("base_url") or "").rstrip("/")
  23. if not re.match(r"^https?://[^/\s]+(:\d+)?$", base_url):
  24. errors.append(f"config.yaml base_url 非法: {base_url}")
  25. # ---------- 遍历校验 ----------
  26. pages, all_ids, menu_keys = [], [], []
  27. def check_page(p, cat, group):
  28. pid = p.get("id")
  29. if not pid:
  30. errors.append(f"缺少 id: {p.get('name')}")
  31. return
  32. if pid in all_ids:
  33. errors.append(f"id 重复: {pid}")
  34. all_ids.append(pid)
  35. path = p.get("path", "")
  36. if not path.startswith("/"):
  37. errors.append(f"[{pid}] path 不以 / 开头: {path}")
  38. url = base_url + path
  39. if "//" in url.replace("://", ""):
  40. errors.append(f"[{pid}] URL 拼接出现双斜杠: {url}")
  41. if not p.get("name"):
  42. errors.append(f"[{pid}] 缺少 name")
  43. if not p.get("desc"):
  44. errors.append(f"[{pid}] 缺少 desc")
  45. mk = p.get("menu_key", "")
  46. if mk:
  47. if mk in menu_keys:
  48. warns.append(f"menu_key 重复: {mk} ({pid})")
  49. menu_keys.append(mk)
  50. pages.append({
  51. "id": pid, "name": p["name"], "aliases": p.get("aliases") or [],
  52. "keywords": p.get("keywords") or [], "menu_key": mk or None,
  53. "path": path, "desc": p.get("desc", ""),
  54. "usage": p.get("usage"), "usage_tip": p.get("usage_tip"),
  55. "visible": p.get("visible", True), "note": p.get("note"),
  56. "category": cat["name"], "group": group,
  57. })
  58. for cat in data.get("categories", []):
  59. if cat.get("pages"):
  60. for p in cat["pages"]:
  61. check_page(p, cat, None)
  62. for g in cat.get("groups") or []:
  63. for p in g.get("pages") or []:
  64. check_page(p, cat, g["name"])
  65. # ---------- docx 原 URL 覆盖性比对 ----------
  66. docx_paths = [
  67. "/monitorChannel/monitor-windrect?id=1931597465741889538&deviceType=windrect",
  68. "/monitorChannel/monitor-sensorMonitor",
  69. "/monitorChannel/monitor-fanmain?id=2087348159538384898&deviceType=fanmain",
  70. "/monitorChannel/monitor-fanlocal?id=1712030595700707330&deviceType=fanlocal",
  71. "/monitorChannel/monitor-gate?id=1796536612492324866&deviceType=gate",
  72. "/monitorChannel/monitor-window?id=1427837547259232258&deviceType=window",
  73. "/micro-vent-3dModal/configurable/default/ventS/home?type=model3D&deviceType=model3D&isNoReverse=1",
  74. "/micro-vent-2dModal/configurable/default/ventS/home?type=model3D&deviceType=model3D&isNoReverse=1",
  75. "/micro-vent-3dModal/dashboard/analysis?type=sysMonitor&deviceType=timenetcal",
  76. "/micro-vent-3dModal/dashboard/analysis?type=sysMonitor&deviceType=pathDecison",
  77. "/micro-need-air/",
  78. "/micro-need-air/workAccount/",
  79. "/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=fiber&tabtype=monitor",
  80. "/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=bundletube",
  81. "/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=modelsensor_temperature&deviceid=",
  82. "/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=modelsensor_smoke&deviceid=",
  83. "/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=modelsensor_co&deviceid=",
  84. "/nitrogen-home", "/grout-home",
  85. "/monitorChannel/monitor-firedoor?id=2019226919389147138&deviceType=door",
  86. "/micro-vent-3dModal/configurable/belt/fireS/home",
  87. "/configurable/default/fireDoorMonitor/home",
  88. "/monitorChannel/balancePress-home", "/fire/warn/home?p=0",
  89. "/bundle/bundleTable", "/bundleSpy/bundleSpyTable",
  90. "/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=gaspatrol",
  91. "/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=modelsensor_gas&deviceid=",
  92. "/gas/gas-pump-monitor/home", "/gas/warn/home?p=0", "/gas/gas-report-inspect",
  93. "/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=dusting&tabtype=monitor",
  94. "/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=atomizing",
  95. "/dust/dedustefan/home", "/dust/warn/home", "/dust/dustMonitorTable",
  96. "/monitorChannel/monitor-alarm-home", "/monitorChannel/wokerFace-home",
  97. "/monitorChannel/tunFace-home",
  98. "/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=location&tabtype=monitor",
  99. "/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=vehicle",
  100. "/monitorChannel/device-monitor/safetymonitor", "/camera", "/forcFan/home",
  101. "/fileManager/fileDetail/home", "/monitorChannel/system/user",
  102. "/monitorChannel/system/project-setting",
  103. ]
  104. # 温度/烟雾/CO/瓦斯传感器 docx 旧地址在注册表中有意替换为菜单地址,单独登记
  105. replaced_intentionally = {
  106. "modelsensor_temperature", "modelsensor_smoke", "modelsensor_co", "modelsensor_gas",
  107. }
  108. reg_paths = {p["path"] for p in pages}
  109. for dp in docx_paths:
  110. if dp in reg_paths:
  111. continue
  112. if any(k in dp for k in replaced_intentionally):
  113. warns.append(f"docx地址已按菜单有意替换: {dp}")
  114. continue
  115. if "sysfiretest" in dp:
  116. warns.append(f"docx地址已更新参数(菜单最新值): {dp}")
  117. continue
  118. if "electro-chamber" in dp:
  119. warns.append(f"docx错误地址已弃用(electro-chamber): {dp}")
  120. continue
  121. errors.append(f"docx 页面缺失: {dp}")
  122. # ---------- 统计与输出 ----------
  123. visible_pages = [p for p in pages if p["visible"]]
  124. with_usage = [p for p in pages if p.get("usage")]
  125. out = {
  126. "version": data.get("version"), "updated": data.get("updated"),
  127. "base_url": base_url,
  128. "stats": {
  129. "total": len(pages), "visible": len(visible_pages),
  130. "with_usage": len(with_usage),
  131. "categories": len(data.get("categories", [])),
  132. },
  133. "pages": pages,
  134. }
  135. JSON_PATH.write_text(json.dumps(out, ensure_ascii=False, indent=2), encoding="utf-8")
  136. print(f"总条目: {len(pages)} | 可见: {len(visible_pages)} | 含操作指引: {len(with_usage)} | 分类: {len(data.get('categories', []))}")
  137. print(f"pages.json 已生成: {JSON_PATH}")
  138. for w in warns:
  139. print("WARN:", w)
  140. if errors:
  141. print("\n== 校验失败 ==")
  142. for e in errors:
  143. print("ERROR:", e)
  144. sys.exit(1)
  145. print("校验全部通过 ✔")