validate_navigation_config.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 导航配置验证工具
  5. 版本: 1.0
  6. 描述: 验证导航架构配置文档的完整性和正确性
  7. """
  8. import os
  9. import sys
  10. import json
  11. import re
  12. from pathlib import Path
  13. # 添加项目根目录到Python路径
  14. project_root = Path(__file__).parent.parent.parent
  15. sys.path.append(str(project_root))
  16. class Colors:
  17. """终端颜色定义"""
  18. RED = '\033[91m'
  19. GREEN = '\033[92m'
  20. YELLOW = '\033[93m'
  21. BLUE = '\033[94m'
  22. MAGENTA = '\033[95m'
  23. CYAN = '\033[96m'
  24. WHITE = '\033[97m'
  25. BOLD = '\033[1m'
  26. UNDERLINE = '\033[4m'
  27. RESET = '\033[0m'
  28. def print_message(color, message):
  29. """打印带颜色的消息"""
  30. print(f"{color}{message}{Colors.RESET}")
  31. def print_success(message):
  32. print_message(Colors.GREEN, f"✓ {message}")
  33. def print_info(message):
  34. print_message(Colors.BLUE, f"ℹ {message}")
  35. def print_warning(message):
  36. print_message(Colors.YELLOW, f"⚠ {message}")
  37. def print_error(message):
  38. print_message(Colors.RED, f"✗ {message}")
  39. class NavigationConfigValidator:
  40. """导航配置验证器"""
  41. def __init__(self):
  42. self.project_root = project_root
  43. self.config_file = self.project_root / "process_docs" / "导航架构配置.md"
  44. self.page_list_file = self.project_root / "process_docs" / "页面清单.md"
  45. self.issues = []
  46. def validate_all(self):
  47. """执行所有验证检查"""
  48. print_message(Colors.BOLD + Colors.CYAN, "🔍 导航配置验证工具")
  49. print_message(Colors.CYAN, "=" * 60)
  50. # 检查配置文件是否存在
  51. if not self.config_file.exists():
  52. print_error(f"导航配置文件不存在: {self.config_file}")
  53. return False
  54. # 读取配置文件
  55. try:
  56. with open(self.config_file, 'r', encoding='utf-8') as f:
  57. self.config_content = f.read()
  58. except Exception as e:
  59. print_error(f"读取导航配置文件失败: {e}")
  60. return False
  61. # 读取页面清单文件
  62. if self.page_list_file.exists():
  63. try:
  64. with open(self.page_list_file, 'r', encoding='utf-8') as f:
  65. self.page_list_content = f.read()
  66. except Exception as e:
  67. print_warning(f"读取页面清单文件失败: {e}")
  68. self.page_list_content = ""
  69. else:
  70. print_warning("页面清单文件不存在,将跳过相关验证")
  71. self.page_list_content = ""
  72. # 执行各项验证
  73. all_passed = True
  74. all_passed &= self.validate_config_structure()
  75. all_passed &= self.validate_navigation_json()
  76. all_passed &= self.validate_route_mapping()
  77. all_passed &= self.validate_component_templates()
  78. all_passed &= self.validate_css_javascript_specs()
  79. all_passed &= self.validate_page_consistency()
  80. # 输出验证结果
  81. self.print_summary(all_passed)
  82. return all_passed
  83. def validate_config_structure(self):
  84. """验证配置文档结构"""
  85. print_info("验证导航配置文档结构...")
  86. required_sections = [
  87. "系统导航架构配置",
  88. "主导航结构",
  89. "侧边栏导航结构",
  90. "面包屑导航配置",
  91. "页面路由映射表",
  92. "导航状态管理规范",
  93. "导航HTML组件模板"
  94. ]
  95. missing_sections = []
  96. for section in required_sections:
  97. if section not in self.config_content:
  98. missing_sections.append(section)
  99. if missing_sections:
  100. for section in missing_sections:
  101. print_error(f" 缺少必需章节: {section}")
  102. return False
  103. else:
  104. print_success("配置文档结构完整")
  105. return True
  106. def validate_navigation_json(self):
  107. """验证导航JSON配置"""
  108. print_info("验证导航JSON配置...")
  109. # 查找JSON配置块
  110. json_pattern = r'```json\s*(\{.*?\})\s*```'
  111. json_matches = re.findall(json_pattern, self.config_content, re.DOTALL)
  112. if not json_matches:
  113. print_error("未找到导航JSON配置")
  114. return False
  115. valid_json_count = 0
  116. for i, json_str in enumerate(json_matches):
  117. try:
  118. config = json.loads(json_str)
  119. # 验证主导航配置
  120. if 'header' in config:
  121. if self.validate_header_config(config['header']):
  122. valid_json_count += 1
  123. # 验证侧边栏配置
  124. if 'sidebar' in config:
  125. if self.validate_sidebar_config(config['sidebar']):
  126. valid_json_count += 1
  127. # 验证面包屑配置
  128. if 'breadcrumbs' in config:
  129. if self.validate_breadcrumb_config(config['breadcrumbs']):
  130. valid_json_count += 1
  131. except json.JSONDecodeError as e:
  132. print_error(f" JSON配置 {i+1} 格式错误: {e}")
  133. if valid_json_count > 0:
  134. print_success(f"找到 {valid_json_count} 个有效的JSON配置")
  135. return True
  136. else:
  137. print_error("未找到有效的JSON配置")
  138. return False
  139. def validate_header_config(self, header_config):
  140. """验证头部导航配置"""
  141. required_fields = ['logo', 'mainMenu', 'userMenu']
  142. for field in required_fields:
  143. if field not in header_config:
  144. print_error(f" 头部配置缺少字段: {field}")
  145. return False
  146. # 验证主菜单结构
  147. if 'mainMenu' in header_config and isinstance(header_config['mainMenu'], list):
  148. for menu_item in header_config['mainMenu']:
  149. if not all(key in menu_item for key in ['id', 'label', 'route']):
  150. print_error(" 主菜单项缺少必需字段 (id, label, route)")
  151. return False
  152. return True
  153. def validate_sidebar_config(self, sidebar_config):
  154. """验证侧边栏配置"""
  155. required_fields = ['collapsible', 'sections']
  156. for field in required_fields:
  157. if field not in sidebar_config:
  158. print_error(f" 侧边栏配置缺少字段: {field}")
  159. return False
  160. # 验证菜单项结构
  161. if 'sections' in sidebar_config and isinstance(sidebar_config['sections'], list):
  162. for section in sidebar_config['sections']:
  163. if 'items' in section and isinstance(section['items'], list):
  164. for item in section['items']:
  165. if not all(key in item for key in ['id', 'label', 'route']):
  166. print_error(" 侧边栏菜单项缺少必需字段 (id, label, route)")
  167. return False
  168. return True
  169. def validate_breadcrumb_config(self, breadcrumb_config):
  170. """验证面包屑配置"""
  171. if not isinstance(breadcrumb_config, dict):
  172. print_error(" 面包屑配置格式错误")
  173. return False
  174. # 检查是否有路径映射
  175. if not breadcrumb_config:
  176. print_warning(" 面包屑配置为空")
  177. return True
  178. # 验证路径映射格式
  179. for path, breadcrumb in breadcrumb_config.items():
  180. if not isinstance(breadcrumb, list):
  181. print_error(f" 面包屑路径 {path} 配置格式错误")
  182. return False
  183. for item in breadcrumb:
  184. if not all(key in item for key in ['label', 'route']):
  185. print_error(f" 面包屑项 {path} 缺少必需字段 (label, route)")
  186. return False
  187. return True
  188. def validate_route_mapping(self):
  189. """验证路由映射表"""
  190. print_info("验证页面路由映射表...")
  191. # 查找路由映射表
  192. table_pattern = r'\|[^|]*页面名称[^|]*\|[^|]*路由路径[^|]*\|.*?\n(\|.*?\n)+'
  193. table_match = re.search(table_pattern, self.config_content)
  194. if not table_match:
  195. print_error("未找到页面路由映射表")
  196. return False
  197. # 解析表格内容
  198. table_content = table_match.group(0)
  199. rows = [row.strip() for row in table_content.split('\n') if row.strip().startswith('|')]
  200. if len(rows) < 3: # 至少要有表头、分隔符、一行数据
  201. print_error("路由映射表内容不足")
  202. return False
  203. print_success(f"找到路由映射表,包含 {len(rows)-2} 个页面路由")
  204. return True
  205. def validate_component_templates(self):
  206. """验证组件模板"""
  207. print_info("验证导航组件模板...")
  208. required_templates = [
  209. "Header组件模板",
  210. "Sidebar组件模板",
  211. "Breadcrumb组件模板"
  212. ]
  213. missing_templates = []
  214. for template in required_templates:
  215. if template not in self.config_content:
  216. missing_templates.append(template)
  217. # 查找HTML代码块
  218. html_pattern = r'```html\s*(.*?)\s*```'
  219. html_matches = re.findall(html_pattern, self.config_content, re.DOTALL)
  220. if not html_matches:
  221. print_error("未找到HTML组件模板")
  222. return False
  223. valid_templates = 0
  224. for html_code in html_matches:
  225. if any(tag in html_code.lower() for tag in ['header', 'aside', 'nav']):
  226. valid_templates += 1
  227. if missing_templates:
  228. for template in missing_templates:
  229. print_warning(f" 缺少模板: {template}")
  230. if valid_templates > 0:
  231. print_success(f"找到 {valid_templates} 个HTML组件模板")
  232. return True
  233. else:
  234. print_error("未找到有效的HTML组件模板")
  235. return False
  236. def validate_css_javascript_specs(self):
  237. """验证CSS和JavaScript规范"""
  238. print_info("验证CSS和JavaScript规范...")
  239. # 查找CSS类名规范
  240. css_pattern = r'CSS类名|class|className'
  241. css_found = re.search(css_pattern, self.config_content, re.IGNORECASE)
  242. # 查找JavaScript规范
  243. js_pattern = r'JavaScript|javascript|js|Navigation.*Controller'
  244. js_found = re.search(js_pattern, self.config_content, re.IGNORECASE)
  245. issues = []
  246. if not css_found:
  247. issues.append("缺少CSS类名规范说明")
  248. if not js_found:
  249. issues.append("缺少JavaScript交互规范说明")
  250. if issues:
  251. for issue in issues:
  252. print_warning(f" {issue}")
  253. return True # 这些是警告,不是错误
  254. else:
  255. print_success("CSS和JavaScript规范说明完整")
  256. return True
  257. def validate_page_consistency(self):
  258. """验证与页面清单的一致性"""
  259. print_info("验证与页面清单的一致性...")
  260. if not self.page_list_content:
  261. print_warning("无法验证页面一致性:页面清单文件不存在")
  262. return True
  263. # 从页面清单中提取页面名称
  264. page_pattern = r'(?:#+\s*)?(\d+\.\s*)?([^#\n]+?)(?:页面|页|Page)'
  265. page_matches = re.findall(page_pattern, self.page_list_content)
  266. if not page_matches:
  267. print_warning("未能从页面清单中识别页面名称")
  268. return True
  269. page_names = [match[1].strip() for match in page_matches if match[1].strip()]
  270. # 检查导航配置中是否包含这些页面
  271. missing_pages = []
  272. for page_name in page_names:
  273. if page_name not in self.config_content:
  274. missing_pages.append(page_name)
  275. if missing_pages:
  276. for page in missing_pages:
  277. print_warning(f" 导航配置中可能缺少页面: {page}")
  278. return True # 这是警告,不是错误
  279. else:
  280. print_success(f"导航配置与页面清单一致 (检查了 {len(page_names)} 个页面)")
  281. return True
  282. def print_summary(self, all_passed):
  283. """打印验证结果摘要"""
  284. print_message(Colors.CYAN, "=" * 60)
  285. if all_passed:
  286. print_success("🎉 导航配置验证通过!")
  287. print_info("导航架构配置完整,可以开始页面代码生成")
  288. else:
  289. print_error("❌ 发现导航配置问题")
  290. print_warning("请修复上述问题后再进行页面代码生成")
  291. print_info("建议:")
  292. print(" 1. 补充缺少的配置章节")
  293. print(" 2. 修复JSON配置格式错误")
  294. print(" 3. 完善组件模板定义")
  295. print(" 4. 确保路由映射表完整")
  296. print_message(Colors.CYAN, "=" * 60)
  297. def main():
  298. """主函数"""
  299. try:
  300. validator = NavigationConfigValidator()
  301. success = validator.validate_all()
  302. # 返回适当的退出码
  303. sys.exit(0 if success else 1)
  304. except Exception as e:
  305. print_error(f"验证过程中发生错误: {e}")
  306. import traceback
  307. traceback.print_exc()
  308. sys.exit(1)
  309. if __name__ == "__main__":
  310. main()