资讯详情

资讯详情

微信点餐系统毕设源码:从架构到订单事务的完整闭环

简介面向计算机、电子信息工程及数学类专业学生这份微信点餐系统小程序源码是经导师指导并通过答辩的98分毕业设计项目适合用于毕业设计、课程设计或期末大作业的参考与二次开发。代码基于Java技术栈配合小程序前端Vue页面与WXML/WXSS样式覆盖点餐、订单、用户管理等核心功能项目内附数据库SQL脚本与启动脚本便于搭建运行。资源包共1305个文件压缩后大小19.17MB主要文件类型包括Java源码、Vue组件、微信小程序相关文件js/wxml/wxss/json以及PNG图片素材另含配置文件与说明文档目录层次清晰。代码经严格调试无已知Bug可直接在开发环境中导入使用。该资源已吸引1851人学习下载。通过完整源码与脚本学习者既能理解前后端交互思路也能快速部署体验还可在此基础上扩展模块或改造界面对毕业设计答辩准备或项目实战训练均有实际帮助。1. 微信点餐系统小程序源码一份毕设工程背后的完整闭环很多同学拿到一份“微信点餐系统小程序源码”时第一反应是导入微信开发者工具然后被几十个报错迎面砸懵。其实这个标题背后不是单页 demo而是一套缩水版的点餐闭环小程序端负责菜品浏览、购物车、下单后端提供登录、菜品、订单、支付状态接口数据库存用户和交易记录。跑通它等于把移动端、服务端、数据库三层都摸了一遍。这篇文章不讲“谁的源码更好”只讲一件事给你一份这样的毕业设计源码你怎么在最短时间内让它跑起来、看懂它、改造成自己能答辩的工程。适合正在做小程序类毕设的学生也适合刚入行想了解前后端配合的初级开发者。我会按常见做法和踩坑记录来讲代码可以直接抄参数也按能跑通的标准给。2. 先别急着导代码架构选型与四张核心表的设计2.1 为什么我建议用“原生小程序 Spring Boot MySQL”而不是云开发拿到源码第一步先看技术栈。市面上的微信点餐系统毕设源码主流有两种一种是原生小程序加自建后端Java、Python、PHP 都有另一种是云开发把数据操作写在云函数里。如果你的目的是答辩我强烈建议选前者。云开发虽然省去服务器和域名但业务逻辑全在黑匣子里老师一问你“下单事务怎么保证”你就只能答“云函数自动处理”分数很难拉开差距。原生小程序加自建后端暴露出来的是真实的 HTTP 接口、JSON 数据、数据库事务。比如用 Spring Boot 写订单接口用 MySQL 存数据你可以讲清楚请求怎么进来、参数怎么校验、库存怎么扣减。这份源码如果是 uni-app 工程也没关系因为最终都要在微信开发者工具里打包预览调试方式和原生小程序是一样的。很多人被“uniapp 微信小程序打包”这个词吓住实际只是多一层编译页面生命周期和 wx.request 的调用方式不变。2.2 数据库脚本从 user 到 order_item字段怎么定才不过时看源码先看数据库脚本。一份合格的点餐系统至少要有用户表、菜品表、订单表、订单明细表另外还应该有一张菜品分类表。下面这份建表 SQL 是常见做法字段命名和注释可以直接用在你的设计文档里-- 用户表 CREATE TABLE user ( id int unsigned NOT NULL AUTO_INCREMENT, openid varchar(64) NOT NULL COMMENT 微信登录唯一标识, nickname varchar(64) DEFAULT COMMENT 用户昵称, avatar_url varchar(500) DEFAULT COMMENT 微信头像地址, phone varchar(20) DEFAULT COMMENT 手机号下单时可填可不填, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 菜品分类表 CREATE TABLE category ( id int unsigned NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 分类名如热菜、凉菜, sort int NOT NULL DEFAULT 0 COMMENT 排序权重越小越靠前, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT菜品分类表; -- 菜品表 CREATE TABLE dish ( id int unsigned NOT NULL AUTO_INCREMENT, category_id int unsigned NOT NULL COMMENT 归属分类id, name varchar(100) NOT NULL, price bigint NOT NULL COMMENT 单价单位分, image varchar(500) DEFAULT COMMENT 图片 URL, description varchar(255) DEFAULT COMMENT 菜品描述, stock int NOT NULL DEFAULT 999 COMMENT 当日库存, status tinyint NOT NULL DEFAULT 1 COMMENT 1上架 0下架, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT菜品表; -- 订单表 CREATE TABLE orders ( id int unsigned NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 业务订单号, user_id int unsigned NOT NULL, total_amount bigint NOT NULL COMMENT 订单总价单位分, status tinyint NOT NULL DEFAULT 0 COMMENT 0待支付 1已支付 2制作中 3已取餐 4已取消, pay_type tinyint NOT NULL DEFAULT 0 COMMENT 0模拟支付 1微信支付, pay_time datetime DEFAULT NULL, remark varchar(200) DEFAULT COMMENT 备注, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表; -- 订单明细表 CREATE TABLE order_item ( id int unsigned NOT NULL AUTO_INCREMENT, order_id int unsigned NOT NULL COMMENT 订单表主键, dish_id int unsigned NOT NULL, dish_name varchar(100) NOT NULL COMMENT 菜品名称快照, dish_image varchar(500) DEFAULT COMMENT 菜品图片快照, price bigint NOT NULL COMMENT 下单时单价分, count int NOT NULL COMMENT 购买数量, PRIMARY KEY (id), KEY idx_order_id (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单明细表;这段 SQL 里有几个关键选择。金额不用 decimal 而用 bigint 存“分”是因为浮点计算在多个菜品聚合时容易出精度问题后期对账也方便这是从业者的普遍习惯。订单明细表必须存菜品名称和图片快照因为菜品改价或下架后历史订单仍然需要保留当时的购买信息。外键我没建因为毕设里用逻辑外键更灵活测试数据灌入时不用按主外键顺序折腾查询时通过索引关联即可。你拿到一份源码先看它的 user 表有没有 openid 字段的唯一索引。如果没有登录逻辑大概率是假的。再看 orders 表有没有 order_no 唯一索引没有的话下单接口容易重复。这两点能快速判断这份源码的完成度。3. 小程序端核心功能登录、列表、购物车到底怎么落地3.1 wx.login 换取 openid后端 token 鉴权的标准姿势小程序端第一个要搞懂的是登录。不能直接让前端拿微信 code 去换 openid因为请求需要在后端发起否则会泄露小程序 secret。常见做法是前端把 code 传给后端后端调用微信的 code2session 接口然后签发一个自己的 token 给小程序。之后所有接口都用 token 识别用户。// 小程序端请求封装统一携带 token function request(options) { return new Promise((resolve, reject) { wx.request({ ...options, header: { Content-Type: application/json, Authorization: Bearer wx.getStorageSync(token) }, success: (res) { if (res.data.code 401) { // token 失效重新登录后重放请求 wx.removeStorageSync(token); login().then(() resolve(request(options))); } else { resolve(res.data); } }, fail: reject }); }); } function login() { return new Promise((resolve, reject) { wx.login({ success: async (res) { try { const data await request({ url: http://localhost:8080/api/auth/login, method: POST, data: { code: res.code } }); wx.setStorageSync(token, data.data.token); wx.setStorageSync(userInfo, data.data.user); resolve(data.data); } catch (e) { reject(e); } }, fail: reject }); }); }这段代码里最关键的是 401 后重新调用 login并把原请求重放一次。因为 wx.login 生成的 code 是一次性的整个登录链路必须串起来。注意后端接口地址不要写死在每个页面我一般会单独建一个 config.js开发环境用局域网 IP上线再替换正式域名。很多毕设翻车就翻在 WordPress 的页面调了一堆后端地址改一处漏一处。3.2 菜品列表“加载更多”与顶部导航栏高度适配点餐首页基本就是菜品列表最基础的功能是分页。微信小程序里触底加载用 onReachBottom 生命周期但不能无脑加载。要判断当前已加载数量是否小于总数还要加防重标志否则手指快速上下滑动时onReachBottom 会被连续触发好几次同一个 page 参数请求发出去多遍列表会出现重复数据。Page({ data: { dishes: [], page: 1, pageSize: 10, total: 0, loadingMore: false, categoryId: null }, onLoad(options) { this.setData({ categoryId: options.categoryId || null }); this.loadDishes(true); }, onReachBottom() { if (this.data.dishes.length this.data.total) return; this.loadDishes(false); }, async loadDishes(reset) { if (this.data.loadingMore) return; this.setData({ loadingMore: true }); const page reset ? 1 : this.data.page 1; const { list, total } await getDishes({ page, pageSize: this.data.pageSize, categoryId: this.data.categoryId }); this.setData({ dishes: reset ? list : this.data.dishes.concat(list), total, page, loadingMore: false }); // 切换分类时用当前分类名设置导航栏标题 wx.setNavigationBarTitle({ title: this.data.categoryName || 点餐 }); } });代码里 res.data 的返回结构要和后端对齐我这里假设统一返回 { code, msg, data }data 里是分页对象 { list, total }。分页参数 page 从 1 开始pageSize 一般设 10 或 15图片多的菜品控制在 10 以内比较流畅。顶部导航栏高度是另一个高频坑。微信小程序自定义导航栏时不能写死 44px因为不同机型的胶囊位置不一样。正确做法是取菜单按钮的位置算出导航栏高度// app.js onLaunch 里计算挂到 globalData 上 const menuRect wx.getMenuButtonBoundingClientRect(); const statusBarHeight wx.getSystemInfoSync().statusBarHeight; const navBarHeight (menuRect.top - statusBarHeight) * 2 menuRect.height; wx.setStorageSync(navBarHeight, navBarHeight);这段公式的理解是导航栏高度 胶囊按钮顶部到状态栏底部的距离 × 2 胶囊高度。算出来后在页面的 wxss 里给占位 view 设置 height用内联样式读取 storage 里的值即可。不做这一步在 iPhone X 及之后的全面屏机型上标题文字会撞进状态栏。3.3 购物车数据结构和规格选择单选、数量和缓存怎么设计购物车是点餐系统的核心交互。常见错误是把它设计成数组每次增删都遍历不仅代码难读setData 传整个数组还会导致页面卡顿。更好的做法是用菜品 id 作为 key 的对象每个 value 是 { count, sku, price }。小程序端购物车数据结构可以这样设计// 购物车示例key 是菜品 idvalue 是购买信息 const cart { 12: { count: 2, sku: 大份, price: 1500 }, 15: { count: 1, sku: 标准, price: 1200 } }; function addToCart(dish) { const key String(dish.id); const cart wx.getStorageSync(cart) || {}; if (cart[key]) { cart[key].count 1; } else { // selectedSku 来自规格选择组件 cart[key] { count: 1, sku: dish.selectedSku || 标准, price: dish.price }; } wx.setStorageSync(cart, cart); // 用路径更新避免 setData 整个购物车对象 this.setData({ cartPanel.totalCount: calcTotalCount(cart) }); }规格选择通常用 radio-group 实现单选radio 的 value 要保证唯一比如“大份/中份/小份”。选中结果存在临时变量里等点击“加入购物车”再写入购物车。注意购物车写入 storage 的时机是每次变化都同步不要只在页面卸载时写否则用户中途杀掉小程序会丢单。这里有个重要提示不要直接在 this.data.cart 上做自增小程序 setData 的赋值不是同步生效的连续多次操作 this.data.cart.count 1可能拿到的还是旧值。正确做法是先从 storage 取出整个购物车对象在局部变量里改完再一次性 setStorageSync 并 setData 汇总数字。4. 后端接口设计与订单流程把点餐闭环串成事务4.1 统一返回结构和接口清单前后端联调不打架小程序端和后端联调最怕接口返回格式不统一。有的接口返回 { status: 1 }有的返回 { code: 200 }前端每个页面都写一遍判断后期加功能就是灾难。我会在后端定义一个统一返回体所有接口都返回 { code, msg, data }code 为 0 表示成功非 0 表示业务错误401 单独表示未登录。前端 request 封装里统一拦截 401页面只关心 data。点餐系统常见的后端接口大概这么几张方法路径功能核心参数POST/api/auth/login登录换 tokencodeGET/api/category/list获取分类列表无GET/api/dish/list分页获取菜品page, pageSize, categoryIdPOST/api/order/create创建订单cartItems, remark, powerTokenPOST/api/order/pay模拟支付orderNoGET/api/order/list获取我的订单page, pageSize, statusGET/api/order/detail订单详情orderNo其中 cartItems 是购物车对象后端收到后要把它转换成订单明细。前端传的价格不要信任后端必须重新查数据库里的菜品价格计算总价否则学生改接口价格就能 0 元下单。这个点老师很喜欢在答辩时问你就回答“下单金额以后端服务端计算为准前端传价只做展示”。4.2 下单接口事务、库存扣减和幂等处理下单是整个系统最核心、最容易出错的接口。我见过很多源码里下单就一个 insert 订单表明细表都不写库存也不扣这种只能算演示 demo。一个合格的下单接口至少要包含创建订单头、创建订单明细、扣减库存三步并且三步要放在同一个事务里。常见做法是用 Spring Boot 的 Transactional 标注服务方法数据库层面靠事务保证一致性。同时用 select … for update 对菜品行加锁防止并发下同一道菜超卖。下面是一段核心代码可以直接照着改Transactional(rollbackFor Exception.class) public OrderVO createOrder(OrderCreateReq req) { // 1. 幂等判断powerToken 相同直接返回旧订单 Order existing orderMapper.selectByPowerToken(req.getPowerToken()); if (existing ! null) { return OrderVO.from(existing); } // 2. 查菜品并加行级锁锁定到具体菜品行 ListDish dishes dishMapper.selectByIdsForUpdate(req.getDishIds()); // 3. 校验库存并计算总价 long totalAmount 0; for (Dish dish : dishes) { Integer count req.getItems().get(dish.getId()); if (dish.getStock() count) { throw new BizException(菜品 dish.getName() 库存不足); } totalAmount dish.getPrice() * count; } // 4. 创建订单头和订单明细快照 Order order new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(req.getUserId()); order.setTotalAmount(totalAmount); order.setStatus(0); order.setPowerToken(req.getPowerToken()); orderMapper.insert(order); // 5. 扣减库存 for (Dish dish : dishes) { dishMapper.decreaseStock(dish.getId(), req.getItems().get(dish.getId())); } return OrderVO.from(order); }这段代码的注释要仔细看。第 1 步的幂等判断很关键前端每次进入结算页生成一个随机的 powerToken下单点击时带上。后端收到重复请求时直接返回已创建的订单不会生成第二个订单。第 2 步用 for update 锁菜品行并发场景下要求同一个菜品只能串行扣库存这点能体现你对并发的理解。第 4 步里的订单明细表插入代码我简化了实际还需要在循环里把 dishName、price 快照拷进去。模拟支付接口也不要直接改成“已支付”。我建议后端生成一个支付凭证 payToken前端在支付页面点击“确认支付”时调用 /api/order/pay 并带上 orderNo 和 payToken后端校验订单状态是“待支付”才能改成“已支付”。这样至少能挡住“直接调改状态接口”这种低级问题。4.3 小程序抓包调试用 Charles 定位接口问题做小程序开发总会遇到真机上请求失败、后端却看不出日志的情况。微信开发者工具的 Network 面板能看开发环境的请求但真机预览时的请求看不到。这时候需要一个抓包工具把手机上的请求转发到电脑上查看常见做法是用 Charles 开启 SSL 解密然后让手机 Wi-Fi 设置里填上电脑的局域网地址和端口。具体步骤是电脑和手机连同一个局域网在 Charles 菜单里开启 SSL 解密安装证书到手机并信任证书然后在手机系统 Wi-Fi 设置里把网络转发指向电脑和端口Charles 就能看到小程序发出的全部 HTTPS 请求。需要注意微信小程序用的是系统网络栈证书必须在系统级信任而不是浏览器级。如果手机上提示证书无效检查手机有没有设置锁屏密码部分安卓机不设密码不允许安装证书。抓包最大的价值是看清请求参数和响应原文。比如后端返回 { code: 500 }前端只提示“网络异常”你不知道是参数没传还是后端空指针。抓到包后可以直接把请求复制出来用 Postman 重放逐步排查是前端问题还是后端问题。这一点在答辩前调试时很救急。5. 微信点餐系统毕设最常见的 5 个坑现象、原因、解决5.1 图片一直转圈不显示域名没进白名单演示时最容易翻车的不是代码逻辑而是图片加载不出来。现象是微信开发者工具里图片正常拿手机预览时图片全部转圈。原因是小程序后台没有把图片域名加入 downloadFile 合法域名真机上 wx.request 和图片下载都会校验域名白名单开发工具里默认勾选了“不校验合法域名”所以正常。解决方法是登录小程序管理后台在“开发管理-服务器域名”里把图片所在的域名加到 downloadFile 合法域名并且把校验文件放到该域名的根目录。如果图片是从本地读取的也要改成上传到 OSS 或云存储不能依赖本地临时文件。演示前一定要用真机验证一次不要只看开发者工具。5.2 真机上 wx.request 全部失败HTTPS 证书与调试开关另一种突发是开发者工具里接口全通手机一跑全部请求 fail。原因通常是后端地址是 http://或者 https 证书不是有效证书。微信小程序正式环境要求所有 request 合法域名必须是 https且证书有效不能是自签名证书。解决分两层如果只是本地调试可以在开发者工具里勾选“不校验合法域名”真机预览时需要在预览界面打开“调试”开关但这种只对开发版有效。如果演示用真机且不想被拦就把后端部署到一台有公网域名和 https 证书的服务器上证书用免费的就行。毕设答辩前两周搞定这个不然现场网络一变就直接翻车。5.3 购物车数量越改越乱setData 丢更新很多同学写购物车加号直接在 this.data.cart 里 count 1然后 setData。现象是快速点击时页面数字有时候加了两下有时候只加一下偶尔还会数值跳变。原因很简单this.data 在 setData 执行前不是最新值连续点击的多个事件回调读取的是同一个旧值后一次计算会覆盖前一次。解决方式我在 3.3 里已经给了代码核心是先读 storage在局部变量里改再整体写回。在页面渲染上不要 setData 整个 cart只更新一个汇总数字。这个坑几乎每个做小程序的人都会踩一次答辩时可以主动说出来反而显得你懂性能。5.4 下单按钮点两次生成两单缺幂等处理现象很直观用户手速快下单按钮点了两下订单列表出现两条一模一样的订单。原因是前端没有禁用按钮后端也没有做幂等校验。解决要两头堵前端点击后立刻把按钮设为 disabled 并显示倒计时后端在 orders 表加 power_token 字段的唯一索引插入时重复就报错然后查询已有订单返回。这个坑老师很爱问因为它涉及前端交互和后端数据一致性。你能说出“前端防抖 后端幂等键”两层方案比只列一个 Transactional 要加分。如果你用的是模拟支付还要注意支付按钮同样要做防重否则用户连续点两次支付订单状态被更新两次没问题但如果有积分就会发两次。5.5 切后台再回来订单状态不刷新onShow 和 onHide 没配合好现象是用户下单后切到微信聊天再回到小程序订单状态还是“待支付”其实后台已经标记成“支付超时取消”了。原因是你只在 onLoad 里拉取了一次订单状态后续小程序从后台恢复时不会重新执行 onLoad只会触发 onShow代码没在 onShow 里刷新数据。解决办法把拉取订单状态的方法抽到一个 loadOrder() 函数里在 onShow 中调用。同时用 wx.onHide 监听用户切后台在回调里记录一个“需要刷新”标志回到 onShow 时再决定是否重新加载。微信小程序的页面生命周期里onShow 每次都会触发这是刷新列表和数据的最佳位置。很多学长代码里只写了 onLoad这个问题你在答辩前一定要自查。6. 答辩演示前的验证技巧让小程序在评委面前不翻车6.1 演示前的四类检查性能、兼容性、数据、恢复现场答辩演示时间只有几分钟功能不用多但每一步都不能卡顿。我一般会按下面四类检查过一遍你可以直接当作测试清单。第一接口性能。在开发者工具的 Network 面板里把每个接口的耗时和返回大小过一遍。图片走 CDN接口响应超过 300ms 的都要解释清楚原因比如首页加载时同时请求了分类和菜品可以改成并行请求而不是串行。第二兼容性。用开发者工具的机型模拟切到 iPhone X、iPhone 12、小米 8 各看一次重点看自定义导航栏高度和底部 tabBar 遮挡。你也在模拟器里看不出问题但有耐心就对着真机调一下。第三数据完整性。演示前清掉历史脏数据把菜品库存调充足。如果演示到一半弹出“库存不足”评委不会觉得是真实业务只会觉得你的系统有问题。准备一个“演示账号”购物车、地址、订单状态都是初始化的。第四恢复现场。如果演示现场 WiFi 慢图片加载不出来你至少要准备一个本地兜底方案比如把菜品图改成 base64 嵌入或者在 config.js 里做一个开关紧急情况下切换到本地图片地址。我当年就是没做这套检查答辩时用手机热点连后端图片加载超时页面白了一片。从那以后我习惯把所有请求地址集中到一个 config.js用环境变量控制 baseURL而且正式演示前一定会关掉“不校验合法域名”这个调试开关真走一遍从登录到下单的完整流程。小程序发布需要企业主体认证个人主体很多类目不开放毕设阶段用体验版演示就足够了别花冤枉钱去认证个体户。这个方向值不值得做主要看你想学的是小程序交互还是完整的后端业务闭环。如果时间紧优先把下单事务和购物车逻辑吃透这两个点讲明白了老师的印象分不会低。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →