目录

第一部分:项目规划与架构设计

1.1 项目概览

1.2 核心功能清单

1.3 系统架构图(文字版)

第二部分:完整目录结构

2.1 Monorepo 结构

第三部分:关键代码详解

3.1 共享类型定义(解决前后端对齐问题)

3.2 Zustand 购物车状态管理

3.3 Stripe 支付集成(最关键部分)

3.4 Next.js 商品列表页(服务端渲染)

第四部分:全栈协作的核心挑战

4.1 三大典型挑战与解决方案

4.2 Claude Code 处理全栈的提示词策略

第五部分:最终数据与效果

5.1 开发耗时明细

5.2 上线效果指标

第六部分:系列总结与最终建议

6.1 五篇实战的核心数据汇总

6.2 最终使用建议

6.3 Claude Code 的边

第一部分:项目规划与架构设计

1.1 项目概览

属性

前端

后端

框架

Next.js 14(App Router)

Express 4 + TypeScript

数据库

MongoDB + Mongoose

样式

Tailwind CSS + shadcn/ui

支付

Stripe(前端集成)

Stripe Webhook(后端)

状态管理

Zustand

认证

NextAuth.js

JWT 验证中间件

部署

Vercel

Railway

目标耗时

25小时

传统方式80小时

1.2 核心功能清单

  • 商品模块:商品列表、详情、搜索、分类筛选、库存管理
  • 购物车模块:加入/删除商品、数量修改、本地持久化
  • 订单模块:下单、支付(Stripe)、订单状态追踪
  • 用户模块:注册/登录、个人中心、历史订单
  • 管理后台:商品管理、订单管理、数据统计仪表盘
  • 基础设施:图片上传(Cloudinary)、邮件通知、错误监控

1.3 系统架构图

第二部分:完整目录结构

2.1 Monorepo 结构

全栈项目采用 Monorepo 组织,前后端在同一个仓库中管理:

ecommerce/

├── frontend/                    # Next.js 前端

│   ├── app/

│   │   ├── (shop)/             # 商城页面组

│   │   │   ├── page.tsx        # 首页 / 商品列表

│   │   │   ├── product/[id]/   # 商品详情

│   │   │   └── cart/           # 购物车

│   │   ├── (auth)/             # 认证页面组

│   │   │   ├── login/

│   │   │   └── register/

│   │   ├── dashboard/          # 管理后台

│   │   │   ├── products/

│   │   │   └── orders/

│   │   ├── api/                # Next.js API Routes

│   │   │   └── auth/[...nextauth]/

│   │   └── layout.tsx

│   ├── components/

│   │   ├── ui/                 # shadcn/ui 基础组件

│   │   ├── product/            # 商品相关组件

│   │   └── cart/               # 购物车组件

│   ├── store/                  # Zustand 状态管理

│   │   └── cartStore.ts

│   ├── lib/

│   │   ├── api.ts              # API 请求封装

│   │   └── stripe.ts           # Stripe 客户端

│   └── types/                  # 共享类型定义

│       └── index.ts

│

├── backend/                     # Express 后端

│   ├── src/

│   │   ├── models/             # Mongoose 模型

│   │   │   ├── Product.ts

│   │   │   ├── Order.ts

│   │   │   └── User.ts

│   │   ├── routes/             # API 路由

│   │   │   ├── products.ts

│   │   │   ├── orders.ts

│   │   │   └── users.ts

│   │   ├── middleware/

│   │   │   ├── auth.ts         # JWT 验证

│   │   │   └── errorHandler.ts

│   │   ├── services/

│   │   │   ├── stripeService.ts

│   │   │   └── emailService.ts

│   │   └── app.ts

│   └── package.json

│

├── shared/                      # 前后端共享

│   └── types/

│       └── index.ts            # 统一类型定义

│

└── package.json                 # 根 package.json(workspaces)

第三部分:关键代码详解

3.1 共享类型定义(解决前后端对齐问题)

