Documentation Index
Fetch the complete documentation index at: https://docs.devin.ai/llms.txt
Use this file to discover all available pages before exploring further.
不想手动配置?将本页链接粘贴到 Devin 的 session 中,让它帮你完成所有设置。
向 Devin 展示这个单体应用
你一定见过这样的文件——一个维护了一年半、越长越大的 Express 路由文件。每个业务域的每个接口都塞在 src/routes/index.ts 里:用户注册、支付 webhook、商品搜索全挤在一起。内联的鉴权检查被复制粘贴到了 40 个处理函数里。没人愿意动它,因为对订单逻辑的任何改动,都可能把上面三百行的用户相关接口改坏。这个文件顶部通常长这样:src/routes/index.ts (before — 2,000 lines)
import { Router } from "express";
import { db } from "../db";
import { stripe } from "../lib/stripe";
import { sendEmail } from "../lib/email";
import { logger } from "../lib/logger";
const router = Router();
// ---- 认证中间件(到处复制粘贴)----
const requireAuth = (req, res, next) => {
const token = req.headers.authorization?.split(" ")[1];
if (!token) return res.status(401).json({ error: "Unauthorized" });
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch {
res.status(401).json({ error: "Invalid token" });
}
};
// ---- 用户路由 ----
router.post("/users/register", async (req, res) => { /* 45 lines */ });
router.post("/users/login", async (req, res) => { /* 30 lines */ });
router.get("/users/:id", requireAuth, async (req, res) => { /* 25 lines */ });
router.put("/users/:id", requireAuth, async (req, res) => { /* 40 lines */ });
// ---- 产品路由 ----
router.get("/products", async (req, res) => { /* 60 lines */ });
router.get("/products/:id", async (req, res) => { /* 35 lines */ });
router.post("/products", requireAuth, async (req, res) => { /* 50 lines */ });
// ---- 订单路由 ----
router.post("/orders", requireAuth, async (req, res) => { /* 80 lines */ });
router.get("/orders/:id", requireAuth, async (req, res) => { /* 40 lines */ });
router.post("/orders/:id/refund", requireAuth, async (req, res) => { /* 55 lines */ });
// ---- 支付路由 ----
router.post("/payments/webhook", async (req, res) => { /* 90 lines */ });
router.get("/payments/:id/status", requireAuth, async (req, res) => { /* 30 lines */ });
// ... 还有 1,400 行混合处理器、内联校验,
// 以及重复的认证检查
export default router;
明确告诉 Devin 你希望目标结构是什么样的。 用规范引导 Devin
Devin 会阅读你的 codebase 以推断出 patterns,但在重构场景中,Knowledge 条目能带来最大的价值。为 Devin 需要遵循的规范添加条目:
- Router patterns — “每个业务域的 router 都使用
Router(),并在应用根部通过 app.use('/domain', domainRouter) 挂载”
- Middleware — “认证中间件位于
src/middleware/ 中,并且始终通过导入使用,绝不内联定义”
- Error handling — “所有路由处理函数都使用来自
src/lib/asyncHandler.ts 的 asyncHandler 包装器——绝不直接使用 try/catch”
让 Devin 参考你 codebase 中一个已经结构良好的 router,通常比从零开始用文字描述规范效果更好。在你的提示中加上一句类似 “遵循 src/routes/admin.ts 中的模式,该文件已经实现了清晰的结构分离”。你也可以让 Devin 为你生成 Knowledge 条目——只需描述你的规范,它就会创建结构良好的条目,供你审阅和保存。 审查 Devin 的拉取请求(PR)
Devin 会为每个端点建立映射,跟踪导入关系图,提取共享逻辑,创建领域文件,重新配置根路由,并运行你的测试套件。一个典型的 PR 大致如下:refactor: Split monolithic router into domain-specific route files
Files changed (8):
src/routes/users.ts — 4 endpoints, auth middleware imported
src/routes/products.ts — 3 endpoints, public + auth-protected
src/routes/orders.ts — 3 endpoints, all auth-protected
src/routes/payments.ts — 2 endpoints, webhook + status check
src/routes/index.ts — root router mounting all domains
src/middleware/auth.ts — requireAuth, requireAdmin (extracted)
src/middleware/validate.ts — validateBody schema middleware
Old src/routes/index.ts — 2,000-line monolith replaced
All 112 API tests pass. No URL changes.
这是拆分后精简的根路由配置:src/routes/index.ts (after — 15 lines)
import { Router } from "express";
import usersRouter from "./users";
import productsRouter from "./products";
import ordersRouter from "./orders";
import paymentsRouter from "./payments";
const router = Router();
router.use("/users", usersRouter);
router.use("/products", productsRouter);
router.use("/orders", ordersRouter);
router.use("/payments", paymentsRouter);
export default router;
以及一个已正确导入共享中间件的域路由文件:src/routes/orders.ts (after — excerpt)
import { Router } from "express";
import { requireAuth } from "../middleware/auth";
import { validateBody } from "../middleware/validate";
import { createOrderSchema, refundSchema } from "../schemas/orders";
import { db } from "../db";
const router = Router();
router.post("/", requireAuth, validateBody(createOrderSchema),
async (req, res) => {
const order = await db.orders.create({
userId: req.user.id,
items: req.body.items,
total: req.body.total,
});
res.status(201).json(order);
}
);
router.get("/:id", requireAuth, async (req, res) => {
const order = await db.orders.findByPk(req.params.id);
if (!order) return res.status(404).json({ error: "Order not found" });
res.json(order);
});
router.post("/:id/refund", requireAuth, validateBody(refundSchema),
async (req, res) => {
// 退款逻辑已从单体应用中干净地提取出来
}
);
export default router;
所有 URL 路径保持不变 —— /orders 现在由挂载在 /orders 下的 ordersRouter 处理,因此现有客户端和测试无需任何改动即可继续工作。 (可选)检出该分支并在本地测试
对于这样的结构性重构,建议先拉取该分支,在本地验证无误后再合并。你可以在 Windsurf 或你常用的 IDE 中打开项目,启动应用,并访问几个接口,确认路由、中间件和错误处理的行为与之前完全一致。git fetch origin && git checkout devin/refactor-router-split
npm install && npm test
npm run dev
# 访问几个端点:curl http://localhost:3000/users, /orders, /payments
如果你发现哪里不对,请在该 PR 上留言——Devin 会注意到并推送修复。 继续清理
路由拆分完成后,使用后续提示继续推进这次重构: