☰
基于SpringBoot+Vue+MySQL的奖学金评定管理系统实战
2026/10/17 7:15:28 网站建设 项目流程

每年奖学金评定季,都是高校学生工作和教务管理最容易出乱子的阶段。学生抱着一摞获奖证书和成绩单排队交材料,辅导员在Excel里一遍遍核对分数和名额,学院教务员把各班的排名表合并汇总,中间只要有一个分数填错或者名额超了,就要从头再对一遍。我最近完整跑通了一套基于SpringBoot + Vue + MySQL的奖学金评定管理系统,从建库、启动后端、启动前端到走通"学生申报-辅导员打分-学院审核-学校公示"全流程,前后花了一个周末。这篇文章就把这套系统的技术栈选型、数据库设计、后端核心逻辑、前端组织方式、直接运行的完整步骤,以及我实测中踩到的坑一次讲清楚。不管是想拿来做课程设计参考,还是准备毕业设计,或者单纯想看看一个前后端分离的管理系统是怎么把真实业务落地的,这篇内容都能给你实打实的参考。

1. 奖学金评定管理的真实痛点与系统解决思路

1.1 手工评定流程为什么容易出问题

在聊技术之前,得先理解这个系统到底在解决什么。奖学金评定不是一个简单的"按分数排名取前N名"的过程,它涉及的环节相当多:学生要先提交申请,附上成绩单、获奖证明、参与活动记录等材料;辅导员要组织班级评议,逐项核对学生的综合测评分数;学院要按专业、按年级分配名额并审核排名;学校汇总后还要公示,公示期内有人提出异议,又得重新核对。

手工模式下,最常见的问题有三类。第一类是材料管理混乱,纸质证明到处传,弄丢一份学生就得补办一次;第二类是分数核对特别耗时,一张综测表几十个评分项,用Excel公式连表拉数据,一个单元格引用错了,排名就全错了;第三类是过程不可追溯,评定结束后如果有人问"为什么这个学生没评上",连当时的得分明细和审核记录都找不全。

1.2 系统设计要覆盖的核心业务链路

这套奖学金评定管理系统,本质上是把上面这条手工链路线上化、流程化。系统的核心业务链路可以概括为三条:

  • 申报链路:学生在线填写申请,上传佐证材料,选择要申报的奖学金类型,系统自动带出学习成绩等基础数据。
  • 评定链路:辅导员或班主任按评分项逐项录入分数,系统按权重计算综合分,再按班级或专业自动排名,结合名额生成拟推荐名单。
  • 审批与公示链路:学院管理员审核名单,校级管理员做最终审批,通过后发布公示信息,学生可以查看结果并提出异议。

三条链路串起来,就形成了"申请-评定-审核-公示-归档"的完整闭环。这也是这套系统后端接口设计和前端页面组织的主线逻辑,后面讲数据库和代码的时候,都会反复回到这条主线上。

1.3 角色权限是怎么划分的

这套系统里角色的划分很典型,基本对应高校学生工作的真实组织架构。权限设计得好不好,直接决定了系统能不能落地。常见的角色包括:

角色核心权限典型操作
学生申报与查询提交申请、上传材料、查看自己的分数与评选结果
辅导员/班主任班级评定查看本班学生申请、录入评分、确认班级排名
学院管理员学院审核审核班级报送的名单、统筹院内名额、导出汇总表
校级管理员系统管理配置奖学金类型与名额、发布公示、管理用户、查看全局统计

后端在做权限控制的时候,一般就是围绕这四类角色来做接口级别的访问控制。学生不能看到别人的评分明细,辅导员只能操作自己带的学生,校级管理员能改奖学金名额配置但不能帮某个学生打分,这些限制都是靠角色和接口的绑定实现的。后面讲后端代码结构的时候,我会专门说一下怎么用拦截器做这层控制。

2. 技术选型分析:为什么是SpringBoot + Vue + MySQL

这一节先说结论:技术选型没有唯一正确答案,但SpringBoot + Vue + MySQL这套组合,对奖学金评定这种典型的管理信息系统来说,是性价比最高的方案。下面拆开讲每个选型的理由。

2.1 SpringBoot在后端的角色

