简介:这是一套面向Java Web初学者与课程设计需求的银行排队叫号系统完整项目,采用SSM框架搭配JSP技术实现,运行于JDK1.8与Tomcat7环境,数据库使用MySQL5.7。项目涵盖取号、叫号、窗口管理与业务统计等典型银行场景模块,适合作为毕业设计、课程实训或SSM框架练手参考。压缩包共856个文件,约29.65MB,包含87个Java源文件、49个JSP页面、217个JavaScript脚本、97个CSS样式及大量png、gif、jpg图片素材,另有sql建表脚本、xml配置与properties文件,前端还集成了Bootstrap与UEditor等组件,结构完整、层次清晰。资源同时附带演示视频,方便直观了解系统运行效果与交互流程。目前已有465人学习下载,读者可据此快速搭建环境、梳理SSM整合思路、参考页面布局与业务逻辑实现,并借助源码排查常见配置问题。
1. 从一次银行网点改造说起:这套 JSP 叫号系统到底能跑通什么
去年帮一个做系统集成的朋友看现场,某农商行支行要换掉用了七八年的老叫号机,原厂报价高得离谱,功能还锁死。他手里只有一台旧服务器和一堆零散需求:取号、叫号、窗口呼叫、语音播报、数据统计。翻遍手头资源,最后落到了这套基于 JSP 的银行排队叫号系统上。它不是什么高精尖的东西,但胜在完整——SSM 框架打底,JSP 做视图,MySQL 存业务数据,Tomcat 跑服务,源码加演示视频一起给,拿过来改改就能往网点里塞。这套东西适合两类人:一是做 Java 课程设计或毕业设计的学生,需要一套能跑通、有业务逻辑、能讲清楚流程的完整项目;二是接私活或做小型集成的一线开发,想快速搭一个叫号原型,省去从零写取号排队算法的功夫。它解决的核心问题很具体:把线下排队这件事数字化,让取号、排队、叫号、统计四个环节在浏览器里闭环。别指望它扛住大流量,但一个网点几十个窗口、日均几百个号,它够用。
2. 环境选型与依赖锁定:为什么 JDK 1.8 + MySQL 5.7 不能乱换
2.1 版本组合的硬约束从哪来
这套项目标注的版本组合是 JDK 1.8、Tomcat 7、MySQL 5.7、Maven 3.3.9,开发工具给了 Eclipse、MyEclipse、IDEA 三个选项。很多人第一反应是“我本地有 JDK 17,直接上不行吗”,血泪经验告诉你不行。原因在 JSP 和 SSM 的依赖链上:项目用的 Spring 版本较老,JDK 9 之后模块化系统对反射和类加载做了限制,老版本 Spring 在启动时扫描注解会直接抛InaccessibleObjectException。Tomcat 7 对应的是 Servlet 3.0 规范,Tomcat 9 之后 JSP 编译器的行为有变化,web.xml里的一些配置会失效。MySQL 5.7 这个点更关键,项目里大概率用了utf8而不是utf8mb4,且连接串没配时区参数,换到 MySQL 8 会报Unknown system variable 'query_cache_size'或者时区错误。所以最稳的做法是:本地装一个 JDK 1.8,单独配一个 Tomcat 7,MySQL 用 5.7 的安装版而不是绿色版,Navicat 11 用来导数据。
2.2 依赖安装与项目导入的实操步骤
先确认本地 Java 环境,命令行敲:
java -version # 期望输出:java version "1.8.0_xxx" javac -version # 期望输出:javac 1.8.0_xxx如果版本不对,去配JAVA_HOME和Path,这一步网上教程很多,不展开。接着装 MySQL 5.7,安装时字符集选utf8,root 密码设简单点方便调试。装完用 Navicat 11 连上,新建一个数据库,名字按项目里 SQL 文件里的CREATE DATABASE语句来,通常是bank_queue之类。然后导入 SQL 文件,右键数据库选“运行 SQL 文件”,选中项目里的.sql,编码选utf8,执行。
Maven 依赖这块,项目根目录有pom.xml,在 IDEA 里打开后等它自动下载。如果卡在某个依赖下不来,检查 Maven 的settings.xml里镜像配的是不是阿里云。常见做法是在settings.xml的<mirrors>里加一段:
<mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>这段配置的作用是把中央仓库请求重定向到国内镜像,mirrorOf写*表示所有仓库都走这个镜像。参数上唯一要注意的是id不能和已有镜像重复,否则 Maven 会报Duplicate mirror id。
2.3 Tomcat 7 的配置与项目部署
Tomcat 7 解压后,在 IDEA 里配 Run Configuration,选 Tomcat Server,指向解压目录。部署方式选war exploded,这样改 JSP 不用重启。Application context设成/或者/bank,看项目里web.xml的display-name。启动前检查web.xml里的welcome-file-list,通常是index.jsp。启动后浏览器访问http://localhost:8080/,如果看到取号界面,说明部署成功。如果报 404,先看 Tomcat 日志里有没有ClassNotFoundException,多半是 Maven 依赖没打进WEB-INF/lib,在 IDEA 的 Project Structure 里把 Maven 依赖加到 Artifact 的WEB-INF/lib下即可。
3. 取号排队叫号的业务闭环:从 index.jsp 到窗口呼叫的代码链路
3.1 取号逻辑与号池生成
取号的核心在index.jsp提交后走的那个 Controller。常见做法是:用户点“取号”,前端发一个 POST 到/ticket/get,后端从数据库里查当前最大号,加一,插入一条新记录,返回号票信息。号池的生成有两种策略:一种是每天凌晨定时任务重置,一种是按业务类型分前缀,比如个人业务 A 开头,对公业务 B 开头。项目里大概率是前者,在 Service 层有一个@Scheduled或者手动触发的重置方法。看代码时重点看TicketService里的getNextNumber方法,它决定了并发下会不会重号。如果用的是SELECT MAX(number) FROM ticket再INSERT,高并发下会翻车,正确做法是用数据库自增或者加锁。一个典型的取号 Controller 片段:
@RequestMapping("/get") @ResponseBody public Map<String, Object> getTicket(@RequestParam String businessType) { Map<String, Object> result = new HashMap<>(); // 查询当前该类业务最大号 Integer maxNum = ticketService.getMaxNumber(businessType); int nextNum = (maxNum == null ? 0 : maxNum) + 1; Ticket ticket = new Ticket(); ticket.setNumber(nextNum); ticket.setBusinessType(businessType); ticket.setStatus("WAITING"); ticket.setCreateTime(new Date()); ticketService.insert(ticket); result.put("code", 200); result.put("number", nextNum); return result; }这段代码的逻辑是:先查当前业务类型的最大号,加一作为新号,插入数据库,返回给前端。参数businessType区分业务窗口,status标记为等待中。问题在于getMaxNumber和insert之间没有事务隔离,两个请求同时进来会拿到同一个 maxNum。改进方式是在getMaxNumber的 SQL 里加FOR UPDATE,或者直接用INSERT INTO ticket(number, business_type) SELECT IFNULL(MAX(number),0)+1, ? FROM ticket WHERE business_type = ?这种原子写法。
3.2 窗口呼叫与状态流转
窗口呼叫的逻辑在call.jsp或对应的 Controller 里。柜员登录后,点“呼叫下一位”,后端从ticket表里查status = 'WAITING'且business_type匹配的第一条记录,把status改成CALLING,同时更新window_id和call_time。前端轮询或者用 WebSocket 推送给显示屏。项目里如果是 JSP 老架构,大概率是轮询,每 3 秒发一次 AJAX 查当前叫号。状态流转是:WAITING→CALLING→DONE或MISSED。柜员点“完成”后置为DONE,点“过号”置为MISSED。这里有个坑:如果柜员点了呼叫但客户没来,过号后这个号是回到队首还是队尾?项目里通常置为MISSED不再参与排队,但有些网点要求过号后顺延三位,这个逻辑得自己改。
3.3 语音播报与显示屏对接
语音播报这块,项目里一般用前端SpeechSynthesisAPI 或者后端生成音频文件。JSP 页面里嵌一段 JavaScript:
function speak(number, windowName) { // 检查浏览器是否支持语音合成 if ('speechSynthesis' in window) { var msg = new SpeechSynthesisUtterance(); msg.text = "请" + number + "号到" + windowName + "办理业务"; msg.lang = 'zh-CN'; msg.rate = 0.9; window.speechSynthesis.speak(msg); } else { console.warn("当前浏览器不支持语音合成,请使用 Chrome"); } }这段代码用浏览器的语音合成接口播报叫号信息,rate控制语速,lang设成zh-CN保证中文发音。参数上rate建议 0.8 到 1.0,太快听不清。注意这个 API 在部分国产浏览器里被阉割了,现场部署时最好用 Chrome 或者 Edge。如果网点要求用 LED 屏,那就得走串口或者网络协议,项目里通常留了一个display.jsp做轮询展示,改造成本不高。
4. 数据库表结构与 SQL 导入:五个核心表怎么改才不崩
4.1 核心表清单与字段含义
项目 SQL 文件里通常有五张核心表,字段和用途如下:
| 表名 | 用途 | 关键字段 |
|---|---|---|
ticket | 号票记录 | id,number,business_type,status,window_id,create_time |
window | 窗口信息 | id,window_name,status,user_id |
user | 柜员账号 | id,username,password,role |
business | 业务类型 | id,type_name,prefix |
record | 叫号历史 | id,ticket_id,call_time,finish_time |
ticket表是核心,status字段的取值决定了整个系统的状态机。window表里的status标记窗口是否开放。user表存柜员登录信息,密码通常是 MD5 或者明文,如果是明文,上线前必须改。business表定义业务类型和号票前缀。record表用于统计报表。
4.2 导入 SQL 时的字符集与引擎设置
导入前先确认数据库的字符集:
SHOW VARIABLES LIKE 'character_set_database'; -- 期望输出 utf8 SHOW VARIABLES LIKE 'collation_database'; -- 期望输出 utf8_general_ci如果不是utf8,在my.ini里改character-set-server=utf8然后重启 MySQL。导入时用 Navicat 的“运行 SQL 文件”,编码选utf8,不要选自动,否则中文会变问号。表引擎默认 InnoDB,如果 SQL 文件里写了 MyISAM,改成 InnoDB,否则事务不生效。导入后检查ticket表的AUTO_INCREMENT值,如果是 0 或者 1,没问题;如果很大,说明之前有数据,清空表再重置。
4.3 改表结构的三个安全操作
第一个,加字段。比如要加一个priority字段标记优先号,用:
ALTER TABLE ticket ADD COLUMN priority INT DEFAULT 0 COMMENT '优先级,0普通,1优先';DEFAULT 0保证老数据不受影响,COMMENT方便后续维护。第二个,改字段类型。比如number从int改成varchar支持字母前缀:
ALTER TABLE ticket MODIFY COLUMN number VARCHAR(20) NOT NULL COMMENT '号票号码';改之前先备份,MODIFY会重建表,数据量大时锁表。第三个,加索引。排队查询频繁按status和business_type过滤,加联合索引:
CREATE INDEX idx_status_type ON ticket(status, business_type);索引顺序不能反,status区分度高放前面。加完用EXPLAIN看查询计划,确认走了索引。
5. 避坑与排查:这套老项目最容易翻车的五个地方
5.1 启动报 404 或 ClassNotFoundException
现象:Tomcat 启动没报错,但访问页面 404,日志里翻到ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet。原因:Maven 依赖没打进WEB-INF/lib,IDEA 的 Artifact 配置里漏了。解决:打开 Project Structure → Artifacts → 选中 war exploded → 在WEB-INF/lib下点加号 → Library Files → 全选 Maven 依赖 → 确定 → 重新部署。
5.2 中文乱码从取号到显示全链路
现象:取号页面输入中文业务类型,存到数据库变问号,或者 JSP 显示乱码。原因:三个环节的编码不一致——JSP 页面pageEncoding、数据库连接串characterEncoding、MySQL 服务端字符集。解决:JSP 头部确保<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>;JDBC 连接串加?useUnicode=true&characterEncoding=utf8;MySQL 的my.ini里character-set-server=utf8。三处都对齐后重启 Tomcat 和 MySQL。
5.3 叫号重复或跳号
现象:两个窗口同时点呼叫,叫到同一个号;或者号票从 5 直接跳到 7。原因:取号时的MAX+1并发问题,或者AUTO_INCREMENT步长被改过。解决:取号改用原子 SQL 或者加锁;检查SHOW VARIABLES LIKE 'auto_increment_increment',正常是 1,如果是 2 说明被主从配置改过,改回 1。
5.4 Tomcat 7 启动慢或内存溢出
现象:启动要两三分钟,或者跑一会儿报OutOfMemoryError: PermGen space。原因:Tomcat 7 默认 PermGen 太小,老项目类加载多。解决:在catalina.bat或catalina.sh里加 JVM 参数:
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512mPermSize是 JDK 7 及以前的永久代,JDK 8 换成了 Metaspace,但 Tomcat 7 配 JDK 8 时这两个参数仍能生效。Xmx别超过物理内存一半。
5.5 语音播报没声音
现象:叫号后显示屏更新了,但没声音。原因:浏览器自动播放策略限制,或者speechSynthesis被禁用。解决:确保页面有过用户交互(比如柜员点了按钮),Chrome 才允许语音;检查系统音量合成器里浏览器没被静音;如果是无头模式或者远程桌面,语音 API 可能不可用,换成本地音频文件播放。
6. 从能跑到好用:三个进阶改造与验证方法
第一个改造,把轮询改成 WebSocket。轮询每 3 秒一次,一个网点 20 个窗口,一天下来请求量不小。用 Spring 的WebSocketHandler推叫号消息,前端onmessage收到后更新显示和播报。改造后验证方法:开两个浏览器,一个取号一个呼叫,看另一个页面是否秒级更新。第二个改造,加一个简单的统计报表。用record表按天分组,统计每个窗口的叫号量和平均办理时长。SQL 这么写:
SELECT w.window_name, COUNT(r.id) AS total_calls, AVG(TIMESTAMPDIFF(SECOND, r.call_time, r.finish_time)) AS avg_seconds FROM record r JOIN window w ON r.window_id = w.id WHERE DATE(r.call_time) = CURDATE() GROUP BY w.window_name;TIMESTAMPDIFF算两个时间戳的秒差,AVG求平均。验证方法:手动造几条record数据,跑一下看结果对不对。第三个改造,把密码从明文改成 BCrypt。项目里user表的password如果是明文,用 Spring Security 的BCryptPasswordEncoder在登录时比对。改完记得把存量密码批量加密,写个临时接口跑一遍。从那以后我每次拿到这种老项目,都强制走一遍“先锁版本、再导数据、最后改配置”的流程,顺序反了必翻车。希望帮到你。
本文还有配套的精品资源,点击获取