微信小程序点餐外卖源码拆解:云开发与支付流程详解

发布时间:2026/10/18 19:06:43
微信小程序点餐外卖源码拆解:云开发与支付流程详解 简介这是一份专为餐饮行业设计的微信小程序开箱即用方案目标用户是中小餐饮商家与具备小程序开发基础的技术人员。项目围绕在线点餐、外卖配送、堂食叫号排队、微信支付等核心链路展开同时覆盖用户账号系统、配送管理、数据分析与推送通知等模块可作为二次开发或直接部署的业务基底。包体共521个文件压缩包约839KB结构以137个JavaScript业务逻辑文件为骨架配以102个WXML页面结构、108个WXSS样式和104个JSON配置另有44个wxs辅助脚本与22个png图标资源代码组织清晰便于按模块追踪功能入口。资源内保留了bee-master核心源码仓库包含小程序前端页面与后端接口定义开发者可据此理解点餐下单、订单状态跟踪、支付回调等关键实现并结合自身需求定制菜品管理、配送对接等功能。目前已有315人学习或下载。整体而言这是一套体量紧凑但功能完整的餐饮小程序参考实现适合希望在现有框架上快速落地或进一步学习小程序全栈开发的人群。1. 餐饮点餐外卖小程序拿过来就能跑而不是只供“学习参考”很多餐饮小商家想做点餐小程序卡在“不会写代码”或者“没时间从零搭”。这套微信小程序点餐外卖项目解决的恰恰是这个问题它不是一个只有前端页面的静态 demo而是把菜单展示、购物车、订单、外卖地址、微信支付、云开发后端全部串起来的完整源码包。你解压、导入、开通云环境、上传云函数理论上两天内就能把点餐下单链路跑通。它适合三种人想给自家店快速上线点餐的小老板接触小程序电商但不想重复造轮子的新手开发者以及需要一套可改造底座来做餐饮私域运营的技术人员。开箱即用不是噱头因为项目真正的复杂度都放在云函数和数据库结构里前端页面只是薄薄一层。2. 项目架构与功能模块从顾客点餐到订单支付的数据链路想把一套源码跑起来先不用急着点按钮把它的架构看明白比写代码更重要。这个项目的整体思路很清晰前端只负责交互展示凡是涉及金额计算、订单生成、支付参数这类敏感动作全部放进云函数。这样设计的好处很直接就算有用户通过抓包篡改前端请求服务端仍然会用数据库里的真实价格重新算一遍账。2.1 技术选型为什么选择原生微信小程序搭配云开发餐饮点餐类小程序市面上有三条技术路线原生微信小程序配合自建后端套用低代码平台快速搭建原生小程序配合微信云开发。这个项目选的是第三条路。原生小程序的 wxml、wxss、JavaScript 组合虽然是微信私有的但胜在运行稳定、生态资料多不会像低代码平台那样遇到“拖拽解决不了的问题”。云开发的定位更偏向中小商家的轻量业务。它提供了云数据库、云函数、云存储、身份鉴权你不用再去买一台服务器配 Nginx、配 SSL 证书、处理 HTTPS 的备案。对刚起步的餐饮店来说少一个域名少一层运维成本。从代码维护角度看云函数本质是一个 Node.js 函数它可以访问 wx-server-sdk用起来跟写普通 Node 接口区别不大技术团队接手起来也快。这里有个选型上的设计点项目没有单独做一套商家管理后台而是让商家直接进云开发控制台改数据库里的菜品数据。这种做法在小店场景里很务实单店菜品数量多半在几十个到一两百个直接在表里编辑 JSON 反而更直观。如果后续要做多门店、多店员权限那再引入独立后台而不是一开始就把项目做成重型 CMS。2.2 功能模块清单从菜单到订单状态的全链路从用户视角看小程序可以拆出五个核心页面首页菜单、菜品详情、购物车、订单列表、个人中心。首页菜单是点餐的入口用户在分类栏切换面食、米饭、饮品等品类点“加入购物车”后数量角标会更新。菜品详情页不是纯粹展示图片它承担了用户选择规格、备注辣度、查看原材料说明的职责。购物车页面承担价格汇总、配送费展示和选中状态管理。点“去结算”时会读取当前购物车数据跳转到确认订单页这一步前端会把用户选中的地址、备注、配送方式一起带给下单云函数。订单列表页按状态分组待支付、备餐中、配送中、已完成都有对应操作按钮。个人中心则负责用户登录态和收货地址管理。从数据维度看项目至少包含四个集合users 用户表、dishes 菜品表、orders 订单表、addresses 地址表。其中 dishes 表的字段设计尽量做到“可展示即可用”不需要前端做二次加工。orders 表则是整个项目数据链路的终点所有前端产生的数据最终都会聚合到订单里。2.3 目录结构拿到源码后先看这几个关键位置├── app.js ├── app.json ├── app.wxss ├── pages/ │ ├── index/ # 首页菜单与分类展示 │ ├── detail/ # 菜品详情 │ ├── cart/ # 购物车确认页 │ ├── order/ # 订单列表与订单详情 │ └── user/ # 登录、个人中心、地址管理 ├── components/ │ └── stepper/ # 数量加减组件 ├── utils/ │ ├── config.js # 云环境 ID 配置 │ └── money.js # 金额格式化工具 ├── cloudfunctions/ │ ├── login/ # 云函数登录并获取 openid │ ├── getMenu/ # 云函数获取分类与菜品 │ ├── createOrder/ # 云函数创建订单并服务端计价 │ ├── payOrder/ # 云函数微信支付 │ └── payCallback/ # 云函数支付结果回调 └── db/ ├── dishes.json # 菜品初始化数据 ├── orders.md # 订单表字段说明 └── addresses.json # 地址表初始化数据读目录时你会注意到 pages 下面只有五个目录菜单的复杂逻辑都被抽象到了 components/stepper 里。这个设计符合“复用”思路因为菜单页、购物车页、确认订单页都可能需要增减菜品数量共用一个数量组件能避免三处各写一套加减逻辑的尴尬。cloudfunctions 列表里的 payCallback 函数容易被忽略它是支付结果异步通知的入口微信在支付成功后会把结果回调到指定云函数由它去更新订单状态。真正要动手改业务时大部分时间都花在 cloudfunctions 和 db 两个目录上。2.4 数据库设计菜单、订单、地址的字段与权限规则用云开发做这类项目集合权限一定不能偷懒。dishes 集合的权限设为“所有用户可读”因为菜单是公开数据任何点餐用户不需要登录就能看到。orders 集合权限必须收死最稳的做法是只允许通过云函数读写不让前端直接发起 orders 集合的请求避免用户构造出低价订单。dishes 表的一条典型文档长这样{ name: 招牌牛肉面, price: 28.0, category: 面食, image: cloud://food-dev.6666-food-dev-xxxx/upload/beef_noodle.png, description: 微辣可选默认加青菜, soldOut: false, sort: 1 }这里的关键字段是 price 和 soldOut。price 用数字类型而不是字符串否则前端计算购物车金额时容易出现字符串拼接最后把 20 8 算成 208 元。soldOut 用于控制售罄状态商家在云开发控制台把某道菜改成 true前端菜单页应该马上展示“已售罄”并禁止加入购物车。sort 字段用来控制菜品在同一个分类里的排序避免数据库默认的随机顺序让菜单看起来没有章法。orders 表的设计要重点说一句建议把下单时的菜品快照直接冗余进订单而不是只存 dishId。下单后商家改了价格历史订单里的金额和服务端重新计算的对不上。订单表通常包含 orderNo、openid、items、totalAmount、status、addressSnapshot、createTime。items 是数组结构每个元素包含 dishId、name、price、count。status 建议用 pending、paid、making、delivering、finished、canceled 这一组枚举值前端订单状态栏直接对应这个枚举映射。创建完集合后还要给订单表的 status 和 createTime 建一个普通索引。餐饮订单列表大概率按时间倒序查没有索引的话数据量稍大就会出现查询超时这个问题在云开发控制台的索引管理里加一条联合索引就能解决。3. 部署运行全流程AppID、云环境与云函数的四步配置“开箱即用”的保质期取决于环境配置。这个项目的代码里凡是对接微信能力的部分比如登录、支付、数据库都需要你的小程序账号先开通对应权限。下面按顺序走一遍从空白环境到真机下单的完整部署流程。3.1 部署前准备注册小程序并确认主体类型打开微信公众平台注册小程序账号。这里要特别提醒想跑通微信支付必须用企业主体注册个人主体的小程序没有支付接口权限。注册完成后进入“开发管理 - 开发设置”页面找到 AppID 并复制下来。AppID 是整个项目唯一的身份标识后续导入项目、调用云开发、配置支付都要用到。同时确认小程序是否已开通“微信云开发”。在微信开发者工具中打开任何一个项目工具栏上就能看到“云开发”按钮如果按钮是灰色无法点击先检查自己是否在企业主体账号下个人主体和未认证的小程序通常无法开通云开发环境。这一步做好了后面的配置才有意义。另外建议把“开发管理 - 服务器域名”提前看一下云开发默认不需要添加额外域名但如果后续你接入自建后台再把请求域名登记到这里。开发阶段可以靠开发者工具里的“不校验合法域名”选项绕开上线前一定要改成正版配置。3.2 导入项目微信开发者工具的首次使用安装微信开发者工具选择“小程序”模式点击导入项目目录指向源码包解压后的根目录。导入时开发者工具会读取 project.config.json自动识别 AppID 配置你只需要确认AppID 填成自己账号的正式 AppID后端服务选择“微信云开发”语言选择 JavaScript导入后不要急着预览。先点一次“编译”让开发者工具生成一个基础缓存同时观察底部 tab 栏是否正常渲染。网络请求此时会失败因为云环境还没绑定页面只显示骨架和空数据。这一步的目的是验证前端代码没有语法错误如果你看到一堆报错先检查开发者工具版本是不是太旧建议基础库调到 2.30 以上。3.3 开通云环境并绑定环境 ID点击开发者工具栏上的“云开发”图标进入控制台。首次进入会让你创建环境环境名称建议用 food-dev 这种能表达用途的英文创建时选择按量付费或基础套餐都可以开发阶段资源消耗很低。创建完成后把环境 ID 复制出来打开 utils/config.js找到类似下面的配置// utils/config.js module.exports { cloudEnv: food-dev // 替换成你自己的云环境 ID }把这个 cloudEnv 改成控制台显示的环境 ID保存后重新编译。这一步非常容易踩坑因为云开发控制台每创建一个新环境会产生不同的环境 ID如果你的项目里同时有多个环境而 app.js 里初始化时用了默认环境就会出现“当前小程序已绑定某个云环境但你调用的数据不在那个环境里”的情况。稳妥做法是把环境 ID 写死在 config.js 里并在 app.js 启动时统一读取。3.4 创建数据库集合并导入初始化数据进入云开发控制台选择数据库逐个创建 dishes、orders、users、addresses 四个集合。集合名注意只能用字母、数字和下划线建好之后暂时不需要手工添加字段。接下来把 db 目录下的 dishes.json 导入到 dishes 集合导入步骤是选择 dishes 集合点击“导入”选择 JSON 文件确认导入。导入成功之后集合列表里应能看到几十条菜品记录。初始化数据导入后还要调整集合权限。dishes 设置为“所有用户可读”addresses 设置为“仅创建者可读写”orders 设置为“仅创建者可读写”。这里有一个补充点orders 表虽然前端可以读但新增订单要通过云函数而不是前端直接 add。云函数使用服务端权限可以绕过客户端权限限制。完成导入后可以用这一小段代码在开发者工具控制台验证数据是否通const db wx.cloud.database() db.collection(dishes).limit(5).get() .then(res console.log(res.data)) .catch(err console.error(err))控制台返回 dishes 数组说明数据库连接正常返回 Permission denied 则说明权限设置有问题需要回到数据库权限里重新选。3.5 上传云函数右键部署和离线测试云函数不是打包好的静态文件而是一小段 Node.js 服务。部署时在 cloudfunctions 目录下找到对应函数文件夹右键选择“创建并部署云端安装依赖”。这会让云开发控制台自动读取 package.json 并安装 wx-server-sdk完成后本地无需再执行 npm install。四个必须部署的函数是 login、getMenu、createOrder、payOrder。如果你想测试 getMenu 有没有通可以在云开发控制台云函数列表中点击该函数选择“测试”直接运行。返回结果里能看到菜品数据就说明数据库和云函数链路已经通了一半。payCallback 函数同样需要上传它承担支付结果通知漏掉它会导致支付成功但订单状态不变。3.6 本地配置与真机预览在微信开发者工具的“详情 - 本地设置”里勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”。这个选项只影响开发者工具本地运行不会带到真机版本。勾选后重新编译首页应该能加载出菜单数据。真机预览时点击“预览”按钮生成二维码手机微信扫码打开的是开发版。开发版能调用真实的 openid 登录也能读取菜单但支付能力受限。要完整测试支付需要把代码上传为体验版在微信公众平台“成员管理 - 体验成员”里添加测试者然后手机扫码进入体验版走一遍“加购物车 - 选地址 - 提交订单 - 微信支付”全流程。体验版的请求环境与你当前云环境一致数据会真实写入 orders 集合。4. 核心模块代码拆解登录、菜单、购物车与支付的完整实现前几章把项目跑起来了接下来要回答的问题是一套点餐系统最核心的代码到底长什么样。本章按照一次真实下单的链路顺序把登录、菜单、购物车、创建订单、支付五段代码分别拆开讲。每段代码都能从源码包里找到对应文件读完再去看源码会有一种“原来如此”的感觉。4.1 用户登录链路从 wx.login 到云函数拿到 openid用户进入小程序第一件事是登录。项目没有做手机号强制绑定而是用云开发自带的 openid 来识别用户。openid 是每个微信用户在当前小程序下的唯一标识用它做账号主键不会泄露用户隐私也不怕重复。登录云函数 login/index.js 的代码逻辑如下const cloud require(wx-server-sdk) cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) exports.main async (event, context) { const db cloud.database() const wxContext cloud.getWXContext() const users db.collection(users) let user await users.where({ openid: wxContext.OPENID }).get() if (user.data.length 0) { await users.add({ data: { openid: wxContext.OPENID, nickname: event.nickname || 微信用户, avatar: event.avatarFileID || , createdAt: db.serverDate() } }) } return { code: 0, openid: wxContext.OPENID } }这段代码的核心是cloud.getWXContext()它只能在云函数端使用返回的 OPENID 是系统级数据客户端无法伪造。前端即使传入假的 nickname 或 avataropenid 永远以微信返回值准。users 表用 openid 做查询条件查不到说明是新用户直接插入一条记录下次再访问就复用已有数据。前端调用这个云函数时需要把wx.login的 code 和用户资料一起传过去。code 代表临时登录凭证云开发后台会自动把它换成用户身份。前端大致这样写wx.login({ success: async () { const res await wx.cloud.callFunction({ name: login, data: { nickname: 新顾客, avatarFileID: } }) app.globalData.openid res.result.openid } })真正生产环境建议用 button 的 open-typechooseAvatar 让用户主动授权头像和昵称而不是直接读取我的默认值。个人中心页需要头像昵称时再触发授权这样隐私合规更稳妥。4.2 菜单展示与分类切换从集合读取菜品菜单页数据来源于 dishes 集合。前端把所有菜品一次性拉回来然后在本地做分类过滤。菜品数量少时这个策略比每次切分类再请求数据库高效得多。云函数 getMenu/index.js 通常是这样的const cloud require(wx-server-sdk) cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db cloud.database() exports.main async (event, context) { const res await db.collection(dishes) .orderBy(sort, asc) .get() return { code: 0, data: res.data } }这里用了 orderBy(sort, asc)保证菜单按 sort 字段从小到大排序而不是数据库默认的随机顺序。如果你的菜品表很大超过 100 条限制可在云函数里使用分页循环获取但单店餐饮场景下目前足够。首页拿到 data 后通过wx.cloud.database()同样能获取但云函数多一层好处是可以隐藏集合名和字段结构避免客户端知道内部表名。实际项目里我更倾向于统一改为“菜单数据由云函数返回”因为后面如果要在 getMenu 里叠加“今日特价”或“售罄过滤”逻辑服务端改起来更方便。分类切换前端代码可以这样const categories [...new Set(dishes.map(item item.category))] this.setData({ categories, currentCategory: categories[0], filteredDishes: dishes.filter(item item.category categories[0]) }) switchCategory(e) { const category e.currentTarget.dataset.category this.setData({ currentCategory: category, filteredDishes: this.data.allDishes.filter(item item.category category) }) }分类和菜品用前端过滤而不是重新拉接口能明显减少等待时间。用户体验上切换到“面食”分类时几乎无延迟没有 loading 闪烁。4.3 购物车计算数量、价格和配送费的细节处理购物车是餐饮小程序最容易出隐藏 bug 的地方。价格计算如果直接在页面里用浮点数累加容易出现 0.1 0.2 0.30000000000000004 的问题。项目的正确做法是统一使用“分”作为金额单位或者每次算完用 toFixed(2) 截断。购物车页的数据结构通常是这样的Page({ data: { cart: [], totalQuantity: 0, totalAmount: 0.00, deliveryFee: 3 }, changeQuantity(e) { const { id, delta } e.currentTarget.dataset const cart [...this.data.cart] const index cart.findIndex(item item.dishId id) if (index -1) return cart[index].count delta if (cart[index].count 0) { cart.splice(index, 1) } const totalQuantity cart.reduce((s, item) s item.count, 0) const totalAmount cart .reduce((s, item) s item.price * item.count, 0) .toFixed(2) this.setData({ cart, totalQuantity, totalAmount }) } })这段代码里最关键的是.toFixed(2)。餐饮订单里总金额必须保留两位小数否则后端在转成分时可能出现 1 分钱的误差。totalQuantity 的统计用 reduce 而不是单独设置计数器能保证从购物车中间删掉一项后再计算数量仍然正确。配送费通常不参与满减逻辑但要在确认订单页单独展示给用户。购物车的持久化有两种选择一种是前端全局缓存另一种是存到云数据库。这个项目在开发阶段放在前端全局 data 里关闭小程序后购物车丢失。如果要做到跨设备保留购物车可以把购物车数据同步到 users 集合里每次用户进入首页时拉取。加购和改数量时 write 一次数据库。这个改动不影响整条链路属于体验优化项。4.4 创建订单云函数服务端重新计价才是真校验前端点“提交订单”时会把购物车里的 dishes 和新填写的收货地址传给 createOrder 云函数。创建订单这步是整个系统最核心的安全边界。const cloud require(wx-server-sdk) cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db cloud.database() exports.main async (event, context) { const { items, addressId, remark } event const wxContext cloud.getWXContext() const openid wxContext.OPENID if (!Array.isArray(items) || items.length 0) { return { code: 1, msg: 购物车不能为空 } } const dishesCol db.collection(dishes) let totalAmount 0 for (let i 0; i items.length; i) { const item items[i] const dishRes await dishesCol.doc(item.dishId).get() const dish dishRes.data if (!dish) { return { code: 1, msg: 菜品不存在 } } if (dish.soldOut) { return { code: 1, msg: ${dish.name} 已售罄 } } totalAmount dish.price * item.count } const orderNo new Date().getTime() openid.slice(-6) const addResult await db.collection(orders).add({ data: { orderNo, openid, items, totalAmount: Math.round(totalAmount * 100) / 100, remark: remark || , status: pending, addressId, createdAt: db.serverDate() } }) return { code: 0, orderId: addResult._id, orderNo, totalAmount: Math.round(totalAmount * 100) / 100 } }看到这段代码重点不是它调用了几个接口而是它用数据库最新价格重新计算了一遍 totalAmount。前端传入的 items 里可能带着自己修改过的 price但云函数里只取 item.dishId 和 item.countprice 完全以 dishes 表为准。这样就规避了“改价格下单”的漏洞。同时 soldOut 的检查放在循环里如果某道菜刚好在下单前被商家改成售罄这个订单会被拒绝而不是让用户付款后再退款。订单号 orderNo 由时间戳和 openid 尾号拼成基本可保证唯一。trade number 只用于支付系统内部不参与金额计算。支付成功后payCallback 云函数会把 orders 表里的 status 更新为 paid然后通知商户接单。4.5 微信支付云函数中的 unifiedOrder微信支付在云开发里有专门的cloud.cloudPay.unifiedOrder方法它会代替你构建支付参数并返回给前端。前提是你的小程序已经完成微信支付商户号绑定并且商户号与小程序是同一主体。payOrder 云函数长这样const cloud require(wx-server-sdk) cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db cloud.database() exports.main async (event, context) { const { orderId } event const wxContext cloud.getWXContext() const orderRes await db.collection(orders).doc(orderId).get() const order orderRes.data if (order.openid ! wxContext.OPENID) { return { code: 1, msg: 无权支付该订单 } } const payRes await cloud.cloudPay.unifiedOrder({ body: 餐饮订单-${order.orderNo}, outTradeNo: order.orderNo, spbillCreateIp: 127.0.0.1, subMchId: 你的商户号, totalFee: Math.round(order.totalAmount * 100), envId: food-dev, functionName: payCallback }) return payRes }参数 totalFee 是订单金额乘以 100表示“分”。很多第一次接支付的人在这里少乘 100导致订单金额变成 0.01 或 0.28 元。spbillCreateIp 在云函数里可以填 127.0.0.1因为真实 IP 由微信侧获取这个字段只是兼容支付接口。functionName 是支付成功后的回调云函数名回调里要再次校验订单号与金额防止伪造回调。前端拿到支付参数后调用wx.requestPaymentconst res await wx.cloud.callFunction({ name: payOrder, data: { orderId: this.data.orderId } }) if (res.result.code 0) { wx.requestPayment({ timeStamp: res.result.payment.timeStamp, nonceStr: res.result.payment.nonceStr, package: res.result.payment.package, signType: RSA, paySign: res.result.payment.paySign, success: () { wx.showToast({ title: 支付成功 }) }, fail: () { // 用户取消或支付失败订单仍为 pending } }) }失败分支里不需要急着改订单状态因为用户可能只是中途取消。等支付回调成功再更新状态最可靠。如果支付后回调没有触发去云开发控制台看 payCallback 的日志确认是否有异常。4.6 外卖地址用微信原生组件还是自维护地址管理在外卖场景下几乎是必选项。项目早期可以用wx.chooseAddress获取微信里保存的收货地址直接返回完整字段。它会唤起微信原生的地址选择面板用户确认之后结果通过回调返回给小程序。async chooseAddress() { try { const res await wx.chooseAddress() await wx.cloud.database().collection(addresses).add({ data: { openid: app.globalData.openid, name: res.userName, phone: res.telNumber, province: res.provinceName, city: res.cityName, county: res.countyName, detail: res.detailInfo, isDefault: false } }) } catch (e) { console.error(取消选择地址, e) } }从wx.chooseAddress返回的字段名与平常的表单不一致比如 userInfo 里的收货人叫 userName电话叫 telNumber不能用惯例猜测。如果项目要多次下单并保留历史地址需要把获取到的地址存进 addresses 集合并在确认订单页列出让用户选中一条作为 addressId。5. 避坑指南开箱即用背后常见的 6 个部署问题源码能跑和在你环境里能跑是两回事下面这些问题我在部署这类小程序时基本每次都会遇到。按“现象、原因、解决”三段式写清楚对照排查效率最高。5.1 云函数初始化报错Env 不能为空现象打开开发者工具后点击菜单页没有数据打开云函数日志显示Cloud API isnt configured: env或Environment not found。原因云函数里的cloud.init()没有指定环境。开发者工具能自动识别当前环境但云函数部署到云端后不认本地设置它需要一个明确的环境 ID。解决在每个云函数 index.js 开头统一改成cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })DYNAMIC_CURRENT_ENV 会自动使用云函数当前所在环境避免不同环境下反复改配置。改完重新上传并部署。5.2 金额显示 NaN 或 0.30000000000000004现象购物车页面显示 NaN修改数量后金额变成一长串小数或者支付金额比菜单价格少 1 分钱。原因数据库 price 字段是字符串比如28前端在数字运算时字符串自动转换为数字但遇到null或空字符串时会得到 NaN。浮点相加也容易出现尾数问题。解决前端统一用Number(item.price)转换或者在上文提到数据库导入时把 price 字段类型改为数字。结算金额计算后调用.toFixed(2)截断支付金额在服务端再乘 100 并取整Math.round(totalAmount * 100)。5.3 微信支付调不起来商户号缺失或未绑小程序现象点“去支付”没有反应云函数 payOrder 返回 code 500日志里写着subMchId invalid或appid and mch_id 不匹配。原因小程序没有绑定微信支付商户号或者 payOrder 云函数里的 subMchId 填了别人家的商户号。个人主体小程序也无法开通微信支付。解决先用企业主体账号完成微信支付商户号申请然后在微信公众平台“微信支付”菜单里关联商户号。把商户号填到 payOrder 云函数里重新部署。如果只是体验流程可以在 createOrder 返回后模拟把订单状态标记为 paid跳过真实支付。5.4 真机预览菜单为空但开发者工具有数据现象开发者工具编译后菜单正常真机扫码进入却空白或提示 401。原因常见的是环境 ID 不一致。开发者工具默认连接的是第一个云环境真机预览版读取的却是 config.js 里的环境 ID如果 config.js 里没有改或者 app.js 初始化时写死了一个不存在的环境就会导致访问不到数据库。解决打开 utils/config.js确认 cloudEnv 与云开发控制台里的环境 ID 完全一致重新编译并生成预览码。5.5 订单创建成功但订单列表查不到现象用户在确认订单页提交订单成功回到订单列表页却是空的。原因orders 集合的权限设置了“仅创建者可读写”但订单列表页通过云函数或者用前端查询时用了另一个 openid 条件。最常见的是前端没有登录成功提交订单时 openid 为空写入的订单里 openid 字段为空列表查询自然匹配不上。解决登录后再允许进入下单流程。创建订单云函数返回 orderId前端拿到 orderId 后可以直接追加到本地订单列表同时订单列表云函数按 openid 查询。建议给 orders 集合按 openid 建索引避免 all 查询超时。5.6 小程序审核被拒餐饮类目资质不全现象把代码上传为审核版审核结果提示“服务类目与页面内容不符”且没有通过。原因小程序名字或页面内容有“外卖”“点餐”字样但注册时选择的服务类目是“工具”没有提交餐饮相关的食品经营许可证。解决在微信公众平台“设置 - 服务类目”中改为“餐饮 - 外卖点餐”或“餐饮 - 餐饮服务”上传营业执照和食品经营许可证。如果只是内部测试不提交审核通过体验版访问即可绕开审核问题。6. 二次开发技巧把模板改成你自己的点餐品牌源码真正值钱的地方在于能快速改成自己的业务。本章讲三个二次开发最常见的改动点分别是改菜单品牌、增加桌台扫码点餐、替换云开发为自建后端。6.1 改店铺信息与菜品数据不写代码也能维护如果只是想换成自家店的菜品和价格直接进云开发控制台找到 dishes 集合像编辑表格一样改文档里的 name、price、image 字段。菜品的 image 字段建议先上传到云存储再把云存储文件 ID 填进来不要直接粘贴外部图片链接外部图片域名如果不加白名单真机里会加载失败。首页的店名、公告、营业时间多半写在 app.js 或 pages/index/index.js 里的一个 storeInfo 对象里。这个对象没有统一配置入口需要你全局搜索“店名”或“营业时间”关键字替换成自己的。如果你不希望开发者每次去改源码可以把店铺信息也存到数据库 settings 集合前端获取菜单时同时拉取 settings这样商家自己就能通过控制台维护。6.2 增加桌台扫码点餐不用改支付链路想做堂食扫码点餐可以在这套模板基础上加一个“桌台号”字段。先在 user 下单链路里新增一个 tableNo 字段不需要动支付逻辑。前端生成堂食二维码时把桌台号拼进小程序码参数里。用户扫码进入小程序页面 onLoad 解析options.scene或options.qonLoad(options) { if (options.scene) { const scene decodeURIComponent(options.scene) const tableNo scene.split(tableNo)[1] this.setData({ tableNo }) } }下单时把 tableNo 写入 orders 表商家订单列表就能看到“12 号桌点了 3 个菜”这样的信息。这个改动的成本极低但能覆盖堂食场景让小程序从单纯外卖延伸到店内点餐。6.3 把云开发替换成自建后端给你自己的服务端留接口如果团队已经有 Java 或 Node 后端不打算用云开发最平滑的改法是保留所有前端页面只把数据请求层换掉。小程序里凡是调用wx.cloud.callFunction的地方统一替换成 utils/request.js 封装好的request()方法该方法内部改用wx.request。例如获取菜单的接口从云函数切换为后端接口// utils/request.js function request(url, data, method POST) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method, data, header: { Authorization: Bearer ${app.globalData.token} }, success: resolve, fail: reject }) }) } // 页面里调用 async loadMenu() { const res await request(/menu/list, { page: 1 }) this.setData({ dishes: res.data.data }) }后端接口建议照搬云函数里的计价逻辑尤其是 createOrder 里重新遍历菜品价格的那个循环无论后端用什么语言都必须保留。替换完数据层再把 app.js 里的云开发初始化删掉小程序就与微信云开发解耦了。自从接触这类模板我养成了一个习惯接手任何一套新源码先写一个最小冒烟测试把登录、菜单列表、加购、下单这四条链路走通再开始改业务。这样后面做二次开发时遇到问题能第一时间定位是模板本身的问题还是我改出来的问题。如果你正在做点餐外卖小程序希望这篇拆解能帮你少踩几个坑。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询