资讯动态

SSM+Vue高校学生综合素质评价系统:从登录权限到学分联动开发实践

发布时间:2026/10/18 10:43:19 来源:尧图企业网站定制
简介这是一套基于SSM框架与MySQL的高校学生综合素质评价系统完整源码包面向高校计算机相关专业毕业设计、课程设计及需要快速搭建管理系统的开发者。系统角色覆盖管理员、学生、教师核心功能包括登录注册、个人中心、修改密码以及学生管理、教师管理、专业信息管理、报考信息管理、成绩信息管理、获奖信息管理、学分信息管理、学生评价管理等内容后端采用SpringSpringMVCMyBatisMaven前端基于VueCSSJS构建兼容JDK1.8及以上与MySQL5.5及以上环境。资源包共434个文件约10.69MB其中包含103个Java源文件、42个Vue前端组件、162个SVG图标、19个XML配置、19个JS脚本、2个SQL数据库脚本以及HTML、CSS、属性文件、Maven配置和开发文档等源码与数据库脚本齐全目录结构清晰便于二次开发或直接部署。目前已有127人学习下载。所有代码经过严格调试可在IDEA或Eclipse中直接运行随包附带的开发文档与配置说明能帮助初学者理解SSM框架整合流程和前后端分离的项目组织方式是完成毕业设计或巩固Java Web开发技能的实用参考。1. 项目定位SSMVue把综合素质评价从纸质表搬进系统如果你在一所高校里处理过学生综合素质评价大概率经历过这样的场景成绩单在教务系统里获奖证书在辅导员手里学分记录在另一张Excel表到最后评审时要把所有材料汇总成一份评价表。这个基于SSM框架mysql的高校学生综合素质评价系统解决的就是这个“数据收拢”问题。它把管理员、教师、学生三个角色放进同一套流程覆盖登录注册、专业和报考信息维护、成绩与获奖管理、学分核算、学生评价这些环节是一套功能完整、可以直接跑起来的SSMVue全栈项目。对需要完成课程设计或者想学习SpringMVCMyBatis分层开发的人来说它的代码结构比普通增删改查Demo完整又比企业级微服务体系更容易读透。2. 技术栈与整体架构SSM后端和Vue前端是怎么拼起来的这套系统虽然叫 SSM 框架但并不是把所有代码都塞进Servlet的旧式写法。后端按 SpringMVC MyBatis 的标准分层组织Spring 统一管理对象SpringMVC 负责接收 HTTP 请求MyBatis 负责把 Java 对象和 MySQL 表互相映射。前端则是独立的 Vue 工程通过 axios 调后端接口构建成静态文件后放到和前端一样的服务器目录下。2.1 后端分层Controller-Service-Mapper的边界打开源码你会看到典型的包结构controller、service、mapper有的项目叫 dao再加上 entity/pojo 放数据库表对应的实体类。我第一次读这种项目时会习惯先从 controller 找入口因为一个请求从哪进来、需要哪些参数基本都写在 controller 里。比如“专业信息管理”这个模块的查询入口大致长这样RestController RequestMapping(/api/major) public class MajorInfoController { Resource private MajorInfoService majorInfoService; GetMapping(/list) public Result list(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer limit) { PageInfoMajorInfo pageInfo majorInfoService.queryPage(page, limit); return Result.ok(pageInfo); } }这里 Resource 是 Spring 的依赖注入MajorInfoService 是业务接口。Controller 里只做了三件事接收 page 和 limit 参数、调用 Service、把结果包成 Result 对象返回。很多初学者容易犯的问题是把 SQL 拼接逻辑写在 Controller一旦后面要加权限、加缓存整个接口会变得没法维护。Result.ok 是常见的统一响应包装里面包含 code、msg、data 三个字段前端拿到后先判断 code 再渲染数据。我一般会把分页查询的真实 SQL 放到 Mapper 层。对应的 Mapper 接口如下public interface MajorInfoMapper { ListMajorInfo selectPage(Param(offset) Integer offset, Param(limit) Integer limit); int count(); }对应的 XML 里写一条 selectPage 的 SQLoffset 和 limit 作为分页参数。注意这里没有在 XML 里写死页码而是由 Service 传进来这样做的好处是以后接 PageHelper 插件也只需要改一处。如果项目里的分页用的是 PageHelper其实连 offset 都不用自己算但你得知道底层执行的是LIMIT ?, ?。另外要提醒MyBatis 的#{}是预编译参数千万不要用${}直接拼接字符串否则开发阶段没什么感觉上线之后就是 SQL 注入漏洞。2.2 前端Vue和SSM后端的数据交互前端项目里能看到 IndexAsideStatic.vue.bak、IndexHeader.vue.bak、BreadCrumbs.vue.bak 这类文件.bak 是备份文件所以实际使用到的布局组件应该是对应的 .vue 文件。这是一个典型的管理后台布局左侧菜单、顶部标题栏、中间内容区。页面里的数据请求不会直接写 fetch而是统一走 axios 封装实例。开发环境下最常见的配置是代理vue.config.js 里大概是这样module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } }这段配置的意思是当前端页面请求 /api/major/list 时vue 的开发服务器会把请求转发到 http://localhost:8080 这个后端地址同时用 pathRewrite 把路径里的 /api 去掉实际后端收到的是 /major/list。这个重写非常关键我第一次跑这种前后端分离项目时就因为没加 pathRewrite后端接口收到带 /api 的路径直接 404。如果后端和前端部署到同一台服务器更稳妥的办法是前端 build 之后把 dist 目录复制到 Tomcat 的 webapps/ROOT这样同源就不会有跨域问题。项目里的 3-build.bat 应该做的就是这件事执行 npm run build然后把产物输出到指定目录。1-install.bat 和 2-run.bat 从命名看分别是安装依赖和启动服务你打开看一眼内容就能确认。2.3 数据库表结构角色、专业、评价是三条主线数据库脚本是这个项目里比较值钱的部分因为它不是简单的两张表而是按“基础档案—业务记录—评价结果”三层建模的。我把它拆成三类表分类表名示例作用关键字段账号表t_admin / t_student / t_teacher三类登录账号id, username, password, name基础信息表t_major / t_apply专业与报考记录id, major_name, student_id, status业务记录表t_score / t_award / t_credit / t_evaluation成绩、获奖、学分、评价student_id, teacher_id, score, content这个表设计有一个思路值得学习成绩、获奖、学分、评价这四类业务数据表都带 student_id相当于以学生为主键把各种表现串联起来。最终评价学生的时候并不需要去对四张表做复杂 join而是先在这四张子表里各自维护好数据再按学生编号汇总。这种设计虽然多写几个 Mapper 方法但每张表的变更互不影响教师在录成绩时只需要关心成绩表辅导员上传获奖信息时只需要关心获奖表。管理员端要做的“学生评价管理”本质上就是把这些子表的数据按学生维度汇总成一条评价记录。而专业信息和报考信息则更像基础档案学生注册时先选专业报考时再关联专业表这样教务在维护专业名称时不会出现“同一专业写了三个叫法”的情况。如果你以后接手类似系统建议先花半小时把数据库表relationship画出来再读代码很多疑问会直接消失。3. 核心模块拆解登录、注册与权限控制登录、注册和权限控制是这套系统的入口也是很多毕设项目最容易祖传“漏洞”的地方。这一块代码不算难但涉及角色区分、Session 状态、拦截器三个坑值得单独拆开聊。3.1 登录接口的实现细节登录是三个角色共用的入口但不同角色登录后看到的菜单不一样。后端一般在 login 接口里接收 username、password 和 role先判断当前账号属于哪张表再比对密码。因为三张表的结构不完全相同常见做法是在 Service 里按角色类型去查不同表。Controller 里的逻辑大概是这样PostMapping(/login) public Result login(RequestBody LoginParam param, HttpSession session) { String username param.getUsername(); String password param.getPassword(); String role param.getRole(); // student / teacher / admin Object user null; if (student.equals(role)) { user studentService.login(username, password); } else if (teacher.equals(role)) { user teacherService.login(username, password); } else if (admin.equals(role)) { user adminService.login(username, password); } if (user ! null) { session.setAttribute(loginUser, user); session.setAttribute(role, role); return Result.ok(user); } return Result.fail(用户名或密码错误); }这段代码把“用户存到 Session”这个动作放到了登录成功后后面拦截器就可以直接从 Session 里取登录状态。前端传的 role 必须和后端约定的字符串一致否则用户明明在教师表里存在却因为传了 student 去查学生表最后提示密码错误。这种问题排查起来很恶心我一般会在前端登录表单里做成下拉框而不是让用户手输角色。密码比对通常放在 Service 里项目里如果是快速开发版本大概率存的是 MD5 值。MD5 本身不是安全方案但因为是毕设或内部系统很多人都这么用。如果你要上线至少换成 BCryptPasswordEncoder改动面不大注册时加密登录时用一个matches方法校验。3.2 学生/教师注册的代码路径注册功能学生和教师都有实现路径类似。学生注册时除了用户名密码还要填专业、班级、学号教师注册要填工号和所属教研室。Controller 里先校验用户名是否存在再插入对应表。学生注册的代码很像用户注册的标准模板PostMapping(/register) public Result register(RequestBody StudentRegisterParam param) { Student student new Student(); student.setUsername(param.getUsername()); student.setPassword(DigestUtils.md5DigestAsHex(param.getPassword().getBytes())); student.setStudentNo(param.getStudentNo()); student.setMajorName(param.getMajorName()); student.setClassName(param.getClassName()); int count studentMapper.checkUsernameExists(param.getUsername()); if (count 0) { return Result.fail(用户名已被注册); } studentMapper.insert(student); return Result.ok(注册成功); }这里的 StudentRegisterParam 是前端通过 JSON 传过来的请求体里面至少包含 username、password、studentNo、majorName、className。注意checkUsernameExists在插入前执行是防重名的第一道防线。如果你在二次开发时加了学号字段还应该再检查 student_no 是否重复否则会出现一个学号注册多个账号的脏数据。省略密码加密、参数校验等步骤我实际写的时候还会在 Service 层做一个事务先查重再插入避免并发注册时两个请求同时通过查重。加了 Transactional 之后哪怕插入阶段报错之前的查询状态也不会有副作用。3.3 基于拦截器的权限校验登录之后不是所有接口都能访问比如学生不能去调“删除获奖信息”的接口。SSM 项目没有 Spring Security 时最直接的做法是写一个 HandlerInterceptor在 preHandle 里判断 Session。代码核心部分public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); Object loginUser session.getAttribute(loginUser); if (loginUser null) { response.setStatus(401); return false; } String role (String) session.getAttribute(role); String uri request.getRequestURI(); if (uri.contains(/admin/) !admin.equals(role)) { response.setStatus(403); return false; } return true; } }然后在 SpringMVC 配置文件里注册拦截器并配置不需要拦截的路径比如 /api/login、/api/register。这样权限控制就收敛到一层不用在每个 Controller 里重复判断。要注意的是如果前端使用跨域请求Session 依赖 Cookie必须让 axios 配置withCredentials: true否则每次请求都不会带 Session拦截器永远认为你是匿名用户。这个问题我在第5章的老坑里还会再提。个人中心和修改密码这两个功能其实就是从 Session 里取出当前用户 id再把表单里的新密码 update 到对应表。看起来简单但要注意修改密码后不能把 Session 里的旧用户对象丢掉否则用户改完密码重新请求一次Session 里存的还是旧密码明文拦截器放行照样不影响但两次改密之间会出现数据不一致的错觉。4. 核心业务模块实战成绩、获奖、学分与学生评价系统里真正有业务深度的部分是成绩、获奖、学分和学生评价之间的联动。表面上这是四张独立的表实际使用时它们都会被汇总到学生的综合素质评价结果里。这一章我会选三个典型实现来讲成绩查询的多表关联、获奖到学分的自动加分、评价表的建表设计。4.1 成绩信息管理的查询与统计成绩信息管理是综合素质评价的核心数据来源。一个学生可能会有多门课程成绩管理员或教师需要按学号、姓名、课程名称筛选。查询接口通常返回的是成绩表和基础信息关联后的结果而不是裸的成绩表。Service 调用 Mapper 的代码如下public ListScoreVO queryScoreList(String studentNo, String courseName) { return scoreMapper.selectScoreList(studentNo, courseName); }对应的 Mapper XMLselect idselectScoreList resultTypeScoreVO SELECT s.id, s.student_id, st.student_no, st.name AS student_name, s.course_name, s.score, s.semester FROM t_score s LEFT JOIN t_student st ON s.student_id st.id where if teststudentNo ! null and studentNo ! AND st.student_no #{studentNo} /if if testcourseName ! null and courseName ! AND s.course_name LIKE CONCAT(%, #{courseName}, %) /if /where ORDER BY s.semester DESC /select这里LEFT JOIN t_student是为了查出学生姓名和学号前端列表可以直接展示不需要再二次请求。where标签是 MyBatis 动态 SQL 的经典用法它可以自动去掉第一个条件前面的 AND如果两个参数都为空就变成一个全表查询的 SELECT。全表查询在数据量小的内部系统里没问题但如果你管理的是全校数据建议强制加上学期参数或者分页否则一次把几万条成绩加载到前端浏览器会卡住。LIKE 查询这里要注意LIKE CONCAT(%, #{courseName}, %)是正确写法。如果直接写%#{courseName}%MyBatis 会把 #{} 当作预编译占位符处理但因为它在字符串字面量里实际执行可能报错或者查不到结果。我第一次写 MyBatis 动态模糊查询时就在这里翻过车。4.2 获奖信息与学分的联动获奖信息通常会影响学分。比如校级一等奖加 2 学分国家级一等奖加 5 学分。在设计上获奖表里会有 award_level、award_type 字段学分表里则记录加分的来源和数量。添加获奖记录时后端可以自动往学分表插一条记录这是最常见的“积分联动”写法Transactional public void addAward(Award award) { awardMapper.insert(award); Credit credit new Credit(); credit.setStudentId(award.getStudentId()); credit.setSourceType(Award); credit.setSourceId(award.getId()); credit.setCreditValue(calcCredit(award.getAwardLevel())); creditMapper.insert(credit); } private int calcCredit(String level) { if (国家级.equals(level)) return 5; if (省级.equals(level)) return 3; if (校级.equals(level)) return 2; return 1; }这里 Transactional 保证两个 insert 同时成功或同时失败避免出现获奖记录有了但学分表没加分的脏数据。calcCredit 方法里的加分规则目前是写死的如果你想把它变成可配置第一反应可能是加 if else但更好的做法是在数据库里建一张“加分规则表”存奖项级别和对应学分然后在代码里查询这张表。具体改造思路我会在最后一章展开。addAward 里有一个隐藏细节award.getId()必须先拿到数据库自动生成的主键。MyBatis 中需要在 insert 语句配置useGeneratedKeystrue和keyPropertyid否则插入后 id 还是 null后面的 credit 表 source_id 就关联不上。这是很多人写联动业务时最容易漏的一步。4.3 学生评价模块的数据库设计学生评价模块可能是教师对学生的综合评价也可以包含学生自评。常见设计是 t_evaluation 表字段结构如下CREATE TABLE t_evaluation ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, teacher_id INT NOT NULL, evaluation_type VARCHAR(10) DEFAULT teacher, content VARCHAR(500), evaluation_score DECIMAL(5,2), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 0, FOREIGN KEY (student_id) REFERENCES t_student(id), FOREIGN KEY (teacher_id) REFERENCES t_teacher(id) );student_id 和 teacher_id 分别做外键evaluation_type 区分是教师评价还是学生自评。status 字段设计成 0 未审核、1 已审核由管理员统一管控。这里要注意表结构里的外键在生成数据库脚本时务必要保证 t_student 和 t_teacher 先建否则创建 t_evaluation 会因为外键找不到父表而报错。如果你从源码的数据库脚本里看到的字段比这个多也很正常比如加了学期、评价维度、总分等字段。学生评价的管理后台一般是一个列表管理员可以按学生姓名筛选点进去看到该生的成绩平均分、获奖加分、学分记录以及教师的评价内容。这种汇总不需要在页面端做复杂计算而是后端在打开详情时分别调用四个 Mapper 查询最后组装成一个详情 VO。虽然看着多查了四次但对中小并发系统来说比写一个巨型 join 更好维护。5. 项目运行与常见问题排查避坑指南拿到资源包之后最大的坎不是代码难而是环境不一致。原项目里带着 Eclipse 的工程文件也带着 .bat 脚本但你手上可能已经换了 IDEA数据库可能是 MySQL 5.7 也可能是 8.0。这一章把我认为最值得注意的运行步骤和踩过的坑列出来。5.1 从解压到跑通的完整环境准备先把环境版本对齐JDK 1.8 及以上Maven 3.6 左右MySQL 5.5 及以上前端用 npm 装依赖。后端用 IDEA 或 Eclipse 都行但最大前提是不要直接双击源码里的 .classpath 导入那样 Maven 会识别不完整。推荐的启动顺序是新建一个空数据库用数据库工具执行项目里提供的 .sql 脚本把表和基础数据导入。在 IDEA 里选择 Import Project 或 File - New - Project from Existing Sources选中 pom.xml用 Maven 方式导入。修改数据库配置文件通常是 properties 或 xml 里的 jdbc.url、jdbc.username、jdbc.password。先启动后端确认 Tomcat 端口没有被占用接口能访问。再进入前端工程目录执行 npm install然后 npm run dev。如果项目里提供了 1-install.bat、2-run.bat、3-build.bat可以打开看内容它们通常是帮你把前面的步骤封装成双击执行。关于 .bat 脚本我的习惯是不要直接双击先看一遍内容。因为脚本里可能写死了路径比如cd /d E:\project在你电脑上就不存在。把它当成提示比当成一键启动工具更稳妥。5.2 我踩过的几个坑坑1数据库连不上现象后端启动时报Access denied for user rootlocalhost。原因配置文件里的密码和本地 MySQL 不一致或者账号不是 root再或者 MySQL 8.0 的驱动版本和项目里的 cj 驱动不匹配。解决核对 jdbc.url / username / passwordurl 后面加上useUnicodetruecharacterEncodingutf8如果是 MySQL 8.0确认 pom.xml 里的 mysql 驱动版本是 8.x。坑2Maven 依赖下载慢或失败现象IDEA 导入后一直报依赖找不到或者 build 时下载卡住。原因Maven 默认的中央仓库在国外网络不稳定也可能是项目用的 JDK 版本和编译级别不一致。解决在 settings.xml 里配置阿里云镜像把项目 maven.compiler.source 和 target 改成 1.8然后重新加载。坑3Mapper 接口找不到现象启动报org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)。原因Mapper XML 没有被扫描进去或者 XML 里的 namespace 和接口全限定名对不上。解决确认 Spring 配置里的mapperLocations指向classpath:mapper/*.xml再看 XML 的 namespace 是否和接口包名一致。这个问题常常发生在从 Eclipse 迁移到 IDEA 之后因为资源文件没有被拷贝到 target/classes。坑4前端请求接口 404现象页面能打开但所有请求都返回 404。原因vue.config.js 里代理配了/api但后端接口没有/api前缀且没有配置 pathRewrite。解决在 proxy 里加pathRewrite: { ^/api: }或者让后端把项目访问路径加上/api前缀两者选其一不要两边都配。坑5Session 失效现象登录后调用其他接口返回 401但后端日志里能看到请求打到了接口上。原因前后端端口不同跨域请求没有携带 CookieSession 形同虚设。解决前端 axios 实例设置withCredentials: true后端 CORS 配置里allowCredentials(true)。如果还不行检查浏览器控制台里 Cookie 的 SameSite 属性Chrome 对跨域 Cookie 限制很严格可能需要开发环境用代理来规避。5.3 从Eclipse迁移到IDEA的注意点因为项目里带着 .classpath、.project 这类 Eclipse 文件直接拿 IDEA 打开很可能会被识别成普通文件夹而不是 Maven 工程。正确做法是忽略这些文件直接用 pom.xml 导入。导入后检查 Project Structure 里的 Project SDK 和 Modules 语言级别统一设为 1.8。另外特别注意Eclipse 的编译输出目录是 build/classesIDEA 默认是 target/classes。迁移后如果启动时找不到 XML 或 properties先执行一次 mvn clean compile看 target/classes 里有没有把 resources 目录下的 xml 文件带过来。没有的话就在 pom.xml 的 build 节点里配置 resources 资源过滤。这种问题表面看着像代码错误其实是构建工具差异导致的。6. 二次开发把固定评价改成可配置的指标模板如果你觉得 t_evaluation 表里只存一个 content 字段太单薄想支持“思想道德、学业成绩、创新能力、身体素质”这类多维度评价最直接的改造思路是引入指标模板表。原来的评价表不动在它旁边加两张表一张存模板一张存模板明细CREATE TABLE t_evaluation_template ( id INT PRIMARY KEY AUTO_INCREMENT, template_name VARCHAR(50), total_score DECIMAL(5,2), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE t_evaluation_item ( id INT PRIMARY KEY AUTO_INCREMENT, template_id INT NOT NULL, item_name VARCHAR(50), max_score DECIMAL(5,2), weight DECIMAL(3,2), FOREIGN KEY (template_id) REFERENCES t_evaluation_template(id) );改造后教师在评价学生时不需要再直接填一段文字而是选择一个模板前端根据模板动态渲染多个评分项。后端保存评价结果时可以逐项打分并写入明细表最后按权重汇总出总分。这种方式比把多个字段硬编码在 t_evaluation 表里扩展性好得多以后学校说要增加一个“公益服务”维度只需要在后台加一条指标记录不用改 Java 代码。具体代码层面只需要增加一个 EvaluationTemplateController提供getTemplateList和getTemplateItems两个查询接口。原来的评价提交接口里加一个 templateId 参数Service 里遍历明细表逐个保存再把加权总分写入 evaluation_score。验证方法很简单建两套不同权重的模板同一学生的同一组分数评价结果应该按模板不同而不同。如果还要更灵活可以把 weight 改成表达式比如“0.3score 0.7avg_score”但那样就是引入规则引擎了学生评价系统暂时没必要。从我自己的经验看这类改造一定要先改数据库脚本再改后端接口最后动前端页面。顺序反了会导致前端接口联调时拿不到新字段你又得回过去补后端的 VO。从那以后我每次改需求都强制走一遍“先画表结构、再定义接口、再写页面”的流程评价模块重构时只花了一下午就上线。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