资讯动态

扫码点餐小程序多门店源码跑通指南

发布时间:2026/10/18 7:16:49 来源:尧图企业网站定制
简介这是一套基于Java后端与uniappVue3前端实现的在线点餐小程序完整源码面向计算机、电子信息等专业学生及初级全栈开发者适用于课程设计、期末大作业或毕业设计参考。系统支持扫码点餐、外卖配送与到店自取双模式并具备多门店统一管理能力涵盖用户端、商家端、后台管理端三大模块体现典型前后端分离架构实践。压缩包共2005个文件含1323个Java后端业务与接口代码、257个Vue3组件与页面逻辑、161个JS工具与交互脚本、84份Markdown项目说明与接口文档整体18.17MB结构清晰、注释较全便于分层学习与功能拓展。目前已有98人学习下载读者可直接运行调试快速掌握Spring BootMyBatis后端开发、uniapp跨端构建、多门店数据隔离设计及扫码支付集成等核心技能。1. 为什么一个「扫码点餐小程序」源码包比你想象中更难跑通这不是一个拿来就能上线的“开箱即用”模板——它是一套真实业务场景倒逼出来的工程切片顾客扫桌牌二维码进小程序选餐、选自取/外卖、选门店比如A店堂食、B店外送下单后订单实时推送到对应门店的后厨屏管理员在后台能按门店维度看销量、改菜单、调库存。整个链路横跨微信小程序前端、Spring Boot 后端、MySQL Redis 数据库、多租户式门店隔离逻辑还隐含了微信支付回调验签、订单状态机流转、并发下单锁库存等硬核细节。很多开发者解压 ZIP 后第一反应是“怎么连登录都进不去”第二反应是“为什么切换门店后菜单还是旧的”第三反应是“支付成功但订单没变状态”。问题不在代码写得烂而在于这套源码默认假设你已具备① 微信开放平台基础配置能力不是只懂wx.login()② Spring Boot 多数据源或逻辑分库分表意识③ 小程序wx.request与后端CrossOrigin/ JWT 鉴权的协同调试经验。它适合两类人一是正要启动本地餐饮SaaS项目的Java后端工程师想拿它当最小可行架构参考二是带毕设任务的学生需在2周内交付可演示的“多门店扫码点餐”系统——但必须亲手填平那几个关键断层否则永远卡在“页面白屏”或“401 Unauthorized”。2. 搭建环境从解压 ZIP 到首页渲染成功的最小闭环2.1 解压后目录结构识别与关键文件定位先别急着mvn clean install。打开 ZIP你会看到典型前后端分离结构├── backend/ ← Spring Boot 项目Maven │ ├── pom.xml ← 注意 JDK 版本常见为 1.8 或 11、Spring Boot 版本2.7.x 或 3.0 │ └── src/main/ │ └── resources/ │ ├── application.yml ← 核心配置入口数据库、微信参数、多门店开关 │ └── application-prod.yml ← 生产环境配置通常被忽略但多门店逻辑常在此激活 ├── frontend/ ← 微信小程序源码非 H5是 .wxml/.wxss/.js 结构 │ ├── project.config.json ← 小程序 AppID 配置位置必须改 │ ├── app.js ← 全局逻辑含门店切换拦截器 │ └── pages/index/index.js ← 首页数据拉取逻辑重点看 wx.request 的 url 域名 └── docs/ ← 项目说明文档PDF 或 Markdown含数据库 ER 图、接口列表提示project.config.json中的appid字段必须替换成你自己的小程序 AppID否则真机扫码必报request:fail url not in domain list。这是新手翻车率最高的第一步。2.2 后端服务启动绕过微信认证的本地调试法源码默认依赖微信开放平台鉴权如wx.getUserProfile获取用户信息但本地开发时无法触发微信授权弹窗。正确做法是临时注释鉴权逻辑直连模拟数据// backend/src/main/java/com/example/controller/OrderController.java PostMapping(/order/create) // PreAuthorize(hasRole(USER)) ← 注释掉这行Spring Security 配置 public Result createOrder(RequestBody OrderDTO dto) { // 临时跳过微信用户校验 Long userId 1001L; // 强制设为测试用户ID return orderService.createOrder(userId, dto); }同时在application.yml中关闭微信敏感操作wechat: enable: false # 关闭微信登录、支付回调等模块避免启动报错 appid: your_appid_here secret: your_secret_here启动命令cd backend mvn spring-boot:run -Dspring.profiles.activedev验证访问http://localhost:8080/actuator/health返回{status:UP}即后端就绪。2.3 小程序前端配置与域名白名单设置微信要求所有wx.request请求域名必须在「小程序后台 → 开发管理 → 开发者工具 → 服务器域名」中备案。但本地调试时你无法将localhost:8080加入白名单。解决方案是使用微信开发者工具的「不校验合法域名」开关仅限开发版打开微信开发者工具 → 右上角「详情」→「本地设置」→ 勾选「不校验合法域名、https 以及 web-view 业务域名」在frontend/app.js中修改请求 baseURL// 替换原 config.js 中的 apiHost const API_HOST http://127.0.0.1:8080; // 不要用 localhostiOS 真机调试会失败点击开发者工具「编译」若控制台无net::ERR_CONNECTION_REFUSED报错且 Network 面板能看到/menu/list返回 JSON则前端与后端通路打通。注意127.0.0.1是关键。localhost在 iOS 真机调试时会被解析失败必须用 IP 地址。3. 多门店模式落地从数据库设计到前端门店切换逻辑3.1 数据库表结构中的「门店租户字段」识别多门店不是靠前端下拉框切换实现的而是后端在每张核心表menu,order,store_stock中强制添加store_id字段并在 SQL 查询时自动注入WHERE store_id ?条件。查看backend/src/main/resources/mapper/MenuMapper.xmlselect idlistByStore resultTypeMenuVO SELECT * FROM menu WHERE store_id #{storeId} AND status 1 !-- 这里隐含了「同一菜品在不同门店可独立上下架」 -- /select关键点store_id是外键关联store表含门店名称、地址、营业状态所有增删改查接口的 DTO 中storeId必须作为必传参数如MenuDTO.setStoreId(1L)若application.yml中multi-store.enabledtrue则 MyBatis Plus 的MetaObjectHandler会自动填充store_id字段防止漏写。3.2 前端门店选择器与全局状态同步小程序首页顶部有「切换门店」按钮点击后需① 更新本地缓存wx.setStorageSync(currentStore, store)② 重新拉取该门店菜单③ 同步购物车中商品所属门店避免跨店下单。核心逻辑在pages/index/index.js// 切换门店后触发 switchStore(e) { const storeId e.detail.value; wx.setStorageSync(currentStore, { id: storeId, name: e.detail.label }); // 重点清空当前购物车因不同门店菜单ID体系独立 wx.removeStorageSync(cartItems); // 重新拉取菜单 this.loadMenuList(storeId); }, loadMenuList(storeId) { wx.request({ url: ${API_HOST}/menu/list?storeId${storeId}, // URL 中显式传参 success: (res) { this.setData({ menuList: res.data }); } }); }血泪经验若忘记清空购物车用户在A店加购后切到B店仍显示A店商品提交时后端因store_id不匹配直接拒绝——错误日志里只会打Invalid store_id in cart items很难定位。3.3 后端门店上下文传递ThreadLocal 拦截器方案为避免每个 Controller 方法都手动接收storeId参数源码采用StoreContext工具类 HandlerInterceptor自动提取// StoreContext.java public class StoreContext { private static final ThreadLocalLong STORE_ID_HOLDER new ThreadLocal(); public static void setStoreId(Long storeId) { STORE_ID_HOLDER.set(storeId); } public static Long getStoreId() { return STORE_ID_HOLDER.get(); } } // StoreInterceptor.java注册在 WebMvcConfigurer 中 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String storeId request.getHeader(X-Store-ID); // 前端请求头携带 if (storeId ! null !storeId.isEmpty()) { StoreContext.setStoreId(Long.valueOf(storeId)); } return true; }前端请求时必须加 Headerwx.request({ header: { X-Store-ID: wx.getStorageSync(currentStore).id } });这样后续 Service 层可直接调用StoreContext.getStoreId()获取当前门店无需层层透传。4. 外卖与自取双模式状态机设计与库存扣减差异4.1 订单类型字段定义与业务分流在order表中关键字段是order_typeTINYINT值含义库存扣减时机配送逻辑1自取下单成功即扣减无需配送员仅通知门店2外卖支付成功后扣减调用第三方配送 API后端创建订单时根据前端传参决定分支// OrderService.java public Result createOrder(Long userId, OrderDTO dto) { if (dto.getOrderType() 1) { // 自取 stockService.deductStock(dto.getItems()); // 立即扣减 } Order order buildOrder(userId, dto); orderMapper.insert(order); if (dto.getOrderType() 2) { // 外卖 // 支付成功回调中再扣库存见 4.3 notifyKitchens(order.getId(), OUTSIDE_DELIVERY); } return Result.success(order.getId()); }4.2 自取模式下的「到店核销」流程实现自取订单生成后顾客会收到取餐码6位数字门店员工在后台输入该码完成核销。核销表order_verification结构CREATE TABLE order_verification ( id BIGINT PRIMARY KEY, order_id BIGINT NOT NULL, verify_code CHAR(6) NOT NULL, -- 索引字段加速查询 verified_at DATETIME, verified_by VARCHAR(32) );核销接口关键逻辑Transactional public Result verifyOrder(Long orderId, String code) { // 1. 校验取餐码是否匹配且未核销 OrderVerification v verificationMapper.selectOne( new QueryWrapperOrderVerification() .eq(order_id, orderId) .eq(verify_code, code) .isNull(verified_at) ); if (v null) return Result.fail(取餐码错误或已核销); // 2. 更新核销时间 订单状态 v.setVerifiedAt(LocalDateTime.now()); verificationMapper.updateById(v); orderMapper.update(null, new UpdateWrapperOrder() .eq(id, orderId) .set(status, 5) // 5已取餐 ); return Result.success(); }玄学坑若verify_code字段未建索引高并发核销时会出现「超时等待锁」导致核销失败。务必执行ALTER TABLE order_verification ADD INDEX idx_code (verify_code);4.3 外卖模式的支付回调与库存补偿机制外卖订单的库存扣减不能放在创建时防恶意下单占库存必须等微信支付成功回调后执行。但回调可能丢失或延迟因此需「最终一致性」设计支付回调接口/pay/callback接收微信通知验签通过后更新订单状态为「已支付」并发送 MQ 消息触发扣库存若 MQ 消费失败启用定时任务扫描status2待支付且pay_time NOW()-5min的订单调用stockService.compensateDeduct(orderId)补偿扣减。补偿方法核心public void compensateDeduct(Long orderId) { Order order orderMapper.selectById(orderId); if (order.getStatus() 2) { // 仍是待支付状态说明回调丢失 stockService.deductStock(order.getItems()); order.setStatus(3); // 已支付 order.setPayTime(LocalDateTime.now()); orderMapper.updateById(order); } }5. 避坑指南那些让开发者凌晨三点还在抓头发的典型问题5.1 现象小程序首页空白Network 显示401 Unauthorized原因后端 JWT 鉴权拦截器未放行/menu/list等免登录接口或前端未在请求头携带Authorization: Bearer xxx。解决检查SecurityConfig.java中antMatchers(/menu/**).permitAll()是否配置若需登录态确认app.js中wx.getStorageSync(token)是否存在且wx.request的header正确拼接。5.2 现象切换门店后菜单不变Network 显示请求 URL 中storeId仍为旧值原因前端未正确读取wx.getStorageSync(currentStore)或loadMenuList()方法未传参storeId。解决在index.js的onLoad生命周期中打印console.log(wx.getStorageSync(currentStore))检查loadMenuList调用处是否遗漏参数this.loadMenuList(this.data.currentStore.id)。5.3 现象支付成功后订单状态卡在「待支付」后台无任何日志原因微信支付回调地址未在公众号/小程序后台配置或application.yml中wechat.notify-url域名未备案必须是 HTTPS 且在白名单。解决登录微信商户平台 → 「产品中心」→ 「开发配置」→ 「支付回调URL」填写https://yourdomain.com/pay/callback本地调试时用ngrok或localtunnel将localhost:8080映射为 HTTPS 域名并填入回调地址。5.4 现象多门店下A店修改菜品价格B店菜单同步变更原因menu表设计错误缺少store_id字段导致所有门店共用同一份菜品数据。解决执行 SQL 添加字段ALTER TABLE menu ADD COLUMN store_id BIGINT NOT NULL DEFAULT 0 AFTER id;修改所有menu相关查询 XMLWHERE 条件增加AND store_id #{storeId}初始化数据时为每条菜品记录插入对应store_id。5.5 现象并发下单时出现超卖库存扣减为负数原因stockService.deductStock()使用普通 SQLUPDATE stock SET quantity quantity - ? WHERE id ? AND quantity ?但未加SELECT ... FOR UPDATE锁表。解决改用悲观锁在事务中先SELECT quantity FROM stock WHERE id ? FOR UPDATE或升级为 Redis 原子操作DECRBY stock:1001 2菜品ID为1001扣2份再判断返回值是否 ≥0。6. 真实可用的验证技巧三步确认你的多门店点餐系统已就绪6.1 门店维度数据隔离验证表用以下 SQL 快速验证多门店数据是否真正隔离以菜单为例验证目标SQL 示例期望结果A店有菜品「红烧肉」SELECT COUNT(*) FROM menu WHERE store_id 1 AND name 红烧肉; 0B店无该菜品SELECT COUNT(*) FROM menu WHERE store_id 2 AND name 红烧肉; 0同一菜品不同价格SELECT store_id, price FROM menu WHERE name 可乐 AND status 1;返回多行price 值各不相同提示若第一条 SQL 返回 0说明初始化脚本未按门店导入数据需检查data.sql中INSERT INTO menu语句是否漏写store_id值。6.2 外卖/自取混合下单压力测试脚本用 JMeter 模拟 50 并发用户分别执行两种下单流程线程组1自取HTTP 请求/order/createBody 中orderType:1storeId:1线程组2外卖同上但orderType:2监听器聚合报告中关注「90% Line」响应时间应 800ms、「Errors」率应为 0数据库验证压测后执行SELECT COUNT(*), order_type FROM order GROUP BY order_type;确认两类订单数量与线程组比例一致。6.3 微信支付沙箱环境联调 checklist生产环境前必须用微信支付沙箱验证资金流商户平台开启沙箱环境获取沙箱mch_id和keyapplication.yml中替换wechat: sandbox: true mch-id: your_sandbox_mch_id key: your_sandbox_key调用/pay/unifiedorder接口返回return_codeSUCCESS且result_codeSUCCESS用沙箱notify_url接收模拟回调微信提供沙箱回调触发工具检查订单状态是否由2待支付变为3已支付且库存准确扣减。我带过的几个模拟项目X都是卡在沙箱回调验签失败——根源是沙箱key末尾多了空格复制时没注意。现在我的习惯是粘贴完立刻echo xxx | md5sum对比官方文档给出的 MD5 值。这种细节不写进文档但能帮你省下 6 小时排查时间。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