news 2026/10/3 14:10:43

SpringBoot+Vue评分系统开发实战:健美操管理平台全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+Vue评分系统开发实战:健美操管理平台全流程解析

每年到毕设季,SpringBoot+Vue 健美操评分系统管理平台源码这类题目都会被反复翻出来。Java 做后端、MySQL 存数据、Vue 写页面,听起来是标准三件套,可真正开始做时你会发现:光是把“多人打分、去掉最高最低、算总分”这套流程理清楚,就需要对项目结构、接口设计、数据库表关系都有一定把握。它适合毕业设计、课程设计,也适合刚学完 SSM 或 SpringBoot 基础、想完整走一遍项目流程的同学。这篇文章不堆概念,按我实际做这个项目的思路,从设计、编码到部署一条线讲清楚。

1. 项目定位与整体设计思路

1.1 为什么是 SpringBoot+Vue,而不是别的组合

SpringBoot 负责处理业务逻辑和提供接口,Vue 负责页面渲染和数据展示,MySQL 保存选手、评分、赛事等结构化数据。选择这套组合,核心原因是分工清楚、开发效率高、学习资料多。SpringBoot 最大的价值是自动配置和约定优于配置,不像早期 SSM 那样要花大量时间写 XML;Vue 的组件化开发又能把评分页、管理页拆成独立组件,后面维护不用在一大堆 JS 里找逻辑。

对比传统 JSP+Servlet 或 Thymeleaf 服务端渲染,前后端分离更容易调试。前端只需要调用/api/xxx接口,后端接口可以用 Swagger 或 Postman 单独测试;部署时前端打成静态文件扔到 SpringBoot 里,一套 jar 就能跑起来。对毕设和课设来说,这套组合既能体现“会 Java”“会数据库”“会前端”,又不会因为技术太偏导致老师看不懂。

1.2 业务场景与角色拆解:先把人理清楚

做任何管理系统,第一步都不是写代码,而是弄清谁会用它。健美操评分平台至少要有三类角色:

角色核心操作对应页面
管理员维护用户、赛事分组、裁判分配、评分规则配置系统管理、赛事管理
裁判进入指定轮次,给选手打各维度分数评分录入页
选手/观众查看成绩与排名,不需要登录或只读账号成绩查询页

每个角色对应不同的菜单和接口权限,这是系统架构里最重要的边界。如果一开始没把权限定义清楚,后面会出现裁判能进后台删数据的尴尬情况。场景上,比赛前管理员创建赛事和轮次,把选手分组,给每个轮次安排裁判;比赛时裁判登录后只能看到分配给自己的选手名单,逐项打分;比赛结束或每一轮结束后,系统自动计算总分、生成排名。这个流程跟体操、跳水等项目也能复用,所以“健美操”本质上是一个典型的赛事评分业务模型。

在业务设计上,我建议把“评分规则”独立成一张配置表,而不是写死在代码里。因为不同学校、不同级别的比赛对艺术分、完成分权重定义可能不一样。把维度代码、维度名称、权重存到数据库,管理员可以在界面上调整,后期不用改代码重新部署。这也是容易被答辩老师问到的点,提前做了会加分。

2. 核心功能与评分逻辑实现

2.1 分值构成与计分算法:不是简单平均分

健美操评分通常分成几个维度:艺术分、完成分、难度分,以及违规扣分。实际学校比赛可能不同,但都可以抽象成“多维度评分”。一种常见规则是:每个维度由多位裁判打分,去掉一个最高分和一个最低分,剩下的取平均值;然后各个维度按权重加权汇总;最后减去违规扣分,得到最终成绩。

为什么要去掉最高最低?最直接的原因是避免某一个裁判打分偏高或偏低,把整体分数带偏。这个规则虽然简单,但实现时要注意边界:裁判人数至少要有 3 个,否则去掉最高最低后只剩一个分数,平均值就失去了意义。代码里要加一个校验:裁判人数小于 3 时直接提示“无法计算”。

