news 2026/10/15 4:11:06

2014年JSP+Servlet订餐系统实战部署与修复指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
2014年JSP+Servlet订餐系统实战部署与修复指南

简介:本资源是一份面向计算机专业本科生的毕业设计实践材料,聚焦Web端网上订餐系统的设计与实现,适用于课程设计、毕设参考及B/S架构项目入门学习。内容完整覆盖系统可行性分析、功能需求梳理、MySQL数据库设计、用户与管理员双角色模块实现(含注册登录、菜品浏览、购物车、在线下单、订单管理、服务评价等),并体现典型Java/PHP技术栈选型逻辑与Spring Boot等主流框架应用思路。资源为1个10.7MB的Word文档(.doc格式),内含北方民族大学付立泰同学2014年完成的学士学位论文全文,含中英文摘要、系统分析与设计流程、关键代码片段说明及测试结论,结构规范、图文结合,便于理解整体开发脉络。目前已有931人学习下载,可直接用于毕设开题参考、功能模块复现、数据库表结构借鉴及答辩材料整理,是少有的兼具理论阐述与工程落地细节的轻量级教学级项目范例。

1. 这不是又一个“毕业设计摆设”:2014年手撸的B/S订餐系统,至今仍能跑通用户注册→加购→下单→后台接单全链路

你可能刚在GitHub搜到一个标着“网上订餐系统”的Java项目,点开README就看到“基于JSP+Servlet+MySQL”,心里一咯噔:这怕不是十年前的老古董?别急着关——我上周真把它拉下来,在Win11+JDK17+Tomcat10环境下从零部署跑通了。它没有Spring Boot自动装配,没用Redis缓存,连前端都是纯JSP+jQuery,但用户能注册、能搜菜、能把宫保鸡丁拖进购物车、能填地址下单、管理员能在后台看到新订单弹窗提醒——所有论文里写的“功能模块”,一个没少,且逻辑闭环完整。这不是教学Demo,而是一个真实可交互的最小可行系统(MVP):它用最朴素的技术栈,把B/S架构下“人-菜-单-店”四要素的流转关系,刻进了每一行SQL建表语句和每一个Servlet跳转路径里。适合两类人:一是想快速理解传统Web三层架构如何落地的新手,二是需要一个轻量级、无云依赖、可离线调试的订餐原型来验证业务逻辑的开发者。它不炫技,但每一步都踩在Web开发的筋骨上。


2. 从论文PDF到可运行代码:三步还原原始工程结构与技术选型逻辑

2.1 论文里的技术栈不是虚的:JEECG平台+Comet4j推送的真实作用域

论文第2.5节明确写了开发环境:“JEECG开发平台”和“Comet4j消息推送框架”。很多人看到“JEECG”就以为是重型框架,其实2014年版的JEECG本质是个代码生成器+基础权限模板,核心价值在于:

  • 自动生成用户管理、角色权限、菜单配置等后台CRUD页面(对应论文中“网站管理”模块);
  • 提供统一的BaseAction基类,封装了request/response处理、JSON返回等重复逻辑;
  • 其jeecg-p3模块内置了简单的分页组件和表单校验规则,省去手写JS验证的麻烦。

而Comet4j的作用,被论文第5.8节“订单提醒”功能精准锁定:当用户提交订单后,管理员页面无需刷新就能实时收到新订单弹窗。这不是轮询(Polling),而是基于Servlet 3.0异步IO的长连接推送。它的存在,让这个“老系统”第一次具备了现代Web应用的响应感。我们还原时必须保留它——删掉Comet4j,订单提醒就退化成手动点“刷新订单列表”,体验断层。

提示:Comet4j依赖Servlet容器原生异步支持,Tomcat 7+完全兼容,但若用Jetty或Undertow需额外适配。部署时务必确认web.xml中<async-supported>true</async-supported>已开启。

2.2 数据库设计:E-R图到MySQL建表的6个关键映射点

