Skip to content

第 29 章 接单开发网站

第 28 章把分而治之搬到了写作上,本章把它搬到了商业交付上:接单做网站不是「聊一个做一个」,而是把整条交付链——从需求、报价、合同,到开发、验收、部署、维护——固化成可复用的脚手架 + 命令 + 子代理 + SOP。素材全部来自一套已落地的 web-studio 工作流,不是凑出来的案例。

29.1 结论:接单的核心是把交付流程固化成一条龙

用 Claude Code 接单开发网站,核心策略只有一句话:把交付流程固化成「脚手架 + 命令 + 子代理 + SOP + Docker 部署」一条龙,每个客户项目都是这条流水线的一次实例化

这条策略解决了接单开发的三个根本矛盾:

  1. 重复矛盾:每个客户项目都要从零搭脚手架、配数据库、写后台、部署上线,80% 的工作是重复劳动。固化脚手架把这部分成本摊到一次。
  2. 质量矛盾:一人接单最大风险是「这单做完了下一单质量下滑」。固化命令与子代理把质量标准从「我当时状态」变成「流水线默认值」。
  3. 边界矛盾:接单最容易扯皮的是「这个算不算范围内」「维护包不包含改文案」。固化 SOP 与商务文档把边界写死,减少纠纷。

本章给出的 web-studio 工作流已落地在 /Users/mba/claude/web-studio/,主打企业官网/落地页,全包交付(开发 + 部署 + 维护)。技术栈:Next.js 16 (App Router) + React 19 + Prisma 7 + PostgreSQL 16 + NextAuth v5 + shadcn/ui + Tailwind v4 + Docker(standalone + Caddy)。

本章与第 06 章的可执行闭环、第 19 章的子代理一脉相承:整条流水线就是把闭环七步(限定范围 → 事实模型 → 修改 → 验证 → 读失败 → 自我修正 → 人类审查)从「一次任务」放大到「一次交付」。

29.2 web-studio 三件套总览

text
web-studio/
├── template/          # ① 全栈脚手架(新单复制其副本)
│   ├── src/           #    前台 (site) + 后台 (admin) + actions + lib
│   ├── prisma/        #    schema.prisma + seed.ts
│   ├── scripts/       #    backup.sh / restore.sh / deploy.sh / migrate.sh
│   ├── Dockerfile     #    多阶段 standalone
│   ├── docker-compose.yml  # app + db + caddy
│   ├── Caddyfile      # 自动 HTTPS
│   ├── CLAUDE.md      # 工程约定(规则至上)
│   └── AGENTS.md      # Next.js 16 breaking changes
├── commands/web/      # ② 9 个 /web:* slash command
├── agents/            # ② 3 个子代理:ui-builder / api-builder / qa-reviewer
├── docs/              # ③ 商务文档模板(报价/合同/验收/维护/迁移)
├── SOP.md             # ③ 端到端接单流程
└── install.sh         # 把命令/agent 软链到 ~/.claude/

三件套对应三个固化维度:代码固化(template)、操作固化(commands + agents)、商务固化(SOP + docs)。三者缺一不可:只有脚手架没有命令,Claude 每次都要重新理解项目;只有命令没有 SOP,技术做完了但商务扯皮;只有 SOP 没有脚手架,每单都从零开始。

安装一次,全局可用:

bash
bash /Users/mba/claude/web-studio/install.sh

install.sh 用软链而非复制——改源文件即同步生效,便于升级。安装后重启 Claude Code,/web:* 命令与三个子代理在任意客户项目目录都可用。

预期输出:

text
✓ 已安装 slash commands(/web:new 等):
new.md  spec.md  model.md  ui.md  admin.md  check.md  up.md  deploy.md  handoff.md
✓ 已安装 subagents(ui-builder 等):
api-builder.md  qa-reviewer.md  ui-builder.md
重启 Claude Code(或新开会话)后即可使用 /web:<name>。