public BigDecimal averageAfterTrim(List<BigDecimal> sourceScores) { if (sourceScores == null || sourceScores.size() < 3) { throw new IllegalArgumentException("至少需要3个裁判评分"); } List<BigDecimal> sorted = new ArrayList<>(sourceScores); Collections.sort(sorted); BigDecimal sum = BigDecimal.ZERO; for (int i = 1; i < sorted.size() - 1; i++) { sum = sum.add(sorted.get(i)); } int count = sorted.size() - 2; return sum.divide(BigDecimal.valueOf(count), 2, RoundingMode.HALF_UP); }

这里要强调一点:计算必须用BigDecimal,不要用double。评分场景对精度敏感,double 的二进制浮点误差在 9.99 这种数值上会出现类似 9.990000001 的问题;加上总分要保留两位小数,必须统一使用 BigDecimal 并指定舍入模式。

最终总分公式可以抽象成:

totalScore = artScore * artWeight + completionScore * completionWeight + difficultyScore * difficultyWeight - deduction

这些权重和扣分值都从配置表读取。这样当老师问“如果规则改成 5 个裁判怎么处理”时,可以直接回答:打分列表长度改成 5,其余逻辑不变。

2.2 后端评分接口怎么设计:权限、事务、重复提交

评分接口是系统最核心的接口。一个典型的打分请求可以这样定义:

{ "roundId": 1, "playerId": 101, "judgeId": 5, "dimensionCode": "ART", "score": 8.50 }

后端要做三件事:第一,权限校验,这个裁判是否被分配到这个轮次、是否有权给这个选手打分;第二,分数范围校验,不能低于 0、不能超过满分;第三,事务处理,保存明细后立刻重算该选手的总分。

@PostMapping("/api/scores/submit") public Result<String> submitScore(@RequestBody ScoreSubmitDTO dto) { boolean authorized = judgeService.canScore(dto.getJudgeId(), dto.getRoundId(), dto.getPlayerId()); if (!authorized) { return Result.fail("当前裁判无权为该选手打分"); } if (dto.getScore() == null || dto.getScore().compareTo(BigDecimal.ZERO) < 0 || dto.getScore().compareTo(new BigDecimal("10.00")) > 0) { return Result.fail("分数必须在0到10之间"); } scoreService.submitScore(dto); return Result.ok("提交成功"); }

Service 层要用@Transactional(rollbackFor = Exception.class)保证明细表和汇总表同时更新。我之前见过不少代码只写了明细表,排名页面靠实时查明细计算,数据少时没问题,但赛事轮次一多,查询越来越慢;而且如果某个裁判分数录入错误去改明细,排名数据会出现临时的不一致。更合理的做法是维护一张汇总表,每次裁判提交分数后,重算该选手当前轮次的成绩写回汇总表,这个动作和写入明细必须放在同一事务里。

还有一个容易忽略的点:如果允许裁判修改打错的分数,提交接口要先判断明细表里是否存在该裁判、该轮次、该选手、该维度的记录。存在就执行更新,不存在就插入。为了防止并发重复提交,数据库层面还需要唯一索引兜底。

2.3 数据库设计要点:成绩明细表不能省

核心表可以这样规划:

  • sys_user:登录账号、角色标识
  • competition:比赛信息
  • round:轮次或分组
  • player_info:选手基本信息
  • judge_assignment:裁判与轮次的分配关系
  • score_detail:每个裁判给每个选手每个维度打的分
  • score_summary:选手在当前轮次的最终得分和排名

重点看一下打分明细表:

CREATE TABLE score_detail ( id BIGINT PRIMARY KEY AUTO_INCREMENT, round_id BIGINT NOT NULL, player_id BIGINT NOT NULL, judge_id BIGINT NOT NULL, dimension_code VARCHAR(20) NOT NULL, score DECIMAL(5,2) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_round_player_judge_dim (round_id, player_id, judge_id, dimension_code) );

