简介:本资源是一套专为餐饮行业商户定制的微信小程序源码模板,面向小程序初学者、中小型商户技术负责人及快速原型开发者,解决从零搭建点餐系统耗时长、功能模块不完整、适配微信规范难等实际问题。压缩包共202个文件,含32个JS逻辑层代码、28个WXML视图结构、28个WXSS样式文件、28个JSON配置项及84个PNG图标资源,辅以GIF加载动画与gitignore等工程配置,整体仅689KB,轻量易读、结构清晰,便于二次开发与品牌定制。目前已有46人学习下载,适合希望快速上线外卖点单、订单管理、菜品展示与在线支付等功能的实战场景。源码严格遵循微信官方开发规范,涵盖index.js主入口、service-control与merchandise-control等业务控制层、edit-mine-info等用户模块,提供可直接运行的完整骨架,大幅缩短开发周期,降低试错成本。
1. 这不是又一个“点餐页面+轮播图”的空壳模板:它是一套能直接跑通「商户入驻—菜品上架—用户下单—后台核销」闭环的微信小程序源码,含真实可用的云开发结构、带权限控制的管理端和适配iOS/Android双端的UI组件库
你可能已经下载过十几个标着“餐饮模板”的小程序源码包,解压后发现只有首页轮播、菜单分类、底部TabBar——连登录态都靠wx.setStorageSync硬存,更别说商户怎么进后台、订单怎么查、核销码怎么生成。这次不一样。这个【微信小程序源码】餐饮商户模板.zip是某高校实验室为本地3家社区餐饮店落地验证过的完整工程,不是Demo,不是教学示例,而是真正在微信开发者工具里能一键预览、在真机上扫码可订、后台能实时看到新订单的生产级代码包。它用云开发替代传统服务器,省掉域名备案、HTTPS配置、Node.js部署这些新手最易翻车的环节;所有接口调用走云函数,数据库集合按角色隔离(商户库/用户库/订单库),连核销员扫码核销时的权限校验逻辑都写死了。适合两类人:一是想快速上线轻量餐饮服务的小商家技术对接人,二是刚学完小程序基础、卡在“怎么把页面串成业务流”这一步的前端开发者。别再从零搭登录、写订单状态机、反复调试云数据库权限了——这套源码里,每个.js文件名后面都跟着一句注释:“此处已接入真实支付回调校验”。
2. 拆包即用:从解压到真机扫码,5步完成本地运行与基础配置
2.1 解压后目录结构解析:看清哪些文件是核心、哪些可删、哪些必须改
解压【微信小程序源码】餐饮商户模板.zip后,你会看到标准的微信小程序项目结构,但关键在于识别出不可删减的核心模块和必须修改的配置锚点:
├── cloudfunctions/ # 云函数目录(全部必需,含login、orderCreate、verifyCodeGenerate等) ├── miniprogram/ │ ├── app.js # 全局App逻辑,含云开发初始化(重点看第12行) │ ├── project.config.json # 项目配置,含appid(需替换!) │ ├── sitemap.json # 搜索优化配置,可删但不建议 │ └── pages/ │ ├── index/ # 用户端首页(含商户列表、搜索、轮播) │ ├── merchant/ # 商户入驻页(表单提交直连云函数) │ ├── order/ # 订单页(含地址选择、支付跳转、状态轮询) │ └── admin/ # 管理端(仅限指定openid访问,见3.2节) ├── project.private.config.json # 私有配置(含云环境ID,必须填!) └── README.md # 实际包含4个关键配置项说明(非通用模板)提示:
project.private.config.json是本项目真正的“心脏”。它不上传云端,只存本地,里面env字段必须填你自己的云开发环境ID(格式如my-env-12345),否则所有云函数调用都会返回Error: env not found。这个文件在Git中已被忽略,但解压后必须手动编辑。
2.2 替换两个ID:appid与云环境ID,缺一不可
微信小程序运行的前提是合法身份标识。本模板要求你准备两个ID:
- 你的小程序 AppID:在 微信公众平台 > 开发管理 > 开发基本信息中获取。
- 你的云开发环境ID:在 云开发控制台 > 环境列表中创建一个新环境(地域选
上海或广州,避免跨域问题),复制环境ID。
替换位置有两个:
miniprogram/project.config.json中"appid"字段:{ "description": "项目配置文件", "setting": { ... }, "appid": "wx1234567890abcdef", // ← 替换为你自己的AppID "condition": { ... } }project.private.config.json中"env"字段:{ "env": "prod-abc123", // ← 替换为你创建的云环境ID "region": "ap-shanghai" }
注意:
project.private.config.json文件名带.private后缀,是微信开发者工具识别的私有配置文件,不会被上传到远程仓库或云端。它只在你本地生效,所以可以安全存放敏感配置。如果误删,重新建一个同名文件即可,内容只需这两行。
2.3 在开发者工具中一键上传云函数:别手动一个个部署
云函数是本模板的业务中枢,共12个,覆盖从用户登录鉴权、菜品增删改查、订单创建与状态更新、到核销码生成与验证全流程。切勿在开发者工具里右键单个云函数 → “上传并部署”,因为部分函数存在依赖关系(如orderCreate依赖utils/orderHelper.js),手动上传会因加载顺序错乱导致require error。
正确做法是:
- 打开微信开发者工具,确保左上角“云开发”开关已开启;
- 在左侧项目树中,右键点击
cloudfunctions文件夹 → 选择“上传所有云函数”; - 勾选“上传时自动触发安装依赖”和“上传后自动部署”;
- 点击确定,等待右下角提示“全部云函数上传成功”。
上传完成后,立即去 云开发控制台 > 云函数列表,确认所有函数状态为“运行中”,且最近调用时间是当前时间。若某个函数显示“初始化失败”,大概率是package.json中"engines": {"node": "16.x"}与你本地Node版本不匹配——此时需在开发者工具设置中将“云函数Node版本”改为16.19.1(本模板实测兼容版本)。
2.4 首次真机扫码前必做的三件事:检查登录态、测试云数据库、验证支付沙箱
很多开发者卡在“开发者工具能跑,真机扫不出首页”,根本原因是真机环境缺失关键初始化步骤。请严格按顺序执行:
清空真机微信缓存:微信 → 我 → 设置 → 通用 → 存储空间 → 小程序 → 找到你的小程序 → 删除。这是最常被忽略的一步,旧缓存中的
token或openid残留会导致app.js中wx.cloud.callFunction({name:'login'})返回401。手动在云数据库中插入一条测试商户数据:
进入云开发控制台 → 数据库 → 新建集合merchants→ 插入如下文档(注意_id必须为字符串,不能是ObjectId):{ "_id": "mch-test-001", "name": "测试餐厅", "address": "XX路123号", "phone": "13800138000", "status": "approved", "createdAt": "2024-06-01T08:00:00Z" }此操作让首页商户列表有数据可渲染,避免白屏。
启用支付沙箱环境并配置密钥:
本模板使用微信支付V3接口,但未强制要求正式商户号。首次测试用沙箱环境即可:- 登录 微信支付商户平台 → 账户中心 → API安全 → 沙箱密钥 → 下载
apiclient_key.pem; - 将该文件放入
cloudfunctions/paySandbox/目录下; - 修改
cloudfunctions/paySandbox/index.js第7行const SANDBOX_KEY_PATH = './apiclient_key.pem'。
沙箱支付无需真实资金,回调地址也无需备案,是本地联调支付流程的后悔药。
- 登录 微信支付商户平台 → 账户中心 → API安全 → 沙箱密钥 → 下载
完成以上三步,打开真机微信,扫描开发者工具右上角二维码,首页应正常显示“测试餐厅”轮播图与菜单分类。若仍白屏,请直接跳到第4章排查。
3. 权限与角色设计:为什么普通用户打不开/admin页面?商户如何进入自己的管理后台?
3.1 三角色权限模型:user / merchant / admin,全由云数据库roles集合驱动
本模板没有用复杂的RBAC(基于角色的访问控制)框架,而是用极简但可靠的“集合+字段”方案实现权限隔离:
- 用户(user):通过微信授权登录,
openid存于云数据库users集合,无特殊字段; - 商户(merchant):入驻审核通过后,系统向
merchants集合插入文档,并同步向roles集合写入一条记录:{ "_id": "role_mch_abc123", "role": "merchant", "targetId": "mch-test-001", // 对应merchants._id "authOpenid": "oAbcDeFgHiJkLmNoPqRsTuVwXyZ" // 商户管理员的openid } - 管理员(admin):
roles集合中role === 'admin'的记录,authOpenid填你自己的微信openid(可在开发者工具Console中执行wx.getStorageSync('openid')获取)。
所有页面的权限校验逻辑统一收口在miniprogram/utils/auth.js的checkRole()函数中。例如,pages/admin/index.js在onLoad时会调用:
// pages/admin/index.js const auth = require('../../utils/auth') Page({ onLoad() { auth.checkRole('admin').then(hasAuth => { if (!hasAuth) { wx.showToast({title: '无权限', icon: 'none'}) setTimeout(() => wx.navigateBack(), 1500) } }) } })逻辑说明:
checkRole()内部执行云数据库查询db.collection('roles').where({role: targetRole, authOpenid: currentOpenid}).get(),只查到结果才放行。这意味着——只要没在roles集合里给你加一条role: 'admin'的记录,你就永远打不开/admin页面。这是硬性权限,不是前端路由拦截,无法绕过。
3.2 商户入驻流程:从填写表单到获得独立后台入口的完整链路
商户不是后台人工添加的,而是通过用户端页面自助入驻。流程如下:
- 用户打开
/pages/merchant/apply.js页面,填写餐厅名称、地址、联系人、电话、上传营业执照照片(调用wx.chooseImage+wx.cloud.uploadFile); - 点击“提交”后,前端调用云函数
merchantApply,传入表单数据; cloudfunctions/merchantApply/index.js接收数据,执行:- 校验手机号格式、图片是否上传成功;
- 自动生成唯一
_id(如mch-20240601-001); - 向
merchants集合插入待审状态文档(status: 'pending'); - 向
roles集合插入一条role: 'merchant'记录,targetId指向刚生成的商户ID,authOpenid为当前提交者openid;
- 提交成功后,页面跳转至
/pages/merchant/waiting.js,显示“审核中”,并提示“审核通过后,您将收到消息通知,并可在‘我的’页面进入管理后台”。
关键参数说明:
merchantApply函数中第23行const newMerchantId =mch-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`` 是防重ID生成策略,保证高并发下不冲突;而roles集合的写入是原子操作,避免出现“商户入库成功但权限丢失”的脏数据。
3.3 管理后台的动态路由:为什么每个商户看到的/admin页面内容不同?
/pages/admin/index.js并不是一个静态页面,而是一个根据当前用户角色动态渲染的容器页。其核心逻辑在onLoad中:
// pages/admin/index.js onLoad() { const openid = wx.getStorageSync('openid') // 1. 查自己在roles中的记录 db.collection('roles').where({authOpenid: openid}).get() .then(res => { if (res.data.length === 0) return const roleDoc = res.data[0] // 2. 根据role类型加载不同子页面 if (roleDoc.role === 'merchant') { this.setData({ currentPage: 'merchantDashboard' }) // 加载商户专属数据:订单统计、菜品列表、核销记录 } else if (roleDoc.role === 'admin') { this.setData({ currentPage: 'adminDashboard' }) // 加载全平台数据:所有商户列表、总订单数、投诉处理 } }) }因此,当你用商户A的微信扫码进入/admin,页面实际渲染的是pages/admin/merchantDashboard.wxml,其数据全部来自db.collection('orders').where({merchantId: 'mch-test-001'}).count()等商户维度查询;而用管理员微信进入,则渲染adminDashboard,查询的是全量数据。这种设计避免了为每个角色单独建一套页面,复用率高,维护成本低。
4. 避坑指南:那些让你调试到凌晨三点、却只因一个空格的致命细节
4.1 现象:开发者工具能运行,真机扫码首页白屏,Console无报错
原因:真机微信未开启“调试基础库”,导致云开发API(如wx.cloud.database())在低版本基础库中不可用。本模板最低要求基础库版本2.25.0。
解决:真机微信 → 我 → 设置 → 普通设置 → 发现页管理 → 关闭“发现页小程序”开关 → 重启微信 → 重新扫码。此操作会强制微信更新基础库至最新稳定版。若仍无效,在开发者工具详情 → 本地设置 → 勾选“调试基础库”,再试。
4.2 现象:云函数orderCreate调用失败,日志显示Error: Cannot find module './utils/orderHelper'
原因:cloudfunctions/orderCreate/index.js第5行const helper = require('./utils/orderHelper')中路径错误。该utils文件夹实际位于cloudfunctions/orderCreate/同级目录,而非其子目录。
解决:将require('./utils/orderHelper')改为require('../utils/orderHelper')。注意:云函数上传时,开发者工具不会校验相对路径有效性,必须手动检查所有require语句的..层级。
4.3 现象:商户上传营业执照图片后,在管理后台看不到,云存储控制台显示文件为空
原因:wx.cloud.uploadFile调用时,cloudPath参数未拼接文件扩展名。原代码中const cloudPath = 'merchant/' + Date.now() + '.jpg'固定写死.jpg,但用户可能选PNG或HEIC格式。
解决:在pages/merchant/apply.js中,chooseImage回调内动态获取后缀:
wx.chooseImage({ success: res => { const tempFilePath = res.tempFilePaths[0] const ext = /\.\w+$/.exec(tempFilePath)[0] // 如.png const cloudPath = 'merchant/' + Date.now() + ext wx.cloud.uploadFile({ cloudPath, filePath: tempFilePath }) } })4.4 现象:用户下单后,商户后台“待处理订单”数量不增加,云数据库orders集合无新文档
原因:cloudfunctions/orderCreate/index.js中,db.collection('orders').add()调用前,未对data对象做深拷贝,导致data.createdAt = new Date()被序列化为Invalid Date,云数据库拒绝写入。
解决:在add()前添加时间字段标准化:
const now = new Date() data.createdAt = now.toISOString() // 必须转为ISO字符串 data.updatedAt = now.toISOString() db.collection('orders').add({ data })云数据库只接受ISO 8601格式字符串作为时间字段,new Date()对象直接传入会被忽略。
4.5 现象:核销员扫码核销时,提示“该订单已核销”,但订单状态仍是unverified
原因:cloudfunctions/verifyCodeVerify/index.js中,db.collection('orders').doc(orderId).update()使用了set而非update,导致整条文档被覆盖,原有字段(如customerName,phone)丢失,后续查询失效。
解决:严格使用update方法,并只更新目标字段:
db.collection('orders').doc(orderId).update({ data: { status: 'verified', verifiedAt: new Date().toISOString(), verifierOpenid: context.OPENID } })set会重置整个文档,update才是原子更新。
5. 核销码生成与验证:从随机字符串到真机扫码的端到端实战
5.1 核销码不是简单MD5:它由三段加密信息构成,兼顾安全性与可追溯性
很多模板用Math.random().toString(36).substr(2, 8)生成8位核销码,看似随机,实则可暴力穷举。本模板采用三段式加密核销码,每段承担不同职责:
| 段位 | 长度 | 内容 | 作用 |
|---|---|---|---|
| 前缀 | 2位 | V1或V2 | 版本标识,便于未来升级算法 |
| 主体 | 10位 | orderId的Base32编码(去除了0,O,I,l等易混淆字符) | 绑定订单,不可伪造 |
| 校验 | 4位 | HMAC-SHA256(orderId + secretKey, timestamp)的截断 | 防重放、防篡改 |
生成逻辑在cloudfunctions/verifyCodeGenerate/index.js:
const crypto = require('crypto') const base32 = require('base32-encode') exports.main = async (event, context) => { const { orderId } = event const timestamp = Math.floor(Date.now() / 60000) * 60000 // 精确到分钟 const secretKey = 'your-secret-key-here' // 应从环境变量读取,此处为示意 // 1. Base32编码orderId(去混淆) const cleanOrderId = orderId.replace(/[^a-zA-Z0-9]/g, '') const encodedId = base32.encode(Buffer.from(cleanOrderId), 'Crockford') // 2. 生成HMAC校验码 const hmac = crypto.createHmac('sha256', secretKey) hmac.update(`${orderId}${timestamp}`) const digest = hmac.digest('hex').substr(0, 4).toUpperCase() return `${'V1'}${encodedId.substr(0, 10)}${digest}` }参数说明:
timestamp精确到分钟,意味着同一订单在1分钟内生成的核销码相同,超过1分钟则失效——这是防重放的核心。base32.encode(..., 'Crockford')使用Crockford Base32变种,自动剔除易混淆字符,提升用户手动输入准确率。
5.2 真机扫码验证:如何用手机微信“扫一扫”调起核销页面?
核销不是让用户手动输码,而是扫码跳转。实现分两步:
第一步:在订单详情页生成带参数的二维码pages/order/detail.js中,当订单状态为paid时,调用云函数生成核销码,再用wx.canvasToTempFilePath绘制二维码(使用weapp-qrcode库):
// 生成核销码 const res = await wx.cloud.callFunction({ name: 'verifyCodeGenerate', data: { orderId } }) const code = res.result // 绘制二维码(code为字符串,非URL) const qrcode = QRCode.draw(code, { width: 200, height: 200 }) // ... canvas绘制逻辑第二步:配置扫码跳转规则
在微信公众平台 → 小程序管理 → 开发管理 → 扫码跳转配置中,添加规则:
- 规则类型:
URL Link - URL Link:
https://wxaurl.cn/xxxxxx(需先在云开发控制台生成短链) - 启动页面:
/pages/verify/scan?code=+{code} - 备注:
餐饮核销专用
关键技巧:
{code}是微信扫码时自动注入的占位符,实际扫码后,小程序会启动/pages/verify/scan.js并在options.code中拿到真实核销码。scan.js页面立即调用verifyCodeVerify云函数校验,通过则更新订单状态并弹窗“核销成功”。
5.3 核销失败的五种状态码:比“核销失败”四个字更有用的调试依据
verifyCodeVerify云函数返回的不是布尔值,而是带语义的状态码,直接映射到前端Toast文案:
| 状态码 | 含义 | 常见场景 | 前端处理 |
|---|---|---|---|
200 | 核销成功 | 订单存在、未核销、时间有效 | 显示绿色Toast,跳转订单详情 |
404 | 订单不存在 | orderId解析错误或数据库无此记录 | 显示“订单不存在”,引导联系客服 |
409 | 已核销 | status === 'verified' | 显示“该订单已核销”,附上次核销时间 |
410 | 已过期 | timestamp与当前时间差 > 10分钟 | 显示“核销码已过期,请刷新重试” |
403 | 校验失败 | HMAC签名不匹配(可能被篡改) | 显示“非法核销码”,记录日志并告警 |
在pages/verify/scan.js中,wx.cloud.callFunction的fail回调会捕获这些状态码:
wx.cloud.callFunction({ name: 'verifyCodeVerify', data: { code } }) .then(res => { switch(res.result.code) { case 200: this.showSuccess(); break; case 409: this.showToast('已核销'); break; case 410: this.refreshQrCode(); break; // 自动刷新二维码 default: this.showToast('核销失败'); } })从那以后我每次部署新环境,都强制走一遍“商户入驻→用户下单→真机扫码核销”全流程,哪怕只是用测试数据。因为核销是整个业务闭环的最后一环,也是用户感知最直接的一环——它不响,整个系统就等于没跑通。希望帮到你。
本文还有配套的精品资源,点击获取