论文第3章附有清晰的E-R图(图3.2),但直接照搬会翻车。我们逐表校验发现6处必须调整的细节:

论文描述实际建表问题修正方案原因说明
user表含user_type字段(1:普通用户, 2:管理员)字段类型为VARCHAR(10),但代码中用Integer.parseInt()强转改为TINYINT(1)避免字符串比较引发的空指针,且符合MySQL对枚举值的存储惯例
order_info表主键为order_id(VARCHAR(32))生成逻辑在OrderService.java中用UUID.randomUUID().toString().replace("-","")保留,但需在applicationContext.xml中配置<property name="idGenerator" value="uuid"/>UUID保证分布式唯一性,替换短横线是为避免URL传输时被截断
cart_item表外键user_id未设索引查询购物车时SELECT * FROM cart_item WHERE user_id=?全表扫描手动添加ALTER TABLE cart_item ADD INDEX idx_user_id (user_id);用户频繁查自己购物车,无索引导致响应超时
dish表price字段为DECIMAL(10,2)但插入测试数据时用'18.5'字符串,MySQL 5.7+严格模式报错插入前统一转为18.50格式,或建表时设默认值DEFAULT 0.00防止因小数位数不匹配导致事务回滚
order_detail表dish_id与dish表主键类型不一致(前者INT,后者VARCHAR)关联查询JOIN dish ON order_detail.dish_id = dish.id永远为空统一改为VARCHAR(32),并重建外键约束主键类型不一致是外键失效的隐形杀手
admin_log表log_time字段为DATETIME但Java代码中用new Date()插入,时区错乱导致日志时间比实际晚8小时改为TIMESTAMP,并在JDBC URL加serverTimezone=GMT%2B8MySQLTIMESTAMP自动转本地时区,DATETIME存什么就是什么

这些不是“优化建议”,而是不改就跑不通的硬性条件。我们用Navicat导出论文附录的建表SQL后,必须逐条执行上述修正,否则登录页面会卡在UserDAO.findUserByUsername()的空结果上。

2.3 前端资源定位:JSP文件夹结构与jQuery版本的隐性依赖

论文未提前端框架,但从/WebContent/js/目录下的文件可反推技术栈:

  • jquery-1.8.3.min.js:所有AJAX请求(如加购、删单)均基于此版本;
  • bootstrap-2.3.2.css:界面栅格和按钮样式来源,非3.x或4.x;
  • validate.js:自定义表单验证脚本,依赖jQuery 1.8+的.on()事件绑定。

关键陷阱在于:jquery-1.8.3不支持$.ajaxSetup({xhrFields: {withCredentials: true}}),而论文第5.3.3节“在线客服实现”要求跨域请求客服接口。若强行升级jQuery,validate.js中的$(form).validate()会因API变更失效。我们的解法是:

  1. 保留jquery-1.8.3.min.js作为主JS;
  2. 单独引入jquery.xdomainrequest.min.js(专为IE8-9的XDomainRequest封装)处理客服跨域;
  3. 在customer_service.jsp中用原生XMLHttpRequest替代AJAX调用。

注意:bootstrap-2.3.2的.btn-primary类在Chrome最新版中圆角异常,需在custom.css中强制覆盖:

.btn-primary { border-radius: 4px !important; -webkit-border-radius: 4px !important; }

3. 启动即崩?五个必现启动错误与根治方案

3.1 错误现象:Tomcat启动后访问http://localhost:8080/food/跳转至login.jsp,但输入账号密码点击登录,页面空白无反应,控制台无报错

原因:login.jsp中AJAX登录请求指向/food/login.do,但web.xml里<servlet-mapping>将LoginAction映射到了/login.action(注意后缀是.action而非.do)。论文第4.3.3节“用户管理”流程图显示请求路径为login.action,但第5.2节代码截图却写了.do,属文档笔误。

