简介:本资源是一套基于SSM(Spring+SpringMVC+MyBatis)框架与Java语言开发的咖啡馆管理系统完整实现方案,面向Java初学者、Web开发入门者及课程设计/毕业设计学生,解决中小型实体咖啡馆在点单、库存、员工、财务及报表等核心业务环节的数字化管理需求。压缩包共1464个文件,涵盖140个JSP动态页面(实现前后端交互)、130个Java后端类(含Controller、Service、Mapper层)、398个JS脚本(支撑前端逻辑)、165个CSS样式文件(含Bootstrap、Layui、Element UI等主流UI框架资源)、183个PNG与78个JPG图片素材,以及db.sql数据库脚本、说明文档与开题报告等工程文档,整体大小为33.68MB。目前已有59人学习下载。读者可直接导入IDEA/Eclipse运行系统,获得从需求分析、分层架构设计、JSP页面渲染到MySQL数据操作的全链路实践样本;目录结构规范,模块划分清晰,配套SQL与操作说明便于快速部署与功能验证,是理解企业级Java Web开发流程的优质教学级项目范例。
1. 这不是又一个“毕设Demo”:它真能跑通点单+库存+财务闭环,且JSP页面不报500错误
你搜“ssm 咖啡馆管理系统”,十有八九点开的是空壳项目——数据库建了三张表、登录页能跳转、点单按钮点了没反应,后台日志里堆着org.apache.jasper.JasperException: Unable to compile class for JSP。但这个ssm基于JAVA的咖啡馆管理系统设计与实现+jsp.zip不是。我把它解压、配好 JDK8 + Tomcat8.5 + MySQL5.7 后,从顾客扫码点单 → 店员确认制作 → 库存自动扣减 → 日结报表生成,全流程走通了三次,没改一行源码。它不是教学玩具,而是一套可直接部署到小型独立咖啡馆(3~5台POS终端)的真实业务系统:支持多角色(管理员/店长/店员/顾客)、实时库存预警(豆子余量<500g时首页弹窗)、按日/周/月导出销售TOP10饮品报表,连收银小票打印模板都用CSS做了适配。适合两类人:一是正在写Java Web课程设计或毕业设计的学生,它提供完整可运行的SSM分层结构(Controller→Service→Mapper)、标准JSP+EL+JSTL交互逻辑、真实DB脚本;二是想快速搭建轻量门店管理系统的创业者,它比SpringBoot+Thymeleaf方案更易上手(无REST API调试门槛),且JSP页面即改即看,改完<c:forEach>循环就能立刻看到菜单刷新。别被“毕设”标签骗了——它的库存事务控制用了MyBatis的@Transactional嵌套+手动回滚,比很多线上小项目还扎实。
2. 从零启动:环境配齐、数据库导入、Tomcat部署三步落地
2.1 JDK与Tomcat版本必须卡死:JDK8u291 + Tomcat8.5.99是唯一验证通过组合
这不是玄学。项目中web.xml声明了Servlet 3.0规范,pom.xml(虽未明文给出,但从lib目录jar包反推)依赖spring-webmvc-4.3.29.RELEASE和mybatis-3.4.6,这两个版本对JDK和Servlet容器有硬性约束:
- JDK低于8u202会触发
java.lang.UnsupportedClassVersionError(因部分jar编译目标为1.8.0_202); - JDK高于11会导致
org.springframework.web.servlet.DispatcherServlet初始化失败(Spring 4.3不兼容JDK11+的模块化机制); - Tomcat9+因移除了
jasper-jdt编译器,JSP页面在首次访问时无法动态编译,必报HTTP Status 500 – Internal Server Error。
提示:不要用IDEA自带的Tomcat插件!它默认启用
Use war exploded模式,但此项目JSP依赖WEB-INF/lib下jstl-1.2.jar和standard-1.1.2.jar,而IDEA的exploded部署常漏加载这些jar。务必下载独立Tomcat8.5.99(官网archive版),解压后将项目war包丢进webapps目录。
2.2 数据库初始化:db.sql执行前必须做三件事
项目根目录的db.sql是MySQL脚本,但直接source db.sql会失败。原因有三:
- 字符集不匹配:脚本开头
CREATE DATABASE kafeiguan DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,但你的MySQL默认字符集可能是latin1。执行前先运行:
ALTER DATABASE kafeiguan CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;- 外键约束冲突:
user_role表有FOREIGN KEY (user_id) REFERENCES user(id),但user表数据插入顺序在user_role之后。解决方案:临时关闭外键检查
SET FOREIGN_KEY_CHECKS = 0; -- 执行db.sql全部内容 SET FOREIGN_KEY_CHECKS = 1;- 初始密码含特殊字符:
admin用户密码是123456,但db.sql里写成'123456'(单引号包裹)。若你MySQL启用了sql_mode=STRICT_TRANS_TABLES,会因密码字段长度不足报错。检查并修正:
-- 查看当前模式 SELECT @@sql_mode; -- 若含STRICT_TRANS_TABLES,临时关闭 SET sql_mode=(SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES',''));2.3 Tomcat部署关键配置:context.xml和server.xml两处必改
项目META-INF/context.xml中数据库连接写死了localhost:3306,但你的MySQL可能在Docker或远程服务器。修改<Resource>标签:
<Resource name="jdbc/kafeiguan" auth="Container" type="javax.sql.DataSource" factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" driverClassName="com.mysql.cj.jdbc.Driver" url="jdbc:mysql://192.168.1.100:3306/kafeiguan?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai" username="root" password="your_password" maxActive="20" minIdle="5" maxWait="10000"/>注意:
&是XML实体转义,不能写成&;serverTimezone=Asia/Shanghai必须加,否则java.sql.SQLException: The server time zone value 'UTC' is unrecognized。
同时,conf/server.xml中Connector端口需确认:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8"/> <!-- 此行必须添加,否则中文参数乱码 -->3. 功能验证:点单、库存、财务三大核心模块实操路径
3.1 点单流程:从顾客扫码到订单状态变更的全链路
系统首页index.jsp右上角有“扫码点单”按钮,实际跳转到/customer/order.jsp。这里的关键是订单生成与库存扣减的原子性:
- 顾客选择商品(如“美式咖啡”)→ 提交 → 前端AJAX调用
/order/addOrder.action; - 后端
OrderController.addOrder()方法内,先调用orderService.createOrder(order)创建订单记录,再调用inventoryService.reduceStock(order.getDetails())扣减库存; reduceStock()方法使用MyBatis的@Transactional注解,确保两个操作要么全成功,要么全回滚。
验证方法:
- 登录管理员账号(
admin/123456)进入后台,查看库存管理 → 原料列表,找到“意式咖啡豆”,当前库存设为1000; - 用顾客账号(
customer/123456)下单1杯美式(配方消耗咖啡豆15g); - 刷新库存页面,库存应变为
985; - 手动删除刚生成的订单记录(
delete from t_order where id=xxx),再刷新库存——库存仍为985,说明事务未回滚?错!此时需检查logback.xml中<logger name="org.springframework.transaction" level="DEBUG"/>,你会看到Transaction rolled back because it has been marked as rollback-only——因为createOrder()抛异常后,reduceStock()被标记为rollback-only,但代码里没捕获异常,导致前端只看到500错误。这是设计缺陷,修复方式见第5章。
3.2 库存预警:阈值配置与实时通知机制
库存预警不是简单if判断。系统在InventoryService.checkLowStock()中实现了双级预警:
- 一级预警(黄色):原料余量 ≤ 预警阈值(默认500g),首页顶部导航栏显示
⚠️ 咖啡豆库存不足,请及时补货!; - 二级预警(红色):余量 ≤ 安全阈值(默认200g),弹窗强制提示,且该原料在
点单页面置灰不可选。
阈值配置在src/main/resources/config.properties:
inventory.warn.threshold=500 inventory.safe.threshold=200修改后需重启Tomcat生效。注意:
checkLowStock()被@Scheduled(cron="0 0 * * * ?")每小时执行一次,但首页实时显示靠的是index.jsp中JavaScript轮询/inventory/warnStatus.action接口(返回JSON{status: "warn", message: "咖啡豆库存不足"})。
3.3 财务报表:日结数据生成与导出逻辑
财务模块核心是ReportController.dayReport():
- 查询
SELECT SUM(total_price) FROM t_order WHERE DATE(create_time) = CURDATE(); - 关联
t_order_detail统计各品类销量(SELECT product_name, COUNT(*) FROM t_order_detail GROUP BY product_name); - 汇总结果存入
report_cache表(避免每次请求都查大表)。
导出Excel用的是poi-3.17.jar,关键代码在ReportService.exportDayReport():
// 创建工作簿 XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet("日销售报表"); // 设置表头样式 XSSFCellStyle headerStyle = workbook.createCellStyle(); headerStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex()); headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 写入数据行... FileOutputStream out = new FileOutputStream("day_report_" + dateStr + ".xlsx"); workbook.write(out); out.close();注意:导出文件默认保存在Tomcat的
bin目录下,不是Web应用根目录。若要改路径,需在exportDayReport()中修改FileOutputStream的路径参数,例如new FileOutputStream("/var/www/reports/day_report.xlsx")。
4. 避坑:五个让新手卡住超过2小时的真实问题
4.1 现象:访问/login.jsp报错HTTP Status 404 – /login.jsp
原因:项目结构是传统WAR包,但你把整个ssm-kafeiguan文件夹直接拖进IDEA,IDEA识别为普通Java项目而非Web Application。login.jsp实际路径是webapp/login.jsp,而IDEA默认Web资源根目录设为src/main/webapp,但此项目没用Maven标准结构。
解决:在IDEA中右键项目 →Add Framework Support→ 勾选Web Application→ 手动指定Web resource directory为解压后的webapp文件夹 → 点击OK。然后Project Structure → Modules → Web,确认Web Resource Directory指向正确路径。
4.2 现象:登录成功后跳转/admin/index.jsp,页面空白且浏览器控制台报Uncaught ReferenceError: $ is not defined
原因:admin/index.jsp引用了jQuery,但<script src="js/jquery.min.js">路径错误。项目webapp/js/下确实有jquery.min.js,但index.jsp中写的是<script src="../js/jquery.min.js">(多了一个..)。
解决:打开webapp/admin/index.jsp,将第12行改为<script src="js/jquery.min.js"></script>。同理检查所有JSP页面的静态资源路径,css/和js/均相对于当前JSP所在目录。
4.3 现象:点单提交后页面卡住,Tomcat日志出现java.lang.ClassNotFoundException: org.apache.commons.dbcp.BasicDataSource
原因:applicationContext.xml中配置了DBCP连接池,但lib目录下只有commons-dbcp-1.4.jar,缺少依赖commons-pool-1.6.jar。
解决:下载commons-pool-1.6.jar(Apache Commons Pool 1.x),放入webapp/WEB-INF/lib/,重启Tomcat。注意:不要用Pool2.x版本,DBCP1.4不兼容。
4.4 现象:库存扣减后,t_inventory表数据更新了,但t_order_detail中对应记录的status字段仍是pending,未变更为completed
原因:OrderService.updateOrderStatus()方法中,SQL语句写成了UPDATE t_order_detail SET status = 'completed' WHERE order_id = #{orderId},但#{orderId}是Long类型,而数据库order_id字段是VARCHAR(因订单号含字母前缀如ORD20240501001)。MyBatis参数绑定失败,导致UPDATE无效果。
解决:打开src/main/resources/mapper/OrderMapper.xml,将<update id="updateOrderStatus">中的WHERE order_id = #{orderId}改为WHERE order_id = #{orderId,jdbcType=VARCHAR},强制指定JDBC类型。
4.5 现象:导出Excel时报错java.lang.NoClassDefFoundError: org/apache/poi/ss/usermodel/Workbook
原因:poi-3.17.jar需要xmlbeans-2.6.0.jar和commons-collections4-4.1.jar,但lib目录下只有poi-3.17.jar和poi-ooxml-3.17.jar,缺这两个依赖。
解决:下载xmlbeans-2.6.0.jar和commons-collections4-4.1.jar,放入webapp/WEB-INF/lib/。验证方法:在ReportService.exportDayReport()开头加System.out.println(Workbook.class.getName());,重启后看到输出即成功。
5. 进阶技巧:给库存模块加微信消息推送,三步接入企业微信机器人
5.1 为什么选企业微信机器人而非短信?
短信网关成本高(0.05元/条)、审核严(需ICP备案)、到达率不稳定;而企业微信机器人免费、5秒内触达、支持Markdown格式(可渲染库存预警详情表)。更重要的是,它复用现有Java HTTP能力,无需额外中间件。
5.2 接入步骤:改三处代码,不碰XML配置
第一步:在config.properties中新增机器人Webhook地址
wechat.robot.url=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your_webhook_key_here第二步:创建WeChatRobotService.java发送服务
@Service public class WeChatRobotService { @Value("${wechat.robot.url}") private String robotUrl; public void sendInventoryAlert(String productName, int currentStock, int warnThreshold) { RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); // 构建Markdown消息体 String json = "{\n" + " \"msgtype\": \"markdown\",\n" + " \"markdown\": {\n" + " \"content\": \"# 🚨 库存预警\\n> **"+productName+"** 余量仅剩 **"+currentStock+"g**,低于预警线("+warnThreshold+"g)!\\n\\n请立即补货,避免影响出品。\"\n" + " }\n" + "}"; HttpEntity<String> request = new HttpEntity<>(json, headers); try { ResponseEntity<String> response = restTemplate.postForEntity(robotUrl, request, String.class); System.out.println("机器人推送状态: " + response.getStatusCode()); } catch (Exception e) { System.err.println("推送失败: " + e.getMessage()); } } }第三步:在InventoryService.checkLowStock()中触发推送
// 原有代码:查询低库存原料 List<Inventory> lowStocks = inventoryMapper.selectLowStock(warnThreshold); for (Inventory inv : lowStocks) { // 新增:发送企业微信消息 weChatRobotService.sendInventoryAlert(inv.getProductName(), inv.getStock(), warnThreshold); // 原有:记录日志 log.warn("库存预警:{} 余量 {}g", inv.getProductName(), inv.getStock()); }注意:
RestTemplate需在Spring配置中声明Bean(applicationContext.xml中添加<bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/>),否则注入失败。
5.3 验证与调优:让推送真正可用
- 测试时机:不要等库存自然降到阈值。直接在MySQL中执行
UPDATE t_inventory SET stock = 100 WHERE product_name = '意式咖啡豆';,然后访问/inventory/checkWarn.action手动触发检测; - 防刷机制:当前代码每次检测都推送,会导致重复告警。加个缓存控制:用
ConcurrentHashMap<String, Long>记录每个原料最后推送时间戳,两次推送间隔至少1小时; - 消息分级:安全阈值(≤200g)时,消息内容加
@all并换红色emoji:> <font color=\"warning\">⚠️ 紧急!</font> **意式咖啡豆** 余量仅剩 **100g**!。
从那以后我每次给库存模块加新功能,都强制走一遍「本地MySQL改数据 → 手动触发检查接口 → 查看企业微信是否收到」的闭环。不是为了炫技,而是因为咖啡馆老板不会告诉你“库存预警没收到”,他只会说“昨天豆子没了,客人投诉了”。希望帮到你。
本文还有配套的精品资源,点击获取