Files
schemaCheck/docs/redis序列化结构检测实施方案.md
dongzi bef70dd032
All checks were successful
序列化结构检查 / serialization-schema-check (push) Successful in 3s
feat: 文档更新
2026-07-15 18:06:31 +08:00

647 lines
23 KiB
Markdown
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.

# 序列化结构变更检测 — 实施方案
> 版本v0.3
> 日期2026-07-15
> 技术栈Java 11 + Maven + JavaParser
> 目标仓库:`schemaCheck`(工具) / `jnpf-java-cloud`(被检测业务仓库)
> 当前阶段:**Phase 1 + Phase 2 已完成****MQ 扩展方案已文档落地**(见 `docs/MQ序列化结构检测方案.md`
---
## 1. 背景与目标
### 1.1 背景
业务仓库 `jnpf-java-cloud` 是多模块 Java 微服务项目,广泛使用 Redis 缓存业务对象。当开发者修改 VO/DTO 字段、调整序列化包装结构、或变更 Redis 写入逻辑时,线上 Redis 中可能仍存在旧结构数据,导致:
- 反序列化失败
- 字段读取为空
- 新旧结构并存引发隐蔽 Bug
典型变更示例(租户库信息缓存):
**变更前(逻辑上等价于直接缓存 `TenantVO`**
```json
{
"dbName": "",
"linkList": [{ "id": "", "serviceName": "", "...": "..." }]
}
```
**变更后(`TenantDbContentCacheHelper` 包装为 `CacheEnvelope`**
```json
{
"vo": {
"dbName": "",
"linkList": [{ "id": "", "serviceName": "", "...": "..." }]
},
"expiresAtMs": 0
}
```
该变更在业务代码中已有真实对应:
- Key`tenant:db:content:{encode}`
- 写入类:`jnpf.util.TenantDbContentCacheHelper#cacheSuccess`
- Value 类型:`CacheEnvelope { TenantVO vo; Long expiresAtMs; }`
### 1.2 目标
**push 时** 自动执行检测:
1. 对比两次提交(`old-sha` vs `new-sha`)之间的代码差异
2. 识别 Redis value 序列化结构是否发生变更
3. 生成结构化变更报告
4. 通过企微机器人发送通知
5. 通过开关控制 **仅通知****阻断流水线**
### 1.3 非目标
- 不连接真实 Redis 实例做运行时校验
- 不扫描 Maven 依赖 jar 中的类(仅分析业务仓库源码)
- 不做全量历史扫描(仅 diff 触发)
- 不替代 CodeChecker / AI Code Review 等现有能力
---
## 2. 业务仓库调研结论
基于对 `jnpf-java-cloud` 的静态检索,得出以下判断(作为方案输入,不再向业务方重复确认):
### 2.1 Redis 序列化方式
| 类型 | 出现频率 | 策略 |
|------|----------|------|
| `JSON.toJSONString(obj)` / `JSONObject.toJSONString(obj)` | 高 | **已支持**W01/W02 |
| `JsonUtil.getObjectToString(obj)` | 高 | **已支持**W03 |
| `RedisTemplate.opsForValue().set(key, obj)` 直接写对象 | 中 | **已支持**W04 |
| `RedisTemplate.opsForHash().put(key, field, obj)` | 中 | **已支持**W05 |
| `StringRedisTemplate` 写 JSON 字符串 | 高 | **已支持** |
| 锁 / 计数器 / token 简单值 | 高 | **默认忽略** |
### 2.2 Key 与实体映射
- 不存在统一的「Key → 类型」注册中心
- 存在大量 `static final String``String.format(...)``buildCacheKey(...)` 等模式
- 因此采用:**写入点静态推断 + 可选 YAML 人工补充映射**
### 2.3 多模块特征
-`pom.xml` 下约 30+ 顶层模块、200+ 子模块
- Java 版本混用8/9/10/11工具统一使用 **JDK 11** 编译运行
- 公共工具类 `RedisUtil``JsonUtil` 来自外部依赖,不在业务仓源码内 — 仅分析调用方,不深入依赖实现
---
## 3. 总体架构
### 3.1 交付形态
```text
schemaCheck 仓库
├── 开发 Java 分析工具
├── mvn package 打 fat-jar
├── 发布到 Nexuscom.codechecker:serialization-schema-checker:{version}
└── 提供默认配置模板
jnpf-java-cloud 仓库
├── .gitea/workflows/serialization-schema-check.yaml
├── .gitea/config/serialization-schema-check-config.yaml
└── push 时下载 jar 并执行检测
```
### 3.2 架构图
```mermaid
flowchart TB
subgraph Gitea["Gitea Push Pipeline"]
A[push 事件] --> B[浅克隆 old/new 提交]
B --> C[下载 serialization-schema-checker.jar]
C --> D[java -jar 执行检测]
end
subgraph Checker["serialization-schema-checker (JDK 11)"]
D --> E[GitDiffScanner]
E --> F[RedisWritePointDetector]
F --> G[JavaSchemaExtractor]
G --> H[SchemaDiffer]
H --> I{有结构变更?}
I -->|否| J[exit 0]
I -->|是| K[ReportBuilder]
K --> L[WeComNotifier]
L --> M{mode=block 且含变更?}
M -->|是| N[exit 1]
M -->|否| J
end
```
### 3.3 核心设计原则
1. **纯静态分析**:基于 Java 源码 AST + 符号解析,不启动 Spring 容器
2. **Diff 驱动**:只分析本次 push 变更涉及的文件及其关联类型
3. **本仓限定**:类型解析仅在业务仓库 `src/main/java` 范围内
4. **可配置**:忽略规则、通知开关、阻断开关均可 YAML 配置
5. **可演进**:已覆盖 JSON 字符串写入与 Template 直写 / Hash后续可扩展读路径反向确认、报告落盘等
---
## 4. 技术选型
| 组件 | 选型 | 版本建议 | 说明 |
|------|------|----------|------|
| 语言 | Java | 11 | 与 CI Runner `jdk11` 对齐 |
| 构建 | Maven | 3.8+ | 与现有私库发布流程一致 |
| AST 解析 | JavaParser | 3.25.x | 完整 Java 语法树 |
| 符号解析 | javaparser-symbol-solver-core | 3.25.x | 跨文件类型推断 |
| 配置 | SnakeYAML | 2.x | 读取检测配置 |
| HTTP 通知 | JDK HttpClient / OkHttp | 11 内置 / 4.x | 企微 Webhook |
| 报告 | Jackson | 2.15.x | JSON/Markdown 报告序列化 |
| 测试 | JUnit 5 | 5.10.x | 单元测试 + 夹具样本 |
**Lombok 处理策略**:基于源码字段 + `@Data` 等注解推断序列化字段;对 `@Builder``@SuperBuilder` 等复杂场景标记为低置信度提示。后续可选集成 `lombok.ast` 或 delombok 预处理。
---
## 5. 工程结构schemaCheck 仓库)
```text
schemaCheck/
├── pom.xml # 单模块工程(无父子结构)
├── docs/
│ ├── 实施方案.md
│ ├── 配置说明.md
│ ├── CI集成说明.md
│ └── MQ序列化结构检测方案.md # MQ 消息体 Schema 扩展(方案)
├── src/
│ ├── main/
│ │ ├── resources/
│ │ │ └── default-config.yaml # 内置默认配置(随 jar 发布)
│ │ └── java/com/codechecker/cache/
│ │ ├── cli/ # 命令行入口
│ │ ├── config/ # 配置模型
│ │ ├── git/ # Git 操作
│ │ ├── analyze/ # 编排与工作树扫描
│ │ ├── detector/ # Redis 写入点检测W01~W05
│ │ ├── schema/ # Schema 提取与注解
│ │ ├── diff/ # 结构对比
│ │ ├── key/ # Key 推断
│ │ ├── report/ # 报告 / 企微 Markdown
│ │ └── notify/ # 企微通知
│ └── test/
│ ├── resources/fixtures/{tenant,lock,template}/
│ └── java/...
├── .gitea/
│ ├── workflows/serialization-schema-check.yaml
│ └── config/serialization-schema-check-config.yaml
└── target/ # 构建产物
```
### 5.1 Maven 坐标
```xml
<groupId>com.codechecker</groupId>
<artifactId>serialization-schema-checker</artifactId>
<version>1.0.0</version>
```
打包为 **shaded/fat jar**,主类:`com.codechecker.cache.cli.SerializationSchemaCheckerMain`
---
## 6. 执行流程详解
### 6.1 CLI 参数
```bash
java -jar serialization-schema-checker-1.0.0.jar \
--config .gitea/config/serialization-schema-check-config.yaml \
--repo-root /path/to/jnpf-java-cloud \
--old-sha abc123 \
--new-sha def456 \
--branch feature/xxx \
--modifier zhangsan \
--modify-time "2026-07-13 14:00:00"
```
| 参数 | 必填 | 说明 |
|------|------|------|
| `--config` | 是 | 检测配置文件路径 |
| `--repo-root` | 是 | 业务仓库根目录 |
| `--old-sha` | 是 | 对比基准提交 |
| `--new-sha` | 是 | 当前提交 |
| `--branch` | 否 | 分支名,用于报告展示 |
| `--modifier` | 否 | 提交人Gitea actor |
| `--modify-time` | 否 | 提交时间 |
### 6.2 对比基准old-sha获取策略
流水线使用 **push 区间累计对比**(方案:`before``after`
1. `--new-sha` = `gitea.sha`push 后 tip
2. `--old-sha` = `gitea.event.before`push 前 tip
3. `before` 为空或全 `0`(新分支首次 push→ 跳过检测,`exit 0`
4. `workflow_dispatch` 无 before 时回退 `HEAD~1`
5. 浅克隆当前 tip`--depth 1`),再按需 `git fetch --depth 1 <before>`;统计 commit 数时 deepen 至 `before` 为祖先,或直接用事件 `commits` 数组长度(**无需全量历史**
> 一次 push 含多个 commit 时,只做 **一次** 检测,对比区间为整次 push 的累计 diff`before..after`),不会漏掉中间 commit 留下的结构变更。
> 不做「每个 commit 各告警一条」;中间引入又被末 commit 改回的净无变更,累计结果可能为「无变更」(符合阻断「最终结构」的目标)。
> 日志中的 commit 数:优先 `gitea.event.commits`;勿在未 deepen 时用浅库 `rev-list`(会少算)。
### 6.3 处理步骤
#### Step 1加载配置
读取 `serialization-schema-check-config.yaml`,合并默认值(见 `docs/配置说明.md`)。
#### Step 2Git Diff 扫描
```bash
git diff --name-only {old-sha} {new-sha} -- '*.java'
```
输出变更 Java 文件列表。同时记录 diff hunks用于判断「是否仅注释/格式变更」。
#### Step 3构建双版本源码索引
`old-sha``new-sha` 分别:
1. `git show {sha}:path/to/File.java` 提取文件内容(无需完整 checkout 两个 worktree
2. 解析为 `CompilationUnit`
3. 建立 `类全名 → CompilationUnit` 索引(仅本仓 `src/main/java`
#### Step 4Redis 写入点检测
**变更文件** 中扫描以下 AST 模式:
| 模式 ID | 匹配表达式 | 提取信息 | 状态 |
|---------|------------|----------|------|
| W01 | `redisUtil.insert(key, JSON.toJSONString(expr), ttl)` | key 表达式、value 表达式 | ✅ |
| W02 | `redisTemplate.opsForValue().set(key, JSON.toJSONString(expr), ...)` | 同上 | ✅ |
| W03 | `stringRedisTemplate.opsForValue().set(key, JsonUtil.getObjectToString(expr), ...)` | 同上 | ✅ |
| W04 | `redisTemplate.opsForValue().set(key, expr, ...)` 且 expr 非字面量 | 直写对象类型 | ✅ |
| W05 | `redisTemplate.opsForHash().put(key, field, expr)` | Hash 写出 value 类型 | ✅ |
**忽略规则**(自动):
- value 为字符串字面量、数字、`UUID``"1"` 等琐碎值
- 方法名含 `setIfAbsent``increment``delete``remove``expire`
- key 匹配 `ignore.key_patterns` 配置(锁 / token / 登录计数等)
#### Step 4b读侧类型辅助W06非写入模式
W06 **不产生独立告警**,只扫描反序列化调用,用读到的 `Xxx.class` **补强**同文件(或同 key写入点的 value 类型 / 根数组标记。
| 匹配示例 | 作用 |
|----------|------|
| `JSON.parseObject(raw, Xxx.class)` | 补强对象类型 |
| `JSON.parseArray(raw, Xxx.class)` / `JsonUtil.getJsonToList` | 补强 `List<Xxx>`rootArray |
| `JsonUtil.getJsonToBean(raw, Xxx.class)` | 同上 |
| 可关联到 `redisUtil.getString(key)` / `opsForValue().get(key)` | 同时补强 key 模式 |
开关:`detection.read_hints_enabled`(默认 `true`)。优先级:`manual_mappings` > W06 补强 > 写侧 AST 推断。
#### Step 5类型推断
对每个写入点的 `expr`,使用 JavaParser Symbol Solver 推断类型:
```java
// 示例TenantDbContentCacheHelper.cacheSuccess
CacheEnvelope envelope = new CacheEnvelope();
envelope.setVo(vo);
envelope.setExpiresAtMs(expiresAtMs);
redisUtil.insert(buildCacheKey(encode), JSON.toJSONString(envelope), ttl);
```
推断链:
1. `JSON.toJSONString(envelope)` → 实参类型 `CacheEnvelope`
2. 定位 `CacheEnvelope` 类(内部类需支持 `Outer$Inner`
3. 读取字段 `vo: TenantVO``expiresAtMs: Long`
4. 递归展开 `TenantVO``dbName: String``linkList: List<TenantLinkModel>`
5. 继续展开 `TenantLinkModel` 全部字段
**注解处理**
| 注解 | 行为 | 状态 |
|------|------|------|
| `@JSONField(serialize = false)` | 排除字段 | ✅ |
| `@JSONField(name = "xxx")` | 字段名映射 | ✅ |
| `@JsonIgnore` | 排除字段 | ✅ |
| `@JsonProperty("xxx")` | 字段名映射 | ✅ |
| `@JsonIgnoreProperties({...})` | 类级忽略字段 | ✅ |
| `@Schema` | 忽略(不影响序列化) | ✅ |
#### Step 6生成 JSON Schema
将 Java 类型转为统一的 `TypeSchema` 树:
```json
{
"typeName": "jnpf.util.TenantDbContentCacheHelper.CacheEnvelope",
"fields": [
{
"path": "vo",
"javaType": "jnpf.model.TenantVO",
"jsonType": "object",
"children": [
{ "path": "vo.dbName", "jsonType": "string" },
{ "path": "vo.linkList", "jsonType": "array", "itemType": "jnpf.model.TenantLinkModel" }
]
},
{
"path": "expiresAtMs",
"javaType": "java.lang.Long",
"jsonType": "number"
}
]
}
```
#### Step 7Schema Diff
对比同一写入点在 old/new 两个版本的 `TypeSchema`,输出 `SchemaChange` 列表。
**变更类型**(有结构差异即告警;`block` 下任意变更均阻断):
| 变更类型 | 示例 |
|----------|------|
| `FIELD_REMOVED` | 删除 `dbName` |
| `TYPE_CHANGED` | `linkList` 从数组变对象 |
| `WRAPPER_ADDED` | 顶层增加 `vo` 包装 |
| `FIELD_PATH_MOVED` | `dbName``vo.dbName` |
| `FIELD_ADDED` | 新增 `expiresAtMs` |
| `KEY_PATTERN_CHANGED` | key 常量变更 |
| `WRITE_POINT_REMOVED` | 删除缓存写入 |
| `WRITE_POINT_ADDED` | 新增缓存写入 |
| `LOW_CONFIDENCE` | 类型推断失败(仍提示,置信度较低) |
#### Step 8报告与通知
生成 `CheckReport`,包含:
- 仓库名、分支、old/new sha、提交人、时间
- 按 Key 聚合的结构变更(骨架 before/after+ 字段级明细
- 每项通用展示:**Key**、**位置**`Class#method:line`)、**类型**、旧/新序列化骨架
企微 Markdown 规则:
- 抬头不含 mode 汇总;正文按 Key 展示骨架
- **删除字段**:旧骨架中橙色 `<font color="warning">`
- **新增字段**:新骨架中绿色 `<font color="info">`
- key 未解析时展示源码表达式 + 灰色「key 未解析)」
- 单条超 4096 UTF-8 字节时按 Key 拆成多条依次发送
CI 控制台额外输出字段级变更明细(变更类型 + 位置 + 摘要),再打印与企微一致的 Markdown。
调用企微 Webhook 发送 Markdown支持 `--dry-run` 仅本地输出)。
#### Step 9退出码
| 条件 | 退出码 |
|------|--------|
| `enabled=false` / 无变更 | 0 |
| `mode=notify` 且存在变更 | 0仍通知 |
| `mode=block` 且存在任意结构变更 | 1 |
| 配置错误 / 执行异常 | 2 |
---
## 7. Redis Key 推断策略
### 7.1 自动推断
| 优先级 | 模式 | 示例 | 结果 |
|--------|------|------|------|
| 1 | 字符串字面量 | `"tenant:db:content:" + encode` | `tenant:db:content:*` |
| 2 | 常量引用 | `CACHE_KEY_PREFIX + encode` | 追溯常量值 |
| 3 | `String.format(CONST, args)` | `String.format(ATTENDANCE_BASE_SETTING_CACHE_KEY, tenantId)` | `fbt:attendance:base_setting:cache:*` |
| 4 | 方法调用 | `buildCacheKey(encode)` | 读取方法内 return 表达式 |
| 5 | 变量 | `redisKey` | `unknown-key` |
### 7.2 人工补充(配置)
```yaml
manual_mappings:
- id: tenant-db-content
writer_method: "jnpf.util.TenantDbContentCacheHelper#cacheSuccess"
key_pattern: "tenant:db:content:*"
value_type: "jnpf.util.TenantDbContentCacheHelper.CacheEnvelope"
```
当自动推断置信度低时,以 `manual_mappings` 为准。
---
## 8. 配置与开关设计
采用 **双层配置合并** 策略:
| 层级 | 位置 | 职责 |
|------|------|------|
| 默认配置 | 工具 jar 内 `default-config.yaml` | 检测模式、忽略规则等默认值 |
| 业务覆盖 | `jnpf-java-cloud/.gitea/config/serialization-schema-check-config.yaml` | mode、notify、include_modules、manual_mappings |
合并规则:**业务配置覆盖默认配置**,未声明的项沿用默认值。
CLI 调用:
```bash
java -jar serialization-schema-checker.jar \
--config .gitea/config/serialization-schema-check-config.yaml \
...
```
工具启动时自动加载 jar 内 `default-config.yaml`,再与 `--config` 指定的业务配置深度合并。
详见 `docs/配置说明.md`。核心开关:
```yaml
# 总开关false 时跳过检测与通知,流水线直接通过
enabled: true
# 运行模式notify仅通知| block检测到结构变更即阻断流水线
mode: notify
# 是否发送企微通知
notify:
enabled: true
webhook_url: "" # 企微 Webhook 完整 URL兼容旧字段 webhook_env
```
企微消息约定见 `docs/配置说明.md` §5**位置/类型**为每个 Key 的通用项;删除字段橙色、新增字段绿色。
---
## 9. CI 集成方案
详见 `docs/CI集成说明.md`。核心流程:
```yaml
# jnpf-java-cloud/.gitea/workflows/serialization-schema-check.yaml要点
# 检出:浅克隆 tipdepth 1
# 检测:--old-sha = gitea.event.before--new-sha = gitea.sha
# 按需 fetch before 提交对象,覆盖一次 push 的多 commit 累计 diff
```
完整模板见 `docs/CI集成说明.md` / `.gitea/workflows/serialization-schema-check.yaml`
---
## 10. 分阶段交付计划
### Phase 1 — MVP✅
**目标**:跑通端到端链路,覆盖租户缓存典型场景。
| 任务 | 产出 | 状态 |
|------|------|------|
| Maven 工程骨架 + CLI | 可执行 fat-jar | ✅ |
| Git diff 扫描 | 变更文件列表 | ✅ |
| W01~W03 写入点检测 | 覆盖 JSON 字符串写入 | ✅ |
| 基础 Schema 提取 | 支持普通类、内部类、List、嵌套 | ✅ |
| Schema Diff | 字段增删、包装、路径迁移 | ✅ |
| 企微通知 | 按 Key 骨架 Markdown | ✅ |
| notify/block / enabled | 配置驱动 | ✅ |
| 样本夹具测试 | TenantVO/CacheEnvelope 等 | ✅ |
**验收标准**
-`TenantDbContentCacheHelper` 的结构变更能输出报告并通知
- 流水线 push 后能收到企微通知
- `mode=block` 时任意结构变更导致 exit 1
### Phase 2 — 增强✅
| 任务 | 说明 | 状态 |
|------|------|------|
| W04/W05 模式 | RedisTemplate 直写对象、Hash 写入 | ✅ |
| 注解完整支持 | Fastjson/Jackson`@JsonIgnoreProperties` | ✅ |
| Key 推断增强 | `String.format`、常量拼接、`buildXxxKey` | ✅ |
| 忽略规则完善 | 锁/计数器/token/字面量/setIfAbsent | ✅ |
| 多模块性能 | 并行读文件、索引批量装载、`manual_mappings` | ✅ |
| 企微高亮 | 删除橙 `warning` / 新增绿 `info`;位置+类型通用项 | ✅ |
| W06 读侧辅助 | `parseObject`/`parseArray` 等补强写入 value 类型 | ✅ |
---
## 11. 测试策略
### 11.1 单元测试
- `SchemaDifferTest`:纯字段路径对比逻辑
- `JavaSchemaExtractorTest`:类字段展开、注解、内部类
- `RedisWritePointDetectorTest`:各种写入 AST 模式匹配
- `MqWritePointDetectorTest`RocketMQ/Kafka 投递(含 MQ04 MessageBuilder、MQ-K03 ProducerRecord
- `MqReadHintDetectorTest`MQ-R Listener / parseObject 补强
- `RedisKeyResolverTest`常量、format、拼接推断
### 11.2 样本夹具测试fixtures
「夹具」= 放在测试资源里的**脱敏源码样本**(不是连真实 Redis / 不是起 Gitea 流水线)。
路径:`src/test/resources/fixtures/`。测试代码加载这些 `.txt`/Java 片段,在内存中跑检测器 / Schema 对比,用来验证典型业务场景是否被正确识别。
| 夹具目录 | 验证点 |
|----------|--------|
| `fixtures/tenant/` | 包装结构变更TenantVO → CacheEnvelope |
| `fixtures/lock/` | 锁/计数器/token 应被忽略 |
| `fixtures/template/` | W04 Template 直写 |
| `fixtures/mq/rocket-wallet/` | RocketMQ syncSendMQ01 |
| `fixtures/mq/rocket-im/` | MessageBuilder + asyncSendMQ04、convertAndSendMQ03 |
| `fixtures/mq/kafka-patrol/` | Kafka List 根数组MQ-K01 |
| `fixtures/mq/kafka-record/` | ProducerRecordMQ-K03、JSON 字符串 sendMQ-K04 |
### 11.3 端到端测试
`jnpf-java-cloud` 开测试分支,故意提交一个 VO 字段变更,验证流水线 + 企微通知。
---
## 12. 风险与限制
| 风险 | 影响 | 缓解措施 |
|------|------|----------|
| 类型推断失败 | 漏报 | 标记 `LOW_CONFIDENCE`,配置 `manual_mappings` |
| Lombok 复杂注解 | 字段遗漏 | 基于源码字段 + 注解;后续 delombok |
| 同一 key 多分支写不同类型 | 误报 | 报告注明置信度;人工 suppression |
| 浅克隆拿不到 before | 漏检 / exit 2 | 按 SHA `fetch --depth 1` + deepen 兜底;见 CI 说明 |
| 一次 push 多 commit | 旧方案仅看末 commit 会漏检 | 已改为 `before..after` 累计对比 |
| 依赖 jar 中的类型 | 字段展开不完整 | 配置 `manual_mappings` 补充 |
| JsonUtil 实现不可见 | 序列化规则猜测 | 默认按字段名序列化;与 Fastjson 对齐 |
---
## 13. 已确认决策Grill 共识)
| # | 决策项 | 结论 |
|---|--------|------|
| 1 | 阻断范围 | `block` 模式下 **任意结构变更均阻断**exit 1 |
| 2 | 发布坐标 | 独立产物 `com.codechecker:serialization-schema-checker:1.0.0` |
| 3 | 配置归属 | **双层配置**jar 内 `default-config.yaml` + 业务仓覆盖合并 |
| 4 | 上线策略 | 先 `notify` ,稳定后手动切 `block` |
| 5 | 检测范围 | **仅 `src/main/java`**,不扫描测试代码 |
以上决策已纳入实施方案;**Phase 1 / Phase 2 已交付**。缓存侧可进入 Phase 3MQ 侧以 [`MQ序列化结构检测方案.md`](MQ序列化结构检测实施方案.md) 为准评审后开发。
相关文档:
| 文档 | 内容 |
|------|------|
| `docs/配置说明.md` | 缓存检测双层配置 |
| `docs/CI集成说明.md` | 流水线 before/after、排障 |
| `docs/MQ序列化结构检测方案.md` | MQ 消息体 Schema 监控方案(扩展) |
---
## 14. 附录:关键类设计草图
### WritePoint
```java
public class WritePoint {
String filePath;
int lineNumber;
String enclosingClass;
String enclosingMethod;
String keyExpression; // 原始 AST 表达式
String resolvedKeyPattern; // 推断结果,如 tenant:db:content:*
String valueExpression;
String resolvedValueType; // 全限定类名
double confidence; // 0.0 ~ 1.0
}
```
### SchemaChange
```java
public class SchemaChange {
ChangeType changeType; // FIELD_REMOVED, WRAPPER_ADDED, ...
String keyPattern;
String writeLocation; // class#method:line
String fieldPath; // 如 vo.dbName
String oldValue;
String newValue;
String message; // 人类可读描述
}
```
### CheckReport
```java
public class CheckReport {
String repository;
String branch;
String oldSha;
String newSha;
String modifier;
String modifyTime;
String mode;
List<SchemaChange> changes;
boolean blocked;
int exitCode;
}
```