Skip to the content.

架构设计

分层

web / cli          入口层:REST 控制器、命令行
service            编排层:一次分析的完整流水线、进度、异步
impact             分析层:契约匹配 + 传播 + 打分
report             输出层:报告模型、Markdown、AI 解读
diff               变更层:文件 diff、语义变更检测
parse              索引层:Maven / Java / SQL / API / 配置 / 消息
source             来源层:本地目录、Git、GitHub、GitLab、PR
store              持久层:SQLite
schedule           调度层:每日定时扫描

依赖方向是单向的:入口 → 编排 → 分析 → 索引 → 来源。没有反向依赖,也没有循环。

一次分析的数据流

RepoSource(代码源记录)
   │  RepositoryPreparer
   ▼
RevisionPair { base, head }   ← 两个 FileSource 快照
   │  FileDiffCollector
   ▼
List<FileChange>              ← 哪些文件变了
   │  CodeIndexer × 2(base / head 各索引一次)
   ▼
CodeIndex base, CodeIndex head  ← 两套代码知识图谱
   │  ChangeDetector
   ▼
List<ChangeRecord>            ← 语义变更(带稳定符号 key)
   │  PeerImpactEngine(每个 peer 一次)
   ▼
List<PeerImpact>              ← 每个 peer 的契约命中 + 影响
   │  ReportBuilder
   ▼
ImpactReport                  ← 落库 + 返回给前端

关键设计决定

为什么不合并两个仓库的图

技术上可以合并,但会引入一堆伪关系:两个仓库里同名但无关的类会被误连。 契约是唯一可靠的连接点,所以先用契约接上,再在各自的图里传播。

为什么索引不落库

索引随代码变化,落库就要处理失效。每次分析从快照重建(毫秒级到秒级), 永远和代码一致。只有报告落库。

为什么用快照抽象而不是直接读工作区

FileSource 接口统一了”某个版本的文件集合”:工作区、某个提交、远程仓库缓存都是它。 这样索引逻辑完全不关心代码来自哪里,也让 base/head 可以用同一套代码处理。

为什么风险与置信度分开

见 评分模型。简而言之:合并成一个数字会丢掉”不确定”这个信息, 而”不确定”恰恰是静态分析最需要诚实表达的部分。

并发模型

目录结构

src/main/java/com/geek/association/
├── source/     快照抽象与仓库准备
├── parse/      各类分析器(Java / SQL / API / 配置 / 消息)
├── diff/       文件 diff 与语义变更检测
├── impact/     契约匹配、传播、评分
├── report/     报告模型与渲染
├── service/    分析编排
├── schedule/   定时扫描
├── store/      SQLite 持久化
├── web/        REST 接口
├── cli/        命令行入口
├── config/     配置绑定
└── util/       工具类