ai-engineering-init 1.12.1 → 1.13.0

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.
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: lanhu-design
3
+ description: |
4
+ 蓝湖设计稿与 Axure 原型分析。通过 lanhu MCP Server 直接读取蓝湖项目的原型页面、设计图、切图资源。
5
+
6
+ 触发场景:
7
+ - 需要从蓝湖获取 Axure 原型页面进行需求分析
8
+ - 需要查看蓝湖 UI 设计图和设计参数
9
+ - 需要提取蓝湖设计稿中的切图资源
10
+ - 需要通过蓝湖团队留言板协作
11
+ - 收到蓝湖邀请链接需要解析
12
+
13
+ 触发词:蓝湖、lanhu、设计稿、原型图、蓝湖链接、设计图、切图
14
+ ---
15
+
16
+ # 蓝湖设计稿分析
17
+
18
+ ## 前置条件
19
+
20
+ lanhu MCP Server 已注册在 `.claude/settings.json`。首次使用需配置蓝湖 Cookie:
21
+
22
+ 1. 浏览器登录蓝湖 → F12 开发者工具 → Network → 复制请求头中的 Cookie
23
+ 2. 填入 `.claude/settings.json` 的 `lanhu.env.LANHU_COOKIE`
24
+
25
+ ## MCP 工具清单
26
+
27
+ | 工具 | 用途 | 输入 |
28
+ |------|------|------|
29
+ | `lanhu_resolve_invite_link` | 解析蓝湖邀请链接 | 邀请链接 URL |
30
+ | `lanhu_get_pages` | 获取原型文档页面列表+截图 | 含 docId 的蓝湖 URL |
31
+ | `lanhu_get_ai_analyze_page_result` | AI 分析原型页面(开发/测试/探索视角) | 含 docId 的 URL + 页面名 + 模式 |
32
+ | `lanhu_get_designs` | 获取 UI 设计图列表 | 不含 docId 的蓝湖 URL |
33
+ | `lanhu_get_ai_analyze_design_result` | AI 分析设计图(含尺寸/颜色/CSS) | 不含 docId 的 URL + 设计图名 |
34
+ | `lanhu_get_design_slices` | 提取设计稿切图资源 | 不含 docId 的 URL + 设计图名 |
35
+ | `lanhu_say` | 发送团队留言 | URL + 内容 |
36
+ | `lanhu_say_list` | 查询留言列表 | URL 或 'all' |
37
+ | `lanhu_say_detail` | 查看留言详情 | 消息 ID |
38
+ | `lanhu_say_edit` | 编辑留言 | URL + 消息 ID + 新内容 |
39
+ | `lanhu_say_delete` | 删除留言 | URL + 消息 ID |
40
+ | `lanhu_get_members` | 获取项目成员 | URL |
41
+
42
+ ## URL 类型区分(重要)
43
+
44
+ | URL 类型 | 特征 | 用于 |
45
+ |---------|------|------|
46
+ | **原型文档 URL** | 含 `docId` 参数 | `lanhu_get_pages`、`lanhu_get_ai_analyze_page_result` |
47
+ | **设计项目 URL** | 不含 `docId` | `lanhu_get_designs`、`lanhu_get_design_slices` |
48
+ | **邀请链接** | `lanhuapp.com/link/#/invite?sid=xxx` | 先用 `lanhu_resolve_invite_link` 解析 |
49
+
50
+ ## 典型工作流
51
+
52
+ ### 1. 需求分析(与 analyze-requirements 联动)
53
+
54
+ ```
55
+ 收到蓝湖链接
56
+
57
+ ├─ 邀请链接? → lanhu_resolve_invite_link → 获取真实 URL
58
+
59
+ ├─ 含 docId?(原型文档)
60
+ │ ├─ lanhu_get_pages → 获取页面列表和截图
61
+ │ └─ lanhu_get_ai_analyze_page_result(mode="development") → 获取字段/逻辑/流程
62
+
63
+ └─ 不含 docId?(设计项目)
64
+ ├─ lanhu_get_designs → 获取设计图列表
65
+ └─ lanhu_get_ai_analyze_design_result → 获取尺寸/颜色/CSS
66
+ ```
67
+
68
+ ### 2. 前端开发(获取设计参数)
69
+
70
+ ```
71
+ lanhu_get_designs → 查看设计图列表
72
+ → lanhu_get_ai_analyze_design_result → 获取精确设计参数(尺寸/间距/颜色/字体)+ HTML+CSS 代码
73
+ → lanhu_get_design_slices → 提取切图/图标资源
74
+ ```
75
+
76
+ ### 3. 分析模式选择
77
+
78
+ `lanhu_get_ai_analyze_page_result` 支持 3 种模式:
79
+
80
+ | 模式 | 关注点 | 适用场景 |
81
+ |------|--------|---------|
82
+ | `development` | 字段规则、业务逻辑、全局流程图 | 后端/前端开发 |
83
+ | `testing` | 测试场景、用例、边界值、校验规则 | 测试编写 |
84
+ | `exploration` | 核心功能概览、模块依赖、评审要点 | 需求评审 |
85
+
86
+ ## 与其他技能联动
87
+
88
+ | 场景 | 先用 lanhu-design | 再用 |
89
+ |------|-------------------|------|
90
+ | 需求分析 | 获取原型截图和页面结构 | `analyze-requirements` 输出开发任务清单 |
91
+ | 前端开发 | 获取设计参数和切图 | `ui-pc` 实现页面 |
92
+ | 接口开发 | 从原型推导接口字段 | `api-development` / `crud-development` |
93
+
94
+ ## 注意
95
+
96
+ - 蓝湖 Cookie 有效期有限,过期需重新获取
97
+ - 原型文档和设计项目使用不同的 URL 格式,工具不能混用
98
+ - 大型原型建议分页面分析,避免单次请求过大
99
+ - 与 `analyze-requirements` 的区别:本技能负责**从蓝湖获取数据**,`analyze-requirements` 负责**分析数据输出任务**
@@ -0,0 +1,453 @@
1
+ ---
2
+ name: auto-test
3
+ description: |
4
+ API 自动化测试技能。基于 Apifox MCP 读取接口文档,使用 Hurl 生成并执行真实 HTTP 测试,
5
+ 支持单接口测试、单业务组合(CRUD 生命周期)、跨业务串联(多模块流程),生成 HTML/JSON 测试报告。
6
+ 测试失败时自动调用 fix-bug 技能走标准修复流程。
7
+
8
+ 触发场景:
9
+ - 需要对 API 接口进行自动化测试
10
+ - 需要验证接口是否符合文档定义
11
+ - 需要组合多个接口形成业务流程测试
12
+ - 需要生成接口测试报告
13
+ - 开发完成后需要回归测试验证
14
+
15
+ 触发词:自动测试、auto-test、接口测试、API测试、Hurl、测试报告、回归测试
16
+ ---
17
+
18
+ # API 自动化测试(Hurl + Apifox MCP)
19
+
20
+ ## 概述
21
+
22
+ 通过 Apifox MCP 读取接口文档 → AI 生成 Hurl 测试文件 → 执行真实 HTTP 请求 → 生成测试报告 → 失败项**自动调用 `/fix-bug` 走标准修复流程**。
23
+
24
+ **前置依赖**:
25
+ - Hurl CLI(`brew install hurl` / `winget install hurl` / `apt install hurl`)
26
+ - Apifox MCP Server(已配置项目 ID 和 Access Token)
27
+ - 后端服务运行中
28
+
29
+ ## 测试粒度
30
+
31
+ | 粒度 | 说明 | 文件命名 | 示例 |
32
+ |------|------|---------|------|
33
+ | **单接口** | 一个 API 端点的正常+异常场景 | `{接口名}.hurl` | `create-order.hurl` |
34
+ | **单业务组合** | 一个模块的 CRUD 生命周期 | `{模块}-lifecycle.hurl` | `order-lifecycle.hurl` |
35
+ | **跨业务串联** | 多模块联动的完整业务流程 | `{流程名}-flow.hurl` | `menu-order-payment-flow.hurl` |
36
+
37
+ ## 文件结构
38
+
39
+ ```
40
+ tests/hurl/
41
+ ├── env/
42
+ │ ├── dev.env # 开发环境变量
43
+ │ ├── test.env # 测试环境
44
+ │ └── prod.env # 生产环境(只读接口)
45
+ ├── {模块名}/ # 按业务模块组织
46
+ │ ├── {接口名}.hurl # 单接口测试
47
+ │ └── {模块}-lifecycle.hurl # 单业务组合
48
+ ├── flows/ # 跨业务串联
49
+ │ └── {流程名}-flow.hurl
50
+ └── reports/ # 测试报告(gitignore)
51
+ ├── index.html
52
+ └── report.json
53
+ ```
54
+
55
+ ## 系统接口请求响应规范
56
+
57
+ > 详细参考:`references/api-conventions.md`(首次生成测试时必须读取)
58
+
59
+ ### 核心契约速查
60
+
61
+ | 项目 | 规范 |
62
+ |------|------|
63
+ | 请求封装 | `{"content": { ... }}` (LeRequest) |
64
+ | 成功码 | `code == 10000` |
65
+ | 认证头 | `X-Token` + `merchant-id` + `Merchant-Id` |
66
+ | 未授权 | 不带 X-Token → HTTP 401 |
67
+
68
+ ### 返回类型 → data 结构映射
69
+
70
+ | Controller 返回类型 | data 结构 | 断言关键路径 |
71
+ |---------------------|----------|-------------|
72
+ | `Long`(save) | 雪花 ID | `$.data` isInteger |
73
+ | `void`(delete/submit/export) | null | 仅验证 `$.code == 10000` |
74
+ | `XxxVO`(detail) | 对象 | `$.data.id` exists |
75
+ | `PageVO<T>`(设置类 page) | `$.data.records[]` | `$.data.records` isCollection |
76
+ | `ReportBaseTotalVO<T>`(报表 page) | `$.data.resultPage.records[]` + `$.data.totalLine` | 分页 + 合计行 |
77
+
78
+ ### content 传值类型(最易出错)
79
+
80
+ | 场景 | content 类型 | 示例 |
81
+ |------|-------------|------|
82
+ | 分页查询 | 对象 | `{"page":{"current":1,"size":10},"keyword":"xxx"}` |
83
+ | 新增/编辑 | 对象 | `{"name":"xxx","entries":[...]}` |
84
+ | 详情/删除/提交 | **裸 Long** | `{{id}}`(不是 `{"id":{{id}}}`) |
85
+
86
+ > **关键区别**:detail/delete/submit 的 content 是**直接传 ID 值**,不需要包装成对象。
87
+
88
+ ## Hurl 语法速查
89
+
90
+ ### 请求头规范(必须遵守)
91
+
92
+ 每个需要认证的请求必须携带以下 Header:
93
+
94
+ | Header | 来源 | 用途 |
95
+ |--------|------|------|
96
+ | `X-Token` | 环境变量 `{{x_token}}`(配置文件提供) | 认证令牌 |
97
+ | `merchant-id` | 环境变量 `{{merchant_id}}` | 商户路由(数据源切换) |
98
+ | `Merchant-Id` | 环境变量 `{{merchant_id_auth}}` | 商户权限校验 |
99
+
100
+ ### 基础请求 + 断言
101
+
102
+ ```hurl
103
+ # 请求
104
+ POST {{base_url}}/api/v2/web/order/add
105
+ Content-Type: application/json
106
+ X-Token: {{x_token}}
107
+ merchant-id: {{merchant_id}}
108
+ Merchant-Id: {{merchant_id_auth}}
109
+ {
110
+ "content": {
111
+ "menuId": 1001,
112
+ "quantity": 2
113
+ }
114
+ }
115
+
116
+ # 响应断言
117
+ HTTP 200
118
+ [Asserts]
119
+ jsonpath "$.code" == 10000
120
+ jsonpath "$.msg" == "操作成功"
121
+ jsonpath "$.data" isInteger
122
+ ```
123
+
124
+ ### 变量捕获(请求间传递数据)
125
+
126
+ ```hurl
127
+ # 如果需要动态获取 Token,可以通过登录接口捕获
128
+ POST {{base_url}}/api/v2/web/auth/login
129
+ Content-Type: application/json
130
+ {
131
+ "username": "{{username}}",
132
+ "password": "{{password}}"
133
+ }
134
+
135
+ HTTP 200
136
+ [Captures]
137
+ x_token: jsonpath "$.data.token"
138
+ ```
139
+
140
+ > 注意:X-Token 优先使用 env 文件中预配置的值,避免每次都调登录接口。
141
+ > 仅在测试登录流程本身或 Token 过期场景时,才通过登录接口动态获取。
142
+
143
+ ### 常用断言
144
+
145
+ ```hurl
146
+ [Asserts]
147
+ # 状态码
148
+ status == 200
149
+
150
+ # JSON 路径
151
+ jsonpath "$.code" == 10000
152
+ jsonpath "$.data" exists
153
+ jsonpath "$.data" isCollection
154
+ jsonpath "$.data.id" isInteger
155
+ jsonpath "$.data.name" isString
156
+ jsonpath "$.data.list" count > 0
157
+
158
+ # 类型检查
159
+ jsonpath "$.data.price" isFloat
160
+
161
+ # 包含
162
+ jsonpath "$.msg" contains "成功"
163
+
164
+ # 正则
165
+ jsonpath "$.data.phone" matches "^1[3-9]\\d{9}$"
166
+ ```
167
+
168
+ ### 环境变量文件格式
169
+
170
+ ```properties
171
+ # env/dev.env
172
+ base_url=http://192.168.97.235:58300
173
+
174
+ # 认证 Token(X-Token 请求头,配置后所有请求自动携带)
175
+ x_token=你的Token值
176
+
177
+ # 商户路由(merchant-id 请求头,用于数据源切换)
178
+ merchant_id=你的商户ID
179
+
180
+ # 商户权限(Merchant-Id 请求头,用于权限校验)
181
+ merchant_id_auth=你的商户ID
182
+ ```
183
+
184
+ ## 执行命令
185
+
186
+ ```bash
187
+ # 运行单个测试
188
+ hurl --test --variables-file tests/hurl/env/dev.env tests/hurl/order/create-order.hurl
189
+
190
+ # 运行模块所有测试
191
+ hurl --test --variables-file tests/hurl/env/dev.env tests/hurl/order/*.hurl
192
+
193
+ # 运行全部测试 + 生成报告
194
+ hurl --test \
195
+ --variables-file tests/hurl/env/dev.env \
196
+ --report-html tests/hurl/reports \
197
+ --report-json tests/hurl/reports/report.json \
198
+ tests/hurl/**/*.hurl
199
+
200
+ # 传递动态变量(如时间戳避免唯一键冲突)
201
+ TS=$(date +%s) && hurl --test \
202
+ --variables-file tests/hurl/env/dev.env \
203
+ --variable "voucher_type_ts=$TS" \
204
+ --variable "voucher_word_ts=$TS" \
205
+ tests/hurl/finance/*.hurl
206
+
207
+ # 指定超时(毫秒)
208
+ hurl --test --connect-timeout 5000 --max-time 30000 ...
209
+
210
+ # 失败时继续执行(不中断)
211
+ hurl --test --continue-on-error ...
212
+ ```
213
+
214
+ ## 生成测试的流程
215
+
216
+ ### 第一步:读取接口文档
217
+
218
+ 通过 Apifox MCP 读取指定模块的接口列表:
219
+ - 接口路径、HTTP 方法
220
+ - 请求参数(Header、Body、Query)
221
+ - 响应结构(字段名、类型、示例值)
222
+
223
+ ### 第二步:读取 Param/VO 源码(关键!)
224
+
225
+ **必须读取后端源码**以确保测试完整性:
226
+
227
+ 1. **Param 类**:提取所有查询条件字段,确保每个字段都有对应测试用例
228
+ 2. **VO 类**:提取所有响应字段,在断言中验证字段存在性和类型
229
+ 3. **Mapper XML**:理解 SQL 逻辑、JOIN 关系、动态条件
230
+
231
+ ```
232
+ 示例:SubjectDetailParam 有 startDate、endDate、areaId、canteenId、keyword
233
+ → 测试用例必须覆盖:
234
+ - 基础分页查询(startDate + endDate)
235
+ - keyword 搜索(科目编码/名称)
236
+ - 区域+食堂筛选(areaId + canteenId)
237
+ - 单日查询(startDate == endDate)
238
+ - 空结果区间(验证空数组返回)
239
+ - 分页第二页
240
+ - 导出接口
241
+ - 未授权测试
242
+ ```
243
+
244
+ ### 第三步:准备测试数据(先查后用,禁止硬编码)
245
+
246
+ **核心原则:测试数据必须从真实环境动态获取,不能硬编码不存在的 ID 或编码。**
247
+
248
+ ```hurl
249
+ # ✅ 正确:先查询获取真实数据,再用于后续请求
250
+ # 0a. 获取真实食堂 ID
251
+ POST {{base_url}}/api/v2/alloc/canteen/page-canteen
252
+ ...
253
+ [Captures]
254
+ canteen_id: jsonpath "$.data.records[0].canteenId"
255
+ area_id: jsonpath "$.data.records[0].areaId"
256
+
257
+ # 0b. 获取关联表的真实记录
258
+ POST {{base_url}}/report/finance/setting/voucher-type/page
259
+ ...
260
+ [Captures]
261
+ voucher_type_id: jsonpath "$.data.records[0].id"
262
+ ```
263
+
264
+ ```hurl
265
+ # ❌ 错误:硬编码不存在的引用数据
266
+ {
267
+ "content": {
268
+ "costNo": "9999", # 可能不存在
269
+ "voucherTypeId": 1 # 可能不存在
270
+ }
271
+ }
272
+ ```
273
+
274
+ **测试数据准备检查清单**:
275
+ - [ ] 外键引用的 ID 通过查询接口动态获取
276
+ - [ ] 业务编码(如 costNo)使用数据库中已存在的真实记录
277
+ - [ ] 唯一键字段使用时间戳变量避免冲突(`{{xxx_ts}}`)
278
+ - [ ] 创建的测试数据标记 summary 含 `auto-test` 便于识别清理
279
+ - [ ] 测试结束后有清理步骤(删除测试数据)
280
+
281
+ ### 第四步:生成 .hurl 文件
282
+
283
+ **查询条件完整覆盖**(每个 Param 字段都要有测试用例):
284
+
285
+ ```
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. 数据清理 — 删除测试创建的数据
295
+ ```
296
+
297
+ **数据正确性验证**(不只是结构存在,还要验证值合理):
298
+
299
+ ```hurl
300
+ # ✅ 结构验证 + 数据正确性
301
+ [Asserts]
302
+ jsonpath "$.code" == 10000
303
+ jsonpath "$.data.resultPage.records" isCollection
304
+ jsonpath "$.data.resultPage.records" count > 0
305
+ # VO 字段存在性
306
+ jsonpath "$.data.resultPage.records[0].costNo" isString
307
+ jsonpath "$.data.resultPage.records[0].costTypeName" isString # 关联字段不能为 null
308
+ jsonpath "$.data.resultPage.records[0].debitAmount" exists
309
+ # 合计行数据合理性
310
+ jsonpath "$.data.totalLine.debitAmount" exists
311
+ jsonpath "$.data.totalLine.creditAmount" exists
312
+ ```
313
+
314
+ ### 第五步:执行并生成报告
315
+
316
+ ```bash
317
+ hurl --test \
318
+ --variables-file tests/hurl/env/dev.env \
319
+ --report-html tests/hurl/reports \
320
+ --report-json tests/hurl/reports/report.json \
321
+ --continue-on-error \
322
+ tests/hurl/{模块}/*.hurl
323
+ ```
324
+
325
+ ### 第六步:解析报告
326
+
327
+ 读取 `report.json`,输出摘要:
328
+
329
+ ```markdown
330
+ ## 测试报告摘要
331
+
332
+ | 状态 | 数量 | 占比 |
333
+ |------|------|------|
334
+ | PASS | 15 | 75% |
335
+ | FAIL | 3 | 15% |
336
+ | ERROR| 2 | 10% |
337
+
338
+ ### 失败详情
339
+
340
+ | 文件 | 接口 | 失败原因 |
341
+ |------|------|---------|
342
+ | order/create-order.hurl | POST /api/v2/web/order/add | 预期 code=10000,实际 code=40001 |
343
+ | menu/query-menu.hurl | GET /api/v2/web/menu/get/1 | 预期 HTTP 200,实际 HTTP 500 |
344
+ ```
345
+
346
+ ### 第七步:失败项自动触发 fix-bug 流程
347
+
348
+ **当测试存在失败项时,必须自动调用 `/fix-bug` 走标准修复流程:**
349
+
350
+ ```
351
+ 测试失败 → 分析失败原因 → 分类处理:
352
+
353
+ 1. 测试数据问题(非代码 Bug):
354
+ - 引用数据不存在 → 修正测试用例中的数据
355
+ - 唯一键冲突 → 添加时间戳变量
356
+ - 权限/状态限制 → 调整测试前置条件
357
+
358
+ 2. 后端代码 Bug:
359
+ → 自动调用 Skill(fix-bug) 走标准修复流程
360
+ → 包含:排查报告 → 用户确认 → 修复代码 → 重跑测试验证
361
+
362
+ 3. 接口文档与实现不一致:
363
+ → 输出差异报告,等待用户确认以哪个为准
364
+ ```
365
+
366
+ **fix-bug 自动触发条件**:
367
+ - HTTP 状态码非预期(如 500)
368
+ - 业务码非预期(如 code != 10000)
369
+ - 响应字段缺失或类型不匹配
370
+ - 关联字段为 null(如 costTypeName 为 null 说明 JOIN 失败)
371
+ - 导出数据异常(原始字段暴露、金额未转换等)
372
+
373
+ **Bug 报告格式**(传递给 fix-bug):
374
+
375
+ ```
376
+ Bug 信息:
377
+ - 接口:{METHOD} {URL}
378
+ - 请求参数:{request body}
379
+ - 预期响应:{expected}
380
+ - 实际响应:{actual}
381
+ - Hurl 文件:{file path}
382
+ - 失败断言:{assertion detail}
383
+ - 关联源码:{Controller/Service/Mapper 文件路径}
384
+ ```
385
+
386
+ ## LeRequest 适配
387
+
388
+ leniu 项目的 POST 请求体使用 `LeRequest<T>` 封装:
389
+
390
+ ```hurl
391
+ # ✅ 正确:使用 content 包装 + 三个必要 Header
392
+ POST {{base_url}}/api/v2/web/order/add
393
+ Content-Type: application/json
394
+ X-Token: {{x_token}}
395
+ merchant-id: {{merchant_id}}
396
+ Merchant-Id: {{merchant_id_auth}}
397
+ {
398
+ "content": {
399
+ "menuId": 1001,
400
+ "quantity": 2
401
+ }
402
+ }
403
+
404
+ # ❌ 错误:直接传业务字段 / 缺少 Header
405
+ POST {{base_url}}/api/v2/web/order/add
406
+ {
407
+ "menuId": 1001,
408
+ "quantity": 2
409
+ }
410
+ ```
411
+
412
+ ## 已知陷阱(Lessons Learned)
413
+
414
+ ### 1. del_flag 约定不统一
415
+ leniu 主表用 `2=正常, 1=删除`,但某些设置表(如 `finance_voucher_type`, `finance_voucher_word`)使用 `@TableLogic` 默认值 `0=正常, 1=删除`。**不要盲目统一,要检查每张表的实际约定。**
416
+
417
+ ### 2. 测试数据必须使用真实存在的引用数据
418
+ 关联查询(JOIN)依赖引用数据存在且状态正确。例如:
419
+ - `cost_type` 表的 `state = 1` 才是有效记录
420
+ - `costNo` 必须在 `cost_type` 中存在且 `state = 1`
421
+ - 否则 LEFT JOIN 后关联字段(如 `costTypeName`)为 null
422
+
423
+ ### 3. 导出验证要点
424
+ 导出接口是异步的(返回 void → code=10000),需要额外关注:
425
+ - VO 的 `@ExcelIgnore` 是否正确标记了内部字段
426
+ - `@ExcelProperty(order=N)` 的顺序是否符合产品要求
427
+ - 金额字段是否配置了 `converter = CustomNumberConverter.class`(分→元)
428
+ - 枚举字段是否有对应的描述字段(如 submitStatus → submitStatusDesc)
429
+
430
+ ### 4. 唯一键冲突
431
+ CRUD 生命周期测试中,新增记录的唯一键字段(如 typeCode)需要加时间戳变量:
432
+ ```hurl
433
+ "typeCode": "AT_HURL_{{voucher_type_ts}}"
434
+ ```
435
+ 执行时通过 `--variable "voucher_type_ts=$(date +%s)"` 传入。
436
+
437
+ ### 5. 后端未重启
438
+ 修改后端代码后必须重启服务才能生效。如果测试结果不符合预期,先确认后端是否已重启。
439
+
440
+ ## .gitignore 配置
441
+
442
+ ```gitignore
443
+ # Hurl 测试报告
444
+ tests/hurl/reports/
445
+ ```
446
+
447
+ ## 注意
448
+
449
+ - 测试文件(`.hurl`)需要 Git 管理,报告目录不需要
450
+ - `env/*.env` 中的密码/Token 建议用环境变量替代,不要提交敏感信息
451
+ - 如果是 leniu 项目的 POST 接口,请求体必须用 `{"content": {...}}` 包装
452
+ - 生成测试前确保 Hurl CLI 已安装(`hurl --version`)
453
+ - 与 `test-development` 技能的区别:本技能是真实 HTTP 请求的集成测试,`test-development` 是 JUnit5 单元测试/MockMvc 测试