很多团队在接触 Claude Code 时,第一反应是“让它帮我写个网站”。实际用下来你会发现,真正容易翻车的不是“能不能写”,而是“怎么让它按工程标准写”:目录怎么分层、密钥放哪里、登录态怎么刷新、上传的视频转码后存哪、部署到服务器后 CI 怎么跑。这篇文章就用一条完整的实战主线来拆开讲:用 Claude Code 从零搭一个带后端鉴权、数据库、多媒体处理的商业级网站,并且配上自动化部署。
我会把任务拆成一个真实工程来推进:Node.js 后端 + MySQL + ffmpeg 多媒体处理 + JWT 登录鉴权 + GitHub Actions 自动部署。为了让 Claude Code 不“自由发挥”,每一阶段都会先给出约束条件、再给提示词工作流、最后补验证方式。这样你照着做,既能看到 AI 编码的上限,也知道哪些环节必须人来兜底。
如果你已经装了 Claude Code,可以直接跳到工程初始化部分;如果还没装,先看环境准备。全程不追求把代码堆得花里胡哨,重点是把“能用、可维护、可部署”这条链路走通。
1. Claude Code 核心能力与项目定位
Claude Code 是 Anthropic 推出的命令行 AI 开发代理工具,它不是在网页里和你聊代码,而是直接进入终端、读取你的工程目录、修改文件、执行命令。也就是说,它能基于真实代码库完成任务,而不是基于一段上下文猜测代码。
| 能力项 | 说明 |
|---|---|
| 交互方式 | 终端命令行交互,在项目目录内运行 |
| 工程能力 | 阅读工程结构、定位代码、跨文件修改、调用构建与运行命令 |
| 项目记忆 | 通过CLAUDE.md约定技术栈、命名规范、业务规则,避免每次重新交代 |
| 安装门槛 | 依赖 Node.js,官方提供 npm 安装方式 |
| 适合任务 | 全栈功能开发、代码重构、数据库脚本、自动化部署配置、测试补全 |
| 不适合任务 | 未经确认的全局大重构、生产环境直接改库、完全替代人工 Code Review |
这个项目定位很明确:不是让你把 Claude Code 当“自动生成器”用,而是把它当“按需求施工的高级工程师”。你负责拆需求、定边界、做审查,它负责把接口、数据表、中间件、部署脚本这些琐碎但量大的工作落地。
商业级网站意味着三个硬指标:第一,登录鉴权必须安全,不能把用户密码明文存库;第二,数据库结构必须清晰,后续加字段不能牵一发动全身;第三,部署必须可重复,换一台服务器也能自动跑通。这三条正好是 Claude Code 能切入的发力点。
2. 商业网站模块划分与交付顺序
建议先构建一个“用户上传并管理多媒体资源”的内容平台,它同时覆盖了登录鉴权、用户体系、内容管理、文件存储和视频转码。这个模型很典型:用户注册、登录、上传图片/视频、查看自己的资源列表、删除资源,后台按角色区分普通用户和管理员。
模块拆分如下:
| 模块 | 核心职责 | 技术选型示例 |
|---|---|---|
| 用户认证 | 注册、登录、访问令牌刷新 | JWT + 刷新令牌 |
| 权限控制 | 区分普通用户与管理员 | RBAC 或简单角色字段 |
| 数据库层 | 用户、角色、媒体资源、令牌存储 | MySQL 8.x |
| 对象/文件存储 | 图片、视频文件保存与访问 | 本地磁盘或云存储 |
| 多媒体处理 | 视频转码、封面抽取、图片压缩 | ffmpeg + ffprobe |
| 后端 API | 暴露登录、资源上传、列表接口 | Node.js + TypeScript + Express |
| 自动化部署 | 推送代码后自动构建并发布 | GitHub Actions + PM2 + Nginx |
交付顺序建议按“先不稳定后稳定”的依赖链走:先搭数据库和连接层,再做用户注册登录,然后做鉴权中间件,再写多媒体上传与转码,最后接自动化部署。这样前一步的产物是后一步的输入,验证路径最短。
让 Claude Code 干活前,必须先把技术栈和目录结构定死。最好的办法不是口头描述,而是直接在仓库里放一个CLAUDE.md。这个文件会被 Claude Code 在会话开始时读取,相当于项目的“施工说明书”。
3. 本地环境与 Claude Code 安装
开始前需要准备以下环境:
| 依赖 | 用途 | 检查命令 |
|---|---|---|
| Node.js 18+ | 运行 Claude Code 与后端服务 | node -v |
| npm | 安装 Claude Code CLI | npm -v |
| Git | 管理代码变更 | git --version |
| MySQL 8.x | 后端数据库 | mysql --version |
| ffmpeg/ffprobe | 视频转码与信息探测 | ffmpeg -version |
| Claude Code | AI 编程代理 | claude --version |
安装 Claude Code 使用官方 npm 方式:
npm install -g @anthropic-ai/claude-code claude --version安装完成后,你需要有可用的认证凭据,通常是通过ANTHROPIC_API_KEY环境变量指定 API 密钥,或者在首次启动时按工具引导完成登录。
export ANTHROPIC_API_KEY="你的密钥"需要留意的是,这类编程代理会直接调用你的终端命令,因此建议在独立项目目录里运行,而不是在系统根目录或包含敏感文件的目录里随意使用。
ffmpeg 在不同操作系统下的安装方式不同,但装完后一定要验证两个命令都存在,因为后端的视频转码和封面生成依赖它们:
ffmpeg -version ffprobe -version终端没问题后,进入工作目录,执行claude即可进入交互模式。启动后,Claude Code 会扫描当前工作目录,如果项目刚初始化,它需要你对整体规划给出清晰指令。
4. 从零初始化工程:给 Claude Code 一份可执行的施工说明
很多项目翻车不是因为 AI 能力不够,而是第一步就给了一个过于宽泛的指令:“帮我做个网站”。正确做法是把目标拆成具体工程约束,再让 Claude Code 执行。
先手动创建项目根目录,并初始化 Git:
mkdir commerce-site cd commerce-site git init在启动 Claude Code 前,先把项目结构说明写进CLAUDE.md。这个文件不用很长,但必须写清楚技术栈、目录约定、命名规范、禁止事项。然后启动:
claude在会话里给出第一阶段指令,注意这里不要让它一口气写完整站:
我需要你帮我在当前目录下初始化一个全栈项目: 1. 后端使用 Node.js + TypeScript + Express。 2. 使用 MySQL 作为数据库,禁止直接使用 SQL 文件初始化,必须提供可重复执行的数据库迁移脚本。 3. 环境变量统一从 .env 读取,禁止把密钥硬编码到源码里。 4. 先搭建工程骨架:src/index.ts、src/config、src/routes、src/controllers、src/services、src/middlewares、migrations 目录。 5. 先不要写业务接口,完成目录结构和依赖安装即可。这一步的目的不是让 Claude Code 生成完整业务代码,而是先建立工程骨架。生成结果可以人工检查文件结构是否符合预期。建议的单仓库结构如下:
commerce-site/ ├── CLAUDE.md ├── .env.example ├── .gitignore ├── package.json ├── src/ │ ├── index.ts │ ├── config/ │ │ └── env.ts │ ├── middleware/ │ │ ├── auth.ts │ │ └── error.ts │ ├── routes/ │ │ ├── auth.ts │ │ ├── media.ts │ │ └── user.ts │ ├── service/ │ │ ├── auth.service.ts │ │ └── media.service.ts │ └── db/ │ └── pool.ts ├── migrations/ └── scripts/ └── deploy.sh骨架确认没问题后,让 Claude Code 在内存中形成“项目文档”,后续每一步都拿这个结构约束它。注意:AI 生成的代码不一定符合你团队规范,所以骨架阶段一定要看关键文件的实际内容,特别是package.json和.env.example。
5. 后端鉴权:从建表到 JWT 路由守卫
后端鉴权是商业级网站最核心的一条链路。用户注册、登录、访问令牌刷新、角色权限控制,任何一个环节松懈都可能导致越权或数据泄露。
5.1 数据库表结构设计
建议让 Claude Code 先设计用户基础表和角色表,并让它把“不存明文密码”写成表字段约束。下面是一组参考 DDL:
CREATE TABLE users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, email VARCHAR(190) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, nickname VARCHAR(60) NOT NULL DEFAULT '', role ENUM('user', 'admin') NOT NULL DEFAULT 'user', status TINYINT NOT NULL DEFAULT 1 COMMENT '1=正常 0=禁用', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;用户登录后如果使用 JWT,短期令牌通常不需要落库,但如果你需要支持主动踢人、续期,可以在表里加一个会话或刷新令牌表。
5.2 注册登录逻辑与安全要求
向 Claude Code 提出需求时,至少包含下面几点:
- 密码不能存储明文,使用 bcrypt 或 argon2 哈希。
- 登录成功后返回 access token,有效期通常 30 到 120 分钟。
- access token 过期后,通过 refresh token 换取新 token。
- 登录失败次数过多时,返回统一的“邮箱或密码错误”,不暴露用户是否存在。
下面的 Node.js 注册服务代码展示了哈希和用户创建的核心流程,可以让 Claude Code 基于这个思路生成完整实现:
import argon2 from "argon2"; import { getPool } from "../db/pool"; export async function register(email: string, password: string, nickname: string) { if (!email || !password || password.length < 8) { throw new Error("INVALID_PARAM"); } const pool = getPool(); const passwordHash = await argon2.hash(password, { type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1, }); const [result] = await pool.execute( `INSERT INTO users (email, password_hash, nickname, role) VALUES (?, ?, ?, 'user')`, [email, passwordHash, nickname] ); return (result as any).insertId; }这段代码只是骨架,真实实现还需要处理邮箱已被注册时的去重错误,建议让 Claude Code 补上ER_DUP_ENTRY错误捕获并返回友好的中文提示。
5.3 JWT 鉴权中间件与角色守卫
登录完成后,需要保护/api/media这类业务接口。推荐把鉴权逻辑放在一个中间件里,注册后统一应用到需要登录的路由。可以给 Claude Code 下发如下任务:
在 src/middleware/auth.ts 里: 1. 读取 Authorization 请求头,解析 Bearer Token。 2. 使用 JWT 密钥校验 token,token 解析失败时返回 401。 3. 校验通过后把 userId 和 role 写入 req,供后续控制器使用。 然后新增 requireAdmin 中间件,当当前用户不是 admin 时返回 403。下面是常见的 JWT 鉴权中间件实现:
import type { Request, Response, NextFunction } from "express"; import jwt from "jsonwebtoken"; const JWT_SECRET = process.env.JWT_SECRET || ""; interface JwtPayload { sub: number; role: string; } export function requireAuth(req: Request, res: Response, next: NextFunction) { const header = req.headers.authorization || ""; const token = header.startsWith("Bearer ") ? header.slice(7) : ""; if (!token) { res.status(401).json({ code: 401, message: "缺少访问令牌" }); return; } try { const payload = jwt.verify(token, JWT_SECRET) as JwtPayload; req.userId = payload.sub; req.role = payload.role; next(); } catch { res.status(401).json({ code: 401, message: "令牌无效或已过期" }); } } export function requireAdmin(req: Request, res: Response, next: NextFunction) { if ((req as any).role !== "admin") { res.status(403).json({ code: 403, message: "没有管理员权限" }); return; } next(); }模型里强调一点:JWT_SECRET绝不能写死在源码中,必须通过环境变量注入,且生产环境要使用足够长的随机字符串。可以让 Claude Code 生成一个.env.example文件,把密钥明文替换成占位符。
5.4 接口验证
鉴权模块写完,先用本地服务验证链路。启动 API 服务后,用 curl 模拟注册、登录、访问受保护资源三步:
curl -X POST http://127.0.0.1:8080/api/auth/register \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com","password":"12345678","nickname":"测试用户"}'curl -X POST http://127.0.0.1:8080/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com","password":"12345678"}'登录成功后会返回类似下面的结构:
{ "accessToken": "一串JWT", "refreshToken": "一串refresh token", "expiresIn": 7200 }拿着 access token 去访问受保护接口:
curl http://127.0.0.1:8080/api/user/me \ -H "Authorization: Bearer 刚才返回的accessToken"一个可靠的鉴权链路至少满足三条:没有 token 返回 401,错误 token 返回 401,admin 接口用普通用户 token 应返回 403。如果这三条都不满足,说明中间件没挂对或 token 解析逻辑有问题。
6. 数据库事务与多媒体处理
6.1 用迁移脚本管理数据库变更
正式项目中不建议用CREATE TABLE一遍遍手写建表,最好让 Claude Code 生成可重复执行的迁移脚本。迁移脚本的本质是记录数据库的每一次结构变更,并按序号执行。如果后续要加字段,只需要新增一个迁移文件,而不是修改旧的 DDL。
# 迁移文件目录 migrations/ ├── 001_init.sql ├── 002_add_media_table.sql以媒体表为例,一个常见的 DDL 如下:
CREATE TABLE medias ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, name VARCHAR(255) NOT NULL, type VARCHAR(20) NOT NULL COMMENT 'image/video', storage_path VARCHAR(500) NOT NULL, mime_type VARCHAR(100) NOT NULL DEFAULT '', size_bytes BIGINT UNSIGNED NOT NULL DEFAULT 0, width INT UNSIGNED DEFAULT NULL, height INT UNSIGNED DEFAULT NULL, duration_seconds DECIMAL(10,3) DEFAULT NULL, cover_path VARCHAR(500) DEFAULT NULL, status TINYINT NOT NULL DEFAULT 1 COMMENT '1=处理中 2=成功 3=失败', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_user (user_id), CONSTRAINT fk_media_user FOREIGN KEY (user_id) REFERENCES users(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;向 Claude Code 提出需求时,要明确“禁止在生产环境直接改表结构”,让数据库变更全部走迁移脚本。这个约定能避免项目后期结构混乱。
6.2 事务与用户资源一致性
上传多媒体资源时,通常有两个写操作:插入媒体记录、更新用户的媒体数量统计。这种场景必须使用事务,保证两个操作要么同时成功、要么同时失败。
import { getConnection } from "../db/pool"; export async function createMediaRecord(userId: number, meta: MediaMeta) { const conn = await getConnection(); try { await conn.beginTransaction(); const [result] = await conn.execute( `INSERT INTO medias (user_id, name, type, storage_path, mime_type, size_bytes, status) VALUES (?, ?, ?, ?, ?, ?, 1)`, [userId, meta.name, meta.type, meta.storagePath, meta.mimeType, meta.sizeBytes] ); await conn.execute( `UPDATE users SET media_count = media_count + 1 WHERE id = ?`, [userId] ); await conn.commit(); return (result as any).insertId; } catch (err) { await conn.rollback(); throw err; } finally { conn.release(); } }注意,事务结束后才启动转码任务,不要把耗时任务包进事务里,否则数据库连接会长时间占用,并发一高很容易把连接池打满。
6.3 视频上传与 ffmpeg 转码
商业级网站的多媒体处理不能只接收文件。建议让 Claude Code 实现以下流程:
- 客户端上传原始图片/视频。
- 后端校验文件大小、MIME 类型和后缀名。
- 文件先存储到“待处理目录”。
- 通过 ffprobe 读取视频元数据。
- 通过 ffmpeg 转码为标准 H.264 + AAC 的 MP4。
- 生成视频封面。
- 更新数据库中的处理状态。
如果是图片,可以生成缩略图并压缩,避免原图直接暴露。
关键命令示例:
# 读取视频的时长、分辨率、码率信息 ffprobe -v error -show_streams -show_format input.mp4 # 转码成 H.264 + AAC ffmpeg -y -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -movflags +faststart output.mp4 # 截取第 5 秒作为视频封面 ffmpeg -y -ss 5 -i input.mp4 -frames:v 1 -q:v 4 cover.jpg在 Node.js 中通过子进程调用 ffmpeg 时,加一个超时时间是必要的,避免转码进程卡死:
import { execFile } from "child_process"; import { promisify } from "util"; const execFileAsync = promisify(execFile); export async function transcodeVideo(input: string, output: string) { await execFileAsync( "ffmpeg", [ "-y", "-i", input, "-c:v", "libx264", "-crf", "23", "-preset", "medium", "-c:a", "aac", "-movflags", "+faststart", output, ], { timeout: 10 * 60 * 1000 } ); }如果上传量很大,不建议在 HTTP 请求内同步执行 ffmpeg,而是把任务丢到任务队列里异步处理。项目早期没有 Redis 队列时,可以先让 Claude Code 用“数据库表字段标记状态 + 定时扫描处理”的方式实现一个极简异步任务表。
6.4 多媒体处理模块验证
验证转码是否成功,不要只看返回 200,要看数据库里媒体记录有没有从“处理中”变成“成功”,并确认生成后的文件确实存在于目标目录。
ffprobe -v error -show_entries format=duration,size -of json output.mp4如果输出里没有error信息,且 duration 是数值,表示转码产物可用。还有一种很隐蔽的问题:转码进程“看似成功”,但产物是空文件,通常是因为磁盘空间不足或 ffmpeg 没有写权限,需要重点关注日志里的编码器错误。
7. 自动化部署与持续集成
7.1 用 GitHub Actions 做 CI 部署
自动化部署的关键是让代码合并到主分支后,测试、构建、推送服务器全部自动执行。下面是一个 GitHub Actions 工作流示例,它在代码推送到 main 分支时触发:
name: deploy on: push: branches: [ "main" ] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: 拉取代码 uses: actions/checkout@v4 - name: 安装 Node uses: actions/setup-node@v4 with: node-version: 20 - name: 安装依赖 run: npm ci - name: 执行测试 run: npm run test --if-present - name: 编译项目 run: npm run build - name: 远程部署 uses: appleboy/ssh-action@v1.2.0 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | cd /var/www/commerce-site git pull origin main npm ci --omit=dev npm run build pm2 reload api --update-env使用第三方 action 时要注意:它们的版本会更新,上例中的版本号需要根据当前可用版本调整。更稳妥的方法是先用 SSH 命令手动部署一次,确认服务器上的目录和权限没问题后,再接入 CI。
7.2 PM2 守护 Node 进程
自动化部署后,Node 服务不能前台跑在终端里,需要使用 PM2 作为进程守护。这里给一个ecosystem.config.js示例:
module.exports = { apps: [ { name: "api", script: "dist/src/index.js", instances: 1, exec_mode: "fork", env: { NODE_ENV: "production", }, }, ], };服务器上的部署命令可以手动先跑一遍:
cd /var/www/commerce-site git pull origin main npm ci --omit=dev npm run build pm2 reload api --update-env7.3 Nginx 反向代理与静态资源
如果是前后端分离项目,前端打包后的静态文件由 Nginx 托管,后端 API 通过反向代理转发:
server { listen 80; server_name your-domain.com; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 50m; } location /uploads/ { alias /var/www/commerce-site/uploads/; expires 7d; } }client_max_body_size大小要根据你的多媒体上传需求设置,默认 1m 太小,视频上传基本必失败。如果上传超过 Nginx 限制,客户端会收到 413 错误,这属于配置层问题,不是代码问题。
7.4 CI 部署验证
自动化部署完成后,至少验证三条链路:访问登录接口能返回 JSON;访问受保护接口无 token 时返回 401;用测试用户走一遍注册流程,确认数据库表有新增记录。如果 CI 里执行了数据库迁移,要看是否做了幂等保护,否则重复部署时可能会因为重复建表而失败。
8. Claude Code 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
claude命令不存在 | npm 全局目录不在 PATH 中 | 检查claude --version | 重新安装或配置 npm 全局 bin 到 PATH |
| Claude Code 启动后无法登录 | API 密钥无效,或登录凭据配置错误 | 检查环境变量和登录状态 | 按官方引导重新登录或更新 API 密钥 |
| 第三方模型接入时报模型不识别 | 配置的模型名不被当前版本或 API 网关支持 | 查看启动日志中实际使用的模型标识 | 确认账号权限,使用与当前客户端版本匹配的模型标识 |
| Claude Code 生成的代码没有遵循项目规范 | 项目根目录缺少CLAUDE.md,或新会话未重新读取 | 检查CLAUDE.md是否存在 | 在项目根目录维护清晰的项目说明文件 |
| AI 一次改动太多,代码难以审查 | 指令范围过大 | 拆分需求,一次只完成一个模块 | 按“建表→鉴权→接口→部署”逐个推进 |
| 登录接口密码错误时报错但不友好 | 数据库异常被直接抛出 | 查看后端日志与响应体 | 增加统一异常处理与错误码 |
| ffmpeg 转码失败 | ffmpeg 未安装或服务器缺少编码器 | ffmpeg -version检查 | 安装完整 ffmpeg,确认 libx264 可用 |
| 上传大视频时接口超时 | Nginx 超时或后端同步转码 | 查看 Nginx error.log | 调大超时时间,或改用异步队列处理 |
| CI 部署后服务没有更新 | PM2 未 reload 或远程目录不对 | 登录服务器手动执行命令 | 检查部署脚本的远程路径和 PM2 服务名 |
另一个常见问题是:Claude Code 生成的代码“看着能跑”,但存在严重的目录越界。例如上传接口允许用户传入自定义路径,导致任意文件覆盖。遇到这种问题,建议单独让 Claude Code 做一次安全审计,并把“禁止拼接用户输入到文件路径”写进CLAUDE.md。
9. 安全合规、边界与团队协作建议
这套项目里涉及三个敏感面:用户账号数据、上传的多媒体内容、部署服务器的访问凭据。商业级系统对这些数据必须有明确边界:
- 数据库密码、JWT 密钥、云存储密钥不能进 Git 仓库,统一走环境变量或密钥管理服务。
- 用户上传的图片、视频可能包含个人肖像或版权内容,上线前必须获得合法授权,明确素材的版权归属和使用范围。
- 包含人脸或声音的数据处理功能,必须遵守相关法律法规,并在产品中向用户说明数据用途。
- 生产环境不允许直接修改数据库,所有变更必须经过迁移脚本并在测试库先执行。
- 给普通用户开放的接口,要测试越权场景:用户 A 能否访问用户 B 的资源。
在团队协作层面,Claude Code 生成的代码不能直接合并到主分支。建议把它输出当作“高密度代码草稿”,每一个关键文件都要过一遍人工 Code Review。重点检查三处:鉴权中间件有没有实际挂到路由上;文件上传的目录是否可控;数据库连接有没有正确释放。
如果你在本机同时跑多个环境,注意.env文件不一致导致的问题。最容易踩的坑就是本地测试能用,推到服务器后因为缺少某个环境变量而崩溃。一个比较稳的实践是:把.env.example提交到仓库,把真实.env加到.gitignore中,部署时由 CI 从仓库 Secrets 注入。
最后给出一个可以直接沿用的工作流:先让 Claude Code 输出一段需求拆解,再由你把拆解结果写进CLAUDE.md;之后按数据库、鉴权、业务接口、外部集成、部署配置的顺序,每个部分单独开启一次会话;每次会话结束后,让人工审查关键 diff 并运行一次最小测试。这个循环走通以后,你会看到 Claude Code 在标准工程里的产出质量会稳定很多,它最擅长的是在清晰边界内快速生成大量可编译的代码,而最需要的恰恰是你对边界和验收标准的把控。