这条唯一索引的意义是:在数据库层面防止同一个裁判对同一个选手的同一个维度重复提交。但要注意,如果允许裁判修改分数,不能简单依赖这个唯一索引做“阻止重复插入”,而是在提交接口里先查记录是否存在,存在就更新,不存在就插入。

汇总表用round_id + player_id做唯一约束,保证一个选手在一个轮次只有一条最终成绩。维度在明细表里用dimension_code描述,比单独存维度名称好扩展。管理员配置新的维度后,只要代码合法,数据库不需要改表结构。实际项目里表数量控制在 6~8 张就够,不要贪多,否则写起来累,答辩也讲不清。

3. 从零搭建前后端项目:实操记录

3.1 后端:SpringBoot 初始化和依赖选择

创建项目时建议选 Spring Boot 2.7.x 版本。原因到第 4 部分再展开:3.x 需要 JDK17,而很多毕设环境还是 JDK8,换成 2.7.x 可以少踩坑。依赖选择 Spring Web、MySQL Driver、Lombok,然后手动加 MyBatis-Plus。

pom.xml 加 MyBatis-Plus:

<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency>

为什么选 MyBatis-Plus 而不是裸 MyBatis?因为单表 CRUD 不需要写 SQL,实体类加注解就能操作数据库,评分明细的插入、汇总表的更新都不需要维护一堆 XML。对课设项目来说,这是提高开发效率的关键。

后端目录结构我习惯这样分:

controller // 接口层 service // 业务逻辑 mapper // 数据访问 entity // 数据库实体 dto // 前端请求参数 config // 跨域、拦截器配置

application.yml里重点看数据源:

spring: datasource: url: jdbc:mysql://localhost:3306/aerobics?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver

useSSL=false是开发环境关掉 SSL 校验,serverTimezone=Asia/Shanghai解决 MySQL 8 的时区报错。很多源码里没有 SQL 脚本,需要自己用 Navicat 把建表语句执行一遍,这一步跑通了,后端基本就成功了一半。

3.2 前端:Vue 项目初始化和路由规划

前端我用 Vue 2 + Element UI,老项目多、案例多、遇到问题几乎都能搜到。用 Vue CLI 创建:

vue create aerobics-web npm install element-ui axios vue-router

路由按业务划分:

const routes = [ { path: '/login', component: Login }, { path: '/', component: Layout, children: [ { path: 'dashboard', component: Dashboard }, { path: 'players', component: PlayerList }, { path: 'rounds', component: RoundList }, { path: 'score-entry', component: ScoreEntry }, { path: 'ranking', component: Ranking } ] } ]

如果要做权限菜单,可以在登录返回的 userInfo 里带上 roles,前端用addRoutes动态添加对应路由,避免裁判看到管理菜单。这个功能能体现对 Vue Router 的掌握,属于加分项,第一天不用急着写。

页面规划不需要太复杂:登录页、主页布局、选手列表页、轮次管理页、评分录入页、成绩排名页。评分录入页是最核心的页面,建议做成“左侧选手列表,右侧分数表单”,这样裁判可以快速切换选手,不用反复返回列表。Element UI 里el-table和el-form足够覆盖这些场景。

3.3 联调与部署:开发代理、打包合并、刷新404

开发环境最常见的跨域问题,可以在vue.config.js里配置代理:

module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }

这样前端请求/api/scores/submit会被代理到后端 8080,浏览器没有跨域问题。

生产环境适合毕设演示的方式是前端打包后直接放进 SpringBoot 静态目录,变成单 jar。做法是:

npm run build

把生成的dist目录里的文件复制到后端的src/main/resources/static下,然后正常启动 SpringBoot。此时访问http://localhost:8080就是前端页面。

需要注意:如果 Vue Router 用了 history 模式,直接访问子路径(如/score-entry)会返回 404,因为后端没有这个路径。最简单的处理是用 hash 模式,路由地址会带上#;如果想用 history 模式,后端需要写一个 forwardController 把所有非/api的前端路由转发到 index.html。毕设答辩时,我建议用 hash 模式,省事、不会出错。

