简介:这是一套面向计算机相关专业毕业设计与课程设计场景的学生考勤管理系统完整项目包,采用SpringBoot后端与Vue前端分离架构,以MySQL作为数据库,适合正在准备毕设的Java学习者以及需要项目实战练习的开发者直接参考或二次开发。压缩包共428个文件,约69.47MB,其中107个java文件承载后端业务逻辑,43个vue文件构建前端页面,另有xml、yml、properties等配置文件和sql数据库脚本,以及docx开发说明文档、答辩PPT、演示视频与代码注释,覆盖从环境搭建到功能演示的完整链路。系统按管理员、教师、学生三类角色划分模块,包含学生管理、班级与课程信息管理、签到与考勤信息管理、请假信息管理及考勤统计等功能,业务闭环清晰,便于理解权限控制与数据统计的实现思路。目前已有677人学习下载,可作为高分毕设参考,帮助读者快速掌握项目结构、排错方法与答辩要点。
1. 从一份能跑起来的考勤系统源码说起:它到底解决了什么问题
很多同学做毕业设计时,最头疼的不是写不出代码,而是拼不出一套能演示、能答辩、能交差的完整系统。这份基于 SpringBoot + Vue 的学生考勤管理系统源码包,恰好卡在这个痛点上——它不是零散的功能片段,而是一套前后端分离、带数据库脚本、带配套文档的可运行工程。后端用 SpringBoot 提供 REST 接口,前端用 Vue 做页面交互,数据库落 MySQL,覆盖学生信息管理、课程考勤记录、请假审批、统计报表这几条主线。适合谁?正在做课程设计或毕业设计、需要一套结构清晰可二次开发的参考项目的人;也适合刚接触前后端分离、想找一个真实业务场景练手的初学者。它解决的核心问题是:把「考勤」这个具体业务从需求到落地的全链路跑通,让你拿到手就能启动、能改、能讲清楚每一层在干什么。
2. 环境准备与工程结构:先把 SpringBoot 和 Vue 的依赖理顺
2.1 后端技术栈与版本选择
这套工程后端基于 SpringBoot 框架,配合 MyBatis 做持久层,MySQL 存数据。常见做法是把 SpringBoot 版本控制在 2.7.x 这一档,原因很实际:2.7 是 2.x 的最后一个稳定分支,对 JDK 8 和 JDK 11 都友好,网上资料多,遇到问题好搜。如果你本地装的是 JDK 17,SpringBoot 3.x 也能跑,但要注意 3.x 把 javax 包换成了 jakarta,老代码里的javax.servlet相关引用会直接编译不过,这就是热词里「springboot版本太高」的典型翻车现场。
选版本时我一般会先确认三件事:JDK 版本、MySQL 版本、Maven 版本。JDK 8 配 SpringBoot 2.7 是最省心的组合;MySQL 用 5.7 或 8.0 都行,但 8.0 的驱动类名和连接串参数跟 5.7 有区别,下面会讲。Maven 用 3.6 以上即可。
后端目录结构大致是这样:
src/main/java/com/example/attendance/ ├── controller/ // 接口层,接收前端请求 ├── service/ // 业务逻辑层 ├── mapper/ // MyBatis 映射接口 ├── entity/ // 数据库实体类 └── config/ // 跨域、拦截器等配置 src/main/resources/ ├── application.yml // 数据源、端口等配置 └── mapper/ // XML 映射文件这个分层不是摆设。controller 只负责参数校验和返回,service 写业务规则(比如「同一学生同一节课不能重复签到」),mapper 管 SQL。改需求时你清楚该动哪一层,不会牵一发动全身。
2.2 前端 Vue 工程与依赖安装
前端是 Vue 单页应用,用 Vue CLI 或 Vite 搭建都行。热词里「vue安装及环境配置」「vue安装依赖」是高频问题,这里说清楚:先装 Node.js,建议 16.x 或 18.x LTS 版本,然后全局装包管理器。
# 确认 node 和 npm 版本 node -v npm -v # 进入前端目录,安装依赖 cd attendance-frontend npm install # 启动开发服务器 npm run servenpm install这一步最容易卡住,原因通常是网络拉包慢或某个依赖版本冲突。如果卡在某个包上,先删掉node_modules和package-lock.json重来,别硬等。启动后默认跑在 8080 端口,如果跟后端端口撞了,改vue.config.js里的devServer.port。
前端跟后端联调靠的是接口地址配置。一般在src/utils/request.js里配 axios 的baseURL,指向后端服务地址,比如http://localhost:9090。这里有个细节:后端端口别用 8080,跟前端错开,否则两个服务抢端口,启动时报「端口被占用」,新手容易懵。
2.3 数据库脚本导入与连接配置
数据库是这套系统的地基。源码包里通常带一个.sql文件,导入方式:
# 登录 MySQL mysql -u root -p # 创建数据库 CREATE DATABASE attendance_db DEFAULT CHARACTER SET utf8mb4; # 导入脚本 use attendance_db; source /path/to/attendance.sql;导入后检查表是否齐全,一般会有学生表、课程表、考勤记录表、用户表这几张。然后改后端application.yml的数据源配置:
spring: datasource: url: jdbc:mysql://localhost:3306/attendance_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver注意driver-class-name:MySQL 8.0 用com.mysql.cj.jdbc.Driver,5.7 用com.mysql.jdbc.Driver。写错了启动就报「Cannot load driver class」,这是血泪经验里排第一的坑。serverTimezone参数在 8.0 驱动下必须加,否则连接时报时区错误。
3. 核心功能模块拆解:考勤记录、请假审批与统计报表怎么落地
3.1 考勤签到接口的实现逻辑
考勤系统的核心是「签到」这个动作。后端一般提供一个 POST 接口,前端传学生 ID、课程 ID、签到时间,后端校验后写库。看一段典型的 controller 代码:
@RestController @RequestMapping("/api/attendance") public class AttendanceController { @Autowired private AttendanceService attendanceService; // 学生签到接口 @PostMapping("/checkin") public Result checkin(@RequestBody CheckinDTO dto) { // 校验该学生是否已签到,避免重复 boolean exists = attendanceService.isCheckedIn(dto.getStudentId(), dto.getCourseId()); if (exists) { return Result.fail("本节课已签到,请勿重复操作"); } attendanceService.saveRecord(dto); return Result.success("签到成功"); } }这段逻辑的关键在isCheckedIn这个校验。没有它,学生连点两次按钮就产生两条记录,统计就乱了。CheckinDTO是数据传输对象,只装前端传来的字段,不直接暴露实体类,这是常见做法,能避免前端传多余字段污染数据库。
service 层写业务规则,比如判断签到时间是否在课程有效时间段内:
public void saveRecord(CheckinDTO dto) { // 查询课程信息,拿到上课时间段 Course course = courseMapper.selectById(dto.getCourseId()); LocalTime now = LocalTime.now(); if (now.isBefore(course.getStartTime()) || now.isAfter(course.getEndTime())) { throw new BusinessException("不在签到时间段内"); } Attendance record = new Attendance(); record.setStudentId(dto.getStudentId()); record.setCourseId(dto.getCourseId()); record.setCheckinTime(LocalDateTime.now()); record.setStatus("正常"); attendanceMapper.insert(record); }参数说明:startTime和endTime存在课程表里,格式用TIME类型。BusinessException是自定义异常,配合全局异常处理器返回统一错误格式,前端好解析。
3.2 请假审批的状态流转
请假审批是个典型的状态机:学生提交 → 待审批 → 教师审批 → 通过或驳回。数据库里请假表要有一个status字段,用数字或字符串表示状态。常见做法是用枚举:
public enum LeaveStatus { PENDING("待审批"), APPROVED("已通过"), REJECTED("已驳回"); private final String desc; LeaveStatus(String desc) { this.desc = desc; } public String getDesc() { return desc; } }审批接口接收请假 ID 和审批结果,更新状态:
@PostMapping("/leave/approve") public Result approve(@RequestBody ApproveDTO dto) { Leave leave = leaveService.getById(dto.getLeaveId()); if (!LeaveStatus.PENDING.getDesc().equals(leave.getStatus())) { return Result.fail("该申请已处理,不能重复审批"); } leave.setStatus(dto.getApproved() ? LeaveStatus.APPROVED.getDesc() : LeaveStatus.REJECTED.getDesc()); leave.setApproveTime(LocalDateTime.now()); leaveService.updateById(leave); return Result.success("审批完成"); }这里的状态校验是防重复审批的关键。没有它,教师误点两次,状态就被覆盖了。审批通过后,通常还要联动考勤记录——把对应课程的考勤状态改成「请假」,这一步在 service 里用事务包起来,保证请假状态和考勤记录同时更新,要么都成功要么都回滚。
3.3 统计报表的 SQL 聚合写法
统计报表是答辩时的加分项。常见需求:按课程统计出勤率、按学生统计缺勤次数。这类查询用 SQL 聚合函数直接搞定,别在 Java 里循环算,数据量一大就慢。
-- 按课程统计出勤率 SELECT c.course_name, COUNT(a.id) AS total, SUM(CASE WHEN a.status = '正常' THEN 1 ELSE 0 END) AS normal_count, ROUND(SUM(CASE WHEN a.status = '正常' THEN 1 ELSE 0 END) / COUNT(a.id) * 100, 2) AS rate FROM attendance a JOIN course c ON a.course_id = c.id GROUP BY c.course_name;CASE WHEN是条件计数的标准写法,ROUND保留两位小数。如果考勤表数据量大,记得在course_id和student_id上建索引,否则 GROUP BY 会全表扫描。前端拿到这个结果,用 ECharts 画个柱状图或饼图,答辩演示效果直接拉满。
4. 前后端联调与打包部署:从本地跑通到放进 SpringBoot
4.1 跨域问题的三种解法
前后端分离开发时,前端 8080、后端 9090,浏览器同源策略会拦请求,报 CORS 错误。这是新手必踩的坑。解法有三种,按场景选:
第一种,后端加跨域配置,最省事:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowCredentials(true) .maxAge(3600); } }第二种,前端配代理,开发环境用:
// vue.config.js module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:9090', changeOrigin: true } } } }第三种,Nginx 反向代理,生产环境用。开发阶段用前两种就行,别一上来就折腾 Nginx。
4.2 Vue 打包放进 SpringBoot 的正确姿势
热词里「vue打包放进springboot中」是个高频需求,很多人想把前后端打成一个 jar 包部署。步骤是:先npm run build生成dist目录,再把dist里的文件拷到 SpringBoot 的src/main/resources/static下,然后配一个转发规则,让非接口请求都指向index.html。
@Controller public class IndexController { // 匹配非 /api 开头的路径,转发到前端首页 @RequestMapping(value = "/{path:[^\\.]*}") public String forward() { return "forward:/index.html"; } }这个正则[^\\.]*的意思是「不包含点的路径」,避免把.js、.css这类静态资源也转发走。配好后打包成 jar,java -jar一跑,前后端就都在一个端口了。注意 Vue 路由要用 history 模式才需要这个转发,hash 模式(带 #)不用。
4.3 打包命令与启动验证
后端打包:
# 跳过测试打包,加快速度 mvn clean package -DskipTests # 启动 java -jar target/attendance-0.0.1-SNAPSHOT.jar启动后看日志有没有报错,重点看数据源连接和端口监听。浏览器访问http://localhost:9090,能出登录页就说明前后端都通了。登录后走一遍签到、请假、看报表的流程,确认核心功能正常。这一步别偷懒,答辩前一定要完整走一遍,我见过太多「本地能跑、换台机器就崩」的情况,多半是数据库没导或配置没改。
5. 避坑与排查:那些让项目跑不起来的常见问题
5.1 启动报「Cannot load driver class」
现象:SpringBoot 启动直接失败,日志里一行Cannot load driver class: com.mysql.jdbc.Driver。
原因:MySQL 驱动版本和驱动类名不匹配。8.0 的驱动包已经移除了com.mysql.jdbc.Driver,只剩com.mysql.cj.jdbc.Driver。
解决:改application.yml里的driver-class-name,同时确认pom.xml里 mysql-connector 的版本跟本地 MySQL 对得上。8.0 数据库配 8.0 驱动,别混用。
5.2 前端 npm install 卡死或报错
现象:npm install跑一半不动了,或者报一堆ERESOLVE依赖冲突。
原因:网络拉包慢,或者依赖树里有版本冲突。
解决:先换国内镜像源npm config set registry https://registry.npmmirror.com,再删掉node_modules和package-lock.json重装。如果还报 ERESOLVE,加--legacy-peer-deps参数绕过严格检查。别用--force,那个会把依赖装乱。
5.3 数据库中文乱码
现象:页面显示的学生姓名、课程名变成问号或乱码。
原因:数据库、表、连接串三处的字符集不统一。
解决:建库时用utf8mb4,连接串加characterEncoding=utf8,MyBatis 的 XML 文件存成 UTF-8 编码。三处都对齐了才不会乱。MySQL 8.0 默认就是 utf8mb4,5.7 要手动指定。
5.4 接口返回 404 或 401
现象:前端调接口,浏览器控制台报 404 或 401。
原因:404 多半是接口路径写错或后端没启动;401 是拦截器把请求拦了,没带 token 或 token 过期。
解决:先在浏览器直接访问接口地址,看后端是否响应。如果后端正常,检查前端baseURL和接口路径拼接对不对。401 的话看拦截器配置,登录接口和静态资源要放行,别一股脑全拦。
5.5 打包后页面空白
现象:jar 包启动后访问首页一片空白,控制台报资源 404。
原因:Vue 打包的publicPath配置不对,默认是/,但放进 SpringBoot 后资源路径变了。
解决:在vue.config.js里把publicPath改成./,重新打包再拷进 static 目录。这个坑很隐蔽,本地 dev 模式看不出来,一打包就现形。
6. 二次开发与功能扩展:把这份源码改成你自己的项目
拿到一套能跑的源码只是起点,答辩时老师最常问的是「你做了什么改进」。所以得会在这套基础上做二次开发。下面说几个投入产出比高的扩展方向。
第一个是加「考勤异常预警」。思路是写一个定时任务,每天扫一遍考勤记录,把连续缺勤超过 N 次的学生筛出来,往教师端推消息。用 SpringBoot 的@Scheduled注解就能实现:
@Component public class AttendanceWarningTask { @Autowired private AttendanceMapper attendanceMapper; // 每天晚 8 点执行 @Scheduled(cron = "0 0 20 * * ?") public void checkAbsence() { // 查出连续缺勤超过 3 次的学生 List<WarningVO> list = attendanceMapper.selectContinuousAbsence(3); for (WarningVO vo : list) { // 这里可以接消息推送或写预警表 System.out.println("预警学生:" + vo.getStudentName()); } } }cron表达式0 0 20 * * ?表示每天 20:00 触发。对应的 SQL 用窗口函数或自连接算连续缺勤,MySQL 8.0 支持窗口函数,写起来更简洁。这个功能加进去,答辩时能讲「从被动记录到主动预警」,立意就上去了。
第二个是「考勤数据导出 Excel」。教师端经常需要把考勤表导出来存档。用 EasyExcel 或 POI 都行,EasyExcel 内存占用低,适合大数据量。核心代码就是查数据、写流、设响应头,网上模板很多,套进去改改字段就行。
第三个是「人脸识别签到」这种听起来唬人的功能。实际落地可以用现成的 SDK,前端调摄像头拍照,后端调接口比对。但这个方向坑很深,涉及模型部署和性能,毕业设计层面做个 demo 演示即可,别真往生产环境想。我一般建议学生把精力放在业务逻辑的完整性上,而不是堆花哨功能——老师更看重你对业务流程的理解,而不是调了几个 API。
验证扩展功能有没有做对,我的习惯是:先写一条能复现的测试数据,手动走一遍流程,看数据库里字段变化对不对,再看前端展示是否一致。三步都对上了,才算真的通了。从那以后我每次改完代码,都强制自己走一遍「造数据 → 跑流程 → 查库 → 看页面」这个闭环,能挡掉八成低级错误。希望这份拆解能帮你把这套考勤系统真正跑起来、改明白。
本文还有配套的精品资源,点击获取