All checks were successful
序列化结构检查 / serialization-schema-check (push) Successful in 3s
381 lines
12 KiB
Markdown
381 lines
12 KiB
Markdown
# 序列化结构检测 — 配置说明
|
||
|
||
> **双层配置**:工具 jar 内置 `default-config.yaml`(默认) + 业务仓库 `.gitea/config/serialization-schema-check-config.yaml`(覆盖)
|
||
> 工具坐标:`com.codechecker:serialization-schema-checker:1.0.0`
|
||
> 主类:`com.codechecker.cache.cli.SerializationSchemaCheckerMain`
|
||
|
||
---
|
||
|
||
## 1. 配置合并机制
|
||
|
||
```text
|
||
jar 内 default-config.yaml(工具仓维护)
|
||
↓ 深度合并
|
||
业务仓 serialization-schema-check-config.yaml(业务仓维护)
|
||
↓
|
||
最终生效配置
|
||
```
|
||
|
||
- 业务配置**仅需写差异项**,不必复制全部默认规则
|
||
- 升级 jar 时,默认忽略规则/检测模式自动跟随工具版本演进
|
||
- 业务仓必须存在 `--config` 指定的配置文件(可为仅含 `mode` 的最小文件)
|
||
|
||
### 1.1 业务仓最小配置示例
|
||
|
||
```yaml
|
||
# jnpf-java-cloud/.gitea/config/serialization-schema-check-config.yaml
|
||
enabled: true
|
||
mode: notify
|
||
|
||
notify:
|
||
enabled: true
|
||
# 推荐直接写完整 Webhook;也可用环境变量在流水线注入
|
||
webhook_url: ""
|
||
|
||
include_modules:
|
||
- jnpf-tenant
|
||
```
|
||
|
||
流水线通常把 Secret 注入环境或配置文件中的 `webhook_url`。兼容旧字段:`webhook_env`(值为 `http` 开头时当作 URL 使用)。
|
||
|
||
### 1.2 工具内置默认配置(jar 内 default-config.yaml)
|
||
|
||
由 `redisCheck` 仓库维护,随 jar 发布,默认包含:
|
||
|
||
- `detection.patterns`:**W01~W05**(JSON 字符串写入 + Template 直写 + Hash)
|
||
- `detection.mq_patterns`:**MQ01~MQ05、MQ-K01~MQ-K04**(RocketMQ / Kafka 生产侧)
|
||
- `detection.read_hints_enabled`:W06 读侧反序列化类型辅助(默认 true)
|
||
- `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`
|
||
- `mode: notify`、`enabled: true`
|
||
|
||
---
|
||
|
||
## 2. 业务仓完整配置示例
|
||
|
||
```yaml
|
||
# 总开关:false 时不执行检测、不发通知、流水线直接通过
|
||
enabled: true
|
||
|
||
# 运行模式
|
||
# notify - 仅通知,不阻断流水线
|
||
# block - 检测到结构变更即阻断流水线(exit 1)
|
||
mode: notify
|
||
|
||
# 是否扫描测试代码(已确认:不扫描)
|
||
scan_test_sources: false
|
||
|
||
# 源码扫描根目录(仅 main,不含 test)
|
||
source_roots:
|
||
- "src/main/java"
|
||
|
||
# 通知配置
|
||
notify:
|
||
enabled: true
|
||
# Webhook 完整 URL(优先)
|
||
webhook_url: ""
|
||
# 兼容旧字段:值为 http 开头时视为 URL
|
||
# webhook_env: WECOM_ROBOT_WEBHOOK
|
||
# 无变更时是否也发通知(一般 false)
|
||
notify_on_clean: false
|
||
# 消息标题前缀
|
||
title_prefix: "[序列化结构变更]"
|
||
|
||
# 忽略规则
|
||
ignore:
|
||
# 忽略的 key 模式(glob)
|
||
key_patterns:
|
||
- "*:lock"
|
||
- "*:lock:*"
|
||
- "*lock*"
|
||
- "loginCount:*"
|
||
- "Authorization:*"
|
||
|
||
# 忽略的文件路径模式
|
||
file_patterns:
|
||
- "**/test/**"
|
||
|
||
# 忽略的写入方法(类全名#方法名)
|
||
writer_methods: []
|
||
|
||
# 忽略的 MQ destination(topic 或 topic:tag,glob)
|
||
mq_destinations: []
|
||
|
||
# 检测规则
|
||
detection:
|
||
# 启用的写入模式(默认已全部开启)
|
||
patterns:
|
||
- W01 # redisUtil.insert + JSON.toJSONString
|
||
- W02 # redisTemplate.opsForValue().set + JSON.toJSONString
|
||
- W03 # stringRedisTemplate + JsonUtil.getObjectToString
|
||
- W04 # redisTemplate 直写对象
|
||
- W05 # opsForHash().put
|
||
|
||
# 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 读侧补强(Listener / parseObject)
|
||
mq_read_hints_enabled: true
|
||
|
||
# 类型推断最低置信度,低于此值标记为低置信度提示
|
||
min_confidence: 0.6
|
||
|
||
# 字段展开最大深度(防止循环引用死循环)
|
||
max_field_depth: 8
|
||
|
||
# 人工补充映射(自动推断失败或需精确指定时使用)
|
||
manual_mappings:
|
||
- id: tenant-db-content
|
||
writer_method: "jnpf.util.TenantDbContentCacheHelper#cacheSuccess"
|
||
key_pattern: "tenant:db:content:*"
|
||
value_type: "jnpf.util.TenantDbContentCacheHelper.CacheEnvelope"
|
||
description: "租户库信息缓存"
|
||
|
||
- id: attendance-base-setting
|
||
writer_method: "jnpf.attendance.service.impl.AttendanceBaseSettingServiceImpl#getStringAttendanceBaseSettingMap"
|
||
key_pattern: "fbt:attendance:base_setting:cache:*"
|
||
value_type: "java.util.Map"
|
||
description: "考勤基础设置缓存(Map<String, AttendanceBaseSetting>)"
|
||
|
||
# 抑制规则(已知误报)
|
||
suppressions:
|
||
- id: ignore-export-progress
|
||
key_pattern: "file:download:user:progress:*"
|
||
reason: "导出进度缓存,结构变更不影响业务读取"
|
||
|
||
# 模块过滤(可选,默认扫描全部模块)
|
||
include_modules:
|
||
- jnpf-tenant
|
||
- jnpf-ftb
|
||
- jnpf-file
|
||
- fantaibao-data-analysis
|
||
|
||
# exclude_modules: []
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 配置项说明
|
||
|
||
### 3.1 enabled
|
||
|
||
总开关。默认 `true`。
|
||
|
||
| 值 | 行为 |
|
||
|----|------|
|
||
| `true` | 正常执行检测(再按 `mode` / `notify` 行为) |
|
||
| `false` | 跳过检测与通知,流水线 `exit 0`(与 `notify.enabled` 无关) |
|
||
|
||
临时关闭时可仅改此项,无需删除 workflow。
|
||
|
||
### 3.2 mode
|
||
|
||
| 值 | 行为 |
|
||
|----|------|
|
||
| `notify` | 检测到变更 → 发企微 → `exit 0` |
|
||
| `block` | 检测到任意结构变更 → 发企微 → `exit 1` |
|
||
|
||
建议上线初期仍使用 `mode: notify` 观察误报情况,确认稳定后再切换:
|
||
|
||
```yaml
|
||
mode: block
|
||
```
|
||
|
||
### 3.3 notify
|
||
|
||
| 字段 | 类型 | 默认值 | 说明 |
|
||
|------|------|--------|------|
|
||
| `enabled` | boolean | true | 是否发企微 |
|
||
| `webhook_url` | string | `""` | 企微机器人 Webhook 完整 URL(优先) |
|
||
| `webhook_env` | string | — | 兼容旧字段;值为 `http` 开头时当作 URL |
|
||
| `notify_on_clean` | boolean | false | 无变更时是否通知 |
|
||
| `title_prefix` | string | [序列化结构变更] | 消息标题前缀 |
|
||
|
||
### 3.4 ignore.key_patterns
|
||
|
||
支持 glob:
|
||
|
||
- `*` 匹配单层
|
||
- `**` 匹配多层
|
||
|
||
内置/代码层常见过滤:
|
||
|
||
- 分布式锁 key(配置 glob + 方法名忽略)
|
||
- 登录计数、session/token
|
||
- 琐碎 value:字面量、`"1"`、`UUID.randomUUID()` 等
|
||
- 方法:`setIfAbsent` / `increment` / `delete` / `expire` 等
|
||
|
||
### 3.5 detection.patterns
|
||
|
||
| 模式 | 说明 | 状态 |
|
||
|------|------|------|
|
||
| W01 | `redisUtil.insert(key, JSON.toJSONString(x), ttl)` | 已启用 |
|
||
| W02 | `redisTemplate.opsForValue().set(key, JSON.toJSONString(x), ...)` | 已启用 |
|
||
| W03 | `stringRedisTemplate.opsForValue().set(key, JsonUtil.getObjectToString(x), ...)` | 已启用 |
|
||
| W04 | `redisTemplate.opsForValue().set(key, obj, ...)` 直写对象 | 已启用(Phase 2) |
|
||
| W05 | `redisTemplate.opsForHash().put(key, field, obj)` | 已启用(Phase 2) |
|
||
|
||
业务仓可通过只声明子集暂时关闭某些模式,例如仅保留 JSON 写入:
|
||
|
||
```yaml
|
||
detection:
|
||
patterns: [W01, W02, W03]
|
||
```
|
||
|
||
### 3.5.1 detection.read_hints_enabled(W06 辅助)
|
||
|
||
| 字段 | 类型 | 默认 | 说明 |
|
||
|------|------|------|------|
|
||
| `read_hints_enabled` | boolean | true | 扫描 `parseObject` / `parseArray` / `getJsonToBean` 等,补强同文件或同 key 写入点的 value 类型 |
|
||
|
||
- **不是写入模式**:不会单独因为「多了一处 parse」而告警
|
||
- 能关联到 `redis get(key)` 时,还可补强 unresolved key
|
||
- 覆盖优先级:`manual_mappings` > W06 > 写侧 AST
|
||
- **仅补强 Redis 写入点**;MQ 投递点走 `mq_read_hints_enabled`(MQ-R)
|
||
|
||
关闭示例:
|
||
|
||
```yaml
|
||
detection:
|
||
read_hints_enabled: false
|
||
```
|
||
|
||
### 3.5.2 detection.mq_patterns / ignore.mq_destinations(MQ)
|
||
|
||
| 配置项 | 说明 |
|
||
|--------|------|
|
||
| `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`。
|
||
|
||
详见:[MQ序列化结构检测方案.md](MQ序列化结构检测实施方案.md)
|
||
|
||
### 3.6 manual_mappings
|
||
|
||
当自动推断不准确时使用。匹配优先级 **高于** 自动推断(按 `类全名#方法名` 覆盖 key 模式与 value 类型)。
|
||
|
||
| 字段 | 必填 | 说明 |
|
||
|------|------|------|
|
||
| `id` | 是 | 唯一标识 |
|
||
| `writer_method` | 否 | `类全名#方法名`,精确匹配写入点 |
|
||
| `key_pattern` | 否 | 精确 key 模式 |
|
||
| `value_type` | 否 | 强制指定 value 类型 |
|
||
| `description` | 否 | 备注 |
|
||
|
||
### 3.7 suppressions
|
||
|
||
用于屏蔽已知可接受的变更:
|
||
|
||
```yaml
|
||
suppressions:
|
||
- id: my-suppression
|
||
key_pattern: "file:download:user:progress:*"
|
||
change_types:
|
||
- FIELD_ADDED
|
||
reason: "新增字段向后兼容"
|
||
```
|
||
|
||
### 3.8 include_modules / exclude_modules
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| `include_modules` | 非空时仅扫描列出的顶层模块;空表示全仓 |
|
||
| `exclude_modules` | 始终排除的顶层模块 |
|
||
|
||
顶层模块取路径第一段,例如 `jnpf-tenant/jnpf-tenant-biz/src/main/java/...` → `jnpf-tenant`。
|
||
|
||
---
|
||
|
||
## 4. 环境变量 / Secret
|
||
|
||
| 变量 | 必填 | 说明 |
|
||
|------|------|------|
|
||
| `WECOM_ROBOT_WEBHOOK` | 视流水线写法 | 可将 Secret 写入配置中的 `webhook_url`,或在启动前注入 |
|
||
|
||
在 Gitea 仓库 Settings → Secrets 中配置。
|
||
|
||
---
|
||
|
||
## 5. 企微消息格式
|
||
|
||
### 5.1 结构说明
|
||
|
||
- 抬头:仓库、分支、提交、提交人、时间(不展示 mode)
|
||
- 正文:按 **一个 Redis Key 一块**,展示位置、类型与前后序列化骨架
|
||
- 超长(UTF-8 > 4096 字节)时按 key **拆成多条**消息依次发送
|
||
- CI 控制台另打「字段明细」(变更类型 / 位置 / 摘要),企微侧按骨架展示
|
||
|
||
### 5.2 字段高亮颜色
|
||
|
||
| 变更 | 企微颜色 | Markdown |
|
||
|------|----------|----------|
|
||
| 字段删除(标在旧骨架) | 橙色 | `<font color="warning">…</font>` |
|
||
| 字段新增 / 包装层(标在新骨架) | 绿色 | `<font color="info">…</font>` |
|
||
| 路径迁移 | 旧橙 / 新绿 | 同上 |
|
||
| key 未解析提示 | 灰色 | `<font color="comment">(key 未解析)</font>` |
|
||
|
||
### 5.3 示例(已解析 key)
|
||
|
||
```markdown
|
||
## [序列化结构变更] jnpf-java-cloud
|
||
|
||
> **分支**: code/redis_change_detection_v1.0
|
||
> **提交**: cedd161c → 67c8a6eb
|
||
> **提交人**: dongzi
|
||
> **时间**: 2026-07-13 16:54:17
|
||
|
||
- Key --> `tenant:db:content:*`
|
||
> **位置**: `TenantDbContentCacheHelper#cacheSuccess:92`
|
||
> **类型**: `CacheEnvelope`
|
||
> **value值由:** “{"dbName":"","linkList":[{"id":""}]}”
|
||
> **变更为:** “…(仅新增/迁移字段片段带 <font color="info">绿色</font>)…”
|
||
```
|
||
|
||
实际发送时仅对改动属性片段染色:新增 → `info`(绿),删除 → `warning`(橙)。
|
||
|
||
### 5.4 示例(key 未解析)
|
||
|
||
```markdown
|
||
- Key --> `req.getKey()` <font color="comment">(key 未解析)</font>
|
||
> **位置**: `ClockInXxxService#export:128`
|
||
> **类型**: `List<ClockInExportVo>`
|
||
> **value值由:** “{"a":""}”
|
||
> **变更为:** “…新增字段带绿色高亮…”
|
||
```
|
||
|
||
未解析时按「写入位置 + key 表达式」拆分聚合,避免多个未知 key 串在一起。
|
||
|
||
---
|
||
|
||
## 6. 推荐上线配置
|
||
|
||
业务仓默认配置:
|
||
|
||
```yaml
|
||
enabled: true
|
||
mode: notify
|
||
|
||
notify:
|
||
enabled: true
|
||
webhook_url: "" # 由流水线写入真实 Webhook
|
||
|
||
include_modules: [] # 全仓
|
||
```
|
||
|
||
--- |