第 6 章
Cursor Composer 实战——多文件编辑、大型功能开发的完整工作流
第6章:Cursor Composer 实战——多文件编辑、大型功能开发的完整工作流
Composer 是 Cursor 和其他 AI 编辑器最大的区别:你描述一个功能,它同时修改 5-10 个文件,展示完整的 diff,你审查后一键应用。这不是补全,是协作开发。本章讲 Composer 的工作原理、三个必须掌握的技巧,以及一个从零添加 Stripe 支付的完整实战案例。
Chat vs Composer:什么时候用哪个
| 维度 | Cursor Chat | Cursor Composer |
|---|---|---|
| 主要用途 | 理解、讨论、获取建议 | 直接生成和修改代码 |
| 代码修改 | 需要手动复制粘贴 | 自动 diff,一键应用 |
| 文件范围 | 单文件讨论为主 | 跨多文件协调修改 |
| 适合任务 | 问"为什么"、学习原理 | 做"是什么"、实现功能 |
| 上下文控制 | @ 引用精确控制 | 自动分析项目结构 |
决策规则:需要修改代码时,用 Composer;只需要理解代码时,用 Chat。
Composer 的内部工作流程
当你在 Composer 里提交一个 Prompt,背后发生了这些步骤:
- 项目分析:Cursor 扫描项目结构,建立文件依赖图
- 语义搜索:在向量索引里找到与你描述相关的文件
- 上下文组装:把相关文件内容、.cursorrules 规则、你的 Prompt 组合成一个超长 context
- 生成计划:AI 先规划要修改哪些文件、添加什么内容
- 生成代码:按计划逐文件生成修改内容
- 展示 Diff:用类似 git diff 的界面展示所有改动,你可以逐文件审查
关键的是步骤 6:你在应用之前能看到所有改动。这是 Composer 和"直接帮我写"最重要的区别——AI 是建议者,你是决策者。
三个必须掌握的 Composer 技巧
技巧一:用 Checkpoint 分阶段提交
不要用一个 Prompt 让 Composer 实现整个功能。按逻辑阶段拆分:
# 第一轮 Prompt(数据层)
在 Prisma schema 里添加 Payment 模型,字段包括:
- id: UUID 主键
- userId: 外键关联 User
- amount: Integer(用整数存分,不用浮点数存元)
- currency: String(默认 "USD")
- status: Enum(pending / completed / failed)
- stripePaymentIntentId: String(可为空)
- createdAt: DateTime
生成对应的 migration 文件,不要修改其他任何文件。
第一轮应用完,测试数据库 migration 是否成功,再开第二轮:
# 第二轮 Prompt(服务层)
基于刚才创建的 Payment 模型,创建 PaymentService:
- createPaymentIntent(userId, amount, currency): 调用 Stripe API 创建支付意图
- confirmPayment(paymentIntentId): 在 Webhook 里确认支付成功,更新数据库状态
- getUserPayments(userId): 查询用户的支付历史
使用 @src/lib/stripe.ts 里已有的 Stripe 客户端实例,不要重新初始化。
分阶段的好处:每一步都能测试和验证,哪一步出问题很容易定位,不会出现"改了50个文件但不知道哪里改坏了"。
技巧二:用 Agent 模式处理不确定的大型任务
点击 Composer 里的 "Agent" 按钮,Cursor 会进入自主执行模式:读取文件、运行命令、自己决定下一步。适合"帮我完成这个大型重构"这类任务。
Agent 模式的典型 Prompt:
分析整个项目的错误处理方式,然后统一改成这个标准:
1. 所有 API 错误返回 { success: false, error: { code, message } } 格式
2. 服务层抛出自定义 AppError 类(已在 src/errors.ts 定义)
3. 路由层统一 catch 并格式化返回
先列出计划再执行,每改完一个模块就说明做了什么。
Agent 模式注意: 它会真正执行命令,包括可能的文件删除。开始前确保 git 状态干净,方便出问题时回滚。
技巧三:审查 Diff 的正确方式
Composer 展示 diff 时,不要直接点"Accept All"。正确的审查流程:
- 先看文件列表:有没有不该被修改的文件被改了?
- 重点看新增的外部调用:新引入了哪些库、哪些 API?是否符合项目规范?
- 检查边界情况:错误处理是否完整?空值、零值、超长输入怎么处理?
- 验证约束条件:你在 Prompt 里要求的约束(不改接口、保持测试通过),AI 是否都遵守了?
实战案例:添加 Stripe 支付(两轮 Composer)
目标:给现有的 Next.js + Prisma 项目添加 Stripe 一次性支付功能。
第一轮:数据模型和服务层
给项目添加 Stripe 支付功能,第一阶段:基础设施
1. 在 prisma/schema.prisma 里添加 Payment 模型:
- id, userId(FK), amount(Int,分), currency(String),
status(Enum:pending/completed/failed),
stripePaymentIntentId(String?), createdAt
2. 创建 src/lib/stripe.ts:初始化 Stripe 客户端(使用 STRIPE_SECRET_KEY 环境变量)
3. 创建 src/services/PaymentService.ts:
- createPaymentIntent(userId: string, amount: number): Promise<{clientSecret: string, paymentId: string}>
- confirmPayment(stripePaymentIntentId: string): Promise<void>
只做这三件事,不要碰路由和前端。
第二轮:API 路由和 Webhook
基于上一步的 PaymentService,完成支付功能的第二阶段:
1. POST /api/payments/create-intent:
- 验证用户登录(用 src/lib/auth.ts 里的 getCurrentUser)
- 调用 PaymentService.createPaymentIntent
- 返回 clientSecret 给前端
2. POST /api/webhooks/stripe:
- 验证 Stripe Webhook 签名(用 stripe.webhooks.constructEvent)
- 处理 payment_intent.succeeded 事件,调用 PaymentService.confirmPayment
- 注意:这个路由必须用 req.text() 不能用 req.json()(Stripe 签名需要原始 body)
不要创建前端组件,只做后端 API。
两轮完成后,支付后端功能就完整了,整个过程清晰可控。
5 个常见失败模式
| 失败模式 | 症状 | 解决方法 |
|---|---|---|
| Prompt 太模糊 | 生成了一堆看起来合理但不符合项目风格的代码 | 加具体约束:使用哪个库、遵循哪个已有文件的风格 |
| 一次要求太多 | 改了几十个文件,不知道哪里改坏了 | 拆成小阶段,每次只做一件事 |
| 没有 .cursorrules | AI 不知道项目约定,每次生成风格不一致 | 先写 .cursorrules,把技术栈和规范写进去 |
| 不审查直接 Accept | 发现问题时已经难以追溯是哪轮改坏的 | 每次 Accept 前认真看 diff |
| Agent 模式失控 | Agent 修改了不该修改的文件,删除了重要内容 | 开始前 git commit,出问题用 git checkout 回滚 |
本章要点
- Composer 适合"做功能",Chat 适合"理解代码"——搞清楚边界,工具才能发挥价值
- 分阶段 Checkpoint 是关键:一个大功能拆成 3-5 个 Prompt,每轮验证再进行下一轮,远比一次提交靠谱
- Agent 模式是双刃剑:强大但有风险,使用前确保 git 状态干净,使用中实时监控它在做什么
- Diff 审查不是形式:重点看新增的外部调用、边界情况处理、约束条件是否遵守
- .cursorrules 是 Composer 的前置投资:没有 .cursorrules,Composer 每次生成的代码风格会漂移