SpringBoot解决的是后端开发中最繁琐的配置问题。早期的SSM项目要写大量XML配置,不同的Spring版本之间还有兼容性坑,光是搭环境就能卡住一周。SpringBoot通过自动配置和约定大于配置的方式,把环境搭建的工作量压到很低,一个注解就能启动内嵌容器,写好的业务代码不需要额外部署Tomcat,直接java -jar跑起来。

对学生申请、辅导员打分、管理员审核这类CRUD密集型业务,SpringBoot自带的Spring MVC + MyBatis或Spring Data JPA已经覆盖了绝大多数需求。而且Spring生态的文档和社区案例特别多,哪怕是第一次接触Java后端的同学,遇到报错也容易搜到解决方案。奖学金评定系统的难点在业务流程而不是在并发性能上,SpringBoot这种成熟稳定的框架反而是最稳妥的选择。

2.2 Vue在前端的价值

奖学金评定系统的前端交互有一个很明显的特点:表单多、列表多、状态多。学生端要填申请表单、传文件;辅导员端要在一张大表格里给几十个学生打分;管理端要切换不同类型的审批列表。这种界面用传统JSP时代的服务端渲染来实现,每点一次按钮就要刷新一次页面,体验很差,开发效率也低。

Vue作为前端框架,核心优势就是用组件化的方式组织界面,配合Element UI或Element Plus这样的组件库,表格、弹窗、表单、日期选择器、分页器全都开箱即用。一个管理后台页面,本质上就是"表格 + 筛选栏 + 操作按钮 + 弹窗表单"的组合,用Vue写起来非常顺手。再加上Vue Router做前端路由控制、Axios做接口请求,整个前后端分离的开发模式很清晰,一个开发者完全能独立拿下来。

2.3 MySQL为什么够用

MySQL在这套系统里负责存所有业务数据。奖学金评定系统的数据特征非常明确:结构化、强关联、事务性强。学生信息和班级学院之间有外键关系,申请单和评分明细之间是一对多关系,每次审核都要产生一条记录,这些都需要事务来保证数据一致性。

举个例子,辅导员给一个学生的综测打分,要同时更新评分明细表和申请单的总分字段,如果只更新了一个表、另一个表更新失败,前后的数据就对不上了。MySQL的事务机制能保证这组操作要么全部成功、要么全部回滚。这类需求用NoSQL反而要自己管理事务一致性,MySQL天然就支持,所以关系型数据库在这个场景是最合适的选择。而且MySQL免费、跨平台、资料多,作为课程设计和毕设项目的数据层,完全不用花额外成本。

2.4 这套组合对课程设计与毕业设计的适配度

我见过不少学生项目,有人选前后端不分离的JSP+Servlet,有人选Python Flask,有人甚至用纯Java Swing做桌面端。对比下来,SpringBoot + Vue + MySQL有几个很现实的好处:

  • 技术栈主流,毕业后写在简历上是有分量的;
  • 前后端分离更贴近真实企业项目的工作方式;
  • 工程量和难度可调,既适合单人完成,也能拆成小组协作;
  • 演示效果好,网页端操作直观,答辩时不用拿着命令行工具演示。

对比其他方案:

方案开发效率学习成本演示效果简历含金量
Servlet + JSP低,前后端耦合低一般一般
SSM + JSP中,仍需配置多中一般中
SpringBoot + Vue + MySQL高中好高
Django + Vue中中好中

从这个表能看出来,SpringBoot + Vue + MySQL不是哪个维度都最强,但它是综合分最高的选择,这也是大量管理系统类课程设计和毕设项目都选这套组合的原因。

3. 数据库设计拆解:奖学金评定的地基怎么搭

3.1 核心表清单与关系

数据库设计是这类系统最见功力的部分。奖学金评定涉及的数据实体不算多,但表格之间的关系如果没理清楚,后面写业务逻辑会非常痛苦。这套系统的核心表大致有以下这些:

