简介:这是一套面向高校计算机专业毕业设计的SSM+Vue学生成绩管理系统完整项目包,适合正在准备Java方向毕设的本科生参考与二次开发。项目采用B/S架构,后端基于SSM框架,前端使用Vue,数据库为MySQL,按管理员、教师、学生三种角色划分权限,覆盖学生与教师管理、教学课件、在线答疑、公告信息、优秀学生与优秀教师评选、班级与学生成绩、试题试卷、考试管理及系统管理等模块,功能链路较为完整。压缩包共990个文件,约67.89MB,其中166个java源文件承载后端业务逻辑,63个vue组件与156个js、116个html构成前端页面,另有4个sql脚本用于建库建表,并附带说明文档与演示录像,便于快速理解项目结构与运行流程。目前已有224人学习下载,可作为毕设选题落地的参考方案,帮助读者梳理角色权限设计、成绩与考试模块实现思路,并对照源码完成环境搭建与功能调试。
1. 从一份 SSM+Vue 学生成绩管理系统源码说起:它到底能帮你解决什么
如果你正在搜「SSM+Vue 项目实战」或者「Java 毕业设计源码」,大概率不是想听人讲 MVC 分层有多优雅,而是卡在某个具体环节:开题过了但系统跑不起来、前端 npm install 报一堆错、答辩前三天发现成绩统计逻辑对不上。这份学生成绩管理系统的源码包,本质上是把「SSM 后端 + Vue 前端」这套组合拳落到了一个真实业务场景里——学生、课程、成绩、教师、班级这几张表之间的增删改查和统计。它适合两类人:一类是 Java 基础刚过、想找一个能跑通全流程的毕业设计骨架的在校生;另一类是工作里用惯了 Spring Boot,但需要回头补一补传统 SSM 配置和 Vue 前后端分离对接细节的初中级工程师。热词里反复出现的「vue安装及环境配置」「vue路由」「vue项目源码怎么发给别人」,恰恰说明大部分人的卡点不在业务逻辑,而在环境和工程结构。这一章先把这套系统能覆盖什么、边界在哪讲清楚,后面几章再一层层拆到能照着复现。
2. SSM 后端骨架:从 Maven 依赖到 MyBatis 映射文件怎么配才不翻车
SSM 是 Spring + SpringMVC + MyBatis 的缩写,这套组合在 Spring Boot 普及之前是 Java Web 项目的主流。学生成绩管理系统的后端通常分三层:Controller 接请求、Service 写业务、Mapper 做数据库操作。很多人拿到源码第一步就卡在 pom.xml 依赖冲突上,所以这一章先把依赖和配置讲透,再落到 MyBatis 的映射文件。
2.1 先理清 SSM 三件套的职责边界
Spring 负责 IoC 容器和事务管理,SpringMVC 负责 URL 路由和参数绑定,MyBatis 负责 SQL 映射和结果集转换。三者通过 web.xml 和 spring 配置文件串起来。常见做法是在applicationContext.xml里配数据源和 MyBatis 的SqlSessionFactory,在spring-mvc.xml里配注解驱动和视图解析器。这里有个血泪经验:如果你把<context:component-scan>在两个配置文件里都扫了同一个包,Spring 会重复创建 Bean,启动时报BeanDefinitionOverrideException或者事务不生效。正确做法是让 Spring 只扫 Service 和 Mapper,SpringMVC 只扫 Controller。
2.2 Maven 依赖版本对齐:别让 mysql-connector 和 MyBatis 打架
下面这段 pom.xml 是这套系统后端最核心的依赖片段,版本号我按常见稳定组合写,你拿到源码后先核对这里。
<!-- pom.xml 核心依赖片段 --> <dependencies> <!-- Spring 核心,5.x 与 JDK8 兼容性最好 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.30</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.30</version> </dependency> <!-- MyBatis 与 Spring 整合包,不是单独的 mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.1.2</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.13</version> </dependency> <!-- MySQL 驱动,8.x 必须用 com.mysql.cj.jdbc.Driver --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!-- 连接池,Druid 或 HikariCP 二选一 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.20</version> </dependency> </dependencies>逻辑说明:mybatis-spring是整合包,它负责把 MyBatis 的SqlSession交给 Spring 管理,少了它你会遇到SqlSessionFactory注入失败。参数说明:MySQL 8 的驱动类名是com.mysql.cj.jdbc.Driver,URL 要带serverTimezone=Asia/Shanghai,否则时间字段会差 8 小时。Druid 连接池的initialSize建议设 5,maxActive设 20,毕业设计场景够用。
2.3 MyBatis 映射文件:成绩统计 SQL 怎么写才不返回 null
成绩管理系统里最典型的查询是「按班级统计某门课的平均分、最高分、及格率」。这种多表关联加聚合的 SQL,用注解写会很难维护,建议放在 XML 映射文件里。
<!-- ScoreMapper.xml 成绩统计查询 --> <select id="selectClassStats" resultType="map"> SELECT c.class_name AS className, co.course_name AS courseName, AVG(s.score) AS avgScore, MAX(s.score) AS maxScore, SUM(CASE WHEN s.score >= 60 THEN 1 ELSE 0 END) / COUNT(*) AS passRate FROM score s JOIN student stu ON s.student_id = stu.id JOIN class c ON stu.class_id = c.id JOIN course co ON s.course_id = co.id WHERE co.id = #{courseId} GROUP BY c.id, co.id </select>逻辑说明:resultType="map"让 MyBatis 把每行结果直接塞进 Map,前端拿到的就是{className, courseName, avgScore...}这种结构,省去建 DTO 的功夫。参数说明:#{courseId}是预编译占位符,能防 SQL 注入;passRate用SUM(CASE WHEN...)算,注意 MySQL 里整数除法会截断,如果想让及格率带小数,把COUNT(*)写成COUNT(*) * 1.0。失败时先看日志里打印的 SQL,把参数手动替换进去在客户端跑一遍,八成是关联字段名写错了。
3. Vue 前端工程:从 npm install 到路由和接口联调的最小闭环
前端这块是搜「vue安装及环境配置」「vue路由」的人最集中的痛点。这套系统用的是 Vue 2 + Element UI 的组合,工程结构是标准的vue-cli脚手架。这一章按「装依赖 → 配路由 → 调接口」的顺序走,每一步都给出可抄的命令和配置。
3.1 环境配置:node-sass 和 node 版本的血泪坑
先确认 Node 版本,Vue 2 项目建议用 Node 14 或 16,Node 18 以上跑老版本node-sass会直接编译失败。
# 查看当前 node 和 npm 版本 node -v npm -v # 如果 node 版本过高,用 nvm 切换(Windows 用 nvm-windows) nvm install 16.20.2 nvm use 16.20.2 # 进入前端目录安装依赖,国内建议先换源 npm config set registry https://registry.npmmirror.com npm install逻辑说明:npm install会读package.json里的依赖树,node-sass是二进制包,版本和 Node 强绑定,这是新手翻车最多的地方。参数说明:如果package.json里写的是sass而不是node-sass,那对 Node 版本宽容得多,可以考虑把依赖换掉。安装完先跑npm run serve,看到App running at http://localhost:8080才算环境通了。
3.2 路由配置:成绩录入页和统计页怎么挂
Vue Router 的核心是「路径 → 组件」的映射。成绩管理系统一般有登录页、学生管理、课程管理、成绩录入、成绩统计几个视图。
// src/router/index.js import Vue from 'vue' import Router from 'vue-router' import Layout from '@/layout/index.vue' Vue.use(Router) export default new Router({ routes: [ { path: '/login', component: () => import('@/views/login.vue') }, { path: '/', component: Layout, redirect: '/score/input', children: [ { path: 'score/input', name: 'ScoreInput', component: () => import('@/views/score/input.vue'), meta: { title: '成绩录入' } }, { path: 'score/stats', name: 'ScoreStats', component: () => import('@/views/score/stats.vue'), meta: { title: '成绩统计' } } ] } ] })逻辑说明:Layout是带侧边栏的父容器,子路由渲染在<router-view>里。meta.title用来给面包屑和菜单提供文案。参数说明:redirect设成默认跳转页,避免访问根路径白屏。路由懒加载用() => import(...),打包时会按路由分块,首屏加载更快。如果刷新页面 404,检查后端有没有配 history 模式回退,或者干脆用 hash 模式。
3.3 接口联调:axios 封装和跨域怎么处理
前后端分离项目里,前端跑在 8080,后端跑在 8081 或 9090,跨域是必踩的。开发阶段用vue.config.js的代理最省事。
// vue.config.js module.exports = { devServer: { port: 8080, proxy: { '/api': { target: 'http://localhost:9090', // 后端地址 changeOrigin: true, pathRewrite: { '^/api': '' } // 去掉 /api 前缀再转发 } } } }逻辑说明:前端请求/api/score/stats,代理会转发到http://localhost:9090/score/stats,浏览器看到的是同源请求,跨域问题消失。参数说明:changeOrigin: true会改请求头里的 Host,后端如果做了域名校验必须开。生产环境这个代理不生效,得靠 Nginx 配location /api反向代理,或者后端加 CORS 头。联调时如果返回 404,先看 Network 面板里请求的真实 URL,再核对pathRewrite有没有多删或少删前缀。
4. 数据库设计与成绩统计逻辑:表结构定错后面全白干
很多人拿到源码直接跑 SQL 建表,跑完发现成绩统计对不上,回头改表结构比重新建还麻烦。这一章把学生成绩管理系统的核心表结构和统计逻辑讲清楚,尤其是外键和冗余字段的取舍。
4.1 五张核心表的关系和字段取舍
系统最少需要五张表:学生表、班级表、课程表、成绩表、教师表。成绩表是核心,它连接学生和课程。
| 表名 | 关键字段 | 说明 |
|---|---|---|
| student | id, name, student_no, class_id | student_no 唯一索引,class_id 外键 |
| class | id, class_name, grade | grade 存入学年份,便于按届统计 |
| course | id, course_name, credit, teacher_id | credit 学分,用于加权平均分 |
| score | id, student_id, course_id, score, exam_type | exam_type 区分期中期末 |
| teacher | id, name, title | title 职称,统计页可能用到 |
逻辑说明:成绩表不存学生姓名和课程名,只存 ID,靠 JOIN 取名字,这是范式化的做法。但统计页如果每次都要 JOIN 四张表,数据量大时会慢,常见优化是在 score 表冗余class_id字段,统计时少 JOIN 一层。参数说明:score字段用DECIMAL(5,2)而不是FLOAT,避免浮点误差导致平均分算出来是 84.99999。exam_type用TINYINT存 1/2 比存字符串省空间。
4.2 加权平均分和及格率的 SQL 实现
加权平均分是「每门课分数 × 学分 之和 ÷ 学分之和」,这个逻辑在毕设答辩里经常被问到。
-- 计算某学生所有课程的加权平均分 SELECT s.student_id, SUM(sc.score * c.credit) / SUM(c.credit) AS weightedAvg FROM score sc JOIN course c ON sc.course_id = c.id WHERE sc.student_id = #{studentId} GROUP BY sc.student_id;逻辑说明:SUM(score * credit)是加权总分,除以SUM(credit)得到加权平均。参数说明:如果某学生有缺考记录(score 为 NULL),SUM会跳过 NULL,但SUM(credit)不会,导致分母偏大、结果偏低。解决办法是用IFNULL(sc.score, 0)把缺考当 0 分,或者在 WHERE 里过滤掉 NULL 记录。这个细节答辩时能讲出来,说明你真跑过数据。
4.3 成绩录入的批量插入和事务控制
成绩录入页通常是一次提交一个班几十条记录,逐条 insert 太慢,用批量插入。
<!-- ScoreMapper.xml 批量插入 --> <insert id="batchInsert" parameterType="java.util.List"> INSERT INTO score (student_id, course_id, score, exam_type) VALUES <foreach collection="list" item="item" separator=","> (#{item.studentId}, #{item.courseId}, #{item.score}, #{item.examType}) </foreach> </insert>逻辑说明:<foreach>把 List 拼成多条 VALUES,一次网络往返搞定。参数说明:collection="list"是默认名,如果 Mapper 方法参数用了@Param("scores"),这里要改成scores。Service 层必须加@Transactional,否则批量插入中途失败会留下半截数据。注意 MySQL 默认max_allowed_packet是 4MB,一次插几千条可能超限,分批 500 条提交比较稳。
5. 避坑与排查:源码跑不起来时先查这五个地方
这一章是我自己复现这类项目时踩过的坑,按「现象 → 原因 → 解决」列出来,你遇到问题可以对照着查。
5.1 现象:启动报NoClassDefFoundError: javax/servlet/ServletContext
原因:Servlet API 的 scope 配成了compile,被打进 war 包后和 Tomcat 自带的冲突。解决:在 pom.xml 里把javax.servlet-api的<scope>改成provided,Tomcat 容器会提供这个类。
5.2 现象:前端页面能打开但所有接口返回 401 或 302
原因:后端配了拦截器做登录校验,但前端登录后没把 token 或 session 带上。解决:检查 axios 请求拦截器有没有加Authorization头,或者后端拦截器有没有放行/login和静态资源路径。如果是 session 方案,确认axios.defaults.withCredentials = true且后端 CORS 配了allowCredentials(true)。
5.3 现象:MyBatis 查询返回的字段名全是下划线,前端拿不到驼峰字段
原因:数据库字段是student_no,Java 属性是studentNo,MyBatis 默认不自动转换。解决:在applicationContext.xml的SqlSessionFactoryBean里加<property name="configuration"><bean class="org.apache.ibatis.session.Configuration"><property name="mapUnderscoreToCamelCase" value="true"/></bean></property>,或者在 MyBatis 全局配置里开mapUnderscoreToCamelCase。
5.4 现象:成绩统计页数据对不上,平均分总是差零点几
原因:score字段用了FLOAT,累加时浮点误差被放大。解决:把字段类型改成DECIMAL(5,2),Java 侧用BigDecimal接收,SQL 里AVG结果用ROUND(AVG(score), 2)保留两位。
5.5 现象:npm run build打包后部署到服务器,刷新页面 404
原因:Vue Router 用了 history 模式,服务器没配回退到index.html。解决:Nginx 里加try_files $uri $uri/ /index.html;,或者把路由模式改成 hash 模式(URL 带#),后者不需要服务端配合,毕设演示更省事。
6. 把源码变成自己的东西:二次开发和答辩演示的实用技巧
拿到源码跑通只是第一步,真正让这份 SSM+Vue 学生成绩管理系统在答辩里站住脚的,是你能说清楚哪些是你改的、为什么这么改。我一般会做三件事:第一,把默认的登录密码加密从明文改成 MD5 加盐,在 Service 层加一个PasswordUtil,答辩时演示数据库里看不到明文;第二,给成绩统计页加一个导出 Excel 的按钮,用 Apache POI 写,代码量不大但演示效果直观;第三,把applicationContext.xml里的数据库密码抽到jdbc.properties,用<context:property-placeholder>加载,体现配置分离意识。
验证改动是否生效有个笨办法但很管用:改完一处就在浏览器里走一遍完整流程,从登录到录入到统计到导出,别攒一堆改动再一起测,否则出问题定位成本翻倍。另外,源码里的注释如果太少,建议你按自己的理解补一遍,答辩老师翻代码时看到有注释的模块,印象分会高不少。录像部分如果是要提交演示视频,提前把数据准备好,别现场录的时候发现某张表是空的。
最后说个习惯:每次改完一个功能,用git commit留一条记录,写清楚改了什么、为什么改。答辩前如果被问到「这个功能是你自己写的吗」,你能直接翻提交历史,比空口说强得多。这套 SSM+Vue 的组合虽然不算新技术,但把传统分层和前后端分离的对接细节吃透,对你后面转 Spring Boot 或者看 MyBatis-Plus 的源码都有帮助。希望帮到你。
本文还有配套的精品资源,点击获取