基于JSP+Servlet+MySQL的网上订餐系统毕业设计完整方案
简介这份资源是北方民族大学计算机科学与工程学院2014届学士学位论文《基于Web的网上订餐系统的设计与实现》面向计算机相关专业的毕业设计者与网上订餐系统开发学习者。论文完整论述了网上订餐系统的分析、设计与实现过程涵盖系统可行性分析、功能需求分析、数据库设计、详细功能实现与系统测试等环节并给出了用户注册登录、菜品浏览、购物车管理、在线下单、订单处理等模块的设计思路可作为同类毕业设计选题的参考范本。系统整体采用B/S架构围绕订单管理流程展开能帮助读者理解从用户下单到餐厅接单再到配送评价的完整闭环。资源包为单一doc文档体积10.7MB包含论文全文可直接阅读也可作为论文写作与排版参考适合借鉴其中的系统架构、数据库表设计以及论文写作结构。目前已有931人学习下载内容结构清晰尤其适合需要完成系统设计类毕业设计的本科学生参考。1. 基于Web的网上订餐系统为什么这份毕业设计方案值得从头到尾做一遍在2014年那会儿外卖平台还不是今天这个样子多数餐厅靠电话接单饭点一到前台一边接电话一边手写菜单地址听岔、菜品漏记是常有的事。网上订餐系统要解决的本质上是「订单不落地、消息不同步」的问题。所以这份毕业设计并不只是写一个菜单页加购物车那么简单它的完整拼图是用户在网页上注册登录、浏览餐品、加入购物车并在线下单餐厅管理员能接收订单、制定餐品、安排配送最后完成交易并提供服务评价。技术栈采用JSP Servlet MySQL Tomcat的组合配合JEECG快速开发平台和Comet4j消息推送框架是那几年Web毕设里相当经典的一套B/S架构方案。不管是正在为毕业设计做开题的学生还是想用低成本快速搭一套线上订餐演示系统的开发者都可以把这套方案当作底稿下文按需求、设计、实现、验收的顺序拆开讲透。2. 需求分析先行可行性判断、角色划分与用例建模2.1 可行性分析从技术、经济、操作、法律四个维度先踩坑做这个系统之前我先把可行性四个维度过了一遍这四个维度不只是写在论文里的「凑章节」它直接决定整个方案后面怎么走。技术上选型核心是Java还是PHP。电话订餐时代结束后餐厅端的并发压力集中在午晚高峰PHP上手快但线程模型在大并发下运维限制多Java是当时大型服务器端的常用语言JSP做前端餐品展示非常合适Servlet处理业务逻辑也很成熟。配合开源的MySQL和Tomcat技术链条完整且社区资料丰富遇到问题基本都能搜到答案。经济上整个开发环境几乎零成本Eclipse是开源IDEMySQL和Tomcat都是免费软件餐厅端只需要一台普通PC加网络就能跑起来用户端更不用多说一台能上网的手机就能完成下单。操作上B/S架构决定了用户不需要安装任何客户端打开浏览器就能访问这对非技术背景的餐厅营业员很友好登录后按界面提示操作即可。法律和社会因素方面系统只处理餐品展示和订单信息不涉及敏感业务只要注意用户手机号、收货地址这类隐私数据的采集与使用规范即可。这四关过了方案才敢往详细设计阶段推。2.2 角色与用例分析用户、餐厅管理员两条主线的边界这个系统里只有两类角色但它们的职责边界必须划清楚。普通用户是「点餐者」整个使用路径是注册→登录→浏览餐品→加入购物车→提交订单→查看历史订单→评价餐厅管理员则是「接单者」路径是登录后台→接收新订单→确认接单→备餐→配送→完成交易→菜品管理→销量统计。用户侧的用例包括注册、登录、注销、修改个人信息、浏览餐品、按关键词查询餐品、查看餐品详情、加入购物车、查看购物车、修改购物车中商品数量、删除商品、清空购物车、确认订单、查看历史订单。管理员侧的用例包括管理员登录、接收订单、处理订单状态、餐品上架下架、修改餐品价格库存、查看销量统计。两边用例的边界要特别注意用户只能查看和处理自己的订单管理员不能代替用户下单这是权限设计的基本底线。如果用例分析时把「用户改订单」和「管理员改订单」混在一起后面的模块实现就会乱套。2.3 活动图梳理订餐、接单、查历史订单的流程闭环用例图解决「谁能做什么」活动图解决「事情按什么顺序发生」。订餐服务的主流程是用户登录后浏览餐品列表点击餐品查看详情把选中的餐品加入购物车进入购物车确认数量和金额填写收货地址和联系电话提交订单系统生成待确认订单管理员在后台看到新订单后确认接单进入备餐和配送环节用户收到餐品后确认完成最后对服务做出评价。这个流程里每一个分支都要有出口比如购物车为空时不能提交库存不足时提示用户修改数量。历史订单查询的活动图相对简单用户登录后进入个人中心按时间倒序看到历史订单列表点击某条订单可以查看订单包含哪些餐品、单价多少、总价多少、当前状态是什么。订单处理的活动图则站在管理员角度新订单进入待处理队列管理员确认后状态改为已接单备餐完成后改为配送中用户签收后改为已完成如果用户取消或管理员拒单则进入已取消状态。三张活动图连起来看就是从「用户下单选菜」到「餐厅履约」的完整闭环这也为第三章的数据库状态字段设计提供了依据。3. 数据库设计E-R模型与六张核心业务表的落地3.1 概念结构设计从实体关系到E-R图的抽法数据库设计如果直接建表十有八九会返工。我习惯先画E-R图把实体、属性和联系定下来再转表结构。这个系统里的核心实体有六个用户、餐品、购物车、订单、订单明细、管理员。它们之间的关系是一个用户对应多条购物车记录一条购物车记录对应一个餐品一个用户可以创建多个订单一个订单包含多条订单明细一条订单明细对应一个餐品管理员独立于用户体系只负责后台操作。餐品和订单明细之间要注意历史快照问题因为餐品价格会调整而订单一旦生成明细里的价格就不能跟着餐品表变否则用户的历史订单金额会「自动变化」这是很多初学者容易忽略的点。用户和订单之间还要区分「逻辑删除」和「物理删除」订单一般不做物理删除而是通过状态字段标识取消或完成。3.2 逻辑结构设计六张核心表与字段落地实体关系定下来之后转成表结构就顺理成章了。整个系统共设计了六张表我挑核心字段列在下面。用户表 t_user 存储用户登录和配送信息字段包括用户ID、用户名、密码MD5后32位字符串、真实姓名、手机号、收货地址、注册时间用户名字段加唯一索引防止重复注册。密码绝对不存明文这是安全底线即使是毕业设计也要按生产标准要求自己。餐品表 t_dish 存储菜品信息字段包括餐品ID、餐品名称、分类ID、单价、图片路径、描述、上下架状态、库存数量。价格字段用DECIMAL(10,2)而不是FLOAT因为浮点数在金额计算上会出精度问题虽然单次看起来误差很小累计起来就麻烦了。购物车表 t_cart 的字段包括购物车记录ID、用户ID、餐品ID、数量、加入时间。这里要说明购物车在运行期主要放Session里表结构是为了支持用户下次登录时恢复购物车数据。如果只做Session不落库用户换浏览器或清理缓存后购物车就丢了体验很差。订单主表 t_order 记录一次交易的汇总信息字段包括订单ID、订单号、用户ID、总金额、状态、收货地址、联系电话、备注、创建时间、支付时间。订单号用时间戳加用户ID加随机数生成并在订单号上建唯一索引。订单明细表 t_order_item 记录订单包含的每道餐品字段包括明细ID、订单ID、餐品ID、餐品名称、单价、数量。这里冗余了餐品名称和单价目的是保留下单时刻的快照即使餐品后来改名或调价历史订单的展示也不受影响。管理员表 t_admin 结构简单字段包括管理员ID、用户名、密码、真实姓名、最后登录时间管理员不对外开放注册由系统初始化时写入数据。3.3 物理设计与存储规划引擎、字符集、索引怎么选存储引擎选择InnoDB因为订单场景需要事务支持MyISAM在表锁和高并发下会出现写入阻塞而且不支持事务回滚。字符集统一使用utf8MySQL连接串里明确加上characterEncodingutf8否则中文乱码问题会在页面层反复出现。索引设计上用户表的username加唯一索引订单表的user_id加普通索引用于查询用户的历史订单订单明细表的order_id加索引用于关联查询餐品表的category_id加索引用于按分类筛选。主键统一用INT自增不用UUID做主键因为UUID无序且占用空间大在数据量大时索引效率明显下降。外键约束在表与表之间要保留比如订单表的user_id外键指向用户表订单明细表的order_id外键指向订单表。新人在设计时总觉得外键麻烦但在这个场景里外键能保证不会出现「孤儿订单」——也就是订单明细指向一个不存在的订单。等系统稳定后也可以把外键拆到应用层控制但毕设阶段保留外键更稳妥。4. 总体架构设计B/S三层结构与五大功能模块划分4.1 三层架构表现层、业务层、数据层的边界怎么切系统采用典型B/S三层架构从上到下依次是表现层、业务逻辑层、数据访问层。表现层用JSP页面负责餐品展示和表单交互用户看到的是热卖餐品轮播、餐品列表、购物车页面业务逻辑层由Servlet和Service类组成负责处理登录校验、购物车计算、订单生成这类业务规则数据访问层用DAO封装JDBC操作向上层屏蔽SQL细节只提供findUserById、insertOrder这样语义明确的方法。请求流转路径是用户在浏览器发起请求JSP页面提交表单或AJAX请求到ServletServlet调用Service层处理业务Service层调用DAO层读写MySQL数据库处理结果返回给JSP渲染或重定向到新页面。Comet4j在这个结构里属于表现层和业务层之间的「反向通道」当订单业务层插入新订单后通过长连接把消息推送到管理员页面。三层架构的好处是职责单一餐品展示改版不影响订单逻辑换数据库只要改DAO层这也方便后面测试时逐层验证。4.2 功能模块划分餐品管理、订单管理、用户管理、网站管理、在线订餐模块名称核心功能使用者餐品管理餐品增删改查、上架下架、改价、库存调整餐厅管理员订单管理接收新订单、确认接单、状态流转、配送处理餐厅管理员用户管理注册、登录、注销、个人资料修改普通用户网站管理热卖餐品动态展示、餐品查询、在线客服、公告维护餐厅管理员在线订餐浏览餐品、加入购物车、提交订单、历史订单查询普通用户餐品管理模块要支持图片上传餐品图片路径存在数据库文件本身放到服务器指定目录页面上用img标签加载。订单管理模块的核心是状态流转从待确认到已接单、配送中、已完成、已取消每一步都要校验当前状态是否允许跳转比如已完成订单不能再改回配送中。网站管理里的在线客服其实是一个简单的站内信功能用户提交咨询管理员回复不涉及第三方即时通讯工具。五个模块之间的依赖关系要理清楚用户管理是基础餐品管理是内容在线订餐是入口订单管理是核心闭环网站管理是运营支撑。4.3 开发环境选型与JEECG、Comet4j的定位开发环境上首先要有JDK配合Eclipse作为IDEWeb服务器用Tomcat数据库用MySQL版本都不需要追求最新稳定可复现即可。这里有两个工具值得单独说因为它们是这个方案区别于「裸写JSPServlet」的关键。JEECG是一个基于J2EE的快速开发平台它的价值在于代码生成器可以直接生成单表的增删改查代码。餐品管理和用户管理这类标准CRUD功能用JEECG能省掉大量重复的DAO和页面代码。用它的正确姿势是先生成基础代码再手工改造业务逻辑而不是直接在生成代码上堆砌功能。Comet4j是一个消息推送框架原理是浏览器与服务器保持长连接服务器在数据变化时主动向客户端推送消息这与传统的轮询「每隔几秒刷新页面」不同。订单提醒功能用Comet4j管理员在后台不用刷新页面就能收到新订单通知这是答辩时很能讲的一个技术亮点。5. 关键功能实现与避坑排查从注册登录到订单推送5.1 注册登录MD5加密、Session会话与未登录拦截注册模块的逻辑是前端表单提交用户名和密码Servlet接收参数后先检查用户名是否已存在存在则返回错误提示不存在则把密码做MD5哈希后写入用户表。密码加密这一步经常被忽略但一旦做成明文数据库泄露就意味着所有用户账号直接暴露做任何Web系统都不应该踩这个坑。protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String password request.getParameter(password); if (userDao.findByUsername(username) ! null) { request.setAttribute(msg, 用户名已存在); request.getRequestDispatcher(register.jsp).forward(request, response); return; } User user new User(); user.setUsername(username); user.setPassword(MD5Util.md5(password)); // 密码绝不存明文 userDao.insert(user); response.sendRedirect(login.jsp); }登录成功后把用户对象放入Session键名用loginUser后续页面通过Session判断用户是否已登录。为了不让每个页面都写一遍判断逻辑用一个Filter统一拦截放行login.jsp、register.jsp和静态资源其余URL都要求Session里有loginUser没有就重定向到登录页。这样后续新加页面不需要重复写权限判断也不会出现「忘了校验」这种低级漏网。5.2 购物车与会话绑定Session购物车对象的设计与实现购物车是订餐系统里交互最频繁的模块它的设计直接决定用户体验。这里用Session保存购物车对象购物车的结构用Mapkey是餐品IDvalue是购物车对象。public class Cart implements Serializable { private MapInteger, CartItem items new HashMap(); public void add(Integer dishId, CartItem item) { if (items.containsKey(dishId)) { CartItem old items.get(dishId); old.setQuantity(old.getQuantity() item.getQuantity()); } else { items.put(dishId, item); } } public void remove(Integer dishId) { items.remove(dishId); } public void clear() { items.clear(); } public double getTotalPrice() { double total 0.0; for (CartItem item : items.values()) { total item.getPrice() * item.getQuantity(); } return total; } }为什么用Map而不用List因为用户重复点击「加入购物车」时Map按餐品ID直接命中已存在的条目然后修改数量比遍历List逐个比较更高效。每次操作完购物车要把新的Cart对象重新set到Session里因为Session中的对象需要刷新。实现Serializable接口是为Tomcat的Session钝化机制做准备服务器重启时Session内容能持久化不至于用户一刷新购物车就空了。购物车的操作路径要把「加购、改数量、删单品、清空」四个操作都覆盖到页面上的加减号对应修改数量接口删除按钮对应移除单项清空按钮对应clear方法。5.3 订单生成事务保证订单主表和明细表的一致写入订单生成是整个系统最核心的一段代码因为它涉及多张表的写入操作任何一个环节失败都会造成数据不一致。这里必须在同一个数据库事务里完成插入订单主表、遍历购物车插入订单明细表、扣减餐品库存、清空购物车。public Long createOrder(User user, Cart cart, String address, String phone) throws Exception { Connection conn getConnection(); try { conn.setAutoCommit(false); // 开启事务 String insertOrder insert into t_order(order_no, user_id, total_price, status, address, phone) values(?, ?, ?, 0, ?, ?); // orderNo 时间戳 用户ID 随机数保证不重复 PreparedStatement ps conn.prepareStatement(insertOrder, Statement.RETURN_GENERATED_KEYS); // 执行后取回自增订单ID用于写明细表外键 String insertItem insert into t_order_item(order_id, dish_id, dish_name, price, quantity) values(?, ?, ?, ?, ?); // 遍历购物车逐条写入明细金额以库里价格为准不信任前端传值 String updateStock update t_dish set stock stock - ? where dish_id ? and stock ?; // 带库存条件更新防止超卖 conn.commit(); // 全部成功才提交 } catch (Exception e) { conn.rollback(); // 任何一步失败都回滚 throw e; } finally { // 关闭连接 } }这段代码里有三个设计点值得在答辩时展开。第一订单总价必须在后端重新计算不能直接采用前端传来的金额因为前端传值可以被篡改用户改一下hidden input的金额就能以低价下单。第二库存扣减使用update ... where stock ?的条件更新而不是先查库存再更新这样能避免并发下单时超卖。第三明细表里冗余的dish_name和price是下单时刻的快照之后餐品表怎么改价都不影响历史订单展示。5.4 订单提醒与热卖展示Comet4j长连接消息推送管理员能不能第一时间看到新订单决定了整个系统的「实时感」。这里用Comet4j实现服务端推送管理员页面建立长连接后一旦有新订单插入服务端主动把订单号推到页面页面上弹出提醒。前端接入方式如下。script typetext/javascript srcjs/comet4j.js/script script typetext/javascript // 连接Comet4j长连接通道 JS.Engine.start(); // 监听服务端推送的新订单事件 JS.Engine.on({ newOrder: function (data) { // data.orderNo 是后台推送过来的订单号 // 该回调异步执行不阻塞页面其他操作 showNotice(收到新订单 data.orderNo); } }); /script服务端在订单插入成功后调用推送接口向所有在线的管理员页面广播newOrder事件。这个机制和传统的Ajax轮询相比优势在于订单到达的瞬间通知就能到达不需要管理员手动刷新也不会在长时间无新订单时反复发无效请求。热卖餐品的动态展示也可以复用这个思路后台调整推荐位后首页无须刷新就能更新展示。对于答辩来说这是技术复杂度比较高的一个点能讲清楚长连接和轮询的差异就已经超出多数同类毕设的水平了。5.5 常见问题排查五个最容易翻车的点做了几个类似项目后我把最容易翻车的点整理成了清单每个都有现象、原因和解决办法。第一类中文乱码。页面显示用户名和地址全是问号或乱码。原因是JSP文件编码、Servlet读取请求参数的编码、数据库连接串编码三者不一致。解决方法是统一使用UTF-8JSP页面设置pageEncodingUTF-8Servlet里调用request.setCharacterEncoding(UTF-8)MySQL连接串追加characterEncodingutf8三处缺一不可。第二类Session失效导致购物车清空。用户登录后放了十分钟回来购物车空了还要重新登录。原因是Tomcat默认Session超时时间可能被设置得过短或者服务器重启导致Session钝化配置缺失。解决方法是在web.xml里配置session-timeout为30分钟同时确认购物车对象实现了Serializable接口以便Session持久化恢复。第三类订单金额和购物车显示金额不一致。用户加购时看的是一个价下单后结算变另一个价。原因多半是餐品价格在用户浏览期间被管理员修改或者前端传了修改过的总价。解决方法是后端在下单时按数据库里的最新价格重新计算总价前端显示的价格只作展示用途所有金额计算以服务端为准。第四类订单提醒收不到。管理员页面开着但新订单就是不弹提示。原因通常是Comet4j连接断开后没有重连机制或者服务端订单插入成功但没有调用推送方法。解决办法是页面加载后检测连接状态断开则自动重新start服务端把推送调用放在事务提交成功之后保证推到页面的订单确实已落库。第五类数据库连接池耗尽导致系统卡死。订餐高峰期一过Tomcat日志里出现连接获取超时页面转圈很久才打开。原因是DAO层每次访问都手动new一个Connection用完没有正确关闭连接数被耗尽。解决方法是改用连接池在finally块里确保ResultSet、Statement、Connection按顺序关闭三层资源少关一层就可能泄漏。注意数据库连接在使用完成后一定要关闭很多人只关了Connection漏了Statement和ResultSet同样会造成连接泄漏。这三层资源都要在finally里逐一释放。6. 系统测试与答辩验收怎么证明这份网上订餐方案能交付6.1 分层测试单元、集成、系统用例怎么设计测试不只是「点一下按钮看能不能跑」要按层次设计。单元测试重点覆盖纯逻辑方法比如购物车的add、remove、getTotalPrice订单号生成器的唯一性集成测试重点覆盖Servlet调用Service、Service调用DAO的链路比如注册时用户名重复能不能正确拦截下单事务中库存不足能不能正确回滚系统测试站在用户视角走完整流程用一张测试用例表把关键场景列清楚。用例名称操作步骤预期结果用户注册填写用户名密码提交注册成功跳转登录页重复用户名被拦截餐品查询输入关键词搜索返回匹配餐品列表无结果有友好提示加入购物车重复加入同一餐品数量累加总价实时更新提交订单购物车结算下单生成待确认订单库存同步扣减管理员接单对新订单确认接单状态变为已接单用户端可见状态变化历史订单查看已完成订单明细展示下单时餐品快照和实际金额6.2 验收演示路径一条主线加三个亮点演示时不要零散地展示功能按一条主线走用户注册、登录、浏览热卖餐品、搜索餐品、加购两样菜、修改数量、清空再重加、提交订单然后切换到管理员账号演示新订单实时提醒、确认接单、完成配送再切回用户账号确认订单完成。这一条线走完核心功能全部覆盖。三个亮点单独讲一是Comet4j订单实时推送不用刷新页面二是订单和库存放在同一个事务里保证一致性三是历史订单展示餐品快照不受改价影响。这三个点能讲清楚这套方案的技术深度就立住了。6.3 答辩时值得讲深的三件事答辩最容易犯的错是「面面俱到但每个都浅」。与其全讲一遍不如把三件事讲透。第一件是需求分析到数据库设计的推导过程讲清楚用例图里的每一个操作对应哪张表的哪次读写第二件是订单事务的实现细节把setAutoCommit、commit、rollback的执行顺序讲清楚让评审老师知道你理解事务的边界第三件是消息推送的选型对比为什么不用轮询而用Comet4j两者的网络开销差异在哪里。这三件事讲完老师自然会觉得这套方案是完整的、动过脑子的。我印象最深的一次翻车就是上线前反复测试发现下单接口在快速点击时会生成重复订单号问题出在我只用了时间戳加随机数后来改成时间戳加用户ID加数据库唯一索引双重保障才算彻底解决。从那以后我每做完一个模块都会先写两条异常用例再继续往下写希望帮到你。本文还有配套的精品资源点击获取