efficjump/system-graph-viewer
GitHub: efficjump/system-graph-viewer
一款本地优先的系统图谱分析工具,通过静态分析 Spring 与 MyBatis 源码,以依据可追溯的方式重建从 HTTP 请求到数据库表的完整调用路径与结构关系。
Stars: 0 | Forks: 0
# System Graph Viewer
System Graph Viewer(Web UI:**Atlascope**)是一款本地分析工具,允许您在不实际运行应用程序的情况下探索其结构与数据流。您可以在一个界面中查看从 API 到 Service、Mapper,再到 SQL 与 Table 的路径,Table 之间的物理与逻辑关系,以及各项推断的代码依据和分析局限。
- **以流程为中心的探索**:按顺序追踪从 HTTP endpoint 到 Controller、Service、Mapper、SQL、Table 的调用路径。
- **以依据为核心的 Graph**:区分声明的关系、静态分析结果以及 SQL `JOIN` 推断,并显示对应的文件与行位置及置信度。
- **以搜索为中心的路径恢复**:在搜索方法或类时,不仅会显示直接匹配项,还会扩展显示从上游 endpoint 到下游 Table 的连接路径。
- **基于源码的 ERD**:即使没有数据库连接,也能从 JPA entity 与 `CREATE TABLE`、`ALTER TABLE` DDL 中恢复 Table、Column、PK、FK,并结合 Repository 与 MyBatis 的使用依据。
- **持久化与增量分析**:保留本地快照与各分析器的结果,但在 JVM 中仅保留每个项目的最新 graph,并且仅重新运行其输入的 content fingerprint 发生变化的分析器。
- **合并 Runtime 依据**:将本地观察到的批处理数据合并到静态 graph 中,将实际运行过的关系作为独立依据显示。
- **本地优先设计**:不会将源码、DB metadata 或分析 graph 发送到外部服务,可选择性地仅连接本地 AI。
目前,分析器主要集中在 Spring MVC Java AST、Spring Data JPA、MyBatis XML 与注解 SQL、SQL DDL 以及 MariaDB metadata 上,但并未将分析器类型限制为固定的枚举。未来可以通过相同的契约添加 jOOQ、PostgreSQL、Oracle、Kotlin、TypeScript、批处理以及消息分析器。如果已安装的分析器遇到无法解析的代码,系统不会将其粉饰为成功,而是将其标记为 `未支持` 或 `部分完成`,并按扩展名列出文件数量。
默认的主界面是 IDE 风格的代码工作区。您可以通过 lazy tree 浏览项目文件夹,以标签页形式打开多个源文件,还可以将当前文件和分析 graph 依据绑定到受限的 context 中,直接向本地 AI 模型提问。
## 先通过界面预览
### 一目了然的解析范围与状态

它总结了已分析的组件、关系、数据表和 coverage,并直接以 graph 形式展示了代表性请求流。
### 依据与置信度追踪的系统 graph