验证:ls ~/.claude/commands/web/ls ~/.claude/agents/ 看到软链;新会话里 /agents 列出三个子代理。

失败边界:不要把 commands/web/*.md 裸拷进 ~/.claude/commands/——会丢失 namespace 保护,可能与未来内置 /web 冲突。install.shcommands/web/ 子目录正是为了强制 web: namespace(第 16 章的 namespace 纪律)。

29.3 脚手架:Next.js 16 全栈 + Prisma 7 driver adapter

脚手架是流水线的物理基础。template/ 不是空目录,而是一个已通过 tsc + eslint + prisma generate/validate 的最小可运行全栈应用:前台 6 页(首页/关于/服务/案例/新闻/联系)+ 后台 CMS 增删改查 + NextAuth 登录 + SEO(sitemap/robots)+ seed + Docker 部署。业务模型已预置:SiteSetting / Page / Post / Product / Work / Inquiry / User

技术栈选型理由

选型理由
框架Next.js 16 App Router全栈一体,server actions 免写 API 路由,SSR/SSG 对 SEO 友好
UIshadcn/ui + Tailwind v4复制源码进项目,可定制无锁定;企业官网审美门槛低
数据库PostgreSQL 16 + Prisma 7关系型契合 CMS 结构;Prisma 类型安全
鉴权NextAuth v5(credentials + jwt)仅后台 CMS 需登录,前台免登录,最小化攻击面
部署Docker standalone + Caddystandalone 镜像轻量;Caddy 自动 HTTPS,免证书运维

两个非显然的坑

坑一:Prisma 7 用 driver adapter,不是 PrismaPostgres。Prisma 7 的 PostgreSQL driver adapter 类名是 PrismaPg,从 @prisma/adapter-pg 导入,传 { connectionString }:

ts
// src/lib/db.ts
import { PrismaClient } from "@/generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";

const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL! });
export const prisma = globalForPrisma.prisma ?? new PrismaClient({ adapter });

脚手架里的 AGENTS.md 早期版本写成 PrismaPostgres,首次 tsc 报错已修正。这个坑很隐蔽——Prisma 5/6 训练数据里的写法不适用,模型会反复回退到旧 API。AGENTS.md 必须把正确写法钉死,子代理开工前必读

坑二:Next.js 16 的 params/searchParams 是 Promise。这是 Next 16 的 breaking change,动态路由须 await:

tsx
// src/app/(site)/news/[slug]/page.tsx
export default async function Page({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;  // 必须 await
  const post = await prisma.post.findUnique({ where: { slug } });
  // ...
}

AGENTS.md 里钉死这条:「params/searchParams 是 Promise,须 await;不确定的 API 先查 node_modules/next/dist/docs/」。这正是第 06 章「命令真实性」的延伸——不照搬训练数据,以运行时版本为准。

脚手架本地验证

bash
cd /Users/mba/claude/web-studio/template
cp .env.example .env
docker compose up -d --wait db
npm install
npx prisma migrate dev --name init
npm run db:seed
npm run dev   # http://localhost:3000  后台 /admin

验证命令:npx tsc --noEmit && npm run lint && npm run build。tsc 与 lint 必过;build 在国内可能因 Docker Hub(registry-1.docker.io)拉取受阻失败——需在 Docker Desktop → Settings → Docker Engine 的 registry-mirrors 配镜像加速器(如 docker.m.daocloud.io)。

失败边界:脚手架与客户需求不匹配是最大风险。本脚手架强项是「企业官网/落地页 + 轻量 CMS」,遇到复杂 SaaS、电商、多语言、支付分账,要评估接或转介,不要硬塞脚手架。SOP 第 1 步(线索与初评)就是干这件事。

29.4 9 个 /web:* 命令:从脚手架到部署

9 个命令对应交付链的 9 个节点。顺序链:

text
/web:new → /web:spec → /web:model → /web:ui → /web:admin
        → /web:check → /web:up → /web:deploy → /web:handoff
命令用途产出委托
/web:new <项目名>接单初始化:复制脚手架、init git、草拟 PRD客户项目目录 + docs/PRD.draft.md主代理
/web:spec从草稿生成结构化 PRD + 报价单docs/PRD.md + docs/QUOTE.md主代理
/web:model按 PRD 增删改 Prisma schema + 迁移prisma/schema.prisma + migration主代理
/web:ui前台页面与组件src/app/(site)/* + src/components/site/*ui-builder
/web:admin后台 CMS 与鉴权src/actions/cms.ts + src/app/(admin)/*api-builder
/web:check交付前自检(类型/构建/安全/SEO)docs/CHECKLIST.mdqa-reviewer
/web:up本地起完整环境db + app + caddy 运行态主代理
/web:deploy上线部署(需人工二次确认)docs/DEPLOY.md主代理
/web:handoff生成交付包docs/handoff/* + 源码包主代理

关键命令示例:/web:check

/web:check 是第 06 章可执行闭环的典型落地——它把「完成」从一句话变成 6 项机器可检查状态:

text
执行(委托 qa-reviewer):
1. 类型与规范:npx tsc --noEmit、npm run lint 必须通过
2. 构建:npm run build 必须通过(需 db 运行)
3. 安全 checklist:
   - .env 未进版本控制;无硬编码密钥
   - 后台所有写操作有鉴权(middleware + requireAdmin)
   - 表单输入经 zod 校验
   - 无未消毒的 dangerouslySetInnerHTML
4. 响应式:关键页面移动端可用
5. SEO:每页 metadata、sitemap、robots 正确
6. 内容:占位素材/文案待替换项已列出

产出:docs/CHECKLIST.md — 逐项结果 + 阻塞项清单
阻塞项全部清零后方可 /web:deploy

预期输出:docs/CHECKLIST.md,每项 ✅/❌ + 证据 + 阻塞项清单。

验证:cat docs/CHECKLIST.md 看到逐项结果;阻塞项计数为 0。

失败边界:阻塞项未清零就进 /web:deploy 是最贵的返工——线上修 Bug 比本地贵 10 倍。/web:deploy 命令自身会校验 CHECKLIST.md 无阻塞项,作为第二道闸门。

关键命令示例:/web:deploy(红线命令)

/web:deploy 是典型的红线命令——执行到「准备就绪」即停,列出将执行的生产命令,等用户明确确认:

text
⚠️ 推送到生产属用户红线。本命令执行到「准备就绪」即停,
列出将执行的命令,等待用户明确确认后再执行。

执行(准备阶段):
1. 校验 .env:AUTH_SECRET 已生成(非占位);
   NEXTAUTH_URL/SITE_URL/SITE_DOMAIN 为生产域名
2. 校验 docs/CHECKLIST.md 无阻塞项
3. 备份(若已有数据):bash scripts/backup.sh
4. 列出将执行的生产命令:
   bash scripts/deploy.sh(git pull → pnpm install
   → prisma migrate deploy → docker compose build → up -d)
5. 停下来,向用户展示命令清单,请求确认

红线:未确认前不执行任何推送/上线命令;部署前必须 backup.sh

这呼应了 CLAUDE.md 的红线边界:部署到生产环境即使在 auto-accept 模式下也必须绝对中止并向用户二次确认。命令的真实形态不是「一键部署」,而是「一键准备 + 人工确认 + 执行」。

29.5 3 个子代理:各司其职

三个子代理是流水线的「工位」。每个工位有明确的职责边界、开工前必读、完成后自证。这是第 19 章子代理的实战展开。

ui-builder:前台 UI

yaml
职责: src/app/(site)/* 前台页面 + src/components/site/* 业务组件
开工前必读: CLAUDE.md、AGENTS.md(Next 16 breaking changes)
工具: Read, Write, Edit, Glob, Grep, Bash
约束: 不写后台代码(交给 api-builder);占位素材不外链版权图
自证: npx tsc --noEmit

api-builder:后台 CMS 与数据层

yaml
职责: server actions(CRUD,经 requireAdmin)+ src/app/(admin)/*
开工前必读: CLAUDE.md、AGENTS.md、prisma/schema.prisma、src/actions/cms.ts
工具: Read, Write, Edit, Glob, Grep, Bash
约束: Prisma 7 用 PrismaPg;改 schema 必须 migrate + generate;
      后台路径受 middleware 保护,写操作二次 requireAdmin
自证: npx tsc --noEmit

qa-reviewer:交付前质量把关

yaml
职责: 类型/构建/安全/响应式/SEO checklist
开工前必读: 无(只读核验)
工具: Read, Glob, Grep, Bash(无 Write/Edit)
约束: 默认怀疑,不给「差不多就行」
产出: docs/CHECKLIST.md

三个设计要点

要点一:工具权限最小化qa-reviewer 不给 Write/Edit——它是裁判,不能下场踢球。这是第 05 章权限模型在子代理层的延伸:读操作放开,写操作按职责收紧。

要点二:开工前必读。每个子代理 frontmatter 下第一段就是「开工前必读」,强制读 CLAUDE.mdAGENTS.md。这把第 06 章「用 CLAUDE.md 提供稳定上下文」落到了子代理层——子代理不继承父对话历史,必须自己读项目约定。

要点三:完成后自证ui-builderapi-builder 都以 npx tsc --noEmit 自证。这不是装饰——子代理返回「我做完了」不算数,类型检查通过才算数。可执行闭环的第七步(人类审查)由 qa-reviewer 接棒。

失败边界:agent 越权ui-builder 跑去改后台代码、api-builder 跑去改 UI、qa-reviewer 自己动手修 Bug——这都是越权。frontmatter 的 description 与「职责」段是契约,主代理派任务时要把边界复述一遍。发现越权立即叫停,回退到第 06 章闭环的步骤 1(重新限定范围)。

29.6 SOP:需求 → 报价 → 合同 → 开发 → 验收 → 部署 → 维护

SOP.md 把交付链拆成 10 阶段,每阶段标注命令 / 产出 / 检查 / 红线。这是第 06 章可执行闭环的工程化放大:闭环七步从「一次任务」放大到「一次交付」,每阶段都是一个迷你闭环。

阶段命令产出红线
0. 前置(一次)install.sh命令/agent 装好Docker Desktop 可用
1. 线索与初评接/不接判断不轻易承诺范围与工期
2. 需求确认/web:specPRD.md + QUOTE.mdPRD 必须有「验收标准」与「不在范围内」
3. 报价与合同合同签署未收定金不开工
4. 初始化/web:new项目目录 + git不改 template 本身
5. 开发/web:model/web:ui/web:admin代码每模块完成跑 tsc --noEmit
6. 自检/web:checkCHECKLIST.md阻塞项未清零不验收/部署
7. 客户验收验收确认范围外走变更计费
8. 部署/web:deployDEPLOY.md部署前 backup;需人工确认
9. 交付/web:handoffdocs/handoff/*凭据加密渠道交付,不入 git
10. 收款与维护收尾款 + 维护月费维护边界以 docs/维护月费范围.md 为准

阶段 2 的关键:PRD 必须有边界

/web:spec 生成的 docs/PRD.md 强制包含两节:验收标准(逐条可勾选)与不在范围内(明确边界,防需求蔓延)。这两节是后续验收与变更计费的依据。没有这两节,客户可以说「这个不就算送的吗」,你没有反驳依据。

阶段 3 的红线:未收定金不开工

这是商业红线,不是技术红线。SOP.md 明确:付款节点为定金 40% → 中期 30% → 尾款 30%,开工以收到定金为准。即使 Claude Code 让你「一天就能出原型」,也不代表你可以免定金开工——定金是诚意金,不是工钱。

29.7 商务要素:报价/合同/验收/维护月费

docs/ 下五份模板把商务固化。这不是法律文本,是核对清单——用客户或平台的标准合同,以此清单核对遗漏。

报价单:WBS + 付款节点 + 变更计费

报价单核心是工作分解(WBS),基准工时(企业官网/落地页):

模块人日
需求/设计1–2
前台页面0.5–1/页
后台 CMS2–4
表单/留言0.5
SEO0.5
部署上线1
培训/交付0.5

加维护月费选项(全包托管),不含项(域名、服务器、素材版权),变更计费规则(范围外按人日计)。这三段——不含项、变更计费、维护边界——是防扯皮的三道闸门。

合同要点:九节核对清单

合同要点分九节:标的与范围、价款与付款、工期与里程碑、知识产权、维护、变更、验收、违约与终止、保密与数据。其中两节最常被忽略:

知识产权:自定义业务源码交付客户;开发者保留脚手架/通用模块复用权与作品集展示权(脱敏)。这一条是「一人公司」复用模式的法律基础——web-studio/template 本身是你的资产,不随单交付。

验收:以 PRD「验收标准」为准;客户约定期限内不反馈视为通过。这一条防止「客户拖着不验收也不付尾款」。

维护月费:标准维护 + 不含项 + SLA

维护月费模板明确「含」与「不含」:

  • :内容更新(限 N 篇/月)、Bug 修复、定期备份、可用性监控、SSL 续期、安全补丁
  • 不含(另计):新功能开发、大规模改版、内容批量录入、第三方服务费用

SLA:响应工作日 X 小时、紧急故障 X 小时、可用性 99.5%。续费按月/季/年预付,逾期暂停维护。

失败边界:维护边界不清。最典型的扯皮是「改个文案算不算维护」「加个 banner 算不算新功能」。模板用「限 N 篇/月」「新功能另计」把灰色地带量化,但 N 必须在签维护合同时填死,不能口头约定。

29.8 Docker 部署:compose + Caddyfile

部署是流水线的最后一公里,也是客户最关心的「能不能上线」。

docker-compose:app + db + caddy 三件套

yaml
services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
  app:
    build: .
    depends_on:
      db:
        condition: service_healthy
    environment:
      DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public
      AUTH_SECRET: ${AUTH_SECRET}
  caddy:
    image: caddy:2-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro

本地与生产共用一份 compose,差异仅在 .envCaddyfileSITE_DOMAIN。本地 SITE_DOMAIN 缺省走 localhost(http),生产设为真实域名(Caddy 自动签 HTTPS)。这是「一份配置两种环境」的极简实现。

Dockerfile:多阶段 standalone

多阶段构建,最终镜像只含 node server.js + standalone 产物 + static 资源,无 prisma CLI、无 node_modules 全量。迁移由 scripts/deploy.sh 在服务器上用 prisma migrate deploy 执行,不进容器——容器是无状态的,迁移是有状态的,两者必须分离。

prisma generate 在 builder 阶段跑,用占位 DATABASE_URL(不连库);运行期由 compose 注入真实值。这是 Prisma 7 driver adapter 模式的正确姿势——adapter 在运行期才创建连接。

Caddyfile:自动 HTTPS

text
{$SITE_DOMAIN:localhost} {
    encode gzip zstd
    reverse_proxy app:3000
}

两行配置,localhost 走 http,真实域名自动申请并续期 Let's Encrypt 证书。Caddy 比手动 certbot 省一个运维项,对一人公司接单是最佳选择。

迁移:换服务器标准流程

客户换服务器是接单常见需求,docs/迁移手册.md 标准化八步:

text
1. 旧服务器 bash scripts/backup.sh → backups/db-YYYYMMDD-HHMMSS.sql.gz
2. 上传新服务器:git clone + .env + public/uploads + backups/xxx.sql.gz
3. docker compose up -d db
4. bash scripts/restore.sh backups/xxx.sql.gz
5. bash scripts/deploy.sh  (migrate deploy + build + up)
6. DNS A 记录指向新 IP
7. 验证:访问域名、登录后台、核对数据
8. 确认无误后停旧实例

回滚:迁移失败,DNS 切回旧 IP(旧实例未停即可恢复)。

验证:curl -I https://域名 返回 200;后台登录正常;数据库记录数与旧服务器一致。

失败边界:部署配置泄密.envAUTH_SECRET、数据库密码、管理员密码,绝不进 git、不进镜像、不进日志。Dockerfile 不 COPY .env;.gitignore 必含 .env;/web:handoff 的账号交接表单独加密渠道交付。AUTH_SECRETopenssl rand -base64 32 生成,每个客户独立。一次泄密毁所有客户信任。

29.9 验证与失败边界

验证矩阵

整条流水线的验证分三层:

命令通过标准
类型npx tsc --noEmit0 error
规范npm run lint0 error(eslint flat config)
构建npm run build成功(需 db 运行)
安全/web:checkCHECKLIST.md 阻塞项为 0
部署curl -I https://域名200 + HTTPS 有效

ui-builderapi-builder 每次完成后跑第一层;qa-reviewer 跑前三层 + 安全 checklist;/web:deploy 上线后跑第五层。三层之间不替代——类型过不等于构建过,构建过不等于安全过。

四类失败边界

一、脚手架与客户需求不匹配。本脚手架强项是企业官网/落地页 + 轻量 CMS。遇到复杂 SaaS(多租户、工作流)、电商(库存、支付、对账)、多语言(i18n 路由)、社交(实时、用户关系),要评估接或转介。硬塞脚手架的结果是:80% 的时间在改脚手架的预设模型,反而比从零写更慢。SOP 第 1 步(线索与初评)就是干这件事,不要跳过。

二、agent 越权ui-builder 改后台、api-builder 改 UI、qa-reviewer 动手修 Bug。子代理的 frontmatter description 与「职责」段是契约,主代理派任务时把边界复述一遍。发现越权立即叫停,回退到闭环步骤 1。预防手段:工具权限最小化(qa-reviewer 不给 Write/Edit)。

三、部署配置泄密.env 入 git、AUTH_SECRET 用占位上线、数据库密码硬编码、账号表明文交付——任何一条都是红线。/web:check 会扫「.env 未进版本控制、无硬编码密钥」,但这是机器检查,人工复核不可省。/web:handoff 的账号交接表必须加密渠道单独交付,提醒客户改密。

四、维护边界不清。「改个文案算不算维护」「加个 banner 算不算新功能」「服务器挂了算不算故障」——没有 docs/维护月费范围.md 锁死边界,这些问题会耗掉你所有利润。维护合同必须填死:N 篇/月、SLA 响应时间、可用性目标、不含项清单。口头约定等于没约定。

一条容易被忽略的边界:作品集展示权

合同要点里有一条「开发者保留作品集展示权(脱敏)」。这是「一人公司」复用模式的法律基础——你做的每一个客户项目,都可以(脱敏后)放进作品集,作为接下一单的信用背书。没有这一条,客户可以以「保密」为由阻止你展示,你的获客成本就会越来越高。这一条必须在签合同时写入,不要事后补。

29.10 小结

接单开发网站的 Claude Code 用法,可以浓缩成一句:

脚手架固化代码,命令固化操作,子代理固化质量,SOP 固化商务,Docker 固化部署。

五层固化,每层都把「我当时状态」换成「流水线默认值」。这正是一人公司对抗产能波动的唯一办法——你状态最好的时候把流程做对,状态下滑时流水线替你兜底。

本章与第 06 章的可执行闭环、第 19 章的子代理是同一条主线:闭环七步从「一次任务」放大到「一次交付」;子代理从「主代理的助手」升级为「流水线的工位」。第 32 章会从成本角度重新审视这条流水线——每个子代理的 token 消耗、每次 /web:check 的迭代次数,都会进到接单的成本核算里。


上一章:第 28 章 内容生产工作流


下一章:一人公司系统性框架 | 返回目录