资讯动态

SpringBoot+Vue大学生考勤系统设计与实现全解析

发布时间:2026/10/5 4:33:51 来源:尧图企业网站定制
每年到毕业季总有同学在后台问我关于Java Web毕设的事情。说实话几年看下来题库里的题目来来去去但XX管理系统永远是大头而考勤系统又是这类题目里的常客。原因很简单业务场景清晰、用户角色分明、数据链路完整而且可以顺理成章地挂上SSM、SpringBoot、Vue这些主流技术栈。一套合格的SpringBootVue大学生考勤系统不只是应付查重和答辩它能让你把前后端分离、RESTful接口设计、数据库建模、权限控制这些实际工作中最常用的技能完整过一遍。这篇文章我就以这套完整项目为线索从源码结构、SQL脚本、接口设计一路聊到跑通联调和答辩准备把那些文档里不会写清楚的细节一并说透。1. 考勤系统的业务本质为什么它天生适合做毕设1.1 先还原一个真实的考勤场景我习惯在写代码前先把业务故事讲清楚。大学生考勤系统的使用场景是这样的学校有好多个班级每个班级有几十名学生每学期开若干门课程每门课程由一位老师负责。上课时老师要统计出勤情况——谁来了、谁迟到、谁请假、谁旷课。传统做法是纸质点名或者课代表拿张表挨个划勾期末再人工汇总。这个场景搬进系统之后就天然形成三条业务线学生端查看自己的课程表、签到打卡、提交请假申请、查看考勤记录。教师端查看所授课程的学生名单、发起或管理签到、审批请假、查看考勤统计。管理员端维护班级、学生、课程、教师的基础数据查看全局统计。这三条线几乎覆盖了Java Web毕设能考核的所有知识点多角色权限、CRUD操作、事务处理、关联查询、数据统计。这也是我每次推荐学生选这类题目的原因——它能把项目做得看起来完整同时不要求太高深的前沿技术适合本科阶段的真实水平。1.2 技术选型SpringBoot Vue 的组合逻辑讲道理考勤系统用JSP Servlet也能做出来三年前的老项目全是这么搞的。但为什么现在推荐SpringBoot Vue核心原因有三个。第一开发效率差太多了。SpringBoot的自动配置帮我们省掉了大把XML配置内嵌Tomcat让项目一键启动配合MyBatis-Plus这种ORM工具基础的增删改查基本是零成本写出来的。学生用户、课程、班级这些表的CRUD真有经验的开发者几乎不需要动脑。第二前后端分离是现在团队协作的真实形态。前端用Vue管理视图后端只管返回JSON数据两边通过接口文档对接。这个协作模式在课程设计里可能显得杀鸡用牛刀但在实习或者工作之后你会发现公司里的Java开发就是这么干的前端一个仓库后端一个仓库中间靠Swagger或者YApi这样的文档工具配合。第三从毕设答辩的角度来说SpringBootVue的项目可以讲的内容更多。光是一个跨域问题怎么解决或者前端路由守卫怎么实现就能体现你确实做了功课这在答辩时是有实用价值的。1.3 完整源码SQL接口文档这个组合意味着什么一个完整的毕设项目交付通常包含三样东西后端源码、前端源码、SQL脚本有心的同学还会补一份接口文档。SQL脚本解决的是数据库从无到有的问题评审老师拿到项目后在本地新建一个数据库导入脚本再改一下连接配置项目就能跑起来。接口文档解决的是前后端怎么配合的问题后端同学写完接口后把路径、参数、返回值约定清楚前端同学照着文档调就完了。这三样东西在项目中的角色缺一不可。只有源码没有SQL别人无法复现你的数据环境有SQL没文档前端对接基本靠猜。后面我讲的每一步都是基于这个交付组合来展开的。2. SQL脚本设计五张核心表把考勤逻辑落地2.1 从用户到考勤表结构的设计顺序考勤系统的表不需要很多把覆盖面做对才是关键。我一般这样设计用户表、学生表、教师表或合并进用户表、课程表、班级表、考勤记录表、请假表。这里给出一套经过验证的经典设计。用户表sys_user承担登录与角色标识的功能字段包含用户ID、用户名、密码、角色类型、状态。角色类型用数字区分管理员是0教师是1学生是2。注意这里不要把学生、教师的信息硬塞进用户表而是用用户ID关联到学生表、教师表这样可以避免字段冗余也让业务边界更清晰。学生表student存学号、姓名、班级ID、手机号、邮箱。教师表teacher存工号、姓名、职称。班级表classes存班级名称、专业、入学年份。课程表course存课程名称、授课教师ID、上课地点、上课周次、上课时间。这些表之间的关联关系很简单学生属于一个班级教师教授多门课程课程和班级之间通过选课关系student_course关联起来。2.2 考勤记录表整个系统的心脏考勤业务最关键的落点就是attendance_record这张表。我见过不少设计把它做成每天每个学生一条数据这种做法在报送教务时好使但在日常签到场景里会产生大量状态为未签到的空白记录数据量虚高且难维护。更合理的做法是只有发生了签到或请假动作才产生一条记录。字段设计大概是这样的字段名类型说明record_idbigint主键自增student_idbigint学生ID关联student表course_idbigint课程ID关联course表attendance_datedate考勤日期sign_in_timedatetime签到时间statustinyint考勤状态0-正常 1-迟到 2-早退 3-旷课 4-请假remarkvarchar备注这里有个特别重要的设计细节需要注意避免同一学生同一课程同一天重复签到。最可靠的做法不是在后端代码里先查再插而是直接在表上加唯一约束比如(UNIQUE KEY uk_student_course_date (student_id, course_id, attendance_date))。这样哪怕前端双击提交、接口被并发调用数据库层面也会把第二条记录拦下来算是防御性编程里最典型的兜底方案。2.3 状态判定迟到早退怎么算考勤状态里的迟到和早退是否准确是很多毕设里最有争议的地方。我的做法是在系统里维护一个考勤规则配置config表或直接在application.yml里配置设置课程的开始时间和结束时间。学生签到时只需记录当前时间后端收到请求后从课程表中读取这门课的计划上课时间再和签到时间对比签到时间在[startTime, startTime迟到宽限分钟]范围内记为正常签到时间超过宽限时间但还在课程结束前记为迟到整节课都没有签到记录记为旷课早退的判断则依赖签退逻辑学生下课前再次签到触发签退如果签退时间早于课程结束时间且缺席时长超过阈值标记早退状态。这套规则我不建议写死在代码里。学生上课迟到5分钟算正常还是10分钟算正常每个学校都不一样。做成配置项之后老师或者管理员可以灵活调整而代码逻辑不需要改动。这也是答辩时一个很好的亮点话题能够体现出你对业务需求的理解。2.4 SQL脚本里的基础数据与细节坑SQL脚本除了建表还应该包含基础数据。至少要有一个管理员账号、一个教师账号、几个学生账号、一个班级、两门课程、几份考勤记录。没有演示数据项目跑起来之后界面上空空荡荡老师甚至不知道怎么演示操作。演示账号我一般统一写成admin/123456、teacher/123456、student/123456并在README里写清楚。MySQL建表的几个小细节值得注意表名和字段名统一用小写加下划线避免不同操作系统之间大小写敏感导致的差异字符集统一使用utf8mb4因为utf8在MySQL里存emoji会报错utf8mb4是utf8的超集排序规则用utf8mb4_general_ci就够了。至于是否建外键我的建议是逻辑外键优先也就是只在字段上标注关联student表但不在数据库层面建物理外键。物理外键在删除数据时会有一堆约束麻烦而MyBatis-Plus的关联查询根本用不到数据库外键。2.5 请假表审批流转的状态机设计请假表leave_request是考勤系统里唯一的流程数据。字段包括请假ID、学生ID、课程ID、请假开始时间、结束时间、请假原因、审批状态、审批意见、提交时间。审批状态建议用数字0-待审批1-已通过2-已驳回。这里需要特别注意请假通过之后考勤记录表里是否要生成一条status4的记录。我建议在审批通过的操作里通过事务同时插入考勤记录这样老师查看考勤统计时某节课的应到人数、实到人数、请假人数可以一次性算清楚。如果审批通过了但考勤表里没有数据统计就永远对不上。3. 接口文档签到、请假、统计三大核心链路怎么设计3.1 统一返回体与接口风格约定接口文档不是写一句话登录接口就行而是要把请求路径、请求方式、请求参数、返回结果、错误码全部约定清楚。项目里的每个接口统一返回相同格式的JSON结构我习惯用一个Result对象封装{ code: 200, message: 操作成功, data: {} }code为200表示正常非200表示业务异常或系统异常。前端axios拦截器拿到响应后统一判断code如果不是200就弹出message提示不需要每个页面重复写错误处理逻辑。接口路径设计上遵循RESTful风格资源用名词复数动作交给HTTP方法。核心接口列表大概是这样的方法路径说明POST/api/auth/login登录返回token与用户角色GET/api/student/courses学生查看已选课程列表POST/api/attendance/signIn学生签到GET/api/attendance/history学生查看个人考勤记录POST/api/leave/add学生提交请假申请POST/api/leave/approve教师审批请假GET/api/attendance/statistics考勤统计多维筛选3.2 登录鉴权从token到路由守卫考勤系统一定涉及角色所以登录不能只校验用户名密码还要把后续请求的身份信息带上。常见做法是JWT。用户登录成功后后端生成一个包含用户ID和角色信息的token返回前端。前端把token存在localStorage里面后续每个请求都在请求头里附加Authorization字段。后端用一个拦截器解析token如果token过期或者非法直接返回401前端收到401后跳回登录页。这样做的好处是接口是无状态的后端服务不需要保存用户会话。对于毕设项目来说这一套东西足以体现你对权限控制的理解。配合前端Vue Router的全局前置守卫未登录用户访问任何页面都会被拦截不同角色访问对方页面也会被重定向用户体验和安全性都能兼顾。3.3 签到接口的反重设计与事务问题签到接口是最容易出问题的接口。学生点击签到后后端要做三件事查询课程信息、读取当前时间、判断迟到状态、写入考勤记录。如果这段逻辑不加以控制高并发下可能会出现重复签到和判断错乱。我的做法很简单先让数据库的唯一约束兜底然后service层里把判断插入放到同一个事务中。Spring的Transactional注解可以保证这两个操作要么都成功要么都失败。如果插入时出现DuplicateKeyException就说明该学生这节课已经签到过直接返回请勿重复签到。另外一个容易忽略的问题是服务器时区。很多同学在本地测试没问题部署到服务器后签到时间差8个小时这是因为MySQL连接串里没设serverTimezone或者系统时区不是Asia/Shanghai。连接串里加上serverTimezoneAsia/Shanghai基本能解决。3.4 请假审批状态变更与数据联动请假接口的难点在于审批通过后要更新考勤记录。如果学生请的是某一天的假那么审批通过后应该自动生成一条status4的记录。这里最稳妥的做法是审批接口内嵌套一个事务先更新leave_request表的状态再往attendance_record表插入请假记录两个操作统一提交。如果没有事务保护假条批了但考勤记录没生成期末统计就对不上账。审批驳回时不需要对考勤表做任何操作只需更新请假单状态并填写审批意见。前端列表里展示待审批已通过已驳回三种状态时直接读取数字映射成文字注意驳回状态下把审批意见展示给用户方便重新申请。3.5 统计接口查询条件的组合策略考勤统计是这类项目里最容易被问到怎么实现的接口。统计维度一般有三种按课程统计某次课的学生出勤情况、按学生统计本学期各课程出勤情况、按班级统计一段时间内的出勤率。后端实现时我一般用MyBatis-Plus的QueryWrapper拼条件或者写一段自定义SQL。比如查询某门课程的整体出勤统计一个典型SQL可以这样设计SELECT c.course_name, COUNT(DISTINCT a.student_id) AS total_students, SUM(CASE WHEN a.status 0 OR a.status 1 OR a.status 4 THEN 1 ELSE 0 END) AS present_count, SUM(CASE WHEN a.status 1 THEN 1 ELSE 0 END) AS late_count, SUM(CASE WHEN a.status 3 THEN 1 ELSE 0 END) AS absent_count, SUM(CASE WHEN a.status 4 THEN 1 ELSE 0 END) AS leave_count FROM attendance_record a LEFT JOIN course c ON a.course_id c.course_id WHERE a.course_id #{courseId} AND a.attendance_date BETWEEN #{startDate} AND #{endDate} GROUP BY c.course_id, c.course_name;实际开发时我不建议把大量业务逻辑写在SQL里但统计类接口例外因为数据库聚合函数处理大量行数据的速度比Java内存循环快太多。前端拿到统计结果后用ECharts画饼图、柱状图呈现效果会很直观。4. Vue前端工程从登录页到考勤看板4.1 前端工程目录与基础设施前端部分我用Vue CLI初始化的标准项目如果你熟悉Vite也可以但Vue CLI生态更成熟对于毕设来说坑更少。src目录下面大致分为views页面组件、router路由配置、store状态管理、api接口请求封装、utils工具函数五块。api目录里的每个文件对应一个后端模块。比如user.js放登录相关接口attendance.js放签到与考勤查询接口leave.js放请假相关接口。统一用axios实例发送请求并在axios的请求拦截器里自动附加tokenimport axios from axios const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) service.interceptors.response.use(res { const data res.data if (data.code ! 200) { // 统一错误提示 return Promise.reject(new Error(data.message)) } return data }) export default service这里把baseURL设置为/api是配合Vue CLI的devServer代理。开发环境下前端跑在8080端口后端跑在8081端口如果不做代理就会出现跨域问题。在vue.config.js里配置如下module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } }这样前端请求/api/auth/login时开发服务器会把它转发到http://localhost:8081/api/auth/login跨域问题就绕过去了。这个代理配置是整个前后端联调的第一道关卡几乎每次帮人看联调问题都从这里开始排查。4.2 核心页面拆解签到页、审批页、统计页签到页是学生端最重要的页面。学生登录后进入课程列表选择当前要签到的课程点击签到按钮。页面上最好展示当前时间签到成功后显示本次考勤状态正常还是迟到状态用不同颜色块标识一眼能看清。如果这节课已经签过到按钮置灰并显示已签到防止误操作。教师端的核心页面是请假审批页和考勤统计页。审批页用表格列示待审批的请假单每条记录后面放通过和驳回按钮通过时不需要填内容驳回时弹窗要求填写意见。统计页用ECharts展示我习惯用双图表布局——上面一张饼图展示某次课各状态人数占比下面一张柱状图展示一个班级两周内的出勤趋势。管理员端的页面相对枯燥就是班级管理、学生管理、课程管理但注意表单里要做基础校验比如学号必须唯一、手机号格式正则、课程时间不能相互冲突。这些校验在前端做一遍后端DTO里也要用注解再校验一遍双保险。4.3 路由与权限控制守卫不是摆设Vue Router的全局前置守卫是我强烈建议写的。逻辑很简单进入每个路由前先看localStorage有没有token没有就跳登录页有token再看目标路由要求的角色和当前用户角色是否匹配不匹配就跳回首页。router.beforeEach((to, from, next) { const token localStorage.getItem(token) const role localStorage.getItem(role) if (to.path ! /login !token) { next(/login) } else if (to.meta.roles !to.meta.roles.includes(Number(role))) { next(/) } else { next() } })后端接口也必须有对应的权限拦截不能前端藏个路由就以为安全了。SpringBoot里做法很简单写一个WebMvcConfigurer注册HandlerInterceptor拦截所有/api/**的请求放行登录接口其他请求解析token并校验角色。前后端双权限控制答辩时被问到安全问题你能把这一层说清楚评审老师通常不会再追问更深的东西。4.4 打包部署前端构建产物放进SpringBoot很多同学问过我毕设要交一个能跑的系统前后端要分别启动两个服务吗其实可以不。Vue项目执行npm run build之后会在dist目录下生成静态文件。把这些文件复制到SpringBoot项目的src/main/resources/static目录下然后重新打jar包。启动这个jar前端页面和后端接口就都在同一个端口上提供服务了。访问http://localhost:8081就能直接进入系统不需要开两个终端。这个方法在毕设演示时非常省事而且看起来很专业。部署阶段把前端的请求路径直接写成相对路径/api/...即可不要再带跨域地址。5. 把项目跑起来环境准备与联调踩坑记录5.1 一套稳妥的开发环境版本组合项目的环境版本直接影响你是否能顺利跑起来。下面是经过实测比较稳妥的组合组件推荐版本说明JDK1.8 或 11毕设项目不需要太高版本JDK8兼容性最好Maven3.6.3IDEA自带的Maven也可以用MySQL5.7 或 8.0注意8.0驱动与5.7的连接串有区别Node.js14.x 或 16.x太新的Node在某些老依赖上会编译失败IDEA2021社区版就够用SpringBoot版本选择上我建议用2.x系列不要一上来就选3.x。3.x是基于JDK17的很多教程、依赖和你参考的代码都是基于2.x写的版本太高反而会遇到各种兼容性报错。如果热词里的springboot版本太高这个问题你也遇到了大概率就是把项目建在了3.x上要么降版本要么把所有依赖都升级适配对毕设来说不划算。5.2 三步导入项目数据库、后端、前端把项目从源码变成可运行的系统步骤非常简单但每一步都有容易出错的地方。第一步导入SQL脚本。用Navicat或命令行工具新建一个数据库比如叫attendance_db选择运行SQL文件把脚本导入。导入完成后检查一下有没有报错看看表和数据是否生成。第二步启动后端。IDEA里以Maven项目方式导入后端源码等待依赖下载完成。修改application.yml里的数据库用户名和密码改成你自己本机的配置。然后运行启动类如果控制台出现Started Application in x seconds就说明后端起来了。默认端口在application.yml里配置我用的是8081避免和前端常见端口冲突。第三步启动前端。在命令行进入前端目录依次执行npm install和npm run serve。npm install是最容易耗时或者失败的步骤网络原因、Node版本原因都可能出问题。遇到下载慢就配置淘宝镜像执行npm config set registry https://registry.npmmirror.com。安装完成后启动浏览器访问localhost:8080看到登录页就成功了。5.3 常见启动报错与排查方法我整理过一份高频报错清单这里挑几个典型的说说。No Access-Control-Allow-Origin header is present。这是跨域问题的经典报错。原因通常是前端直接请求了后端地址而不是走代理路径。解决方式是确认前端请求路径是以/api开头并检查vue.config.js的proxy配置是否生效修改代理配置后必须重启前端服务。Access denied for user rootlocalhost。这是数据库账号权限问题或者密码错误。先确认application.yml里的密码是否和本机MySQL一致。MySQL8默认使用caching_sha2_password认证插件如果驱动版本太旧会连不上把pom里的mysql-connector-java升级到8.0.33及以上一般就解决了。端口被占用。启动后端时提示Port 8081 was already in use。要么改application.yml里的server.port要么找到占用进程关掉。Windows下用netstat -ano | findstr 8081查进程PID再taskkill /F /PID xxx结束进程。5.4 联调阶段的排查思路前端页面打开了、后端接口也通了但页面数据不对这种问题最让人头疼。我的排查顺序是这样的打开浏览器开发者工具的Network面板先看请求是否发出状态码是不是200响应体长什么样。如果是404检查路径是否匹配后端的RequestMapping如果是401或403检查token有没有带上、token有没有过期如果是500直接把后端控制台的异常信息拉出来看大部分是空指针和SQL语法问题。这套排查链路难得的是顺序。不要一上来就翻代码先通过Network面板和Console定位到请求层面还是渲染层面的问题再决定去后端找还是去前端找能省下大量时间。6. 答辩前必须准备的问题和话术6.1 为什么选这个课题、这套技术栈答辩第一个问题基本跑不掉你这个系统是做什么的为什么这么设计我建议从业务痛点切入——传统点名方式耗时且易出错老师课后还要手工统计效率低系统要解决的就是考勤数据采集、审批流程和统计分析三个问题。技术栈选型时提到SpringBoot简化了配置与部署Vue提供了组件化开发和响应式渲染前后端通过JSON交互逻辑清晰。这个问题不要背书式地回答最好结合你实际项目里的某个页面来介绍比如签到页面的实时反馈就是前端响应式的体现请假审批的状态流转就是后端事务一致性的体现。举具体的例子比背概念有说服力得多。6.2 考勤记录表为什么不用物理外键这个问题我有过教训。当初用物理外键时学生删除功能被外键约束卡死有考勤记录的学生根本删不掉。后来改成逻辑外键删除之前先手动检查有没有关联数据。答辩时老师问起你可以坦诚地说物理外键在并发写入时有额外锁开销并且不利于数据归档逻辑外键配合Service层的存在性检查已经足够保证业务正确性。这话一说老师反而会觉得你考虑过生产实践比那些照搬教科书说要建外键的同学印象分更高。6.3 如果考勤并发量很大系统会挂吗这道题是考察你对系统瓶颈的分析能力。老实说毕设的单机架构承受不了互联网级别的并发但校园考勤场景同时签到的人数也就是几百人单机加数据库完全扛得住。如果你还想展示一点设计能力可以把方案分成几层来说数据库连接池用HikariCP默认10个连接按1000人同时签到算每个人请求耗时几十毫秒排队等待可以接受热点数据比如课程信息和考勤规则放入缓存哪怕是本地缓存减少数据库查询如果需要更大并发可以引入消息队列削峰但这就超出了毕设范围。6.4 如何证明系统是你自己做的这是个很微妙的问题。老师见过太多网上随便下载改个标题交差的项目。我的建议是一开始就在项目里留下自己的痕迹接口文档自己写、数据库注释按自己的习惯写、代码整洁度要高并且在演示时重点讲解自己真正写过的、能说清楚的部分。比如你可以说统计模块的报表SQL是自己反复调整过的请长假审批这条链路是自己梳理的。真实做过和没做过在问答环节是藏不住的所以最稳妥的策略是把整个项目过一遍每个页面都能讲出来龙去脉。7. 这套系统后续的扩展方向考勤系统做完只是个开始。如果时间充裕有几个扩展方向的性价比很高。第一个是二维码签到老师每次上课生成一个动态二维码学生扫码签到防止代签这是传统考勤系统常见的痛点。第二个是接入课表导入通过读取教务系统导出的Excel自动创建课程和选课关系省去手工录入。第三个是移动端适配不用开发原生App把现有Vue项目适配成移动端布局或者用uni-app套壳打包就能变成Android应用。这些扩展不需要改动核心表结构只是在现有架构上增加接口和页面对能力的提升却非常有帮助。回到这篇文章一开始说的一套考勤系统最核心的价值不是你用了多少新技术而是你有没有把业务链路完整走通。从数据库设计到接口开发从前端交互到权限控制每一个环节都有可以深挖的细节。把这份经验沉淀下来即便以后不做毕设项目进入实际项目组里做业务开发这些基本功都是通用的。最后分享一个我自己一直坚持的习惯给别人看的项目交付物里接口文档永远比代码更先写清楚。后端代码是写给机器跑的接口文档是写给协作的人看的把文档写好了工作里的沟通成本会低一半以上。

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

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

免费获取报价 →
↑