
简介这是一份基于微信小程序云开发的点餐系统项目源码适合在校学生将期末大作业、课程设计或毕业设计作为起步参考也便于刚接触小程序前后端开发的学习者快速上手。项目在云开发快速启动模板基础上改造完整覆盖点餐场景常用页面与交互代码中配有详细注释能够帮助理解小程序前后台接口调用与云函数逻辑。压缩包共47个文件包含11个json配置文件、10个js逻辑文件、9个wxss样式文件、8个png图标资源、7个wxml页面结构以及说明文档和gitignore文件整体约656KB目录结构与原生小程序工程一致便于定位和修改。所有代码均经过运行测试简单部署后即可展示效果并附有README说明。目前已有167人学习下载对于需要快速完成课程作业或初步搭建点餐小程序原型的人来说是一份轻量而完整的可参考素材也支持在此基础上做二次功能扩展。1. 微信小程序点餐系统期末大作业为什么都选它期末大作业选“点餐系统”几乎是微信小程序开发课程里最稳的题目没有之一。原因是它的业务闭环非常完整用户进入小程序看菜单、把菜品加入购物车、提交订单、商家在后端收到单子整条链路能在一个小程序里完整演示。和“记账本”“天气查询”这类单页应用相比点餐系统天然包含列表渲染、状态管理、数据持久化、页面跳转这几个小程序必考知识点评卷时一眼就能看出你写了真实业务逻辑而不是堆了一堆静态页面。这个标题里最值得注意的词是“简单部署”。大多数期末大作业死在最后一步代码写完了演示时却连不上数据库或者小程序一关数据就丢。一个能“简单部署”的点餐系统核心思路是把后端依赖降到最低——推荐直接使用微信云开发不需要自己买服务器、配域名、搞备案只需要在开发者工具里开通云环境把云函数传上去就能跑通。本文就按这个路线从工程结构、核心业务、数据模型到最后的注释与文档规范完整讲一遍我给学生改作业时最常用的一套方案。2. 点餐小程序的技术选型与工程结构怎么搭2.1 原生小程序还是 uniapp期末大作业别纠结微信小程序点餐系统有两条主流技术路线一是微信原生语法WXML WXSS JS直接写在开发者工具里二是用 uniapp 或 Taro 这类跨端框架写 Vue 或 React 语法再编译成小程序。期末大作业的场景我一般直接劝退 uniapp。原因很简单uniapp 的编译链路是“源码 - 编译 - 小程序”一旦编译环节出现问题报错信息会被框架包装过排查起来比你用原生语法多绕一层。而原生小程序打开开发者工具就能跑WXML 的渲染逻辑和页面的 JS 一一对应上课讲什么你就能写什么不需要额外的框架知识。特别是评卷老师如果要现场打开你的项目原生小程序的导入路径最短。再说项目结构。下面是一个期末大作业常见的最小目录组织每个页面一个文件夹页面相关文件都放在一起miniprogram/ ├── pages/ │ ├── index/ # 菜品列表页首页 │ ├── cart/ # 购物车页 │ ├── order/ # 订单确认页 │ └── mine/ # 我的页订单历史 ├── utils/ │ └── format.wxs # 价格格式化等辅助函数 └── app.jspages下的每个目录包含四个文件.js页面逻辑、.wxml页面结构、.wxss页面样式、.json页面配置。这种“四件套”结构是原生小程序的核心约定页面之间通过wx.navigateTo跳转公共配置放在顶层的app.json里。2.2 app.json 里的 tabBar 与页面注册一个点餐小程序至少要有三个主页面菜品浏览、购物车、个人中心。这三个页面适合放到底部 tabBar 中用户切换起来方便演示时也更像真实产品。下面是一份可以直接抄的app.json配置{ pages: [ pages/index/index, pages/cart/cart, pages/mine/mine ], window: { navigationBarTitleText: 扫码点餐, navigationBarBackgroundColor: #07c160, navigationBarTextStyle: white }, tabBar: { color: #999999, selectedColor: #07c160, list: [ { pagePath: pages/index/index, text: 点餐 }, { pagePath: pages/cart/cart, text: 购物车 }, { pagePath: pages/mine/mine, text: 我的 } ] } }代码里pages数组的第一个元素是启动页这里就是菜品列表页。tabBar的list每一项对应一个页面pagePath必须和pages里的路径完全一致否则编译时报错。navigationBarTitleText是顶部导航栏标题如果你的点餐系统是给食堂用的改成“学生食堂”这类具体场景名会显得更完整。2.3 云开发环境初始化没有服务器的部署方案标题强调“简单部署”所以我推荐把数据层放在微信云开发上。云开发提供免费的数据库、云函数和存储个人开发者不需要购买云服务器也不用关心鉴权体系——微信登录后云开发会自动帮你拿到用户的openid这个唯一标识就是区分“哪个用户下的单”的关键。初始化只需要两步。第一步在开发者工具顶部工具栏点击“云开发”按钮按提示开通环境得到一个环境 ID。第二步在app.js里添加初始化代码// app.js App({ onLaunch() { if (!wx.cloud) { console.error(当前基础库版本过低请使用 2.2.3 以上版本); return; } wx.cloud.init({ env: your-env-id, // 换成你自己的环境 ID traceUser: true }); } });env参数指定使用哪个云环境默认是第一个创建的环境。traceUser: true会在数据库访问记录里带上用户 openid方便调试时定位数据来源。这一步做完后面所有wx.cloud.database()调用都会自动复用这个环境不需要每个页面再初始化一次。3. 点餐核心功能菜品列表、购物车与下单全流程3.1 菜品列表从云数据库读取的完整链路菜品数据放在云数据库的dishes集合中每个菜品一条记录字段包含菜名、价格、图片、分类、销量等。页面启动时从数据库拉取菜品并渲染成列表这是整个点餐系统的基础。常见的做法是在页面的onLoad生命周期里直接调用数据库 API。下面是pages/index/index.js里的核心代码// pages/index/index.js const db wx.cloud.database(); Page({ data: { dishes: [], loading: true }, onLoad() { this.fetchDishes(); }, async fetchDishes() { wx.showLoading({ title: 加载菜单中 }); try { const res await db.collection(dishes) .orderBy(sales, desc) .limit(20) .get(); this.setData({ dishes: res.data, loading: false }); wx.hideLoading(); } catch (err) { console.error(拉取菜品失败, err); wx.hideLoading(); wx.showToast({ title: 菜单加载失败, icon: none }); } } });代码逻辑分三步先显示加载提示再通过db.collection(dishes).get()拉取集合里的记录最后把结果放进data中触发页面渲染。.orderBy(sales, desc)表示按销量字段倒序排列销量高的菜排前面这是一个很真实的点餐业务细节。.limit(20)限制最多拉 20 条避免一次性加载过多数据导致首屏变慢。需要注意云开发前端默认一次最多返回 20 条记录超过的部分需要通过.skip()做分页或者把拉取逻辑挪到云函数中。期末演示菜品数量一般不超过 20 个前端直查就够了。WXML 侧的渲染用wx:for循环关键点在于wx:key必须绑定唯一字段view classdish-card wx:for{{dishes}} wx:key_id view classdish-name{{item.name}}/view view classdish-price¥{{item.price}}/view button bindtapaddToCart>// utils/cart.js const CART_KEY cart_items; function getCart() { return wx.getStorageSync(CART_KEY) || []; } function addToCart(dish) { const cart getCart(); const found cart.find(item item._id dish._id); if (found) { found.count 1; } else { cart.push({ ...dish, count: 1 }); } wx.setStorageSync(CART_KEY, cart); return cart; } function updateCount(dishId, delta) { const cart getCart(); const index cart.findIndex(item item._id dishId); if (index -1) return cart; cart[index].count delta; if (cart[index].count 0) { cart.splice(index, 1); } wx.setStorageSync(CART_KEY, cart); return cart; } module.exports { getCart, addToCart, updateCount };addToCart做的事是先查缓存里有没有这道菜有就数量加一没有就新建一条记录。注意这里把整个菜品对象dish都塞进了购物车因为dishes集合里的字段可能后续调整而购物车保存的是“下单那一刻”的菜品快照这样即使菜单改了历史订单展示时菜名和价格仍然与当时一致。这是点餐系统设计里的一个关键细节。updateCount用于购物车页面加减数量当数量减到 0 时直接移除该条记录避免出现数量为 0 的脏数据。3.3 订单提交时为什么必须用云函数购物车数据存在本地但订单必须写数据库。如果把购物车数据直接从前端提交到orders集合等于告诉别人“谁都能往订单表里插数据”安全性形同虚设。更关键的是下单涉及金额计算和库存扣减这类带业务规则的写操作应该放在云函数里由服务端统一处理。下面是一个创建订单的云函数createOrder的完整代码// cloudfunctions/createOrder/index.js const cloud require(wx-server-sdk); cloud.init(); const db cloud.database(); const _ db.command; exports.main async (event) { const { cartList, tableNo, remark } event; const { OPENID } cloud.getWXContext(); if (!cartList || cartList.length 0) { return { code: 400, msg: 购物车不能为空 }; } let total 0; for (const item of cartList) { total item.price * item.count; } total Math.round(total * 100) / 100; const order { openid: OPENID, tableNo: tableNo || 0, remark: remark || , total: total, status: 1, // 1-已提交 2-制作中 3-已完成 detail: cartList, createTime: db.serverDate() }; const res await db.collection(orders).add({ data: order }); return { code: 200, orderId: res._id, total: total }; };函数开头用cloud.getWXContext()拿OPENID这是云端识别用户身份的标准方式不需要前端传任何用户信息防止伪造。总价在服务端根据购物车里的单价与数量重新计算避免用户篡改前端传入的total金额——这是支付类业务里最基本的防刷手段。Math.round(total * 100) / 100把结果保留两位小数处理 JavaScript 浮点数运算的精度丢失问题。订单状态status: 1表示用户刚提交商家侧可以按状态筛选处理。db.serverDate()由云服务器生成时间戳避免手机本地时间不准导致订单时间混乱。前端调用这个云函数时用wx.cloud.callFunction把购物车数据传上去// pages/order/order.js async submitOrder() { const cartList cartModule.getCart(); const res await wx.cloud.callFunction({ name: createOrder, data: { cartList, tableNo: this.data.tableNo } }); if (res.result.code 200) { wx.removeStorageSync(cart_items); wx.showToast({ title: 下单成功 }); wx.navigateTo({ url: /pages/mine/mine }); } }wx.cloud.callFunction的name是云函数目录名data是传给exports.main的event。下单成功清空购物车缓存并跳转到订单历史页。这里购物车数据是一次性使用的提交完必须清掉否则下次进入订单页会发现旧数据又恢复了。3.4 订单历史页的动态渲染与状态展示用户下完单之后要能看到自己的历史订单这就是mine页面的职责。逻辑上按当前用户的 openid 拉取订单记录以列表展示。查询时注意用where过滤出当前用户的数据// pages/mine/mine.js const db wx.cloud.database(); Page({ data: { orders: [] }, onShow() { this.fetchOrders(); }, async fetchOrders() { const res await db.collection(orders) .orderBy(createTime, desc) .get(); const orders res.data.map(item ({ ...item, statusText: { 1: 已提交, 2: 制作中, 3: 已完成 }[item.status], time: this.formatTime(item.createTime) })); this.setData({ orders }); } });因为云数据库默认只允许用户读取自己的记录这里的.get()不需要显式加where({ openid })权限配置会自动过滤。但如果你在云控制台把集合权限设成了“所有用户可读”那这里就必须手动加上where({ openid: {openid} })的写法。statusText映射表把数字状态转成用户能看懂的文字是点餐系统里很常见的数据字典用法。4. 数据模型设计、权限边界与库存防超卖处理4.1 三个集合的字段设计与类型约定点餐系统最少需要两张数据表dishes菜品表和orders订单表。如果要做商家端统计建议再加一张categories分类表。字段设计要克制够用就行期末大作业不需要过度设计。dishes集合建议字段如下字段类型说明_idstring云数据库自动生成主键namestring菜品名称pricenumber单价以元为单位允许小数imagestring菜品图片文件 ID 或 URLcategorystring所属分类如“热菜”“凉菜”salesnumber销量用于排序展示stocknumber库存0 表示售罄createTimedate创建时间orders集合建议字段如下字段类型说明_idstring订单号openidstring下单用户的 openidtableNonumber桌号食堂点餐可省略totalnumber订单总额单位元statusnumber1-已提交 2-制作中 3-已完成 4-已取消detailarray菜品快照数组createTimedate下单时间价格统一用“元”这个单位JavaScript 做浮点运算时要用Math.round(x * 100) / 100收尾前面已经写过。库存字段stock是后面处理超卖问题的关键。4.2 集合权限设置期末作业最容易忽略的一步把数据从云开发控制台手动录入后别忘了设置集合权限。云开发数据库的权限是分级的如果选择“所有用户可读仅创建者可读写”那就意味着任何登录用户都能看到所有的菜品合理但只能改自己创建的记录安全。点餐系统中菜品数据是管理员录入的普通用户只要读订单数据是用户自己提交的用户要能读自己的单子。推荐的权限组合是dishes集合设成“所有用户可读仅管理端可写”orders集合设成“仅创建者可读写”。在云开发控制台里找到对应集合选择权限模板即可。注意订单集合如果选了“仅创建者可读写”云函数写入的订单会带有云函数的OPENID上下文而用户前端读取自己的订单时也要确保 openid 对得上。4.3 库存的防超卖处理事务与条件更新的应用点餐系统放大到真实场景会遇到两个用户同时提交同一道只剩一份的菜结果两个人都下单成功库存变成负数。这个问题叫做“超卖”。防超卖最简单的做法是用云数据库的条件更新只有当stock 需要的数量时库存才允许扣减。在createOrder云函数里扣减库存的核心代码写成下面这样// 云函数内扣减库存 for (const item of cartList) { const res await db.collection(dishes) .where({ _id: item._id, stock: _.gte(item.count) }) .update({ data: { stock: _.inc(-item.count) } }); if (res.stats.updated 0) { return { code: 500, msg: 菜品 ${item.name} 库存不足 }; } }这段代码里的_.gte(item.count)是“库存必须大于等于购买数量”的条件_.inc(-item.count)是“在原值基础上减去购买数量”。云数据库的update操作是原子性的多个用户同时请求时数据库会在底层按顺序执行只有条件满足的那次更新才会成功。res.stats.updated是更新影响的记录数0 表示没有任何记录满足条件说明库存不够此时要回滚整个订单。期末大作业为了节省代码量可以在检测到库存不足时直接返回错误让用户重新下单生产系统则需要做事务把订单创建和库存扣减放在同一事务里保证一致性。云开发目前提供了db.runTransaction能力但代码复杂度明显更高演示场景用上面的条件更新即可。4.4 金额计算的常见坑浮点精度与前端信任点餐系统里金额是用户最敏感的数据。价格字段不能设成整数分存储再展示时转换而是直接在数据库里以元为单位保留两位小数在 JS 计算时注意浮点问题。比如0.1 0.2的结果不是0.3而是0.30000000000000004直接展示会很难看。统一做法是凡是涉及金额的计算先乘以 100 变成整数分计算完再除以 100。乘法运算没有精度问题减法也没有只有除法和普通加法会出现误差。云函数里我用了Math.round(total * 100) / 100先把结果放大到整数分再四舍五入最后缩回元这就是完整的处理方案。另外一个原则是所有金额以服务端计算为准。前端传过来的total字段只作为展示用不参与实际扣库存和计算逻辑。如果用户通过抓包工具修改前端请求把下单金额改成 0.01 元后端也不会上当因为最终金额是服务端根据菜品价格和数量重新算的。5. 部署文档、代码注释与验收前的自测清单5.1 微信开发者工具中的部署步骤与常见报错“简单部署”四个字的关键在于拿到你代码的人能否在半小时内跑起来。微信云开发项目的部署流程可以压缩成三步导入项目、开通云环境、上传云函数。第一步打开微信开发者工具选择“导入项目”填入自己的小程序 AppID测试号也可以但云开发必须要真实 AppID后端服务选择“微信云开发”。第二步点击工具栏“云开发”按钮创建环境拿到环境 ID替换app.js里的env字段。第三步在cloudfunctions目录下找到每个云函数目录右键选择“上传并部署云端安装依赖”。一个常见的问题是报错env check invalid原因是app.js里的环境 ID 写成了默认值your-env-id没有替换。另一个常见问题是云函数调用超时默认超时时间是 3 秒如果云函数里做了多次数据库读写建议在云函数配置里把超时时间改成 10 秒。在云开发控制台找到对应云函数点击配置修改超时时间即可。还有一点必须强调小程序端请求云开发数据库时要对请求做容错处理。云开发偶尔会因为网络波动返回错误代码里要把catch分支写好至少给用户一个wx.showToast提示而不是白屏。5.2 代码注释写什么注释思路而不是复述代码标题里“详细注释”是评卷时的加分项但很多学生的注释写得毫无价值比如// 定义一个变量 x let x 5;这种注释是噪音。真正有用的注释分三类第一种是“为什么”解释这段代码的存在理由比如为什么订单里要保存菜品快照第二种是“边界”说明这个参数在什么情况下会有特殊行为比如库存不足时返回 500 的处理第三种是“调用约定”说明这个函数被谁在什么时机调用。下面是一个示范// 加入购物车传入菜品对象返回更新后的购物车数组 // 如果菜品已存在数量 1否则插入新记录。 // 为什么用本地缓存加购操作频率高且不需要服务端参与。 function addToCart(dish) { // ... }代码注释不必每行都写。核心函数头部写清楚输入、输出、副作用和调用场景业务关键行写清楚“为什么这么做”其他普通赋值语句不需要注释。5.3 README 文档说明的标准模板文档说明不需要长篇大论但要能在 5 分钟内让人把项目跑起来。一个合格的项目 README 至少要包含六个部分项目简介、技术栈、目录结构、运行步骤、功能清单和常见问题。下面是一个可以直接套用的模板# 微信小程序点餐系统 ## 项目简介 扫码点餐小程序支持菜品浏览、加入购物车、提交订单、订单历史查询。 ## 技术栈 - 微信小程序原生框架 - 微信云开发云数据库 云函数 ## 目录结构 miniprogram/ 小程序前端代码 cloudfunctions/ 云函数代码 ## 运行步骤 1. 克隆代码后用微信开发者工具导入项目 2. 开通云开发环境将 app.js 中的 env 替换为你的环境 ID 3. 在云数据库创建 dishes、orders 集合 4. 在 cloudfunctions 目录右键上传所有云函数 5. 在 dishess 集合中手动添加几条菜品数据 6. 编译运行 ## 功能清单 - [x] 菜品列表展示 - [x] 菜品加入购物车 - [x] 购物车数量增减 - [x] 提交订单并写入云数据库 - [x] 订单历史查询 ## 常见问题 Q: 页面白屏 / 数据不显示 A: 检查云环境是否初始化数据库集合名是否拼写正确。5.4 验收前自测用一条完整路径把系统跑通部署完成后不要直接提交先跑一遍完整自测。推荐按下面的顺序人工检查首次打开小程序菜品列表有数据图片能加载点几道菜加进购物车购物车角标数字正确进入购物车页面加减数量后总价实时变化提交订单后云开发控制台orders集合新增一条记录且 openid 是当前用户进入“我的”页面历史订单按时间倒序展示。这五个点覆盖了标题里“点餐系统”的全部核心链路。额外的加分项是在微信开发者工具的“真机调试”模式下跑一遍确认云开发在小程序真机上能正常工作。最后在提交前清掉全部缓存——在开发者工具中点击“清缓存 - 清除全部缓存”模拟用户第一次新装打开小程序时的状态。这样做的意义是验证项目不依赖任何历史调试数据换一台机器导入也一样能跑起来这才是“简单部署”四个字的真正完成态。本文还有配套的精品资源点击获取