Claude Code 实战一:从零开发电商小程序(全流程)
提到 AI 编程,很多人第一反应还是拿它补几行函数、改个样式、写点注释。但我这次想做点更硬的验证:完全用 Claude Code 这个命令行 AI 编程工具,在不提前写一行业务代码的前提下,从零开发一个电商小程序。
不是玩具 Demo,而是包含用户登录、商品展示、购物车、下单、订单管理、后台数据统计的完整闭环。最后的结果超出了我的预期——整个项目从初始化到拿到可体验版本,用了不到两天,而且代码质量让我这个有十年开发经验的人都觉得“这活儿干得确实可以”。
这篇文章我会把完整的实战过程拆开来讲:项目怎么初始化、Claude Code 的工作流怎么跑、核心模块(商品、购物车、订单、用户)是怎么一步步实现的、遇到了哪些坑、怎么排查。全程零基础也能跟着走,有经验的开发者同样能从里面的技术选型和架构设计里拿到东西。
1. 项目整体设计与 Claude Code 工作流
1.1 为什么选 Claude Code 而不是其他 AI 编程工具
先说结论:Claude Code 在“复杂多文件工程类任务”上的表现,是目前我用过的 AI 编程工具里最接近“结对程序员”的。
和普通的对话式 AI 不同,Claude Code 运行在终端里,能直接读写你项目里的文件、执行命令、跑测试、看报错信息,然后自己决定接下来改哪些代码。这意味着它不是“你问一句它答一句”,而是“你给它一个目标,它自己拆解任务、动工、验证、收尾”。
比如我说“帮我建一个微信小程序项目,原生框架,支持商品列表和详情页”,它会先去读微信小程序的项目结构规范,然后自己创建 app.json、app.js、app.wxss、页面目录、组件目录,生成完整代码。这个过程中你随时可以打断、纠正、要求修改。
1.2 项目定位:一个什么级别的电商小程序
为了避免“看着像样但根本跑不通”的空中楼阁,我定了几个硬指标:
- 前端:微信小程序原生框架(WXML + WXSS + JS + JSON),不依赖第三方 UI 库
- 后端:微信云开发(云函数 + 云数据库 + 云存储),免服务器部署
- 功能范围:用户登录态、首页商品瀑布流、商品详情、SKU 选择、购物车增删改、下单、订单状态流转(待支付/已支付/已发货/已完成)、订单列表、个人中心
- 数据层:所有商品数据、订单数据、用户数据全部走云数据库,图片走云存储
- 管理侧:一个简单的后台管理页面(云函数实现),能查看商品列表、订单统计、手动发货
选择原生框架和云开发,是因为这套组合最贴近微信生态的真实开发方式,不需要租服务器、配域名、搞备案。对个人开发者和小团队来说,这是性价比最高的起步方案。
1.3 Claude Code 的核心协作模式:Plan 先行
用 Claude Code 做项目,和传统开发最大的不同是先规划后编码。
我在项目根目录下新建了一个docs/文件夹,第一件事不是让它写代码,而是让 Claude Code 输出三份文档:
docs/01-项目结构.md:整个小程序的文件目录设计docs/02-数据库设计.md:所有数据集合(Collection)及其字段结构docs/03-页面与接口清单.md:每个页面包含什么功能,每个云函数提供什么接口
这个习惯帮了我大忙。因为 Claude Code 上下文窗口再大也是有限的,如果你不在一开始把架构定下来,后面它写代码时很容易前后矛盾——前面定义的商品字段,后面详情页就取了另一个字段名。
实际对话命令参考:
请先阅读微信小程序原生开发和云开发的官方文档要点, 然后基于电商小程序的常见需求,在 docs/ 目录下设计完整的技术方案, 包括目录结构、数据库 collections 设计、页面列表和云函数接口清单。 设计完成后请总结给我确认。它会先用 claude 命令读官方文档结构、梳理需求,然后生成方案文件。我检查之后觉得数据库设计里缺少“优惠券”相关字段,直接回复:“现在不需要优惠券功能,但请预留字段”,它立刻更新所有设计文档并保持关联文件同步。
2. 关键技术选型与架构决策
2.1 原生框架 vs 第三方跨端框架
做小程序,绕不开的第一个选择就是技术栈:原生小程序、Taro、uni-app,还是别的?
我最终选了原生框架,理由有三个:
- Claude Code 对微信原生框架的代码模式学习得最透彻,翻车概率最低
- 原生框架不需要额外编译链,出问题时更容易定位(AI 生成的代码里编译错误最容易让人头疼)
- 小程序原生已经是跨端了(iOS/Android/微信PC),没必要为了多端复用再套一层框架
如果你是团队已有前端基础、打算以后同时做 App 和 H5,Taro/uni-app 确实可以。但对本项目的目标是快速落地、逻辑清晰、最大化利用 AI 能力,原生框架是最优解。
2.2 云开发:省掉后端服务端的全部脏活
云开发这个决策,节省了我至少两天工作量。
传统模式你需要搞一台服务器(哪怕是便宜的云主机),装数据库、写后端 API、配接口鉴权、处理图片上传。云开发直接把这三件事打包了:
- 云数据库:文档型数据库,类似 MongoDB,支持权限控制
- 云函数:Node.js 运行环境,直接在函数里操作数据库、调用微信开放能力
- 云存储:存商品图片、用户头像等静态资源,自带 CDN
本地开发时,微信开发者工具里可以一键开通云开发环境;写好的云函数右键就能上传部署;数据库可以直接在开发者工具里可视化编辑。整个流程几乎没有“脱离微信生态”的操作,非常顺滑。
2.3 数据模型设计:先想清楚集合再动手
数据库设计可以说是我这个项目里收益最高的一环。Claude Code 在 docs/02-数据库设计.md 里给出了 6 个集合,我们逐个过一遍:
| 集合名 | 用途 | 关键字段 |
|---|---|---|
| goods | 商品信息 | name, price, images, stock, category, sales, detail, sku |
| sku | 商品规格库存 | goodId, spec, price, stock |
| users | 用户信息 | openid, nickName, avatarUrl, addressList |
| cart | 购物车条目 | openid, goodId, skuId, quantity, checked, updateTime |
| orders | 订单信息 | orderNo, openid, items, totalPrice, status, address, createTime |
| banners | 首页轮播图 | image, linkType, linkUrl, sort |
这里有个关键设计思想值得说:购物车和订单都存了“商品快照”。具体来说,orders 里的 items 数组里,每个商品除了 goodId 之外,还会冗余存一份商品名、图片、单价、规格描述。
为什么?因为商品信息是可变的——价格会调、图片会换、商品可能下架。订单一旦生成,记录的应该是“下单那一刻的商品状态”,而不是“当前商品的实时状态”。下单后再去查商品表,取到的可能是涨价后的价格,用户就有理由投诉了。
我把这个原因讲给 Claude Code 听之后,它自动在数据校验里给订单快照加了一个snapshot: true的标识字段,细节拉满。
2.4 云函数拆分原则
电商类应用最核心的判断是:哪些逻辑必须放服务端,哪些可以放前端?
我的原则是:涉及钱、库存、数据写权限的操作,一律走云函数。前端可以只负责展示和基础交互。具体拆分:
login:接收前端传的登录凭证,调用微信接口换取 openid,查询或创建用户记录,返回用户信息getGoodsList:分页获取商品列表,支持分类筛选和排序getGoodsDetail:读取商品数据 + 对应 SKU 列表addCart/updateCart/deleteCart:购物车增删改,校验商品是否上架createOrder:核心云函数,做库存扣减、生成订单号、计算总价、创建订单记录getOrderList:按用户和状态查询订单列表updateOrderStatus:模拟支付、发货操作getAdminStats:后台统计,返回商品数、订单数、销售额、待发货订单数
核心原则一句话:前端永远不要去操作另一个用户的数据,也不要做和钱、库存相关的关键判断。比如购物车数量、订单金额的前端显示只做展示,下单时云函数会重新从数据库读取价格、重新计算总价,任何前端篡改都是无效的。
3. 全流程实操:从 0 搭建一套可运行的电商小程序
3.1 环境准备:装好三样东西
动手之前,先把环境配齐:
- 微信开发者工具(稳定版即可,注意登录时选择“小程序模式”而非“公众号模式”)
- Node.js 16+(云函数本地调试要用)
- Claude Code CLI(如果你是 npm 安装,
npm install -g @anthropic-ai/claude-code)
我在项目目录下直接执行claude命令进入交互模式。第一件事不是写代码,而是先通过对话设定项目的“宪法”文件。
我让 Claude Code 在项目根目录生成了一份CLAUDE.md,内容包含:项目技术栈、目录约定、命名风格(页面文件用 camelCase)、数据库操作规范(必须用云函数、前端不得直连数据库写操作)、注释规范(中文注释)。这份文件的作用是让 AI 在后续每一次对话里都能读取项目约定,不跑偏。
3.2 初始化项目骨架:让 AI 自己搭架子
如果手动搭小程序骨架,你需要手动创建数十个文件:app.json、pages 目录、每个页面的四件套(wxml、wxss、js、json)、utils、components……
我用一条指令搞定:
请基于原生微信小程序 + 云开发的技术栈,初始化整个项目骨架。 包含 app.json、app.js、app.wxss、project.config.json、sitemap.json, 以及以下页面:home(首页)、goods/detail(商品详情)、cart(购物车)、 order/confirm(确认订单)、order/list(订单列表)、user(个人中心)、admin(管理后台)。 每个页面生成四件套文件。云开发环境变量统一放在 config/index.js 里。 创建完成后,请用 tree 命令列出项目结构让我确认。它用了不到五分钟就把所有文件生成完毕,并给出了目录树:
. ├── CLAUDE.md ├── app.js ├── app.json ├── app.wxss ├── config/ │ └── index.js ├── pages/ │ ├── admin/ │ ├── cart/ │ ├── goods/ │ ├── home/ │ ├── order/ │ └── user/ ├── utils/ │ ├── auth.js │ ├── cart.js │ └── format.js ├── cloudfunctions/ │ ├── addCart/ │ ├── createOrder/ │ ├── deleteCart/ │ ├── getAdminStats/ │ ├── getGoodsDetail/ │ ├── getGoodsList/ │ ├── getOrderList/ │ ├── login/ │ ├── updateCart/ │ └── updateOrderStatus/ └── project.config.json唯一要手动操作的是在微信开发者工具里导入项目目录,并把project.config.json里的cloudfunctionRoot指向cloudfunctions/,然后开通云开发。
3.3 首页商品流:从数据库到界面的完整链路
首页是电商门面,我要求的效果是商品卡片两列流式排列、顶部有分类 tab、支持下拉刷新和触底加载下一页。
数据库里手工 seed 了 16 件商品数据,商品图片我临时用了几张本地图上传到了云存储(真实项目建议用图床 + 云存储做 CDN 加速)。
首页用到的核心云函数是getGoodsList。我让 Claude Code 实现时,特别要求它做到:
- 支持分类筛选(all / 数码 / 服饰 / 食品 / 家居)
- 支持排序切换(综合 / 销量 / 价格升序 / 价格降序)
- 使用
skip + limit分页,单次返回 10 条 - 返回的每条商品数据必须包含
id、name、price、images[0](封面图)、sales、stock
实际生成的云函数核心逻辑简化后大概长这样:
const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database() const _ = db.command exports.main = async (event) => { const { category = 'all', sort = 'default', page = 1, pageSize = 10 } = event const dbCmd = db.command // 构建查询条件 let where = {} if (category !== 'all') { where.category = category } where.status = dbCmd.neq('offline') // 状态不等于下架 const countRes = await db.collection('goods').where(where).count() const total = countRes.total const totalPages = Math.ceil(total / pageSize) // 构建排序 let orderBy = {} switch (sort) { case 'sales': orderBy = { sales: 'desc' } break case 'priceAsc': orderBy = { price: 'asc' } break case 'priceDesc': orderBy = { price: 'desc' } break default: orderBy = { createTime: 'desc' } } const goodsRes = await db.collection('goods') .where(where) .orderBy(Object.keys(orderBy)[0], Object.values(orderBy)[0]) .skip((page - 1) * pageSize) .limit(pageSize) .get() return { code: 0, data: { list: goodsRes.data, page, pageSize, total, totalPages, hasMore: page < totalPages } } }首页前端拿到数据后渲染两列瀑布流。这里 Claude Code 用了一个小技巧:用 CSS (column-count: 2) 实现瀑布流效果,而不是传统的 flex 双列。好处是实现简单、高度自动排列;代价是数据顺序会变成“先左列满再右列”。实测下来视觉差异不大,商品卡片高度本身就不统一,瀑布流观感反而自然。
注意:云函数里排序字段一定要确认数据库里建了索引,否则数据量大之后会有性能问题。云开发控制台里对
goods集合的category + status + createTime建一个联合索引就好了。
3.4 商品详情与 SKU 选择:最考耐心的部分
商品详情页是整个小程序里交互最复杂的部分。
它涉及:
- 顶部轮播图(商品多图滑动)
- 价格、销量、库存展示
- SKU 规格选择(比如颜色、尺码)
- 加入购物车 / 立即购买两个动作
- 底部操作栏吸底
SKU 的数据库结构是sku集合,每个 SKU 记录对应一个商品的一种规格组合:
{ goodId: "g_1001", spec: ["黑色", "M码"], price: 199, stock: 50, image: "cloud://xxx/black-m.png" }前端拿到商品详情和全部 SKU 后,就需要实现一个“SKU 选择面板”——点击颜色和尺寸,只有当某个组合仍有库存时才能选中,否则置灰。这个逻辑我让 Claude Code 写了一个独立 util 封装,核心代码:
function getSkuByCombination(skuList, selectedSpecs) { // 过滤掉未选择的维度 const activeSpecs = Object.values(selectedSpecs).filter(Boolean) // 找到所有包含当前选中规格组合的 sku(按数组长度降序匹配) const matches = skuList .filter((sku) => activeSpecs.every((spec) => sku.spec.includes(spec))) .sort((a, b) => b.spec.length - a.spec.length) return matches[0] || null } function getAvailableSpecs(skuList, selectedSpecs, specGroupIndex) { // 当前维度外已选中的规格 const lockedSpecs = selectedSpecs.filter((spec, idx) => idx !== specGroupIndex && spec) return skuList .filter((sku) => lockedSpecs.every((spec) => sku.spec.includes(spec))) .flatMap((sku) => sku.spec[specGroupIndex]) }逻辑本身不算难,但实现中有一个容易踩的坑:选中 SKU 后价格显示和加入购物车用的 skuId 必须来自同一个 SKU 对象。我一开始就让 Claude Code 把选中状态(selectedSku)统一定义为整个 sku 对象,而不是只存 skuId,否则库存校验时会出现查不到对应 SKU 的情况。
加入购物车时,云函数addCart会接收goodId + skuId + quantity,然后做三件事:
- 校验商品存在且未下架
- 校验 SKU 存在且库存足够
- 检查购物车是否已有同款(同用户 + 同商品 + 同 SKU),有则合并数量,无则新建条目
这样购物车数据不会膨胀,用户操作体验也顺滑。
3.5 购物车:设计一个不惹事的模块
购物车的坑不在“增删改查”本身,而在细节状态管理。
我的页面结构是:顶部有全选按钮,每个商品项有勾选框、数量步进器、左滑删除(原生自带的movable-view或长按删除)。底部有一个悬浮结算栏,实时显示“已选 N 件,合计 ¥XXX”,点击结算跳转到确认订单页。
为了让 Claude Code 实现的购物车在前端保持状态一致,我要求它把购物车逻辑封装到一个独立的utils/cart.js模块里,页面只做渲染和事件绑定。模块暴露这些方法:
getCartList():从云函数拉取购物车列表,同时合并实时商品信息(商品图、名称、单价)updateCheckedStatus(cartId, checked):更新单个条目的选中态selectAll(checked):全选 / 取消全选changeQuantity(cartId, delta):数量加减removeItem(cartId):删除getSelectedTotal():计算已选商品的总价
其中有一个细节很关键:购物车列表里的商品价格必须实时从商品表里读,而不是购物车条目里存死价格。因为用户加购之后,商品可能调价了。你购物车显示的价格是旧的,结算时却用新价格,用户会骂人。
Claude Code 的实现方式是:拉取购物车列表后,批量根据 goodId 去查询商品表的最新价格、名称、图片、库存状态,组装后返回前端。若商品已下架,前端显示“已失效”且不允许勾选。
整个购物车的状态管理逻辑,Claude Code 一次写对,我都没怎么改。唯一手动调整的是:当购物车全选状态下删除最后一件商品时,底部结算栏要自动切到“未全选”状态。这是典型的前端状态副作用,测试时注意别漏。
3.6 创建订单:库存扣减的正确姿势
createOrder是整个系统里我最看重的云函数,因为它是资金和库存流转的关口。
订单生成的完整流程:
- 前端从购物车把勾选的
cartIds传给云函数 - 云函数从数据库读取购物车条目,然后根据
goodId + skuId读取商品和 SKU 的当前数据 - 逐个校验商品状态,计算总价(以数据库实时价格为准)
- 生成唯一订单号,格式:
yyyymmddHHMMSS + 6位随机数 + 用户标识后3位 - 执行库存扣减:
sku.stock -= 数量,校验扣减后不为负数 - 生成订单记录:订单状态置为
pending_payment,记录商品快照和地址快照 - 清空对应购物车条目
- 返回订单号和前端展示的订单详情
关键点在第三步和第五步。Claude Code 写这段逻辑时,一开始用的是“先读库存,判断 >= 数量,再写入新库存”的普通方式。我直接指出:如果两个用户同时下单同一件商品,同时读到库存 5,都判断可以扣减,都写入 4,最终数据就错了。库存超卖是电商大忌。
我让它改用事务(transaction)来保证原子性。云开发数据库支持事务操作,把“判断库存并扣减”包在一个事务里,只有事务提交成功后才算抢购成功。改造后的关键代码:
const transaction = await db.startTransaction() try { const skuRes = await transaction.collection('sku').doc(skuId).get() const sku = skuRes.data if (!sku || sku.stock < quantity) { throw new Error('库存不足') } await transaction.collection('sku').doc(skuId).update({ data: { stock: sku.stock - quantity } }) // ... 写入订单等其他操作 await transaction.commit() } catch (err) { await transaction.rollback() throw err }这段代码是整篇项目里含金量最高的部分之一。电商系统里“并发导致的库存超卖”是最典型的线上事故,新手往往在“单用户测试通过”之后就以为完事了,忽略了并发场景。用事务之后,同一时间只有一个事务能成功扣减库存,其他请求要么拿到库存不足、要么等待重试,数据安全有了兜底。
提示:云开发事务对操作次数有限制(默认不超过 100 次),我们单笔订单操作次数远低于上限,放心用。
3.7 订单列表与状态流转
订单状态我定义了五个:pending_payment(待支付)、paid(已支付)、shipped(已发货)、completed(已完成)、cancelled(已取消)。
getOrderList云函数支持按照状态筛选,返回的每笔订单里包含:
- 订单号、创建时间、总价、状态
- 商品快照数组(图片、名称、单价、数量、规格)
- 收货地址(脱敏显示,手机号中间四位打码)
- 物流信息(发货后有物流单号)
前端做了三个 tab:全部、待支付、已发货。待支付订单有“取消订单”和“模拟支付”按钮,已发货订单有“确认收货”按钮。
模拟支付的实现非常实用:点击后调用云函数updateOrderStatus,传入orderNo + status: paid,云函数校验订单归属人确为当前用户后,把pending_payment改为paid。
这里的订单归属人校验,也可以看作是写这类云函数的基本功。同时在orders集合上创建了openid + status的索引,确保查询效率。
3.8 个人中心与后台统计
个人中心页面展示用户头像、昵称、地址管理入口、订单状态入口,以及一个“我是管理员”的入口(真实项目建议用管理员 openid 白名单控制)。
后台统计页调用了getAdminStats云函数,基于多个集合的聚合查询返回:
- 商品总数
- 总订单数
- 总销售额(已支付 + 已发货 + 已完成订单的总价)
- 待发货订单数
- 按分类统计的商品数量分布
这些数据只在管理后台展示,所以我对getAdminStats做了一层简单的 openid 判断,只有配置里的管理员 openid 才能调用成功。
4. 联调、测试与常见问题排查实录
4.1 联调中的三个典型报错
整个开发过程中,不可能每一步都顺利。我整理了三个最有代表性的坑,希望能帮你避雷。
第一个坑:云函数调用一直超时,控制台报Function not found
原因基本可以锁定为:云函数文件已上传到本地云端,但云端环境没有部署成功,或者环境 ID 不匹配。解决步骤是:
- 在微信开发者工具的资源管理器中,右键云函数目录,选择“上传并部署:云端安装依赖”
- 确认云开发环境 ID 与
config/index.js里配置的一致 - 在云开发控制台检查云函数列表,确认函数名正确
第二个坑:数据库权限失控,前端直接读到了所有用户的订单
这属于云开发新手最容易犯的错。数据库默认权限是“仅创建者可读写”,但如果配置成“所有用户可读”,任何用户都能遍历别人数据。
我的处理方案:前端只读商品、Banner 这类公共数据;涉及用户的cart/orders/users集合权限一律设置为“仅创建者可读写”,并且所有写操作必须走云函数。云函数里通过cloud.getWXContext().OPENID拿到当前用户身份,查询时强制带上 openid 条件,双保险。
第三个坑:本地开发一切正常,真机预览时图片加载失败
原因是云存储的图片链接默认带环境 ID,本地调试环境和你线上预览的环境可能不是同一个。我统一用fileID传给前端<image>标签,组件内部用wx.cloud.getTempFileURL来换取临时链接。或者干脆配置一个全局变量,在切换环境时自动更新存储域名。
4.2 常见问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
云函数调用报FunctionName not found | 云函数未部署或部署失败 | 右键重新上传,检查环境 ID |
| 小程序页面空白 | app.json 的 pages 列表遗漏页面 | 检查注册页面路径 |
| 数据重复展示 | 分页参数没带上 | 确认 skip 使用(page-1)*pageSize |
| SKU 选择后价格不变 | 选中状态未关联 SKU | 选中对象改为整个 SKU 对象 |
| 库存扣成负数 | 未使用事务操作 | 改用事务包裹库存扣减逻辑 |
| 真机图片 404 | 存储环境不一致 | 使用 fileID + getTempFileURL |
| 订单列表显示别人的订单 | 权限设置过宽 | 数据库权限改为仅创建者可读写 |
4.3 实测表现与性能优化
整个项目完成后的实测:
- 首页首屏加载约 0.8 秒(开发环境),云函数响应稳定在 300ms 以内
- 购物车连续添加 20 件不同类型商品,无卡顿
- 模拟两个账号同时抢购同一 SKU 的最后 1 件库存,只有一个能成功下单,另一个正确提示“库存不足”——这是事务生效的表现
- 主体代码量:前端页面 + 云函数总计约 4500 行,其中 Claude Code 生成约 90%,我手动调整约 10%
如果想进一步压缩启动时间,可以在app.js里把云开发初始化提前,并缓存部分不常变的商品分类 / Banner 数据到本地 storage。商品列表页也可以加一个骨架屏,体验会更好。这些优化动作和 Claude Code 协作实现都很方便,基本上你一句“首页加个骨架屏”它就能帮你搞定。
5. 关于 AI 协作的一点个人心得
项目跑通那一刻的爽感,说实话比手写代码来得更强烈一些。但使用 Claude Code 代打,并不是躺平,恰恰相反,它把我从写代码里腾出来,把精力投入到更重要的判断上:架构怎么定、模块怎么切、哪些逻辑必须做严、哪些坑不能踩。
几天的实战下来,我最大的感受是:你问得越清楚,它给你的东西越靠谱。拼多多式的需求描述只能得到拼多多式的代码,但条例清晰的需求(比如“库存扣减必须用事务”、“用户数据只能走云函数”)它就能按工程标准执行。在代码安全和数据一致性的关键问题上,AI 不会主动替你兜底,具体怎么设计,判断权始终在你手里。
如果你打算自己上手试,我的建议是不要从电商这种中大型项目开始,先拿一个 500 行的小工具练手;跑通一遍整个流程(初始化、生成、调试、修改)之后,再来搞电商这种多模块项目。等踩过一两轮坑,你会发现这个东西真的能成为你的另一个开发搭子。