表名核心字段说明
sys_userid, username, password, role, student_no登录账号表,学生账号关联学号
student_infoid, student_no, name, gender, college, major, class_name, entry_year学生基本信息表
scholarship_typeid, type_name, amount, total_quota, criteria, apply_start, apply_end奖学金类型及名额配置
applicationid, student_no, scholarship_id, status, total_score, apply_time申请单主表,记录申报状态和综合分
eval_itemid, item_name, weight, max_score评定项目配置表,如学习成绩、德育、文体
eval_scoreid, application_id, item_id, score, evidence_url评分明细表,每个申请对应多条评分
review_logid, application_id, operator, action, comment, review_time审核记录表,全流程留痕
announcementid, title, content, publish_time公示信息表

这里面的关键设计有三个。第一,sys_user和student_info分开,账号信息和学生档案解耦,这样管理员账号不用硬造一个学生档案也能存在。第二,application和eval_score拆成主表和明细表,是因为一个申请单的评分项数量是不固定的,如果全塞在一个表里,要么预置一堆冗余列,要么就要改表结构,拆表是标准做法。第三,review_log单独成表,目的是让每一步审核操作都有据可查,公示期有异议时可以直接调出完整的审批链。

3.2 申请单的状态机设计

申请单的status字段是这套系统里最重要的字段之一,它记录的是申请单从创建到结束的完整生命周期。一套合理的状态流转大概是:

  • 草稿:学生保存了但还没提交,可以随时修改。
  • 已提交:学生确认提交,辅导员可见,学生不能再改材料。
  • 班级评定中:辅导员正在逐项打分,总分和排名还没最终确定。
  • 学院审核中:班级评定结束,名单提交给学院管理员审核。
  • 校级审批中:学院审核通过,校级管理员做最终把关。
  • 已公示:审批通过,系统生成公示信息,学生可见。
  • 已驳回:某一级审核不通过,学生可以看到驳回原因,按条件可修改后重新提交。

状态机设计最大的好处是让业务流转变得可控。后端在处理每次状态变更时,只需要判断当前状态和下一个状态是否合法,比如"已提交"不能直接跳到"已公示",必须经过评定和审核环节。这个逻辑可以用一个简单的状态映射表来管理,比在代码里散落一堆if判断清晰得多。

3.3 评分项存储与权重配置的设计

评分项和权重的设计也值得单独说。很多管理系统项目会把评分项写死在代码里,比如score = score1 * 0.6 + score2 * 0.4。这样做的缺点是,不同学院对德育、文体、科研的权重要求不一样,一旦学校调整评定办法,就要改代码重新部署。

更好的做法是把评定项目放到eval_item表里,权重作为字段存起来。前端在录入分数时动态根据配置渲染评分项,后端计算总分时从数据库读取权重。这样不同学院的评定规则不同,只需要在配置层面加一个class级别的过滤条件就行,不用动代码。实战中如果要做二次开发,我建议优先把这块配置化做完整,性价比非常高。

4. 后端核心逻辑:SpringBoot项目结构与评定算法

4.1 项目分层与目录设计

拿到这套源码,先看项目结构。后端是一个标准的SpringBoot多模块分层结构,核心目录大致如下:

src/main/java ├── com.example.scholarship │ ├── controller # 接口层,接收前端请求 │ ├── service # 业务层,处理核心业务逻辑 │ ├── mapper # 数据访问层,MyBatis接口 │ ├── entity # 数据库实体类 │ ├── config # 配置类,如CORS、拦截器、MyBatis配置 │ ├── common # 公共类,如统一返回结果Result │ └── utils # 工具类,如JWT工具、Excel工具 src/main/resources ├── application.yml # 数据源、端口等配置 ├── mapper # MyBatis XML文件

这个分层很标准:Controller层只负责接收参数和返回结果,不写业务逻辑;Service层承载所有业务规则;Mapper层只做数据库交互。奖学金评定这种系统,业务逻辑不算复杂,但流程长,如果所有逻辑都堆在Controller里,后面改一个评定规则要翻很久代码。分层清晰的最大价值就是可维护性。

4.2 登录鉴权与角色控制的实现思路

系统的权限控制通常通过登录鉴权和接口拦截两层来实现。登录成功后,后端生成一个令牌返回给前端,前端每次请求都在请求头里带上这个令牌,后端通过拦截器解析令牌、识别当前用户和角色。

