ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于Java的实时评分系统毕设:从WebSocket到数据库设计全解析

2026/9/23 19:11:00 拓冰建站 浏览量
基于Java的实时评分系统毕设:从WebSocket到数据库设计全解析 简介面向赛事评分场景的Java实时评分系统毕业设计项目针对传统手写评分、人工计分慢且易错的问题利用大屏展示、手机扫码与实时计算提供一套从评分到结果展示的完整方案。压缩包内共61个文件体积仅138KB包含48个Java源码文件覆盖实体类、控制器、服务层与Mapper接口另有6个XML文件提供MyBatis映射与相关配置1个SQL脚本用于初始化数据库以及YML配置文件和Markdown项目说明文档目录分层清晰便于按模块查阅。目前已有354人学习下载适合Java方向毕业设计、课程设计或作为SpringBoot集成MyBatis、Redis的实战练习。项目基于JDK1.8、SpringBoot2.3.7、MyBatis、Redis等技术栈源码可直接运行数据库脚本与说明文档齐全能帮助读者理解实时评分中评委端扫码、服务端计分、大屏同步展示的关键逻辑并快速复用为其他实时展示类系统框架是一份结构完整、上手门槛低的毕设项目参考资料。1. 基于Java的实时评分系统毕设项目最容易被低估的三件事如果只把“基于java开发的实时评分系统源码sql数据库项目说明文档”当成一个普通的课程作业压缩包那就低估了这类项目在面试和答辩里的分量。实时评分系统的核心不是CRUD而是“实时”两个字——评分数据从产生到展示延迟必须压到秒级甚至毫秒级这背后牵扯到WebSocket推送、并发写入、数据库事务边界和缓存策略。对于Java方向的毕业生来说这是少数能同时展示后端基本功和中间件认知的选题。这类项目的典型场景是课堂互动评分、比赛打分、活动投票数据特征是小而高频单次写入量不大但写入频率高读多写多且实时性要求强。很多初学者把评分做成了“提交后刷新页面看结果”这不算实时只能算“事后查询”。真正能拿得出手的实时评分系统至少要解决三个问题评分事件如何低延迟推送到大屏或客户端高并发下评分数据如何不丢不重以及数据库结构怎么设计才能既支持实时统计又支持后期回溯分析。这篇博文就顺着这三个问题展开从技术选型讲到数据库建表从WebSocket推送讲到项目说明文档的写作套路最后给出演示技巧和答辩准备。整条链路打通之后你会发现这个毕设项目不是“做完就扔”而是能录入简历、能聊出深度的作品集项目。2. 技术选型与项目骨架为什么实时评分系统首选Spring Boot前后端分离2.1 技术栈的组合逻辑Spring Boot MySQL WebSocket足够覆盖九成需求实时评分系统的技术选型不需要追新但要有明确的选择理由。后端框架推荐Spring Boot原因很直接自动配置减少繁琐的XML配置内嵌Tomcat让部署变简单而且Spring家族的WebSocket支持非常成熟。持久层用MyBatis-Plus或Spring Data JPA都可以前者写SQL更直观适合需要手动调优评分统计场景的毕设项目后者开发速度快但复杂统计查询要写JPQL或原生SQL略绕。数据库用MySQLInnoDB引擎支持行级锁和事务能应对评分场景的并发写。如果觉得单机MySQL不够可以引入Redis做评分计数缓存但这属于加分项不是必选项。前端骨架建议用Vue 3 Element Plus或者更轻量的原生HTML WebSocket客户端。毕设答辩时答辩老师更关心数据怎么流转而不是前端用了多炫酷的框架。常见做法是前后端分离Vue项目负责页面展示和WebSocket连接Spring Boot提供REST API和WebSocket端点。项目结构 rating-system/ ├── pom.xml ├── src/main/java/com/example/rating/ │ ├── controller/ # REST接口 页面跳转 │ ├── service/ # 业务逻辑评分事务边界 │ ├── mapper/ # MyBatis-Plus的Mapper接口 │ ├── model/ # 实体类对应数据库表 │ ├── config/ # WebSocket、跨域等配置 │ └── websocket/ # WebSocket端点和处理逻辑 ├── src/main/resources/ │ ├── application.yml # 数据源、端口配置 │ └── mapper/ # XML形式的SQL语句 └── sql/ # 数据库建表脚本和初始化数据这个结构的好处是职责清晰mapper层和service层分离答辩时能清楚说明每一层的职责。Controller只做参数接收和响应封装Service层处理评分业务规则Mapper层跟数据库打交道。WebSocket端点是独立包不跟REST接口混在一起代码可读性更高。2.2 三个必调参数数据库连接池、WebSocket缓冲、事务超时很多毕设项目在本地跑得好好的一放到演示环境就卡死或报错多半是默认参数没调。三个地方是必须动手改的数据库连接池的初始大小和最大大小WebSocket的sendBufferSize和sessionLimit以及事务的超时时间。# application.yml 核心配置 spring: datasource: url: jdbc:mysql://localhost:3306/rating_system?useSSLfalseserverTimezoneAsia/ShanghairewriteBatchedStatementstrue username: root password: 123456 hikari: minimum-idle: 5 maximum-pool-size: 20 connection-timeout: 5000 servlet: multipart: max-file-size: 10MB连接池用HikariCP是默认的不用换。maximum-pool-size设20是因为评分并发量通常不大过大反而浪费数据库连接资源。rewriteBatchedStatementstrue是批量写入的优化参数如果成绩单导入或多条评分批量提交这个参数能提升明显性能。WebSocket配置稍微冷门但很关键Configuration public class WebSocketConfig implements WebSocketConfigurer { Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new ScoreWebSocketHandler(), /ws/score) .addInterceptors(new ScoreHandshakeInterceptor()) .setAllowedOrigins(*); } }setAllowedOrigins(*)在开发阶段方便调试但上线前必须收紧否则任意跨域请求都能建立WebSocket连接这是安全检查项。生产环境改成具体的前端域名即可。事务超时要用Transactional(timeout 3)显式声明。评分写入逻辑是“先更新评分记录再更新统计表”两步操作必须在一个事务里默认的无限超时在数据库死锁时会把线程堵死加上timeout等于提前释放连接。3. 数据库设计评分系统的SQL表结构与实时统计的字段取舍3.1 三张核心表的设计思路评分记录表、评分项表、实时统计表先看一眼数据库表结构会设计成什么样。实时评分系统的库名定成rating_system下面至少三张表score_record存每一条评分记录rating_item存被评分的对象比如选手、老师、课程rating_statistics存每个评分项的实时汇总数据。-- 评分记录表 CREATE TABLE score_record ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, rating_item_id BIGINT NOT NULL COMMENT 评分项ID, user_id BIGINT NOT NULL COMMENT 评分人ID, score TINYINT NOT NULL COMMENT 评分值1-10, remark VARCHAR(255) DEFAULT NULL COMMENT 评语, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 评分时间, PRIMARY KEY (id), KEY idx_rating_item_id (rating_item_id), KEY idx_create_time (create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT评分明细表;score用TINYINT而不是INT因为评分值基本是1-10的整数TINYINT只占1字节索引和存储都更省。create_time加索引很关键统计“某时间段内的平均分”和“最近10条评分”都需要按时间排序没有这个索引全表扫描会拖慢接口响应。-- 实时统计表 CREATE TABLE rating_statistics ( rating_item_id BIGINT NOT NULL COMMENT 评分项ID主键, total_score DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT 总得分, score_count INT NOT NULL DEFAULT 0 COMMENT 评分总次数, avg_score DECIMAL(4,2) NOT NULL DEFAULT 0 COMMENT 平均分, rating_rank INT DEFAULT NULL COMMENT 排名, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (rating_item_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT评分实时统计表;这张统计表的rating_item_id直接当主键因为每个评分项只对应一行统计数据。avg_score用DECIMAL(4,2)而不是FLOAT避免浮点精度问题导致平均分显示成9.999。ON UPDATE CURRENT_TIMESTAMP让更新时间自动刷新省去一条更新代码。3.2 统计SQL怎么写才能扛住高频率评分写入实时评分系统的统计查询如果每次都SELECT AVG(score) FROM score_record WHERE rating_item_id?数据量一旦上万就会明显变慢。常见做法是“写时统计”每次评分写入时同步更新统计表的total_score和score_count平均分由这两个字段计算得出。-- 写入评分并更新统计事务包裹 BEGIN; INSERT INTO score_record (rating_item_id, user_id, score, remark) VALUES (1, 1001, 9, 表现优秀); UPDATE rating_statistics SET total_score total_score 9, score_count score_count 1, avg_score total_score / score_count WHERE rating_item_id 1; COMMIT;这个方案的核心是事务保证两条SQL要么同时成功要么同时失败。用一条UPDATE替代每次查询才能算出平均值性能提升显著。端到端全流程Java代码里先insert后update同一个事务内执行然后用WebSocket把最新的avg_score推送出去。并发写入不存在脏数据问题因为InnoDB的行锁会串行化对同一评分项的更新。如果评分量实在太大一个评分项每秒几百次写入行锁竞争也会成为瓶颈可以引入Redis把统计计数做在内存里批量落库。毕设阶段不推荐这么做复杂度增加翻倍但可以在项目说明文档里作为“扩展展望”提到这是加分写法。3.3 初始化SQL脚本的编写规范数据要能落地演示SQL脚本不能只建表要有基础数据否则答辩演示时界面空白什么都没有。至少要准备10到15条评分数据、3到5个评分项比如“选手A、选手B、选手C”加上每次评分的用户ID。组织这类演示数据的示例INSERT INTO rating_item (id, item_name, description) VALUES (1, 创新创意, 评价项目的创新性), (2, 技术难度, 评价技术实现复杂度), (3, 现场表现, 评价答辩表现);初始化数据尽量模拟真实场景评分值分布在7到10之间别全是10否则统计图没有梯度视觉上不真实。评分记录数据的create_time要错开比如间隔几十秒到几分钟这样时间轴图表才能画出曲线。项目说明文档里可以放一行命令说明如何导入初始化 SQLmysql -u root -p rating_system sql/init_data.sql参数说明-u root是用户名的指定方式-p会提示交互式输入密码rating_system是目标数据库名重定向让命令行工具读取SQL脚本文件。Java端每次启动时还可以用spring.sql.init.modealways自动执行新增的SQL脚本在application.yml里配置即可方便重置演示环境。4. 实时推送的核心实现从轮询到WebSocket的评分展示演进4.1 为什么轮询不是实时评分系统的可靠方案很多人一上来写前端定时器每2秒调用一次REST接口拿最新评分数据。这叫轮询不叫实时推送。轮询的问题在于延迟不可控2秒轮询意味着评分后最多要等2秒才显示资源浪费没有新数据时请求照样打满服务端高并发下数据库压力大每条轮询请求都要查一次统计表。WebSocket的模型完全不一样客户端建立一次TCP连接后保持打开服务端有数据变化时主动推给客户端。评分发生时延迟在几十毫秒级别浏览器页面感知不到等待。这背后的原理是HTTP是“一问一答”的请求响应模型而WebSocket是“长连接 双工通信”的传输协议。建立连接时需要一次HTTP升级握手之后数据帧直接走TCP通道省去了大量HTTP头部的重复传输开销。4.2 Spring Boot后端WebSocket端点的完整代码与逻辑说明后端实现一个WebSocket端点接收评分事件后向所有会话广播最新统计结果。Component public class ScoreWebSocketHandler extends TextWebSocketHandler { private static final CopyOnWriteArrayListWebSocketSession sessions new CopyOnWriteArrayList(); private final RatingStatisticsService statsService; public ScoreWebSocketHandler(RatingStatisticsService statsService) { this.statsService statsService; } /** * 连接建立后先把当前评分数据推给新连接的前端 */ Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.add(session); ListRatingStatisticsVO allStats statsService.getAllStatistics(); String json new ObjectMapper().writeValueAsString(allStats); session.sendMessage(new TextMessage(json)); } /** * 收到客户端消息时通常是前端发来的评分指令 */ Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { ScoreRequest request new ObjectMapper().readValue(message.getPayload(), ScoreRequest.class); // 核心落库 更新统计 广播都在service里完成 ListRatingStatisticsVO allStats statsService.submitScoreAndGetStats(request); String json new ObjectMapper().writeValueAsString(allStats); for (WebSocketSession s : sessions) { if (s.isOpen()) { s.sendMessage(new TextMessage(json)); } } } Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { sessions.remove(session); } }这段代码用CopyOnWriteArrayList保存所有会话这个线程安全的集合在广播遍历时不会抛ConcurrentModificationException代价是写入时复制数组但WebSocket连接的增删频率不高性价比合适。submitScoreAndGetStats是Service层方法封装了上一章的事务逻辑写入评分记录、更新统计表、返回最新列表再把列表序列化成JSON广播出去。这样好处是“推出去的数据永远跟库里的一致”不会出现统计表还没更新完就广播旧数据的错位。前端在Vue里连接WebSocket并监听消息const ws new WebSocket(ws://${location.host}/ws/score); ws.onmessage (event) { const stats JSON.parse(event.data); // 更新页面的评分排行和平均分展示 this.ratingList stats; this.refreshChart(stats); }; function submitScore(itemId, score) { ws.send(JSON.stringify({ ratingItemId: itemId, score: score, userId: 1001 })); }注意ws://后面接的是后端地址如果前后端分离部署在同一个域名下直接用location.host跨域时则需要拼完整的后端IP和端口。前端评分操作通过ws.send发送JSON串后端接收后走完整的事务流程。这个设计的好处是评分提交和结果推送共用一个长连接不额外发HTTP请求。这是保证“实时”的关键设计决策。4.3 前端断线重连的边界问题别让评分丢在路由切换里WebSocket最大的坑是“连接会断”校园网不稳定、服务器重启、浏览器切后台太久都可能导致连接关闭。前端必须在onclose里重连不然评分功能悄无声息地失效。let ws null; let reconnectTimer null; function connectWebSocket() { ws new WebSocket(ws://${location.host}/ws/score); ws.onopen () console.log(评分通道已连接); ws.onmessage (event) handleRankingUpdate(event); ws.onclose () { // 2秒后重连避免服务端还没恢复时的无效握手风暴 clearTimeout(reconnectTimer); reconnectTimer setTimeout(connectWebSocket, 2000); }; ws.onerror () ws.close(); // 触发onclose统一走重连逻辑 } connectWebSocket();这里的关键设计是错误发生时主动关闭连接让onclose统一处理重连避免onerror和onclose各写一套重连逻辑导致连接被创建两次。重连间隔设2秒既不会在服务端恢复期疯狂握手也不会让用户等太久。这个细节写到项目说明文档里评委老师会觉得你考虑过生产环境的稳定性问题。5. 项目说明文档的写作套路从ER图到部署步骤的完整链条5.1 说明文档应该包含的六个部分附标题模板很多毕设项目源码和数据库都很完整但说明文档要么是流水账要么只有开发环境配置导致答辩时老师追问系统设计讲不清楚。一份能让老师快速理解系统的说明文档建议按下述结构组织引言与选题背景描述为什么需要实时评分、需求分析的用例描述、数据库设计的ER图和字段说明、核心接口API文档包括REST和WebSocket消息格式、部署运行步骤、系统测试部分并发评分测试和实时性验证。数据库设计部分要放下面的表格表名用途核心字段与其他表的关系score_record记录每次评分的明细id, rating_item_id, score, create_time多对一关联rating_itemrating_item被评分对象列表id, item_name, status主表被score_record引用rating_statistics每个评分项的聚合结果rating_item_id, avg_score, score_count一对一关联rating_item5.2 部署步骤要写成能跟着照做的程度文档里的部署章节得是“照着操作就能跑”的程度不能只写“导入项目并运行”。关键命令和配置一处都不能省安装JDK 17配置环境变量JAVA_HOME。安装MySQL 8.x执行项目的sql/init_schema.sql和sql/init_data.sql创建库和初始化数据。修改application.yml中的数据库用户名和密码确保spring.datasource.url里的数据库名与建库名一致。在项目根目录执行mvn spring-boot:run启动后端服务。进入前端目录执行npm install安装依赖随后npm run dev启动开发服务。访问http://localhost:8080向评分接口提交一条测试数据验证页面是否实时更新。部署部分有一个非常常见的坑要写清楚后端端口改了前端Vite的代理配置也要跟着改。比如后端跑在8081前端vite.config.js里要设server.proxy指向http://localhost:8081否则联调时页面请求全部404。这些细节写进说明文档能省下答辩评委自己折腾环境的时间。5.3 文档的技术深度不需要贴全部源码但接口协议要写细说明文档不是代码注释的堆砌更不需要贴所有单表的CRUD方法。重点是接口协议和消息结构因为它们决定了系统能不能被别人复用。REST接口用表格列出路径、方法、参数和响应示例就足够。WebSocket的消息格式要单独写清楚前端发送{ratingItemId:1,score:9,userId:1001}后端返回[{ratingItemId:1,avgScore:8.7,scoreCount:42},...]。用JSON Schema的方式描述清楚字段类型和含义尤其是业务规则的约束比如“同一用户不能重复给同一个评分项打分”。这种约束用SQL的联合唯一索引实现文档里要说明索引设计和实际效果方便老师理解系统在数据一致性上的考虑。6. 答辩演示的最佳实践实时评分系统怎么展示才区分度足够6.1 演示的关键要展示实时性而不是操作流程多数人演示这个项目是用浏览器开两个页面左面提交评分右面看结果更新证明WebSocket生效了。这个做法太常规老师看太多遍没有记忆点。更有效的做法是开两个浏览器窗口一个窗口模拟评委打分另一个窗口切换到大屏展示模式两个窗口同步更新排名变化加上平均分实时变动。这需要前端支持“评分端”和“展示端”两套路由展示端页面字号要大、信息要少只显示排行榜和实时分数。如果提前用JMeter或Postman的WebSocket客户端脚本做了一次50并发评分请求并在演示时播放后端日志中的推流记录这个展示效果比口头说明强得多。50个请求同时写评分记录数据库连接池参数、事务隔离级别、行锁等待时间都会被暴露出来所以压测要在本地反复跑过确认稳定后再演示否则一旦卡顿就是减分项。存储过程或SQL脚本不是重点重点是用时序图展示一次评分的完整生命周期WebSocket发送评分消息、Service层事务写入数据库、统计表更新、最后一秒内广播给所有连接的客户端。画成PPT里的图答辩时指着图讲比对着代码讲清晰得多。6.2 评委老师常问的三个问题与应对导师大概率会问三个问题一是WebSocket和轮询的区别为什么不用SSE二是数据库事务为什么能保证统计表不出现负数或平均数错乱三是若线上评分量大如何处理性能瓶颈。第一个问题的回答要点是SSE是单向的客户端能收服务端推送但很难做到评分消息的上行和下行共用同一条通道而实时评分系统既有提交请求又有结果推送WebSocket的双工优势是天然匹配。第三个问题是区分度最高的地方能否说出“Redis计数 异步批量落库 WebSocket广播”的分层架构决定了18分和25分的差距。这三个问题不要背标准答案按自己设计系统时的取舍来讲。自己的系统用了事务保证统计一致就说事务的边界在哪里用了CopyOnWriteArrayList管理会话就分析为什么会话数量少时它比同步容器更合适。这里每讲出一个设计点都是简历上能写的技术深度。本文还有配套的精品资源点击获取