微信小程序实战案例 - 餐馆点餐系统 阶段 3 - 下单 & 云数据库

🚀 阶段 3 -- 下单 & 云数据库

目标:

  1. 把购物车内容写入 orders 集合,生成唯一订单号
  2. 用云函数保证写入安全
  3. 实现确认订单页 & 下单成功页(仅使用原生控件)
  4. 下单成功后清空购物车,并打 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. 添加 1~2 道商品进购物车
  2. 进入购物车页面 → 点击「去结算」
  3. 确认页显示商品、价格、总价
  4. 点击「提交订单」 → 跳转成功页
  5. 云开发控制台 → 数据库 → orders 集合中出现新记录
  6. 本地购物车已清空,角标消失
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!

相关推荐
徒 花3 分钟前
数据库知识复习07
数据库·作业
素玥19 分钟前
实训5 python连接mysql数据库
数据库·python·mysql
jnrjian26 分钟前
text index 查看index column index定义 index 刷新频率 index视图
数据库·oracle
瀚高PG实验室43 分钟前
审计策略修改
网络·数据库·瀚高数据库
言慢行善1 小时前
sqlserver模糊查询问题
java·数据库·sqlserver
韶博雅1 小时前
emcc24ai
开发语言·数据库·python
有想法的py工程师1 小时前
PostgreSQL 分区表排序优化:Append Sort 优化为 Merge Append
大数据·数据库·postgresql
迷枫7122 小时前
达梦数据库的体系架构
数据库·oracle·架构
夜晚打字声2 小时前
9(九)Jmeter如何连接数据库
数据库·jmeter·oracle
Chasing__Dreams2 小时前
Mysql--基础知识点--95--为什么避免使用长事务
数据库·mysql