#!/usr/bin/env python3 """ 验证 API v3.0 重构是否完整 检查代码中是否还有旧的逻辑残留 """ import os import re from pathlib import Path def print_header(title): print(f"\n{'='*60}") print(f" {title}") print('='*60) def search_in_file(filepath, pattern, description): """在文件中搜索模式""" try: with open(filepath, 'r', encoding='utf-8') as f: content = f.read() matches = re.findall(pattern, content, re.MULTILINE) return matches except Exception as e: return None def check_removed_code(): """检查已移除的代码""" print_header("检查已移除的代码") checks = [ { "file": "search/es_query_builder.py", "pattern": r"if field == ['\"]price_ranges['\"]", "description": "硬编码的 price_ranges 逻辑", "should_exist": False }, { "file": "search/es_query_builder.py", "pattern": r"def add_dynamic_aggregations", "description": "add_dynamic_aggregations 方法", "should_exist": False }, { "file": "api/models.py", "pattern": r"aggregations.*Optional\[Dict", "description": "aggregations 参数(在 SearchRequest 中)", "should_exist": False }, { "file": "frontend/static/js/app.js", "pattern": r"price_ranges", "description": "前端硬编码的 price_ranges", "should_exist": False }, { "file": "frontend/static/js/app.js", "pattern": r"displayAggregations", "description": "旧的 displayAggregations 函数", "should_exist": False } ] all_passed = True for check in checks: filepath = os.path.join("/home/tw/SearchEngine", check["file"]) matches = search_in_file(filepath, check["pattern"], check["description"]) if matches is None: print(f" ⚠️ 无法读取:{check['file']}") continue if check["should_exist"]: if matches: print(f" ✓ 存在:{check['description']}") else: print(f" ✗ 缺失:{check['description']}") all_passed = False else: if matches: print(f" ✗ 仍存在:{check['description']}") print(f" 匹配:{matches[:2]}") all_passed = False else: print(f" ✓ 已移除:{check['description']}") return all_passed def check_new_code(): """检查新增的代码""" print_header("检查新增的代码") checks = [ { "file": "api/models.py", "pattern": r"class RangeFilter", "description": "RangeFilter 模型", "should_exist": True }, { "file": "api/models.py", "pattern": r"class FacetConfig", "description": "FacetConfig 模型", "should_exist": True }, { "file": "api/models.py", "pattern": r"class FacetValue", "description": "FacetValue 模型", "should_exist": True }, { "file": "api/models.py", "pattern": r"class FacetResult", "description": "FacetResult 模型", "should_exist": True }, { "file": "api/models.py", "pattern": r"range_filters.*RangeFilter", "description": "range_filters 参数", "should_exist": True }, { "file": "api/models.py", "pattern": r"facets.*FacetConfig", "description": "facets 参数", "should_exist": True }, { "file": "search/es_query_builder.py", "pattern": r"def build_facets", "description": "build_facets 方法", "should_exist": True }, { "file": "search/searcher.py", "pattern": r"def _standardize_facets", "description": "_standardize_facets 方法", "should_exist": True }, { "file": "api/routes/search.py", "pattern": r"@router.get\(['\"]\/suggestions", "description": "/search/suggestions 端点", "should_exist": True }, { "file": "api/routes/search.py", "pattern": r"@router.get\(['\"]\/instant", "description": "/search/instant 端点", "should_exist": True }, { "file": "frontend/static/js/app.js", "pattern": r"function displayFacets", "description": "displayFacets 函数", "should_exist": True }, { "file": "frontend/static/js/app.js", "pattern": r"rangeFilters", "description": "rangeFilters 状态", "should_exist": True } ] all_passed = True for check in checks: filepath = os.path.join("/home/tw/SearchEngine", check["file"]) matches = search_in_file(filepath, check["pattern"], check["description"]) if matches is None: print(f" ⚠️ 无法读取:{check['file']}") continue if check["should_exist"]: if matches: print(f" ✓ 存在:{check['description']}") else: print(f" ✗ 缺失:{check['description']}") all_passed = False else: if matches: print(f" ✗ 仍存在:{check['description']}") all_passed = False else: print(f" ✓ 已移除:{check['description']}") return all_passed def check_documentation(): """检查文档""" print_header("检查文档") docs = [ "API_DOCUMENTATION.md", "API_EXAMPLES.md", "MIGRATION_GUIDE_V3.md", "CHANGES.md" ] all_exist = True for doc in docs: filepath = os.path.join("/home/tw/SearchEngine", doc) if os.path.exists(filepath): size_kb = os.path.getsize(filepath) / 1024 print(f" ✓ 存在:{doc} ({size_kb:.1f} KB)") else: print(f" ✗ 缺失:{doc}") all_exist = False return all_exist def check_imports(): """检查模块导入""" print_header("检查模块导入") import sys sys.path.insert(0, '/home/tw/SearchEngine') try: from api.models import ( RangeFilter, FacetConfig, FacetValue, FacetResult, SearchRequest, SearchResponse, ImageSearchRequest, SearchSuggestRequest, SearchSuggestResponse ) print(" ✓ API 模型导入成功") from search.es_query_builder import ESQueryBuilder print(" ✓ ESQueryBuilder 导入成功") from search.searcher import Searcher, SearchResult print(" ✓ Searcher 导入成功") # 检查方法 qb = ESQueryBuilder('test', ['field1']) if hasattr(qb, 'build_facets'): print(" ✓ build_facets 方法存在") else: print(" ✗ build_facets 方法不存在") return False if hasattr(qb, 'add_dynamic_aggregations'): print(" ✗ add_dynamic_aggregations 方法仍存在(应该已删除)") return False else: print(" ✓ add_dynamic_aggregations 方法已删除") # 检查 SearchResult sr = SearchResult(hits=[], total=0, max_score=0, took_ms=10, facets=[]) if hasattr(sr, 'facets'): print(" ✓ SearchResult.facets 属性存在") else: print(" ✗ SearchResult.facets 属性不存在") return False if hasattr(sr, 'aggregations'): print(" ✗ SearchResult.aggregations 属性仍存在(应该已删除)") return False else: print(" ✓ SearchResult.aggregations 属性已删除") return True except Exception as e: print(f" ✗ 导入失败:{e}") return False def main(): """主函数""" print("\n" + "🔍 开始验证 API v3.0 重构") print(f"项目路径:/home/tw/SearchEngine\n") # 运行检查 check1 = check_removed_code() check2 = check_new_code() check3 = check_documentation() check4 = check_imports() # 总结 print_header("验证总结") results = { "已移除的代码": check1, "新增的代码": check2, "文档完整性": check3, "模块导入": check4 } all_passed = all(results.values()) for name, passed in results.items(): status = "✓ 通过" if passed else "✗ 失败" print(f" {status}: {name}") if all_passed: print(f"\n 🎉 所有检查通过!API v3.0 重构完成。") else: print(f"\n ⚠️ 部分检查失败,请检查上述详情。") print("\n" + "="*60 + "\n") return 0 if all_passed else 1 if __name__ == "__main__": exit(main())