全栈最大的痛点是前后端类型不一致。解决方案:在 shared/ 目录统一定义,前后端都引用同一份。

// shared/types/index.ts

// ── 商品 ──

export interface Product {

  _id:         string

  name:        string

  description: string

  price:       number

  stock:       number

  images:      string[]

  category:    string

  tags:        string[]

  createdAt:   string

}



export interface ProductQuery {

  page?:     number

  limit?:    number

  category?: string

  minPrice?: number

  maxPrice?: number

  search?:   string

}



// ── 购物车 ──

export interface CartItem {

  product:  Product

  quantity: number

}



// ── 订单 ──

export type OrderStatus = 'pending' | 'paid' | 'shipped' | 'delivered' | 'cancelled'



export interface Order {

  _id:            string

  userId:         string

  items:          CartItem[]

  totalAmount:    number

  status:         OrderStatus

  stripePaymentId?: string

  shippingAddress: Address

  createdAt:      string

}



// ── 地址 ──

export interface Address {

  street:   string

  city:     string

  province: string

  zipCode:  string

  country:  string

}



// ── API响应 ──

export interface ApiResponse<T> {

  success: boolean

  data:    T

  message?: string

}



export interface PaginatedResponse<T> extends ApiResponse<T[]> {

  total:      number

  page:       number

  totalPages: number

}

3.2 Zustand 购物车状态管理

// frontend/store/cartStore.ts

import { create } from 'zustand'

import { persist } from 'zustand/middleware'

import type { CartItem, Product } from '../../shared/types'



interface CartStore {

  items:        CartItem[]

  totalItems:   number

  totalAmount:  number

  addItem:      (product: Product, quantity?: number) => void

  removeItem:   (productId: string) => void

  updateQty:    (productId: string, quantity: number) => void

  clearCart:    () => void

}



export const useCartStore = create<CartStore>()(

  persist(

    (set, get) => ({

      items:       [],

      totalItems:  0,

      totalAmount: 0,



      addItem: (product, quantity = 1) => {

        const existing = get().items.find(i => i.product._id === product._id)

        const newItems = existing

          ? get().items.map(i =>

              i.product._id === product._id

                ? { ...i, quantity: i.quantity + quantity }

                : i

            )

          : [...get().items, { product, quantity }]



        set({

          items:       newItems,

          totalItems:  newItems.reduce((sum, i) => sum + i.quantity, 0),

          totalAmount: newItems.reduce((sum, i) => sum + i.product.price * i.quantity, 0),

        })

      },



      removeItem: (productId) => {

        const newItems = get().items.filter(i => i.product._id !== productId)

        set({

          items:       newItems,

          totalItems:  newItems.reduce((sum, i) => sum + i.quantity, 0),

          totalAmount: newItems.reduce((sum, i) => sum + i.product.price * i.quantity, 0),

        })

      },



      updateQty: (productId, quantity) => {

        if (quantity <= 0) { get().removeItem(productId); return }

        set({ items: get().items.map(i =>

          i.product._id === productId ? { ...i, quantity } : i

        )})

      },



      clearCart: () => set({ items: [], totalItems: 0, totalAmount: 0 }),

    }),

    { name: 'cart-storage' }   // 自动持久化到 localStorage

  )

)

3.3 Stripe 支付集成(最关键部分)

前端:创建支付会话

// frontend/app/(shop)/checkout/page.tsx

import { loadStripe } from '@stripe/stripe-js'

import { useCartStore } from '@/store/cartStore'



const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_KEY!)