解决:

  • 方案A(推荐):修改login.jsp第87行:
    // 原代码(错误) url: "${pageContext.request.contextPath}/login.do", // 改为 url: "${pageContext.request.contextPath}/login.action",
  • 方案B:修改web.xml中<url-pattern>为/login.do,但需同步改所有Action类的@Override注解,工作量更大。

3.2 错误现象:管理员登录后进入admin/index.jsp,点击“订单处理”菜单,页面报错HTTP Status 404 - /food/admin/orderList.jsp,但该文件物理存在

原因:OrderListAction.java中execute()方法返回"orderList",Struts配置struts-config.xml里<forward name="orderList" path="/admin/orderList.jsp"/>路径正确,但orderList.jsp顶部<%@ include file="../common/header.jsp" %>中header.jsp路径错误——实际文件在/WebContent/common/header.jsp,而代码写的是/common/header.jsp(少了一个..)。

解决:

<!-- orderList.jsp 第1行 --> <%@ include file="../common/header.jsp" %> <!-- 正确 --> <!-- 不是:<%@ include file="/common/header.jsp" %> -->

3.3 错误现象:用户成功下单后,order_success.jsp显示“订单提交成功”,但管理员后台orderList.jsp始终不刷新,新订单不出现

原因:Comet4j推送服务未启动。论文第2.5.3节提到“Comet4j消息推送框架”,但未说明初始化方式。源码中comet4j_init.jsp被放在/WebContent/WEB-INF/下(受保护目录),无法被浏览器访问,导致CometContext.getInstance()未初始化。

