简介:这是一份基于Java的农产品网上销售系统毕业设计文档,适用于计算机相关专业学生在课程设计、毕业设计或系统开发学习中使用。文档从实际项目出发,围绕农产品网上销售系统的建设展开,涵盖开发背景、需求分析、总体设计、功能模块划分以及数据库设计等核心环节,并以JSP技术与MySQL数据库为主要技术方案,帮助读者理清从系统规划到落地实现的完整思路。资源仅包含1个docx文件,大小约1.27MB,文件内容为完整的论文文档,包括摘要、绪论、系统分析、设计实现等章节,可作为撰写同类设计报告的重要参考。目前已有72人学习,对于需要完成类似课题或了解电商类系统设计流程的读者具有较高的借鉴价值,同时文档结构完整、逻辑清晰,能够为系统设计过程中的难点提供参考。
1. 农产品网上销售系统:这份JSP+MySQL课设源码先看哪几件事
做课设、备答辩拿到一套“基于 Java 的农产品网上销售系统设计与实现”时,第一件事不是急着跑起来,而是先弄清它是什么。这套系统是典型的 JSP + MySQL 组合,B/S 结构,把账号角色分成用户和管理员两端:用户侧有注册登录、商品浏览、购物车、下单、退货、留言;管理员侧负责会员、商品类别、商品和订单审核。覆盖了电商类毕设最常考的核心流程,功能完整、表结构清楚,适合想快速拿一个能演示的项目、又不想从头写框架的同学。下面按“先看设计、再搭环境、后改功能”的顺序把它拆开,重点把表结构、登录逻辑和部署踩坑说透。
2. 架构与功能边界:为什么JSP+MySQL把用户端和管理员端分开设计
2.1 从需求到模块:两端功能边界先拆清楚
农产品网上销售系统最有价值的地方,在于它把“卖货”这件事从买家和卖家两个视角做了完整拆分。买家关心怎么注册、怎么找商品、怎么把东西放进购物车、怎么跟踪订单;卖家关心的是商品上架、库存、订单处理、用户管理。功能上一拆,数据库表和 JSP 页面就都有了归属,开发的时候不会绕晕。
从功能需求整理出来的边界大致像下面这样:
| 角色 | 功能模块 | 对应页面/操作 |
|---|---|---|
| 用户 | 注册登录 | 账号、密码、验证码,登录后进入用户主界面 |
| 用户 | 商品信息 | 浏览商品列表、查看商品详情 |
| 用户 | 购物车 | 加入购物车、调整数量、删除 |
| 用户 | 订单管理 | 下单、查看我的订单、取消/退货 |
| 用户 | 留言 | 提交留言,展示留言记录 |
| 管理员 | 会员管理 | 用户列表、用户状态维护 |
| 管理员 | 类别管理 | 添加农产品类别、维护类别排序 |
| 管理员 | 商品管理 | 商品类别、产品列表、添加产品 |
| 管理员 | 订单管理 | 审核中订单、已发货订单、退货订单 |
注意这张表和论文目录里第五章“系统实现”是对得上的。也就是说,这份资源里的界面代码基本就是围绕这些功能写的,后面改代码时按这个边界找文件即可。
2.2 技术选型:JSP+Servlet+B/S在毕设场景的真实地位
现在很多同学一听到 JSP 就觉得老、落后,但在这个项目里它反而是稳妥选择。JSP 本身是 Java 系的动态网页技术,天然能和 Servlet、JavaBean 配合,一次编写到处运行,从 Eclipse 或 MyEclipse 里部署到 Tomcat 就能跑。在课设和毕设答辩环境里,JSP 的优点是:源码直观,逻辑容易解释,页面和逻辑代码文件分开,评委问起来你能讲清楚“哪一段是页面、哪一段是服务器逻辑”。比起一上来就上 Spring Boot + Vue,这种“裸 JSP”方案的学习成本和排错成本都低得多。
B/S 结构在这类系统里几乎是标配。客户端只需要浏览器,服务端放 Tomcat 和 MySQL,所有业务处理和数据访问都在服务器上完成。相比 C/S 结构,B/S 不需要装客户端,演示时只要同一局域网能访问就行,对设备和网络的要求低很多。原文里提到的表示逻辑层、控制逻辑层、数据展现层,翻译成工程语言就是:JSP 页面管展示,Servlet 管接收请求和跳转,DAO 层管数据库读写。三者相对独立又相互关联,这是 B/S 系统拆解的基础。
实际动手时,MyEclipse 和 Tomcat 的组合有一个门槛要先跨过去:Tomcat 版本和 JDK 版本必须匹配。Tomcat 8.5 配 JDK 1.8 是课设里的常见组合,启动报Unsupported major.minor version就表示 JDK 版本不匹配。把编译级别调到 JDK 1.8,再检查 web.xml 头部的 Servlet 版本声明,年代老的代码最好别直接用 Tomcat 10,因为 Jakarta 命名空间变化会导致 Servlet 类找不到。这是很多老课设拿到新环境跑不起来的第一个坎,先确认版本再启动。
2.3 登录判定:一个入口、两个主界面的角色分流
这套系统最核心的入口是登录模块,活动图表述很简单:用户输入账号和密码,系统先判断账号是否存在、密码是否正确,再判断是管理员还是普通用户,分别进入不同主界面。判断逻辑不复杂,但在 JSP 里新手最容易犯的错是把判断直接写在 JSP 页面里,混着一堆 HTML 标签,改起来非常痛苦。
常见做法是在 Servlet 里做统一校验:
@WebServlet("/login") public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); // 先判断是不是管理员,管理员账号通常有独立校验逻辑 if ("admin".equals(username) && "admin123".equals(password)) { request.getSession().setAttribute("admin", username); response.sendRedirect("admin/index.jsp"); return; } // 普通用户去数据库 users 表里查 UserDao userDao = new UserDao(); User user = userDao.findByUsernameAndPassword(username, password); if (user != null) { request.getSession().setAttribute("loginUser", user); response.sendRedirect("index.jsp"); } else { // 登录失败,回传提示信息并回显到登录页 request.setAttribute("msg", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } } }这段代码把登录拆成两件事:先判断是不是管理员,再查普通用户表。放到 session 里的是角色标记(admin 或 loginUser),后面的 JSP 页面用<c:if>或 session 取值来判断显示哪套菜单。参数上注意 password 这里为了演示用了明文比对,实际课设建议至少做一层 MD5,答辩时算一个能说出口的安全改进点。
登录状态拦截也有两种做法。一种是在每个需要登录的 JSP 页面顶部写一小段 Java 脚本或 JSTL 判断,快速但不够优雅。另一种是写一个 LoginFilter,在 doFilter 里判断 uri 和 session 状态,不需要每个页面重复贴判断代码。如果答辩时间充裕,我会把第二种作为改进点提出来,属于小成本高展示度的优化。
3. 数据库设计:users、product、orders这类核心表怎么建才不返工
3.1 六张表对应两端功能:先列清单再动手
很多人在建库时容易随手建几张表,导致后面查订单、查购物车时关联不上。这个系统的表设计其实是跟着功能走的,我在拆的时候整理出的核心表大概是这六张:
| 表名 | 用途 | 关键字段 |
|---|---|---|
| users | 用户/会员信息 | id、username、password、phone、address |
| category | 农产品类别 | id、name、sort |
| product | 农产品商品 | id、category_id、name、price、stock、image、description |
| cart | 购物车(可选) | id、user_id、product_id、quantity |
| orders | 订单主表 | id、order_no、user_id、total_price、status、create_time |
| order_detail | 订单明细 | id、order_id、product_id、price、quantity |
如果项目里还要做留言功能,再加一张 message 表,字段就是 id、user_id、content、create_time。这套表结构不需要太复杂,能跑通“用户下单→管理员发货”的完整链路就够了。
3.2 用户表与类别表:建表SQL里的字段取舍
用户表的作用不只是存账号密码,它还要承载收货信息,因为订单配送时需要用户姓名、电话和地址。字段类型上,id 用自增主键,username 加唯一索引,password 建议存加密后的定长字符串,phone 和 address 用 varchar 以兼容不同长度。
CREATE TABLE `users` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '用户ID', `username` VARCHAR(50) NOT NULL COMMENT '登录账号', `password` VARCHAR(64) NOT NULL COMMENT '登录密码,可存MD5值', `realname` VARCHAR(50) DEFAULT NULL COMMENT '收货姓名', `phone` VARCHAR(20) DEFAULT NULL COMMENT '联系电话', `address` VARCHAR(200) DEFAULT NULL COMMENT '收货地址', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';逻辑说明:这里把 realname、phone、address 放进用户表,是因为这套课设没有独立的收货地址表,一个用户对应一条地址记录就够了;如果后面想支持多地址,再把收货信息拆成单独的表。参数上需要注意 CHARSET 用 utf8mb4,能存中文和 emoji,避免农产品名称或备注里带特殊字符时写入失败。
类别表更简单,一般就是 id、name、sort 三个字段:
CREATE TABLE `category` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '类别ID', `name` VARCHAR(50) NOT NULL COMMENT '类别名称,如蔬菜、水果', `sort` INT DEFAULT 0 COMMENT '排序值,越小越靠前', PRIMARY KEY (`id`), UNIQUE KEY `uk_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='农产品类别表';这里用 sort 字段而不是直接按 id 排序,是因为管理员调整类别展示顺序时需要一组可控的排序值,改 sort 比改 id 安全得多。
3.3 商品表与购物车:库存、图片和Session两种方案
商品表是用户端浏览的核心。产品列表和详情页显示的名称、图片、价格、库存都从这张表取,所以字段要覆盖展示和下单两件事:
CREATE TABLE `product` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '商品ID', `category_id` INT NOT NULL COMMENT '所属类别ID', `name` VARCHAR(100) NOT NULL COMMENT '商品名称', `price` DECIMAL(10,2) NOT NULL COMMENT '销售单价', `stock` INT NOT NULL DEFAULT 0 COMMENT '库存数量', `image` VARCHAR(200) DEFAULT NULL COMMENT '商品图片URL', `description` TEXT COMMENT '商品描述', `status` TINYINT DEFAULT 1 COMMENT '1上架 0下架', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '上架时间', PRIMARY KEY (`id`), KEY `idx_category` (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='农产品商品表';price 用 DECIMAL(10,2),不要用 FLOAT 或 DOUBLE,结算时涉及金额累计,浮点数容易出现 0.1+0.2 不等于 0.3 这类问题。stock 字段在下单后要同步扣减,也可以加一个无符号约束,防止库存减成负数。
购物车在 JSP 课设里最常见有两种方案。一种是建 cart 表落库,一种是直接在 Session 里存一个Map<商品ID, 数量>。落库的好处是用户换设备购物车还在,坏处是要写增删改查和关联查询;Session 方案简单,刷新页面、换浏览器就丢。对于这个规模的项目,我倾向用 Session 方案,代码量少,答辩时也好解释。
3.4 订单表与订单详情:一个订单头带多个明细
订单表设计成“主表 + 明细表”两层的核心原因是一个订单可能包含多种农产品。orders 表存订单编号、用户、总价、状态,order_detail 表存每一条商品的单价和数量,两张表通过 order_id 关联:
CREATE TABLE `orders` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '订单ID', `order_no` VARCHAR(32) NOT NULL COMMENT '订单编号', `user_id` INT NOT NULL COMMENT '下单用户ID', `total_price` DECIMAL(10,2) NOT NULL COMMENT '订单总价', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0待审核 1已发货 2已完成 3退货中 4已退货', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '下单时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表';CREATE TABLE `order_detail` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '明细ID', `order_id` INT NOT NULL COMMENT '订单ID', `product_id` INT NOT NULL COMMENT '商品ID', `price` DECIMAL(10,2) NOT NULL COMMENT '下单时单价', `quantity` INT NOT NULL COMMENT '购买数量', PRIMARY KEY (`id`), KEY `idx_order` (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';注意到 order_detail 里的 price 存的是下单时的单价,而不是去 product 表实时查。这是电商系统的常见做法:成交价以订单记录为准,避免之后商品改价影响历史订单的金额核算。订单编号建议用时间戳加随机数生成,保证唯一,方便用户和管理员用编号定位问题。两张表通过 order_id 关联查询,一个订单头带多条明细,这是订单模块最稳定的结构。
4. 关键模块实现:从登录校验到订单状态流转的代码解读
4.1 登录与Session管理:一个Servlet区分用户和管理员
2.3 节里已经给出登录 Servlet 的骨架,这里细化一下它和页面之间怎么配合。login.jsp 表单提交到 /login,Servlet 里先做字符编码处理,再取参数,然后判断角色。管理员账号有的项目写在配置里,有的单独建了 admin 表,具体以源码为准,但套路都一样:取参数、比对、存 session、跳转。
登录成功之后,重点是 session 的使用。管理员和普通用户要分别存不同 key,避免两个主界面互相污染。我在 JSP 页面里一般用request.getSession().getAttribute("loginUser")来判断是否已登录,在 Servlet 里做拦截;如果 session 里没有 loginUser,就重定向回 login.jsp。参数说明:登录时还经常遇到验证码,session 里一般存一个 code 字段,比对时忽略大小写,生成逻辑单独放一个验证码 Servlet,跟主流程不耦合。
// 登录拦截的Filter示例,按需配置在web.xml或注解里 public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; // 放行登录页和静态资源,其余请求必须登录 String uri = request.getRequestURI(); if (uri.endsWith("login.jsp") || uri.endsWith("login") || uri.contains("/css/")) { chain.doFilter(req, resp); return; } if (request.getSession().getAttribute("loginUser") != null || request.getSession().getAttribute("admin") != null) { chain.doFilter(req, resp); } else { response.sendRedirect("login.jsp"); } } }这个 Filter 把登录校验从每个页面里抽出来,URI 以 .jsp 结尾的都检查一遍,登录页和静态资源放行。写 Filter 要注意放行名单不要漏了验证码图片和静态样式,否则页面会变成光头样式。
4.2 商品加入购物车:参数传递与Session购物车
购物车如果采用 Session 方案,核心就是传 productId 和 quantity 两个参数。商品列表页里每个商品的“加入购物车”按钮是一个链接或表单,提交后由 CartAddServlet 处理。代码示意:
@WebServlet("/cart/add") public class CartAddServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); // 购物车使用Map,key是商品ID,value是数量 Map<Integer, Integer> cart = (Map<Integer, Integer>) session.getAttribute("cart"); if (cart == null) { cart = new HashMap<Integer, Integer>(); } int productId = Integer.parseInt(request.getParameter("productId")); int quantity = 1; if (request.getParameter("quantity") != null) { quantity = Integer.parseInt(request.getParameter("quantity")); } // 已存在该商品则累加数量,否则新增一条 if (cart.containsKey(productId)) { cart.put(productId, cart.get(productId) + quantity); } else { cart.put(productId, quantity); } session.setAttribute("cart", cart); response.sendRedirect("cart.jsp"); } }这里用 Map<Integer, Integer> 存购物车,key 是商品ID,value 是数量。加购逻辑是:购物车里已有该商品就累加数量,没有就新增。参数 productId 必须用 Integer.parseInt 转成 int,如果页面里没传或传了非数字,会抛 NumberFormatException,所以页面端要保证参数名一致。我的习惯是给每个加购链接都写成cart/add?productId=${p.id},避免拼错。购物车页面上展示商品名和单价时,要用 productId 去 product 表查商品信息,再跟 cart 里的数量组合成列表。
4.3 订单状态流转:待审核、已发货、退货怎么落库
订单状态在表设计时已经预留了 status 字段,这套系统围绕它做流转:
| status | 含义 | 触发操作 |
|---|---|---|
| 0 | 待审核 | 用户提交订单后默认值 |
| 1 | 已发货 | 管理员在订单管理中点发货 |
| 2 | 已完成 | 用户确认收货或系统自动完成 |
| 3 | 退货中 | 用户发起退货 |
| 4 | 已退货 | 管理员同意退货并退款标记 |
写更新语句时,最需要注意的是“状态只能正向流转”。我见过很多新手在管理员发货时直接UPDATE orders SET status=1,不去判断当前状态是不是 0,结果已完成的订单被重复发货。稳妥做法是先查当前状态,再更新:
UPDATE orders SET status = 1 WHERE id = ? AND status = 0;受影响行数为 0 就说明订单不在可发货状态,需要在日志里记录。用户退货同理,只有 status=1 或 status=2 的订单才能发起退货申请,提交后 status 改成 3,管理员审核通过后再改成 4。这个条件更新是防止状态错乱最简单的办法,也方便答辩时讲“并发安全”的思路。
4.4 管理员端的商品与订单管理:两种常见实现套路
管理员端功能不复杂,但在 JSP 里存在两个实现流派。一种是每个管理功能建独立的 Servlet,比如 ProductManageServlet、OrderManageServlet,通过 action 参数区分 add、update、delete、list;另一种是用一个 DispatchServlet 接收不同类型和动作,内部用 switch 分发。课设里第一种更直观,文件多但每个类职责清晰。代码上就是 CRUD 四件事,商品管理比订单管理多一个图片上传和类别下拉框。
商品管理页面通常由三部分组成:列表展示区、编辑表单区、删除按钮。JSP 里列表用 JSTL 的<c:forEach>循环输出商品。分页如果做,常见做法是接收 page 参数,SQL 用LIMIT (page-1)*size, size,同时查 count(*) 得到总记录数,页面上渲染上一页下一页。注意 page 参数要做 Integer.parseInt 容错,转数字失败时默认第一页,否则用户手动改 URL 参数会直接把页面炸白。
订单管理更简单,管理员按 status 分组查看待审核和已发货订单,点按钮触发状态更新。按钮点击后提交 orderId 到后端 Servlet,后端重新查一次当前状态再做更新,避免客户端随意改状态。这就是这套系统用户端和管理员端完整的实现思路,把这几条线理清楚,整个代码结构就透明了。
5. 部署避坑:MySQL驱动、JSP乱码和Tomcat路径那点事儿
5.1 MySQL 8.x驱动类名变了,程序直接ClassNotFoundException
现象:原来的代码用Class.forName("com.mysql.jdbc.Driver")启动后报ClassNotFoundException,页面全白。
原因:MySQL 8.x 之后驱动类名改成了com.mysql.cj.jdbc.Driver,新版本的连接器 jar 包里旧类名已经不再使用。代码里写的是老类名,驱动自然加载不到。
解决:确认 mysql-connector-java.jar 的版本,8.x 就用新类名:
Class.forName("com.mysql.cj.jdbc.Driver");如果项目还是 MySQL 5.7,继续用旧类名没问题。经验是:拿到项目先看一眼 lib 目录里的驱动包,再看代码里的类名,两者一致再启动。
5.2 连接URL没加时区参数,连接时又慢又报错
现象:数据库连接偶尔成功,偶尔报The server time zone value ... is unrecognized,或者连接后第一次查询特别慢。
原因:MySQL 8.x 对时区要求更严格,连接字符串里没有serverTimezone参数,驱动拿不到可识别的时区,直接拒绝连接或回退到服务器本地时区,导致每次建连都在等时区解析。
解决:在 JDBC URL 显式加时区:
jdbc:mysql://localhost:3306/farm_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8参数说明:useSSL=false 是因为本地开发没有配置 SSL 证书,关掉避免握手告警;characterEncoding=utf8 保证中文正常写入。如果用到高版本连接器,还可以加allowPublicKeyRetrieval=true,解决 MySQL 8 的 caching_sha2_password 插件带来的公钥获取失败问题。
提示:如果连接 URL 里的端口写成 3307 而本机 MySQL 是 3306,会出现 Connection refused。拿到代码先确认 MySQL 监听端口,再改 URL,别在端口上玄学排查半天。
5.3 JSP页面和数据库双向乱码,中文变问号
现象:页面显示正常中文,但从数据库读出来的中文是问号;或者往数据库插入中文后变成乱码。
原因:三层编码不一致。JSP 页面没设置 pageEncoding,Servlet 里没设 request 编码,MySQL 表字符集和连接串编码不统一,任意一层断掉都会乱码。
解决:把三层统一成 UTF-8。JSP 头部加pageEncoding="UTF-8",Servlet 里第一行写request.setCharacterEncoding("UTF-8"),数据库连接 URL 里的 characterEncoding 保持 utf8,建表时用 utf8mb4。这套组合大部分乱码都能解决。少数情况下还需要检查 MySQL 服务端默认字符集,用SHOW VARIABLES LIKE 'character_set%'查看,不一致就改 my.ini 后重启。
5.4 改完代码不生效,Tomcat缓存和虚拟路径在捣乱
现象:改了 JSP 或 Servlet,刷新页面还是旧效果,清理浏览器缓存也没用。
原因:第一种可能是 Tomcat 没把新编译的 class 加载进来,尤其是改了 Servlet 后没有重启;第二种是项目部署用的是外部映射目录,访问的页面跟实际编辑的文件不是同一个,你改的是 workspace 里的文件,Tomcat 跑的是另一个副本。
解决:改 Servlet 一定要重启 Tomcat;改 JSP 一般会自动重新编译,但如果写了复杂的 taglib 或自定义标签,也需要重启。部署时我习惯直接把项目放到 Tomcat 的 webapps 下,用 IDE 发布时不要勾选“使用自定义部署路径”,免得找不到文件。
5.5 论文前后技术栈描述不一致,先对着核心代码核一遍
现象:摘要把系统写成 JSP,后面正文里又出现“本系统采用 PHP 技术”,页面里也有 .jsp 文件但有人按 PHP 去配环境,折腾半天跑不起来。
原因:论文模板复制痕迹太重,技术栈描述前后没统一,这是这类课设文档里最典型的问题。标题写 Java,摘要写 JSP,正文又冒出来一句 PHP,不是项目本身有问题,是文字抄串了。
解决:拿到资源先翻两个地方:一是 lib 目录的 jar 包,二是后缀名是 .jsp 还是 .php。只要是 JSP 页面和 JDBC 驱动,就按 JSP + MySQL 这一套来搭环境,不要被论文文字带偏。这也是我拆这类资源时最常遇到、也一定要提醒的话。
6. 跑通后的核对表与三个可以写进简历的进阶改法
6.1 一张表核对功能有没有跑全
环境搭好之后,我建议按下面的顺序完整走一遍,而不是只看到首页能打开就认为部署成功:
| 顺序 | 操作路径 | 预期结果 |
|---|---|---|
| 1 | 注册一个新用户 | 数据库 users 表新增记录,密码建议加密存储 |
| 2 | 用户登录进入首页 | 成功进入用户主界面,session 有 loginUser |
| 3 | 浏览商品并加入购物车 | cart.jsp 列表出现商品,数量可累加 |
| 4 | 提交订单 | 订单表新增记录,状态为 0 待审核 |
| 5 | 管理员登录 | 能进入管理端,看到待审核订单 |
| 6 | 管理员发货 | 订单状态变为 1 已发货 |
| 7 | 用户查看我的订单 | 订单显示已发货,可发起退货 |
| 8 | 管理员处理退货 | 订单状态变为 4 已退货 |
走通这八步,核心链路就完整了,剩下的是商品类别、留言这些边缘功能的点检。每一步出问题,先看数据库对应表的状态,再看页面报错,能快速定位是页面逻辑问题还是数据问题。
6.2 三个进阶改法:连接池、MVC分层、Ajax查重
第一个原子级改动是数据库连接改用连接池。课设里常见的 DAO 用 DriverManager.getConnection 每次现开现关,并发一高就会卡。换成 Druid 或 C3P0,配置一个数据源,DAO 里取连接改成从池里拿,代码动得不多但能作为答辩里的优化点。
第二个是分层。这套系统很多 JSP 页面直接写 Java 代码,时间一长维护很痛苦。可以先把数据库访问抽到 DAO 类里,再把公共的登录、购物车逻辑抽到 Servlet,JSP 里只留 JSTL 和标签,这样工程结构从“页面里写逻辑”升级成“MVC 分层”。如果项目是 5 个页面以上,这一步收益很明显。
第三个是注册时用 Ajax 查用户名是否重复。传统做法是提交后整个页面刷新,体验一般。用 jQuery 的$.post("checkUsername", {username:...})在输入框失焦时查库,返回可用或不可用提示,代码量不多,但能明显提升用户端的完成度,答辨时也是可以现场演示的亮点。
6.3 一个验收习惯,能帮你少走很多弯路
从那以后我拿到任何一个 JSP 课设项目,都会先建库、再登录、再跑一单全流程,三件事确认完才去改代码。这套习惯帮我快速判断一份资源到底能不能用、坑在哪里,也避免了在错误环境上浪费大量时间。数据库确认、角色分流、订单状态流转,三件事走通,这个项目你就真正吃透了。希望帮到你。
本文还有配套的精品资源,点击获取