@devflow-tools/plugin-nest 0.12.6 → 0.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.
- package/dist/analyzers/diagnose-bug.d.ts +7 -0
- package/dist/analyzers/diagnose-bug.d.ts.map +1 -0
- package/dist/analyzers/diagnose-bug.js +66 -0
- package/dist/analyzers/diagnose-bug.js.map +1 -0
- package/dist/analyzers/new-module.d.ts +3 -0
- package/dist/analyzers/new-module.d.ts.map +1 -0
- package/dist/analyzers/new-module.js +108 -0
- package/dist/analyzers/new-module.js.map +1 -0
- package/dist/analyzers/types.d.ts +19 -0
- package/dist/analyzers/types.d.ts.map +1 -0
- package/dist/analyzers/types.js +2 -0
- package/dist/analyzers/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1626 -0
- package/dist/index.js.map +1 -0
- package/package.json +2 -2
package/dist/index.js
ADDED
|
@@ -0,0 +1,1626 @@
|
|
|
1
|
+
export const nestPlugin = {
|
|
2
|
+
meta: {
|
|
3
|
+
name: "nest",
|
|
4
|
+
version: "0.1.0",
|
|
5
|
+
description: "NestJS 专家 — 模块架构、守卫调试、微服务配置、Swagger 生成",
|
|
6
|
+
icon: "🐱",
|
|
7
|
+
tags: ["backend", "nestjs", "typescript", "microservices"],
|
|
8
|
+
},
|
|
9
|
+
knowledge: {
|
|
10
|
+
sources: [
|
|
11
|
+
{
|
|
12
|
+
name: "nestjs-docs",
|
|
13
|
+
urls: [
|
|
14
|
+
"https://docs.nestjs.com/",
|
|
15
|
+
"https://docs.nestjs.com/providers",
|
|
16
|
+
"https://docs.nestjs.com/guards",
|
|
17
|
+
"https://docs.nestjs.com/microservices/basics",
|
|
18
|
+
],
|
|
19
|
+
pluginName: "nest",
|
|
20
|
+
version: "10.3.0",
|
|
21
|
+
renderJs: true,
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
memory: {
|
|
26
|
+
project: {
|
|
27
|
+
stack: ["NestJS 10", "TypeScript", "Prisma", "PostgreSQL"],
|
|
28
|
+
conventions: [
|
|
29
|
+
{ key: "modules", value: "每个功能封装为独立 Module", source: "manual", confidence: 0.95 },
|
|
30
|
+
{ key: "di", value: "使用构造函数依赖注入,通过 interface 解耦", source: "manual", confidence: 0.9 },
|
|
31
|
+
{ key: "validation", value: "使用 class-validator + ValidationPipe 校验请求", source: "manual", confidence: 0.9 },
|
|
32
|
+
{ key: "exception", value: "使用 NestJS 内置异常层,统一错误响应格式", source: "manual", confidence: 0.85 },
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
global: [
|
|
36
|
+
{
|
|
37
|
+
id: "nest:module-architecture",
|
|
38
|
+
source: "nest",
|
|
39
|
+
title: "Module 架构设计",
|
|
40
|
+
content: "NestJS 应用由 Module 组成。每个 Module 使用 @Module() 装饰器定义 controllers、providers、imports、exports。使用 exports 暴露可共享的 Provider。",
|
|
41
|
+
embedding: [],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "nest:guards-interceptors",
|
|
45
|
+
source: "nest",
|
|
46
|
+
title: "守卫与拦截器执行链",
|
|
47
|
+
content: "Guard → Interceptor(前) → Pipe → Handler → Interceptor(后) → Filter。Guards 用于认证授权,Interceptors 用于请求前后处理,Pipes 用于数据验证,Filters 用于异常处理。",
|
|
48
|
+
embedding: [],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "nest:microservices",
|
|
52
|
+
source: "nest",
|
|
53
|
+
title: "微服务传输层",
|
|
54
|
+
content: "支持 TCP、Redis、MQTT、Kafka、RabbitMQ、NATS、gRPC。@MessagePattern() 处理请求-响应,@EventPattern() 处理事件广播。ClientProxy 用于服务间通信。",
|
|
55
|
+
embedding: [],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "nest:prisma-integration",
|
|
59
|
+
source: "nest",
|
|
60
|
+
title: "Prisma 集成最佳实践",
|
|
61
|
+
content: "将 PrismaService 封装为全局 Module。使用 OnModuleInit 生命周期连接数据库。使用 Prisma Client 类型生成 DTO。事务使用 prisma.$transaction。",
|
|
62
|
+
embedding: [],
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
workflow: {
|
|
67
|
+
steps: [],
|
|
68
|
+
templates: [
|
|
69
|
+
{
|
|
70
|
+
name: "new-module",
|
|
71
|
+
description: "新建 NestJS 模块(Controller + Service + Module + DTO + 测试)",
|
|
72
|
+
version: "2.0.0",
|
|
73
|
+
steps: [
|
|
74
|
+
{
|
|
75
|
+
id: "analyze-domain",
|
|
76
|
+
title: "分析业务领域确定模块边界",
|
|
77
|
+
instruction: [
|
|
78
|
+
"## 分析业务领域确定模块边界",
|
|
79
|
+
"",
|
|
80
|
+
"根据需求描述,识别独立的业务领域,确定新 Module 的职责边界和与现有模块的关系。",
|
|
81
|
+
"",
|
|
82
|
+
"**执行动作**:",
|
|
83
|
+
"1. 查看现有模块结构:",
|
|
84
|
+
" ```bash",
|
|
85
|
+
" find src/modules -maxdepth 2 -type d",
|
|
86
|
+
" find src/ -name '*.module.ts'",
|
|
87
|
+
" ```",
|
|
88
|
+
"2. 查看现有 Module 的 exports 清单:",
|
|
89
|
+
" ```bash",
|
|
90
|
+
" grep -rn 'exports:' src/ --include='*.module.ts'",
|
|
91
|
+
" ```",
|
|
92
|
+
"3. 查看项目根模块 AppModule 的 imports:",
|
|
93
|
+
" ```bash",
|
|
94
|
+
" grep -rn 'imports:' src/app.module.ts",
|
|
95
|
+
" ```",
|
|
96
|
+
"4. 根据业务领域划分新 Module 的边界:",
|
|
97
|
+
" - 是否应该新增独立 Module,还是扩展现有 Module",
|
|
98
|
+
" - 新 Module 需要提供哪些 Controller 端点",
|
|
99
|
+
" - 新 Module 依赖哪些现有 Module(通过 imports)",
|
|
100
|
+
" - 新 Module 要 exports 哪些 Provider 供其他模块使用",
|
|
101
|
+
"5. 检查是否存在命名约定(如 `feature/` vs `modules/`)",
|
|
102
|
+
"",
|
|
103
|
+
"**产出格式**:",
|
|
104
|
+
"```",
|
|
105
|
+
"module_name: <PascalCase>",
|
|
106
|
+
"module_path: src/modules/<module-name>/",
|
|
107
|
+
"responsibilities: [<一句话描述>, ...]",
|
|
108
|
+
"depends_on: [<existing module name>, ...]",
|
|
109
|
+
"exports: [<provider name>, ...]",
|
|
110
|
+
"endpoints: [{ method, path, description }, ...]",
|
|
111
|
+
"```",
|
|
112
|
+
].join("\n"),
|
|
113
|
+
suggestedTools: ["Bash", "Read"],
|
|
114
|
+
output: "模块边界定义、依赖关系、端点规划",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: "define-dtos",
|
|
118
|
+
title: "定义 DTO 和验证规则",
|
|
119
|
+
instruction: [
|
|
120
|
+
"## 定义 DTO 和验证规则(class-validator + Swagger 装饰器)",
|
|
121
|
+
"",
|
|
122
|
+
"为 Module 的所有请求和响应定义类型安全的 DTO,并加上 class-validator 验证规则和 Swagger 文档装饰器。",
|
|
123
|
+
"",
|
|
124
|
+
"**执行动作**:",
|
|
125
|
+
"1. 在 `src/modules/<name>/dto/` 目录下创建 DTO 文件:",
|
|
126
|
+
" - `create-<name>.dto.ts` — 创建请求体",
|
|
127
|
+
" - `update-<name>.dto.ts` — 更新请求体",
|
|
128
|
+
" - `query-<name>.dto.ts` — 查询参数(分页、过滤、排序)",
|
|
129
|
+
" - `<name>-response.dto.ts` — 响应体(可选)",
|
|
130
|
+
"2. 每个字段使用 class-validator 装饰器:",
|
|
131
|
+
" ```typescript",
|
|
132
|
+
" import { IsString, IsInt, MinLength, IsOptional } from 'class-validator';",
|
|
133
|
+
" import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';",
|
|
134
|
+
"",
|
|
135
|
+
" export class CreateUserDto {",
|
|
136
|
+
" @ApiProperty({ description: '用户名', example: 'alice' })",
|
|
137
|
+
" @IsString()",
|
|
138
|
+
" @MinLength(3)",
|
|
139
|
+
" username: string;",
|
|
140
|
+
"",
|
|
141
|
+
" @ApiPropertyOptional({ description: '年龄' })",
|
|
142
|
+
" @IsOptional()",
|
|
143
|
+
" @IsInt()",
|
|
144
|
+
" age?: number;",
|
|
145
|
+
" }",
|
|
146
|
+
" ```",
|
|
147
|
+
"3. 每个字段同时使用 `@ApiProperty` 和 `@IsXxx` 装饰器,避免遗漏",
|
|
148
|
+
"4. 使用 `@ApiProperty({ example: ... })` 提供示例值",
|
|
149
|
+
"5. 查询类 DTO 使用 `@ApiPropertyOptional` 标记可选参数",
|
|
150
|
+
"6. 对于分页 DTO,使用 `@Type(() => Number)` 配合 class-transformer:",
|
|
151
|
+
" ```typescript",
|
|
152
|
+
" import { Type } from 'class-transformer';",
|
|
153
|
+
" @IsOptional()",
|
|
154
|
+
" @Type(() => Number)",
|
|
155
|
+
" @IsInt()",
|
|
156
|
+
" page?: number;",
|
|
157
|
+
" ```",
|
|
158
|
+
"7. 复用已有 DTO 时使用 `PartialType`、`PickType`、`OmitType`:",
|
|
159
|
+
" ```typescript",
|
|
160
|
+
" export class UpdateUserDto extends PartialType(CreateUserDto) {}",
|
|
161
|
+
" ```",
|
|
162
|
+
"",
|
|
163
|
+
"**产出格式**:",
|
|
164
|
+
"```",
|
|
165
|
+
"dto_files_created: [{ path, class_name, fields: [{ name, type, validators }] }]",
|
|
166
|
+
"```",
|
|
167
|
+
].join("\n"),
|
|
168
|
+
suggestedTools: ["Read", "Write", "Edit"],
|
|
169
|
+
output: "DTO 文件清单、字段验证规则、Swagger 装饰器",
|
|
170
|
+
depends: ["analyze-domain"],
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: "create-service",
|
|
174
|
+
title: "创建 Service",
|
|
175
|
+
instruction: [
|
|
176
|
+
"## 创建 Service(业务逻辑 + Prisma 查询)",
|
|
177
|
+
"",
|
|
178
|
+
"封装业务逻辑的 Service,使用 Prisma 进行数据库操作。",
|
|
179
|
+
"",
|
|
180
|
+
"**执行动作**:",
|
|
181
|
+
"1. 创建 Service 文件 `src/modules/<name>/<name>.service.ts`:",
|
|
182
|
+
" ```typescript",
|
|
183
|
+
" import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';",
|
|
184
|
+
" import { PrismaService } from '../prisma/prisma.service';",
|
|
185
|
+
" import { CreateXxxDto } from './dto/create-xxx.dto';",
|
|
186
|
+
"",
|
|
187
|
+
" @Injectable()",
|
|
188
|
+
" export class XxxService {",
|
|
189
|
+
" constructor(private readonly prisma: PrismaService) {}",
|
|
190
|
+
"",
|
|
191
|
+
" async create(dto: CreateXxxDto) {",
|
|
192
|
+
" try {",
|
|
193
|
+
" return await this.prisma.xxx.create({ data: dto });",
|
|
194
|
+
" } catch (error) {",
|
|
195
|
+
" if (error.code === 'P2002') {",
|
|
196
|
+
" throw new ConflictException('记录已存在');",
|
|
197
|
+
" }",
|
|
198
|
+
" throw error;",
|
|
199
|
+
" }",
|
|
200
|
+
" }",
|
|
201
|
+
"",
|
|
202
|
+
" async findAll(query: QueryXxxDto) {",
|
|
203
|
+
" const { page = 1, pageSize = 20 } = query;",
|
|
204
|
+
" return this.prisma.xxx.findMany({",
|
|
205
|
+
" skip: (page - 1) * pageSize,",
|
|
206
|
+
" take: pageSize,",
|
|
207
|
+
" orderBy: { createdAt: 'desc' },",
|
|
208
|
+
" });",
|
|
209
|
+
" }",
|
|
210
|
+
"",
|
|
211
|
+
" async findOne(id: string) {",
|
|
212
|
+
" const record = await this.prisma.xxx.findUnique({ where: { id } });",
|
|
213
|
+
" if (!record) throw new NotFoundException(`Xxx #${id} not found`);",
|
|
214
|
+
" return record;",
|
|
215
|
+
" }",
|
|
216
|
+
"",
|
|
217
|
+
" async update(id: string, dto: UpdateXxxDto) {",
|
|
218
|
+
" await this.findOne(id);",
|
|
219
|
+
" return this.prisma.xxx.update({ where: { id }, data: dto });",
|
|
220
|
+
" }",
|
|
221
|
+
"",
|
|
222
|
+
" async remove(id: string) {",
|
|
223
|
+
" await this.findOne(id);",
|
|
224
|
+
" return this.prisma.xxx.delete({ where: { id } });",
|
|
225
|
+
" }",
|
|
226
|
+
" }",
|
|
227
|
+
" ```",
|
|
228
|
+
"2. 使用 `@Injectable()` 装饰器标记所有 Service",
|
|
229
|
+
"3. 通过构造函数注入 PrismaService(或其他依赖的 Service)",
|
|
230
|
+
"4. 使用 NestJS 内置 HttpException 子类(NotFoundException, ConflictException 等)",
|
|
231
|
+
"5. Prisma 已知异常(P2002 唯一约束、P2025 记录不存在)要显式捕获并转换为 HttpException",
|
|
232
|
+
"6. 事务场景使用 `prisma.$transaction([ ... ])` 或交互式事务",
|
|
233
|
+
"7. 避免在 Service 中处理 HTTP 请求/响应细节(那是 Controller 的职责)",
|
|
234
|
+
"",
|
|
235
|
+
"**产出格式**:",
|
|
236
|
+
"```",
|
|
237
|
+
"service_file: <path>",
|
|
238
|
+
"class_name: <Name>Service",
|
|
239
|
+
"methods: [{ name, signature, throws }]",
|
|
240
|
+
"dependencies: [PrismaService, ...]",
|
|
241
|
+
"```",
|
|
242
|
+
].join("\n"),
|
|
243
|
+
suggestedTools: ["Read", "Write", "Edit"],
|
|
244
|
+
output: "Service 文件、方法签名、依赖注入清单",
|
|
245
|
+
depends: ["define-dtos"],
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
id: "create-controller",
|
|
249
|
+
title: "创建 Controller",
|
|
250
|
+
instruction: [
|
|
251
|
+
"## 创建 Controller(路由 + 守卫 + 拦截器)",
|
|
252
|
+
"",
|
|
253
|
+
"定义 HTTP 路由和处理请求参数,将业务逻辑委托给 Service。",
|
|
254
|
+
"",
|
|
255
|
+
"**执行动作**:",
|
|
256
|
+
"1. 创建 Controller 文件 `src/modules/<name>/<name>.controller.ts`:",
|
|
257
|
+
" ```typescript",
|
|
258
|
+
" import {",
|
|
259
|
+
" Controller, Get, Post, Put, Delete, Patch,",
|
|
260
|
+
" Body, Param, Query, ParseIntPipe,",
|
|
261
|
+
" UseGuards, UseInterceptors,",
|
|
262
|
+
" HttpCode, HttpStatus,",
|
|
263
|
+
" } from '@nestjs/common';",
|
|
264
|
+
" import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';",
|
|
265
|
+
" import { XxxService } from './xxx.service';",
|
|
266
|
+
" import { CreateXxxDto } from './dto/create-xxx.dto';",
|
|
267
|
+
" import { UpdateXxxDto } from './dto/update-xxx.dto';",
|
|
268
|
+
" import { QueryXxxDto } from './dto/query-xxx.dto';",
|
|
269
|
+
" import { JwtAuthGuard } from '../auth/jwt-auth.guard';",
|
|
270
|
+
"",
|
|
271
|
+
" @ApiTags('xxx')",
|
|
272
|
+
" @ApiBearerAuth()",
|
|
273
|
+
" @UseGuards(JwtAuthGuard)",
|
|
274
|
+
" @Controller('xxx')",
|
|
275
|
+
" export class XxxController {",
|
|
276
|
+
" constructor(private readonly xxxService: XxxService) {}",
|
|
277
|
+
"",
|
|
278
|
+
" @Post()",
|
|
279
|
+
" @ApiOperation({ summary: '创建 xxx' })",
|
|
280
|
+
" create(@Body() dto: CreateXxxDto) {",
|
|
281
|
+
" return this.xxxService.create(dto);",
|
|
282
|
+
" }",
|
|
283
|
+
"",
|
|
284
|
+
" @Get()",
|
|
285
|
+
" @ApiOperation({ summary: '查询 xxx 列表' })",
|
|
286
|
+
" findAll(@Query() query: QueryXxxDto) {",
|
|
287
|
+
" return this.xxxService.findAll(query);",
|
|
288
|
+
" }",
|
|
289
|
+
"",
|
|
290
|
+
" @Get(':id')",
|
|
291
|
+
" @ApiOperation({ summary: '查询单个 xxx' })",
|
|
292
|
+
" findOne(@Param('id') id: string) {",
|
|
293
|
+
" return this.xxxService.findOne(id);",
|
|
294
|
+
" }",
|
|
295
|
+
"",
|
|
296
|
+
" @Patch(':id')",
|
|
297
|
+
" @ApiOperation({ summary: '更新 xxx' })",
|
|
298
|
+
" update(@Param('id') id: string, @Body() dto: UpdateXxxDto) {",
|
|
299
|
+
" return this.xxxService.update(id, dto);",
|
|
300
|
+
" }",
|
|
301
|
+
"",
|
|
302
|
+
" @Delete(':id')",
|
|
303
|
+
" @HttpCode(HttpStatus.NO_CONTENT)",
|
|
304
|
+
" @ApiOperation({ summary: '删除 xxx' })",
|
|
305
|
+
" remove(@Param('id') id: string) {",
|
|
306
|
+
" return this.xxxService.remove(id);",
|
|
307
|
+
" }",
|
|
308
|
+
" }",
|
|
309
|
+
" ```",
|
|
310
|
+
"2. 路由前缀使用 `@Controller('<resource-name>')`(kebab-case 复数,如 `users`)",
|
|
311
|
+
"3. Controller 只做两件事:接收参数 + 委托给 Service",
|
|
312
|
+
"4. 使用 `@ApiTags()`、`@ApiBearerAuth()`、`@ApiOperation()` 生成 Swagger 文档",
|
|
313
|
+
"5. 使用 `@UseGuards(JwtAuthGuard)` 应用认证守卫(按需)",
|
|
314
|
+
"6. 使用 `@UseInterceptors()` 应用拦截器(如 TransformInterceptor)",
|
|
315
|
+
"7. 参数转换使用 NestJS 内置 Pipe:`ParseIntPipe`、`ParseUUIDPipe` 等",
|
|
316
|
+
"8. 不要在 Controller 中写业务逻辑,所有逻辑在 Service 中",
|
|
317
|
+
"",
|
|
318
|
+
"**产出格式**:",
|
|
319
|
+
"```",
|
|
320
|
+
"controller_file: <path>",
|
|
321
|
+
"class_name: <Name>Controller",
|
|
322
|
+
"route_prefix: <resource-name>",
|
|
323
|
+
"endpoints: [{ method, path, handler, guards, pipes }]",
|
|
324
|
+
"```",
|
|
325
|
+
].join("\n"),
|
|
326
|
+
suggestedTools: ["Read", "Write", "Edit"],
|
|
327
|
+
output: "Controller 文件、路由端点清单、使用的 Guard/Interceptor",
|
|
328
|
+
depends: ["define-dtos"],
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
id: "create-module",
|
|
332
|
+
title: "创建 Module 并注册依赖",
|
|
333
|
+
instruction: [
|
|
334
|
+
"## 创建 Module 并注册依赖",
|
|
335
|
+
"",
|
|
336
|
+
"将所有 Controller、Service、以及依赖的其他 Module 组合成一个内聚的 Module。",
|
|
337
|
+
"",
|
|
338
|
+
"**执行动作**:",
|
|
339
|
+
"1. 创建 Module 文件 `src/modules/<name>/<name>.module.ts`:",
|
|
340
|
+
" ```typescript",
|
|
341
|
+
" import { Module } from '@nestjs/common';",
|
|
342
|
+
" import { XxxController } from './xxx.controller';",
|
|
343
|
+
" import { XxxService } from './xxx.service';",
|
|
344
|
+
" import { PrismaModule } from '../prisma/prisma.module';",
|
|
345
|
+
" import { AuthModule } from '../auth/auth.module';",
|
|
346
|
+
"",
|
|
347
|
+
" @Module({",
|
|
348
|
+
" imports: [PrismaModule, AuthModule],",
|
|
349
|
+
" controllers: [XxxController],",
|
|
350
|
+
" providers: [XxxService],",
|
|
351
|
+
" exports: [XxxService],",
|
|
352
|
+
" })",
|
|
353
|
+
" export class XxxModule {}",
|
|
354
|
+
" ```",
|
|
355
|
+
"2. `imports`:列出所有依赖的其他 Module(提供 PrismaService、AuthService 等)",
|
|
356
|
+
"3. `controllers`:列出本 Module 的所有 Controller",
|
|
357
|
+
"4. `providers`:列出本 Module 的所有 Service / Provider",
|
|
358
|
+
"5. `exports`:如果其他 Module 需要使用本 Module 的 Service,必须显式 export",
|
|
359
|
+
"6. 遵循单一职责:一个 Module 只负责一个业务领域",
|
|
360
|
+
"7. 如果 PrismaService 是全局注册的(`@Global()`),则不需要在 imports 中重复添加",
|
|
361
|
+
"8. 检查是否遗漏了任何依赖的 Provider 未在 Module 中注册",
|
|
362
|
+
"",
|
|
363
|
+
"**产出格式**:",
|
|
364
|
+
"```",
|
|
365
|
+
"module_file: <path>",
|
|
366
|
+
"class_name: <Name>Module",
|
|
367
|
+
"imports: [<module names>]",
|
|
368
|
+
"controllers: [<controller names>]",
|
|
369
|
+
"providers: [<service names>]",
|
|
370
|
+
"exports: [<exported service names>]",
|
|
371
|
+
"```",
|
|
372
|
+
].join("\n"),
|
|
373
|
+
suggestedTools: ["Read", "Write", "Edit"],
|
|
374
|
+
output: "Module 文件、依赖注册清单",
|
|
375
|
+
depends: ["create-service", "create-controller"],
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
id: "register-parent",
|
|
379
|
+
title: "在父模块中注册新模块",
|
|
380
|
+
instruction: [
|
|
381
|
+
"## 在父模块中注册新模块",
|
|
382
|
+
"",
|
|
383
|
+
"将新创建的 Module 注册到根模块(AppModule)或特性父模块中,让 NestJS 能发现并加载它。",
|
|
384
|
+
"",
|
|
385
|
+
"**执行动作**:",
|
|
386
|
+
"1. 找到根模块或父模块文件:",
|
|
387
|
+
" ```bash",
|
|
388
|
+
" grep -rn 'imports:' src/app.module.ts",
|
|
389
|
+
" # 或找到特性父模块",
|
|
390
|
+
" grep -rn 'XxxModule' src/ --include='*.module.ts'",
|
|
391
|
+
" ```",
|
|
392
|
+
"2. 在父模块的 `imports` 数组中添加新 Module:",
|
|
393
|
+
" ```typescript",
|
|
394
|
+
" import { XxxModule } from './xxx/xxx.module';",
|
|
395
|
+
"",
|
|
396
|
+
" @Module({",
|
|
397
|
+
" imports: [",
|
|
398
|
+
" // ... existing imports",
|
|
399
|
+
" XxxModule,",
|
|
400
|
+
" ],",
|
|
401
|
+
" })",
|
|
402
|
+
" export class AppModule {}",
|
|
403
|
+
" ```",
|
|
404
|
+
"3. 如果是特性模块,注册到对应的父特性模块而不是 AppModule",
|
|
405
|
+
"4. 检查 import 路径是否正确(使用相对路径)",
|
|
406
|
+
"5. 检查是否应该使用动态 Module(`XxxModule.forRoot()` / `XxxModule.register()`)",
|
|
407
|
+
"6. 验证 Module 注册顺序(被依赖的 Module 应该先注册)",
|
|
408
|
+
"",
|
|
409
|
+
"**产出格式**:",
|
|
410
|
+
"```",
|
|
411
|
+
"parent_module_file: <path>",
|
|
412
|
+
"parent_class_name: AppModule | <Feature>Module",
|
|
413
|
+
"import_statement: <added import line>",
|
|
414
|
+
"registration_order_ok: true | false",
|
|
415
|
+
"```",
|
|
416
|
+
].join("\n"),
|
|
417
|
+
suggestedTools: ["Read", "Edit", "Bash"],
|
|
418
|
+
output: "父模块修改 diff、注册确认",
|
|
419
|
+
depends: ["create-module"],
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
id: "create-tests",
|
|
423
|
+
title: "生成单元测试和 E2E 测试",
|
|
424
|
+
instruction: [
|
|
425
|
+
"## 生成单元测试和 E2E 测试",
|
|
426
|
+
"",
|
|
427
|
+
"为新 Module 的 Service 和 Controller 编写完整的测试用例。",
|
|
428
|
+
"",
|
|
429
|
+
"**执行动作**:",
|
|
430
|
+
"1. 创建 Service 单元测试文件 `src/modules/<name>/<name>.service.spec.ts`:",
|
|
431
|
+
" ```typescript",
|
|
432
|
+
" import { Test, TestingModule } from '@nestjs/testing';",
|
|
433
|
+
" import { NotFoundException, ConflictException } from '@nestjs/common';",
|
|
434
|
+
" import { XxxService } from './xxx.service';",
|
|
435
|
+
" import { PrismaService } from '../prisma/prisma.service';",
|
|
436
|
+
"",
|
|
437
|
+
" const mockPrisma = {",
|
|
438
|
+
" xxx: {",
|
|
439
|
+
" create: jest.fn(),",
|
|
440
|
+
" findMany: jest.fn(),",
|
|
441
|
+
" findUnique: jest.fn(),",
|
|
442
|
+
" update: jest.fn(),",
|
|
443
|
+
" delete: jest.fn(),",
|
|
444
|
+
" },",
|
|
445
|
+
" };",
|
|
446
|
+
"",
|
|
447
|
+
" describe('XxxService', () => {",
|
|
448
|
+
" let service: XxxService;",
|
|
449
|
+
"",
|
|
450
|
+
" beforeEach(async () => {",
|
|
451
|
+
" const module: TestingModule = await Test.createTestingModule({",
|
|
452
|
+
" providers: [",
|
|
453
|
+
" XxxService,",
|
|
454
|
+
" { provide: PrismaService, useValue: mockPrisma },",
|
|
455
|
+
" ],",
|
|
456
|
+
" }).compile();",
|
|
457
|
+
" service = module.get<XxxService>(XxxService);",
|
|
458
|
+
" });",
|
|
459
|
+
"",
|
|
460
|
+
" afterEach(() => jest.clearAllMocks());",
|
|
461
|
+
"",
|
|
462
|
+
" it('should create a record', async () => {",
|
|
463
|
+
" mockPrisma.xxx.create.mockResolvedValue({ id: '1', ... });",
|
|
464
|
+
" const result = await service.create({ ... });",
|
|
465
|
+
" expect(result).toEqual({ id: '1', ... });",
|
|
466
|
+
" });",
|
|
467
|
+
"",
|
|
468
|
+
" it('should throw NotFoundException when not found', async () => {",
|
|
469
|
+
" mockPrisma.xxx.findUnique.mockResolvedValue(null);",
|
|
470
|
+
" await expect(service.findOne('999')).rejects.toThrow(NotFoundException);",
|
|
471
|
+
" });",
|
|
472
|
+
" });",
|
|
473
|
+
" ```",
|
|
474
|
+
"2. 创建 Controller 单元测试文件 `src/modules/<name>/<name>.controller.spec.ts`:",
|
|
475
|
+
" ```typescript",
|
|
476
|
+
" describe('XxxController', () => {",
|
|
477
|
+
" // 使用 TestingModule 创建 controller",
|
|
478
|
+
" // mock XxxService 的所有方法",
|
|
479
|
+
" // 测试每个端点的返回和异常处理",
|
|
480
|
+
" });",
|
|
481
|
+
" ```",
|
|
482
|
+
"3. (可选)创建 E2E 测试 `test/<name>.e2e-spec.ts`:",
|
|
483
|
+
" ```typescript",
|
|
484
|
+
" import { INestApplication } from '@nestjs/common';",
|
|
485
|
+
" import * as request from 'supertest';",
|
|
486
|
+
" describe('Xxx (e2e)', () => {",
|
|
487
|
+
" let app: INestApplication;",
|
|
488
|
+
" beforeAll(async () => {",
|
|
489
|
+
" const moduleFixture = await Test.createTestingModule({ imports: [AppModule] }).compile();",
|
|
490
|
+
" app = moduleFixture.createNestApplication();",
|
|
491
|
+
" await app.init();",
|
|
492
|
+
" });",
|
|
493
|
+
" it('GET /xxx', () => request(app.getHttpServer()).get('/xxx').expect(200));",
|
|
494
|
+
" });",
|
|
495
|
+
" ```",
|
|
496
|
+
"4. Service 测试使用 `Test.createTestingModule` + 手动 mock Provider",
|
|
497
|
+
"5. Controller 测试 mock Service 的方法返回值",
|
|
498
|
+
"6. 测试覆盖所有分支:成功路径 + 所有异常分支(NotFoundException、ConflictException 等)",
|
|
499
|
+
"7. 使用 `jest.clearAllMocks()` 避免测试间污染",
|
|
500
|
+
"",
|
|
501
|
+
"**产出格式**:",
|
|
502
|
+
"```",
|
|
503
|
+
"test_files_created: [{ path, test_count: N }]",
|
|
504
|
+
"test_cases: [{ describe, it, status: 'written' }]",
|
|
505
|
+
"coverage_target: { statements: '>80%', branches: '>75%' }",
|
|
506
|
+
"```",
|
|
507
|
+
].join("\n"),
|
|
508
|
+
suggestedTools: ["Bash", "Read", "Write"],
|
|
509
|
+
output: "单元测试和 E2E 测试文件、测试用例清单",
|
|
510
|
+
depends: ["create-controller", "create-service"],
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
id: "run-tests",
|
|
514
|
+
stepTemplate: "run-tests",
|
|
515
|
+
depends: ["create-tests"],
|
|
516
|
+
},
|
|
517
|
+
],
|
|
518
|
+
triggers: [{ type: "cli", command: "new:module" }, { type: "mcp", tool: "new_module" }],
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
name: "bug-diagnosis",
|
|
522
|
+
description: "NestJS Bug 诊断:异常定位 → 依赖链追踪 → 修复",
|
|
523
|
+
version: "2.0.0",
|
|
524
|
+
steps: [
|
|
525
|
+
{
|
|
526
|
+
id: "capture-exception",
|
|
527
|
+
title: "收集异常信息",
|
|
528
|
+
instruction: [
|
|
529
|
+
"## 收集 NestJS 异常信息",
|
|
530
|
+
"",
|
|
531
|
+
"完整记录 NestJS 运行时异常的所有上下文,这是后续诊断的基础。",
|
|
532
|
+
"",
|
|
533
|
+
"**执行动作**:",
|
|
534
|
+
"1. 复制完整的异常堆栈(包括 `message`、`stack`、`name`)",
|
|
535
|
+
"2. 记录 HTTP 上下文:",
|
|
536
|
+
" - 请求方法(GET / POST / PUT / DELETE)",
|
|
537
|
+
" - 请求路径(如 `/api/users/:id`)",
|
|
538
|
+
" - HTTP 状态码(如 500、400、404、403)",
|
|
539
|
+
" - 请求体参数(如有)",
|
|
540
|
+
"3. 识别异常类型:",
|
|
541
|
+
" - `HttpException` 子类(BadRequestException、NotFoundException 等)",
|
|
542
|
+
" - `Prisma` 异常(P2002 唯一约束、P2025 记录不存在等)",
|
|
543
|
+
" - `NestJS DI` 异常(Provider not found、Circular dependency)",
|
|
544
|
+
" - 原生 JS 异常(TypeError、ReferenceError 等)",
|
|
545
|
+
"4. 查看 NestJS 日志输出(`Logger` 格式),标记关联的 `traceId` 或 `requestId`",
|
|
546
|
+
"5. 如果是 GraphQL 端点,记录 operation name 和 variables",
|
|
547
|
+
"",
|
|
548
|
+
"**产出格式**:",
|
|
549
|
+
"```",
|
|
550
|
+
"exception_type: NotFoundException | PrismaClientKnownRequestError | ...",
|
|
551
|
+
"exception_message: <完整的异常消息>",
|
|
552
|
+
"stack_trace: <关键 3 帧>",
|
|
553
|
+
"http_method: GET",
|
|
554
|
+
"http_path: /api/users/123",
|
|
555
|
+
"http_status: 404",
|
|
556
|
+
"request_body: {}",
|
|
557
|
+
"trace_id: abc-123",
|
|
558
|
+
"```",
|
|
559
|
+
].join("\n"),
|
|
560
|
+
suggestedTools: ["Read", "Bash"],
|
|
561
|
+
output: "结构化的异常信息清单(exception_type, http_status, stack_trace)",
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
id: "trace-module-chain",
|
|
565
|
+
title: "追踪请求链路",
|
|
566
|
+
instruction: [
|
|
567
|
+
"## 追踪 Module/Guard/Interceptor/Pipe 链路",
|
|
568
|
+
"",
|
|
569
|
+
"NestJS 请求经过一条完整的执行链,逐层排查定位异常发生的准确位置。",
|
|
570
|
+
"",
|
|
571
|
+
"**NestJS 执行链顺序**:",
|
|
572
|
+
"```",
|
|
573
|
+
"Client → Middleware → Guard → Interceptor(before) → Pipe",
|
|
574
|
+
" → Controller → Service → Interceptor(after) → Filter",
|
|
575
|
+
"```",
|
|
576
|
+
"",
|
|
577
|
+
"**执行动作**:",
|
|
578
|
+
"1. 从路由路径找到对应的 Controller 方法:",
|
|
579
|
+
" ```bash",
|
|
580
|
+
" grep -rn \"@Get\\|@Post\\|@Put\\|@Delete\\|@Patch\" src/ --include=\"*.controller.ts\"",
|
|
581
|
+
" ```",
|
|
582
|
+
"2. 追踪该 Controller 所在 Module 的 imports 链(从 AppModule 逐层展开)",
|
|
583
|
+
"3. 检查链路上的 Guard(`@UseGuards`):",
|
|
584
|
+
" - 是否有认证/授权逻辑可能抛出 401/403",
|
|
585
|
+
" - Guard 的 `canActivate()` 是否正确处理了边界情况",
|
|
586
|
+
"4. 检查 Interceptor(`@UseInterceptors`):",
|
|
587
|
+
" - 是否在 Interceptor 中修改了请求/响应数据",
|
|
588
|
+
" - 是否有异步操作未正确处理",
|
|
589
|
+
"5. 检查 Pipe(`@UsePipes` 或全局 ValidationPipe):",
|
|
590
|
+
" - DTO 验证规则是否与请求数据匹配",
|
|
591
|
+
" - 自定义 Pipe 的 `transform()` 是否有异常",
|
|
592
|
+
"6. 检查 Exception Filter(`@Catch`):",
|
|
593
|
+
" - 是否有 Filter 吞掉了异常或转换了异常类型",
|
|
594
|
+
"",
|
|
595
|
+
"**产出格式**:",
|
|
596
|
+
"```",
|
|
597
|
+
"controller: src/modules/users/users.controller.ts",
|
|
598
|
+
"handler: findOne(@Param('id') id: string)",
|
|
599
|
+
"module_chain: AppModule → UsersModule",
|
|
600
|
+
"guards: [JwtAuthGuard, RolesGuard]",
|
|
601
|
+
"interceptors: [TransformInterceptor]",
|
|
602
|
+
"pipes: [ValidationPipe, ParseIntPipe]",
|
|
603
|
+
"exception_layer: Service (UsersService.findOne)",
|
|
604
|
+
"```",
|
|
605
|
+
].join("\n"),
|
|
606
|
+
suggestedTools: ["Read", "Bash"],
|
|
607
|
+
output: "请求经过的完整链路、异常发生的准确层级",
|
|
608
|
+
depends: ["capture-exception"],
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
id: "check-di",
|
|
612
|
+
title: "检查依赖注入",
|
|
613
|
+
instruction: [
|
|
614
|
+
"## 检查依赖注入链",
|
|
615
|
+
"",
|
|
616
|
+
"NestJS 的依赖注入是运行时错误的常见来源。验证所有 Provider 是否正确注册。",
|
|
617
|
+
"",
|
|
618
|
+
"**常见 DI 异常**:",
|
|
619
|
+
"- `Nest can't resolve dependencies of <X>` — Provider 未在 Module 中注册",
|
|
620
|
+
"- `Circular dependency detected` — 循环依赖",
|
|
621
|
+
"- `No provider for <X>` — 缺少 Provider",
|
|
622
|
+
"",
|
|
623
|
+
"**执行动作**:",
|
|
624
|
+
"1. 找到异常中提到的 Module / Provider:",
|
|
625
|
+
" ```bash",
|
|
626
|
+
" grep -rn \"@Module\\|@Injectable\" src/ --include=\"*.ts\"",
|
|
627
|
+
" ```",
|
|
628
|
+
"2. 检查 Module 的 `providers` 数组是否包含所有需要的 Service / Provider",
|
|
629
|
+
"3. 检查 Module 的 `imports` 数组是否导入了依赖的其他 Module",
|
|
630
|
+
"4. 检查 Module 的 `exports` 是否导出了需要被其他 Module 使用的 Provider",
|
|
631
|
+
"5. 检查构造函数注入的参数顺序和类型是否与 `@Inject()` 装饰器匹配",
|
|
632
|
+
"6. 如果是自定义 Provider(`useFactory` / `useValue`),检查 `provide` token 是否一致",
|
|
633
|
+
"7. 排查循环依赖:",
|
|
634
|
+
" - 使用 `forwardRef(() => ModuleName)` 临时解决",
|
|
635
|
+
" - 考虑重构提取共享逻辑到独立 Module",
|
|
636
|
+
"",
|
|
637
|
+
"**产出格式**:",
|
|
638
|
+
"```",
|
|
639
|
+
"module_file: src/modules/users/users.module.ts",
|
|
640
|
+
"providers: [UsersService, PrismaService]",
|
|
641
|
+
"imports: [AuthModule, DatabaseModule]",
|
|
642
|
+
"di_issue: PrismaService 未在 UsersModule 的 providers 或 imports 中声明",
|
|
643
|
+
"fix_suggestion: 在 UsersModule 的 imports 中添加 DatabaseModule",
|
|
644
|
+
"```",
|
|
645
|
+
].join("\n"),
|
|
646
|
+
suggestedTools: ["Read", "Bash"],
|
|
647
|
+
output: "DI 注册状态、缺失的 Provider、循环依赖链",
|
|
648
|
+
depends: ["trace-module-chain"],
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
id: "check-db",
|
|
652
|
+
title: "检查数据库查询",
|
|
653
|
+
instruction: [
|
|
654
|
+
"## 检查数据库查询和事务",
|
|
655
|
+
"",
|
|
656
|
+
"NestJS 项目中常用 Prisma 作为 ORM。排查数据库查询和事务是否正确。",
|
|
657
|
+
"",
|
|
658
|
+
"**执行动作**:",
|
|
659
|
+
"1. 找到 Service 中与异常相关的 Prisma 查询:",
|
|
660
|
+
" ```bash",
|
|
661
|
+
" grep -rn \"prisma\\.\\|this\\.prisma\\.\" src/ --include=\"*.service.ts\"",
|
|
662
|
+
" ```",
|
|
663
|
+
"2. 检查查询条件是否正确:",
|
|
664
|
+
" - `where` 条件是否匹配了 Prisma Schema 中的字段名(注意大小写)",
|
|
665
|
+
" - `include` / `select` 的关系名是否正确",
|
|
666
|
+
" - 分页参数(`skip` / `take`)是否合理",
|
|
667
|
+
"3. 检查异常处理:",
|
|
668
|
+
" - Prisma 已知异常(P2002、P2025)是否有 `catch` 处理",
|
|
669
|
+
" - 是否将 Prisma 异常转换为了合适的 `HttpException`",
|
|
670
|
+
"4. 检查事务使用:",
|
|
671
|
+
" - `prisma.$transaction([ ... ])` 中的操作顺序是否正确",
|
|
672
|
+
" - 交互式事务 `prisma.$transaction(async (tx) => { ... })` 是否有死锁风险",
|
|
673
|
+
"5. 检查 Schema 一致性:",
|
|
674
|
+
" - 代码中使用的字段是否与 `schema.prisma` 定义一致",
|
|
675
|
+
" - 是否需要运行 `npx prisma generate` 重新生成类型",
|
|
676
|
+
"6. 如果是数据不一致问题,检查是否有并发写入缺少锁机制",
|
|
677
|
+
"",
|
|
678
|
+
"**产出格式**:",
|
|
679
|
+
"```",
|
|
680
|
+
"query_location: src/modules/users/users.service.ts:42",
|
|
681
|
+
"prisma_operation: prisma.user.findUnique()",
|
|
682
|
+
"where_clause: { id: \"123\" }",
|
|
683
|
+
"db_issue: 查询使用了错误的字段名 'userId' 应为 'id'",
|
|
684
|
+
"prisma_error: P2025 (Record not found)",
|
|
685
|
+
"fix_suggestion: 更正 where 条件中的字段名",
|
|
686
|
+
"```",
|
|
687
|
+
].join("\n"),
|
|
688
|
+
suggestedTools: ["Read", "Bash"],
|
|
689
|
+
output: "有问题的 Prisma 查询、Schema 不一致点、事务问题",
|
|
690
|
+
depends: ["trace-module-chain"],
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
id: "search-knowledge",
|
|
694
|
+
title: "检索 NestJS 知识库",
|
|
695
|
+
instruction: [
|
|
696
|
+
"## 检索 NestJS 知识库",
|
|
697
|
+
"",
|
|
698
|
+
"查阅 NestJS 官方文档和项目知识库,寻找类似问题的最佳实践。",
|
|
699
|
+
"",
|
|
700
|
+
"**执行动作**:",
|
|
701
|
+
"1. 根据异常类型提取关键词(如 `guard 403`、`circular dependency`、`ValidationPipe`、`Prisma P2002`)",
|
|
702
|
+
"2. 调用 `devflow_knowledge_query` 查询项目知识库中的相关条目",
|
|
703
|
+
"3. 调用 `devflow_knowledge_search` 在 NestJS 官方文档中搜索(使用 `scope: \"nest\"`):",
|
|
704
|
+
" - 异常涉及的模块机制(Module / Provider / Guard / Interceptor / Pipe / Filter)",
|
|
705
|
+
" - 相关模式(依赖注入、自定义 Provider、动态 Module)",
|
|
706
|
+
" - 常见陷阱(循环依赖、Provider 作用域、生命周期钩子)",
|
|
707
|
+
"4. 检查项目 `memory` 中是否有类似 Bug 的历史修复记录",
|
|
708
|
+
"5. 整理出 2-3 条最相关的参考建议",
|
|
709
|
+
"",
|
|
710
|
+
"**产出格式**:",
|
|
711
|
+
"```",
|
|
712
|
+
"knowledge_hits: [",
|
|
713
|
+
" { source: nestjs-docs, title: 'Circular dependency', url: '...', summary: '...' },",
|
|
714
|
+
" { source: project-memory, title: '历史类似问题', fix: '...' }",
|
|
715
|
+
"]",
|
|
716
|
+
"recommended_pattern: 使用 forwardRef 解决循环依赖,或重构提取共享 Module",
|
|
717
|
+
"scope: nest",
|
|
718
|
+
"```",
|
|
719
|
+
].join("\n"),
|
|
720
|
+
suggestedTools: ["Bash", "Read"],
|
|
721
|
+
output: "相关文档条目、推荐修复模式、历史类似修复",
|
|
722
|
+
depends: ["capture-exception"],
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
id: "generate-fix",
|
|
726
|
+
title: "生成修复方案",
|
|
727
|
+
instruction: [
|
|
728
|
+
"## 生成修复方案",
|
|
729
|
+
"",
|
|
730
|
+
"综合所有诊断信息,生成最小化、精准的修复代码。",
|
|
731
|
+
"",
|
|
732
|
+
"**执行动作**:",
|
|
733
|
+
"1. 综合以下输入生成修复方案:",
|
|
734
|
+
" - `trace-module-chain` 的异常发生层级",
|
|
735
|
+
" - `check-di` 的依赖注入状态",
|
|
736
|
+
" - `check-db` 的数据库查询问题",
|
|
737
|
+
" - `search-knowledge` 的最佳实践",
|
|
738
|
+
"2. 使用 Edit 工具精准修改代码(不要整文件重写)",
|
|
739
|
+
"3. 修复原则:",
|
|
740
|
+
" - **最小改动**:只改必须改的代码,不做附带重构",
|
|
741
|
+
" - **类型安全**:修复后不能有 `any` 或 `@ts-ignore`",
|
|
742
|
+
" - **Module 契约**:确保 imports/exports/providers 完整一致",
|
|
743
|
+
" - **向后兼容**:不改变下游消费者的行为契约",
|
|
744
|
+
"4. 遵循 NestJS 约定:",
|
|
745
|
+
" - 使用 `@Injectable()` 标记所有 Provider",
|
|
746
|
+
" - 使用构造函数注入而非 `@Inject()` 手动指定(除非接口 token)",
|
|
747
|
+
" - 异常统一使用 `HttpException` 子类",
|
|
748
|
+
" - DTO 验证使用 `class-validator` 装饰器",
|
|
749
|
+
"5. 如果涉及多文件修改,按依赖顺序依次修改",
|
|
750
|
+
"",
|
|
751
|
+
"**产出格式**:",
|
|
752
|
+
"```",
|
|
753
|
+
"fix_summary: 在 UsersModule 中添加 DatabaseModule 的 import 以解决 PrismaService 注入问题",
|
|
754
|
+
"files_changed: [",
|
|
755
|
+
" { path: src/modules/users/users.module.ts, changes: '在 imports 中添加 DatabaseModule' }",
|
|
756
|
+
"]",
|
|
757
|
+
"diff_preview: - imports: [AuthModule]",
|
|
758
|
+
" + imports: [AuthModule, DatabaseModule]",
|
|
759
|
+
"```",
|
|
760
|
+
].join("\n"),
|
|
761
|
+
suggestedTools: ["Read", "Edit"],
|
|
762
|
+
output: "修复代码 diff、修改文件列表、修复说明",
|
|
763
|
+
depends: ["check-di", "check-db", "search-knowledge"],
|
|
764
|
+
},
|
|
765
|
+
{
|
|
766
|
+
id: "type-check",
|
|
767
|
+
title: "运行类型检查",
|
|
768
|
+
instruction: [
|
|
769
|
+
"## 运行 NestJS 构建检查",
|
|
770
|
+
"",
|
|
771
|
+
"验证修复后的代码能通过 TypeScript 编译和 NestJS 构建。",
|
|
772
|
+
"",
|
|
773
|
+
"**执行动作**:",
|
|
774
|
+
"1. 运行 NestJS 构建命令(包含 TypeScript 类型检查):",
|
|
775
|
+
" ```bash",
|
|
776
|
+
" npm run build",
|
|
777
|
+
" # 或等价的: npx nest build",
|
|
778
|
+
" ```",
|
|
779
|
+
"2. 如果项目有特定的 tsconfig(如 `tsconfig.build.json`),构建命令会自动使用",
|
|
780
|
+
"3. 分析输出:",
|
|
781
|
+
" - **构建成功**:✅ 进入测试阶段",
|
|
782
|
+
" - **有编译错误**:记录错误位置,返回 `generate-fix` 步骤重新修复",
|
|
783
|
+
"4. 常见编译错误排查:",
|
|
784
|
+
" - 装饰器参数类型不匹配 → 检查 `@Body()` / `@Param()` 的类型注解",
|
|
785
|
+
" - 模块导入错误 → 检查 `@Module()` 装饰器中的路径",
|
|
786
|
+
" - 泛型类型推断失败 → 为 Prisma 查询显式指定泛型参数",
|
|
787
|
+
"5. 不要使用 `@ts-ignore` 或 `as any` 绕过类型错误",
|
|
788
|
+
"",
|
|
789
|
+
"**产出格式**:",
|
|
790
|
+
"```",
|
|
791
|
+
"build_result: passed | failed",
|
|
792
|
+
"errors_count: 0",
|
|
793
|
+
"errors: []",
|
|
794
|
+
"retry_needed: false",
|
|
795
|
+
"```",
|
|
796
|
+
].join("\n"),
|
|
797
|
+
suggestedTools: ["Bash"],
|
|
798
|
+
output: "构建检查结果(passed/failed)、错误清单",
|
|
799
|
+
depends: ["generate-fix"],
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
id: "run-tests",
|
|
803
|
+
title: "运行 Jest 测试",
|
|
804
|
+
instruction: [
|
|
805
|
+
"## 运行 Jest 测试验证修复",
|
|
806
|
+
"",
|
|
807
|
+
"执行 Jest 测试套件确认修复有效且未引入回归。",
|
|
808
|
+
"",
|
|
809
|
+
"**执行动作**:",
|
|
810
|
+
"1. 优先运行与修改文件直接相关的测试:",
|
|
811
|
+
" ```bash",
|
|
812
|
+
" npx jest --testPathPattern=users",
|
|
813
|
+
" # 或指定具体测试文件:",
|
|
814
|
+
" npx jest src/modules/users/users.service.spec.ts",
|
|
815
|
+
" ```",
|
|
816
|
+
"2. 如果项目有 `test:watch` 配置,先单次运行确认结果:",
|
|
817
|
+
" ```bash",
|
|
818
|
+
" npx jest --runInBand",
|
|
819
|
+
" ```",
|
|
820
|
+
"3. 相关测试通过后,运行完整测试套件确认无回归:",
|
|
821
|
+
" ```bash",
|
|
822
|
+
" npm run test",
|
|
823
|
+
" # 或: npx jest",
|
|
824
|
+
" ```",
|
|
825
|
+
"4. 如果有 E2E 测试,也需要运行:",
|
|
826
|
+
" ```bash",
|
|
827
|
+
" npm run test:e2e",
|
|
828
|
+
" # 或: npx jest --config ./test/jest-e2e.json",
|
|
829
|
+
" ```",
|
|
830
|
+
"5. 分析测试结果:",
|
|
831
|
+
" - **全部通过**:✅ 进入人工审查",
|
|
832
|
+
" - **有失败**:区分是修复不彻底还是引入了新问题",
|
|
833
|
+
"6. 如果测试失败且与修复相关,回到 `generate-fix` 步骤调整方案",
|
|
834
|
+
"",
|
|
835
|
+
"**产出格式**:",
|
|
836
|
+
"```",
|
|
837
|
+
"test_result: passed | failed",
|
|
838
|
+
"tests_run: 28",
|
|
839
|
+
"tests_passed: 28",
|
|
840
|
+
"tests_failed: 0",
|
|
841
|
+
"failed_tests: []",
|
|
842
|
+
"coverage_delta: +1.5%",
|
|
843
|
+
"```",
|
|
844
|
+
].join("\n"),
|
|
845
|
+
suggestedTools: ["Bash"],
|
|
846
|
+
output: "Jest 测试结果(passed/failed)、覆盖率变化、失败用例详情",
|
|
847
|
+
depends: ["type-check"],
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
id: "human-review",
|
|
851
|
+
title: "人工审查确认",
|
|
852
|
+
instruction: [
|
|
853
|
+
"## 人工审查修复方案",
|
|
854
|
+
"",
|
|
855
|
+
"将所有诊断结果和修复方案呈现给用户,等待人工确认。",
|
|
856
|
+
"",
|
|
857
|
+
"**呈现给用户的摘要**:",
|
|
858
|
+
"",
|
|
859
|
+
"### 🐛 NestJS Bug 诊断报告",
|
|
860
|
+
"",
|
|
861
|
+
"**异常**:<一句话描述异常,如 `GET /api/users/123 返回 500`>",
|
|
862
|
+
"**异常类型**:<NotFoundException / PrismaClientKnownRequestError / ...>",
|
|
863
|
+
"**根因**:<引入原因,如 `UsersModule 未导入 DatabaseModule 导致 PrismaService 无法注入`>",
|
|
864
|
+
"**影响范围**:<受影响的 API 端点 / Module>",
|
|
865
|
+
"",
|
|
866
|
+
"### 🔧 修复方案",
|
|
867
|
+
"",
|
|
868
|
+
"**修改文件**:",
|
|
869
|
+
"- `src/modules/users/users.module.ts` — 在 imports 中添加 DatabaseModule",
|
|
870
|
+
"",
|
|
871
|
+
"**关键 diff**:",
|
|
872
|
+
"```diff",
|
|
873
|
+
"- imports: [AuthModule],",
|
|
874
|
+
"+ imports: [AuthModule, DatabaseModule],",
|
|
875
|
+
"```",
|
|
876
|
+
"",
|
|
877
|
+
"### ✅ 验证结果",
|
|
878
|
+
"",
|
|
879
|
+
"- NestJS 构建(`npm run build`):✅ 通过",
|
|
880
|
+
"- Jest 单元测试(28 个):✅ 全部通过",
|
|
881
|
+
"- E2E 测试:✅ 无回归",
|
|
882
|
+
"",
|
|
883
|
+
"**等待用户操作**:",
|
|
884
|
+
"- 审查修复代码是否符合业务预期",
|
|
885
|
+
"- 确认后执行 `git commit` 和 `git push`",
|
|
886
|
+
"- 如需创建 PR,使用 `gh pr create` 命令",
|
|
887
|
+
"",
|
|
888
|
+
"**注意**:此步骤必须等待用户明确确认后才能继续,不可自动跳过。",
|
|
889
|
+
].join("\n"),
|
|
890
|
+
suggestedTools: ["Read"],
|
|
891
|
+
output: "用户确认结果(approved / rejected / needs-changes)",
|
|
892
|
+
depends: ["run-tests"],
|
|
893
|
+
requiresUserConfirmation: true,
|
|
894
|
+
},
|
|
895
|
+
],
|
|
896
|
+
triggers: [{ type: "cli", command: "bug" }, { type: "mcp", tool: "diagnose_bug" }],
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
name: "swagger-generate",
|
|
900
|
+
description: "Swagger 文档生成:扫描 API → 补充装饰器 → 验证",
|
|
901
|
+
version: "2.0.0",
|
|
902
|
+
steps: [
|
|
903
|
+
{
|
|
904
|
+
id: "scan-controllers",
|
|
905
|
+
title: "扫描所有 Controller 和 DTO",
|
|
906
|
+
instruction: [
|
|
907
|
+
"## 扫描所有 Controller 和 DTO",
|
|
908
|
+
"",
|
|
909
|
+
"全面盘点项目中所有 NestJS Controller 和 DTO 文件,建立 API 清单。",
|
|
910
|
+
"",
|
|
911
|
+
"**执行动作**:",
|
|
912
|
+
"1. 找出所有 Controller 文件:",
|
|
913
|
+
" ```bash",
|
|
914
|
+
" find src/ -name '*.controller.ts' -type f",
|
|
915
|
+
" ```",
|
|
916
|
+
"2. 扫描每个 Controller 的路由定义:",
|
|
917
|
+
" ```bash",
|
|
918
|
+
" grep -rn '@Controller\\|@Get\\|@Post\\|@Put\\|@Delete\\|@Patch' src/ --include='*.controller.ts'",
|
|
919
|
+
" ```",
|
|
920
|
+
"3. 找出所有 DTO 文件:",
|
|
921
|
+
" ```bash",
|
|
922
|
+
" find src/ -path '*/dto/*' -name '*.ts' -type f",
|
|
923
|
+
" ```",
|
|
924
|
+
"4. 提取每个 Controller 的 `@Controller(<prefix>)` 前缀",
|
|
925
|
+
"5. 统计每个 Controller 包含的端点数量和 HTTP 方法分布",
|
|
926
|
+
"6. 记录每个端点使用的 DTO 类型(`@Body()`、`@Query()`、`@Param()` 参数类型)",
|
|
927
|
+
"7. 检查是否已经配置了 `SwaggerModule`:",
|
|
928
|
+
" ```bash",
|
|
929
|
+
" grep -rn 'SwaggerModule\\|DocumentBuilder' src/ --include='*.ts'",
|
|
930
|
+
" ```",
|
|
931
|
+
"",
|
|
932
|
+
"**产出格式**:",
|
|
933
|
+
"```",
|
|
934
|
+
"controllers: [",
|
|
935
|
+
" { file: '...', class_name: '...', prefix: '...', endpoints: N }",
|
|
936
|
+
"]",
|
|
937
|
+
"total_endpoints: <N>",
|
|
938
|
+
"dto_files: [{ path, class_name }]",
|
|
939
|
+
"swagger_module_exists: true | false",
|
|
940
|
+
"swagger_module_path: <path> | null",
|
|
941
|
+
"```",
|
|
942
|
+
].join("\n"),
|
|
943
|
+
suggestedTools: ["Bash", "Read"],
|
|
944
|
+
output: "Controller 和 DTO 完整清单、现有 Swagger 配置状态",
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
id: "audit-decorators",
|
|
948
|
+
title: "审计缺失的 Swagger 装饰器",
|
|
949
|
+
instruction: [
|
|
950
|
+
"## 审计缺失的 @ApiTags / @ApiOperation / @ApiResponse 装饰器",
|
|
951
|
+
"",
|
|
952
|
+
"检查每个 Controller 和 DTO 是否具备完整的 Swagger 文档装饰器。",
|
|
953
|
+
"",
|
|
954
|
+
"**Controller 必备装饰器清单**:",
|
|
955
|
+
"- `@ApiTags('<tag-name>')` — 在 Controller 类级别",
|
|
956
|
+
"- `@ApiBearerAuth()` — 如果 Controller 受 JwtAuthGuard 保护",
|
|
957
|
+
"- `@ApiOperation({ summary: '...' })` — 每个 handler 方法",
|
|
958
|
+
"- `@ApiResponse({ status: 200, description: '...', type: XxxDto })` — 每个 handler",
|
|
959
|
+
"- `@ApiParam({ name: 'id', description: '...' })` — 有 URL 参数的 handler",
|
|
960
|
+
"- `@ApiQuery({ name: 'page', description: '...' })` — 有查询参数的 handler(如 DTO 未标注则需手动补)",
|
|
961
|
+
"",
|
|
962
|
+
"**DTO 必备装饰器清单**:",
|
|
963
|
+
"- 每个字段必须有 `@ApiProperty()` 或 `@ApiPropertyOptional()`",
|
|
964
|
+
"- `@ApiProperty({ example: '...', description: '...' })` 提供示例",
|
|
965
|
+
"- 枚举类型使用 `@ApiProperty({ enum: XxxEnum })`",
|
|
966
|
+
"- 嵌套 DTO 使用 `@ApiProperty({ type: () => NestedDto, isArray: true })`",
|
|
967
|
+
"",
|
|
968
|
+
"**执行动作**:",
|
|
969
|
+
"1. 对每个 Controller 文件,逐方法检查:",
|
|
970
|
+
" ```bash",
|
|
971
|
+
" grep -n '@ApiOperation\\|@Get\\|@Post' src/modules/users/users.controller.ts",
|
|
972
|
+
" ```",
|
|
973
|
+
"2. 对比 `@Get/@Post/...` 数量和 `@ApiOperation` 数量,差集即为缺失项",
|
|
974
|
+
"3. 对每个 DTO 文件,检查每个 class 字段是否有 `@ApiProperty`:",
|
|
975
|
+
" ```bash",
|
|
976
|
+
" grep -c '@ApiProperty\\|@ApiPropertyOptional' src/modules/users/dto/create-user.dto.ts",
|
|
977
|
+
" grep -c '^\\s*[a-zA-Z]' src/modules/users/dto/create-user.dto.ts",
|
|
978
|
+
" ```",
|
|
979
|
+
"4. 生成缺失项清单,按优先级排序:",
|
|
980
|
+
" - P0:无 `@ApiTags` 的 Controller",
|
|
981
|
+
" - P1:无 `@ApiOperation` 的 handler",
|
|
982
|
+
" - P2:无 `@ApiProperty` 的 DTO 字段",
|
|
983
|
+
" - P3:缺少 `@ApiResponse` 的 handler",
|
|
984
|
+
"",
|
|
985
|
+
"**产出格式**:",
|
|
986
|
+
"```",
|
|
987
|
+
"audit_summary: {",
|
|
988
|
+
" controllers_without_tags: [<file>],",
|
|
989
|
+
" handlers_without_operation: [{ controller, method, route }],",
|
|
990
|
+
" dto_fields_without_api_property: [{ file, field }],",
|
|
991
|
+
" handlers_without_response: [{ controller, method }],",
|
|
992
|
+
"}",
|
|
993
|
+
"priority_order: [P0, P1, P2, P3]",
|
|
994
|
+
"```",
|
|
995
|
+
].join("\n"),
|
|
996
|
+
suggestedTools: ["Bash", "Read"],
|
|
997
|
+
output: "缺失 Swagger 装饰器的详细清单、优先级排序",
|
|
998
|
+
depends: ["scan-controllers"],
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
id: "supplement-docs",
|
|
1002
|
+
title: "补充缺失的 Swagger 装饰器",
|
|
1003
|
+
instruction: [
|
|
1004
|
+
"## 补充缺失的 Swagger 装饰器",
|
|
1005
|
+
"",
|
|
1006
|
+
"按优先级顺序,为 Controller 和 DTO 补充缺失的 Swagger 装饰器。",
|
|
1007
|
+
"",
|
|
1008
|
+
"**执行动作**:",
|
|
1009
|
+
"1. 优先补充 `@ApiTags`(Controller 类级别):",
|
|
1010
|
+
" ```typescript",
|
|
1011
|
+
" import { ApiTags, ApiBearerAuth, ApiOperation, ApiResponse, ApiProperty } from '@nestjs/swagger';",
|
|
1012
|
+
"",
|
|
1013
|
+
" @ApiTags('users')",
|
|
1014
|
+
" @ApiBearerAuth()",
|
|
1015
|
+
" @Controller('users')",
|
|
1016
|
+
" export class UsersController {}",
|
|
1017
|
+
" ```",
|
|
1018
|
+
"2. 为每个 handler 补充 `@ApiOperation` + `@ApiResponse`:",
|
|
1019
|
+
" ```typescript",
|
|
1020
|
+
" @Get(':id')",
|
|
1021
|
+
" @ApiOperation({ summary: '根据 ID 查询用户' })",
|
|
1022
|
+
" @ApiResponse({ status: 200, description: '查询成功', type: UserResponseDto })",
|
|
1023
|
+
" @ApiResponse({ status: 404, description: '用户不存在' })",
|
|
1024
|
+
" findOne(@Param('id') id: string) {",
|
|
1025
|
+
" return this.usersService.findOne(id);",
|
|
1026
|
+
" }",
|
|
1027
|
+
" ```",
|
|
1028
|
+
"3. 为 DTO 每个字段补充 `@ApiProperty`:",
|
|
1029
|
+
" ```typescript",
|
|
1030
|
+
" export class CreateUserDto {",
|
|
1031
|
+
" @ApiProperty({ description: '用户邮箱', example: 'alice@example.com' })",
|
|
1032
|
+
" @IsEmail()",
|
|
1033
|
+
" email: string;",
|
|
1034
|
+
"",
|
|
1035
|
+
" @ApiPropertyOptional({ description: '用户头像 URL' })",
|
|
1036
|
+
" @IsOptional()",
|
|
1037
|
+
" @IsUrl()",
|
|
1038
|
+
" avatar?: string;",
|
|
1039
|
+
" }",
|
|
1040
|
+
" ```",
|
|
1041
|
+
"4. 枚举类型字段:",
|
|
1042
|
+
" ```typescript",
|
|
1043
|
+
" @ApiProperty({ enum: UserRole, example: UserRole.ADMIN })",
|
|
1044
|
+
" role: UserRole;",
|
|
1045
|
+
" ```",
|
|
1046
|
+
"5. 嵌套 DTO 字段:",
|
|
1047
|
+
" ```typescript",
|
|
1048
|
+
" @ApiProperty({ type: () => AddressDto })",
|
|
1049
|
+
" address: AddressDto;",
|
|
1050
|
+
"",
|
|
1051
|
+
" @ApiProperty({ type: () => TagDto, isArray: true })",
|
|
1052
|
+
" tags: TagDto[];",
|
|
1053
|
+
" ```",
|
|
1054
|
+
"6. 为全局 ExceptionFilter 注册的响应添加 `@ApiResponse` 文档",
|
|
1055
|
+
"7. 使用 `@ApiBody({ type: CreateXxxDto })` 显式声明请求体(如需要)",
|
|
1056
|
+
"",
|
|
1057
|
+
"**产出格式**:",
|
|
1058
|
+
"```",
|
|
1059
|
+
"files_modified: [{ path, decorators_added: N }]",
|
|
1060
|
+
"total_decorators_added: <N>",
|
|
1061
|
+
"coverage_before: <percent>",
|
|
1062
|
+
"coverage_after: <percent>",
|
|
1063
|
+
"```",
|
|
1064
|
+
].join("\n"),
|
|
1065
|
+
suggestedTools: ["Read", "Edit", "Bash"],
|
|
1066
|
+
output: "修改后的文件清单、新增装饰器数量、覆盖率提升",
|
|
1067
|
+
depends: ["audit-decorators"],
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
id: "generate-spec",
|
|
1071
|
+
title: "生成 OpenAPI 规范文件",
|
|
1072
|
+
instruction: [
|
|
1073
|
+
"## 生成 OpenAPI 规范文件",
|
|
1074
|
+
"",
|
|
1075
|
+
"使用 `@nestjs/swagger` 的 `SwaggerModule` 生成 OpenAPI 规范 JSON/YAML 文件。",
|
|
1076
|
+
"",
|
|
1077
|
+
"**执行动作**:",
|
|
1078
|
+
"1. 在 `src/main.ts` 中配置 SwaggerModule(如果尚未配置):",
|
|
1079
|
+
" ```typescript",
|
|
1080
|
+
" import { NestFactory } from '@nestjs/core';",
|
|
1081
|
+
" import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';",
|
|
1082
|
+
" import { AppModule } from './app.module';",
|
|
1083
|
+
"",
|
|
1084
|
+
" async function bootstrap() {",
|
|
1085
|
+
" const app = await NestFactory.create(AppModule);",
|
|
1086
|
+
"",
|
|
1087
|
+
" const config = new DocumentBuilder()",
|
|
1088
|
+
" .setTitle('API Documentation')",
|
|
1089
|
+
" .setDescription('The API description')",
|
|
1090
|
+
" .setVersion('1.0')",
|
|
1091
|
+
" .addBearerAuth()",
|
|
1092
|
+
" .addTag('users', '用户管理')",
|
|
1093
|
+
" .addTag('posts', '文章管理')",
|
|
1094
|
+
" .build();",
|
|
1095
|
+
" const document = SwaggerModule.createDocument(app, config);",
|
|
1096
|
+
" SwaggerModule.setup('api', app, document);",
|
|
1097
|
+
"",
|
|
1098
|
+
" await app.listen(3000);",
|
|
1099
|
+
" }",
|
|
1100
|
+
" bootstrap();",
|
|
1101
|
+
" ```",
|
|
1102
|
+
"2. 生成静态 OpenAPI 规范文件(用于离线查看或外部工具集成):",
|
|
1103
|
+
" ```bash",
|
|
1104
|
+
" # 启动服务后通过 /api-json 获取 JSON 规范文件",
|
|
1105
|
+
" curl http://localhost:3000/api-json > openapi.json",
|
|
1106
|
+
" # 或使用 @nestjs/swagger 的脚本直接生成",
|
|
1107
|
+
" ```",
|
|
1108
|
+
"3. 在 `DocumentBuilder` 中配置:",
|
|
1109
|
+
" - `setTitle()` — 项目名称",
|
|
1110
|
+
" - `setDescription()` — 项目描述",
|
|
1111
|
+
" - `setVersion()` — API 版本",
|
|
1112
|
+
" - `addBearerAuth()` — JWT 认证(如有)",
|
|
1113
|
+
" - `addTag()` — 分组标签描述",
|
|
1114
|
+
" - `addServer()` — 环境地址(dev/staging/prod)",
|
|
1115
|
+
"4. 如果项目使用多个 Module,使用 `SwaggerModule.createDocument(app, config, { include: [XxxModule] })` 分模块生成",
|
|
1116
|
+
"5. 保存生成的 `openapi.json` 到项目根目录或 `docs/` 目录",
|
|
1117
|
+
"",
|
|
1118
|
+
"**产出格式**:",
|
|
1119
|
+
"```",
|
|
1120
|
+
"spec_file: <path>/openapi.json",
|
|
1121
|
+
"spec_version: '3.0.0'",
|
|
1122
|
+
"paths_count: <N>",
|
|
1123
|
+
"tags: [<tag names>]",
|
|
1124
|
+
"schemas_count: <N>",
|
|
1125
|
+
"security_schemes: ['bearer'] | []",
|
|
1126
|
+
"```",
|
|
1127
|
+
].join("\n"),
|
|
1128
|
+
suggestedTools: ["Bash", "Read", "Write"],
|
|
1129
|
+
output: "生成的 OpenAPI 规范文件、路径/标签/Schema 统计",
|
|
1130
|
+
depends: ["supplement-docs"],
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
id: "validate-spec",
|
|
1134
|
+
title: "验证生成的 Swagger 文档完整性",
|
|
1135
|
+
instruction: [
|
|
1136
|
+
"## 验证生成的 Swagger 文档完整性",
|
|
1137
|
+
"",
|
|
1138
|
+
"校验生成的 OpenAPI 规范文件是否符合 OpenAPI 3.0 规范,并与实际端点一一对应。",
|
|
1139
|
+
"",
|
|
1140
|
+
"**执行动作**:",
|
|
1141
|
+
"1. 使用 OpenAPI 校验工具验证文件格式:",
|
|
1142
|
+
" ```bash",
|
|
1143
|
+
" # 使用 swagger-cli 校验",
|
|
1144
|
+
" npx @apidevtools/swagger-cli validate openapi.json",
|
|
1145
|
+
" # 或使用 swagger-parser",
|
|
1146
|
+
" npx swagger-cli bundle openapi.json --validate",
|
|
1147
|
+
" ```",
|
|
1148
|
+
"2. 核对所有 Controller 路由都已出现在 `paths` 中:",
|
|
1149
|
+
" ```bash",
|
|
1150
|
+
" # 从源码提取所有路由",
|
|
1151
|
+
" grep -rn '@Get\\|@Post\\|@Put\\|@Delete\\|@Patch' src/ --include='*.controller.ts'",
|
|
1152
|
+
" # 与 openapi.json 的 paths 字段对比",
|
|
1153
|
+
" jq '.paths | keys' openapi.json",
|
|
1154
|
+
" ```",
|
|
1155
|
+
"3. 验证所有 DTO 都已出现在 `components.schemas` 中:",
|
|
1156
|
+
" ```bash",
|
|
1157
|
+
" jq '.components.schemas | keys' openapi.json",
|
|
1158
|
+
" ```",
|
|
1159
|
+
"4. 验证 `@ApiResponse` 都生成了正确的状态码描述",
|
|
1160
|
+
"5. 验证认证配置:如果有 `@ApiBearerAuth()`,`components.securitySchemes` 必须包含对应定义",
|
|
1161
|
+
"6. 验证请求体 Schema:每个 `@Body()` 参数都映射到正确的 `$ref`",
|
|
1162
|
+
"7. 验证 URL 参数:每个 `:param` 都在 `parameters` 中声明",
|
|
1163
|
+
"8. 使用 Swagger UI 本地预览验证渲染效果:",
|
|
1164
|
+
" ```bash",
|
|
1165
|
+
" npx @redocly/cli preview-docs openapi.json",
|
|
1166
|
+
" ```",
|
|
1167
|
+
"",
|
|
1168
|
+
"**产出格式**:",
|
|
1169
|
+
"```",
|
|
1170
|
+
"validation_result: passed | failed",
|
|
1171
|
+
"spec_valid_openapi: true | false",
|
|
1172
|
+
"paths_match_source: true | false",
|
|
1173
|
+
"missing_paths: [<path>],",
|
|
1174
|
+
"missing_schemas: [<schema name>],",
|
|
1175
|
+
"security_schemes_ok: true | false",
|
|
1176
|
+
"issues: [{ location, description }]",
|
|
1177
|
+
"```",
|
|
1178
|
+
].join("\n"),
|
|
1179
|
+
suggestedTools: ["Bash", "Read"],
|
|
1180
|
+
output: "规范校验结果、缺失项、问题清单",
|
|
1181
|
+
depends: ["generate-spec"],
|
|
1182
|
+
},
|
|
1183
|
+
],
|
|
1184
|
+
triggers: [{ type: "cli", command: "generate:swagger" }],
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
name: "microservice-setup",
|
|
1188
|
+
description: "微服务端点配置:消息模式 → 传输层 → 客户端代理",
|
|
1189
|
+
version: "2.0.0",
|
|
1190
|
+
steps: [
|
|
1191
|
+
{
|
|
1192
|
+
id: "define-patterns",
|
|
1193
|
+
title: "定义 MessagePattern 和 EventPattern",
|
|
1194
|
+
instruction: [
|
|
1195
|
+
"## 定义 MessagePattern 和 EventPattern",
|
|
1196
|
+
"",
|
|
1197
|
+
"根据业务需求规划微服务之间的消息模式:请求-响应 vs 事件广播。",
|
|
1198
|
+
"",
|
|
1199
|
+
"**NestJS 消息模式**:",
|
|
1200
|
+
"- `@MessagePattern(<cmd>)` — 请求-响应,客户端等待结果",
|
|
1201
|
+
"- `@EventPattern(<event>)` — 事件广播,无响应",
|
|
1202
|
+
"",
|
|
1203
|
+
"**执行动作**:",
|
|
1204
|
+
"1. 梳理业务场景,识别哪些操作需要跨服务调用:",
|
|
1205
|
+
" - 用户下单后需要扣减库存 → `@MessagePattern({ cmd: 'deductInventory' })`",
|
|
1206
|
+
" - 订单创建后通知多个服务 → `@EventPattern('order.created')`",
|
|
1207
|
+
"2. 命名规范:",
|
|
1208
|
+
" - MessagePattern: `{ cmd: '<verb><Noun>' }` 或字符串 `'<service>.<action>'`",
|
|
1209
|
+
" - EventPattern: `'<domain>.<event>'`(如 `user.created`、`payment.completed`)",
|
|
1210
|
+
"3. 创建微服务 Controller 文件 `src/modules/<name>/<name>.controller.ts`:",
|
|
1211
|
+
" ```typescript",
|
|
1212
|
+
" import { Controller } from '@nestjs/common';",
|
|
1213
|
+
" import { MessagePattern, EventPattern, Payload } from '@nestjs/microservices';",
|
|
1214
|
+
"",
|
|
1215
|
+
" @Controller()",
|
|
1216
|
+
" export class InventoryController {",
|
|
1217
|
+
" @MessagePattern({ cmd: 'deductInventory' })",
|
|
1218
|
+
" async deductInventory(@Payload() data: { productId: string; quantity: number }) {",
|
|
1219
|
+
" // 业务逻辑:扣减库存",
|
|
1220
|
+
" return { success: true, remaining: 100 };",
|
|
1221
|
+
" }",
|
|
1222
|
+
"",
|
|
1223
|
+
" @EventPattern('order.created')",
|
|
1224
|
+
" async handleOrderCreated(@Payload() data: { orderId: string; items: any[] }) {",
|
|
1225
|
+
" // 事件处理:无需返回值",
|
|
1226
|
+
" }",
|
|
1227
|
+
" }",
|
|
1228
|
+
" ```",
|
|
1229
|
+
"4. 定义每个 Pattern 的 Payload 类型(DTO):",
|
|
1230
|
+
" ```typescript",
|
|
1231
|
+
" export interface DeductInventoryPayload {",
|
|
1232
|
+
" productId: string;",
|
|
1233
|
+
" quantity: number;",
|
|
1234
|
+
" }",
|
|
1235
|
+
" ```",
|
|
1236
|
+
"5. 检查现有微服务是否有类似的 Pattern,避免命名冲突",
|
|
1237
|
+
"",
|
|
1238
|
+
"**产出格式**:",
|
|
1239
|
+
"```",
|
|
1240
|
+
"message_patterns: [{ cmd: '...', payload_type: '...', description: '...' }]",
|
|
1241
|
+
"event_patterns: [{ event: '...', payload_type: '...', consumers: [...] }]",
|
|
1242
|
+
"naming_convention: { cmd: '{ cmd: ... }' | 'service.action', event: 'domain.event' }",
|
|
1243
|
+
"```",
|
|
1244
|
+
].join("\n"),
|
|
1245
|
+
suggestedTools: ["Read", "Write", "Bash"],
|
|
1246
|
+
output: "MessagePattern 和 EventPattern 清单、Payload DTO",
|
|
1247
|
+
},
|
|
1248
|
+
{
|
|
1249
|
+
id: "setup-transport",
|
|
1250
|
+
title: "配置传输层",
|
|
1251
|
+
instruction: [
|
|
1252
|
+
"## 配置传输层(TCP / Redis / Kafka 选型建议)",
|
|
1253
|
+
"",
|
|
1254
|
+
"根据业务场景选择合适的微服务传输层,并配置 `main.ts` 和 Module。",
|
|
1255
|
+
"",
|
|
1256
|
+
"**传输层对比**:",
|
|
1257
|
+
"| 传输层 | 适用场景 | 特点 |",
|
|
1258
|
+
"|--------|----------|------|",
|
|
1259
|
+
"| TCP | 简单内部通信、开发测试 | 默认、无外部依赖 |",
|
|
1260
|
+
"| Redis | 中等规模、消息队列 | 需要 Redis 服务 |",
|
|
1261
|
+
"| Kafka | 高吞吐事件流、日志、事件溯源 | 需要 Kafka 集群 |",
|
|
1262
|
+
"| RabbitMQ | 复杂消息路由、优先级队列 | 需要 RabbitMQ 服务 |",
|
|
1263
|
+
"| NATS | 轻量级、云原生 | 资源占用小 |",
|
|
1264
|
+
"| gRPC | 强类型、跨语言、高性能 | 需要 .proto 文件 |",
|
|
1265
|
+
"",
|
|
1266
|
+
"**执行动作**:",
|
|
1267
|
+
"1. 根据业务场景选定传输层(通常一个微服务只选一种)",
|
|
1268
|
+
"2. 安装对应的传输层包:",
|
|
1269
|
+
" ```bash",
|
|
1270
|
+
" # TCP 无需额外安装(NestJS 内置)",
|
|
1271
|
+
" npm install --save @nestjs/microservices",
|
|
1272
|
+
"",
|
|
1273
|
+
" # Redis",
|
|
1274
|
+
" npm install --save ioredis",
|
|
1275
|
+
"",
|
|
1276
|
+
" # Kafka",
|
|
1277
|
+
" npm install --save kafkajs",
|
|
1278
|
+
"",
|
|
1279
|
+
" # RabbitMQ",
|
|
1280
|
+
" npm install --save amqplib",
|
|
1281
|
+
"",
|
|
1282
|
+
" # NATS",
|
|
1283
|
+
" npm install --save nats",
|
|
1284
|
+
"",
|
|
1285
|
+
" # gRPC",
|
|
1286
|
+
" npm install --save @grpc/grpc-js @grpc/proto-loader",
|
|
1287
|
+
" ```",
|
|
1288
|
+
"3. 在 `main.ts` 中配置微服务启动:",
|
|
1289
|
+
" ```typescript",
|
|
1290
|
+
" import { NestFactory } from '@nestjs/core';",
|
|
1291
|
+
" import { MicroserviceOptions, Transport } from '@nestjs/microservices';",
|
|
1292
|
+
" import { AppModule } from './app.module';",
|
|
1293
|
+
"",
|
|
1294
|
+
" async function bootstrap() {",
|
|
1295
|
+
" const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {",
|
|
1296
|
+
" transport: Transport.TCP, // 或 Transport.REDIS / Transport.KAFKA 等",
|
|
1297
|
+
" options: {",
|
|
1298
|
+
" host: '127.0.0.1',",
|
|
1299
|
+
" port: 3001,",
|
|
1300
|
+
" // Redis: { host: 'localhost', port: 6379 }",
|
|
1301
|
+
" // Kafka: { client: { brokers: ['localhost:9092'] }, consumer: { groupId: 'my-group' } }",
|
|
1302
|
+
" },",
|
|
1303
|
+
" });",
|
|
1304
|
+
" await app.listen();",
|
|
1305
|
+
" }",
|
|
1306
|
+
" bootstrap();",
|
|
1307
|
+
" ```",
|
|
1308
|
+
"4. 如果微服务同时需要提供 HTTP 接口,使用混合应用:",
|
|
1309
|
+
" ```typescript",
|
|
1310
|
+
" const app = await NestFactory.create(AppModule);",
|
|
1311
|
+
" const microservice = app.connectMicroservice<MicroserviceOptions>({",
|
|
1312
|
+
" transport: Transport.TCP,",
|
|
1313
|
+
" options: { port: 3001 },",
|
|
1314
|
+
" });",
|
|
1315
|
+
" await app.startAllMicroservices();",
|
|
1316
|
+
" await app.listen(3000);",
|
|
1317
|
+
" ```",
|
|
1318
|
+
"5. 在 `.env` 文件中配置传输层参数(便于不同环境切换)",
|
|
1319
|
+
"",
|
|
1320
|
+
"**产出格式**:",
|
|
1321
|
+
"```",
|
|
1322
|
+
"transport_selected: TCP | REDIS | KAFKA | RABBITMQ | NATS | GRPC",
|
|
1323
|
+
"transport_options: { host, port, ... }",
|
|
1324
|
+
"packages_installed: ['@nestjs/microservices', ...]",
|
|
1325
|
+
"hybrid_mode: true | false (是否同时提供 HTTP)",
|
|
1326
|
+
"env_vars: { MICROSERVICE_PORT, KAFKA_BROKERS, ... }",
|
|
1327
|
+
"```",
|
|
1328
|
+
].join("\n"),
|
|
1329
|
+
suggestedTools: ["Bash", "Read", "Write"],
|
|
1330
|
+
output: "选定的传输层、配置代码、安装依赖清单",
|
|
1331
|
+
depends: ["define-patterns"],
|
|
1332
|
+
},
|
|
1333
|
+
{
|
|
1334
|
+
id: "create-client",
|
|
1335
|
+
title: "创建 ClientProxy 代理服务",
|
|
1336
|
+
instruction: [
|
|
1337
|
+
"## 创建 ClientProxy 代理服务",
|
|
1338
|
+
"",
|
|
1339
|
+
"在调用方服务中创建代理 Service 用于跨服务调用。",
|
|
1340
|
+
"",
|
|
1341
|
+
"**执行动作**:",
|
|
1342
|
+
"1. 在调用方 Module 中注册 ClientProxy:",
|
|
1343
|
+
" ```typescript",
|
|
1344
|
+
" import { Module } from '@nestjs/common';",
|
|
1345
|
+
" import { ClientsModule, Transport } from '@nestjs/microservices';",
|
|
1346
|
+
" import { OrderModule } from './order.module';",
|
|
1347
|
+
"",
|
|
1348
|
+
" @Module({",
|
|
1349
|
+
" imports: [",
|
|
1350
|
+
" ClientsModule.register([",
|
|
1351
|
+
" {",
|
|
1352
|
+
" name: 'INVENTORY_SERVICE',",
|
|
1353
|
+
" transport: Transport.TCP,",
|
|
1354
|
+
" options: { host: 'localhost', port: 3001 },",
|
|
1355
|
+
" },",
|
|
1356
|
+
" ]),",
|
|
1357
|
+
" OrderModule,",
|
|
1358
|
+
" ],",
|
|
1359
|
+
" })",
|
|
1360
|
+
" export class AppModule {}",
|
|
1361
|
+
" ```",
|
|
1362
|
+
"2. 在 Service 中注入 `@Inject('INVENTORY_SERVICE')` 的 ClientProxy:",
|
|
1363
|
+
" ```typescript",
|
|
1364
|
+
" import { Injectable, Inject, OnModuleInit } from '@nestjs/common';",
|
|
1365
|
+
" import { ClientProxy } from '@nestjs/microservices';",
|
|
1366
|
+
"",
|
|
1367
|
+
" @Injectable()",
|
|
1368
|
+
" export class OrderService implements OnModuleInit {",
|
|
1369
|
+
" constructor(",
|
|
1370
|
+
" @Inject('INVENTORY_SERVICE') private readonly inventoryClient: ClientProxy,",
|
|
1371
|
+
" ) {}",
|
|
1372
|
+
"",
|
|
1373
|
+
" async onModuleInit() {",
|
|
1374
|
+
" await this.inventoryClient.connect();",
|
|
1375
|
+
" }",
|
|
1376
|
+
"",
|
|
1377
|
+
" async createOrder(dto: CreateOrderDto) {",
|
|
1378
|
+
" // 请求-响应",
|
|
1379
|
+
" const result = await this.inventoryClient",
|
|
1380
|
+
" .send<{ success: boolean; remaining: number }>(",
|
|
1381
|
+
" { cmd: 'deductInventory' },",
|
|
1382
|
+
" { productId: dto.productId, quantity: dto.quantity },",
|
|
1383
|
+
" )",
|
|
1384
|
+
" .toPromise();",
|
|
1385
|
+
"",
|
|
1386
|
+
" if (!result.success) {",
|
|
1387
|
+
" throw new BadRequestException('库存不足');",
|
|
1388
|
+
" }",
|
|
1389
|
+
"",
|
|
1390
|
+
" // 创建订单后广播事件",
|
|
1391
|
+
" this.inventoryClient.emit('order.created', {",
|
|
1392
|
+
" orderId: order.id,",
|
|
1393
|
+
" items: dto.items,",
|
|
1394
|
+
" });",
|
|
1395
|
+
"",
|
|
1396
|
+
" return order;",
|
|
1397
|
+
" }",
|
|
1398
|
+
" }",
|
|
1399
|
+
" ```",
|
|
1400
|
+
"3. 使用 `client.send<T>(pattern, data).toPromise()` 做请求-响应调用",
|
|
1401
|
+
"4. 使用 `client.emit(event, data)` 做事件广播调用",
|
|
1402
|
+
"5. 封装成独立的方法,避免业务代码里直接调用 `client.send`",
|
|
1403
|
+
"6. 为 ClientProxy 注入添加完善的 TypeScript 类型(避免 `any`)",
|
|
1404
|
+
"7. 考虑将每个远程调用封装成独立的方法便于测试:",
|
|
1405
|
+
" ```typescript",
|
|
1406
|
+
" async deductInventory(productId: string, quantity: number) {",
|
|
1407
|
+
" return this.inventoryClient.send<{ success: boolean }>(",
|
|
1408
|
+
" { cmd: 'deductInventory' },",
|
|
1409
|
+
" { productId, quantity },",
|
|
1410
|
+
" ).toPromise();",
|
|
1411
|
+
" }",
|
|
1412
|
+
" ```",
|
|
1413
|
+
"",
|
|
1414
|
+
"**产出格式**:",
|
|
1415
|
+
"```",
|
|
1416
|
+
"client_module_setup: { module_file, client_name, transport, options }",
|
|
1417
|
+
"proxy_services: [{ service_name, client_token, methods: [{ name, pattern, return_type }] }]",
|
|
1418
|
+
"lifecycle_hook: OnModuleInit (for connect())",
|
|
1419
|
+
"```",
|
|
1420
|
+
].join("\n"),
|
|
1421
|
+
suggestedTools: ["Read", "Write", "Edit"],
|
|
1422
|
+
output: "ClientProxy 注册代码、代理服务方法清单",
|
|
1423
|
+
depends: ["setup-transport"],
|
|
1424
|
+
},
|
|
1425
|
+
{
|
|
1426
|
+
id: "add-error-handling",
|
|
1427
|
+
title: "添加异常过滤器和重试逻辑",
|
|
1428
|
+
instruction: [
|
|
1429
|
+
"## 添加异常过滤器和重试逻辑",
|
|
1430
|
+
"",
|
|
1431
|
+
"跨服务调用必须考虑网络抖动、服务不可用、超时等异常场景。",
|
|
1432
|
+
"",
|
|
1433
|
+
"**执行动作**:",
|
|
1434
|
+
"1. 为 `ClientProxy` 调用添加超时控制:",
|
|
1435
|
+
" ```typescript",
|
|
1436
|
+
" import { timeout, catchError, throwError, retry } from 'rxjs';",
|
|
1437
|
+
"",
|
|
1438
|
+
" async deductInventory(productId: string, quantity: number) {",
|
|
1439
|
+
" return this.inventoryClient",
|
|
1440
|
+
" .send<{ success: boolean }>({ cmd: 'deductInventory' }, { productId, quantity })",
|
|
1441
|
+
" .pipe(",
|
|
1442
|
+
" timeout(5000), // 5 秒超时",
|
|
1443
|
+
" retry({",
|
|
1444
|
+
" count: 3,",
|
|
1445
|
+
" delay: 1000,",
|
|
1446
|
+
" resetOnSuccess: true,",
|
|
1447
|
+
" }),",
|
|
1448
|
+
" catchError((err) => {",
|
|
1449
|
+
" if (err.name === 'TimeoutError') {",
|
|
1450
|
+
" return throwError(() => new ServiceUnavailableException('库存服务超时'));",
|
|
1451
|
+
" }",
|
|
1452
|
+
" return throwError(() => err);",
|
|
1453
|
+
" }),",
|
|
1454
|
+
" )",
|
|
1455
|
+
" .toPromise();",
|
|
1456
|
+
" }",
|
|
1457
|
+
" ```",
|
|
1458
|
+
"2. 创建 RPC 异常过滤器处理跨服务异常:",
|
|
1459
|
+
" ```typescript",
|
|
1460
|
+
" import { Catch, RpcExceptionFilter, ArgumentsHost } from '@nestjs/common';",
|
|
1461
|
+
" import { RpcException } from '@nestjs/microservices';",
|
|
1462
|
+
" import { Observable, throwError } from 'rxjs';",
|
|
1463
|
+
"",
|
|
1464
|
+
" @Catch(RpcException)",
|
|
1465
|
+
" export class RpcExceptionFilter implements RpcExceptionFilter {",
|
|
1466
|
+
" catch(exception: RpcException, host: ArgumentsHost): Observable<any> {",
|
|
1467
|
+
" const error = exception.getError();",
|
|
1468
|
+
" // 记录日志、转换异常格式",
|
|
1469
|
+
" return throwError(() => ({",
|
|
1470
|
+
" status: 'error',",
|
|
1471
|
+
" message: typeof error === 'string' ? error : error.message,",
|
|
1472
|
+
" });",
|
|
1473
|
+
" }",
|
|
1474
|
+
" }",
|
|
1475
|
+
" ```",
|
|
1476
|
+
"3. 使用 `@UseFilters(new RpcExceptionFilter())` 应用到 Controller",
|
|
1477
|
+
"4. 关键操作添加幂等性保障(使用唯一请求 ID):",
|
|
1478
|
+
" ```typescript",
|
|
1479
|
+
" async createOrder(dto: CreateOrderDto, requestId: string) {",
|
|
1480
|
+
" // 先查询是否已处理",
|
|
1481
|
+
" const existing = await this.prisma.order.findUnique({ where: { requestId } });",
|
|
1482
|
+
" if (existing) return existing;",
|
|
1483
|
+
" // 否则创建新订单",
|
|
1484
|
+
" return this.prisma.order.create({ data: { ...dto, requestId } });",
|
|
1485
|
+
" }",
|
|
1486
|
+
" ```",
|
|
1487
|
+
"5. 配置 Circuit Breaker(断路器)防止雪崩效应(可选,使用 `opossum` 等库)",
|
|
1488
|
+
"6. 对于事件广播,考虑使用 Dead Letter Queue 处理失败事件",
|
|
1489
|
+
"7. 日志记录每次跨服务调用:调用方、被调用方、耗时、成功/失败",
|
|
1490
|
+
"",
|
|
1491
|
+
"**产出格式**:",
|
|
1492
|
+
"```",
|
|
1493
|
+
"retry_config: { count: N, delay_ms: N }",
|
|
1494
|
+
"timeout_ms: N",
|
|
1495
|
+
"rpc_exception_filter: <file path>",
|
|
1496
|
+
"idempotency_strategy: 'request-id' | 'none'",
|
|
1497
|
+
"error_handling_patterns: [{ pattern, file }]```",
|
|
1498
|
+
].join("\n"),
|
|
1499
|
+
suggestedTools: ["Read", "Write", "Edit"],
|
|
1500
|
+
output: "重试/超时/幂等性配置、RPC 异常过滤器文件",
|
|
1501
|
+
depends: ["create-client"],
|
|
1502
|
+
},
|
|
1503
|
+
{
|
|
1504
|
+
id: "integration-test",
|
|
1505
|
+
title: "集成测试验证消息收发",
|
|
1506
|
+
instruction: [
|
|
1507
|
+
"## 集成测试验证消息收发",
|
|
1508
|
+
"",
|
|
1509
|
+
"编写集成测试验证微服务的消息发送和接收正确工作。",
|
|
1510
|
+
"",
|
|
1511
|
+
"**执行动作**:",
|
|
1512
|
+
"1. 创建集成测试文件 `test/<service-name>.e2e-spec.ts`:",
|
|
1513
|
+
" ```typescript",
|
|
1514
|
+
" import { Test, TestingModule } from '@nestjs/testing';",
|
|
1515
|
+
" import { INestApplication, HttpStatus } from '@nestjs/common';",
|
|
1516
|
+
" import { ClientProxy, ClientsModule, Transport } from '@nestjs/microservices';",
|
|
1517
|
+
" import { AppModule } from '../src/app.module';",
|
|
1518
|
+
"",
|
|
1519
|
+
" describe('InventoryMicroservice (e2e)', () => {",
|
|
1520
|
+
" let app: INestApplication;",
|
|
1521
|
+
" let client: ClientProxy;",
|
|
1522
|
+
"",
|
|
1523
|
+
" beforeAll(async () => {",
|
|
1524
|
+
" const moduleFixture: TestingModule = await Test.createTestingModule({",
|
|
1525
|
+
" imports: [AppModule],",
|
|
1526
|
+
" }).compile();",
|
|
1527
|
+
"",
|
|
1528
|
+
" app = moduleFixture.createNestApplication();",
|
|
1529
|
+
" app.connectMicroservice({",
|
|
1530
|
+
" transport: Transport.TCP,",
|
|
1531
|
+
" options: { host: 'localhost', port: 3001 },",
|
|
1532
|
+
" });",
|
|
1533
|
+
" await app.startAllMicroservices();",
|
|
1534
|
+
" await app.init();",
|
|
1535
|
+
"",
|
|
1536
|
+
" client = moduleFixture.get<ClientProxy>('INVENTORY_SERVICE');",
|
|
1537
|
+
" await client.connect();",
|
|
1538
|
+
" });",
|
|
1539
|
+
"",
|
|
1540
|
+
" afterAll(async () => {",
|
|
1541
|
+
" await client.close();",
|
|
1542
|
+
" await app.close();",
|
|
1543
|
+
" });",
|
|
1544
|
+
"",
|
|
1545
|
+
" it('should respond to deductInventory message', async () => {",
|
|
1546
|
+
" const result = await client",
|
|
1547
|
+
" .send({ cmd: 'deductInventory' }, { productId: 'p1', quantity: 1 })",
|
|
1548
|
+
" .toPromise();",
|
|
1549
|
+
" expect(result).toEqual({ success: true, remaining: expect.any(Number) });",
|
|
1550
|
+
" });",
|
|
1551
|
+
"",
|
|
1552
|
+
" it('should handle order.created event', async () => {",
|
|
1553
|
+
" client.emit('order.created', { orderId: 'o1', items: [] });",
|
|
1554
|
+
" // 等待事件被处理",
|
|
1555
|
+
" await new Promise((resolve) => setTimeout(resolve, 100));",
|
|
1556
|
+
" // 验证数据库或状态变化",
|
|
1557
|
+
" });",
|
|
1558
|
+
" });",
|
|
1559
|
+
" ```",
|
|
1560
|
+
"2. 测试 `@MessagePattern` 的请求-响应调用",
|
|
1561
|
+
"3. 测试 `@EventPattern` 的事件广播",
|
|
1562
|
+
"4. 测试异常场景:",
|
|
1563
|
+
" - 服务不可用时的超时处理",
|
|
1564
|
+
" - 重试逻辑是否生效",
|
|
1565
|
+
" - RpcExceptionFilter 是否正确转换异常",
|
|
1566
|
+
"5. 测试边界条件:",
|
|
1567
|
+
" - 并发消息处理",
|
|
1568
|
+
" - 大消息体",
|
|
1569
|
+
" - 网络断开恢复后的行为",
|
|
1570
|
+
"6. 使用 Jest 的 `--runInBand` 串行运行避免端口冲突:",
|
|
1571
|
+
" ```bash",
|
|
1572
|
+
" npx jest --config ./test/jest-e2e.json --runInBand",
|
|
1573
|
+
" ```",
|
|
1574
|
+
"7. 如果项目使用 Docker Compose,测试前启动依赖服务:",
|
|
1575
|
+
" ```bash",
|
|
1576
|
+
" docker-compose -f docker-compose.test.yml up -d",
|
|
1577
|
+
" npm run test:e2e",
|
|
1578
|
+
" docker-compose -f docker-compose.test.yml down",
|
|
1579
|
+
" ```",
|
|
1580
|
+
"",
|
|
1581
|
+
"**产出格式**:",
|
|
1582
|
+
"```",
|
|
1583
|
+
"test_file: <path>",
|
|
1584
|
+
"test_cases: [{ describe, it, status: 'written' }]",
|
|
1585
|
+
"patterns_tested: [{ cmd | event, result }]",
|
|
1586
|
+
"test_result: passed | failed",
|
|
1587
|
+
"services_required: [TCP, Redis, ...]",
|
|
1588
|
+
"```",
|
|
1589
|
+
].join("\n"),
|
|
1590
|
+
suggestedTools: ["Bash", "Read", "Write"],
|
|
1591
|
+
output: "集成测试文件、测试用例清单、测试结果",
|
|
1592
|
+
depends: ["add-error-handling"],
|
|
1593
|
+
},
|
|
1594
|
+
],
|
|
1595
|
+
triggers: [{ type: "cli", command: "setup:microservice" }],
|
|
1596
|
+
},
|
|
1597
|
+
],
|
|
1598
|
+
},
|
|
1599
|
+
prompt: {
|
|
1600
|
+
system: "你是一个 NestJS 专家。使用装饰器模式、依赖注入和模块化架构。遵循 SOLID 原则。",
|
|
1601
|
+
rules: [
|
|
1602
|
+
"每个功能封装为独立 Module(Controller + Service + Module + DTO + Spec)",
|
|
1603
|
+
"使用构造函数依赖注入,通过 interface 解耦",
|
|
1604
|
+
"使用 class-validator + ValidationPipe 校验请求",
|
|
1605
|
+
"Controller 只处理路由和参数解析,业务逻辑在 Service 中",
|
|
1606
|
+
"遇到问题先查 NestJS 官方文档再给方案",
|
|
1607
|
+
"修复 Bug 前先追踪 DI 链和 Module 注册",
|
|
1608
|
+
"使用 Prisma 作为 ORM,事务使用 $transaction",
|
|
1609
|
+
],
|
|
1610
|
+
},
|
|
1611
|
+
tools: [
|
|
1612
|
+
{
|
|
1613
|
+
name: "nest_diagnose_bug",
|
|
1614
|
+
description: "NestJS Bug 诊断:异常定位 → 依赖链追踪 → 修复",
|
|
1615
|
+
inputSchema: { type: "object", properties: { projectRoot: { type: "string" }, errorMessage: { type: "string" }, stackTrace: { type: "string" } }, required: ["projectRoot", "errorMessage"] },
|
|
1616
|
+
handler: async (input) => { const { diagnoseBug } = await import('./analyzers/diagnose-bug.js'); return diagnoseBug(input); },
|
|
1617
|
+
},
|
|
1618
|
+
{
|
|
1619
|
+
name: "nest_new_module",
|
|
1620
|
+
description: "新建 NestJS 模块(Controller + Service + Module + DTO + 测试)",
|
|
1621
|
+
inputSchema: { type: "object", properties: { moduleName: { type: "string" } }, required: ["moduleName"] },
|
|
1622
|
+
handler: async (input) => { const { generateModule } = await import('./analyzers/new-module.js'); return generateModule(input.moduleName); },
|
|
1623
|
+
},
|
|
1624
|
+
],
|
|
1625
|
+
};
|
|
1626
|
+
//# sourceMappingURL=index.js.map
|