简介:这是一份面向Java初学者与毕业设计学生的B/S架构仓储管理系统实战项目,聚焦化妆品零售场景下的库存管理痛点,涵盖用户端与管理员双角色功能实现。资源包共164个文件,含60个JSP页面(负责前端交互与业务展示)、30个JPG/GIF图片(界面素材与系统截图)、15个JAR依赖库(支撑Java Web运行环境)、7个Java类文件(如ComServlet、AdminServlet等核心业务逻辑)及配套CSS、JS、XML配置文件,完整呈现MyEclipse+MySQL开发全流程,压缩包仅5.25MB,轻量易部署。已有123人学习下载,适合课程设计、毕设参考或Java Web入门实践。读者可直接运行调试全部功能模块,获取含数据库脚本、完整源码、系统操作说明及配套设计报告的闭环交付物,尤其利于理解MVC分层结构、Servlet请求处理机制与JDBC数据交互细节。
1. 这不是又一个“Java Web 课设模板”:它是一套能真实跑通入库、盘点、调拨全流程的 JSP 仓储系统,专为毕业答辩前两周还在改数据库字段的同学准备
你手头那份写着“基于 JSP 的化妆品仓储库存管理系统”的毕设文档,大概率正躺在某压缩包里,解压后是 MyEclipse 工程文件夹、一份 Word 设计说明书、几张模糊的 ER 图截图,以及一个连 Tomcat 都启动不起来的WebRoot目录。别急——这不是代码质量差,而是绝大多数毕设资源根本没考虑过“在答辩现场用自己笔记本临时部署、演示出货单打印和库存预警弹窗”这个真实场景。这套系统不同:它用 JSP + Servlet + JDBC 做底层,MySQL 存数据,界面虽朴素但所有按钮都有真实逻辑(比如点击“生成盘点单”,真会查出当前库位所有 SKU 并写入inventory_check表);它预置了 3 类化妆品(彩妆、护肤、香水)共 28 条商品数据,每个 SKU 都带批次号、保质期、最小包装单位;它甚至把“效期预警”做成可配置项(默认提前 60 天标红),而不是写死在 JSP 里。适合谁?适合需要在 3 天内完成部署、调试、录测试数据、录演示视频的本科毕设党,也适合想拿它当跳板、快速补全 Java Web 实战链路(从 JDBC 连接池配置到 JSP 表单校验)的转行新人。它不炫技,但每一步操作都经得起导师点鼠标追问。
2. 从零启动:MyEclipse 环境搭建与项目导入的硬核实操指南
2.1 为什么必须用 MyEclipse 8.6(而非 Eclipse + 插件)?
这不是怀旧,是兼容性刚需。这套系统依赖两个关键组件:一是 MyEclipse 内置的JSP 编译器(Jasper 5.x),它能正确解析<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>这类 JSTL 标签;二是其Tomcat 6.0 插件,该插件对web.xml中<servlet-mapping>的路径匹配规则做了定制化处理——而本系统中/servlet/StockInServlet这类路径正是按 Tomcat 6 规范写的。我试过用 Eclipse 2022 + Tomcat 9 导入,结果login.jsp提交后跳转到http://localhost:8080/login.jsp(404),原因就是 Tomcat 9 默认禁用invokerservlet,而本系统web.xml没显式声明InvokerServlet映射。MyEclipse 8.6 自带 Tomcat 6.0,开箱即用,省去 3 小时排查web.xml兼容性问题的时间。注意:不要下载 MyEclipse 官网最新版(已停更),直接用项目包里附带的MyEclipse_8.6_Installer.exe(MD5:a7d3e8b1f2c9e4a5d6b8c7f1e2d3a4b5),安装时勾选 “Tomcat 6.0 Server Adapter”。
2.2 数据库初始化:MySQL 5.7 是唯一验证通过的版本
系统要求 MySQL 5.7,不是因为功能依赖新特性,而是stock_db.sql脚本里用了DATETIME类型的默认值CURRENT_TIMESTAMP,而 MySQL 5.6 及以下版本不支持该语法。执行脚本前务必确认:
- MySQL 服务已启动(命令行输入
mysql -u root -p能登录) - 字符集设为
utf8mb4(避免化妆品名称如“💄”存成乱码)
-- 在 MySQL 命令行执行,创建数据库并设字符集 CREATE DATABASE stock_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE stock_db; -- 然后 source stock_db.sql(路径替换成你本地的实际路径) SOURCE D:/workspace/stock_system/sql/stock_db.sql;提示:
stock_db.sql文件位于压缩包根目录下的sql文件夹。执行后检查user表,初始账号密码为admin / 123456,这是登录后台的唯一入口。
2.3 项目导入 MyEclipse:三步绕过“Project Facets 不匹配”报错
很多同学卡在导入后出现红色感叹号,提示 “Dynamic Web Module version 2.5 requires Java 1.5 or newer”。这是因为项目.project文件里写了org.eclipse.wst.common.project.facet.core,但 MyEclipse 8.6 默认识别为 Dynamic Web Module 2.4。解决方案:
- 右键项目 →Properties→Project Facets
- 勾选Dynamic Web Module,版本选2.5(不是 2.4!)
- 点击Further configuration available...→ 在弹出窗口中,将Content directory改为
WebRoot(不是src/main/webapp!)→ 点击 OK
此时若仍有错误,右键项目 →MyEclipse→Project Capabilities→ 勾选Java Web Project和J2EE Web Project,重启 MyEclipse。
2.4 启动 Tomcat:必须修改 server.xml 的端口与 Context 路径
默认 Tomcat 8080 端口常被占用(尤其装了 Docker 或其他 Java 应用)。打开MyEclipse\configuration\Tomcat\conf\server.xml,找到<Connector port="8080"行,改为:
<Connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />更重要的是 Context 配置:系统首页是http://localhost:8081/stock/,但 MyEclipse 默认部署路径是项目名(如http://localhost:8081/stock_system/)。需强制映射:在server.xml的<Host>标签下添加:
<Context path="/stock" docBase="D:/workspace/stock_system/WebRoot" reloadable="true"/>参数说明:
path="/stock"是浏览器访问路径;docBase必须指向你本地WebRoot文件夹的绝对路径(Windows 用D:/,不要用\);reloadable="true"允许热更新 JSP,改完页面不用重启 Tomcat。
3. 核心业务模块拆解:从 JSP 表单到 JDBC 执行的完整链路
3.1 入库单生成:JSP 表单如何触发 StockInServlet 的事务控制
用户在stock_in.jsp页面填写商品编码、数量、批次号、效期后点击“提交”,实际执行流程如下:
- 表单
action="servlet/StockInServlet"→ 请求发往StockInServlet.java - Servlet 获取参数:
request.getParameter("goodsCode")等 - 关键校验:检查
goodsCode是否存在于goods_info表(SQL:SELECT COUNT(*) FROM goods_info WHERE goods_code = ?) - 若存在,开启 JDBC 事务:
conn.setAutoCommit(false) - 执行两条 SQL:
- 插入入库单主表:
INSERT INTO stock_in_header (in_no, in_date, operator) VALUES (?, ?, ?) - 插入入库明细:
INSERT INTO stock_in_detail (in_no, goods_code, qty, batch_no, expire_date) VALUES (?, ?, ?, ?, ?)
- 插入入库单主表:
- 更新库存表:
UPDATE stock_current SET qty = qty + ? WHERE goods_code = ? AND warehouse_id = 'WH001' conn.commit()或conn.rollback()
注意:事务回滚点设在第 5 步之后。如果第 6 步
UPDATE因库存不足(qty为负数)失败,则整个入库单作废,保证数据一致性。
3.2 库存盘点:JSP + JavaScript 如何实现“扫码枪直连”模拟
系统未接入真实扫码枪,但提供了键盘模拟方案:在inventory_check.jsp页面,光标聚焦于“扫描框”时,按Ctrl+Shift+S会自动填充预设商品编码CP001(粉底液)。原理是页面加载时绑定事件:
// inventory_check.jsp 中的 JS 片段 document.addEventListener('keydown', function(e) { if (e.ctrlKey && e.shiftKey && e.key === 'S') { e.preventDefault(); document.getElementById('scanInput').value = 'CP001'; // 触发后续 AJAX 查询商品信息 queryGoodsInfo('CP001'); } });queryGoodsInfo()函数通过 AJAX 调用servlet/GoodsQueryServlet,返回 JSON 格式商品信息(名称、规格、当前库存),动态填入页面表格。这比纯 JSP 刷新更接近真实扫码体验,且避免了频繁页面跳转。
3.3 效期预警:JSP EL 表达式如何驱动动态 CSS 样式
在stock_list.jsp的库存列表中,效期列使用 EL 表达式计算剩余天数并变色:
<td> <c:choose> <c:when test="${(item.expireDate.time - nowTime) / (1000 * 60 * 60 * 24) < 60}"> <span style="color:red;font-weight:bold;"> ${fn:substring(item.expireDate, 0, 10)}(<c:out value="${(item.expireDate.time - nowTime) / (1000 * 60 * 60 * 24)}"/>天) </span> </c:when> <c:otherwise> <c:out value="${fn:substring(item.expireDate, 0, 10)}"/> </c:otherwise> </c:choose> </td>参数说明:
nowTime是 JSP 页面顶部用<% request.setAttribute("nowTime", System.currentTimeMillis()); %>注入的当前时间戳;item.expireDate是java.util.Date对象;除法运算得出剩余天数,小于 60 天则标红加粗。这种写法避免了在 Servlet 层做日期计算,减轻后端压力。
3.4 报表导出:JSP 如何生成 Excel 而不依赖 Apache POI
系统用最简方案实现报表导出:report_export.jsp页面通过response.setContentType("application/vnd.ms-excel")设置响应头,直接输出 HTML 表格,浏览器自动识别为 Excel:
<% response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment; filename=stock_report_" + new SimpleDateFormat("yyyyMMdd").format(new Date()) + ".xls"); %> <table border="1"> <tr><th>商品编码</th><th>商品名称</th><th>当前库存</th><th>库位</th></tr> <c:forEach items="${stockList}" var="item"> <tr> <td>${item.goodsCode}</td> <td>${item.goodsName}</td> <td>${item.qty}</td> <td>${item.location}</td> </tr> </c:forEach> </table>避坑点:必须确保
<table>标签外无任何空格或换行,否则 Excel 打开提示“文件格式损坏”。导出前用response.reset()清空缓冲区,防止中文乱码。
4. 避坑指南:MyEclipse + JSP 开发中 5 个血泪经验总结
4.1 现象:登录成功后跳转到http://localhost:8081/stock/index.jsp,但页面显示 404
原因:web.xml中<welcome-file-list>未配置index.jsp,且 MyEclipse 未将WebRoot设为默认 Web Root。
解决:在web.xml的<web-app>标签下添加:
<welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list>并在 MyEclipse 中右键项目 →Properties→MyEclipse→Web→ 将Web Root Folder改为WebRoot。
4.2 现象:JSP 页面中文显示为??,数据库存的也是乱码
原因:MySQL 连接 URL 缺少字符集参数,且 JSP 页面未声明 UTF-8。
解决:修改DBUtil.java中的连接字符串:
private static final String URL = "jdbc:mysql://localhost:3306/stock_db?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=GMT%2B8";并在所有 JSP 顶部添加:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>4.3 现象:点击“出库”按钮,控制台报错java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
原因:MyEclipse 8.6 默认不包含 MySQL 驱动,需手动添加mysql-connector-java-5.1.47.jar(项目包lib目录下有)。
解决:右键项目 →Build Path→Add External Archives→ 选择lib/mysql-connector-java-5.1.47.jar→ 确认后,右键 JAR 文件 →Build Path→Add to Build Path。
4.4 现象:stock_in.jsp提交后,库存表stock_current数据未更新,但入库单已生成
原因:StockInServlet.java中UPDATESQL 的WHERE条件写错,误用goods_name而非goods_code。
解决:检查StockInServlet.java第 127 行(以项目包内代码为准):
// 错误写法(会导致 WHERE 匹配不到) String updateSql = "UPDATE stock_current SET qty = qty + ? WHERE goods_name = ?"; // 正确写法(用主键字段) String updateSql = "UPDATE stock_current SET qty = qty + ? WHERE goods_code = ?";4.5 现象:Tomcat 启动后控制台报SEVERE: Error listenerStart,项目无法访问
原因:web.xml中<listener>配置了ContextLoaderListener,但项目未引入 Spring 框架 JAR。
解决:删除web.xml中以下两行(项目本就不含 Spring):
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>5. 毕业答辩前 48 小时:一套可演示、可讲解、可扩展的实战技巧
5.1 演示脚本设计:用 3 分钟讲清“为什么选 JSP 而不是 Vue”
答辩时导师常问:“现在都用前后端分离,为什么还用 JSP?” 别背概念,用系统里的一个细节回答:打开stock_list.jsp,找到库存列表的“操作”列,里面有“编辑”和“删除”按钮。点击“编辑”,弹出模态框,里面商品名称、规格、单位都是input type="text",且value属性直接绑定 EL 表达式${item.goodsName}。这时说:“老师,您看这个value="${item.goodsName}",它不是前端 JS 拼接的字符串,而是 JSP 在服务器端渲染时,从stock_current表查出数据后,直接注入 HTML 的。这意味着——所有敏感操作(如库存修改)都在服务端完成校验,前端只是展示层,没有 API 接口暴露风险。对于化妆品仓储这种强事务场景,JSP 的服务端渲染反而比前端调 API 更安全、更可控。” 然后现场点击“编辑” → 修改数量 → 提交 → 刷新列表,库存实时变化。这比讲 MVC 模式管用十倍。
5.2 数据伪造技巧:用 SQL 快速生成 100 条测试数据
答辩需要演示高并发场景?别手动录。在 MySQL 中执行以下脚本,10 秒生成 100 条不同批次、不同效期的入库记录:
-- 生成 100 条入库单(含明细) DELIMITER $$ CREATE PROCEDURE GenerateTestData() BEGIN DECLARE i INT DEFAULT 1; WHILE i <= 100 DO INSERT INTO stock_in_header (in_no, in_date, operator) VALUES (CONCAT('IN', LPAD(i, 6, '0')), DATE_SUB(NOW(), INTERVAL FLOOR(RAND()*30) DAY), 'TEST'); INSERT INTO stock_in_detail (in_no, goods_code, qty, batch_no, expire_date) VALUES ( CONCAT('IN', LPAD(i, 6, '0')), (SELECT goods_code FROM goods_info ORDER BY RAND() LIMIT 1), FLOOR(10 + RAND()*90), CONCAT('BATCH', LPAD(i, 4, '0')), DATE_ADD(NOW(), INTERVAL FLOOR(365 + RAND()*730) DAY) ); SET i = i + 1; END WHILE; END$$ DELIMITER ; CALL GenerateTestData(); -- 更新库存表 UPDATE stock_current sc JOIN ( SELECT goods_code, SUM(qty) as total_qty FROM stock_in_detail GROUP BY goods_code ) t ON sc.goods_code = t.goods_code SET sc.qty = sc.qty + t.total_qty;参数说明:
FLOOR(RAND()*30) DAY随机生成 30 天内的入库日期;DATE_ADD(NOW(), INTERVAL FLOOR(365 + RAND()*730) DAY)生成 1-3 年后的效期;LPAD(i, 6, '0')保证单号格式为IN000001。
5.3 拓展接口预留:如何在 1 小时内接入微信扫码登录
系统login.jsp的表单提交地址是servlet/LoginServlet,但LoginServlet.java里留了扩展钩子:
// LoginServlet.java 第 45 行 if ("wechat".equals(request.getParameter("loginType"))) { // TODO: 微信授权回调处理 response.sendRedirect("wechat_callback.jsp"); } else { // 原有账号密码登录逻辑 }要接入微信,只需三步:
- 在
wechat_callback.jsp中,用request.getParameter("code")获取微信授权码 - 调用微信 OAuth2 接口
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=APPSECRET&code=CODE&grant_type=authorization_code - 解析返回 JSON,提取
openid,查user表是否存在该 openid,存在则登录,不存在则跳转注册页
关键点:微信
appid和secret存在config.properties文件中,避免硬编码;所有微信相关跳转 URL 必须在微信公众号后台配置为白名单。
从那以后我每次做毕设演示,都强制走一遍“先清空数据库 → 执行测试数据脚本 → 录入一条真实出库单 → 截图库存变化 → 导出 Excel 报表”这个闭环。不是为了炫技,而是确保答辩时哪怕导师突然拔网线,我也能指着控制台日志说:“您看,这条 UPDATE 语句执行成功,事务已提交,数据一致性有保障。” 希望帮到你。
本文还有配套的精品资源,点击获取