一、本集目标
用 Cursor Agent 全程驱动,从空项目出发,完成一个生产可用的 NestJS 用户管理模块,包含:
- Prisma Schema 定义
- CreateUserDto、UpdateUserDto、FindUsersDto(含参数校验)
- UserService(完整 CRUD + 软删除 + bcrypt 密码加密)
- UserController(含 Swagger 注解)
- UserModule 注册
- Swagger 文档验证可访问
- UserService 单元测试
二、Agent 驱动开发的核心原则
2.1 先确认需求,再让 AI 写代码
让 Agent 先分析需求文档、复述理解内容,你确认没有歧义之后再让它开始实现。
原因:Agent 一旦开始写代码,中途改需求的成本比开始前确认高得多。
2.2 把需求写成文档,不要在 Prompt 里口述
把需求写成 PRD.md 文件,通过 @PRD.md 引用。
原因:
- 文档可以随时修改补充
- Agent 能完整读取,不会遗漏
- 后续其他集的演示可以复用同一份文档
2.3 一次生成后必须审查,不能直接用
Agent 生成完代码,第一件事是逐文件检查,不是直接运行。
重点审查的逻辑:
- 密码是否用 bcrypt 加密(而不是明文存储)
- 查询返回值是否排除了 password 字段
- 软删除是否用
update deletedAt而不是delete - 事务逻辑是否正确
2.4 发现问题,用 Agent 修,不要手动改
在 Chat 里说"这里有个问题:xxx,帮我修复",让 Agent 修正。这样整个过程保持 AI 驱动,也顺带演示了"审查 + 修正"的完整工作流。
三、需求文档:PRD.md
在项目根目录创建 PRD.md,内容如下:
# 用户管理模块需求文档
## 数据字段
| 字段 | 类型 | 说明 |
|------|------|------|
| id | 自增整数 | 主键 |
| username | 字符串 | 用户名,唯一,3~20 个字符 |
| email | 字符串 | 邮箱,唯一 |
| password | 字符串 | 密码,存储时必须用 bcrypt 加密,salt rounds = 10 |
| role | 枚举 | USER \| ADMIN,默认 USER |
| isActive | 布尔 | 是否激活,默认 true |
| createdAt | 时间 | 创建时间,自动生成 |
| updatedAt | 时间 | 更新时间,自动更新 |
| deletedAt | 时间(可空)| 软删除时间,null 表示未删除 |
## 接口列表
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | /api/users | 创建用户(注册) |
| GET | /api/users | 获取用户列表(分页 + username 搜索) |
| GET | /api/users/:id | 获取单个用户详情 |
| PATCH | /api/users/:id | 更新用户信息 |
| DELETE | /api/users/:id | 软删除用户 |
## 业务规则
- 密码存储前必须用 bcrypt 加密,salt rounds = 10
- 所有查询的返回值必须排除 password 字段
- 软删除:DELETE 接口只更新 deletedAt 字段,不真正删除记录
- 查询列表时自动过滤 deletedAt 不为 null 的记录
- 分页:默认第 1 页,每页 10 条
- 返回格式统一:{ code: 0, data: any, message: "ok" }
- 所有接口加 Swagger 注解
## 技术约束
- ORM:Prisma
- 参数校验:class-validator
- 异常类:使用 NestJS 内置异常类
四、演示操作步骤
第一步:初始化项目
# 创建 NestJS 项目
npx @nestjs/cli new agent-demo
cd agent-demo
# 安装必要依赖
npm install @prisma/client prisma
npm install @nestjs/swagger swagger-ui-express
npm install class-validator class-transformer
npm install @nestjs/config
npm install bcrypt
npm install --save-dev @types/bcrypt
# 初始化 Prisma
npx prisma init
执行完后项目结构:
agent-demo/
├── prisma/
│ └── schema.prisma ← 接下来要修改这个文件
├── src/
│ ├── app.module.ts
│ └── main.ts
└── .env ← 配置数据库连接
第二步:配置 .env
打开 .env,修改 DATABASE_URL:
DATABASE_URL="postgresql://postgres:你的密码@localhost:5432/agent_demo"
在 PostgreSQL 里创建数据库:
CREATE DATABASE agent_demo;
验证连接:
npx prisma db pull # 如果能连上会有输出,连不上会报错
第三步:配置 .cursorrules
在项目根目录创建 .cursorrules,写入第 05 集的 NestJS 模板内容(完整内容见第 05 集文档)。
第四步:创建 PRD.md
把第三章的 PRD 内容写入项目根目录的 PRD.md 文件。
第五步:AI 需求确认
在 Cursor Chat(Cmd+L)里输入:
@PRD.md 请阅读这份需求文档,然后:
1. 告诉我你理解的功能点有哪些
2. 列出你准备创建哪些文件
3. 有没有模糊或有歧义的地方需要我补充说明
检查 AI 的复述是否和需求一致,重点确认:
- 它知道要用 bcrypt 加密密码
- 它知道查询返回值要排除 password
- 它知道 DELETE 是软删除
- 它列出的文件清单里有没有遗漏
如果有遗漏(比如没提 common/dto/response.dto.ts),在 Chat 里补充:
你的文件列表里漏了 src/common/dto/response.dto.ts,
这是所有接口返回的统一格式,需要提前创建
第六步:生成 Prisma Schema
切换到 Agent 模式(Cmd+I),输入:
根据 @PRD.md 里的用户字段需求,完成 prisma/schema.prisma 文件里的 User model 定义。
要求:
- 字段完全按需求文档来
- role 用 enum 实现:enum Role { USER ADMIN }
- deletedAt 必须是可空类型(DateTime?)
- createdAt 用 @default(now())
- updatedAt 用 @updatedAt
Agent 修改 schema.prisma 后,检查生成的内容:
# 期望看到的 User model 结构
model User {
id Int @id @default(autoincrement())
username String @unique
email String @unique
password String
role Role @default(USER)
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
}
enum Role {
USER
ADMIN
}
确认无误后运行 migration:
npx prisma migrate dev --name init-user
npx prisma generate
两个命令都成功执行(无报错)才继续下一步。
第七步:生成完整 UserModule
在 Agent 模式输入:
根据 @PRD.md 和 @prisma/schema.prisma,帮我实现完整的 UserModule。
需要创建以下文件:
1. src/common/dto/response.dto.ts
2. src/modules/user/dto/create-user.dto.ts(用 class-validator 校验)
3. src/modules/user/dto/update-user.dto.ts
4. src/modules/user/dto/find-users.dto.ts(分页 + 搜索参数)
5. src/modules/user/user.service.ts
6. src/modules/user/user.controller.ts
7. src/modules/user/user.module.ts
额外要求:
- 密码用 bcrypt.hash(password, 10) 加密后再存
- 所有查询返回值用 Prisma 的 select 排除 password 字段
- findAll 用 where: { deletedAt: null } 过滤软删除记录
- remove 方法用 prisma.user.update 更新 deletedAt,不用 prisma.user.delete
- 分页用 skip: (page - 1) * pageSize, take: pageSize
等待 Agent 执行完成。
第八步:代码审查
Agent 完成后,按以下顺序逐文件检查:
检查**response.dto.ts**
# 期望看到
export class ResponseDto<T> {
code: number;
data: T;
message: string;
}
检查**create-user.dto.ts**
期望看到的校验装饰器:
@IsString()+@MinLength(3)+@MaxLength(20)在 username 上@IsEmail()在 email 上@IsString()+@MinLength(6)在 password 上@IsEnum(Role)+@IsOptional()在 role 上
检查 **user.service.ts** 的关键逻辑
| 方法 | 检查项 | 期望 |
|---|---|---|
| create | 密码加密 | bcrypt.hash(password, 10) |
| create | 返回值 | 用 select排除 password |
| findAll | 过滤软删除 | where: { deletedAt: null } |
| findOne | 找不到时 | 抛 NotFoundException |
| remove | 删除方式 | update({ data: { deletedAt: new Date() } }) |
如果发现问题(以 remove 方法用了硬删除为例)
在 Chat 里输入:
user.service.ts 里的 remove 方法用的是 prisma.user.delete(),
这是硬删除。需要改成软删除:用 prisma.user.update 把 deletedAt 更新为当前时间
等 Agent 修复后再继续。
检查**user.controller.ts**
- 每个方法是否有
@ApiTags、@ApiOperation - 方法参数是否用了对应的 DTO
- 路径是否是 kebab-case
第九步:注册模块
在 Agent 模式输入:
帮我在 app.module.ts 里注册 UserModule,
同时配置好 ConfigModule(全局)和 PrismaService
Agent 修改完后,检查 app.module.ts:
# 期望看到
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
UserModule,
],
})
export class AppModule {}
第十步:配置 Swagger
在 main.ts 里加入 Swagger 配置(如果 Agent 没有加,手动加):
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 全局参数校验
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
// Swagger 配置
const config = new DocumentBuilder()
.setTitle('Agent Demo API')
.setDescription('用户管理模块接口文档')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();
第十一步:启动验证
npm run start:dev
启动成功后,打开浏览器访问:
http://localhost:3000/api
Swagger 文档里应该能看到 Users 分组下的 5 个接口:
POST /api/usersGET /api/usersGET /api/users/{id}PATCH /api/users/{id}DELETE /api/users/{id}
在 Swagger 里测试 POST /api/users
{
"username": "testuser",
"email": "test@example.com",
"password": "123456"
}
期望返回:
{
"code": 0,
"data": {
"id": 1,
"username": "testuser",
"email": "test@example.com",
"role": "USER",
"isActive": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"deletedAt": null
},
"message": "ok"
}
注意:返回值里不应该有 password 字段。
第十二步:生成单元测试
在 Agent 模式输入:
为 user.service.ts 生成单元测试文件 src/modules/user/user.service.spec.ts。
重点测试:
1. create 方法:正常创建(验证密码被加密、返回值无 password)
2. create 方法:email 重复时抛出 ConflictException
3. findAll 方法:正常返回分页数据
4. findOne 方法:找不到用户时抛出 NotFoundException
5. remove 方法:验证执行的是软删除而不是硬删除
使用 Jest,Mock 掉 PrismaService,测试描述用中文
运行测试:
npm test -- user.service.spec.ts --verbose
如果有测试失败,把错误信息贴到 Chat 里:
测试运行报错:[粘贴错误信息],帮我修复
直到所有测试全绿为止。
五、常见问题处理
| 问题 | 原因 | 处理方式 |
|---|---|---|
npm run start:dev报 TypeScript 编译错误 |
AI 生成的类型有误 | 把错误信息贴给 Chat,让 AI 修复 |
| Prisma migration 失败 | .env里数据库连接串不对 |
检查 DATABASE_URL格式和数据库是否启动 |
| bcrypt 相关报错 | 缺少 @types/bcrypt |
npm install --save-dev @types/bcrypt |
| 测试文件 Mock 报错 | Mock 的方法路径不对 | PrismaService 的 Mock 路径是 mockPrisma.user.findUnique,不是 mockPrisma.findUnique |
| Swagger 页面打开是空的 | main.ts里没有配置 Swagger |
手动加入第十步里的 Swagger 配置代码 |
Spec Coding 实战补充:06 用 Cursor Agent 从 0 搭 NestJS 项目
来源:
Spec Coding实战/06 用 Cursor Agent 从 0 搭 NestJS 项目.md,已合并到本章节。
1. 项目目标
这一节做一个完整的用户管理模块,从 nest new 到接口可以跑通,全程用 Cursor Agent 驱动。
技术选型:
- NestJS 10 + TypeScript
- Prisma 5 + PostgreSQL
- class-validator 参数校验
- JWT 认证(仅搭结构,不展开认证逻辑)
- Pino 日志
最终项目结构:
my-project/
├── src/
│ ├── common/
│ │ ├── constants/
│ │ │ └── error-code.ts
│ │ ├── dto/
│ │ │ └── page-query.dto.ts
│ │ ├── exceptions/
│ │ │ └── business.exception.ts
│ │ ├── filters/
│ │ │ └── all-exceptions.filter.ts
│ │ └── interceptors/
│ │ └── transform.interceptor.ts
│ ├── config/
│ │ └── configuration.ts
│ ├── modules/
│ │ └── user/
│ │ ├── user.controller.ts
│ │ ├── user.service.ts
│ │ ├── user.module.ts
│ │ └── dto/
│ │ ├── create-user.dto.ts
│ │ ├── update-user.dto.ts
│ │ └── query-user.dto.ts
│ ├── prisma/
│ │ └── prisma.service.ts
│ └── app.module.ts
├── prisma/
│ └── schema.prisma
├── .cursorrules
├── .env
└── package.json
2. 第一步:初始化项目
创建 NestJS 项目
npm i -g @nestjs/cli
nest new my-project --package-manager npm
cd my-project
安装依赖
# Prisma
npm install prisma @prisma/client
npx prisma init
# 校验
npm install class-validator class-transformer
# 日志
npm install nestjs-pino pino-http pino-pretty
# 配置
npm install @nestjs/config
# JWT(后续认证用)
npm install @nestjs/jwt @nestjs/passport passport passport-jwt
npm install -D @types/passport-jwt
# Swagger
npm install @nestjs/swagger swagger-ui-express
# 密码加密
npm install bcrypt
npm install -D @types/bcrypt
配置 .env
# 数据库
DATABASE_URL="postgresql://postgres:password@localhost:5432/myproject_dev"
# JWT
JWT_SECRET="your-super-secret-key-change-in-production"
JWT_EXPIRES_IN="2h"
# 其他
NODE_ENV="development"
BCRYPT_ROUNDS=10
配置 .cursorrules
把第04节/第05节的 NestJS Rules 模板复制到项目根目录。
3. 第二步:搭建基础设施
这部分用 Cursor Agent 一次性生成,打开 Composer(Cmd+I),切换到 Agent 模式,输入:
帮我搭建 NestJS 项目的基础设施层,需要创建以下内容:
1. src/prisma/prisma.service.ts
- 继承 PrismaClient
- 实现 onModuleInit 连接数据库
- 实现 enableShutdownHooks
2. src/common/exceptions/business.exception.ts
- 继承 HttpException
- 接收 ErrorCode 枚举值
- 包含 code、message 两个字段
3. src/common/constants/error-code.ts
- ErrorCode 枚举:USER_NOT_FOUND(40401)、USER_ALREADY_EXISTS(40901)、
INVALID_CREDENTIALS(40101)、FORBIDDEN(40301)、VALIDATION_ERROR(40001)
4. src/common/filters/all-exceptions.filter.ts
- 捕获所有异常
- BusinessException 返回对应 code 和 message
- 其他异常返回 code:50000, message:'服务器内部错误'
- 统一响应结构:{ code, message, data: null }
5. src/common/interceptors/transform.interceptor.ts
- 包装成功响应:{ code: 0, message: 'ok', data: 原始返回值 }
6. src/common/dto/page-query.dto.ts
- page: number(默认1,最小1)
- pageSize: number(默认20,最大100)
- sortBy: string(默认'createdAt')
- order: 'asc' | 'desc'(默认'desc')
遵循 .cursorrules 的所有约定,完整 import,可直接运行。
Agent 生成完后,检查每个文件,确认 import 路径正确,没有语法错误。
生成的关键文件示例
business.exception.ts
import { HttpException, HttpStatus } from '@nestjs/common'
import { ErrorCode, ERROR_CODE_MAP } from '../constants/error-code'
export class BusinessException extends HttpException {
readonly code: number
readonly bizMessage: string
constructor(errorCode: ErrorCode) {
const { httpStatus, code, message } = ERROR_CODE_MAP[errorCode]
super({ code, message, data: null }, httpStatus)
this.code = code
this.bizMessage = message
}
}
error-code.ts
import { HttpStatus } from '@nestjs/common'
export enum ErrorCode {
USER_NOT_FOUND = 'USER_NOT_FOUND',
USER_ALREADY_EXISTS = 'USER_ALREADY_EXISTS',
INVALID_CREDENTIALS = 'INVALID_CREDENTIALS',
FORBIDDEN = 'FORBIDDEN',
VALIDATION_ERROR = 'VALIDATION_ERROR',
}
export const ERROR_CODE_MAP: Record<
ErrorCode,
{ httpStatus: HttpStatus; code: number; message: string }
> = {
[ErrorCode.USER_NOT_FOUND]: {
httpStatus: HttpStatus.NOT_FOUND,
code: 40401,
message: '用户不存在',
},
[ErrorCode.USER_ALREADY_EXISTS]: {
httpStatus: HttpStatus.CONFLICT,
code: 40901,
message: '邮箱已被注册',
},
[ErrorCode.INVALID_CREDENTIALS]: {
httpStatus: HttpStatus.UNAUTHORIZED,
code: 40101,
message: '邮箱或密码错误',
},
[ErrorCode.FORBIDDEN]: {
httpStatus: HttpStatus.FORBIDDEN,
code: 40301,
message: '无权限执行此操作',
},
[ErrorCode.VALIDATION_ERROR]: {
httpStatus: HttpStatus.BAD_REQUEST,
code: 40001,
message: '请求参数校验失败',
},
}
transform.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'
import { Observable } from 'rxjs'
import { map } from 'rxjs/operators'
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, any> {
intercept(_context: ExecutionContext, next: CallHandler<T>): Observable<any> {
return next.handle().pipe(
map((data) => ({
code: 0,
message: 'ok',
data: data ?? null,
})),
)
}
}
4. 第三步:配置 Prisma Schema
编辑 prisma/schema.prisma:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
email String @unique
password String
name String
status UserStatus @default(ACTIVE)
isDeleted Boolean @default(false) @map("is_deleted")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@index([email])
@@index([createdAt])
@@index([isDeleted, status])
@@map("users")
}
enum UserStatus {
ACTIVE
DISABLED
}
执行迁移:
npx prisma migrate dev --name init_user_table
npx prisma generate
5. 第四步:生成用户模块
打开 Composer,继续用 Agent:
按照以下规格,在 src/modules/user/ 下创建完整的用户模块:
**DTO:**
create-user.dto.ts:
- email: string(@IsEmail,@IsNotEmpty)
- password: string(@IsString,@MinLength(8),@MaxLength(32))
- name: string(@IsString,@MinLength(2),@MaxLength(50))
update-user.dto.ts:
- 继承 PartialType(CreateUserDto),password 字段用 @IsOptional
query-user.dto.ts:
- 继承 PageQueryDto
- search?: string(@IsOptional,@IsString,模糊搜索 name 和 email)
- status?: UserStatus(@IsOptional,@IsEnum(UserStatus))
**UserService 方法:**
- create(dto): 检查邮箱重复 → bcrypt 加密密码 → 创建用户 → 返回(omit password)
- findAll(query): 支持 search 模糊搜索 + status 过滤 + 分页 + 排序,返回 { list, total, page, pageSize }
- findOne(id): 查询单个用户,不存在抛 USER_NOT_FOUND
- update(id, dto): 更新用户信息,id 不存在抛 USER_NOT_FOUND
- remove(id): 软删除(isDeleted = true)
**UserController:**
- POST /users → create
- GET /users → findAll(需要加 @Query() 参数)
- GET /users/:id → findOne
- PATCH /users/:id → update
- DELETE /users/:id → remove
- 所有方法加 @ApiOperation Swagger 注释
使用 PrismaService,遵循 .cursorrules,完整 import,可直接运行。
引用 @src/common/ 目录下的基础设施。
生成后的关键文件
user.service.ts
import { Injectable } from '@nestjs/common'
import * as bcrypt from 'bcrypt'
import { PrismaService } from '../../prisma/prisma.service'
import { BusinessException } from '../../common/exceptions/business.exception'
import { ErrorCode } from '../../common/constants/error-code'
import { CreateUserDto } from './dto/create-user.dto'
import { UpdateUserDto } from './dto/update-user.dto'
import { QueryUserDto } from './dto/query-user.dto'
import { ConfigService } from '@nestjs/config'
@Injectable()
export class UserService {
constructor(
private readonly prisma: PrismaService,
private readonly configService: ConfigService,
) {}
async create(dto: CreateUserDto) {
const existing = await this.prisma.user.findUnique({
where: { email: dto.email },
})
if (existing) {
throw new BusinessException(ErrorCode.USER_ALREADY_EXISTS)
}
const rounds = this.configService.get<number>('BCRYPT_ROUNDS', 10)
const hashedPassword = await bcrypt.hash(dto.password, rounds)
const user = await this.prisma.user.create({
data: {
email: dto.email,
password: hashedPassword,
name: dto.name,
},
omit: { password: true },
})
return user
}
async findAll(query: QueryUserDto) {
const { page = 1, pageSize = 20, sortBy = 'createdAt', order = 'desc', search, status } = query
const whereCondition: any = {
isDeleted: false,
...(status && { status }),
...(search && {
OR: [
{ name: { contains: search, mode: 'insensitive' } },
{ email: { contains: search, mode: 'insensitive' } },
],
}),
}
const [list, total] = await this.prisma.$transaction([
this.prisma.user.findMany({
where: whereCondition,
skip: (page - 1) * pageSize,
take: pageSize,
orderBy: { [sortBy]: order },
omit: { password: true },
}),
this.prisma.user.count({ where: whereCondition }),
])
return { list, total, page, pageSize }
}
async findOne(id: string) {
const user = await this.prisma.user.findFirst({
where: { id, isDeleted: false },
omit: { password: true },
})
if (!user) {
throw new BusinessException(ErrorCode.USER_NOT_FOUND)
}
return user
}
async update(id: string, dto: UpdateUserDto) {
await this.findOne(id) // 验证存在
if (dto.password) {
const rounds = this.configService.get<number>('BCRYPT_ROUNDS', 10)
dto.password = await bcrypt.hash(dto.password, rounds)
}
return this.prisma.user.update({
where: { id },
data: dto,
omit: { password: true },
})
}
async remove(id: string) {
await this.findOne(id) // 验证存在
await this.prisma.user.update({
where: { id },
data: { isDeleted: true },
})
return null
}
}
6. 第五步:注册模块,配置全局
app.module.ts
import { Module } from '@nestjs/common'
import { ConfigModule } from '@nestjs/config'
import { APP_FILTER, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core'
import { ValidationPipe } from '@nestjs/common'
import { LoggerModule } from 'nestjs-pino'
import { PrismaModule } from './prisma/prisma.module'
import { UserModule } from './modules/user/user.module'
import { AllExceptionsFilter } from './common/filters/all-exceptions.filter'
import { TransformInterceptor } from './common/interceptors/transform.interceptor'
import configuration from './config/configuration'
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true, load: [configuration] }),
LoggerModule.forRoot({
pinoHttp: {
transport:
process.env.NODE_ENV !== 'production'
? { target: 'pino-pretty', options: { colorize: true } }
: undefined,
},
}),
PrismaModule,
UserModule,
],
providers: [
{ provide: APP_FILTER, useClass: AllExceptionsFilter },
{ provide: APP_INTERCEPTOR, useClass: TransformInterceptor },
{
provide: APP_PIPE,
useValue: new ValidationPipe({
whitelist: true,
transform: true,
forbidNonWhitelisted: true,
}),
},
],
})
export class AppModule {}
7. 第六步:启动验证
npm run start:dev
正常启动后,访问 Swagger 文档:http://localhost:3000/api
用 Postman 或 curl 测试:
# 创建用户
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"Test1234","name":"张三"}'
# 预期响应
{
"code": 0,
"message": "ok",
"data": {
"id": "uuid...",
"email": "test@example.com",
"name": "张三",
"status": "ACTIVE",
"createdAt": "2025-01-01T00:00:00.000Z"
}
}
# 查询列表
curl "http://localhost:3000/users?page=1&pageSize=10&search=张"
# 邮箱重复测试
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"Test1234","name":"李四"}'
# 预期响应
{
"code": 40901,
"message": "邮箱已被注册",
"data": null
}
8. 小结:Agent 驱动开发的节奏
用 Cursor Agent 从 0 搭项目,有几个关键节奏:
分层推进,不要一次要太多
- 先搭基础设施(filter、interceptor、exception)
- 再建数据模型(prisma schema + migrate)
- 最后做业务模块(dto + service + controller)
每一层验证通过再进入下一层,出问题好定位。
给 Agent 的任务要具体 任务里写清楚:方法名、参数、返回值、异常场景。越模糊的任务,生成质量越差。
生成完立刻编译验证
npm run build
有类型错误或编译错误,立刻贴回 Chat 修。不要攒一堆问题最后一起处理。