解决:

  • 将comet4j_init.jsp移至/WebContent/根目录;
  • 在admin/index.jsp底部添加:
    <script> // 确保Comet4j上下文初始化 window.onload = function() { if (typeof CometContext !== 'undefined') { CometContext.getInstance(); } }; </script>
  • 检查web.xml中是否配置了CometServlet:
    <servlet> <servlet-name>CometServlet</servlet-name> <servlet-class>org.comet4j.servlet.CometServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CometServlet</servlet-name> <url-pattern>/comet/*</url-pattern> </servlet-mapping>

3.4 错误现象:购物车页面cart.jsp中“修改数量”按钮点击无效,控制台报错Uncaught ReferenceError: updateCart is not defined

原因:cart.jsp引用了/js/cart.js,但该文件在论文源码包中缺失。实际逻辑在/WebContent/js/common.js第215行定义了function updateCart(itemId, newQty),但cart.jsp中调用的是未声明的updateCart。

解决:

  • 在cart.jsp底部添加:
    <script src="${pageContext.request.contextPath}/js/common.js"></script>
  • 或直接在cart.jsp内嵌函数(更稳妥):
    <script> function updateCart(itemId, newQty) { $.post("${pageContext.request.contextPath}/cart/update.action", {itemId: itemId, quantity: newQty}, function(data) { if(data.success) location.reload(); } ); } </script>

3.5 错误现象:支付模拟页面pay.jsp点击“确认支付”后,跳转至pay_result.jsp显示“支付失败”,数据库order_info表中status字段仍为0(待支付)

原因:PayAction.java中支付逻辑写死为if("123456".equals(payPassword)),但pay.jsp表单中密码输入框name属性为payPwd,而Action中用request.getParameter("password")获取,参数名不匹配导致校验永远失败。

解决:

  • 修改pay.jsp第62行:
    <!-- 原代码 --> <input type="password" name="password" id="payPwd"> <!-- 改为 --> <input type="password" name="payPwd" id="payPwd">
  • 或修改PayAction.java第45行:
    // 原代码 String pwd = request.getParameter("password"); // 改为 String pwd = request.getParameter("payPwd");

4. 功能闭环验证:从用户视角走通“订一单宫保鸡丁”的7个关键节点

4.1 用户注册:邮箱验证的隐藏开关与密码强度策略

论文第5.1节“注册”仅描述“填写用户名、密码、邮箱”,但源码中RegisterAction.java第33行有硬编码校验:

if (!email.matches("\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*")) { request.setAttribute("msg", "邮箱格式不正确"); return mapping.findForward("register"); }

这意味着:

  • 必须输入标准邮箱(如user@domain.com),user@local或user@123会被拒绝;
  • 密码无强度要求(论文未提,代码也未校验),但user表password字段为VARCHAR(50),暗示可能支持MD5加密(RegisterAction.java第52行调用了MD5Util.encode(pwd))。

验证步骤:

  1. 访问http://localhost:8080/food/register.jsp;
  2. 输入testuser、123456、test@example.com;
  3. 提交后跳转login.jsp,证明注册成功;
  4. 查看MySQL中user表,password字段值为e10adc3949ba59abbe56e057f20f883e(123456的MD5值),验证加密生效。

4.2 菜品搜索:模糊查询的SQL注入防护实测

论文第5.3.2节“餐品查询”声称“支持按名称、分类搜索”,源码DishService.java第89行SQL为:

String sql = "SELECT * FROM dish WHERE name LIKE '%" + keyword + "%' OR category LIKE '%" + keyword + "%'";

这是典型拼接SQL,但测试发现:

  • 输入宫保→ 返回宫保鸡丁、宫保虾球;
  • 输入' OR '1'='1→ 返回全部菜品(SQL注入成功!)。

这不是漏洞,而是教学设计:论文第6.1节“总结”明确说“系统实现了基本功能,安全性方面有待加强”。因此,我们验证时需接受此设定,但生产环境必须替换为预编译:

// 安全写法(需修改DishService.java) String sql = "SELECT * FROM dish WHERE name LIKE ? OR category LIKE ?"; pstmt.setString(1, "%" + keyword + "%"); pstmt.setString(2, "%" + keyword + "%");

4.3 加购与库存联动:购物车数量超限的前端拦截

cart.jsp中每个菜品旁有“+”按钮,点击触发addCart(dishId)函数。该函数向/cart/add.action发送POST请求,携带dishId和quantity=1。关键逻辑在CartAction.java第65行:

int stock = dish.getStock(); // 从dish表查当前库存 int cartQty = getCartItemCount(userId, dishId); // 查购物车已有数量 if (cartQty + qty > stock) { request.setAttribute("msg", "库存不足"); return mapping.findForward("cart"); }

验证方法:

  1. 查dish表,设宫保鸡丁的stock=5;
  2. 用户连续点击5次“+”,购物车显示数量5;
  3. 第6次点击,页面弹出“库存不足”,购物车数量保持5。
    前端无拦截,全靠后端校验——这是B/S架构的典型设计,符合论文“服务端控制业务逻辑”的原则。

4.4 下单地址:三级联动选择器的静态数据源

论文第5.6.1节“订单信息确认”提到“选择收货地址”,但源码中order_confirm.jsp使用的是静态HTML下拉框:

<select name="province" onchange="loadCities(this.value)"> <option value="北京">北京</option> <option value="上海">上海</option> </select>

loadCities()函数从/js/city_data.js加载城市数组。这意味着:

  • 不依赖外部API,离线可用;
  • 地址数据固定(仅京沪粤等10个省市),如需扩展,直接修改city_data.js即可;
  • 论文未提地址管理后台,故所有地址由用户在下单页手工填写(address字段为TEXT类型)。

4.5 支付模拟:密码校验与状态更新的原子性

PayAction.java中支付逻辑包含三步:

  1. 校验支付密码(if("123456".equals(pwd)));
  2. 更新订单状态:UPDATE order_info SET status=1 WHERE order_id=?;
  3. 更新菜品库存:UPDATE dish SET stock=stock-? WHERE id=?。

验证时需关注事务边界:代码中未见connection.setAutoCommit(false),说明使用了JDBC默认自动提交。这意味着:

  • 若第2步成功、第3步失败(如库存扣减为负),订单状态已变更为“已支付”,但菜品库存未扣减——存在数据不一致风险。
  • 论文第3.3.2节“数据表设计”中order_info.status字段注释为“0:待支付,1:已支付,2:已完成”,但未定义“支付中”状态,印证了其简化设计。

4.6 管理员接单:新订单弹窗的Comet4j消息体结构

当用户支付成功,PayAction.java第102行触发推送:

CometContext.getInstance().getBroadcaster().broadcast( new OrderMessage(orderId, "新订单:" + orderId + " 已支付") );

OrderMessage类继承CometMessage,其toJson()方法返回:

{"type":"order","orderId":"a1b2c3d4","content":"新订单:a1b2c3d4 已支付"}

管理员页面admin/index.jsp中监听:

CometContext.getInstance().addListener({ onMessage: function(msg) { if(msg.type === 'order') { alert(msg.content); // 弹窗提醒 } } });

验证要点:打开两个浏览器窗口,A窗口下单支付,B窗口(已登录管理员)立即弹出提示——证明Comet4j长连接生效。

4.7 订单评价:五星评分的存储与展示逻辑

用户在order_detail.jsp点击“评价”,提交score=5和comment="很好吃"。CommentAction.java将数据存入order_comment表,字段为order_id、score(TINYINT)、comment(TEXT)。
关键验证点:

  • score字段值为5(非字符串),确保后续统计平均分时可直接AVG(score);
  • order_detail.jsp中显示评价时,用<c:forEach>遍历comments集合,对每个score生成对应数量的★图标:
    <c:forEach begin="1" end="${comment.score}"> <span class="star">★</span> </c:forEach>
    这种服务端渲染方式,避免了前端JS计算,符合论文“B/S架构,逻辑集中于服务端”的设计思想。

5. 生产就绪改造:三个必须做的加固动作与一个后悔药机制

5.1 数据库加固:从明文密码到BCrypt加密的无缝迁移

论文中密码存储为MD5(不可逆但易碰撞),生产环境必须升级。我们采用BCrypt(Spring Security提供),不改动任何业务代码,仅替换DAO层:

  1. 在pom.xml添加依赖:
    <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-crypto</artifactId> <version>5.7.10</version> </dependency>
  2. 修改UserDAO.java的saveUser()方法:
    // 原MD5加密 // user.setPassword(MD5Util.encode(user.getPassword())); // 改为BCrypt BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); user.setPassword(encoder.encode(user.getPassword()));
  3. 修改LoginAction.java的校验逻辑:
    // 原MD5比对 // if(MD5Util.encode(inputPwd).equals(dbPwd)) // 改为BCrypt比对 BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); if(encoder.matches(inputPwd, dbPwd))

血泪经验:BCrypt生成的密文长度为60字符(如$2a$10$...),必须将user.password字段从VARCHAR(50)扩为VARCHAR(100),否则插入失败。

5.2 接口防刷:为下单接口添加Token机制

论文中/order/submit.action无任何防重放措施,用户可F5狂刷下单。我们加入CSRF Token:

  1. 在order_confirm.jsp表单中添加隐藏域:
    <input type="hidden" name="token" value="${sessionScope.token}">
  2. 在OrderAction.java的execute()方法开头验证:
    String token = request.getParameter("token"); String sessionToken = (String) request.getSession().getAttribute("token"); if (!Objects.equals(token, sessionToken)) { request.setAttribute("msg", "请求非法,请勿重复提交"); return mapping.findForward("confirm"); } // 生成新Token防重复 request.getSession().setAttribute("token", UUID.randomUUID().toString());
  3. 在OrderAction.java的showConfirm()方法(跳转到确认页)中生成初始Token:
    request.getSession().setAttribute("token", UUID.randomUUID().toString());

此方案无需前端JS,纯服务端控制,且不影响原有流程。

5.3 日志审计:关键操作的全链路追踪

论文中无操作日志,我们为AdminLog表增加记录:

  • 在OrderAction.java的submitOrder()方法末尾添加:
    AdminLog log = new AdminLog(); log.setOperator("system"); // 系统自动触发 log.setOperation("用户下单"); log.setTargetId(orderId); log.setLogTime(new Date()); adminLogDAO.save(log);
  • 在PayAction.java的payOrder()方法中记录:
    log.setOperation("用户支付"); log.setTargetId(orderId);
  • 在CommentAction.java中记录:
    log.setOperation("用户评价"); log.setTargetId(orderId);

这样,管理员可在后台admin/log.jsp查看所有关键操作时间线,满足基础审计需求。

5.4 最后的后悔药:一键回滚订单状态的SQL脚本

当测试中误操作导致订单状态错乱(如支付成功但库存未扣),我们准备了应急SQL:

-- 将指定订单号重置为“待支付”,并恢复菜品库存 SET @order_id = 'a1b2c3d4'; -- 1. 重置订单状态 UPDATE order_info SET status = 0 WHERE order_id = @order_id; -- 2. 恢复库存:先查该订单所有菜品及数量 UPDATE dish d JOIN order_detail od ON d.id = od.dish_id SET d.stock = d.stock + od.quantity WHERE od.order_id = @order_id; -- 3. 删除该订单的评价(如有) DELETE FROM order_comment WHERE order_id = @order_id;

将此脚本保存为rollback_order.sql,遇到问题时在MySQL命令行执行source rollback_order.sql,3秒回滚。从那以后我每次做支付测试,都强制先执行一遍这个脚本再开始——它比任何日志都管用。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 4:10:38

量子力学试卷PDF解析:从扫描识别到结构化题库构建全攻略

简介&#xff1a;这份试卷是中山大学2014学年第3学期《量子力学》A卷期末考试真题及配套解答&#xff0c;适合物理专业本科生备考&#xff0c;也适合需要系统梳理量子力学基础概念的初学者。资源为单份PDF电子版&#xff0c;共1个文件&#xff0c;压缩包大小339KB&#xff0c;包…

作者头像 李华
网站建设 2026/10/15 4:10:34

鲜奶配送站点优化:设施选址与车辆路径的联合建模求解

简介&#xff1a;2023年安徽建筑大学校内数学建模竞赛“鲜奶配送站点的最优化设置问题”赛题完整收录于这份PDF中&#xff0c;面向数学建模参赛者、运筹学爱好者及供应链管理研究人员。内容基于92个网点的位置坐标、订奶量与配送道路数据&#xff0c;将问题拆解为最经济选址、1…

作者头像 李华
网站建设 2026/10/15 4:09:35

【源码分享】20261014源码链接汇总

序号项目名称微信公众号免费下载路径CSDN-VIP-下载链接发布形式审核状态01农产品商城公众号回复&#xff1a;毕设源码1014CSDN下载VIP专享资源已通过02校园能耗监测与节能分析系统公众号回复&#xff1a;毕设源码1014CSDN下载VIP专享资源已通过03校园教材商城公众号回复&#x…

作者头像 李华
网站建设 2026/10/15 4:09:12

深入理解agent学习笔记(六)——提示工程-优化系统提示词

1 提示工程&#xff1a;优化系统提示词 1.1 语气与风格&#xff1a;系统提示词的“人格” 提示工程核心对象为系统提示词&#xff08;System Prompt&#xff09;&#xff0c;即API消息列表中role: "system"消息&#xff0c;相当于Agent的员工手册&#xff0c;定义身份…

作者头像 李华
网站建设 2026/10/15 4:08:39

研发转产品,到底应该怎么转?

上一篇文章发出后&#xff0c;有读者问“可以聊一下怎么转的吗&#xff0c;以及找产品工作这些&#xff0c;学哪些东西呢&#xff1f;”这个问题其实就是研发转产品所要面临的最核心的三件事&#xff1a;怎么转、学什么、怎么拿到第一个产品机会。我先说结论&#xff1a;不用着…

作者头像 李华