#!/usr/bin/env python3 """ AI-Check 主入口 — Controller 层接口参数变更检测 流程(对齐第一版需求): 1. Git 检出新旧代码 2. JavaParser AST 解析 Controller 方法签名与参数 3. 对比增 / 删 / 改 / 重命名 4. (可选)LLM 审核参数变更结果 5. (可选)写入日志 6. 企业微信通知(URI + 参数变更明细) 用法: python .gitea/checker/main.py [--config .gitea/config.yaml] [--repo-root .] [推送人] [推送时间] """ import argparse import sys import tempfile from pathlib import Path import yaml CHECKER_DIR = Path(__file__).resolve().parent sys.path.insert(0, str(CHECKER_DIR)) from change_logger import persist_change_log from comparator import compare_endpoints from controller_parser import ( endpoints_to_map, filter_endpoints_by_files, run_java_parser, ) from git_utils import ( get_changed_java_controller_files, get_controller_files_diff, get_current_commit, get_previous_commit_sha, prepare_worktrees, ) from llm_reviewer import review_parameter_changes from notifier import send_parameter_change_notification def load_config(config_path: Path) -> dict: """加载 YAML 配置文件。""" if not config_path.exists(): print(f"[错误] 配置文件不存在: {config_path}") print("请在 .gitea/config.yaml 中填写配置并提交到仓库。") sys.exit(1) with open(config_path, "r", encoding="utf-8") as f: return yaml.safe_load(f) or {} def parse_endpoints_for_version( repo_root: Path, source_subdir: str, jar_path: Path, tmp_dir: Path, label: str, ) -> dict: """对指定版本源码运行 Java AST 解析,提取 Controller 接口参数。""" source_dir = repo_root / source_subdir output_json = tmp_dir / f"endpoints_{label}.json" print(f"[AST] 扫描 {label} 版本: {source_dir}") endpoints = run_java_parser(source_dir, jar_path, output_json) print(f"[AST] {label} 版本共 {len(endpoints)} 个 Controller 接口") return endpoints_to_map(endpoints) def main() -> int: """主流程入口。""" parser = argparse.ArgumentParser( description="AI-Check: Controller 接口参数变更检测" ) parser.add_argument("--config", default=".gitea/config.yaml", help="配置文件路径(相对仓库根目录)") parser.add_argument("--repo-root", default=".", help="Git 仓库根目录") parser.add_argument("push_user", nargs="?", default=None, help="推送人(CI 传入)") parser.add_argument("push_time", nargs="?", default=None, help="推送时间(CI 传入)") args = parser.parse_args() repo_root = Path(args.repo_root).resolve() config = load_config(repo_root / args.config) source_subdir = config.get("source_dir", "src/main/java") jar_path = repo_root / config.get( "java_parser_jar", ".gitea/java-parser/target/controller-parser-1.0.0.jar" ) commit_info = get_current_commit() push_user = args.push_user or commit_info.author push_time = args.push_time or commit_info.commit_time print("Controller 接口参数变更检测") print("=" * 40) print(f"推送人: {push_user}") print(f"推送时间: {push_time}") print(f"LLM 审核: {config.get('llm', {}).get('enabled', True)}") print(f"记录日志: {config.get('log', {}).get('enabled', False)}") print("=" * 40) prev_sha = get_previous_commit_sha() if prev_sha is None: print("[Git] 首次提交,无可对比版本,跳过。") return 0 changed_files = get_changed_java_controller_files(prev_sha, commit_info.sha) if not changed_files: print("[Git] 本次提交未变更 Controller 文件,跳过。") return 0 print(f"[Git] 变更 Controller 文件 {len(changed_files)} 个:") for f in changed_files: print(f" - {f}") git_diff = get_controller_files_diff(prev_sha, commit_info.sha, changed_files) current_dir, prev_dir, _ = prepare_worktrees(repo_root) reports = [] llm_review = None with tempfile.TemporaryDirectory(prefix="ai-check-") as tmp: tmp_dir = Path(tmp) # 1. AST 解析 + 参数对比 new_map = parse_endpoints_for_version( current_dir, source_subdir, jar_path, tmp_dir, "new" ) old_map = parse_endpoints_for_version( prev_dir, source_subdir, jar_path, tmp_dir, "old" ) new_filtered = endpoints_to_map( filter_endpoints_by_files(list(new_map.values()), changed_files) ) old_filtered = endpoints_to_map( filter_endpoints_by_files(list(old_map.values()), changed_files) ) reports = compare_endpoints(old_filtered, new_filtered) print(f"[对比] 检测到 {len(reports)} 个接口存在参数变更") # 2. LLM 审核接口参数变更(非代码审查) if reports: llm_review = review_parameter_changes( reports, config, changed_files, git_diff ) if llm_review: print(f"[LLM] 参数变更审核完成") # 3. 写日志(开关控制) persist_change_log(reports, commit_info, config, llm_review) # 4. 企微通知 notify_cfg = config.get("notify", {}) only_on_change = notify_cfg.get("only_on_change", True) if only_on_change and not reports: print("[通知] 无接口参数变更,跳过企微通知。") return 0 mentioned = notify_cfg.get("mentioned_users", "") mentioned_list = [u.strip() for u in mentioned.split(",") if u.strip()] or None wecom_cfg = config.get("wecom", {}) send_parameter_change_notification( webhook_url=wecom_cfg.get("webhook_url", ""), reports=reports, push_user=push_user, push_time=push_time, llm_review=llm_review, mentioned_users=mentioned_list, ) print("\n完成") return 0 if __name__ == "__main__": sys.exit(main())