feat: 二阶段
This commit is contained in:
@@ -144,7 +144,7 @@ com/codechecker/serialization-schema-checker/1.0.0/
|
||||
| 大量 Redis 误报 | 锁/计数器未过滤 | 补充 ignore.key_patterns |
|
||||
| 大量 MQ 误报 | 压测/临时 topic | 补充 ignore.mq_destinations |
|
||||
| commit 数显示为 1(实际多个) | 浅克隆下 `rev-list` 看不到中间提交 | 已修复:优先事件 `commits` 长度;并 deepen 到 before 为祖先 |
|
||||
| 漏报(模式/模块) | W0x/MQ 未开 / include_modules 过窄 | 确认 W01~W05 与 mq_patterns;检查模块过滤 |
|
||||
| 漏报(模式/模块) | W0x/MQ 未开 / include_modules 过窄 | 确认 W01~W05 与 mq_patterns(含 MQ03~05、MQ-K03/K04);检查 `mq_read_hints_enabled` 与模块过滤 |
|
||||
| 类型展开不完整 | 类型在依赖 jar 中 | 补充 `manual_mappings.value_type` |
|
||||
|
||||
---
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# MQ 消息体序列化结构变更检测 — 方案
|
||||
|
||||
> 版本:v0.3
|
||||
> 版本:v0.4
|
||||
> 日期:2026-07-15
|
||||
> 状态:**Phase M1 已实现**(MQ01/MQ02 + MQ-K01/MQ-K02);M2/M3 未启动
|
||||
> 状态:**Phase M1 + M2 已实现**(生产侧 MQ01~05 / MQ-K01~K04 + 读侧 MQ-R)
|
||||
> 关联:复用 `serialization-schema-checker` 的 Schema 提取、Diff、企微通知与 CI 框架
|
||||
> 业务样本仓:`jnpf-java-cloud`(**RocketMQ + Kafka**)
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
在 push 时静态分析 **消息体类型的序列化 Schema** 是否相对对比区间发生变更,覆盖 **RocketMQ + Kafka**,并复用现有企微通知 / notify|block 能力。
|
||||
|
||||
### 1.3 非目标(本方案首版)
|
||||
### 1.3 非目标
|
||||
|
||||
- 不连接真实 Broker,不拉取积压消息做运行时校验
|
||||
- 不解析依赖 jar 内消息类型(仅本仓 `src/main/java`)
|
||||
@@ -56,8 +56,9 @@
|
||||
| `rocketMQTemplate.syncSend(dest, dto)` | 高(如钱包扣费) | **纳入** |
|
||||
| `asyncSend` / `syncSendOrderly` 等 | 中 | **纳入** |
|
||||
| `convertAndSend` | 视封装而定 | **纳入** |
|
||||
| 先 `JSON.toJSONString` 再发 String | 较低 | unwrap 后取类型 |
|
||||
| 只发 `String` / `byte[]` / `MessageExt` | 有 | **默认忽略** |
|
||||
| `MessageBuilder.withPayload` 再 send | 中(如 IM 延时消息) | **纳入**(MQ04) |
|
||||
| 先 `JSON.toJSONString` 再发 String | 较低 | unwrap 后取类型(MQ05) |
|
||||
| 只发 `String` / `byte[]` / 无泛型 `Message` | 有 | **默认忽略** |
|
||||
|
||||
样本(资金钱包):
|
||||
|
||||
@@ -74,6 +75,8 @@ public class WalletDeductConsumer implements RocketMQListener<WalletDeductReq> {
|
||||
|------|----------|------|
|
||||
| `kafkaTemplate.send(topic, dto)` | 中(值班食安项等) | **纳入** |
|
||||
| `kafkaTemplate.send(topic, List<Xxx>)` | 中(巡店食安项列表) | **纳入**(rootArray) |
|
||||
| `kafkaTemplate.send(ProducerRecord)` | 中 | **纳入**(MQ-K03) |
|
||||
| 先 JSON 再 `send(topic, json)` | 较低 | unwrap(MQ-K04) |
|
||||
| `@KafkaListener` + `String` + `JSONObject.parseObject(..., Xxx.class)` | 有(数据分析中差评) | **读侧补强 MQ-R** |
|
||||
| `KafkaTopicUtil` 仅创建 Topic | 有(租户) | **忽略**(无消息体) |
|
||||
|
||||
@@ -118,6 +121,8 @@ public void handleMessage(String message) {
|
||||
| RocketMQ | `RocketMQListener<T>`、`onMessage(T)` + `@RocketMQMessageListener` |
|
||||
| Kafka | `@KafkaListener` 方法参数类型;或方法内 `parseObject/parseArray(..., Xxx.class)`(与现有 W06 共享解析能力) |
|
||||
|
||||
开关:`detection.mq_read_hints_enabled`(默认 **true**)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 方案总览
|
||||
@@ -135,8 +140,8 @@ public void handleMessage(String message) {
|
||||
```text
|
||||
Git Diff → 变更 Java 文件
|
||||
├─ Redis:W01~W05 + W06 ← 已有
|
||||
└─ MQ:RocketMQ(MQ01~)+ Kafka(MQ-K*)
|
||||
+ Listener / parse 补强(MQ-R) ← 本方案
|
||||
└─ MQ:RocketMQ(MQ01~05)+ Kafka(MQ-K01~K04)
|
||||
+ Listener / parse 补强(MQ-R) ← 已实现
|
||||
↓
|
||||
同一套 TypeSchema / SchemaDiffer / Skeleton / WeCom
|
||||
```
|
||||
@@ -161,7 +166,7 @@ Git Diff → 变更 Java 文件
|
||||
| MQ01 | `rocketMQTemplate.syncSend(dest, payload, …)` | dest、payload 类型 |
|
||||
| MQ02 | `asyncSend` / `syncSendOrderly` / `sendOneWay` 等 | 同上 |
|
||||
| MQ03 | `convertAndSend(dest, payload)` | 同上 |
|
||||
| MQ04 | `MessageBuilder.withPayload(obj)` 再 send | payload 类型 |
|
||||
| MQ04 | `MessageBuilder.withPayload(obj)` 再 send,或 `Message<T>` | payload / `T` |
|
||||
| MQ05 | 先 `toJSONString`/`getObjectToString` 再 send String | unwrap 后类型 |
|
||||
|
||||
### 4.2 生产侧 — Kafka
|
||||
@@ -170,7 +175,7 @@ Git Diff → 变更 Java 文件
|
||||
|---------|------------|------|
|
||||
| MQ-K01 | `kafkaTemplate.send(topic, payload)` | topic、payload 类型 |
|
||||
| MQ-K02 | `kafkaTemplate.send(topic, key, payload)` | 同上(忽略分区 key) |
|
||||
| MQ-K03 | `send(ProducerRecord)` / `ListenableFuture` 封装若可解析 | topic + value 类型 |
|
||||
| MQ-K03 | `send(ProducerRecord)` | topic + value 类型 |
|
||||
| MQ-K04 | 先 JSON 序列化为 String 再 `send(topic, json)` | unwrap 后类型 |
|
||||
|
||||
Payload 为 `List<Xxx>` / `Collection` 时标记 **rootArray**,骨架为 JSON 数组(与 Redis List 一致)。
|
||||
@@ -187,7 +192,7 @@ Payload 为 `List<Xxx>` / `Collection` 时标记 **rootArray**,骨架为 JSON
|
||||
|---------|------|------|
|
||||
| MQ-R01 | `RocketMQListener<T>` / `@RocketMQMessageListener` | 补强同 destination 生产点 |
|
||||
| MQ-R02 | `@KafkaListener` + 参数类型 `T`(非 String) | 补强同 topic |
|
||||
| MQ-R03 | Listener 内 `parseObject`/`parseArray(..., Xxx.class)` | 补强(可复用 W06 检测器) |
|
||||
| MQ-R03 | Listener 内 `parseObject`/`parseArray(..., Xxx.class)` | 补强(复用 parse AST) |
|
||||
|
||||
开关:`detection.mq_read_hints_enabled`(默认 true)。
|
||||
|
||||
@@ -220,7 +225,6 @@ Payload 为 `List<Xxx>` / `Collection` 时标记 **rootArray**,骨架为 JSON
|
||||
|
||||
- 删除字段橙 `warning`;新增绿 `info`
|
||||
- destination 未解析时灰色提示
|
||||
- 可选后缀:「请评估消费积压与兼容反序列化」
|
||||
|
||||
「通道」字段用于区分中间件;若模板求简,可省略通道仅靠 Topic 形态区分。
|
||||
|
||||
@@ -272,37 +276,36 @@ manual_mappings:
|
||||
|
||||
## 7. 分阶段交付
|
||||
|
||||
### Phase M1 — MVP(RocketMQ + Kafka 基础投递)
|
||||
### Phase M1 — MVP(RocketMQ + Kafka 基础投递)✅
|
||||
|
||||
| 任务 | 说明 |
|
||||
|------|------|
|
||||
| MQ01/MQ02 | RocketMQ `syncSend` / `asyncSend` |
|
||||
| MQ-K01/MQ-K02 | Kafka `send(topic, payload)` / 三参 send |
|
||||
| destination 推断 | 字面量、常量、拼接;Kafka 动态 topic → `*` |
|
||||
| Schema Diff + 骨架通知 | 复用 ReportBuilder;可选「通道」行 |
|
||||
| Schema Diff + 骨架通知 | 复用 ReportBuilder;「通道」行 |
|
||||
| 夹具 | `fixtures/mq/rocket-wallet/`、`fixtures/mq/kafka-patrol/` |
|
||||
| 配置 | `mq_patterns`(含 MQ-K*)、`ignore.mq_destinations` |
|
||||
| List 根数组骨架 | `List<CheckItemDetailVo>` 等(与 M2 需求合并交付) |
|
||||
|
||||
**验收**:
|
||||
|
||||
1. 删 `WalletDeductReq` 字段 → 企微出现 RocketMQ Topic 骨架变更
|
||||
2. 删 `CheckItemDetailVo` 字段 → 企微出现 Kafka Topic 骨架变更
|
||||
|
||||
### Phase M2 — 增强
|
||||
### Phase M2 — 增强 ✅
|
||||
|
||||
| 任务 | 说明 |
|
||||
|------|------|
|
||||
| MQ03~MQ05、MQ-K03/K04 | convertAndSend、MessageBuilder、JSON 字符串发送、ProducerRecord |
|
||||
| MQ03~MQ05、MQ-K03/K04 | convertAndSend、MessageBuilder/`Message<T>`、JSON 字符串发送、ProducerRecord |
|
||||
| MQ-R01~R03 | RocketMQ Listener + Kafka `@KafkaListener` / parse 补强 |
|
||||
| List 根数组骨架 | 巡店 `List<CheckItemDetailVo>` 等 |
|
||||
| 夹具 | `fixtures/mq/rocket-im/`、`fixtures/mq/kafka-record/` |
|
||||
|
||||
### Phase M3 — 运营
|
||||
**验收**:
|
||||
|
||||
| 任务 | 说明 |
|
||||
|------|------|
|
||||
| 积压风险提示文案 | 统一提示评估消费 lag / 积压 |
|
||||
| 更多夹具 | 值班 Kafka、中差评 Listener、IM Favorite 等 |
|
||||
| 分 webhook / 标题前缀 | 缓存 vs MQ 可选拆分 |
|
||||
1. `MessageBuilder.withPayload(DutyImNotice)` + `asyncSend` → 命中 MQ04,改 VO 字段可告警
|
||||
2. `kafkaTemplate.send(ProducerRecord)` → 命中 MQ-K03
|
||||
3. Listener / `parseObject` 可补强同 Topic 弱类型生产点
|
||||
|
||||
---
|
||||
|
||||
@@ -312,10 +315,11 @@ manual_mappings:
|
||||
|------|------|
|
||||
| Kafka topic 按租户动态拼接 | 归一 `prefix:*`;`manual_mappings` |
|
||||
| RocketMQ / Kafka 混用同一 VO | 各投递点独立告警(符合预期) |
|
||||
| Listener 收 String、parse 在方法深处 | MQ-R03 + 复用 W06 AST |
|
||||
| Listener 收 String、parse 在方法深处 | MQ-R03 + parse AST |
|
||||
| 生产/消费跨模块对不齐 | 同仓索引 + destination 对齐;失败则仅写侧 |
|
||||
| Jackson / Fastjson / Kafka JsonSerializer 细节差 | 首版字段名;必要时方言或 mapping |
|
||||
| 只改消费未改生产类型 | 不告警(工具职责是消息体 Schema) |
|
||||
| `include_modules` 过窄 | 观察期扩大模块或置空全仓 |
|
||||
|
||||
---
|
||||
|
||||
@@ -334,6 +338,6 @@ manual_mappings:
|
||||
## 10. 下一步
|
||||
|
||||
1. ~~按 Phase M1 开发~~ **已完成**(MQ01/02 + MQ-K01/K02)
|
||||
2. Phase M2:MQ03~05、MQ-K03/K04、MQ-R 读侧补强
|
||||
3. 业务仓验收 Topic:`capital-topic:WALLET_DEDUCT`(RocketMQ)、巡店/值班 Kafka topic
|
||||
4. 观察期按需扩大 `include_modules`
|
||||
2. ~~按 Phase M2 开发~~ **已完成**(MQ03~05、MQ-K03/K04、MQ-R)
|
||||
3. 业务仓验收:`MessageBuilder` IM Topic、巡店/值班 Kafka、`capital-topic:WALLET_DEDUCT`
|
||||
4. 观察期按需扩大 `include_modules`
|
||||
|
||||
18
docs/实施方案.md
18
docs/实施方案.md
@@ -535,20 +535,20 @@ notify:
|
||||
| 误报反馈 | `suppressions` 按写入点 / change_types 精细忽略 |
|
||||
| 更多业务场景覆盖 | 考勤、文件下载进度等 |
|
||||
|
||||
### Phase 4 — MQ 消息体结构检测(方案已落地,含 Kafka,开发待启)
|
||||
### Phase 4 — MQ 消息体结构检测(已落地,含 Kafka)
|
||||
|
||||
业务仓同时存在:
|
||||
|
||||
- **RocketMQ**:`RocketMQTemplate.syncSend(topic:tag, dto)` / `RocketMQListener<T>`
|
||||
- **Kafka**:`KafkaTemplate.send(topic, vo|List)` / `@KafkaListener` + `parseObject`
|
||||
- **RocketMQ**:`RocketMQTemplate.syncSend` / `asyncSend` / `MessageBuilder` / `RocketMQListener<T>`
|
||||
- **Kafka**:`KafkaTemplate.send(topic|ProducerRecord, …)` / `@KafkaListener` + `parseObject`
|
||||
|
||||
消息体字段变更会导致积压旧消息反序列化失败,风险模型与 Redis 同类。
|
||||
|
||||
| 任务 | 说明 | 状态 |
|
||||
|------|------|------|
|
||||
| 方案文档 | RocketMQ + Kafka 模式、destination、复用 Schema Diff/企微 | ✅ 见专用文档 |
|
||||
| Phase M1 | RocketMQ syncSend + Kafka send + 骨架通知 + 夹具 | 待启 |
|
||||
| Phase M2/M3 | Listener/parse 补强、更多投递形态、运营 | 待启 |
|
||||
| 方案文档 | RocketMQ + Kafka 模式、destination、复用 Schema Diff/企微 | ✅ |
|
||||
| Phase M1 | RocketMQ sync/async + Kafka send + 骨架通知 + 夹具 | ✅ |
|
||||
| Phase M2 | MessageBuilder / convertAndSend / ProducerRecord / JSON 字符串 + MQ-R | ✅ |
|
||||
|
||||
**专用方案:** [`docs/MQ序列化结构检测方案.md`](./MQ序列化结构检测方案.md)
|
||||
|
||||
@@ -561,6 +561,8 @@ notify:
|
||||
- `SchemaDifferTest`:纯字段路径对比逻辑
|
||||
- `JavaSchemaExtractorTest`:类字段展开、注解、内部类
|
||||
- `RedisWritePointDetectorTest`:各种写入 AST 模式匹配
|
||||
- `MqWritePointDetectorTest`:RocketMQ/Kafka 投递(含 MQ04 MessageBuilder、MQ-K03 ProducerRecord)
|
||||
- `MqReadHintDetectorTest`:MQ-R Listener / parseObject 补强
|
||||
- `RedisKeyResolverTest`:常量、format、拼接推断
|
||||
|
||||
### 11.2 样本夹具测试(fixtures)
|
||||
@@ -574,6 +576,10 @@ notify:
|
||||
| `fixtures/tenant/` | 包装结构变更(TenantVO → CacheEnvelope) |
|
||||
| `fixtures/lock/` | 锁/计数器/token 应被忽略 |
|
||||
| `fixtures/template/` | W04 Template 直写 |
|
||||
| `fixtures/mq/rocket-wallet/` | RocketMQ syncSend(MQ01) |
|
||||
| `fixtures/mq/rocket-im/` | MessageBuilder + asyncSend(MQ04)、convertAndSend(MQ03) |
|
||||
| `fixtures/mq/kafka-patrol/` | Kafka List 根数组(MQ-K01) |
|
||||
| `fixtures/mq/kafka-record/` | ProducerRecord(MQ-K03)、JSON 字符串 send(MQ-K04) |
|
||||
|
||||
### 11.3 端到端测试
|
||||
|
||||
|
||||
29
docs/配置说明.md
29
docs/配置说明.md
@@ -43,9 +43,9 @@ include_modules:
|
||||
由 `redisCheck` 仓库维护,随 jar 发布,默认包含:
|
||||
|
||||
- `detection.patterns`:**W01~W05**(JSON 字符串写入 + Template 直写 + Hash)
|
||||
- `detection.mq_patterns`:**MQ01/MQ02、MQ-K01/MQ-K02**(RocketMQ / Kafka 生产侧)
|
||||
- `detection.mq_patterns`:**MQ01~MQ05、MQ-K01~MQ-K04**(RocketMQ / Kafka 生产侧)
|
||||
- `detection.read_hints_enabled`:W06 读侧反序列化类型辅助(默认 true)
|
||||
- `detection.mq_read_hints_enabled`:MQ 读侧补强(Phase M2,默认 false)
|
||||
- `detection.mq_read_hints_enabled`:MQ-R 读侧 Listener / parse 补强(默认 true)
|
||||
- `ignore.key_patterns`(锁 / 计数器 / token)
|
||||
- `ignore.mq_destinations`(MQ destination 忽略,默认真空)
|
||||
- `detection.min_confidence`、`max_field_depth`
|
||||
@@ -113,18 +113,23 @@ detection:
|
||||
- W04 # redisTemplate 直写对象
|
||||
- W05 # opsForHash().put
|
||||
|
||||
# MQ 生产侧投递(Phase M1)
|
||||
# MQ 生产侧投递(Phase M1 + M2)
|
||||
mq_patterns:
|
||||
- MQ01 # rocketMQTemplate.syncSend
|
||||
- MQ02 # asyncSend / syncSendOrderly / sendOneWay
|
||||
- MQ03 # convertAndSend
|
||||
- MQ04 # MessageBuilder.withPayload / Message<T>
|
||||
- MQ05 # 先 toJSONString 再 send String
|
||||
- MQ-K01 # kafkaTemplate.send(topic, payload)
|
||||
- MQ-K02 # kafkaTemplate.send(topic, key, payload)
|
||||
- MQ-K03 # kafkaTemplate.send(ProducerRecord)
|
||||
- MQ-K04 # 先 JSON 序列化为 String 再 send
|
||||
|
||||
# W06:读侧反序列化类型辅助(不产生独立告警)
|
||||
read_hints_enabled: true
|
||||
|
||||
# MQ-R 读侧补强(Phase M2,默认关闭)
|
||||
mq_read_hints_enabled: false
|
||||
# MQ-R 读侧补强(Listener / parseObject)
|
||||
mq_read_hints_enabled: true
|
||||
|
||||
# 类型推断最低置信度,低于此值标记为低置信度提示
|
||||
min_confidence: 0.6
|
||||
@@ -240,7 +245,7 @@ detection:
|
||||
- **不是写入模式**:不会单独因为「多了一处 parse」而告警
|
||||
- 能关联到 `redis get(key)` 时,还可补强 unresolved key
|
||||
- 覆盖优先级:`manual_mappings` > W06 > 写侧 AST
|
||||
- **仅补强 Redis 写入点**;MQ 投递点不走 W06(MQ-R 为 Phase M2)
|
||||
- **仅补强 Redis 写入点**;MQ 投递点走 `mq_read_hints_enabled`(MQ-R)
|
||||
|
||||
关闭示例:
|
||||
|
||||
@@ -249,12 +254,12 @@ detection:
|
||||
read_hints_enabled: false
|
||||
```
|
||||
|
||||
### 3.5.2 detection.mq_patterns / ignore.mq_destinations(MQ Phase M1)
|
||||
### 3.5.2 detection.mq_patterns / ignore.mq_destinations(MQ)
|
||||
|
||||
| 配置项 | 说明 |
|
||||
|--------|------|
|
||||
| `mq_patterns` | `MQ01`/`MQ02`(RocketMQ)、`MQ-K01`/`MQ-K02`(Kafka);默认已开启 |
|
||||
| `mq_read_hints_enabled` | MQ-R 读侧补强,**默认 false** |
|
||||
| `mq_patterns` | RocketMQ:`MQ01`~`MQ05`;Kafka:`MQ-K01`~`MQ-K04`;默认已全部开启 |
|
||||
| `mq_read_hints_enabled` | MQ-R 读侧补强(Listener / parse),**默认 true** |
|
||||
| `ignore.mq_destinations` | 忽略 destination(glob),如 `benchmark:*` |
|
||||
|
||||
企微 MQ 块:`- Topic --> ...`,并带 `> **通道**: RocketMQ|Kafka`。
|
||||
@@ -375,10 +380,10 @@ include_modules: [] # 全仓
|
||||
|
||||
---
|
||||
|
||||
## 7. MQ 消息体检测(Phase M1 已落地)
|
||||
## 7. MQ 消息体检测(Phase M1 + M2 已落地)
|
||||
|
||||
同一 jar 默认启用 RocketMQ / Kafka **生产侧**投递检测(`detection.mq_patterns`),与 Redis 共用 Diff、骨架与企微模板。
|
||||
同一 jar 默认启用 RocketMQ / Kafka **生产侧**投递检测(`detection.mq_patterns`)与 **MQ-R** 读侧补强,与 Redis 共用 Diff、骨架与企微模板。
|
||||
|
||||
- 配置说明见 §3.5.2
|
||||
- 读侧 MQ-R、`MessageBuilder` / `ProducerRecord` 等见方案 Phase M2
|
||||
- 含 `MessageBuilder` / `convertAndSend` / `ProducerRecord` / JSON 字符串发送、Listener/`parseObject` 补强
|
||||
- 完整设计:[MQ序列化结构检测方案.md](./MQ序列化结构检测方案.md)
|
||||
Reference in New Issue
Block a user