拦截器的核心逻辑大概是这样:先放行登录接口和静态资源,然后从请求头里取令牌,校验失败直接返回未登录的提示;校验通过后,把用户信息放入请求上下文,接口里就能直接用。角色控制则在接口层面做,比如辅导员评分的接口加了@RequiresRole("counselor")这类注解或者统一的权限判断,学生身份的请求就进不来。

这套设计在奖学金系统里特别关键,因为不同角色的数据边界很敏感。学生只能访问自己的申请记录,辅导员只能看到本班学生,学院管理员只能看本学院数据。在实际二次开发时,我建议对这类"数据可见范围"做统一的逻辑封装,不要在每个接口里重复写判断条件,否则很容易漏掉某个边界导致越权。

4.3 综合测评分数计算与排名算法

后端最核心的业务逻辑是综合测评分的计算。假设评定办法规定:综合分 = 学习成绩60% + 德育素质20% + 文体活动*20%,那后端在辅导员提交评分时,会遍历这个申请关联的所有评分项,按权重算出总分,再写回申请单的总分字段。核心代码大概长这样:

public BigDecimal calculateTotalScore(Long applicationId) { List<EvalScore> scores = evalScoreMapper.selectByApplicationId(applicationId); List<EvalItem> items = evalItemMapper.selectAll(); Map<Long, BigDecimal> weightMap = items.stream() .collect(Collectors.toMap(EvalItem::getId, EvalItem::getWeight)); BigDecimal total = BigDecimal.ZERO; for (EvalScore score : scores) { BigDecimal weight = weightMap.get(score.getItemId()); total = total.add(score.getScore().multiply(weight)); } // 保留两位小数,四舍五入 total = total.setScale(2, RoundingMode.HALF_UP); applicationMapper.updateTotalScore(applicationId, total); return total; }

排名和名额分配的逻辑也在这里面。辅导员打完分后,系统会按班级分组,对申请同一类奖学金的同学按综合分降序排列,再结合名额截取名单。这里有个容易踩坑的点:最后一名如果出现同分,就得有明确的判定规则,比如优先看学习成绩分,再不行看德育分。规则如果没做,最后公示的时候很容易有学生来质疑。

public List<Application> generateRecommendList(Long scholarshipId, String className) { List<Application> applications = applicationMapper .selectByScholarshipAndClass(scholarshipId, className, "班级评定中"); applications.sort((a, b) -> { int cmp = b.getTotalScore().compareTo(a.getTotalScore()); if (cmp != 0) return cmp; // 同分时再比较学习成绩项的得分 cmp = b.getAcademicScore().compareTo(a.getAcademicScore()); return cmp; }); ScholarshipType type = scholarshipTypeMapper.selectById(scholarshipId); return applications.subList(0, Math.min(type.getQuota(), applications.size())); }

这段逻辑看着简单,实际运行中要注意两点:一是名额可能小于申请人数,也可能大于申请人数,列表为空和人数不足的情况都要处理;二是这里生成的是"拟推荐名单",还没有真正通过审核,前端要明确标记这个状态。

4.4 Excel导出与公示处理

管理端一个高频操作是把评定结果导出成Excel,用于存档上报。SpringBoot里做这件事通常用POI或EasyExcel。POI功能强但API偏底层,EasyExcel封装更好、内存占用也更低。导出的时候要注意表头格式和单元格合并,尤其是"班级"和"奖学金类型"这类分类字段,导出的列要对齐,不然学校汇总的人拿到表还要自己调格式。

公示功能则相对简单:校级管理员确认名单后,系统把名单写入公示内容,前台页面展示出来,同时设置公示期起止时间。公示期内学生如果有异议,需要一个反馈入口,反馈信息存库,便于后续处理。这套源码里公示模块不一定都有异议反馈功能,但二次开发时完全可以加。

5. 前端页面组织:Vue如何撑起三个身份的工作台

5.1 路由结构与权限守卫

前端部分,Vue项目的结构一般长这样:

src ├── api # 接口请求封装 ├── assets # 静态资源 ├── components # 公共组件 ├── router # 路由配置 ├── store # 状态管理 ├── views # 页面组件 │ ├── login │ ├── student │ ├── counselor │ ├── college-admin │ ├── school-admin │ └── common ├── utils # 工具类,如请求拦截器 └── main.js

路由设计上,登录页是独立路由,其余页面按角色分目录组织。前端权限守卫的核心逻辑在路由前置守卫里:判断用户是否已登录,没有登录跳转到登录页;已登录但访问了不属于当前角色的页面,就拦截并提示无权限。大致的写法如下:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (!token && to.path !== '/login') { next({ path: '/login' }) } else if (token) { const role = store.state.user.role if (to.meta.roles && !to.meta.roles.includes(role)) { next({ path: '/403' }) } else { next() } } else { next() } })

5.2 学生端申报流程的交互设计

学生端的工作台设计逻辑很直接,核心是三个页面:申报页面、进度查询页面、个人信息页面。

申报页面是交互最复杂的部分。学生在页面上先选择要申报的奖学金类型,系统会根据该类型的时间设置判断是否在申报期内,不在申报期直接禁用按钮。提交申请时,要校验必填项和材料文件。材料上传这个环节特别容易被忽略,如果前端不做文件类型和大小限制,学生传一个几十兆的视频上来,后端处理起来就很头疼。规范做法是限制图片和PDF、单文件不超过10M,并且把文件存到独立的上传目录,数据库只存访问路径。

进度查询页面则完全围绕申请单的status展开。前端用标签展示当前状态,比如"班级评定中""学院审核中",每个状态旁边展示对应的审核记录,学生能看到辅导员、学院、校级的每一步操作时间和意见。这里用时间线组件展示会非常直观。

5.3 管理端审核与统计展示

辅导员和学院管理员的工作台,本质上是待办列表加操作弹窗的组合。列表展示本班或本学院所有申请,带筛选条件:奖学金类型、申报状态、学生姓名或学号。点击"打分"按钮弹出评分表单,表单里的评分项是从后端动态拉取的,每个项目一个数字输入框,输入框旁边显示权重和该项满分范围,超出范围前端直接拦截。提交打分后,列表行的状态随之更新,总分和排名自动刷新。

校级管理员的统计页面要多一点数据可视化。按学院统计的申报人数、按奖学金类型统计的获奖人数、各学院平均综合分的对比,这些都可以用ECharts的画柱状图或饼图来展示。统计页的意义不只是好看,校级管理员在调配名额时需要全局数据支撑,比如某个学院申报人数明显高于其他学院,就要考虑是否调整名额分配。

6. 直接运行的完整步骤:从建库到访问首页

这套项目号称可直接运行,但"可运行"是有条件的。下面从零开始走一遍完整步骤,照着做基本上半小时内能跑起来。

6.1 环境准备清单

先确认电脑上的环境版本,版本不匹配是"跑不起来"最常见的原因。

软件推荐版本说明
JDK1.8很多此类项目基于JDK8开发,版本过高可能有兼容问题
Maven3.6.x负责后端依赖管理
Node.js14.x前端依赖安装和构建工具
MySQL5.7或8.0数据库,导入SQL脚本
开发工具IDEA / VS Code后端用IDEA居多,前端VS Code够用

6.2 数据库初始化

第一步是创建数据库并导入SQL脚本。项目管理良好的一般会自带scholarship.sql文件。打开MySQL命令行或图形工具,执行:

CREATE DATABASE scholarship DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE scholarship; SOURCE /你的路径/scholarship.sql;

这里特别强调utf8mb4编码。如果数据库用了默认的latin1或者utf8,导入后中文全都变成乱码,前端页面显示出来全是"????",排查起来浪费大量时间。

6.3 后端启动步骤

数据库就绪后,用IDEA打开后端项目目录,等待Maven自动下载依赖。下载依赖时注意看IDEA右下角进度,如果一直失败,大概率是网络问题,需要把Maven中央仓库换到镜像源,在settings.xml里配置镜像。这个步骤很不起眼,但能卡住一大半人。

依赖下载完成后,先检查application.yml里的数据源配置:

server: port: 8081 spring: datasource: url: jdbc:mysql://localhost:3306/scholarship?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver

把密码改成自己本地的MySQL密码,然后运行启动类里的main方法。后端控制台出现Spring的启动日志,说明后端已经跑起来了。也可以打包成JAR运行:

mvn clean package -DskipTests java -jar target/scholarship.jar

6.4 前端启动与联调

后端起来后开前端项目。用VS Code打开前端目录,先安装依赖。这一步也容易踩坑,直接npm install经常会因为网络原因失败,建议先配置镜像源:

npm config set registry https://registry.npmmirror.com npm install

安装成功后启动开发服务器:

npm run serve

前端默认端口一般是8080,启动成功后在浏览器访问http://localhost:8080。前端请求后端接口时,通过项目里的代理配置把/api开头的请求转发到http://localhost:8081,这样就避免了浏览器跨域问题。打开登录页,用系统自带的测试账号登录,就可以看到不同角色的工作台了。

账号密码角色
admin123456校级管理员
college01123456学院管理员
counselor01123456辅导员
student01123456学生

提示:不同项目的默认账号密码不一样,具体看项目说明文档或SQL脚本里的初始化数据。如果登录报错,优先检查后端接口地址和数据库密码是否配置正确。

7. 我实际运行中踩到的坑与优化建议

7.1 MySQL驱动名与时区问题

这是后端启动最常见的报错位置。项目里如果用的是MySQL 5.7,驱动类名通常是com.mysql.jdbc.Driver;如果换成MySQL 8.0,驱动类名变成了com.mysql.cj.jdbc.Driver,同时连接串必须加serverTimezone=Asia/Shanghai,否则会报时区错误。很多项目源码里给的配置是按作者本地环境写的,你的数据库版本一不同,这里就要调整。遇到这个报错不要慌,先看驱动名和连接串,比去搜复杂解决方案快得多。

7.2 跨域与端口占用问题

前端启动后如果登录请求报跨域,多半是代理配置没生效或者后端CORS配置缺失。之前的做法是前端代理到后端,但如果前端项目里同时开启了后端的@CrossOrigin注解,代理和跨域双重作用下反而可能出现奇怪的问题。我的经验是:开发环境统一走前端代理,后端不额外开跨域;如果一定要开后端CORS,那前端就直接请求后端完整地址,不走代理。另外端口占用问题也很常见,前端8080、后端8081如果被占用,启动日志里会直接报端口冲突,换一个端口就行,但注意换了前端端口后代理配置里的目标端口也要一致。

7.3 并发重复申报与幂等性

学生在申报期截止前集中填报,如果系统没有做防重限制,同一个学生可能对同一奖学金类型提交多条申请,造成数据混乱。数据库层面对application表的学生学号和奖学金类型字段加一个唯一索引,是成本最低也最可靠的做法:

ALTER TABLE application ADD UNIQUE KEY uk_student_scholarship (student_no, scholarship_id);

同时,后端在提交申请接口里再加一道校验逻辑,查询该学生是否已经存在同一奖学金的"草稿"或"已提交"记录。两层防护都做了,基本就堵住了重复申报的问题。

7.4 前端依赖安装与Node版本问题

前端npm install报错是另一个高发问题。早期很多管理项目用的是node-sass,这个包的安装对编译环境要求很高,Node版本太新就会编译失败。如果遇到这个坑,最快的方法是换用sass这个包实现同样的功能,或者把Node降到项目要求的版本。安装依赖时如果下载缓慢,镜像源一定要提前配好,这是效率差距最大的一个环节。

7.5 二次开发方向的几点建议

最后聊一下拿到源码之后想继续扩展该往哪个方向走。我自己的体会是,这类系统做到"能跑通"只是第一步,真正有提升空间的是:

  • 把评定权重做成可配置的界面,不同学院的规则差异就不用改代码发布;
  • 公示期自动生成拟获奖名单的PDF或可打印页面,方便线下张贴;
  • 给综测佐证材料做在线预览,评审时不用下载文件逐个打开;
  • 增加简单的消息通知机制,申请被驳回、公示开始等关键节点能主动提醒学生;
  • 导出功能从POI替换成EasyExcel,数据量上来之后内存占用差别很明显。

我在实际运行这套系统时最大的感受是:管理信息系统的骨架其实都是相通的,申报、审核、留痕、公示这四个模块一旦跑通,换到其他评审类场景——比如助学金评定、荣誉称号评选、项目申报评审——只需要调整表结构和状态流程,就能快速复用。理解了这一点,这套源码就不再只是一个"能交差的项目",而是可以反复套用的业务开发模板了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询