资讯详情

从零搭建毕业设计成绩管理系统:SpringBoot+Vue+MyBatis实践

📅 2026/10/16 11:21:07 | 华诺云谱 👁 阅读
从零搭建毕业设计成绩管理系统:SpringBoot+Vue+MyBatis实践
又到了一年毕业季办公室里的导师们开始翻着Excel表格来回核对评阅成绩答辩秘书则抱着打印版评分表一份份誊抄。一个学生的总成绩要汇总指导老师评分、评阅老师评分、答辩小组评分三份数据中间只要有一个数字录错最后排名就会乱掉。我前后帮几个实验室搭过这种成绩管理的小系统这次干脆把一套完整的毕业设计成绩管理系统从需求、数据库、后端到前端梳理了一遍。项目采用SpringBootVueMyBatis架构搭配MySQL数据库适合正在做毕设选题的学生、想快速搭建后台管理系统的开发者以及需要给学院或实验室内部做一套成绩管理工具的人参考。这篇文章不吹架构不扯理论直接讲这套系统是怎么从零跑起来的。1. 先搞清楚业务再动手毕业设计成绩管理的真实需求很多人拿到成绩管理系统这类题目第一反应就是做增删改查。但毕业设计成绩管理跟普通的学生选课成绩管理完全不是一回事它的业务流程要复杂得多。你不能上来就建表得先想清楚到底有哪些角色在用这套系统每个角色的权限边界在哪里成绩是怎么流转的1.1 角色梳理学生、指导教师、评阅教师、答辩组、管理员一套毕业设计成绩管理系统涉及的参与者至少有五类学生、指导教师、评阅教师、答辩评审、系统管理员。学生需要提交论文材料、查看自己的最终成绩指导教师需要录入指导环节的评分和评语评阅教师需要独立打分答辩评审需要录入答辩表现分管理员则负责初始化学生名单、分配指导教师、设定成绩权重比例、最终导出成绩单。这五个角色听起来都是登录进去填个分数但权限差异极大学生绝不能看到其他同学的成绩指导教师只能看到自己带的学生评阅教师只能看分配给自己的论文。这个权限模型是整个系统最基本的结构骨架后端在做登录认证和接口鉴权的时候必须把这五类身份区分开。1.2 成绩构成逻辑一套分数是怎么算出来的毕业设计总成绩通常不是单个分数而是由多个环节加权汇总。我参考了几个高校的常见做法指导老师评分占40%评阅老师评分占30%答辩评分占30%或者指导老师占50%评阅占20%答辩占30%。具体权重可以配置但业务逻辑是固定的——先分别录入各环节分数再由系统按照权重自动算出总成绩。成绩计算必须在后端完成不能靠前端算好了再传过来否则学生改一下浏览器请求就能自己改分。这套系统要处理的成绩字段也比一般成绩系统复杂每个环节除了分数还要有评语、修改状态、录入时间、是否确认提交。成绩一旦提交就不能随意修改必须走申请修改—管理员审批的流程这些都是实体表设计时要考虑的字段。1.3 管理系统三件套通知、归档、导出除了成绩录入学生和导师之间还有大量的交互需求选题确认、开题报告、中期检查、论文定稿、答辩安排。成绩管理系统如果精力有限至少要把答辩分组和通知做进去——按答辩小组给学生分组并自动生成答辩时间和地点安排。归档和导出则是管理员的刚需。答辩结束后学院要上报最终成绩系统需要支持按班级、按答辩组筛选导出Excel。如果不做导出功能管理员还是得回到Excel手工整理这个系统的价值就少了一半。这几块需求理清楚之后后端的表结构、接口设计、前端页面数量基本就定了。后面所有开发工作本质上都是在为这些业务场景服务。2. 技术选型不是跟风为什么是SpringBootVueMyBatisMySQL这套技术组合看起来是Java后端开发的老三样网上随便一搜就是几十个类似项目。但每个组件在这个系统里承担什么职责为什么这个组合最适合毕业设计成绩管理值得掰开讲一下。2.1 SpringBoot省去一堆配置的Java后端快捷方式SpringBoot的核心价值在于约定大于配置。用传统的Spring写一个Web项目要配数据源、配事务管理器、配视图解析器、配JSON序列化光配置文件就能写几百行SpringBoot把常用的配置都做成了自动装配我只需要在pom.xml里加几个依赖就能起一个能跑的Web服务。成绩管理系统的后端本质就是个RESTful API服务接收前端请求调用服务层处理成绩计算和权限校验通过MyBatis操作MySQL最后把JSON数据返回给前端。SpringBoot在这个链路里承担的是最外层的HTTP处理、依赖注入、事务管理。它不需要有多花哨但必须稳定、上手快、资料多这对做毕设或者小团队内部系统来说很关键——碰到奇怪的问题搜索引擎一搜基本都有答案。2.2 Vue前端组件化和数据驱动的取舍前端选择Vue是因为成绩管理类的后台界面有大量重复的表格、表单、弹窗。用原生JavaScript写DOM操作改一个状态要手动更新好几个地方用Vue的数据绑定前端只要维护好数据模型界面自动跟着变。比如成绩录入页面教师在表格里改一个分数页面底部的总成绩列通过计算属性自动重新算出来不需要写任何根据DOM事件逐行修改的代码。具体到版本建议用Vue 3配合Element Plus组件库。Element Plus自带表格、对话框、表单校验、消息提示做完整个前端界面基本不需要自己写CSS布局省下来的时间可以拿去调后端接口和做测试。2.3 MyBatisSQL可控的持久层框架MyBatis的核心思路是SQL手写、参数自动绑定。跟JPA/Hibernate这种全自动ORM框架相比MyBatis更强调开发者自己对SQL的把控。在成绩汇总这个场景里我经常要写一些带条件统计、多表联查、动态拼接的SQL比如统计某个答辩组所有学生的平均分并按导师分组MyBatis的XML映射文件可以非常直观地写这些逻辑调试起来也方便——SQL就是SQL复制到Navicat里跑一遍就知道写没写错。有人会问为什么不用MyBatis-Plus标题写的是MyBatis就先用标准MyBatis实现这样对SQL基础本来薄弱的新手也是一个很好的锻炼机会。不过我不否认如果项目后续要加很多简单的单表CRUD接口MyBatis-Plus确实能省不少模板代码。这套系统里我仍然建议保持标准MyBatis核心目的就是让每个SQL都清晰可控、可解释。2.4 MySQL这个量级的数据用MySQL绰绰有余毕业设计成绩管理系统的数据量有多大一个学院一届毕业生几百人加上教师、题目、分组、评语撑死几万条记录。MySQL应对这个量级可以说毫无压力索引设计合理的情况下查询都是毫秒级。选择MySQL还有一个现实原因部署简单、免费、生态好不管是装在本机开发还是部署到服务器都很成熟。技术选型的逻辑到这里就很清楚了这一套组合不是性能最强的也不是代码最少的而是对当前业务最稳、对开发者最友好的组合。做成绩管理系统稳定和省心比炫技重要得多。3. 数据库设计是重头戏成绩怎么存才不混乱业务需求明确了技术栈也定下来了接下来动数据库。这套系统的核心表我建议分成五张用户表、学生信息表、题目表、成绩环节表、成绩汇总表。如果能扩展再加一张答辩分组表。3.1 用户表与角色设计用户表sys_user是最基础的字段包括id、username、password、real_name、role取值为STUDENT、TEACHER、ADMIN、email、phone。密码必须加密存储推荐使用BCrypt加密不要用MD5——MD5撞库太容易了毕业设计答辩时有老师拿这个追问答不上来很尴尬。学生信息表student_info和用户表可以一对一关联也可以直接合并。我做的时候倾向于单独建一张表专门存学号、班级、专业、年级这些教务处同步过来的基础信息避免把这些字段全部塞到用户表里影响认证逻辑。教师信息不需要单独建表用sys_user里的roleTEACHER标记即可额外加一个professional_title字段表示职称后期导出成绩单时要显示评阅人某某副教授。3.2 成绩表设计纵表还是横表成绩存储是设计中最容易纠结的地方。一种做法是横表一张成绩表里直接放三个字段guide_score、review_score、defense_score另一种是纵表每个成绩环节单独一条记录用score_type字段区分是指导分、评阅分还是答辩分。我在这个项目里推荐纵表。原因有三个第一成绩环节可能会调整比如某个学院今年增加了小组互评分纵表加一条记录就行横表得改表结构第二纵表天然能记录每个环节的录入人和录入时间审计追踪方便——答辩组看到分数异常时可以准确知道是哪个环节哪个老师录的第三汇总统计可以用一行GROUP BY完成SQL写起来反而更顺手。成绩表和学生表、题目表的外键关系要建好。成绩记录一定是从属于某个学生和某个题目的不能出现孤儿数据。在这类表设计上我的建议是外键要加索引否则后期多表JOIN查询会越来越慢。3.3 核心建表SQL参考以下是成绩管理最核心的几张表我保留了实际可用的一段SQL供参考CREATE TABLE t_score_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, student_id BIGINT NOT NULL COMMENT 学生ID关联t_student_info, topic_id BIGINT NOT NULL COMMENT 题目ID关联t_topic, score_type TINYINT NOT NULL COMMENT 环节类型1-指导教师 2-评阅教师 3-答辩, score DECIMAL(5,1) NOT NULL COMMENT 分数保留一位小数, comment TEXT COMMENT 评语, recorder_id BIGINT NOT NULL COMMENT 录入人ID, status TINYINT DEFAULT 0 COMMENT 0-草稿 1-已提交 2-已确认, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_student (student_id), KEY idx_topic (topic_id), KEY idx_type (score_type) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT成绩环节记录表; CREATE TABLE t_score_summary ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, topic_id BIGINT NOT NULL, guide_score DECIMAL(5,1), review_score DECIMAL(5,1), defense_score DECIMAL(5,1), final_score DECIMAL(5,1) COMMENT 加权计算后的总成绩, grade CHAR(2) COMMENT 等级优/良/中/及格/不及格, summary_time DATETIME, UNIQUE KEY uk_student_topic (student_id, topic_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT成绩汇总表;注意score字段用DECIMAL(5,1)可以存90.5这种常见答辩分。如果哪天需要存两位小数再改成DECIMAL(5,2)也不迟。3.4 成绩汇总表的冗余设计值得吗t_score_summary这张表其实是冗余表——最终成绩完全可以从t_score_record实时算出来为什么还要单独存一份原因很简单汇总表让查询变得极快管理员的成绩列表、排名、导出都直接查这张表不需要每次都做复杂的加权计算。而且汇总表提供了一层数据快照就算原始成绩单被误改汇总数据也能帮助追溯当时的总评结果。这种冗余在数据量小的系统里没有任何问题反而让代码逻辑清楚很多。需要把握的边界是写入汇总表的时间点一定要校验只有当三个环节的成绩都已提交时才允许生成汇总记录避免出现总成绩算了一半的脏数据。4. 后端落地成绩录入、汇总计算和权限控制的关键代码数据库有了后端可以开工了。这一部分的重点不是贴所有代码而是讲清楚三个关键点登录鉴权怎么做、成绩汇总计算怎么保证准确、接口权限怎么控制。4.1 基于JWT的登录认证与拦截器前后端分离的项目Session方式做登录不太方便主要问题是跨域和前端独立部署时Cookie不好维护。我采用JWT令牌用户登录成功后后端签发一个有效期2小时的JWT返回给前端前端后续每个请求都在请求头带上Authorization: Bearer token后端写一个拦截器统一解析token、获取当前用户信息、放行或拒绝。拦截器里要做两件重要的事一是解析token失败时返回401让前端跳转登录页二是把解析出来的用户ID和角色塞进ThreadLocal或者请求属性方便后续Service层随时拿到当前操作人。ThreadLocal一定要在请求结束后清理否则线程池复用时会出现用户A看到用户B数据的严重问题。这一点我以前就踩过坑用了Finally代码块清理之后才算安全。4.2 成绩汇总计算的业务逻辑实现成绩汇总的核心逻辑在Service层大概流程是校验当前学生三个环节的成绩是否都已存在且状态为已提交从权重配置表中读取当前学院配置的权重比例加权计算最终成绩保留一位小数根据最终成绩映射优90、良80、中70、及格60、不及格60等级写入t_score_summary汇总表同一学生做唯一键冲突时采用更新策略。这段逻辑要包在事务里防止写入汇总表成功但更新状态失败导致的数据不一致。Spring的Transactional注解要加在方法上回滚规则默认是RuntimeException如果代码里手动捕获了异常一定要重新抛出否则事务不会回滚成绩就悄悄错了。4.3 成绩修改的权限校验不是谁都能改成绩录入接口必须做细粒度权限控制。我的做法是Service层根据当前登录用户的角色和ID来判断——教师只能录入指导教师环节的成绩评阅教师只能录评阅环节答辩秘书可以录答辩环节管理员可以改所有环节但必须附加修改原因。这么做的目的只有一个防止角色越权操作。接口层面的校验用注解实现最方便。自定义一个RequireRole注解标注在Controller方法上拦截器在放行前解析注解要求的角色并与当前用户角色做匹配不匹配直接返回403。这样权限逻辑收敛在拦截器里业务方法里不需要写一堆if判断代码干净很多。4.4 一个典型的Mapper写法MyBatis的Mapper层是数据访问的关键。成绩记录这个场景最常用的是条件查询和批量插入我给出标准MyBatis XML映射里的一个片段select idselectScoreRecords resultTypecom.example.entity.ScoreRecord SELECT id, student_id, topic_id, score_type, score, comment, recorder_id, status, create_time, update_time FROM t_score_record where if teststudentId ! null AND student_id #{studentId} /if if testtopicId ! null AND topic_id #{topicId} /if if testscoreType ! null AND score_type #{scoreType} /if if teststatus ! null AND status #{status} /if /where ORDER BY create_time DESC /select使用MyBatis动态SQL能避免Java代码里拼接SQL的痛苦也方便后续加筛选条件。注意字段名和实体类属性名的映射关系如果在application.yml里没配置map-underscore-to-camel-case: true那查询结果无法自动把student_id映射成studentId需要手动在resultMap里配。这个细节常常是新手项目跑不通的元凶。5. 前端联调Vue页面、路由守卫和接口对接的细节后端接口写完之后前端要把这些接口串起来。成绩管理系统的前端界面有几个典型页面登录页、管理员工作台学生管理、教师管理、题目管理、权重配置、教师评阅页已分配学生列表、录入成绩、学生成绩查询页。这一章重点说三个容易出问题的点。5.1 路由守卫前端权限控制的正确姿势前端和后端都要做权限控制但职责不同。后端负责数据安全前端负责用户体验——不能让学生看到管理员的菜单也不能让非管理员手动输入URL就跳到管理页面。Vue Router的全局前置守卫是实现前端路由拦截的常规方案。每次路由跳转前检查是否存在JWT令牌没有则强制跳到登录页若有令牌再根据本地存储的用户角色和路由meta里声明的allowedRoles做匹配不匹配则重定向到403页。router.beforeEach((to, from, next) { const token localStorage.getItem(access_token) if (!token to.path ! /login) { next(/login) } else if (token to.meta.allowedRoles !to.meta.allowedRoles.includes(user.role)) { next(/403) } else { next() } })强烈建议配合axios请求拦截器统一在请求头里注入token以及响应拦截器统一处理401状态码。如果每个请求都手动加header一旦token字段名变了改动成本非常大。5.2 成绩录入页面的交互设计教师录入成绩的页面是整个前端最重要的页面。我的设计思路是左侧是分配给我的学生列表点击某个学生右侧展示该学生的题名、指导老师信息、论文摘要以及一个评分表单——分数输入框、评语文本域、提交按钮。关键交互是保存草稿和确认提交要分开。教师可能录了一半被打断或者还需要磨蹭斟酌评语这时候需要能保存草稿、下次继续编辑。而确认提交之后后端会把状态置为已提交页面同时锁定编辑状态。这个交互能有效减少录完被覆盖的纠纷也符合高校教学管理的实际操作习惯。5.3 前后端联调跨域问题的常规解法开发环境中前端默认跑在http://localhost:5173后端跑在http://localhost:8080浏览器会拦截跨域请求。开发阶段最简单的方案是用Vite的代理配置把前端的/api路径代理到后端的地址这样前端请求的是同源地址不触发浏览器跨域拦截。// vite.config.js export default { server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) } } } }生产部署时前端打包后的静态文件通常由Nginx托管Nginx配置里同样需要做一个反向代理把/api请求转发给后端Java服务。如果你不想在前后端联调上浪费时间这是最快跑通整套系统的路径。6. 部署与踩坑从本地跑通到放上服务器的典型问题系统开发完毕接下来是部署和调试。很多项目死在奇怪的环境问题上——本地跑得好好的换一台机器全部白屏。这里必须把几个高频问题单独拿出来讲。6.1 MyBatis驼峰映射失效什么问题呢查询结果里user_name是null但数据库里明明有值。原因就是数据库字段和Java属性没有完成映射。处理方式很简单在application.yml里加一行mybatis: configuration: map-underscore-to-camel-case: true加上之后MyBatis自动把下划线风格的数据库字段映射到驼峰风格Java属性。如果没有加要么查出来全是null要么手动配置一大堆resultMap白白浪费一晚上。这个配置我建议在写第一个查询接口的时候就加上别等踩坑。6.2 LocalDateTime序列化格式问题Java 8的LocalDateTime如果直接交给Spring Boot默认的Jackson序列化前端拿到的很可能是2025-06-02T14:30:00这种带T的ISO格式看着非常突兀提交到后端做时间解析时也容易出错。解决办法是在application.yml里做全局配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8这样前端拿到的就是2025-06-02 14:30:00符合国内用户的时间阅读习惯。时区配置也不能漏否则部署在服务器上时间会差8小时。6.3 前端history路由刷新404前端用VueRouter的history模式打包部署到Nginx后用户在system页面刷新一下居然404了。原因是history模式下URL不携带#号Nginx找不到对应的静态文件就返回404。需要在Nginx配置里加一个try_files指令让除静态文件外的所有请求都回退到index.html入口location / { root /opt/score-system/dist; index index.html; try_files $uri $uri/ /index.html; }这个问题如果不提前知道部署阶段能卡住一整天而且特别影响答辩演示——演示时刷新了一下直接白屏404心态容易崩。6.4 事务不生效的隐蔽场景前面提过Transactional必须通过代理调用才能生效。有一个隐蔽的坑在同一个类里方法A调用方法B方法B上有Transactional此时事务不生效因为Spring的代理不会拦截类内部方法调用。解决办法是把需要事务的方法拆到另一个Service类中或者通过ApplicationContext重新获取代理对象。解决这类诡异问题的时候打断点和日志输出是最高效的排查手段单纯盯着代码很难看出问题。7. 模拟数据演示与验收这套系统到底能不能用代码写完、服务跑通最关键的一步是用真实场景的模拟数据把所有功能串一遍。我拿一批模拟数据做了一次完整验收这里把过程完整还原出来。7.1 造一批覆盖各种情况的模拟数据准备了三名教师一位做指导老师、一位做评阅老师、一个答辩秘书角色五名学生每名学生分配一个毕设题目。其中两名学生的三个成绩环节都已提交一名学生只录了指导分和评阅分一名学生三个环节都没录还有一个学生答辩分为空。这五名学生覆盖了正常提交、部分提交、完全未提交三种状态足以验证系统各流程分支。造数据有一个建议不要全部用成绩优秀的数据一定要包含几个及格边缘的、分数悬殊的才能验证成绩汇总计算的边界情况。比如某个学生指导分90、评阅分70、答辩分65最终加权成绩会落在哪个区间需要人工预先算好再用系统结果比对。7.2 端到端验收从登录到成绩导出验收流程我按以下步骤走了一遍用管理员账号登录导入学生名单和题目分配表校验列表显示正确。用指导教师账号登录确认只看到自己名下的两名学生录入指导成绩并提交。切换评阅教师、答辩秘书账号分别录入各自环节分数。回到管理员界面打开成绩汇总页面确认三名学生已经算出总成绩其中部分提交的学生提示成绩未完整提交。按答辩组筛选导出Excel核对导出文件中的分数与数据库一致。用学生账号登录确认只能看到自己的最终成绩和评语看不到任何其他学生的信息。这套流程跑下来整个系统的业务闭环就验证完整了。这里要特别提醒Excel导出后的格式问题分数列千万别出现科学计数法导出时把分数列格式设为文本或者保留一位小数否则Excel里90.0显示成9E01就闹笑话了。7.3 验收清单答辩之前必须逐项勾选功能验收之外还要准备一份符合毕设答辩风格的验收清单项目能干净地启动和关闭无控制台报错无端口占用。数据库初始化SQL可以一键执行表结构注释完整关键字段有索引。登录、退出、权限拦截的边界场景都测过未登录访问接口返回401、学生访问管理员接口返回403。成绩计算逻辑有单元测试或人工核对记录权重比例调整后汇总结果能自动更新。前后端打包产物能部署到同一台服务器静态资源和API请求都能正常工作。项目文档里包含运行说明、数据库设计说明、接口说明。先把这几项逐条核对完再考虑加分功能。7.4 可扩展的升级方向系统做完基本功能后有几个方向值得继续扩展。一是文件上传毕业设计论文、任务书、查重报告都是文件给成绩表加一个附件管理功能默认不传文件就不允许提交成绩能一步到位实现成绩和材料绑定。二是消息通知成绩确认提交后自动发送邮件或站内信给指导教师和答辩秘书减少线下沟通成本。三是成绩分析报表用ECharts把各班成绩分布、各导师平均分做成图表给教学秘书做质量分析用。我的体会是这套系统真正的难点不在于技术而在于你愿不愿意把业务流程一遍一遍想清楚。成绩管理系统最怕的不是性能瓶颈而是业务边界模糊导致的数据混乱。把需求想透、把权限管住、把成绩计算逻辑写明确系统自然就稳了。如果你正在做类似的毕设题目希望这篇文章能帮你少走一点弯路。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