您可以同时查看 Application、API、Persistence、SQL、Database 各层,并过滤查看声明、静态分析与推断的关系。
## 目前可以查看的内容
- 结合 Controller 类与方法路由的 HTTP endpoint
- Controller → Service → Mapper → SQL → Table 流程
- Controller → Service → Spring Data repository → JPA entity table 流程
- 按 `SELECT`、`INSERT`、`UPDATE` 区分的读/写 Table
- 包含从源码 DDL 或 DB metadata 获取的 PK、FK、Column 的 Table 目录
- 区分声明的 FK 与从 SQL `JOIN` 推断出的关系
- endpoint 视角的下流影响与 table 视角的上流影响
- 包含文件与行位置的依据、置信度、分析 coverage 及诊断信息
- 利用服务器提供的分析器列表,按项目选择分析器
- 项目管理功能:查看并删除已注册项目的关联快照与执行历史
- 重启后依然可恢复的本地快照及各分析器的 `reused` 运行状态
- 通过 batch ID 防止重复收集的本地 runtime evidence API
- 使用稳定 plugin ID 与基于字符串的 graph kind 的扩展契约
- 基于项目相对路径的 lazy 文件树和多源码标签页
- 基于当前文件、选中代码和 graph evidence 的本地 AI 对话
对于无法通过静态分析确定的关系,我们不会将其呈现为既定事实。对于像 Reflection、动态 SQL、AOP、存储过程等仅靠源码难以解析的部分,我们会将其留作可能的关系或诊断信息。
## 最快的运行方式
所需的工具包括 Docker Desktop 或 Docker Engine 及 Compose v2、`make`、`curl`。
```
make init
make up
make smoke
```
`make init` 会基于 `.env.example` 创建 `.env`,并在本地为 demo MariaDB 随机生成密码。它不会覆盖已有的 `.env` 文件。
在浏览器中打开 [http://127.0.0.1:3000](http://127.0.0.1:3000),系统会自动注册并分析一个关联了仓库中 `samples/order-service` 和 demo MariaDB 的项目。您无需输入任何连接信息,即可先行浏览所有功能。
所有容器均仅对 loopback 地址公开。
| 服务 | 默认地址 | 用途 |
|---|---|---|
| Web | `http://127.0.0.1:3000` | React 探索界面 |
| API | `http://127.0.0.1:8080` | Spring Boot 分析 API |
| Demo MariaDB | `127.0.0.1:3307` | 用于实际 metadata 分析的示例 DB |
若要停止服务,请运行 `make down`。此命令会关闭容器,但会保留 MariaDB 和分析状态的数据卷。
## IDE 代码工作区
在 `代码工作区` 中,系统不会一次性读取当前项目的所有源码根目录,而是在您展开文件夹时逐层加载。选中文件后,会以只读的 editor 标签页打开,您可以查看行号、语言、文件大小以及是否被截断。即使在 AI 依据中选中文件,也会跳转到相同的 editor 标签页。
文件 API 仅接受项目中已注册的 canonical 根目录下的相对路径。包含 `..` 或绝对路径、symlink,以及属于 VCS、dependency、build 目录和常规 credential 文件的内容,都会从文件树和内容查询中被排除。非 UTF-8 文本文件不会以代码形式显示。
对于大文件,系统仅将当前屏幕附近的行渲染到 DOM 中。为了保护浏览器的布局,对于异常长的单行代码,最多只显示 4,096 个字符并在界面上提示截断,但这不会改变服务器读取的原始内容和分析输入。
在 Docker 中打开其他项目时,不使用浏览器的文件上传功能。请将 `.env` 中的 `ANALYSIS_SOURCE_ROOT` 指定为主机项目的绝对路径并以 read-only 方式挂载,然后在新项目页面的文件夹浏览器中选择该项目。您可以通过 `ANALYSIS_CONTAINER_ROOT` 定义浏览器中显示的根目录名称,用户无需手动输入容器的绝对路径。
## 跟踪首次分析
`samples/order-service` 包含 Controller、Service、MyBatis interface 与 XML、注解 SQL 以及 FK。分析完成后,您可以先检查以下路径:
1. 选择 `GET /api/orders/{orderId}` endpoint。
2. 查看从 `OrderController.getOrder` 到 `OrderService.getOrder` 的调用。
3. 查看 `OrderMapper.findOrderDetail` 的 SQL 以及对 `orders`、`customers`、`order_items` 的读取关系。
4. 检查 SQL `JOIN` 的依据与 `schema.sql` 中的 FK 依据是否分别独立显示。
5. 切换至以 `orders` 表为中心,反向探索读取或写入该表的 endpoint 与 Mapper。
您可以在 `POST /api/orders` 中查看写入流程。读取 `customers`、更新 `inventory`,随后写入 `orders` 和 `order_items` 的路径将以不同的运算形式展示。
在系统 graph 中搜索方法的全限定名或 endpoint 路径时,系统不仅会突出显示直接匹配的节点,还会根据分析设定的深度一并展示上下游路径。默认的 `自动` 方向会从 endpoint 向 table 扩展,或从 table 向调用方扩展,而对于一般的逻辑处理则提供双向 context。您可以单独选择 `执行流`、`结构` 或 `全部` 的关系范围;如果搜索结果过大,系统会提示界面复杂度受限的事实。
即使在大型代码仓库中,浏览器接收到完整的 graph JSON 后,也不会仅截取前面部分。初始快照包含反映层级与连接性的代表性路径,而系统 graph 的搜索、过滤与影响范围探索则由后端在每次请求时基于原始快照计算出 bounded subgraph。系统会优先匹配准确的方法名和 signature,并在响应中包含总节点/关系数、显示节点/关系数以及截断原因。
关系图会将中心度较高的代表性 Table 作为概览展示,并且支持通过 Table 名、Column 名或数据类型进行搜索。进行搜索或选中 Table 时,系统不会展开整个连接组件,而是专注于直接匹配的 Table 及其 1-hop FK、SQL JOIN 邻居。自引用 FK 会以专用的环状结构展示,完整的列以及读写路径可在 Table 列表的详情面板中查看。
## 连接 Demo MariaDB
Compose 配置中的 `demo-db` 在首次启动时会执行 [`schema.sql`](./samples/order-service/src/main/resources/schema.sql),并默认自动关联到初始项目。在创建独立项目或重新测试连接时,请根据运行环境使用以下地址:
- 在 Docker 中运行的 API:host `demo-db`,port `3306`
- 在主机上直接运行的 API:host `127.0.0.1`,port 为 `.env` 中的 `DEMO_DB_PORT`
- database 和 username:`.env` 中的 `DEMO_DB_NAME`、`DEMO_DB_USER`
- password:`.env` 中的 `DEMO_DB_PASSWORD`(由 `make init` 生成)
密码不会包含在 API 响应、日志或分析 evidence 中。分析器仅读取 metadata,不会执行任何应用程序的 DDL、DML 或任意 SQL。
## 连接本地 AI
AI 面板默认处于禁用状态,且不会生成虚假回复。您可以连接像 Ollama、LM Studio、vLLM 这样提供 OpenAI-compatible chat completion API 的本地模型。在 Docker 中使用主机上的模型时,请在 `.env` 中添加以下配置:
```
SYSTEM_GRAPH_ASSISTANT_ENDPOINT=http://host.docker.internal:11434/v1
SYSTEM_GRAPH_ASSISTANT_MODEL=
SYSTEM_GRAPH_ASSISTANT_API_KEY=
SYSTEM_GRAPH_ASSISTANT_PROVIDER=OpenAI-compatible local
SYSTEM_GRAPH_ASSISTANT_CONNECT_TIMEOUT=2s
SYSTEM_GRAPH_ASSISTANT_REQUEST_TIMEOUT=120s
SYSTEM_GRAPH_ASSISTANT_STATUS_TIMEOUT=30s
SYSTEM_GRAPH_ASSISTANT_MAX_REQUEST_BYTES=131072
SYSTEM_GRAPH_ASSISTANT_MAX_CONCURRENT_REQUESTS=2
VITE_ASSISTANT_STATUS_TIMEOUT_MS=35000
```
如果后端直接在主机上运行,请将 endpoint host 更改为 `127.0.0.1`。允许的 host 仅限 `localhost`、`127.0.0.1`、`::1` 和 `host.docker.internal`,浏览器中无法输入任意 URL。
对于 vLLM,运行时指定的模型名必须与客户端发送的模型名完全一致。我们不会在文档或代码中固定特定模型名,而是确认正在运行的服务公布的 ID,并将其用于 `.env` 中的 `SYSTEM_GRAPH_ASSISTANT_MODEL`。如果服务器设置了 API key,请在以下请求中也添加相同的 Bearer token:
```
curl --fail --silent http://127.0.0.1:/v1/models | jq -r '.data[].id'
```
由于通过 Docker 运行的后端无法直接访问绑定到 loopback 的 vLLM,请将 endpoint 设置为 `http://host.docker.internal:/v1`。状态检查使用 `/v1/models`,而实际的问答使用 `/v1/chat/completions`。为了确保能够完成包括 Docker Desktop host gateway 延迟在内的状态检查,后端的默认状态超时时间设为 30 秒,而 Web 端会使用 35 秒来等待该探测结果。如果连接本身失败,则会先应用 2 秒的独立 connect timeout。如果大型模型的首次 lazy load 超过 120 秒,您只需在本地 `.env` 中增加 `SYSTEM_GRAPH_ASSISTANT_REQUEST_TIMEOUT` 的时间。
`VITE_ASSISTANT_STATUS_TIMEOUT_MS` 是在前端构建时注入的,因此更改其值后需要重新构建前端镜像。
每次提问时,系统不会发送整个仓库内容,而是在大小限制内提取当前文件、用户选中的代码以及与问题相关的 graph evidence。对于配置文件中类似 password、secret、token 等值,会在模型 context 中进行遮蔽处理,并且对话内容不会保存在浏览器存储中。模型的回复与服务器确认的源码依据会在 UI 中分开显示。
后端不使用 JVM 的默认 proxy,而是直接连接至允许的本地 endpoint。系统对请求 body 与响应、context、对话历史以及并发模型调用数都设定了上限;只有当模型返回的 `[source N]` 落在实际提供的依据范围内时,才会将其显示为 grounded 回复。
## 分析我的项目
在 Docker 环境下,必须显式以只读方式挂载主机文件。请将 `.env` 中的 `ANALYSIS_SOURCE_ROOT` 更改为待分析项目的绝对路径,然后重启栈。
```
ANALYSIS_SOURCE_ROOT=/Users/me/develop
ANALYSIS_CONTAINER_ROOT=/workspace/develop
ANALYSIS_BOOTSTRAP_SOURCE=/workspace/develop/my-project
```
```
make down
make up
```
随后,在新项目页面的文件夹浏览器中选择 `develop` 下的项目。浏览器仅展示后端允许的根目录,且 API 仅收发 root 标识符与相对路径。只有在启动时需要自动注册项目的情况下,才需指定 `ANALYSIS_BOOTSTRAP_SOURCE`。后端会将选择结果再次确认为 canonical path,仅分析该路径,且挂载方式为只读。如果想同时允许多个仓库,可以在本地开发模式下运行,并通过 `SYSTEM_GRAPH_ANALYSIS_ALLOWED_ROOTS` 传入以号分隔的 canonical path。
如果要在不连接数据库的情况下仅通过源码分析非演示项目,可以在 Compose 运行环境中显式指定 `ANALYSIS_BOOTSTRAP_DATABASE_URL=`,这样 demo MariaDB 就不会绑定到分析结果中。如果要连接额外的只读 DB,请同时指定 `ANALYSIS_BOOTSTRAP_DATABASE_URL`、`ANALYSIS_BOOTSTRAP_DATABASE_USERNAME`、`ANALYSIS_BOOTSTRAP_DATABASE_PASSWORD` 和 `ANALYSIS_BOOTSTRAP_DATABASE_SCHEMA`。
分析是作为独立于项目注册请求的后台任务运行的。在快照准备就绪之前,界面会轮询分析状态,完成后会自动显示最新结果。构建产物、VCS metadata、dependency 目录以及常见的敏感文件会在遍历阶段的进入子目录前被排除,因此临时的 Git metadata 文件变化不会中断整体分析过程。
分析快照、执行历史、源码 manifest 以及各分析器的贡献内容会以原子方式保存到 `analysis-state` 本地 Docker volume 中。DB URL、用户名和密码不会存储在这些状态文件中。重新分析时,系统会根据目标仓库的 Git ignore 规则筛选文件并生成 SHA-256 指纹;如果各分析器声明的输入文件集合的指纹未发生变化,则会复用之前的分析结果。Java 文件的变更只会使 Spring、MyBatis 等使用该文件作为输入的分析器失效,而 SQL DDL 的分析结果仍可直接复用。
JVM 中每个项目仅驻留一份最新的 graph snapshot。执行历史界面所需的 coverage、分析器运行状态、诊断信息以及各阶段耗时均会单独保存在 run summary 中,因此无需重新展开完整的过去 graph。只有在通过 ID 显式请求过去保存的 snapshot 时,才会从 gzip 文件中进行延迟加载,且不会长期占用内存;分析器的贡献缓存同样仅在实际分析期间读取。因此,snapshot 的保留数量决定了恢复范围,但不会增加稳态下内存中驻留的 graph 数量。
您可以在 `.env` 中调整保留数量和安全上限。
```
SYSTEM_GRAPH_STATE_ENABLED=true
SYSTEM_GRAPH_INCREMENTAL_ENABLED=true
SYSTEM_GRAPH_SNAPSHOT_RETENTION=5
SYSTEM_GRAPH_MAX_STATE_FILE_BYTES=268435456
SYSTEM_GRAPH_MAX_CONCURRENT_ANALYSES=1
```
为了防止大型仓库的分析任务相互重叠导致 JVM 内存溢出,分析任务默认每次只运行一个。您可以根据环境增加该数值,但前提是要与“将每个 Java 文件的 AST 转换为 compact declaration 后立即释放”的机制结合使用。
## 连接 Runtime 观察依据
应用程序探针可以通过节点 ID 或唯一的 qualified name,将观察到的关系发送至本地 API。即使重复发送相同的 `batchId`,系统也不会重复累加。如果 qualified name 存在于多个模块中,系统不会随意选择,而是要求提供准确的 node ID。
```
curl --fail --silent \
--request POST \
--header 'Content-Type: application/json' \
--data '{
"batchId":"collector-run-42",
"producer":"local-otel-bridge",
"relations":[{
"sourceNodeId":"method:module::main:example.Controller#load()",
"targetNodeId":"method:module::main:example.Service#load()",
"kind":"CALLS",
"observedCount":12
}]
}' \
http://127.0.0.1:8080/api/projects//runtime-evidence
```
Runtime 依据会保存在本地状态存储中,并在下次静态重新分析时再次合并。如果已存在静态关系,系统会补充 `runtimeObserved` metadata 及观察次数;如果不存在,则会将其作为 `OBSERVED` 关系添加。该 API 仅提供收集接口,因此不强制目标应用程序安装特定的 agent 或 SDK。
对于注册错误的项目,您可以通过项目卡片上的垃圾桶图标进行删除。此操作仅删除 Atlascope 的注册信息、分析快照与执行历史,不会更改以只读方式连接的源文件与数据库原始内容。
## 本地开发
在主机上进行开发需要 Java 21、Maven 3.9、Node.js 22 以及 Corepack。前端使用 `pnpm-lock.yaml` 和 `packageManager` 中指定的版本。如果不想额外安装工具进行查看,使用 Docker 运行是最简单的方式。
在一个终端中运行 API:
```
make backend
```
在另一个终端中运行 Vite 开发服务器:
```
make frontend
```
开发 UI 会在 [http://127.0.0.1:5173](http://127.0.0.1:5173) 打开,并将 `/api` 请求 proxy 到 `127.0.0.1:8080`。`make backend` 会将当前仓库根目录设为默认的允许路径。
通过以下命令执行全面验证:
```
make verify
```
此命令会依次执行后端测试、前端 lint/test 脚本(如果存在)、production build 以及 Docker Compose 配置验证。正在运行的 Compose 栈的 health endpoint 可通过 `make smoke` 命令单独验证。
如果默认端口已被占用,您也可以在不修改 `.env` 的情况下直接覆盖单次运行使用的端口:
```
BACKEND_PORT=18081 FRONTEND_PORT=13000 make up
BACKEND_PORT=18081 FRONTEND_PORT=13000 make smoke
```
## 结构
```
frontend/ React + TypeScript explorer
backend/ Spring Boot API와 정적 분석 pipeline
samples/order-service/ 결정적인 분석·시연 fixture
docs/ 제품 범위와 아키텍처 결정
scripts/ 로컬 실행·검증 helper
docker-compose.yml loopback 전용 통합 실행 환경
```
分析 pipeline 按照源码扫描、Spring Java AST 分析、Spring Data JPA 分析、MyBatis 分析、SQL DDL 分析、可选的 MariaDB metadata 收集、归一化 fact 合并、evidence graph 生成的顺序执行。详细的原则与扩展契约可查阅 [`docs/architecture.md`](./docs/architecture.md),质量标准可查阅 [`docs/product-scope.md`](./docs/product-scope.md)。
## 本地安全原则
- Web、API、MariaDB 端口均仅绑定至 `127.0.0.1`。
- 源码仅在用户指定的允许根目录下读取,且 Docker 挂载方式为只读。
- 对于通过 symlink 逃逸出允许根目录的路径,不作为分析对象。
- 密码不放入仓库中,而是在本地通过 `.env` 生成,且 `.env` 会被 Git 忽略。
- 源码、DB metadata、graph 不会传输至外部模型、telemetry 或托管服务。
- Runtime evidence 收集仅通过 loopback API 接收,仅保存 batch ID 与聚合后的关系。
- Evidence 中仅使用项目相对路径和经过清理的 snippet。
- AI endpoint 受服务器配置中的本地 host allowlist 限制,并且不会在日志中记录完整的 prompt 与响应内容。
## 故障排除
如果在执行 `make up` 时出现端口冲突,请将 `.env` 中的 `FRONTEND_PORT`、`BACKEND_PORT`、`DEMO_DB_PORT` 修改为未被占用的端口。
如果需要从头重建 MariaDB schema,在确认数据保留策略后,必须显式删除 Compose volume 并重新启动。为防止数据丢失,`make down` 不会自动删除 volume。
如果分析结果不完整,请先检查 UI 中的 diagnostics 与 coverage。像 MyBatis 中的 `${...}`、provider method、runtime 生成的 SQL 或进程外调用等情况,可能无法仅靠静态分析完全还原。此时,系统的默认行为是绝不掩盖缺失,而是将其显示为 unresolved 或 possible 关系。
## 当前版本的局限
项目、最近的分析 snapshot、执行历史、增量分析 cache 以及 runtime evidence 均保存在本地状态的 volume 中。目前暂不提供分析 snapshot 之间的结构化 diff 以及分析器内部文件级别的 dependency invalidation。代码编辑器是一个安全的只读 viewer,不具备文件保存、重命名或删除功能。对于 TypeScript、JavaScript、Kotlin 等目前未安装对应分析器的代码,系统会将文件数量与扩展名作为诊断信息记录,但不会生成调用 graph。接下来的开发重点将是这些语言的分析器、snapshot 比对、Flyway/Liquibase 补充支持以及 OpenTelemetry collector adapter。
标签:AST解析, JS文件枚举, MyBatis, Spring, 域名枚举, 数据流追踪, 架构可视化, 请求拦截, 错误基检测, 静态代码分析