ai-engineering-init 1.13.0 → 1.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -42,8 +42,7 @@ try {
42
42
 
43
43
  if (changedFiles.length > 0) {
44
44
  console.error(`\n💡 检测到 ${changedFiles.length} 个代码文件变更,建议执行代码审查:`);
45
- console.error(` → 输入 "review" 或 "审查代码" 使用 code-reviewer 进行规范检查`);
46
- console.error(` → 输入 "codex review" 调用 Codex 进行深度代码审查\n`);
45
+ console.error(` → 输入 "review" 或 "审查代码" 使用 code-reviewer 进行规范检查\n`);
47
46
  }
48
47
  }
49
48
  } catch {
@@ -29,7 +29,7 @@ description: |
29
29
  │ - 用户已给出完整字段列表
30
30
 
31
31
  └─ 复杂需求? ──→ Agent 路径(启动 requirements-analyzer)
32
- - 提供了 Axure 原型链接或截图
32
+ - 提供了 Axure 原型截图
33
33
  - 提供了云效任务编号
34
34
  - 多页面/多模块联动
35
35
  - 业务流程复杂,需要状态流转设计
@@ -45,34 +45,12 @@ description: |
45
45
 
46
46
  ## Agent 路径(复杂需求)
47
47
 
48
- ### Axure 链接处理(重要)
49
-
50
- > **Axure 是 SPA 应用,WebFetch 必定失败(TLS/JS 渲染问题)。禁止用 WebFetch 访问 Axure 链接。**
51
-
52
- 当用户提供 Axure 链接时,必须用 Playwright 截图:
53
-
54
- ```bash
55
- # 1. 先用 Playwright 截图(每个页面单独截)
56
- npx playwright screenshot --wait-for-timeout 3000 "https://xxx.axure.cloud/page1" /tmp/axure-1.png
57
- npx playwright screenshot --wait-for-timeout 3000 "https://xxx.axure.cloud/page2" /tmp/axure-2.png
58
-
59
- # 2. 截图完成后,将文件路径传给 image-reader Agent 分析
60
- # 3. 如果原型有多个页面,URL 通常带 #page 参数,逐页截图
61
- ```
62
-
63
- **判断规则**:
64
- - URL 包含 `axure.cloud` 或 `.axshare.com` → Playwright 截图
65
- - 用户说"Axure 链接" → Playwright 截图
66
- - 本地截图文件(.png/.jpg) → 直接 image-reader 分析
67
-
68
- ### 完整流程
69
-
70
48
  ```
71
49
  步骤 1:收集信息(从用户消息中提取)
72
- - Axure 原型链接 → Playwright 截图 → image-reader 分析
73
- - Axure 原型截图文件 → 直接 image-reader 分析
74
- - 云效任务编号 → task-fetcher 获取详情
50
+ - Axure 原型截图路径
51
+ - 云效任务编号
75
52
  - 需求描述文字
53
+ - 关联模块信息
76
54
 
77
55
  步骤 2:启动 requirements-analyzer Agent
78
56
  └── requirements-analyzer(Opus) 内部自动编排:
@@ -91,23 +69,13 @@ npx playwright screenshot --wait-for-timeout 3000 "https://xxx.axure.cloud/page2
91
69
  |---------------|---------|
92
70
  | 只有文字描述 | 快速路径(不启动 Agent) |
93
71
  | 文字 + 原型截图 | requirements-analyzer → 内部调 image-reader |
94
- | 文字 + Axure 链接 | Playwright 截图 → requirements-analyzer → image-reader |
95
72
  | 文字 + 云效任务号 | requirements-analyzer → 内部调 task-fetcher |
96
73
  | 原型截图 + 云效任务号 | requirements-analyzer → 内部并行调 image-reader + task-fetcher |
97
74
 
98
75
  ### 启动示例
99
76
 
100
77
  ```
101
- # 有 Axure 链接(先截图再分析)
102
- Bash: npx playwright screenshot --wait-for-timeout 3000 "https://xxx.axure.cloud/page1" /tmp/axure-1.png
103
- Bash: npx playwright screenshot --wait-for-timeout 3000 "https://xxx.axure.cloud/page2" /tmp/axure-2.png
104
-
105
- Agent(subagent_type="requirements-analyzer",
106
- prompt="分析以下 Axure 原型截图,输出需求分析报告和开发任务清单:
107
- 截图路径:/tmp/axure-1.png, /tmp/axure-2.png
108
- 需求描述:xxx")
109
-
110
- # 有原型截图文件
78
+ # 有原型截图
111
79
  Agent(subagent_type="requirements-analyzer",
112
80
  prompt="分析以下 Axure 原型截图,输出需求分析报告和开发任务清单:
113
81
  截图路径:/path/to/image1.png, /path/to/image2.png
@@ -142,4 +110,3 @@ Agent(subagent_type="requirements-analyzer",
142
110
  - 与 `bug-detective` / `fix-bug` 的区别:本技能面向**新功能开发前的需求分析**,不涉及 Bug 排查
143
111
  - 数据库设计必须遵循项目规范(雪花 ID、审计字段、del_flag=2 正常)
144
112
  - 如果需求信息不完整,主动列出需要确认的点,而不是猜测
145
- - **Axure 链接必须用 Playwright 截图,禁止 WebFetch**
@@ -280,20 +280,192 @@ voucher_type_id: jsonpath "$.data.records[0].id"
280
280
 
281
281
  ### 第四步:生成 .hurl 文件
282
282
 
283
- **查询条件完整覆盖**(每个 Param 字段都要有测试用例):
283
+ #### 4.1 查询条件类型识别与测试策略
284
284
 
285
+ 读取 Param 类后,**逐字段识别条件类型**,按下表生成对应测试用例:
286
+
287
+ | 条件类型 | 识别特征 | 示例字段 | 必须生成的测试用例 |
288
+ |---------|---------|---------|----------------|
289
+ | **日期范围** | `startDate`/`endDate` 成对 | `startDate`, `endDate` | ① 正常区间 ② 单日(start==end) ③ 跨月区间 ④ 必填时留空(预期报错/空结果) |
290
+ | **关键词搜索** | `keyword`, `name`, `code` 等 | `keyword`, `canteenName` | ① 精确匹配已知值 ② 模糊匹配前缀 ③ 无匹配的乱码(验证空数组) ④ 留空(不过滤,验证正常返回) |
291
+ | **枚举/状态** | 字段类型为枚举或 Integer 语义明确 | `status`, `type`, `mealtype` | ① 每个有效枚举值各一条请求 ② 可选时留空(验证不过滤) |
292
+ | **ID / 外键** | 字段名含 `Id`,关联其他表 | `canteenId`, `areaId` | ① 传有效 ID(有数据,验证过滤生效) ② 留空(不过滤,验证正常返回) ③ 传不存在 ID(验证空结果) |
293
+ | **布尔开关** | boolean / Integer 表示开关 | `isSubmitted`, `isEnabled` | ① `true`/`1` ② `false`/`0` ③ 留空 |
294
+ | **集合/多选** | `List<Integer>`, `List<String>` | `mealtimeTypes`, `statusList` | ① 单项 ② 多项组合 ③ 空列表或留空 |
295
+ | **分页** | `page.current`/`page.size` | `page` | ① 第1页 size=10 ② 第2页(验证翻页) ③ size=1(验证精确分页) |
296
+
297
+ #### 4.2 各条件类型 Hurl 示例
298
+
299
+ **① 日期范围**
300
+ ```hurl
301
+ # TC-DATE-1 正常日期区间
302
+ POST {{base_url}}/api/v2/web/report/subject/page
303
+ ...
304
+ {"content": {"page":{"current":1,"size":10},"startDate":"2024-01-01","endDate":"2024-01-31"}}
305
+ HTTP 200
306
+ [Asserts]
307
+ jsonpath "$.code" == 10000
308
+ jsonpath "$.data.resultPage.records" count > 0
309
+
310
+ # TC-DATE-2 单日查询(start == end)
311
+ POST {{base_url}}/api/v2/web/report/subject/page
312
+ ...
313
+ {"content": {"page":{"current":1,"size":10},"startDate":"2024-01-15","endDate":"2024-01-15"}}
314
+ HTTP 200
315
+ [Asserts]
316
+ jsonpath "$.code" == 10000
317
+
318
+ # TC-DATE-3 空区间(故意用无数据的日期)
319
+ POST {{base_url}}/api/v2/web/report/subject/page
320
+ ...
321
+ {"content": {"page":{"current":1,"size":10},"startDate":"2099-01-01","endDate":"2099-01-31"}}
322
+ HTTP 200
323
+ [Asserts]
324
+ jsonpath "$.code" == 10000
325
+ jsonpath "$.data.resultPage.records" count == 0
285
326
  ```
286
- 单接口测试场景清单:
287
- 1. 基础分页查询 — 必填参数,预期 code=10000,验证分页结构+VO 所有字段
288
- 2. 合计行验证 — 如有 totalLine,验证所有合计字段存在
289
- 3. 逐个查询条件 — 每个 Param 字段单独或组合测试
290
- 4. 空结果验证 — 故意使用不匹配的条件,验证 records count == 0
291
- 5. 分页翻页 — current=2 验证翻页正确
292
- 6. 导出接口 — 验证 code=10000(异步导出返回 void)
293
- 7. 未授权测试 — 不带 X-Token,预期 HTTP 401
294
- 8. 数据清理 — 删除测试创建的数据
327
+
328
+ **② 关键词搜索**
329
+ ```hurl
330
+ # TC-KW-1 精确关键词匹配
331
+ POST {{base_url}}/api/v2/web/report/subject/page
332
+ ...
333
+ {"content": {"page":{"current":1,"size":10},"startDate":"2024-01-01","endDate":"2024-01-31","keyword":"{{known_canteen_name}}"}}
334
+ HTTP 200
335
+ [Asserts]
336
+ jsonpath "$.code" == 10000
337
+ jsonpath "$.data.resultPage.records" count > 0
338
+
339
+ # TC-KW-2 无匹配关键词
340
+ POST {{base_url}}/api/v2/web/report/subject/page
341
+ ...
342
+ {"content": {"page":{"current":1,"size":10},"startDate":"2024-01-01","endDate":"2024-01-31","keyword":"__NO_MATCH_XYZ__"}}
343
+ HTTP 200
344
+ [Asserts]
345
+ jsonpath "$.code" == 10000
346
+ jsonpath "$.data.resultPage.records" count == 0
347
+
348
+ # TC-KW-3 留空 keyword(不过滤)
349
+ POST {{base_url}}/api/v2/web/report/subject/page
350
+ ...
351
+ {"content": {"page":{"current":1,"size":10},"startDate":"2024-01-01","endDate":"2024-01-31"}}
352
+ HTTP 200
353
+ [Asserts]
354
+ jsonpath "$.code" == 10000
355
+ jsonpath "$.data.resultPage.records" isCollection
295
356
  ```
296
357
 
358
+ **③ 枚举/状态**
359
+ ```hurl
360
+ # TC-ENUM-1 状态=1(已提交)
361
+ POST {{base_url}}/api/v2/web/order/page
362
+ ...
363
+ {"content": {"page":{"current":1,"size":10},"status":1}}
364
+ HTTP 200
365
+ [Asserts]
366
+ jsonpath "$.code" == 10000
367
+ jsonpath "$.data.records[0].status" == 1
368
+
369
+ # TC-ENUM-2 状态=2(已完成)
370
+ POST {{base_url}}/api/v2/web/order/page
371
+ ...
372
+ {"content": {"page":{"current":1,"size":10},"status":2}}
373
+ HTTP 200
374
+ [Asserts]
375
+ jsonpath "$.code" == 10000
376
+
377
+ # TC-ENUM-3 不传 status(全部状态)
378
+ POST {{base_url}}/api/v2/web/order/page
379
+ ...
380
+ {"content": {"page":{"current":1,"size":10}}}
381
+ HTTP 200
382
+ [Asserts]
383
+ jsonpath "$.code" == 10000
384
+ ```
385
+
386
+ **④ ID / 外键**
387
+ ```hurl
388
+ # TC-ID-1 传有效 canteenId(有数据,过滤生效)
389
+ POST {{base_url}}/api/v2/web/report/subject/page
390
+ ...
391
+ {"content": {"page":{"current":1,"size":10},"startDate":"2024-01-01","endDate":"2024-01-31","canteenId":{{canteen_id}}}}
392
+ HTTP 200
393
+ [Asserts]
394
+ jsonpath "$.code" == 10000
395
+ jsonpath "$.data.resultPage.records" count > 0
396
+ jsonpath "$.data.resultPage.records[0].canteenId" == {{canteen_id}}
397
+
398
+ # TC-ID-2 不传 canteenId(不过滤)
399
+ POST {{base_url}}/api/v2/web/report/subject/page
400
+ ...
401
+ {"content": {"page":{"current":1,"size":10},"startDate":"2024-01-01","endDate":"2024-01-31"}}
402
+ HTTP 200
403
+ [Asserts]
404
+ jsonpath "$.code" == 10000
405
+
406
+ # TC-ID-3 传不存在 ID(空结果)
407
+ POST {{base_url}}/api/v2/web/report/subject/page
408
+ ...
409
+ {"content": {"page":{"current":1,"size":10},"startDate":"2024-01-01","endDate":"2024-01-31","canteenId":999999999}}
410
+ HTTP 200
411
+ [Asserts]
412
+ jsonpath "$.code" == 10000
413
+ jsonpath "$.data.resultPage.records" count == 0
414
+ ```
415
+
416
+ **⑤ 集合/多选**
417
+ ```hurl
418
+ # TC-LIST-1 单项
419
+ POST {{base_url}}/api/v2/web/order/page
420
+ ...
421
+ {"content": {"page":{"current":1,"size":10},"mealtimeTypes":[1]}}
422
+ HTTP 200
423
+ [Asserts]
424
+ jsonpath "$.code" == 10000
425
+
426
+ # TC-LIST-2 多项组合
427
+ POST {{base_url}}/api/v2/web/order/page
428
+ ...
429
+ {"content": {"page":{"current":1,"size":10},"mealtimeTypes":[1,2,3]}}
430
+ HTTP 200
431
+ [Asserts]
432
+ jsonpath "$.code" == 10000
433
+ jsonpath "$.data.records" isCollection
434
+
435
+ # TC-LIST-3 空列表(不过滤)
436
+ POST {{base_url}}/api/v2/web/order/page
437
+ ...
438
+ {"content": {"page":{"current":1,"size":10},"mealtimeTypes":[]}}
439
+ HTTP 200
440
+ [Asserts]
441
+ jsonpath "$.code" == 10000
442
+ ```
443
+
444
+ #### 4.3 测试场景清单(完整版)
445
+
446
+ 每个查询接口必须覆盖以下场景,按顺序在同一 `.hurl` 文件中组织:
447
+
448
+ ```
449
+ 必须生成(顺序执行):
450
+ TC-BASE 基础分页查询 — 只传必填参数,验证分页结构 + VO 所有字段存在
451
+ TC-TOTAL 合计行验证 — 如返回类型含 totalLine,验证所有合计字段
452
+ TC-DATE-* 日期条件用例 — 若有 startDate/endDate(见上方策略)
453
+ TC-KW-* 关键词用例 — 若有 keyword 类字段(见上方策略)
454
+ TC-ENUM-* 枚举用例 — 若有状态/类型枚举,每个枚举值各一条
455
+ TC-ID-* 外键 ID 用例 — 若有 xxxId 过滤(见上方策略)
456
+ TC-LIST-* 集合用例 — 若有多选字段(见上方策略)
457
+ TC-PAGE 翻页验证 — current=2,验证翻页正确(total > size 时才有意义)
458
+ TC-EMPTY 空结果验证 — 使用必然无数据的条件,验证 records count == 0
459
+ TC-EXPORT 导出接口 — 验证 code=10000(若有导出接口)
460
+ TC-UNAUTH 未授权 — 不带 X-Token,预期 HTTP 401
461
+ ```
462
+
463
+ > **命名规范**:在 Hurl 文件中用注释标注场景编号,便于报告定位:
464
+ > ```hurl
465
+ > # TC-DATE-1 正常日期区间
466
+ > # TC-KW-2 无匹配关键词
467
+ > ```
468
+
297
469
  **数据正确性验证**(不只是结构存在,还要验证值合理):
298
470
 
299
471
  ```hurl
@@ -101,6 +101,120 @@ feat(order): 新增订单导出功能
101
101
  Closes #123
102
102
  ```
103
103
 
104
+ ## 数据类型规范
105
+
106
+ ### 布尔语义字段必须使用 Boolean
107
+
108
+ ```java
109
+ // ❌ 错误
110
+ private Integer ifNarrow;
111
+ private Integer isEnabled;
112
+
113
+ // ✅ 正确
114
+ private Boolean narrow; // getter 自动生成 isNarrow()
115
+ private Boolean enabled; // getter 自动生成 isEnabled()
116
+ ```
117
+
118
+ **规则**:
119
+ - 语义为"是/否"的字段,类型必须为 `Boolean`
120
+ - 字段名不加 `if`/`is`/`has` 前缀(JavaBean 规范中 `Boolean` 的 getter 自动生成 `isXxx()`)
121
+ - 数据库字段使用 `TINYINT(1)` 或 `BIT(1)`
122
+
123
+ ### 枚举字段必须提供明确约束
124
+
125
+ ```java
126
+ // ❌ 错误:调用方无法知道合法值
127
+ @ApiModelProperty(value = "操作类型")
128
+ private Integer tradeType;
129
+
130
+ // ✅ 方案一:VO/DTO 层直接用枚举(推荐)
131
+ @ApiModelProperty(value = "操作类型")
132
+ private AccTradeTypeEnum tradeType;
133
+
134
+ // ✅ 方案二:保留 Integer 但标注合法值
135
+ @ApiModelProperty(value = "操作类型:1-充值 2-消费 3-退款", allowableValues = "1,2,3")
136
+ private Integer tradeType;
137
+ ```
138
+
139
+ ### 金额字段禁止使用浮点类型
140
+
141
+ ```java
142
+ // ❌ 错误
143
+ private Double amount;
144
+ private Float price;
145
+
146
+ // ✅ 正确:Entity/Service 层用 Long(分),VO 层展示用 BigDecimal(元)
147
+ private Long amountFen;
148
+ private BigDecimal amountYuan;
149
+ ```
150
+
151
+ ### 原始类型 vs 包装类型
152
+
153
+ | 场景 | 用原始类型 | 用包装类型 |
154
+ |------|----------|----------|
155
+ | Entity / VO / DTO 字段 | — | ✅ 统一用包装类型 |
156
+ | 方法参数(不允许 null) | ✅ `int count` | — |
157
+ | 方法参数(允许 null) | — | ✅ `Integer count` |
158
+ | 局部变量 | ✅ `int i = 0` | — |
159
+
160
+ ## Optional 使用规范
161
+
162
+ ```java
163
+ // ❌ 错误:of() 不接受 null,value 为 null 直接 NPE
164
+ Optional.of(value).orElse(defaultValue);
165
+
166
+ // ✅ 正确:ofNullable() 安全处理 null
167
+ Optional.ofNullable(value).orElse(defaultValue);
168
+
169
+ // ❌ 禁止:Optional 作为方法参数或类字段
170
+ public void process(Optional<String> name) { ... }
171
+ private Optional<String> name;
172
+
173
+ // ✅ 允许:Optional 作为方法返回值、链式处理
174
+ public Optional<Entity> findById(Long id) { ... }
175
+ Optional.ofNullable(entity).map(Entity::getConfig).orElse(DEFAULT_VALUE);
176
+ ```
177
+
178
+ ## @Transactional 规范
179
+
180
+ ```java
181
+ // ❌ 错误:默认只回滚 RuntimeException
182
+ @Transactional
183
+ public void createOrder() { ... }
184
+
185
+ // ✅ 正确:显式指定回滚异常
186
+ @Transactional(rollbackFor = Exception.class)
187
+ public void createOrder() { ... }
188
+ ```
189
+
190
+ - 所有 `@Transactional` 必须显式写 `rollbackFor = Exception.class`
191
+ - 只读查询不加 `@Transactional`(或用 `readOnly = true`)
192
+ - 事务方法不要 try-catch 吞掉异常,否则事务不回滚
193
+
194
+ ## TODO 管理规范
195
+
196
+ ```java
197
+ // ❌ 错误:无负责人、无日期、无跟踪
198
+ // TODO 修改一下
199
+
200
+ // ✅ 正确:完整 TODO 格式
201
+ // TODO(@陈沈杰, 2026-03-20, #TASK-1234): 移动端 AppId 赋值逻辑待产品确认
202
+ ```
203
+
204
+ - 每个 TODO 必须有对应的任务号
205
+ - 超过 2 个迭代未处理的 TODO 必须清理
206
+ - 不用的代码直接删除,不要注释保留
207
+
208
+ ## 代码格式化规范
209
+
210
+ | 项目 | 规范 |
211
+ |------|------|
212
+ | 缩进 | 4 个空格(不用 Tab) |
213
+ | 行宽 | 120 字符 |
214
+ | 大括号 | K&R 风格(同行开始) |
215
+ | 空行 | 方法间 1 个空行,逻辑块间 1 个空行 |
216
+ | import | 分组排序:java → jakarta → org → net → com,组间空行 |
217
+
104
218
  ## 代码示例
105
219
 
106
220
  ### 统一响应格式
@@ -161,3 +275,8 @@ public enum OrderStatusEnum {
161
275
  | Git 提交信息写"fix bug" | 写清楚修了什么:`fix(order): 修复金额计算精度丢失` |
162
276
  | Boolean 变量名:`flag` | 有意义的名字:`isActive`, `hasPermission` |
163
277
  | 缩写命名:`usr`, `mgr` | 完整命名:`user`, `manager` |
278
+ | `Optional.of(可能null值)` | `Optional.ofNullable(value)` |
279
+ | `@Transactional` 无 rollbackFor | `@Transactional(rollbackFor = Exception.class)` |
280
+ | TODO 无负责人和日期 | `// TODO(@负责人, 日期, #任务号): 描述` |
281
+ | 布尔字段用 `Integer` | 用 `Boolean` 类型 |
282
+ | 枚举字段无合法值说明 | `@ApiModelProperty` 标注合法值或直接用枚举类型 |
@@ -88,6 +88,45 @@ Grep pattern: "@Transactional\((?!.*rollbackFor)" path: [命中文件]
88
88
  ```
89
89
  - ❌ `@Transactional` → ✅ `@Transactional(rollbackFor = Exception.class)`
90
90
 
91
+ #### 🔴 A7b. selectOne 无唯一保障
92
+ ```bash
93
+ Grep pattern: "selectOne(" path: [目标目录] glob: "*.java"
94
+ # 命中行检查:是否有 LIMIT 1 或注释说明唯一索引
95
+ ```
96
+ - ❌ 无 LIMIT 且无唯一索引 → ✅ 加 `.last("LIMIT 1")` 或确保有唯一索引
97
+
98
+ #### 🟡 A7c. selectCount 用于存在性判断
99
+ ```bash
100
+ Grep pattern: "selectCount" path: [目标目录] glob: "*.java"
101
+ # 检查命中行是否用于 > 0 判断(存在性),而非真正计数
102
+ ```
103
+ - ❌ `selectCount(w) > 0` → ✅ `mapper.exists(w)` 或 `selectList(w.last("LIMIT 1"))`
104
+
105
+ #### 🔴 A7d. Redis KEYS 命令
106
+ ```bash
107
+ Grep pattern: "keysByPattern|\.keys\(" path: [目标目录] glob: "*.java"
108
+ ```
109
+ - ❌ `redisTemplate.keys()` / `keysByPattern()` → ✅ 使用 Redisson `deleteByPattern()`(内部 SCAN + UNLINK)
110
+
111
+ #### 🟡 A7e. Optional.of 误用
112
+ ```bash
113
+ Grep pattern: "Optional\.of\(" path: [目标目录] glob: "*.java"
114
+ # 排除 ofNullable 的命中
115
+ ```
116
+ - ❌ `Optional.of(可能为null)` → ✅ `Optional.ofNullable(value)`
117
+
118
+ #### 🟡 A7f. 布尔字段类型错误
119
+ ```bash
120
+ Grep pattern: "private Integer (if|is|has)" path: [目标目录] glob: "*.java"
121
+ ```
122
+ - ❌ `private Integer isEnabled` → ✅ `private Boolean enabled`
123
+
124
+ #### 🟡 A7g. Wrapper 嵌套过深
125
+ ```bash
126
+ # 人工审查:Read 文件时检查 Wrapper 嵌套是否超过 2 层
127
+ ```
128
+ - ❌ Wrapper 嵌套 >2 层 → ✅ 迁移到 XML 原生 SQL
129
+
91
130
  #### 🟡 A8. 请求体封装
92
131
  ```bash
93
132
  Grep pattern: "@RequestBody [^L]" path: [目标目录] glob: "*Controller.java"