@Controller public class PageForwardController { @RequestMapping(value = {"/score-entry", "/ranking", "/players", "/rounds"}) public String forward() { return "forward:/index.html"; } }

4. 常见问题排查与避坑指南

4.1 MySQL 连接异常:SSL、时区、驱动版本

很多同学第一次跑源码,卡在数据库连接是正常的。常见报错和解决方式整理成一张表:

报错或现象原因解决办法
Communications link failure数据库没启动、连接串写错、驱动没有加载先确认 MySQL 服务和端口,再检查driver-class-name
The server time zone value ...MySQL 时区问题URL 加serverTimezone=Asia/Shanghai
SSL connection errorMySQL 8 的连接默认开启 SSL,本地环境可能不兼容URL 加useSSL=false
Public Key Retrieval is not allowedMySQL 8 密码加密方式导致URL 加allowPublicKeyRetrieval=true
Access denied for user用户名或密码错误单独用 Navicat 测试连接后再试

这里要提示:useSSL=false是本地开发时方便,生产环境不建议关 SSL。但毕设演示,本地怎么方便怎么来。

4.2 SpringBoot 版本太高引发的奇怪报错

Spring Boot 3.x 发布后,很多同学用 Spring Initializr 默认选了 3.x,结果本地 JDK 是 8,启动直接报UnsupportedClassVersionError。如果跟着网上的教程写代码,还会发现 3.x 里javax变成了jakarta,大量老代码不能直接跑。我的建议是:不是新版本不好,而是项目要稳定。毕设和课设用 Spring Boot 2.7.x、JDK 8 或 11、MyBatis-Plus 3.5.x,这套组合已经被验证过无数次,遇到问题也容易搜到答案。

如果确实要用 Spring Boot 3,需要把 MyBatis-Plus 换成mybatis-plus-spring-boot3-starter,同时注意 Java 版本和 Jakarta 注解的变化。这些都算额外工作量,刚入手时没必要硬上新版本。

4.3 前后端联调时的几个细节坑

第一,时间格式不一致。后端默认返回的 LocalDateTime 可能序列化成一大串数组,前端展示很难看。可以在application.yml里配置:

spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai

第二,接口返回结构不统一。建议所有接口都返回{ code, message, data }这种统一结构,前端 axios 响应拦截器统一处理。否则每个页面都要写res.data.data,很容易乱。

第三,后端数据校验不能省。很多新手只在页面做if (score > 10)这种校验,后端接口完全裸奔。答辩老师随便用 Postman 提交一个 100 分,系统也能接受,这就很尴尬。后端一定要做参数校验,可以用@Validated,也可以在 Service 里手动判断,至少不能依赖前端。

第四,小数精度问题。后端如果接口里用 double,前端显示会踩精度坑。数据库DECIMAL(5,2),Java 用BigDecimal,前端按字符串或 Number 显示,整套链路就不会有问题。

5. 几个能让项目更出彩的扩展点

5.1 权限控制从“有”到“能用”

我见过不少评分系统源码,登录之后所有接口都能调,甚至直接在浏览器控制台调接口就能改分数。这个问题在答辩时很减分。至少要做一个可用的权限控制:后端写一个拦截器,从请求头取 token,解析出当前用户角色;管理员接口检查 ADMIN,裁判接口检查 JUDGE。实现不复杂,但能说明你理解权限模型。

如果技术储备够,可以用 Spring Security + JWT。具体做法是登录接口签发 token,前端 axios 在请求拦截器里加Authorization头,后端配置安全过滤器。这样整体才像一个正经平台。

5.2 成绩导出 Excel 和打印评分表

评委打分完成后,管理员常常需要一张可打印的评分表。这个功能建议直接用 EasyExcel,加入依赖后,一行代码就能导出:

EasyExcel.write(response.getOutputStream(), ScoreSummaryVO.class) .sheet("成绩表") .doWrite(list);

