feat: first commit
This commit is contained in:
204
docs/CI集成说明.md
Normal file
204
docs/CI集成说明.md
Normal file
@@ -0,0 +1,204 @@
|
||||
# Redis 序列化结构检测 — CI 集成说明
|
||||
|
||||
---
|
||||
|
||||
## 1. 集成概览
|
||||
|
||||
```text
|
||||
开发者 push 代码
|
||||
↓
|
||||
Gitea Actions 触发
|
||||
↓
|
||||
浅克隆业务仓库(depth=2)
|
||||
↓
|
||||
从 Nexus 下载 redis-schema-checker.jar
|
||||
↓
|
||||
java -jar 执行(对比 HEAD~1 与 HEAD)
|
||||
↓
|
||||
有 P0/P1 变更 → 企微通知
|
||||
↓
|
||||
mode=block 且含 P0/P1/P2 任一变更 → exit 1(流水线失败)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 前置条件
|
||||
|
||||
| 项 | 说明 |
|
||||
|----|------|
|
||||
| Gitea Runner | 标签 `jdk11`,已安装 Java 11 |
|
||||
| Nexus 私库 | 可访问 `http://192.168.3.25:18081/nexus/repository/maven-releases` |
|
||||
| 工具 JAR | `com.codechecker:redis-schema-checker:1.0.0` 已发布 |
|
||||
| 仓库 Secret | `WECOM_ROBOT_WEBHOOK` 已配置 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 业务仓库文件清单
|
||||
|
||||
在 `jnpf-java-cloud` 中新增:
|
||||
|
||||
```text
|
||||
jnpf-java-cloud/
|
||||
├── .gitea/
|
||||
│ ├── workflows/
|
||||
│ │ └── redis-schema-check.yaml # 流水线
|
||||
│ └── config/
|
||||
│ └── redis-schema-check-config.yaml # 检测配置
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 流水线模板
|
||||
|
||||
```yaml
|
||||
name: Redis序列化结构检查
|
||||
run-name: ${{ gitea.actor }}的Redis结构检查
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REDIS_SCHEMA_CHECKER_VERSION: "1.0.0"
|
||||
REDIS_SCHEMA_CHECKER_REPO_URL: "http://192.168.3.25:18081/nexus/repository/maven-releases"
|
||||
|
||||
jobs:
|
||||
redis-schema-check:
|
||||
if: ${{ gitea.ref != 'refs/heads/pre' && gitea.ref != 'refs/heads/dev' && gitea.ref != 'refs/heads/master-2.0' }}
|
||||
runs-on: jdk11
|
||||
steps:
|
||||
- name: 检出代码
|
||||
run: |
|
||||
git config --global http.sslVerify false
|
||||
git clone --depth 2 --single-branch --branch "${{ gitea.ref_name }}" \
|
||||
"https://${{ gitea.token }}@git.niujiekeji.com/${{ gitea.repository }}.git" .
|
||||
git checkout -B "${{ gitea.ref_name }}" "${{ gitea.sha }}"
|
||||
|
||||
- name: 检查配置文件
|
||||
run: |
|
||||
if [ ! -f .gitea/config/redis-schema-check-config.yaml ]; then
|
||||
echo "错误: 缺少 .gitea/config/redis-schema-check-config.yaml"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: 从 Nexus 下载检测工具
|
||||
run: |
|
||||
GROUP_PATH="com/codechecker/redis-schema-checker"
|
||||
JAR_NAME="redis-schema-checker-${REDIS_SCHEMA_CHECKER_VERSION}.jar"
|
||||
JAR_URL="${REDIS_SCHEMA_CHECKER_REPO_URL}/${GROUP_PATH}/${REDIS_SCHEMA_CHECKER_VERSION}/${JAR_NAME}"
|
||||
JAR_PATH="/tmp/${JAR_NAME}"
|
||||
|
||||
echo "下载: ${JAR_URL}"
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL -o "${JAR_PATH}" "${JAR_URL}"
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "${JAR_PATH}" "${JAR_URL}"
|
||||
else
|
||||
echo "错误: Runner 缺少 curl 或 wget"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -s "${JAR_PATH}" ]; then
|
||||
echo "错误: 下载失败或文件为空"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ls -lh "${JAR_PATH}"
|
||||
|
||||
- name: 验证 JDK
|
||||
run: java -version
|
||||
|
||||
- name: 执行 Redis 结构检测
|
||||
env:
|
||||
WECOM_ROBOT_WEBHOOK: ${{ secrets.WECOM_ROBOT_WEBHOOK }}
|
||||
run: |
|
||||
OLD_SHA=$(git rev-parse HEAD~1 2>/dev/null || echo "")
|
||||
if [ -z "$OLD_SHA" ]; then
|
||||
echo "首次提交,跳过检测"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
COMMIT_TIME=$(git log -1 --format=%cd --date=format:'%Y-%m-%d %H:%M:%S')
|
||||
|
||||
java -jar "/tmp/redis-schema-checker-${REDIS_SCHEMA_CHECKER_VERSION}.jar" \
|
||||
--config .gitea/config/redis-schema-check-config.yaml \
|
||||
--repo-root . \
|
||||
--old-sha "$OLD_SHA" \
|
||||
--new-sha "$(git rev-parse HEAD)" \
|
||||
--branch "${{ gitea.ref_name }}" \
|
||||
--modifier "${{ gitea.actor }}" \
|
||||
--modify-time "$COMMIT_TIME"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 与现有流水线的关系
|
||||
|
||||
| 流水线 | 作用 | 关系 |
|
||||
|--------|------|------|
|
||||
| `demo.yaml` (AI代码质量分析) | AI Code Review | 并行,互不影响 |
|
||||
| `code-check` (CodeChecker) | 通用变更检测 | **同模式**,可并列执行 |
|
||||
| `redis-schema-check` | Redis 结构检测 | 新增 |
|
||||
|
||||
建议:三个 job 独立并行,各自 exit code 独立。
|
||||
|
||||
---
|
||||
|
||||
## 6. 工具发布流程(redisCheck 仓库)
|
||||
|
||||
```bash
|
||||
# 在 redisCheck 仓库
|
||||
mvn clean package -DskipTests
|
||||
|
||||
# 发布到 Nexus(需配置 settings.xml)
|
||||
mvn deploy -DskipTests
|
||||
```
|
||||
|
||||
发布产物:
|
||||
|
||||
```text
|
||||
com/codechecker/redis-schema-checker/1.0.0/
|
||||
├── redis-schema-checker-1.0.0.jar # 可执行 fat-jar
|
||||
└── redis-schema-checker-1.0.0.pom
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 退出码约定
|
||||
|
||||
| 退出码 | 含义 | 流水线表现 |
|
||||
|--------|------|------------|
|
||||
| 0 | 通过(含 notify 模式下的告警) | 绿色 |
|
||||
| 1 | 阻断(block 模式 + P0/P1/P2 任一变更) | 红色 |
|
||||
| 2 | 执行错误(配置缺失、jar 异常等) | 红色 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 故障排查
|
||||
|
||||
| 现象 | 可能原因 | 处理 |
|
||||
|------|----------|------|
|
||||
| 首次提交跳过 | 无 HEAD~1 | 正常行为 |
|
||||
| 下载 jar 失败 | Nexus 地址/版本错误 | 检查 env 变量 |
|
||||
| 未收到企微 | Secret 未配 / notify.enabled=false | 检查配置 |
|
||||
| 大量误报 | 锁/计数器未过滤 | 补充 ignore.key_patterns |
|
||||
| 漏报 | 写入模式未覆盖 | 启用 W04/W05 或补充 manual_mappings |
|
||||
| 类型展开不完整 | 类型在依赖 jar 中 | 补充 manual_mappings.value_type |
|
||||
|
||||
---
|
||||
|
||||
## 9. 本地调试
|
||||
|
||||
```bash
|
||||
# 在 jnpf-java-cloud 根目录
|
||||
java -jar /path/to/redis-schema-checker-1.0.0.jar \
|
||||
--config .gitea/config/redis-schema-check-config.yaml \
|
||||
--repo-root . \
|
||||
--old-sha HEAD~1 \
|
||||
--new-sha HEAD \
|
||||
--branch $(git branch --show-current) \
|
||||
--modifier "$(git log -1 --format=%an)" \
|
||||
--modify-time "$(git log -1 --format=%cd --date=format:'%Y-%m-%d %H:%M:%S')"
|
||||
```
|
||||
|
||||
可加 `--dry-run`(Phase 2 实现)仅输出报告不发企微。
|
||||
665
docs/实施方案.md
Normal file
665
docs/实施方案.md
Normal file
@@ -0,0 +1,665 @@
|
||||
# Redis 序列化结构变更检测 — 实施方案
|
||||
|
||||
> 版本:v0.1
|
||||
> 日期:2026-07-13
|
||||
> 技术栈:Java 11 + Maven + JavaParser
|
||||
> 目标仓库:`redisCheck`(工具) / `jnpf-java-cloud`(被检测业务仓库)
|
||||
|
||||
---
|
||||
|
||||
## 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)` | 高 | **重点支持** |
|
||||
| `JsonUtil.getObjectToString(obj)` | 高 | **重点支持**(按 Fastjson/Jackson 默认字段规则推断) |
|
||||
| `RedisTemplate.opsForValue().set(key, obj)` 直接写对象 | 中 | 第二阶段支持 |
|
||||
| `RedisTemplate.opsForHash().put(key, field, obj)` | 中 | 第二阶段支持 |
|
||||
| `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 交付形态
|
||||
|
||||
沿用现有 `code-checker` 模式(见 `redisCheck/.gitea/demo.yaml`):
|
||||
|
||||
```text
|
||||
redisCheck 仓库
|
||||
├── 开发 Java 分析工具
|
||||
├── mvn package 打 fat-jar
|
||||
├── 发布到 Nexus:com.codechecker:redis-schema-checker:{version}
|
||||
└── 提供默认配置模板
|
||||
|
||||
jnpf-java-cloud 仓库
|
||||
├── .gitea/workflows/redis-schema-check.yaml
|
||||
├── .gitea/config/redis-schema-check-config.yaml
|
||||
└── push 时下载 jar 并执行检测
|
||||
```
|
||||
|
||||
### 3.2 架构图
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Gitea["Gitea Push Pipeline"]
|
||||
A[push 事件] --> B[浅克隆 old/new 提交]
|
||||
B --> C[下载 redis-schema-checker.jar]
|
||||
C --> D[java -jar 执行检测]
|
||||
end
|
||||
|
||||
subgraph Checker["redis-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 直写对象
|
||||
|
||||
---
|
||||
|
||||
## 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 | 单元测试 + 夹具样本 |
|
||||
|
||||
**不采用** Spoon / Eclipse JDT 的原因:JavaParser 足够覆盖第一版需求,依赖更轻,CLI 启动更快。
|
||||
|
||||
**Lombok 处理策略**:第一版基于源码字段 + `@Data` 等注解推断序列化字段;对 `@Builder`、`@SuperBuilder` 等复杂场景标记为「低置信度」并降级为 P2 提示。后续可选集成 `lombok.ast` 或 delombok 预处理。
|
||||
|
||||
---
|
||||
|
||||
## 5. 工程结构(redisCheck 仓库)
|
||||
|
||||
```text
|
||||
redisCheck/
|
||||
├── pom.xml
|
||||
├── docs/
|
||||
│ ├── 实施方案.md # 本文档
|
||||
│ ├── 配置说明.md # YAML 配置项详解
|
||||
│ └── CI集成说明.md # 业务仓库接入步骤
|
||||
├── redis-schema-checker/
|
||||
│ ├── pom.xml
|
||||
│ └── src/
|
||||
│ ├── main/
|
||||
│ │ ├── resources/
|
||||
│ │ │ └── default-config.yaml # 内置默认配置(随 jar 发布)
|
||||
│ │ └── java/com/codechecker/redis/
|
||||
│ │ ├── cli/ # 命令行入口
|
||||
│ │ │ └── RedisSchemaCheckerMain.java
|
||||
│ │ ├── config/ # 配置模型
|
||||
│ │ │ ├── CheckerConfig.java
|
||||
│ │ │ └── ConfigLoader.java
|
||||
│ │ ├── git/ # Git 操作
|
||||
│ │ │ ├── GitDiffScanner.java
|
||||
│ │ │ └── GitException.java
|
||||
│ │ ├── analyze/ # 编排与工作树扫描
|
||||
│ │ │ ├── SchemaCheckAnalyzer.java
|
||||
│ │ │ ├── FileScanner.java
|
||||
│ │ │ └── GlobMatcher.java
|
||||
│ │ ├── detector/ # Redis 写入点检测
|
||||
│ │ │ ├── RedisWritePointDetector.java
|
||||
│ │ │ └── WritePoint.java
|
||||
│ │ ├── schema/ # Schema 提取
|
||||
│ │ │ ├── JavaSchemaExtractor.java
|
||||
│ │ │ ├── SourceIndex.java
|
||||
│ │ │ ├── TypeSchema.java
|
||||
│ │ │ ├── FieldSchema.java
|
||||
│ │ │ ├── JsonType.java
|
||||
│ │ │ └── AnnotationSupport.java
|
||||
│ │ ├── diff/ # 结构对比
|
||||
│ │ │ ├── SchemaDiffer.java
|
||||
│ │ │ ├── SchemaChange.java
|
||||
│ │ │ ├── ChangeType.java
|
||||
│ │ │ └── Severity.java
|
||||
│ │ ├── key/ # Key 推断
|
||||
│ │ │ └── RedisKeyResolver.java
|
||||
│ │ ├── report/ # 报告
|
||||
│ │ │ ├── ReportBuilder.java
|
||||
│ │ │ └── CheckReport.java
|
||||
│ │ └── notify/ # 企微通知
|
||||
│ │ └── WeComNotifier.java
|
||||
│ └── test/
|
||||
│ ├── resources/fixtures/tenant/ # 夹具:TenantVO/Helper 新旧版本
|
||||
│ └── java/... # 各模块单测
|
||||
└── .gitea/
|
||||
└── demo.yaml # 工具自身 CI(可选)
|
||||
```
|
||||
|
||||
### 5.1 Maven 坐标
|
||||
|
||||
```xml
|
||||
<groupId>com.codechecker</groupId>
|
||||
<artifactId>redis-schema-checker</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
```
|
||||
|
||||
打包为 **shaded/fat jar**,主类:`com.codechecker.redis.cli.RedisSchemaCheckerMain`
|
||||
|
||||
---
|
||||
|
||||
## 6. 执行流程详解
|
||||
|
||||
### 6.1 CLI 参数
|
||||
|
||||
```bash
|
||||
java -jar redis-schema-checker-1.0.0.jar \
|
||||
--config .gitea/config/redis-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)获取策略
|
||||
|
||||
与 `demo.yaml` 保持一致,优先级:
|
||||
|
||||
1. 流水线显式传入 `--old-sha`(通常为 `HEAD~1`)
|
||||
2. 若 `HEAD~1` 不存在(首次提交)→ 跳过检测,`exit 0`
|
||||
3. 浅克隆 `--depth 2` 确保 `HEAD~1` 可用
|
||||
|
||||
> 不支持一次 push 多个 commit 时逐个分析;第一版仅对比 `HEAD~1..HEAD`。后续可扩展为 `before..after` 范围分析。
|
||||
|
||||
### 6.3 处理步骤
|
||||
|
||||
#### Step 1:加载配置
|
||||
|
||||
读取 `redis-schema-check-config.yaml`,合并默认值(见 `docs/配置说明.md`)。
|
||||
|
||||
#### Step 2:Git 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 4:Redis 写入点检测
|
||||
|
||||
在 **变更文件** 中扫描以下 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)` | 第二阶段 |
|
||||
| W06 | `JSON.parseObject(cacheValue, Xxx.class)` | 辅助反向确认读取类型 |
|
||||
|
||||
**忽略规则**(自动):
|
||||
|
||||
- value 为字符串字面量、数字、`UUID`、`"1"` 等
|
||||
- 方法名含 `setIfAbsent`、`increment`、`delete`、`remove`、`expire`
|
||||
- key 匹配 `ignore_key_patterns` 配置
|
||||
|
||||
#### 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")` | 字段名映射 |
|
||||
| `@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 7:Schema Diff
|
||||
|
||||
对比同一写入点在 old/new 两个版本的 `TypeSchema`,输出 `SchemaChange` 列表。
|
||||
|
||||
**变更类型与严重级别**:
|
||||
|
||||
| 变更类型 | 示例 | 默认级别 |
|
||||
|----------|------|----------|
|
||||
| `FIELD_REMOVED` | 删除 `dbName` | P0 |
|
||||
| `TYPE_CHANGED` | `linkList` 从数组变对象 | P0 |
|
||||
| `WRAPPER_ADDED` | 顶层增加 `vo` 包装 | P0 |
|
||||
| `FIELD_PATH_MOVED` | `dbName` → `vo.dbName` | P0 |
|
||||
| `FIELD_ADDED` | 新增 `expiresAtMs` | P1 |
|
||||
| `KEY_PATTERN_CHANGED` | key 常量变更 | P1 |
|
||||
| `WRITE_POINT_REMOVED` | 删除缓存写入 | P1 |
|
||||
| `WRITE_POINT_ADDED` | 新增缓存写入 | P2 |
|
||||
| `LOW_CONFIDENCE` | 类型推断失败 | P2 |
|
||||
|
||||
#### Step 8:报告与通知
|
||||
|
||||
生成 `CheckReport`,包含:
|
||||
|
||||
- 仓库名、分支、old/new sha、提交人、时间
|
||||
- 变更列表(按严重级别排序)
|
||||
- 每项:Key 模式、写入位置(类#方法:行号)、旧结构、新结构、变更摘要
|
||||
|
||||
调用企微 Webhook 发送 Markdown 消息。
|
||||
|
||||
#### Step 9:退出码
|
||||
|
||||
| 条件 | 退出码 |
|
||||
|------|--------|
|
||||
| 无变更 / 仅 P2 | 0 |
|
||||
| `mode=notify` 且存在 P0/P1 | 0(仍通知) |
|
||||
| `mode=block` 且存在 P0/P1/P2 | 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/redis-schema-check-config.yaml` | mode、notify、include_modules、manual_mappings |
|
||||
|
||||
合并规则:**业务配置覆盖默认配置**,未声明的项沿用默认值。
|
||||
|
||||
CLI 调用:
|
||||
|
||||
```bash
|
||||
java -jar redis-schema-checker.jar \
|
||||
--config .gitea/config/redis-schema-check-config.yaml \
|
||||
...
|
||||
```
|
||||
|
||||
工具启动时自动加载 jar 内 `default-config.yaml`,再与 `--config` 指定的业务配置深度合并。
|
||||
|
||||
详见 `docs/配置说明.md`。核心开关:
|
||||
|
||||
```yaml
|
||||
# 运行模式:notify(仅通知)| block(P0/P1/P2 全部阻断流水线)
|
||||
mode: notify
|
||||
|
||||
# block 模式下触发 exit 1 的严重级别(全部阻断)
|
||||
block_severities:
|
||||
- P0
|
||||
- P1
|
||||
- P2
|
||||
|
||||
# 是否发送企微通知
|
||||
notify:
|
||||
enabled: true
|
||||
webhook_env: WECOM_ROBOT_WEBHOOK
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. CI 集成方案
|
||||
|
||||
详见 `docs/CI集成说明.md`。核心流程:
|
||||
|
||||
```yaml
|
||||
# jnpf-java-cloud/.gitea/workflows/redis-schema-check.yaml
|
||||
name: Redis序列化结构检查
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
redis-schema-check:
|
||||
if: ${{ gitea.ref != 'refs/heads/pre' && gitea.ref != 'refs/heads/dev' && gitea.ref != 'refs/heads/master-2.0' }}
|
||||
runs-on: jdk11
|
||||
steps:
|
||||
- name: 检出代码
|
||||
run: |
|
||||
git clone --depth 2 --single-branch --branch "${{ gitea.ref_name }}" \
|
||||
"https://${{ gitea.token }}@git.niujiekeji.com/${{ gitea.repository }}.git" .
|
||||
git checkout -B "${{ gitea.ref_name }}" "${{ gitea.sha }}"
|
||||
|
||||
- name: 下载检测工具
|
||||
run: |
|
||||
# 从 Nexus 下载 redis-schema-checker jar
|
||||
...
|
||||
|
||||
- name: 执行检测
|
||||
env:
|
||||
WECOM_ROBOT_WEBHOOK: ${{ secrets.WECOM_ROBOT_WEBHOOK }}
|
||||
run: |
|
||||
OLD_SHA=$(git rev-parse HEAD~1 2>/dev/null || echo "")
|
||||
[ -z "$OLD_SHA" ] && exit 0
|
||||
java -jar /tmp/redis-schema-checker-1.0.0.jar \
|
||||
--config .gitea/config/redis-schema-check-config.yaml \
|
||||
--repo-root . \
|
||||
--old-sha "$OLD_SHA" \
|
||||
--new-sha "$(git rev-parse HEAD)" \
|
||||
--branch "${{ gitea.ref_name }}" \
|
||||
--modifier "${{ gitea.actor }}" \
|
||||
--modify-time "$(git log -1 --format=%cd --date=format:'%Y-%m-%d %H:%M:%S')"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 分阶段交付计划
|
||||
|
||||
### Phase 1 — MVP(约 1.5 周)
|
||||
|
||||
**目标**:跑通端到端链路,覆盖租户缓存典型场景。
|
||||
|
||||
| 任务 | 产出 |
|
||||
|------|------|
|
||||
| Maven 工程骨架 + CLI | 可执行 fat-jar |
|
||||
| Git diff 扫描 | 变更文件列表 |
|
||||
| W01~W03 写入点检测 | 覆盖 JSON 字符串写入 |
|
||||
| 基础 Schema 提取 | 支持普通类、内部类、List、嵌套 |
|
||||
| Schema Diff P0/P1 | 字段增删、包装、路径迁移 |
|
||||
| 企微通知 | Markdown 消息 |
|
||||
| notify/block 开关 | 配置驱动 |
|
||||
| 夹具测试 | TenantVO/CacheEnvelope 样本 |
|
||||
|
||||
**验收标准**:
|
||||
|
||||
- 对 `TenantDbContentCacheHelper` 的结构变更能输出 P0 报告
|
||||
- 流水线 push 后能收到企微通知
|
||||
- `mode=block` 时任意 P0/P1/P2 变更导致 exit 1
|
||||
|
||||
### Phase 2 — 增强(约 1 周)
|
||||
|
||||
| 任务 | 说明 |
|
||||
|------|------|
|
||||
| W04/W05 模式 | RedisTemplate 直写对象、Hash 写入 |
|
||||
| 注解完整支持 | Fastjson/Jackson 注解 |
|
||||
| Key 推断增强 | `String.format`、常量追溯 |
|
||||
| 忽略规则完善 | 锁/计数器/token 自动过滤 |
|
||||
| 多模块性能优化 | 并行解析、缓存索引 |
|
||||
|
||||
### Phase 3 — 运营(约 0.5 周)
|
||||
|
||||
| 任务 | 说明 |
|
||||
|------|------|
|
||||
| 报告落盘 | 可选输出 JSON 报告文件 |
|
||||
| 误报反馈 | `suppressions` 配置支持按写入点忽略 |
|
||||
| 更多业务场景覆盖 | 考勤、文件下载进度等 |
|
||||
|
||||
---
|
||||
|
||||
## 11. 测试策略
|
||||
|
||||
### 11.1 单元测试
|
||||
|
||||
- `SchemaDifferTest`:纯字段路径对比逻辑
|
||||
- `JavaSchemaExtractorTest`:类字段展开、注解、内部类
|
||||
- `RedisWritePointDetectorTest`:各种写入 AST 模式匹配
|
||||
- `RedisKeyResolverTest`:常量、format、拼接推断
|
||||
|
||||
### 11.2 夹具集成测试
|
||||
|
||||
在 `src/test/resources/fixtures/` 放置真实业务代码片段(从 `jnpf-java-cloud` 提取并脱敏),模拟 old/new 两个版本:
|
||||
|
||||
| 夹具 | 验证点 |
|
||||
|------|--------|
|
||||
| `tenant-cache/` | 包装结构变更 P0 |
|
||||
| `attendance-base-setting/` | Map 结构缓存 |
|
||||
| `evaluate-config/` | VO 字段新增 P1 |
|
||||
| `lock-only/` | 应被忽略 |
|
||||
|
||||
### 11.3 端到端测试
|
||||
|
||||
在 `jnpf-java-cloud` 开测试分支,故意提交一个 VO 字段变更,验证流水线 + 企微通知。
|
||||
|
||||
---
|
||||
|
||||
## 12. 风险与限制
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|------|------|----------|
|
||||
| 类型推断失败 | 漏报 | 标记 `LOW_CONFIDENCE`,配置 `manual_mappings` |
|
||||
| Lombok 复杂注解 | 字段遗漏 | 基于源码字段 + 注解;后续 delombok |
|
||||
| 同一 key 多分支写不同类型 | 误报 | 报告注明置信度;人工 suppression |
|
||||
| 浅克隆 parent 不可用 | 跳过检测 | `--depth 2`;文档明确要求 |
|
||||
| 一次 push 多 commit | 仅检最后一个 | 文档说明;后续扩展 range |
|
||||
| 依赖 jar 中的类型 | 字段展开不完整 | 配置 `manual_mappings` 补充 |
|
||||
| JsonUtil 实现不可见 | 序列化规则猜测 | 默认按字段名序列化;与 Fastjson 对齐 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 已确认决策(Grill 共识)
|
||||
|
||||
| # | 决策项 | 结论 |
|
||||
|---|--------|------|
|
||||
| 1 | 阻断范围 | `block` 模式下 **P0/P1/P2 全部阻断**(exit 1) |
|
||||
| 2 | 发布坐标 | 独立产物 `com.codechecker:redis-schema-checker:1.0.0` |
|
||||
| 3 | 配置归属 | **双层配置**:jar 内 `default-config.yaml` + 业务仓覆盖合并 |
|
||||
| 4 | 上线策略 | 先 `notify` 观察 **1 周**,稳定后手动切 `block` |
|
||||
| 5 | 检测范围 | **仅 `src/main/java`**,不扫描测试代码 |
|
||||
|
||||
以上决策已纳入实施方案,可进入开发阶段。
|
||||
|
||||
---
|
||||
|
||||
## 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 {
|
||||
Severity severity; // P0, P1, P2
|
||||
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;
|
||||
}
|
||||
```
|
||||
295
docs/配置说明.md
Normal file
295
docs/配置说明.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# Redis 序列化结构检测 — 配置说明
|
||||
|
||||
> **双层配置**:工具 jar 内置 `default-config.yaml`(默认) + 业务仓库 `.gitea/config/redis-schema-check-config.yaml`(覆盖)
|
||||
|
||||
---
|
||||
|
||||
## 1. 配置合并机制
|
||||
|
||||
```text
|
||||
jar 内 default-config.yaml(工具仓维护)
|
||||
↓ 深度合并
|
||||
业务仓 redis-schema-check-config.yaml(业务仓维护)
|
||||
↓
|
||||
最终生效配置
|
||||
```
|
||||
|
||||
- 业务配置**仅需写差异项**,不必复制全部默认规则
|
||||
- 升级 jar 时,默认忽略规则/检测模式自动跟随工具版本演进
|
||||
- 业务仓必须存在 `--config` 指定的配置文件(可为仅含 `mode` 的最小文件)
|
||||
|
||||
### 1.1 业务仓最小配置示例
|
||||
|
||||
```yaml
|
||||
# jnpf-java-cloud/.gitea/config/redis-schema-check-config.yaml
|
||||
mode: notify
|
||||
|
||||
notify:
|
||||
enabled: true
|
||||
webhook_env: WECOM_ROBOT_WEBHOOK
|
||||
|
||||
include_modules:
|
||||
- jnpf-tenant
|
||||
```
|
||||
|
||||
### 1.2 工具内置默认配置(jar 内 default-config.yaml)
|
||||
|
||||
由 `redisCheck` 仓库维护,随 jar 发布,包含:
|
||||
|
||||
- `detection.patterns`(W01~W03)
|
||||
- `ignore.key_patterns`(锁/计数器/token)
|
||||
- `block_severities`(P0/P1/P2)
|
||||
- `detection.min_confidence`、`max_field_depth` 等
|
||||
|
||||
---
|
||||
|
||||
## 2. 业务仓完整配置示例
|
||||
|
||||
```yaml
|
||||
# 运行模式
|
||||
# notify - 仅通知,不阻断流水线
|
||||
# block - 按 block_severities 阻断流水线(exit 1)
|
||||
mode: notify
|
||||
|
||||
# block 模式下触发 exit 1 的严重级别(全部阻断:P0/P1/P2)
|
||||
block_severities:
|
||||
- P0
|
||||
- P1
|
||||
- P2
|
||||
|
||||
# 是否扫描测试代码(已确认:不扫描)
|
||||
scan_test_sources: false
|
||||
|
||||
# 源码扫描根目录(仅 main,不含 test)
|
||||
source_roots:
|
||||
- "src/main/java"
|
||||
|
||||
# 通知配置
|
||||
notify:
|
||||
enabled: true
|
||||
# 从环境变量读取 Webhook URL
|
||||
webhook_env: WECOM_ROBOT_WEBHOOK
|
||||
# 无变更时是否也发通知(一般 false)
|
||||
notify_on_clean: false
|
||||
# 消息标题前缀
|
||||
title_prefix: "[Redis结构变更]"
|
||||
|
||||
# 忽略规则
|
||||
ignore:
|
||||
# 忽略的 key 模式(glob)
|
||||
key_patterns:
|
||||
- "*:lock"
|
||||
- "*:lock:*"
|
||||
- "loginCount:*"
|
||||
- "Authorization:*"
|
||||
- "Authorization:login:session:*"
|
||||
|
||||
# 忽略的文件路径模式
|
||||
file_patterns:
|
||||
- "**/test/**"
|
||||
|
||||
# 忽略的写入方法(类全名#方法名)
|
||||
writer_methods: []
|
||||
|
||||
# 检测规则
|
||||
detection:
|
||||
# 启用的写入模式
|
||||
patterns:
|
||||
- W01 # redisUtil.insert + JSON.toJSONString
|
||||
- W02 # redisTemplate.opsForValue().set + JSON.toJSONString
|
||||
- W03 # stringRedisTemplate + JsonUtil.getObjectToString
|
||||
# - W04 # redisTemplate 直写对象(Phase 2)
|
||||
# - W05 # opsForHash().put(Phase 2)
|
||||
|
||||
# 类型推断最低置信度,低于此值仅输出 P2 提示
|
||||
min_confidence: 0.6
|
||||
|
||||
# 字段展开最大深度(防止循环引用死循环)
|
||||
max_field_depth: 8
|
||||
|
||||
# 严重级别覆盖(可选)
|
||||
severity_overrides:
|
||||
FIELD_ADDED: P1
|
||||
WRITE_POINT_ADDED: P2
|
||||
|
||||
# 人工补充映射(自动推断失败或需精确指定时使用)
|
||||
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 mode
|
||||
|
||||
| 值 | 行为 |
|
||||
|----|------|
|
||||
| `notify` | 检测到变更 → 发企微 → `exit 0` |
|
||||
| `block` | 检测到 `block_severities` 中的级别 → 发企微 → `exit 1` |
|
||||
|
||||
### 3.2 block_severities
|
||||
|
||||
默认 `["P0", "P1", "P2"]`,`block` 模式下任意级别变更均 `exit 1`。
|
||||
|
||||
建议上线初期仍使用 `mode: notify` 观察误报情况,确认稳定后再切换:
|
||||
|
||||
```yaml
|
||||
mode: block
|
||||
block_severities:
|
||||
- P0
|
||||
- P1
|
||||
- P2
|
||||
```
|
||||
|
||||
### 3.3 notify
|
||||
|
||||
| 字段 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `enabled` | boolean | true | 是否发企微 |
|
||||
| `webhook_env` | string | WECOM_ROBOT_WEBHOOK | 环境变量名 |
|
||||
| `notify_on_clean` | boolean | false | 无变更时是否通知 |
|
||||
| `title_prefix` | string | [Redis结构变更] | 消息标题前缀 |
|
||||
|
||||
### 3.4 ignore.key_patterns
|
||||
|
||||
支持 glob:
|
||||
|
||||
- `*` 匹配单层
|
||||
- `**` 匹配多层
|
||||
|
||||
常见内置忽略(代码层也有硬编码兜底):
|
||||
|
||||
- 分布式锁 key
|
||||
- 登录计数
|
||||
- session/token
|
||||
|
||||
### 3.5 detection.patterns
|
||||
|
||||
| 模式 | 说明 | 阶段 |
|
||||
|------|------|------|
|
||||
| W01 | `redisUtil.insert(key, JSON.toJSONString(x), ttl)` | Phase 1 |
|
||||
| W02 | `redisTemplate.opsForValue().set(key, JSON.toJSONString(x), ...)` | Phase 1 |
|
||||
| W03 | `stringRedisTemplate.opsForValue().set(key, JsonUtil.getObjectToString(x), ...)` | Phase 1 |
|
||||
| W04 | `redisTemplate.opsForValue().set(key, obj, ...)` | Phase 2 |
|
||||
| W05 | `redisTemplate.opsForHash().put(key, field, obj)` | Phase 2 |
|
||||
|
||||
### 3.6 manual_mappings
|
||||
|
||||
当自动推断不准确时使用。匹配优先级 **高于** 自动推断。
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | 是 | 唯一标识 |
|
||||
| `writer_method` | 否 | `类全名#方法名`,精确匹配写入点 |
|
||||
| `key_pattern` | 否 | 精确 key 模式 |
|
||||
| `value_type` | 否 | 强制指定 value 类型 |
|
||||
| `description` | 否 | 备注 |
|
||||
|
||||
### 3.7 suppressions
|
||||
|
||||
用于屏蔽已知可接受的变更:
|
||||
|
||||
```yaml
|
||||
suppressions:
|
||||
- id: my-suppression
|
||||
writer_method: "com.example.FooService#cacheBar"
|
||||
change_types:
|
||||
- FIELD_ADDED
|
||||
reason: "新增字段向后兼容"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 环境变量
|
||||
|
||||
| 变量 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `WECOM_ROBOT_WEBHOOK` | notify.enabled=true 时必填 | 企微机器人 Webhook 完整 URL |
|
||||
|
||||
在 Gitea 仓库 Settings → Secrets 中配置。
|
||||
|
||||
---
|
||||
|
||||
## 5. 企微消息格式示例
|
||||
|
||||
```markdown
|
||||
## [Redis结构变更] jnpf-java-cloud
|
||||
|
||||
> 分支: feature/tenant-cache
|
||||
> 提交: a1b2c3d → e4f5g6h
|
||||
> 提交人: zhangsan
|
||||
> 时间: 2026-07-13 14:00:00
|
||||
> 模式: notify
|
||||
|
||||
### P0 - 顶层结构包装变更
|
||||
- **Key**: `tenant:db:content:*`
|
||||
- **位置**: `TenantDbContentCacheHelper#cacheSuccess:92`
|
||||
- **变更**:
|
||||
- `dbName` → `vo.dbName`(字段路径迁移)
|
||||
- `linkList` → `vo.linkList`(字段路径迁移)
|
||||
- 新增顶层字段 `expiresAtMs`
|
||||
- **影响**: 旧缓存反序列化可能失败,需评估缓存刷新策略
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 推荐上线配置
|
||||
|
||||
### 6.1 观察期(第 1 周,已确认策略)
|
||||
|
||||
业务仓默认配置:
|
||||
|
||||
```yaml
|
||||
mode: notify
|
||||
|
||||
notify:
|
||||
enabled: true
|
||||
webhook_env: WECOM_ROBOT_WEBHOOK
|
||||
|
||||
include_modules:
|
||||
- jnpf-tenant
|
||||
```
|
||||
|
||||
观察满 1 周、确认误报可接受后,手动切换:
|
||||
|
||||
```yaml
|
||||
mode: block
|
||||
block_severities: [P0, P1, P2]
|
||||
include_modules: [] # 扩至全仓
|
||||
```
|
||||
|
||||
### 6.2 全量启用(观察期结束后)
|
||||
|
||||
```yaml
|
||||
mode: block
|
||||
block_severities: [P0, P1, P2]
|
||||
include_modules: [] # 空表示全部模块
|
||||
detection:
|
||||
patterns: [W01, W02, W03, W04, W05]
|
||||
```
|
||||
Reference in New Issue
Block a user