摘要:
本来以为是简单的 CRUD,结果差点死在 Docker 部署上。本文记录了一个全栈 AI 应用从 0 到 1 的完整过程,重点复盘 Next.js Standalone 模式下的 Prisma 迁移、DeepSeek 集成以及 Docker 多阶段构建的血泪史。附最终完美运行的 Dockerfile 和 Compose 配置。
🎙️ 前言
现在是 2026 年,全栈开发的门槛看似降低了,但“陷阱”却更加隐蔽了。
最近心血来潮,想做一个**“CashFlow AI 智能记账本”**。技术栈选型极其现代化:
- 框架:Next.js 16 (App Router)
- 数据库:Prisma 7 + SQLite (本地) / PostgreSQL (生产)
- AI:DeepSeek V3 (国产之光)
- 部署:Docker Compose + 自建 VPS
本来以为两天搞定,结果在部署环节硬生生卡了三天(可能是我比较菜)。今天把这些坑填平了,整理出来,希望后人不要再踩。
🕳️ 坑位一:Prisma 7 的“精神分裂”与 P1012 报错
现象:
在 Prisma 7 中,为了支持 Serverless 环境,官方不再建议在 schema.prisma 里直接写数据库 URL。于是我配置了 prisma.config.ts。
结果本地开发好好的,一上 Docker 就报 P1012,或者报 datasources property unknown。
避坑指南:
在 Docker/VPS 这种 Node.js 原生环境下,千万别整那些花里胡哨的 Adapter(适配器模式)。
- 删掉 prisma.config.ts。
- 回归 schema.prisma 里的 url = env("DATABASE_URL")。
- 代码里直接 new PrismaClient(),零参数启动。
结论: 简单的才是最稳的。
🕳️ 坑位二:DeepSeek 集成时的 Vercel SDK 404
现象:
使用 Vercel AI SDK 集成 DeepSeek 时,直接报错 404 Not Found 或者 response_format unavailable。原因是 SDK 默认拼接的路径 DeepSeek 不认,或者 SDK 强制要求的 JSON Mode 参数 DeepSeek 暂时不支持。
避坑指南:
- 手动挡最稳:不要用 generateObject 这种魔法方法,改用 generateText。
- Prompt 工程:在 System Prompt 里强行要求 AI 输出纯 JSON,然后在代码里 JSON.parse。
- 超时控制:国内网络环境波动大,务必在 fetch 中注入 AbortController,把超时时间拉长到 60s。
🕳️ 坑位三:Next.js Standalone 模式“扔掉了”我的 Prisma
这是最大的坑,卡了我整整一天。
现象:
Docker 构建成功了,但运行 prisma migrate deploy 时报错:Cannot find module 'prisma/config'。
原因:
Next.js 的 output: "standalone" 模式非常智能,打包时会自动分析依赖,把没用到的包扔掉。
不幸的是,我们的业务代码只引用了 @prisma/client,没引用 prisma CLI 工具。所以 Prisma CLI 被 Next.js 当作垃圾扔掉了。
避坑指南(终极 Dockerfile 写法):
必须在 Dockerfile 的最后阶段,手动把被扔掉的包拷回来,或者强行安装。
最优雅的解法是:把 prisma 包从 devDependencies 移到 dependencies,这样 Next.js 就会乖乖把它打包进去了。
🕳️ 坑位四:Docker 多阶段构建找不到 pnpm
现象:
构建时报错 /bin/sh: pnpm: not found。
原因:
我在 deps 阶段装了 pnpm,以为全局通用。但 Docker 的多阶段构建(Multi-stage)是隔离的,builder 阶段并不继承 deps 阶段的全局工具。
避坑指南:
把 npm install -g pnpm 放到最基础的 base 镜像阶段,一劳永逸。
🏆 最终成果:完美的架构方案
经过无数次报错(ENOTFOUND, ERESOLVE, P1001...),我总结出了一套**“生产级”**的部署配置。
1. 完美的 Dockerfile
支持国内镜像源、支持 lock 文件一致性校验、支持 Prisma 迁移。
codeDockerfile
FROM node:20-alpine AS base
WORKDIR /app
# 关键:在这里装 pnpm,所有阶段都能用
RUN npm install -g pnpm
# 1. 依赖阶段
FROM base AS deps
RUN npm config set registry https://registry.npmmirror.com
COPY package.json pnpm-lock.yaml* ./
# 关键:使用 --no-frozen-lockfile 允许自动修复锁文件冲突
RUN pnpm install --no-frozen-lockfile
# 2. 构建阶段
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# 注入假 URL 骗过 Prisma 生成检查
ENV DATABASE_URL="postgresql://dummy:dummy@localhost:5432/dummy"
RUN pnpm prisma generate
RUN pnpm build --no-lint
# 3. 运行阶段
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# ... (用户权限配置略)
# 关键:把 Prisma 的配置文件手动拷进去
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma
# ... (其他 COPY 略)
CMD ["node", "server.js"]
2. 优雅的 Docker Compose 编排
这里有一个架构上的升华:
不要在 App 容器里手动跑迁移,也不要手动 exec。
我们引入一个专门的 migrate 容器(工程车),利用 depends_on 和 healthcheck 实现自动化流水线。
codeYaml
version: "3.9"
services:
db:
image: postgres:15-alpine
# ... (配置略)
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 5
# 专门负责建表的"工程车"
migrate:
build:
context: .
target: builder # 关键:使用 builder 阶段的镜像,环境全
command: npx prisma migrate deploy
depends_on:
db:
condition: service_healthy
# 真正的应用
app:
build: .
depends_on:
migrate:
condition: service_completed_successfully # 等表建好了再启动
# ...
🔚 总结
全栈开发不仅仅是写代码,环境治理和部署架构往往占了 50% 的难度。
这三天的折腾,虽然掉了不少头发,但换来了:
- 一套极低成本的 AI SaaS 架构(0 元数据库,0 元部署)。
- 对 Next.js 构建机制的深度理解。
- 一套可复用的工业级 Docker 配置。