🚀 阶段 3 -- 下单 & 云数据库
目标:
- 把购物车内容写入
orders
集合,生成唯一订单号
- 用云函数保证写入安全
- 实现确认订单页 & 下单成功页(仅使用原生控件)
- 下单成功后清空购物车,并打 Git Tag
v3.0-order
1. 核心知识点
知识点 |
关键 API/概念 |
说明 |
云函数事务 |
db.runTransaction |
原子写多条记录,防止并发冲突 |
云函数安全 |
context.OPENID |
用服务端鉴权写入 userId |
数据结构 |
嵌套数组 vs. 子表 |
小项目直接在 orders.items 存数组即可 |
唯一订单号 |
Date.now() + 随机串 |
避免与支付单号冲突,保持可读性 |
UX 流程 |
确认页 → 下单中 Loading → 成功页 |
提升用户体验 |
2. 🧱 数据结构设计(orders 集合)
json
复制代码
{
"_id": "auto",
"orderNo": "OD20250410-123456",
"userId": "openid_xxx",
"items": [
{ "dishId": "abc123", "name": "宫保鸡丁", "price": 18, "count": 2 },
{ "dishId": "def456", "name": "酸辣汤", "price": 10, "count": 1 }
],
"totalPrice": 46,
"status": "PENDING",
"createdAt": 1712710000000
}
3. ✅ 云函数:addOrder
目录结构
复制代码
cloudfunctions/addOrder/
└── index.js
index.js
js
复制代码
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
exports.main = async (event, context) => {
console.log('接收到的参数:', event)
const { cartItems, totalPrice } = event
const { OPENID } = cloud.getWXContext()
if (!cartItems || cartItems.length === 0) {
return { ok: false, msg: '购物车为空' }
}
const orderNo = `OD${new Date().toISOString().slice(0,10).replace(/-/g,'')}-${Date.now().toString().slice(-6)}-${Math.random().toString(36).substr(2,3)}`
try {
await db.collection('orders').add({
data: {
orderNo,
userId: OPENID,
items: cartItems,
totalPrice,
status: 'PENDING',
createdAt: Date.now()
}
})
return { ok: true, orderNo }
} catch (e) {
console.error('订单添加失败:', e)
return { ok: false, msg: '下单失败' }
}
}
4. ✅ 页面:确认订单页(pages/confirm/index)
index.wxml
xml
复制代码
<view class="page">
<block wx:for="{{list}}" wx:key="_id">
<view class="item">
<text class="name">{{item.name}}</text>
<text class="count">x{{item.count}}</text>
<text class="price">¥{{item.price}}</text>
</view>
</block>
<view class="total">
共 {{totalCount}} 件,总计 ¥{{totalPrice}}
</view>
<button type="primary" bindtap="onSubmit">提交订单</button>
</view>
index.wxss
css
复制代码
.page {
padding: 20rpx;
}
.item {
display: flex;
justify-content: space-between;
margin-bottom: 12rpx;
background: #fff;
padding: 16rpx;
border-radius: 8rpx;
}
.name {
font-weight: bold;
}
.count, .price {
color: #555;
}
.total {
font-size: 30rpx;
font-weight: bold;
margin: 20rpx 0;
text-align: right;
}
button {
width: 100%;
height: 88rpx;
font-size: 32rpx;
}
index.js
js
复制代码
const cart = require('../../store/cart')
Page({
data: {
list: [],
totalCount: 0,
totalPrice: 0
},
onLoad() {
const items = Object.values(cart.data.items)
if (!items.length) {
wx.navigateBack()
return
}
this.setData({
list: items,
totalCount: cart.totalCount(),
totalPrice: cart.totalPrice()
})
},
async onSubmit() {
wx.showLoading({ title: '下单中...', mask: true })
const res = await wx.cloud.callFunction({
name: 'addOrder',
data: {
cartItems: this.data.list,
totalPrice: Number(this.data.totalPrice)
}
})
wx.hideLoading()
if (res.result.ok) {
cart.data.items = {}
cart.save()
wx.removeTabBarBadge({ index: 1 })
wx.redirectTo({
url: `/pages/order-success/index?no=${res.result.orderNo}`
})
} else {
wx.showToast({ title: res.result.msg || '下单失败', icon: 'none' })
}
}
})
5.✅ 页面:下单成功页(pages/order-success/index)
index.wxml
xml
复制代码
<view class="page success">
<image src="/icons/success.png" class="icon" />
<text class="title">下单成功!</text>
<text class="sub">订单号:{{orderNo}}</text>
<button bindtap="back">返回首页</button>
</view>
index.wxss
css
复制代码
.success {
padding: 80rpx;
text-align: center;
}
.icon {
width: 120rpx;
height: 120rpx;
margin-bottom: 20rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
}
.sub {
margin: 20rpx 0 60rpx;
color: #666;
}
button {
width: 60%;
font-size: 32rpx;
}
index.js
js
复制代码
Page({
data: {
orderNo: ''
},
onLoad(options) {
this.setData({ orderNo: options.no })
},
back() {
wx.switchTab({ url: '/pages/index/index' })
}
})
6.✅ 更新购物车页结算按钮
cart/index.js 中新增方法:
js
复制代码
onCheckout() {
if (!this.data.totalCount) {
wx.showToast({ title: '购物车为空', icon: 'none' })
return
}
wx.navigateTo({ url: '/pages/confirm/index' }) // 下一阶段页
}
7.✅ Git Tag & 测试 Checklist
- 添加 1~2 道商品进购物车
- 进入购物车页面 → 点击「去结算」
- 确认页显示商品、价格、总价
- 点击「提交订单」 → 跳转成功页
- 云开发控制台 → 数据库 →
orders
集合中出现新记录
- 本地购物车已清空,角标消失

bash
复制代码
git add .
git commit -m "feat: confirm order"
git tag v3.0-order
git push --tags
8. 权限 & 错误处理要点
场景 |
做法 |
空购物车 |
进入确认页时检测,无数据就 navigateBack |
网络超时 |
云函数 catch 后返回 { ok:false } ,前端 Toast |
多端并发 |
事务内后续可加库存校验(大店才需要) |
店员修改状态 |
先留接口,下一阶段实现 |
9. 练习(加深理解)
难度 |
练习内容 |
⭐ |
在 orders 文档增加 remarks 字段,确认页让用户填写"口味/忌口"。 |
⭐⭐ |
扫桌码:在桌码二维码中带 tableNo 参数,进入小程序后自动写入订单。 |
⭐⭐ |
生成 取餐号 :如 A08-001 (桌号‑序号),写入 orders.pickNo ,成功页展示。 |
⭐⭐⭐ |
给 addOrder 云函数加 幂等校验 :同一 orderNo 不重复写入。 |
阶段小结
- 你已完成 购物车 → 确认 → 下单 → 云数据库持久化 的完整闭环
- 代码量 ≈ 350 行,正式迈入"后端交互"阶段
- 下一步 阶段 4 -- 订单列表 & 状态:让用户和店员都能查看并更新订单进度
继续冲刺,Happy Coding!