基于SpringBoot+Vue的大学生体质测试平台设计与实现

📅 发布时间:2026/8/31 19:58:41
基于SpringBoot+Vue的大学生体质测试平台设计与实现
简介本资源是一套面向高校计算机专业本科生及Java/Vue全栈初学者的毕业设计级项目实战材料聚焦大学生体质测试全流程数字化管理需求。压缩包共含77.8MB内含SpringBoot后端Vue前端完整源码、系统功能演示视频覆盖管理员、教师、学生三角色操作流程以及详尽的WORD文档资料含需求分析、数据库设计、接口说明与部署指南。系统采用B/S架构基于Java语言开发MySQL存储数据模块划分清晰涵盖用户与教师管理、体质测试任务发布、成绩录入与报告生成、留言板互动及系统基础配置等核心功能。已有204人学习下载适合用于课程设计、毕设选题或全栈技术整合实践可直接运行调试、二次开发或作为教学案例参考。1. 项目概述与选型思路1.1 这个项目到底解决什么问题每年开学季各个高校的体育部都要组织大一到大四的学生进行体质健康测试。以前我帮老师整理过一阵子体测数据那会儿还是用Excel手工登记——先打印纸质表格测试当天抬着几箱表去操场测完一项手填一项最后一天下来两三千条成绩要逐条录进电脑。漏填、填错、学号对不上都是家常便饭。期末统计及格率的时候更要命得写公式、做透视表有时候数据源乱了整个年级的合格率报表都要重来。所以当我看到这个“基于SpringBootVue的大学生体质测试平台”的毕业设计题目时第一反应就是这确实是一个业务场景非常清晰、技术栈也足够主流的项目。它要解决的核心问题就是把体测成绩从“纸质登记人工录入Excel统计”变成“学生在线报名、教师现场录入、系统自动生成报表”同时给管理员一个统筹全局的入口。从毕设/课设的角度看这个题目的好处也非常明显业务不复杂但又不至于简单到没东西可写角色分明天然有学生、教师、管理员三种权限自带报表统计能展示技术深度最关键的——它是一套完整的SpringBootVue前后端分离项目技术栈就是目前就业市场上最通行的那套。答辩的时候无论老师问后端、前端还是数据库都有话可讲。1.2 为什么是SpringBoot Vue而不是别的说到底选技术栈不是选最炫的是选最稳妥的、最能说明白的。后端用SpringBoot原因很实在第一它把Spring那套繁琐的XML配置全干掉了一个启动类加几个注解就能跑起来对在校生来说学习曲线友好。第二SpringBoot生态太成熟了整合MyBatis、MySQL、JWT、Swagger都是一条龙的现成方案出了问题随便一搜就有答案。第三实际企业项目里SpringBoot就是绝对的主流写进简历不虚。前端用Vue道理同理。Vue的上手难度比React低不少中文文档齐全教程遍地都是配合Element UI组件库做后台管理类的界面速度非常快——一个体测平台本质上就是几个表格加几个表单Element UI的el-table、el-form、el-dialog基本是量身定做的。再加上Vue Router做页面跳转、Axios做请求这套组合在高校毕设里几乎是“标准答案”。还有一个非常现实的理由这套组合的参考资料最多。搜“SpringBoot Vue 毕设”能翻出来几百个相似项目。但资料多也有副作用很多人直接下个源码改改标题就交了。我建议你千万别这么干。资料多意味着评审老师见得也多一个答辩组里十个学生可能八个都是这类题目老师随便问两句“你这个项目的权限控制怎么做的”“Redis在这里有没有必要用”答不上来反而丢分。正确姿势是拿别人的项目当参考没问题但一定要自己能讲清楚每一行关键代码为什么这么写并且最好加一两个差异化功能。1.3 这套交付包具体包含什么标题里的“源码演示视频WORD文档资料.zip”其实是这种毕设交付包的标准三件套。源码是前端和后端工程演示视频是录制好的系统操作流程通常包含管理员、教师、学生三个角色的完整演示WORD文档则是毕业论文或者课程设计报告。这三样东西的分工很明确源码证明“我能做出来”演示视频证明“它真的能跑”文档证明“我做了并且知道为什么这么做”。很多同学只重视写代码答辩前一晚才拼凑文档这是本末倒置。文档里最重要的不是代码贴了多少而是需求分析、数据库设计、系统测试这几块有没有讲透。后面我会专门用一节讲文档该怎么写才不会被答辩老师挑毛病。2. 功能拆解与数据库设计2.1 三种角色和各自的核心诉求体测平台这类系统的功能设计核心就是按角色拆需求。大学生体测这个场景里涉及的人就三类管测试的老师管理员、执行测试的体育老师/工作人员教师、被测的学生。先看学生端。学生最关心的事只有两件一是知道什么时间、在哪儿测二是测完能不能查到自己的成绩。所以学生端的功能不必做多登录、查看测试通知、查看个人成绩、成绩单打印这几项就够了。有的平台还会加一个“体测预约”功能让学生自己选测试时间段这个可以做但要注意预约时间段的并发问题——几十个人同时抢周二下午三点的场后端得防超卖这就涉及到数据库乐观锁之类的内容了属于加分项。再看教师端。教师的痛点是录入成绩的效率。现场测试的时候一个老师要测一个班好几十人系统录入界面必须够快、够顺手。这里有两个设计细节非常关键一是支持按学号批量录入录完一个回车自动跳到下一个二是支持Excel批量导入导出毕竟体育部手里还有往年纸质数据没转成电子版。这两个功能看着不起眼但实际使用中的好感度远高于花里胡哨的可视化大屏。最后是管理员端。管理员就是体育部负责统筹的老师他要看的是全局学生信息管理批量导入新生数据、体育教师账号管理、体测项目管理设置各项目权重和及格线、测试批次安排、全校体测数据统计分析。统计分析这部分是论文里最好写的一章也最容易出彩——按学院统计及格率、各单项成绩分布、历年对比趋势用ECharts画几个图数据说话比写一千字文字描述都有说服力。2.2 核心数据表设计一个体测平台的后台数据模型并不复杂核心表我认为有五张用户表、学生信息表、体测项目管理表、体测成绩表、测试批次表。设计的时候要注意几个坑。第一张是用户表负责登录认证。字段有主键、用户名、密码、角色类型、创建时间等。密码必须加密存储用MD5加盐或BCrypt都好千万别明文存。很多毕设为了演示方便把密码写死成123456这种做法答辩的时候被问到了非常尴尬——讲用户认证流程时老师很可能追问一句“你密码存的是什么形式”你如果连加密都没做前面的印象分就没了。第二张是学生信息表。这张表要注意学号要设唯一索引姓名、性别、学院、年级、班级都是必填字段。导入Excel的时候需要先按学号判断是新增还是更新不然重复导入会出一堆脏数据。第三张是体测项目管理表不是每个学校都测一样的项目有的测八个有的测五个身高体重、肺活量、50米跑、立定跳远、坐位体前屈、仰卧起坐/引体向上、800米/1000米跑等。项目表中要存项目名称、项目类别、计量单位、及格标准、权重系数、测试状态。尤其注意“及格标准”是变来变去的——不同年级、不同性别的评分标准不一样所以标准应该单独做成一张评分标准表或者用JSON字段存储各分数段对应的成绩区间否则写死在代码里后面调标准就得改代码重新部署太蠢了。第四张是体测成绩表这是整个系统最核心的表。它关联学生ID、项目ID、批次ID分数成绩状态录入时间。一个学生在一个批次测试多个项目所以是一对多关系。查询“某学生所有成绩”的时候用学生ID去关联查询即可。这里有个索引设计的关键点对“学生ID测试批次ID”建联合索引因为这是最高频的查询条件。第五张是测试批次表存储某一次测试的时间、地点、参与学生范围、状态。比如“2024年秋季学期2023级测试批次”。成绩表通过批次ID关联到具体的某次测试。数据库设计的思路讲明白了我们再来看实际操作中更重要的东西——前后端跑通的关键配置。3. 后端工程搭建与核心实现3.1 SpringBoot项目的目录结构与启动流程拿到这样一个毕设项目源码第一步不要急着看业务代码先把工程跑起来。SpringBoot的后端工程结构基本都是标准的包名分层com.example.physicaltest ├── config 配置类CORS跨域、拦截器、Swagger配置 ├── controller 控制层接收前端请求 ├── service 业务层接口 实现类 ├── mapper 数据访问层MyBatis接口 ├── entity 实体类对应数据库表 ├── dto 数据传输对象接收前端参数 ├── common 通用类统一返回结果、全局异常处理 └── utils 工具类JWT工具、密码加密工具等启动流程很简单配置好application.yml里的端口、数据库连接信息运行启动类即可。但实际跑的时候最常见的坑有三个。第一是JDK版本不匹配——很多毕设源码是别人用JDK8写的你电脑装的是JDK17pom.xml里没指定版本的话启动直接报错。解决办法是pom.xml里把java.version改成和你本机一致的版本同时SpringBoot版本也要配套SpringBoot 2.x配JDK8没问题配JDK17得用2.7以上SpringBoot 3.x必须JDK17。第二个坑是数据库连接信息。application.yml里的url、username、password要改成你本地的MySQL配置很多人拿到代码不检查直接启动报一个Communications link failure就慌了其实就是数据库没连上。新建数据库后把项目里带的physical_test.sql导入进去注意字符集选utf8mb4否则后面存中文字符会变成问号。第三个坑是端口冲突。SpringBoot默认8080如果本地有别的服务占用了8080启动会报Port already in use。改server.port即可比如改成8090。改了端口之后前端也要同步改这个后面说。3.2 登录鉴权JWT方案的完整链路体测平台的登录鉴权绝大多数毕设都会用JWTJSON Web Token因为无状态写起来简单也好讲。它的工作流程是用户带着用户名密码请求/login接口后端验证通过后用密钥生成一段Token返回给前端前端把Token存在localStorage里每次请求都在请求头带上Authorization: Bearer xxx后端写一个拦截器拦截需要权限的路径验证Token的合法性和过期时间顺便解析出用户ID和角色。关键代码大概是这样的。登录接口PostMapping(/login) public Result login(RequestBody LoginDTO loginDTO) { User user userService.login(loginDTO.getUsername(), loginDTO.getPassword()); String token JwtUtil.generateToken(user.getId(), user.getRole()); return Result.success(new LoginResponse(token, user)); }JWT工具类里的核心是生成Token和解析Tokenpublic static String generateToken(Integer userId, String role) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() 1000 * 60 * 60 * 24)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); }拦截器实际上更推荐用Spring MVC的HandlerInterceptor里验证TokenOverride public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { String realToken token.substring(7); try { Claims claims JwtUtil.parseToken(realToken); request.setAttribute(userId, claims.getSubject()); request.setAttribute(role, claims.get(role)); return true; } catch (Exception e) { // Token无效或过期 } } response.setStatus(401); return false; }这里有两个实际开发中才遇到的细节。第一拦截器配置里要放行登录接口和Swagger路径不然启动后一调登录接口就404。第二JWT的密钥不要写死在代码里放配置文件里。虽然毕设不涉及太高安全要求但这点可以在文档里提一笔显得你考虑过安全问题。3.3 成绩录入与统计报表的接口设计成绩相关接口是整个系统最核心的部分。设计时要考虑前端页面的使用场景接口才能好用。我的拆分习惯是这样GET /api/student/list?page1size10keyword学号或姓名学生分页查询POST /api/student/importExcel批量导入学生GET /api/score/student/{studentId}查询某学生所有历史成绩POST /api/score/batch按学号批量录入成绩GET /api/statistics/college按学院统计及格率GET /api/statistics/trend历年体测成绩趋势批量录入成绩这个接口是教师端体验的关键。前端传的是JSON数组每个元素包含学号、项目ID、成绩值、测试批次ID。后端循环遍历写入成绩表但这里要处理一个问题如果循环里每条都走一次数据库查询和插入100条成绩要发100次SQL效率很差。正确做法是用MyBatis的批量插入int batchInsert(Param(list) ListScoreRecord list);对应XML里的写法insert idbatchInsert parameterTypelist INSERT INTO physical_score (student_id, project_id, score, test_date, batch_id, status) VALUES foreach collectionlist itemitem separator, (#{item.studentId}, #{item.projectId}, #{item.score}, #{item.testDate}, #{item.batchId}, #{item.status}) /foreach /insert这里要注意一个问题插入之前如果某人已经在同一个批次里录过这个项目的成绩是直接覆盖还是报错让教师确认我的建议是后者——加一个去重判断如果已存在则返回冲突提示前端提示“学号2023010101在该批次已录入过肺活量成绩是否覆盖”这样既避免了重复脏数据也让教师有挽回余地。实际上我在自己做的系统里就是加了一个ON DUPLICATE KEY UPDATE通过联合唯一索引去控制。统计报表的接口也提一下。比如按学院统计及格率SQL的写法是SELECT s.college, SUM(CASE WHEN sc.score 60 THEN 1 ELSE 0 END) AS passCount, COUNT(sc.id) AS totalCount FROM physical_score sc JOIN student s ON sc.student_id s.id WHERE sc.project_id #{projectId} AND sc.batch_id #{batchId} GROUP BY s.college逻辑并不复杂但需要注意不同项目的“及格”判断逻辑不同。成绩类项目如50米跑是成绩越低越好且及格有对应的时间线分数类项目如身高体重指数BMI则是一个区间。如果统一用60分作为及格线就得在录入时就把每个原始成绩换算成百分制分数存到score字段里这样统计时统一用score 60判断就行了。换算标准就来自评分标准表——这是我在前面强调评分标准必须单独建表的原因。4. 前端工程搭建与关键页面4.1 Vue工程的初始化与项目结构Vue前端工程一般用Vue CLI或Vite创建。拿到源代码后第一步是npm install安装依赖——这一步网络不好会非常痛苦建议直接用国内镜像源一行命令搞定npm config set registry https://registry.npmmirror.com安装完依赖后npm run dev启动开发服务器默认端口8080但SpringBoot后端也默认8080所以前端一定要改端口。Vue CLI项目里改vue.config.js的devServer配置Vite项目改vite.config.jsmodule.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8090, changeOrigin: true } } } }这里强烈建议配置代理转发而不是在前端代码里直接写http://localhost:8090/api/xxx。用代理的好处有两个一是避免跨域问题开发环境下让前后端看起来像是同源请求二是以后部署时只需要改代理配置不需要改业务代码里的请求路径。开发模式跑通之后后续你打包部署到Nginx也是用类似的proxy_pass配置。前端源码的目录结构一般长这样src ├── api 接口请求封装按模块分文件 ├── assets 静态资源 ├── components 公共组件 ├── router 路由配置 ├── store Vuex/Pinia状态管理 ├── views 页面组件 │ ├── admin │ ├── teacher │ └── student ├── utils 工具函数request封装、token存储等 ├── App.vue └── main.js4.2 Axios封装与路由守卫Axios请求封装是前端工程里最有必要讲清楚的部分。所有页面里直接axios.get(...)当然也能跑但会出现大量重复代码——每个请求都要带Token、每次报错都要处理401。正确的做法是在utils/request.js里统一封装import axios from axios import { Message } from element-ui import router from /router const service axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器自动携带Token service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) // 响应拦截器统一处理错误码 service.interceptors.response.use( response { const res response.data if (res.code ! 200) { Message.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, error { if (error.response error.response.status 401) { Message.error(登录已过期请重新登录) localStorage.removeItem(token) router.push(/login) } else { Message.error(网络异常请稍后重试) } return Promise.reject(error) } ) export default service路由守卫控制的是页面访问权限。体测平台有三种角色不能让普通学生访问管理员的后台页面。在Vue Router里给需要鉴权的路由添加meta信息然后在全局前置守卫里判断router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path /login) { next() return } if (!token) { next(/login) return } const role localStorage.getItem(role) if (to.meta.roles !to.meta.roles.includes(role)) { next(/403) return } next() })这里要提醒一点前端路由守卫只能控制页面显示真正的权限校验一定要在后端拦截器里做。很多毕设项目只在前端做了控制后端接口没有任何校验任何人拿到接口地址都能直接调用修改数据这个问题在答辩时如果被问到杀伤力极大。正确做法是后端在拦截器里解析出角色后对不同的路径前缀/api/admin/**、/api/teacher/**、/api/student/**做权限匹配。4.3 成绩录入页面的交互设计成绩录入是教师高频使用的页面交互设计值得多说几句。默认List表格展示当前批次、当前项目下的已有成绩页面顶部有学号输入框和成绩输入框教师录入完一个成绩按回车自动提交并清空输入框、聚焦到学号输入框——这样用键鼠就能连续录入不需要反复点鼠标。实现这个连续录入的逻辑其实不复杂。用Element UI的el-input监听keyup.enter.native事件el-input v-modelcurrentStudentId placeholder学号 keyup.enter.nativehandleEnter /在handleEnter方法里先查询该学号是否存在存在则把学号填入当前行再请求后端插入成绩。如果学号不存在直接提示“未找到该学生”。这个交互细节虽然小但实际测试时能明显感受到效率差异。我在演示视频里一般也会重点展示这一段配上说明“支持连续回车快速录入”答辩观感会好很多。录入完成后还有一个容易忽略的点页面刷新后数据一致性。成绩提交成功但列表没刷新教师以为没录进去又重复提交一次就会产生重复记录。所以提交成功回调里必须重新拉取当前列表必要时后端做到幂等——同一学号同一批次同一项目只能有一条成绩记录这个约束在数据库层面用联合唯一索引来做是最靠谱的。4.4 数据可视化ECharts报表展示管理员端的统计报表是展示项目的亮点通常用ECharts来实现。常见的图表包括各学院体测及格率柱状图、各项目成绩分布饼图、近五年体测成绩趋势折线图、各年级体测优秀率对比图。Vue里使用ECharts的方法很成熟了先安装依赖然后在组件里初始化图表实例import * as echarts from echarts // 在mounted里初始化 this.chart echarts.init(this.$refs.chartDiv) // 在拿到数据后设置option this.chart.setOption({ title: { text: 各学院体测及格率 }, tooltip: {}, xAxis: { data: this.collegeNames }, yAxis: {}, series: [{ name: 及格率, type: bar, data: this.passRates }] })有一个实操中的坑当页面从el-tabs组件里切走再切回来时ECharts图表会显示空白或者宽度异常。这是因为隐藏的div宽度为0ECharts初始化时无法正确计算尺寸。解决办法是在图表用v-if控制渲染时机或者在tab切换后调用chart.resize()。第二个方法更简单也最常用。前端的内容到这里已经能够覆盖开发过程中的主要部分了。接下来把视角转到本地运行和线上部署上这部分才是接手一个现成源码时真正费时间的地方。5. 环境配置与项目跑通的完整流程5.1 本地开发环境清单拿到源码后从零开始跑通整个项目我的建议顺序是先装数据库再配后端最后跑前端。不要反过来不然前端启动了没后端接口对接浏览器里全是报错心态先崩一半。环境清单如下组件推荐版本主要事项JDK1.8 或 17注意和SpringBoot版本匹配Maven3.6配置阿里云镜像加速依赖下载MySQL5.7 或 8.0字符集设utf8mb4Node.js14建议保底16兼容Vite和Vue CLIIDEIDEA / VS Code后端用IDEA前端可以用WebStorm或VS CodeMaven的阿里云镜像配置找到本地Maven的settings.xml在mirrors标签里添加mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror不配置这个镜像SpringBoot项目第一次构建时下载依赖可能要半小时起步配置了以后两三分钟就能完事。这个细节虽然不起眼但确实能帮你节约大量的等待时间。5.2 三分钟快速启动手册我建议你写一个README文件记录下每个步骤。“快速启动手册”其实很简单把当初自己跑通项目时一步步的操作记录下来即可但能给后面接手的人省下大量摸索时间。核心步骤只有五步第一步创建数据库CREATE DATABASE physical_test DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后把项目提供的physical_test.sql导入mysql -u root -p physical_test physical_test.sql如果你用Navicat或DataGrip图形界面导入也可以第二步打开后端工程修改application.yml里的数据库连接然后启动主类。第三步打开前端工程执行npm install然后启动开发服务器。第四步浏览器访问http://localhost:3000看到登录页。第五步用初始管理员账号登录。这个步骤清单我建议直接放进论文的“系统运行环境”章节里很实用也显得系统交付完整。到时候答辩老师问“这个系统怎么部署”照着步骤念一遍就行。5.3 部署到Linux服务器Docker方式如果条件允许花点时间把系统部署到云服务器上是演示环节的一个加分项。毕竟是Web项目演示的时候直接用服务器地址访问肯定比本地开三个窗口更有说服力。Docker部署SpringBoot项目的思路一般是这样的后端代码用Maven打成可执行的jar包写一个Dockerfile构建镜像启动时用--link或Docker Compose连接MySQL容器前端代码用npm run build打包成dist目录放到Nginx容器里同时配置Nginx把/api路径反代到后端的容器端口。一个简单的后端Dockerfile可以参考FROM openjdk:8-jdk-alpine COPY target/physical-test-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8090 ENTRYPOINT [java, -jar, /app.jar]前端Nginx的配置里核心是这一段location匹配server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://backend-server:8090; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }有两个部署时的坑说一下。第一Vue是单页应用如果Nginx只配置了root没有try_files刷新非首页路径会报404所以try_files那行不能漏。第二容器里后端和前端是分开部署的前端容器里的/api代理地址要写后端容器的服务名而不是localhostDocker Compose里通过service名称互相访问所以你要先理清两个容器的网络关系。Docker部署这部分内容如果你的论文字数不够完全可以扩展成“基于Docker的部署方案设计”一小节写清楚每个容器的构建方式和宿主机端口映射表字数轻松加两千。6. 常见问题与避坑记录6.1 后端运行报错速查表实际运行这个项目时我记录了一些很常见的报错场景和对应的解法整理成了一张速查表报错信息可能原因解决方案Failed to configure a DataSource数据库连接信息错误检查application.yml中url、用户名、密码确认数据库已创建Port 8080 was already in use端口被占用改server.port或者用lsof -i:8080找出占用进程java.lang.ClassNotFoundException: javax.xml.bind.JAXBExceptionJDK版本过高与旧SpringBoot不兼容换JDK8或换SpringBoot 2.7版本Access denied for user rootlocalhost数据库密码错误重置密码或修正配置Table xxx doesnt exist未导入SQL或导错库确认导入了正确的SQL文件且数据库名一致Invalid bound statement (not found)MyBatis的mapper接口和XML不匹配检查Mapper接口的Mapper注解、XML文件的namespace和id其中Invalid bound statement这个错是MyBatis项目新手最容易踩的。绝大多数情况是SpringBoot配置里没有指定MyBatis的mapper-locations路径默认扫描的路径和XML实际放置的位置不一致。解决方法是application.yml里加上mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.physicaltest.entity6.2 前端运行报错速查表前端的问题集中在依赖安装、跨域、数据展示异常这几类。同样整理一下报错信息可能原因解决方案npm ERR! code ERESOLVE依赖版本冲突删除node_modules和package-lock.json后重新npm installCannot find module node-sass旧项目用node-sass换sassdart-sass或降低Node版本ERR_CONNECTION_REFUSED请求后端失败代理配置错误或后端没启动检查代理路径是否匹配、后端服务是否存活Uncaught SyntaxError: Unexpected token 请求到了HTML而非JSON多半是后端接口路径不对返回了404页面图表空白ECharts初始化时隐藏容器在显示后调用chart.resize()前端还有一个很阴间的坑开发环境一切正常打包部署到服务器后登录接口404。排查的方法是直接访问服务器的http://服务器IP:端口/api/login看返回什么——如果返回的是Nginx的404而不是JSON说明代理没配上如果返回的是index.html说明代理配置生效了但后端拿不到。总之先从网络请求面板看哪个环节断了再对症下药。6.3 演示视频录制的注意事项演示视频是这个交付包里容易被忽视但其实很重要的部分。我见过太多人把视频当成“录屏流水账”打开系统随便点点就完了。正确做法是像讲故事一样设计一段“脚本”按角色分幕录制。建议的顺序是开头高亮项目名称和技术栈然后以管理员身份演示学生管理、体测项目管理、数据统计大屏切到教师身份演示批量录入成绩、Excel导入导出最后切到学生身份演示查看成绩、打印成绩单。切换角色时的登录过程不用完整录剪切掉只保留关键操作就好。视频总时长控制在8到12分钟比较合适太短显得内容单薄太长答辩现场老师也没耐心看完。录制工具用OBS或者Windows自带的录屏都行。关键是要先打开系统的所有页面检查一遍确保没有报错弹窗或者数据加载空白不然录了一半发现页面白屏又要重来。我自己的经验是每次都先跑一遍完整流程确认无误后再开场录制。6.4 论文文档的写作要点最后说说WORD文档。这类毕设文档一般有固定的章节结构绪论背景、意义、国内外研究现状、需求分析可行性分析、功能需求、非功能需求、系统设计总体架构、功能模块设计、数据库设计、系统实现核心功能代码与截图、系统测试测试用例表、测试结论、总结与展望。文档里最容易拿分但最容易被忽略的两个章节一是数据库设计——画好E-R图每张表列出字段说明把外键关系讲清楚这部分能在系统实现之前展示你的全局设计能力二是系统测试——不要只写“测试通过”要真的列出测试用例表格包括但不限于不同角色登录的权限隔离测试、录入非法成绩负数、超长字符串的校验测试、批量导入Excel时重复学号的容错测试、高并发登录时的压力测试哪怕只是用JMeter做个简单的100并发请求。这些测试用例写出来文档的专业度瞬间就不一样了。另外文档里放代码时不要整段贴满一个页面选取关键的几十行加上注释说明逻辑即可。截图别用手机拍屏幕直接截图后放在文档里标注清楚操作步骤和结果。格式上的细节也很重要——统一字体、统一编号、目录自动生成、图表编号交叉引用这些都能给老师留下“认真”的印象。7. 几个能拉开差距的扩展建议7.1 做一个体测预约模块基础版的功能大家都一样想拉开差距就得加功能。体测预约模块是我的第一个建议。体育老师创建测试批次后开放若干时间段供学生预约每个时间段有人数上限满了就不能约。这里涉及的核心技术点是并发控制——怎么保证同一时间段不会超人数预约。最简单的方案是用数据库的乐观锁或者UPDATE ... SET count count 1 WHERE id ? AND count max_count这样的原子操作来判断。这块内容写进论文就是“关键问题解决”答辩老师会感兴趣。7.2 部署时加上监控第二个扩展是给后端加上简单的监控。Spring Boot Actuator依赖加一行配置启动后就能访问/actuator/health、/actuator/metrics等端点查看系统状态。再用Spring Boot Admin做一个可视化的监控页面能看到内存、CPU使用率、请求吞吐量。这个功能技术含量不高但展示效果很好答辩时打开监控界面给老师看一下实时运行指标效果比单纯讲功能好得多。7.3 增加按性别区分评分标准前面提到评分标准要单独建表这里再补一个细节男生和女生的体测标准是不同的。比如女生800米跑和男生1000米跑及格时间线完全不一样。所以评分标准表里至少要有gender字段查询评分时按“性别年级项目”三个维度去匹配。这个功能很多现成的毕设源码其实都没做好如果你自己动手完善了这个也值得在文档里单独写一小节。8. 一点个人体会这个项目前后我在不同阶段整理过三四版从最初自己练手的Vue2Element UI版本到后来为了演示改成Vue3Element Plus再到帮学生调通在Docker上完整部署每一版都会踩一些新的坑。体测平台这个题目本身不算难但把它打磨到“能讲、能演示、能部署、能过答辩”需要投入的时间远比想象多。我的体会是拿到别人的源码最重要的不是直接敲命令跑起来而是先看懂它的表结构再顺着登录请求走一遍前后端的数据流理解每个关键环节的取舍。当你把“为什么这么设计”想明白了这个项目就真正属于你了。往届常有学生把这种项目性演示当作纯看视频的任务跑通以后一关机全忘等到答辩被追问几个细节就卡壳。所以花一个晚上把业务流程图和数据流图画出来、吃透逻辑比多录一遍视频有用得多。最后再分享一个小技巧——无论你在这套系统上做了哪些改动一定要在文档里用单独一个章节记录“系统改进说明”把每处改进和理由清清楚楚列出来。哪怕只是把密码加密从MD5换成了BCrypt也值得写。老师看到你在思考分自然就高了。本文还有配套的精品资源点击获取