🚀 阶段 3 – 下单 & 云数据库
目标:
- 把购物车内容写入
orders
集合,生成唯一订单号 - 用云函数保证写入安全
- 实现确认订单页 & 下单成功页(仅使用原生控件)
- 下单成功后清空购物车,并打 Git Tag
v3.0-order
1. 核心知识点
知识点 | 关键 API/概念 | 说明 |
---|
云函数事务 | db.runTransaction | 原子写多条记录,防止并发冲突 |
云函数安全 | context.OPENID | 用服务端鉴权写入 userId |
数据结构 | 嵌套数组 vs. 子表 | 小项目直接在 orders.items 存数组即可 |
唯一订单号 | Date.now() + 随机串 | 避免与支付单号冲突,保持可读性 |
UX 流程 | 确认页 → 下单中 Loading → 成功页 | 提升用户体验 |
2. 🧱 数据结构设计(orders 集合)
{"_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
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 } = eventconst { 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
<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
.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
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
<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
.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
Page({data: {orderNo: ''},onLoad(options) {this.setData({ orderNo: options.no })},back() {wx.switchTab({ url: '/pages/index/index' })}
})
6.✅ 更新购物车页结算按钮
cart/index.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
集合中出现新记录 - 本地购物车已清空,角标消失

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!