validate_frontend_pages.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 前端页面完整性验证脚本
  5. 检查页面清单中定义的所有页面是否都已生成,并验证页面代码的完整性
  6. 注意:验证时会考虑CSS处理策略
  7. - 原始HTML文件应包含完整的CSS样式
  8. - 拼接后的文档会移除CSS,只保留HTML结构和JavaScript
  9. - 验证会检查CSS省略标记的存在
  10. """
  11. import os
  12. import json
  13. import re
  14. from pathlib import Path
  15. class Colors:
  16. """终端颜色定义"""
  17. RED = '\033[0;31m'
  18. GREEN = '\033[0;32m'
  19. YELLOW = '\033[1;33m'
  20. BLUE = '\033[0;34m'
  21. PURPLE = '\033[0;35m'
  22. CYAN = '\033[0;36m'
  23. NC = '\033[0m' # No Color
  24. def print_colored(color, message):
  25. """打印带颜色的消息"""
  26. print(f"{color}{message}{Colors.NC}")
  27. def print_success(message):
  28. print_colored(Colors.GREEN, f"✅ {message}")
  29. def print_warning(message):
  30. print_colored(Colors.YELLOW, f"⚠️ {message}")
  31. def print_error(message):
  32. print_colored(Colors.RED, f"❌ {message}")
  33. def print_info(message):
  34. print_colored(Colors.BLUE, f"ℹ️ {message}")
  35. def extract_pages_from_page_list(page_list_file):
  36. """从页面清单文档中提取页面列表"""
  37. if not os.path.exists(page_list_file):
  38. print_error(f"页面清单文件不存在: {page_list_file}")
  39. return []
  40. try:
  41. with open(page_list_file, 'r', encoding='utf-8') as f:
  42. content = f.read()
  43. # 提取页面信息的正则表达式模式
  44. patterns = [
  45. r'(\d+)\.\s*(.+?)\.html', # 匹配 "1. login.html"
  46. r'`(.+?)\.html`', # 匹配 "`login.html`"
  47. r'(\w+)\.html', # 匹配 "login.html"
  48. ]
  49. pages = set()
  50. for pattern in patterns:
  51. matches = re.findall(pattern, content, re.MULTILINE)
  52. for match in matches:
  53. if isinstance(match, tuple):
  54. page_name = match[-1] if len(match) > 1 else match[0]
  55. else:
  56. page_name = match
  57. if page_name and not page_name.isdigit():
  58. pages.add(f"{page_name}.html")
  59. return sorted(list(pages))
  60. except Exception as e:
  61. print_error(f"读取页面清单文件失败: {e}")
  62. return []
  63. def check_html_completeness(html_file):
  64. """检查HTML文件的完整性"""
  65. issues = []
  66. try:
  67. with open(html_file, 'r', encoding='utf-8') as f:
  68. content = f.read()
  69. # 检查基本HTML结构
  70. if not re.search(r'<!DOCTYPE\s+html>', content, re.IGNORECASE):
  71. issues.append("缺少 DOCTYPE 声明")
  72. if not re.search(r'<html[^>]*>', content, re.IGNORECASE):
  73. issues.append("缺少 <html> 标签")
  74. if not re.search(r'<head[^>]*>.*</head>', content, re.IGNORECASE | re.DOTALL):
  75. issues.append("缺少完整的 <head> 部分")
  76. if not re.search(r'<body[^>]*>.*</body>', content, re.IGNORECASE | re.DOTALL):
  77. issues.append("缺少完整的 <body> 部分")
  78. # 检查CSS样式(原始HTML文件应该包含CSS,拼接后的文档会移除CSS)
  79. has_css = (
  80. re.search(r'<style[^>]*>.*</style>', content, re.IGNORECASE | re.DOTALL) or
  81. re.search(r'<link[^>]*stylesheet[^>]*>', content, re.IGNORECASE) or
  82. re.search(r'<!-- CSS.*已省略', content, re.IGNORECASE) # 检查CSS省略标记
  83. )
  84. if not has_css:
  85. issues.append("缺少CSS样式或CSS省略标记")
  86. # 检查文件大小(过小可能不完整)
  87. file_size = os.path.getsize(html_file)
  88. if file_size < 1024: # 小于1KB
  89. issues.append(f"文件过小 ({file_size} bytes),可能不完整")
  90. # 检查是否包含省略标记
  91. omission_patterns = [
  92. r'此处省略',
  93. r'代码较长.*省略',
  94. r'其余.*类似',
  95. r'\[注:.*省略.*\]',
  96. r'<!-- 省略 -->',
  97. r'省略其余',
  98. ]
  99. for pattern in omission_patterns:
  100. if re.search(pattern, content, re.IGNORECASE):
  101. issues.append(f"发现省略标记: {pattern}")
  102. return issues
  103. except Exception as e:
  104. return [f"无法读取文件: {e}"]
  105. def validate_frontend_pages():
  106. """验证前端页面完整性"""
  107. print_colored(Colors.PURPLE, "🔍 前端页面完整性验证")
  108. print_colored(Colors.BLUE, "=" * 60)
  109. # 获取项目路径
  110. base_dir = Path(__file__).parent
  111. page_list_file = base_dir / "process_docs" / "页面清单.md"
  112. front_dir = base_dir / "output_sourcecode" / "front"
  113. output_file = base_dir / "output_docs" / "前端源代码.txt"
  114. print_info(f"检查目录: {base_dir}")
  115. print_info(f"页面清单: {page_list_file}")
  116. print_info(f"前端目录: {front_dir}")
  117. print()
  118. # 步骤1: 提取页面清单
  119. print_colored(Colors.CYAN, "📋 步骤1: 分析页面清单")
  120. expected_pages = extract_pages_from_page_list(page_list_file)
  121. if not expected_pages:
  122. print_warning("无法从页面清单中提取页面信息,尝试扫描前端目录...")
  123. if front_dir.exists():
  124. expected_pages = [f.name for f in front_dir.glob("*.html")]
  125. else:
  126. print_error("前端目录不存在且无法提取页面清单")
  127. return
  128. print_info(f"预期页面数量: {len(expected_pages)}")
  129. for page in expected_pages:
  130. print(f" - {page}")
  131. print()
  132. # 步骤2: 检查文件存在性
  133. print_colored(Colors.CYAN, "📁 步骤2: 检查文件存在性")
  134. missing_pages = []
  135. existing_pages = []
  136. if not front_dir.exists():
  137. print_error(f"前端目录不存在: {front_dir}")
  138. return
  139. for page in expected_pages:
  140. page_file = front_dir / page
  141. if page_file.exists():
  142. existing_pages.append(page)
  143. print_success(f"文件存在: {page}")
  144. else:
  145. missing_pages.append(page)
  146. print_error(f"文件缺失: {page}")
  147. print()
  148. # 步骤3: 检查文件完整性
  149. print_colored(Colors.CYAN, "🔍 步骤3: 检查文件完整性")
  150. incomplete_pages = []
  151. for page in existing_pages:
  152. page_file = front_dir / page
  153. issues = check_html_completeness(page_file)
  154. if issues:
  155. incomplete_pages.append((page, issues))
  156. print_warning(f"文件不完整: {page}")
  157. for issue in issues:
  158. print(f" - {issue}")
  159. else:
  160. print_success(f"文件完整: {page}")
  161. print()
  162. # 步骤4: 检查汇总文档
  163. print_colored(Colors.CYAN, "📄 步骤4: 检查汇总文档")
  164. if output_file.exists():
  165. try:
  166. with open(output_file, 'r', encoding='utf-8') as f:
  167. doc_content = f.read()
  168. file_size = os.path.getsize(output_file)
  169. if file_size > 1024 * 1024:
  170. size_str = f"{file_size / (1024 * 1024):.2f} MB"
  171. elif file_size > 1024:
  172. size_str = f"{file_size / 1024:.2f} KB"
  173. else:
  174. size_str = f"{file_size} bytes"
  175. print_success(f"汇总文档存在: {output_file}")
  176. print_info(f"文档大小: {size_str}")
  177. # 检查汇总文档中的页面数量
  178. page_separators = re.findall(r'=== (.+?\.html) ===', doc_content)
  179. print_info(f"汇总文档包含页面: {len(page_separators)}")
  180. # 检查是否有省略标记
  181. omission_found = re.search(r'此处省略|代码较长.*省略|其余.*类似|\[注:.*省略.*\]', doc_content, re.IGNORECASE)
  182. if omission_found:
  183. print_error(f"汇总文档包含省略标记: {omission_found.group()}")
  184. else:
  185. print_success("汇总文档无省略标记")
  186. except Exception as e:
  187. print_error(f"读取汇总文档失败: {e}")
  188. else:
  189. print_warning(f"汇总文档不存在: {output_file}")
  190. print_info("可以运行 python3 generate_frontend_sourcecode.py 生成")
  191. print()
  192. # 生成验证报告
  193. print_colored(Colors.CYAN, "📊 验证报告汇总")
  194. print("=" * 60)
  195. total_pages = len(expected_pages)
  196. existing_count = len(existing_pages)
  197. complete_count = existing_count - len(incomplete_pages)
  198. print(f"📋 预期页面数量: {total_pages}")
  199. print(f"📁 已生成页面数量: {existing_count}")
  200. print(f"✅ 完整页面数量: {complete_count}")
  201. print(f"❌ 缺失页面数量: {len(missing_pages)}")
  202. print(f"⚠️ 不完整页面数量: {len(incomplete_pages)}")
  203. if total_pages > 0:
  204. completion_rate = (complete_count / total_pages) * 100
  205. print(f"💯 完成率: {completion_rate:.1f}%")
  206. print()
  207. # 问题总结
  208. if missing_pages or incomplete_pages:
  209. print_colored(Colors.RED, "🔧 需要修复的问题:")
  210. if missing_pages:
  211. print_error("缺失的页面:")
  212. for page in missing_pages:
  213. print(f" - {page}")
  214. if incomplete_pages:
  215. print_warning("不完整的页面:")
  216. for page, issues in incomplete_pages:
  217. print(f" - {page}:")
  218. for issue in issues:
  219. print(f" * {issue}")
  220. print()
  221. print_info("建议操作:")
  222. print("1. 检查页面清单是否正确定义了所有页面")
  223. print("2. 重新使用AI生成缺失或不完整的页面")
  224. print("3. 确保AI生成时遵循完整性要求")
  225. print("4. 运行 python3 generate_frontend_sourcecode.py 重新生成汇总文档")
  226. else:
  227. print_colored(Colors.GREEN, "🎉 所有页面验证通过!前端代码生成完整。")
  228. return len(missing_pages) + len(incomplete_pages)
  229. def main():
  230. """主函数"""
  231. try:
  232. issues_count = validate_frontend_pages()
  233. return 0 if issues_count == 0 else 1
  234. except Exception as e:
  235. print_error(f"验证过程中发生错误: {e}")
  236. return 2
  237. if __name__ == "__main__":
  238. exit(main())