export default function CheckoutPage() {

  const { items, totalAmount } = useCartStore()



  const handlePayment = async () => {

    const stripe = await stripePromise

    if (!stripe) return



    // 调用后端创建 Stripe Session

    const res = await fetch('/api/create-checkout-session', {

      method: 'POST',

      headers: { 'Content-Type': 'application/json' },

      body: JSON.stringify({ items }),

    })



    const { sessionId } = await res.json()



    // 跳转到 Stripe 支付页

    await stripe.redirectToCheckout({ sessionId })

  }



  return (

    <div className='max-w-lg mx-auto p-8'>

      <h1 className='text-2xl font-bold mb-6'>确认订单</h1>

      <p className='text-lg mb-4'>总计:¥{totalAmount.toFixed(2)}</p>

      <button

        onClick={handlePayment}

        className='w-full bg-blue-600 text-white py-3 rounded-lg font-bold'

      >

        前往支付

      </button>

    </div>

  )

}



后端:处理支付 Webhook

// backend/src/routes/webhook.ts

import Stripe from 'stripe'

import { Order } from '../models/Order'



const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)



export async function handleWebhook(req: Request, res: Response) {

  const sig = req.headers['stripe-signature'] as string

  let event: Stripe.Event



  try {

    // 验证 Webhook 签名(防止伪造)

    event = stripe.webhooks.constructEvent(

      req.body,                        // 原始 Buffer

      sig,

      process.env.STRIPE_WEBHOOK_SECRET!

    )

  } catch (err) {

    return res.status(400).send('Webhook 签名验证失败')

  }



  // 处理支付成功事件

  if (event.type === 'checkout.session.completed') {

    const session = event.data.object as Stripe.Checkout.Session



    await Order.findOneAndUpdate(

      { stripeSessionId: session.id },

      {

        status:          'paid',

        stripePaymentId: session.payment_intent as string,

        paidAt:          new Date(),

      }

    )



    // 触发发货邮件通知(异步,不阻塞响应)

    sendOrderConfirmationEmail(session.customer_email!).catch(console.error)

  }



  res.json({ received: true })

}

3.4 Next.js 商品列表页(服务端渲染)

// frontend/app/(shop)/page.tsx

import ProductCard from '@/components/product/ProductCard'

import type { Product, ProductQuery } from '../../shared/types'



// Next.js 服务端组件 - 直接在服务端获取数据

export default async function ShopPage({

  searchParams

}: {

  searchParams: ProductQuery

}) {

  const params = new URLSearchParams({

    page:     String(searchParams.page  ?? 1),

    limit:    String(searchParams.limit ?? 12),

    category: searchParams.category ?? '',

    search:   searchParams.search   ?? '',

  })



  // 服务端直接请求后端 API

  const res = await fetch(

    `${process.env.BACKEND_URL}/api/products?${params}`,

    { next: { revalidate: 60 } }   // ISR:每60秒重新验证缓存

  )



  const { data: products, total, totalPages } = await res.json()



  return (

    <main className='container mx-auto px-4 py-8'>

      <h1 className='text-3xl font-bold mb-8'>全部商品</h1>



      {/* 商品网格 */}

      <div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6'>

        {products.map((product: Product) => (

          <ProductCard key={product._id} product={product} />

        ))}

      </div>



      {/* 分页信息 */}

      <p className='text-center mt-8 text-gray-600'>

        共 {total} 件商品,第 {searchParams.page ?? 1} / {totalPages} 页

      </p>

    </main>

  )

}

第四部分:全栈协作的核心挑战

4.1 三大典型挑战与解决方案

挑战1:前后端类型不同步

❌ 问题:前端写的 product.price(number)

         后端返回的 price: '199.00'(string)

         → 结算时总价计算错误

✅ 解决:统一在 shared/types/index.ts 定义 Product 接口

         前后端都引用同一份,TypeScript 编译时捕获不一致

挑战2:Stripe Webhook 必须接收原始 Body

❌ 问题:Express 全局使用了 express.json() 解析请求体

         Stripe 签名验证需要原始 Buffer,JSON解析后验证失败

✅ 解决:Webhook 路由单独配置,跳过全局 JSON 中间件

// app.ts

app.post('/webhook', express.raw({ type: 'application/json' }), handleWebhook)

app.use(express.json())   // 其余路由正常使用 JSON 解析

挑战3:Next.js App Router 的缓存策略

