Files
AI-Check-Test/.gitea/checker/src/main/java/com/codechecker/CodeCheckMain.java
dongzi bd7db35db8
All checks were successful
CodeChecker 变更检测 / code-check (push) Successful in 25s
源码update
2026-06-09 16:32:47 +08:00

179 lines
8.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.codechecker;
import com.codechecker.analyzer.ClassChangeAnalyzer;
import com.codechecker.analyzer.DtoNestIndex;
import com.codechecker.analyzer.EndpointIndexBuilder;
import com.codechecker.api.analyzer.ApiChangeAnalyzer;
import com.codechecker.api.analyzer.DtoImpactedApiAnalyzer;
import com.codechecker.api.model.ApiChangeKind;
import com.codechecker.api.model.EndpointChangeReport;
import com.codechecker.api.notify.ApiChangeNotifier;
import com.codechecker.api.scanner.ApiFileChangeScanner;
import com.codechecker.config.AppConfig;
import com.codechecker.git.GitChangeScanner;
import com.codechecker.model.ApiEndpoint;
import com.codechecker.model.ClassChangeReport;
import com.codechecker.notify.OverlapNotificationFilter;
import com.codechecker.notify.WeComNotifier;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
/**
* CLI 入口:加载配置 → 扫描 git 变更 → 分析影响 → 输出/发送企微通知。
*/
@Command(name = "code-checker", mixinStandardHelpOptions = true,
description = "检测类变更与 API 变更并发送企业微信通知")
public class CodeCheckMain implements Callable<Integer> {
@Option(names = "--config", required = true, description = "配置文件路径")
private Path config;
@Option(names = "--repo-root", required = true, description = "仓库根目录")
private Path repoRoot;
@Option(names = "--old-sha", required = true, description = "旧提交 SHA")
private String oldSha;
@Option(names = "--new-sha", required = true, description = "新提交 SHA")
private String newSha;
@Option(names = "--modifier", required = true, description = "修改人")
private String modifier;
@Option(names = "--modify-time", required = true, description = "修改时间")
private String modifyTime;
/** 程序入口 */
public static void main(String[] args) {
int exitCode = new CommandLine(new CodeCheckMain()).execute(args);
System.exit(exitCode);
}
/** 主流程:类变更与 API 变更检测,支持 Dto 跟进与重叠通知策略 */
@Override
public Integer call() throws Exception {
AppConfig appConfig = AppConfig.load(config.toAbsolutePath());
if (!appConfig.isMasterEnabled()) {
System.out.println("变更检测已全部关闭checker.enabled=false");
return 0;
}
GitChangeScanner gitScanner = new GitChangeScanner(repoRoot.toAbsolutePath());
DtoNestIndex nestIndex = DtoNestIndex.build(repoRoot.toAbsolutePath(), appConfig);
List<ClassChangeReport> classReports = List.of();
List<EndpointChangeReport> apiReports = List.of();
if (appConfig.isClassCheckEnabled()) {
classReports = analyzeClassChanges(appConfig, gitScanner, nestIndex);
} else {
System.out.println("类变更检测已关闭class_check.enabled=false");
}
if (appConfig.isApiCheckEnabled()) {
apiReports = analyzeApiChanges(appConfig, gitScanner, classReports, nestIndex);
} else {
System.out.println("API 变更检测已关闭api_check.enabled=false");
}
OverlapNotificationFilter.FilterResult filtered = OverlapNotificationFilter.apply(
classReports, apiReports, appConfig.getDtoOverlapMode(), nestIndex);
int totalSent = sendClassNotifications(appConfig, filtered.classReports())
+ sendApiNotifications(appConfig, filtered.apiReports());
if (totalSent == 0 && appConfig.isOnlyOnChange()) {
System.out.println("无变更,静默退出");
}
return 0;
}
private List<ClassChangeReport> analyzeClassChanges(AppConfig appConfig, GitChangeScanner gitScanner,
DtoNestIndex nestIndex) throws Exception {
System.out.println("=== 类变更检测 ===");
EndpointIndexBuilder indexBuilder = new EndpointIndexBuilder();
Map<String, ApiEndpoint> endpointIndex = indexBuilder.buildIndex(repoRoot.toAbsolutePath(), appConfig);
System.out.println("已索引接口数量: " + endpointIndex.size());
ClassChangeAnalyzer analyzer = new ClassChangeAnalyzer(gitScanner);
List<ClassChangeReport> reports = analyzer.analyze(
repoRoot.toAbsolutePath(), appConfig, oldSha, newSha, endpointIndex, nestIndex);
System.out.println("检测到需通知的类变更数量: " + reports.size());
return reports;
}
private List<EndpointChangeReport> analyzeApiChanges(AppConfig appConfig, GitChangeScanner gitScanner,
List<ClassChangeReport> classReports,
DtoNestIndex nestIndex) throws Exception {
System.out.println("=== API 变更检测 ===");
ApiFileChangeScanner fileScanner = new ApiFileChangeScanner(gitScanner);
Set<String> changedApiFiles = new LinkedHashSet<>(fileScanner.scanChangedFiles(
repoRoot.toAbsolutePath(), appConfig.getAllApiScanDirs(), oldSha, newSha));
ApiChangeAnalyzer analyzer = new ApiChangeAnalyzer(gitScanner);
List<EndpointChangeReport> reports = new ArrayList<>();
if (!changedApiFiles.isEmpty()) {
reports.addAll(analyzer.analyze(repoRoot.toAbsolutePath(), appConfig, oldSha, newSha));
}
if (appConfig.isDtoApiFollowUpEnabled() && !classReports.isEmpty()) {
DtoImpactedApiAnalyzer dtoAnalyzer = new DtoImpactedApiAnalyzer(gitScanner);
List<EndpointChangeReport> followUpReports = dtoAnalyzer.analyze(
repoRoot.toAbsolutePath(), appConfig, oldSha, newSha, classReports, changedApiFiles, nestIndex);
if (!followUpReports.isEmpty()) {
System.out.println("Dto 跟进检测到 API 参数变更数量: " + followUpReports.size());
reports.addAll(followUpReports);
}
}
reports = dedupeApiReports(reports);
System.out.println("检测到需通知的 API 变更数量: " + reports.size());
return reports;
}
private List<EndpointChangeReport> dedupeApiReports(List<EndpointChangeReport> reports) {
Map<String, EndpointChangeReport> merged = new LinkedHashMap<>();
for (EndpointChangeReport report : reports) {
String key = report.getChangeKind() + "|" + report.getHttpMethod() + "|" + report.getUri();
EndpointChangeReport existing = merged.get(key);
if (existing == null) {
merged.put(key, report);
continue;
}
if (report.getChangeKind() == ApiChangeKind.PARAM_CHANGED
&& existing.getChangeKind() == ApiChangeKind.PARAM_CHANGED) {
report.getParameterChanges().forEach(existing::addParameterChange);
}
}
return new ArrayList<>(merged.values());
}
private int sendClassNotifications(AppConfig appConfig, List<ClassChangeReport> reports) {
if (reports.isEmpty()) {
return 0;
}
WeComNotifier notifier = new WeComNotifier();
if (appConfig.isWecomEnabled()) {
return notifier.sendAll(appConfig.getWecomWebhookUrl(), reports, modifier, modifyTime);
}
notifier.logAll(reports, modifier, modifyTime);
return reports.size();
}
private int sendApiNotifications(AppConfig appConfig, List<EndpointChangeReport> reports) {
if (reports.isEmpty()) {
return 0;
}
ApiChangeNotifier notifier = new ApiChangeNotifier();
return notifier.sendAll(appConfig.getWecomWebhookUrl(), reports, modifier, modifyTime,
appConfig.isWecomEnabled());
}
}