前端只需要用window.location.href请求一个文件下载接口。工作量不大,但是演示效果很好,评委和老师都会觉得系统完整。

5.3 选手照片和参赛视频的扩展

如果选手列表要上传证件照、比赛视频,可以考虑引入对象存储。MinIO 是一个很轻量的方案,在 SpringBoot 里引入 SDK,配置桶名和地址,上传接口把文件流写入 MinIO,再把返回的 URL 存到选手表。这个扩展能体现对文件存储的认识,但不是必需功能,时间不够可以不加。

我自己在实际项目里的做法是:先把照片路径放在服务器本地磁盘,用 Nginx 映射访问,实现简单;等系统稳定后再接 MinIO。毕设阶段没必要一上来就追求分布式文件存储,把核心业务做好更重要。

最后分享一点个人的实战体会。评分系统这类项目最关键的,是把“业务规则”和“技术实现”对应起来。如果你拿到源码,别急着把项目跑起来,先看数据库设计,再顺着“管理员建比赛 → 分配裁判 → 裁判打分 → 系统算分 → 公布排名”这条线读代码,这样评审时问任何流程都能接上。我自己在这个项目里踩过最大的坑就是改分事务:先改了 score_detail,忘记重算 score_summary,导致排名一直不对。后来在 Service 里加了一个@Transactional方法,把删除旧分、插入新分、重算汇总放到同一个事务里,问题才算彻底解决。这个思路你在做任何带“计算汇总”的项目时,都可以直接用上。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 14:07:42

高校学生消费行为分析:Python时间序列聚类实战

简介&#xff1a;本资源是一套面向高校学生与数据分析初学者的Python校园消费行为分析实战项目&#xff0c;适用于期末大作业、课程设计及毕业设计场景&#xff0c;聚焦学生消费偏好、时段规律与食堂就餐结构等实际问题&#xff0c;助力掌握数据清洗、统计分析与可视化全流程技…

作者头像 李华
网站建设 2026/10/3 14:04:28

Microsoft Store空白页全排查:从缓存重置到系统修复

我接手过不少次“Microsoft Store打开是空白页、还提示需要刷新”的求助&#xff0c;这个问题的共性很强&#xff1a;商店能启动&#xff0c;但主界面空空如也&#xff0c;要么转圈要么灰屏&#xff0c;偶尔冒出个刷新按钮&#xff0c;点了也基本没反应。网上搜一圈&#xff0c…

作者头像 李华
网站建设 2026/10/3 14:01:59

高速采集脉冲计数偏少?揭秘死区成因与排查方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 14:01:45

AI、鸿蒙与云计算:开发者如何跑通端侧到云端的完整链路

每年一到华为开发者大会&#xff08;HDC&#xff09;的节点&#xff0c;开发者社区就会分成两拨人&#xff1a;一拨刷发布会亮点截图&#xff0c;转发各种新名词&#xff1b;另一拨翻出开发文档&#xff0c;默默把环境装好&#xff0c;开始跑一个最小的示例。两年后再回头看&am…

作者头像 李华
网站建设 2026/10/3 14:01:44

武大机器学习与模式识别实验课Python源码拆解:从感知机到神经网络

简介&#xff1a;这份资源是武汉大学机器学习与模式识别课程的实验配套源码与文档&#xff0c;面向计算机、人工智能、通信、自动化等专业的在校学生及自学者&#xff0c;可用于课程实验、课程设计、毕业设计或项目立项演示。内容覆盖监督学习、无监督学习、神经网络等典型算法…

作者头像 李华
网站建设 2026/10/3 14:01:12

手写PL/0编译器:从词法分析到解释器的编译原理课程实验指南

简介&#xff1a;山东大学SDU编译原理课程PL/0编译器实验的完整实现包&#xff0c;面向高校计算机专业学生及编译原理学习者&#xff0c;适合作为课程实验参考、复习与二次开发基础。资源包含完整的C/C源代码&#xff08;.cpp/.h/.c&#xff09;、CMake构建脚本、测试用例&…

作者头像 李华