❌ 问题:用户下单后,商品库存已减少

         但商品列表还显示旧库存(Next.js 缓存未更新)

✅ 解决:下单成功后主动触发缓存重验证
 

// 下单 API Route

import { revalidatePath } from 'next/cache'

export async function POST(req: Request) {

  // ... 创建订单逻辑 ...

  revalidatePath('/')              // 首页商品列表

  revalidatePath('/product/[id]') // 对应商品详情页

  return Response.json({ success: true })

}

4.2 Claude Code 处理全栈的提示词策略

全栈开发中,提示词需要同时说清楚前端和后端的期望:

提示词示例(生成商品创建功能):

"实现电商平台的'创建商品'功能,要求:

【后端】Express + TypeScript

- POST /api/products 路由

- 需要管理员 JWT 验证

- 支持图片上传(Cloudinary)

- Mongoose 模型中库存必须 >= 0

- 返回格式遵循 ApiResponse<Product>(shared/types)

【前端】Next.js + TypeScript

- 管理后台表单页面 /dashboard/products/new

- 字段:名称、描述、价格、库存、分类、图片上传

- 使用 react-hook-form 管理表单

- 上传成功后跳转到商品列表页

- 错误状态需要友好提示

类型从 shared/types/index.ts 引用,不要重复定义"

第五部分:最终数据与效果

5.1 开发耗时明细

模块

Claude Code

手写传统

节省

备注

架构设计 + 目录

30分钟

180分钟

⬇ 83%

Monorepo

共享类型定义

20分钟

60分钟

⬇ 67%

后端 API

90分钟

300分钟

⬇ 70%

4个路由模块

前端页面

120分钟

360分钟

⬇ 67%

6个主要页面

Stripe集成

45分钟

180分钟

⬇ 75%

含Webhook

状态管理

30分钟

90分钟

⬇ 67%

Zustand

Docker + 部署

30分钟

120分钟

⬇ 75%

调试与修复

135分钟

510分钟

⬇ 74%

人工主导

合计

500分钟(≈25h)

1800分钟(≈80h)

⬇ 72%

5.2 上线效果指标

指标

Claude Code版

手写版

评价

Lighthouse 性能分

91

94

略低

首页加载时间(LCP)

1.4秒

1.2秒

相当

API平均响应时间

42ms

38ms

相当

TypeScript覆盖率

100%

78%

Code大优

测试覆盖率

76%

55%

Code更优

代码复用率

41%(共享类型)

18%

Code大优

第六部分:系列总结与最终建议

6.1 五篇实战的核心数据汇总

篇章

项目类型

Code耗时

传统耗时

节省

第3篇

Web前端

12小时

40小时

70%

第4篇

Python后端

16小时

50小时

68%

第5篇

全栈项目

25小时

80小时

69%

平均

17.7小时

56.7小时

69%

6.2 最终使用建议

经过5篇的系统测评,我得出一个清晰的结论:

  • 原型和新项目:Claude Code 是第一选择,节省 70% 时间
  • 已有项目维护:传统 IDE 更稳,Claude Code 辅助生成特定模块
  • 学习阶段:先用 IDE 手写建立理解,再用 Claude Code 提速
  • 团队协作:Claude Code 生成框架,人工审查合并,建立提示词模板库
  • 质量保证:Claude Code 的类型和文档往往比手写更完整,但需要人工把关逻辑

6.3 Claude Code 的边界

有一些事情 Claude Code 目前还做不好,需要你来补:

  • 业务逻辑设计:数据库表结构、API 设计方案,这些需要你决策
  • 性能优化:索引选择、缓存策略、SQL 优化,需要你主导
  • 安全审查:生成的代码要过安全检查,不能无脑上线
  • 复杂算法:涉及特定业务规则的复杂逻辑,还是手写更可控

感谢你跟完这 5 篇系列。如果这个系列对你有帮助,欢迎评论区分享你用 Claude Code 的实战经验!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