基于SpringBoot的考勤管理系统开发实践与答辩指南
2026/9/10 3:07:34 网站建设 项目流程

毕设季又到了,每年这个时候都有一堆人私信问我:“学长,我分到的题目是基于SpringBoot的考勤管理系统,能不能给我个源码参考一下?”说实话,考勤系统在Java毕设里属于经典中的经典,几乎每个学校每个年级都有人做,但正因为做的人多,老师对它的要求也水涨船高——你说自己做了个考勤系统,老师脑子里默认的功能清单可比你想象的多得多。

这篇文章就专门拆一下“基于SpringBoot的单位考勤管理系统”这个题目。我会从需求分析、技术选型、数据库设计、核心功能实现,到答辩时的隐藏雷区,一条龙讲清楚。特别是那些网上流传的各种毕设源码包,真正拿到手之后该怎么用、怎么改成自己的,也会单独说一节。如果你正准备做这个题目,或者已经下载了某份源码但无从下手,这篇文章应该能帮你少走不少弯路。

1. 拿到这个题目,先别急着建项目,把“考勤”两个字拆开看

很多同学第一反应是打开IDEA新建SpringBoot项目,然后就开始写代码。这种做法不能说错,但对于毕设来说,风险很大。理由很简单:你连你要做的系统边界在哪里都没划清楚,写出来的代码要么功能缺失被老师挑刺,要么功能堆砌最后烂尾。

1.1 考勤系统的核心角色与业务场景

一个单位内部的考勤管理系统,表面上就是“员工打卡、管理员查看记录”,但如果你把视角切换到真实的公司场景,会发现至少涉及以下角色和流程:

  • 普通员工:上下班打卡、查看个人考勤记录、提交请假/加班/外出申请、查看审批结果。
  • 部门主管/审批人:审批下属的请假、加班、外出申请,查看本部门考勤汇总。
  • 考勤管理员/HR:维护员工信息、配置考勤规则(上下班时间、迟到阈值、旷工判定)、处理异常考勤(补卡、修正记录)、生成月度考勤报表。
  • 系统管理员:管理部门、角色、账号权限等基础数据。

也就是说,一个“完整”的考勤系统,至少应该包含基础信息管理、打卡签到、请假管理、加班管理、考勤统计报表这五个核心模块。如果你做的是课程设计,简化到只有打卡和查询也能交差,但如果是毕业设计,请务必把这五个模块都覆盖到,否则答辩时老师一句“你这系统只能打卡,那请假怎么算?迟到怎么算?”就能把你问住。

1.2 需求优先级怎么排

毕设周期一般三到六个月,你不可能把所有功能都做到完美,所以要分清主次:

优先级功能模块理由
P0员工管理、部门管理、打卡签到、考勤记录查询一个考勤系统的底线,缺了就不叫考勤系统
P1请假管理(含审批流)、加班申请、考勤月报统计展示业务完整性的关键,老师最喜欢细问这一块
P2异常考勤处理(补卡)、公告通知、导出Excel、图表统计加分项,做出来是亮点,做不出来不至于挂

按照这个优先级排开发计划,先把P0完成,再逐级推进。千万不要一上来就琢磨Excel导出和ECharts图表,那些都是后话。

2. 技术选型的“标准答案”:SpringBoot + MyBatis-Plus + MySQL + Vue

2.1 后端为什么是SpringBoot而不是更“老”的方案

既然题目指定了SpringBoot,那这就没得选了。不过你仍然要能说清楚SpringBoot的优势,因为答辩必问:“你为什么选SpringBoot?”标准回答有三条:

第一,SpringBoot简化了Spring的配置流程。传统的SSH或SSM项目,光配置XML就要写一大堆,SpringBoot用自动配置和Starter依赖把大部分重复劳动干掉了,让开发者能专注于业务代码。这也是为什么现在的企业项目绝大多数都是SpringBoot,毕设选它意味着你在跟主流技术栈接轨。

第二,SpringBoot生态丰富,和前端Vue、数据库MySQL、缓存Redis等组件集成都非常成熟。以典型的“SpringBoot + Vue前后端分离”架构为例,后端只需要通过RESTful接口提供数据,前端独立部署,联调方便,也符合现代Web开发的模式。

第三,SpringBoot对部署和运维友好。内嵌的Tomcat容器让它能直接打成jar包跑起来,不用单独装Tomcat、配JNDI那一套,对毕设演示来说体验极好——在老师电脑上只要装了JDK,一条java -jar命令就能看到系统跑起来。

2.2 前端选Vue还是Thymeleaf

这里有个很实际的分岔路。如果你本身Java基础一般,不想写太多前端代码,那后端直接用SpringBoot + Thymeleaf做服务端渲染,一套代码搞定所有页面,学习成本最低。Thymeleaf的好处是它的语法和HTML天然融合,你在后端通过Model把数据塞给模板,页面上用th:eachth:if就能渲染出来,对于“能跑、能演示”的毕设目标来说非常稳。

但如果你愿意多花一点时间,我更推荐用Vue 2/3 + Element UI做前后端分离。理由同样现实:这符合当前主流开发模式,答辩时能成为亮点;而且Vue + Element UI的组件风格很统一,做出来的界面比Thymeleaf的默认样式好看不止一个档次。老师看系统第一眼看的是界面,界面漂亮首先印象分就加了一截。

前端怎么选,取决于你的余量。我的建议是:如果你从现在开始还有两个月以上时间,果断上Vue前后端分离;如果只剩三四周就要交,老老实实用Thymeleaf,把精力放在后端逻辑上更稳妥。

3. 数据库设计决定你的系统能跑多深:核心表结构拆解

考勤系统功能多不多、逻辑深不深,从数据库表设计就能看出来。很多网上流传的源码,表结构只有可怜的三四张表,那种做出来只能叫“打卡记录展示器”。要做到能应对答辩,至少要有以下这些表。

3.1 基础数据表:用户、部门、角色

用户表是系统的地基,建议命名为sys_user,字段设计可以参考:

字段名类型说明
idbigint主键,自增
usernamevarchar(32)登录账号,唯一
passwordvarchar(128)密码,BCrypt加密存储
real_namevarchar(32)真实姓名
emp_novarchar(32)工号
dept_idbigint所属部门ID
positionvarchar(32)职位
phonevarchar(20)联系电话
emailvarchar(64)邮箱
avatarvarchar(255)头像地址
statustinyint状态:0禁用,1启用
create_timedatetime创建时间

部门表sys_dept字段就相对简单:id、部门名称、上级部门ID(做树形结构)、负责人ID、创建时间。角色表sys_role和用户角色关联表sys_user_role也建议加上,虽然考勤系统权限没那么复杂,但有RBAC基础的权限控制能让你在答辩时显得专业。

3.2 考勤核心表:打卡记录与考勤明细分开

考勤相关表是整个系统的核心,这里有一句话要记住:打卡流水表(原始数据)和考勤结果表(处理后的数据)一定要分开设计。

打卡流水表attendance_record记录每一次打卡的原始数据:

字段名类型说明
idbigint主键
user_idbigint员工ID
clock_timedatetime打卡时间
typetinyint类型:1上班卡,2下班卡
locationvarchar(255)打卡地点(可选)
photo_urlvarchar(255)打卡照片(可选)
sourcetinyint来源:0PC端,1移动端
create_timedatetime记录创建时间

考勤结果表attendance_summary则存储每天通过规则计算后的结果:

字段名类型说明
idbigint主键
user_idbigint员工ID
work_datedate工作日
clock_in_timedatetime实际上班打卡时间
clock_out_timedatetime实际下班打卡时间
statustinyint状态:1正常,2迟到,3早退,4旷工,5请假,6出差,7异常
late_minutesint迟到分钟数
early_minutesint早退分钟数
overtime_hoursdecimal(4,1)加班时长(小时)
remarkvarchar(255)备注

为什么要把流水和结果分开?因为一条打卡流水是“原始事实”,它只有时间、来源这些客观信息;而“迟到”“早退”这些结论,是系统根据考勤规则推算出来的。如果直接把结论写进流水表,万一考勤规则变了(比如上班时间从9点改成9点半),你可能得把历史数据全部重新算一遍。分开设计之后,规则变更只需要重新执行一次汇总计算,流水数据不用动。这一点,面试官和答辩老师都认。

3.3 请假与加班表:考勤和审批的衔接

请假表leave_apply要包含:申请人ID、请假类型(事假、病假、年假、调休等)、开始时间、结束时间、请假时长、请假事由、审批人ID、审批状态(0待审批,1通过,2驳回)、审批意见、申请时间。

加班表overtime_apply结构类似:申请人ID、加班日期、开始时间、结束时间、时长、加班事由、审批状态、审批意见。加班时间在月末汇总时,可以按照公司的规则计入加班时长或调休余额。

这里有一个很多毕设源码都会忽略的点:请假审批通过之后,要自动把请假日期范围内的考勤结果标记为“请假”状态。如果你在做汇总统计时反复把请假记录和考勤记录分开算,代码会很麻烦。更合理的做法是:生成每日考勤结果的逻辑里,先判断当天有没有已通过的请假申请,有就直接标记为请假,不再算迟到早退。

4. 核心功能实现:打卡、请假审批、统计报表三大硬骨头

功能模块多,不代表每个都难。真正值得花时间去抠的,就三块:打卡逻辑、请假审批流、考勤统计。这三块也是答辩时你最可能被追问代码细节的地方。

4.1 打卡逻辑:判断上班卡还是下班卡的关键点

打卡接口是考勤系统里最常被问“你会不会考虑异常情况”的地方。一个粗糙的实现,是后端收到打卡请求就直接往attendance_record里插一条记录。但稍微想深一层,就会出现这些问题:

  • 员工早上打了卡,中午出去吃饭,下午回来又打了一次卡,系统怎么知道哪次是上班卡哪次是下班卡?
  • 员工一天打了八次卡,怎么处理?
  • 员工昨天忘记打下班卡,今早来补打,怎么算?

常见的解法是“按时间窗口判断”:系统预设两个时间窗口,比如上午4:00-12:00的打卡计入上班卡,下午12:00-23:59的打卡计入下班卡,同时处理重复打卡和漏卡。核心逻辑参考:

public void handleClockIn(Long userId, LocalDateTime clockTime) { WorkShift shift = workShiftService.getCurrentShift(); // 判断是上班时间窗口还是下班时间窗口 ClockType clockType; if (clockTime.toLocalTime().isBefore(shift.getMidSeparateTime())) { clockType = ClockType.MORNING; // 上班卡 } else { clockType = ClockType.EVENING; // 下班卡 } // 检查当天该类型是否已打过卡,如果是重复打卡则直接返回 boolean exists = attendanceRecordMapper.exists(userId, LocalDate.now(), clockType); if (exists) { throw new BusinessException("您今天已经打过" + clockType.getDesc() + "了"); } // 保存打卡流水 AttendanceRecord record = new AttendanceRecord(); record.setUserId(userId); record.setClockTime(clockTime); record.setType(clockType.getCode()); attendanceRecordMapper.insert(record); // 实时更新当天的考勤结果 attendanceSummaryService.refreshDailySummary(userId, LocalDate.now()); }

这里有个细节很值得提醒:打卡接口本身不要直接写“迟到”“早退”的结论,它只负责记录事实,然后调用refreshDailySummary去重新计算当天的汇总结果。这样即使你调整了迟到阈值,历史数据也能一键重算。

4.2 请假审批:单体系统里的“轻量工作流”

很多人一听“审批流”就害怕,以为要上Activiti、Flowable这类工作流引擎。其实对于毕设级别的考勤系统,一个请假申请牵扯到的角色最多就是“员工提申请 -> 直属主管审批 -> 考勤管理员可见”,用一张申请表加一个状态字段就能搞定,根本不需要引入流程引擎。

推荐的做法是:leave_apply表里存applicant_idapprover_id,审批操作就是更新audit_statusaudit_comment。核心的联动逻辑在于审批通过后,需要把请假日期范围内的考勤结果标记为“请假”。伪代码如下:

@Transactional public void approveLeave(Long leaveId, Long approverId, String comment, boolean approved) { LeaveApply leave = leaveApplyMapper.selectById(leaveId); if (!leave.getApproverId().equals(approverId)) { throw new BusinessException("您不是该申请的审批人"); } leave.setAuditStatus(approved ? AuditStatus.PASSED : AuditStatus.REJECTED); leave.setAuditComment(comment); leave.setAuditTime(LocalDateTime.now()); leaveApplyMapper.updateById(leave); if (approved) { // 遍历请假期间的每一天,将考勤结果标记为请假 List<LocalDate> dates = getBetweenDates(leave.getStartDate(), leave.getEndDate()); for (LocalDate date : dates) { attendanceSummaryService.markLeave(leave.getApplicantId(), date); } } }

如果还想让审批链更有层次感一点,可以再加一个level字段区分一级审批和二级审批。不过要记住:功能不要过度设计。毕设的核心是把你做出来的东西讲清楚、圆得住,不是追求企业级复杂度。

4.3 考勤月报统计:SQL聚合和Excel导出的配合

统计报表是考勤系统的“门面”——因为老师演示的时候,最爱看的不是你点打卡按钮,而是月底汇总能不能算出每个人的出勤天数、迟到次数、加班时长。

月度汇总可以用一条带条件的聚合SQL来实现:

SELECT user_id, SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS normal_days, SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) AS late_days, SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) AS early_days, SUM(CASE WHEN status = 4 THEN 1 ELSE 0 END) AS absent_days, SUM(CASE WHEN status = 5 THEN 1 ELSE 0 END) AS leave_days, SUM(late_minutes) AS total_late_minutes, SUM(overtime_hours) AS total_overtime_hours FROM attendance_summary WHERE work_date BETWEEN #{startDate} AND #{endDate} GROUP BY user_id

这里有几个容易踩坑的细节。第一,SQL里一定要用SUM(CASE WHEN ... THEN 1 ELSE 0 END)的方式统计,不要用COUNT,因为在有GROUP BY的情况下COUNT很容易统计到NULL行。第二,overtime_hours字段设计成decimal(4,1),是为了避免浮点误差——如果用double,0.3加0.6可能会算成0.8999999,做展示时会很难看。

报表导出功能一般用EasyExcel或POI做。我的建议是直接用EasyExcel,它比POI的API友好很多,几行代码就能导出:

EasyExcel.write(outputStream) .head(AttendanceReportVO.class) .sheet("考勤月报") .doWrite(reportList);

4.4 打卡缺卡和异常数据的处理

考勤系统里最让人头疼的其实是异常数据。员工总会有各种意外:手机没电忘打卡、赶地铁跑太急打了卡但忘了按下班卡、临时有事早退等等。一套成熟的考勤系统,必须给考勤管理员留出补卡申请/后台修正的入口。

可以单独开一张attendance_correction表,字段包括:user_idwork_dateclock_typereasonstatusapply_timeapprove_time。员工提交补卡申请后,管理员审批通过,系统再把该天的考勤结果从“异常”改回“正常”或“迟到n分钟”。

这块功能虽然不大,但在答辩时很出彩,因为它体现了你考虑到了真实业务场景,而不是只写了个理想化的demo。

5. 那些网上源码包不会告诉你的隐藏问题

既然标题里提到“源码+教程打包送”,我就多说几句关于源码使用的实在话。每年都有人从网上下载考勤系统的源码包,有的能跑起来,有的跑不起来,还有的改了三天最后发现不如自己写。以下几个问题在网上下载的源码包里极其常见。

5.1 数据库脚本和SpringBoot版本对不上

这是最常见的坑。你下载了一个号称“SpringBoot 2.7”的项目,结果导入IDEA发现它的pom.xml写的是SpringBoot 1.5——不要惊讶,很多网传源码都是几年前的,根本没更新版本。如果你的本地JDK是17甚至21,用SpringBoot 1.5连跑都跑不起来。

解决办法有两种:一种是下载源码后统一升到SpringBoot 2.7+,同时把javax.*改成jakarta.*包名(SpringBoot 3.0之后的大坑);另一种是本地安装JDK 8,然后配一个低版本的IDEA运行。对毕设来说,我更推荐直接上SpringBoot 2.7.18 + JDK 8 + MySQL 5.7这个组合,兼容性最好,网上资料也最多。

5.2 表名和字段名到处冲突

还有一个很常见的坑是SQL脚本里的表名、字段名跟代码里的实体类对不上,或者字段名用了MySQL函数名(比如rankorder)。遇到这种情况,跑起来后各种SQL报错,解决方案只能是一条条去核对。建议你在导入源码后第一件事不是启动,而是打开项目的application.yml,把数据库连接改成自己的地址,再打开SQL脚本从头到尾执行一遍,确认没有报错再启动项目。

5.3 前端项目跑起来之后后端接口404

前后端分离的源码,最容易出现这种问题。常见原因是前端项目的baseURL设置成localhost:8080,而后端实际端口是8081,或者后端项目部署了/api上下文前缀。这类问题很琐碎,但只要你知道是路径问题,排查起来很快:打开前端页面的Network面板,看请求的实际URL,跟前端配置文件里的axios.defaults.baseURL、后端的server.portcontext-path做个比对。

5.4 如何把网上的源码变成“自己的源码”

这是最核心的问题。很多同学拿到源码后就直接把整个项目搬过去提交了,这非常危险。且不论学术规范问题,单从答辩的角度看——老师让你现场讲代码,你连关键类在哪个包底下、核心方法叫什么名字都不知道,那不是明显露馅吗?

正确做法是:把下载的源码当作“参考答案”,自己重写一遍

具体可以这么操作:

  1. 先看数据库脚本,理解表结构,自己画一遍ER图。
  2. 再看后端Controller层的接口列表,发现系统有哪些功能。
  3. 挑核心功能(比如打卡、汇总统计)的ServiceImpl仔细读,读懂思路后把代码关掉,自己在IDEA里从零写一遍。
  4. 前端页面按自己审美重新调样式,至少把logo、标题、配色换掉。
  5. 最后加上一个下载源码包里没有的小功能——比如导出Excel、短信提醒、图表统计,哪怕只是很简单的柱状图,也能成为你的“个性化亮点”。

这样做下来,虽然代码量很大,但你对系统的理解会完全不同,答辩时无论老师问到哪个角落你都能接住。

6. 答辩前必须准备的五个追问

这部分想跟你说说考勤系统答辩时最容易被挑战的地方。这些问题的答案如果你心里没谱,我建议你在答辩前自己写一遍,不然老师几个问题下来就容易卡壳。

6.1 “你这个系统怎么防止员工代替打卡?”

这是个非常经典的问题。如果你做了移动端H5打卡或者小程序打卡,就可以回答:通过GPS定位校验打卡地点距离公司半径范围,同时结合摄像头拍照上传,管理员可以在后台比对打卡照片。如果只做了PC端网页版打卡,老实说确实很难防止代打卡,你可以答:本系统当前定位为PC端考勤管理场景,移动端打卡通过手机GPS定位和Wi-Fi环境校验来防止代打卡,这是后续扩展方向。然后补充一句:“不过从技术框架看,后端已经为移动端预留了接口,接入定位功能不需要改底层逻辑。”这种回答既承认了现状,又展示了你的系统架构有扩展性。

6.2 “考勤数据量大之后,报表查询变慢怎么办?”

这是性能类问题。你可以从两个层面回答:

  • 索引优化:针对attendance_summary表的user_idwork_date字段建立联合索引,避免全表扫描。
  • 读写分离/定时汇总:把日报表和月报表做成预聚合表,每天凌晨通过定时任务把当天数据算好,报表查询只面对结果表而不是原始流水表。

哪怕你实际没做定时任务,也要表现出你“知道该怎么做”,因为毕设阶段老师看重的其实是你的思路。

6.3 “你考勤状态里的‘异常’是怎么定义的?怎么处理?”

这个问题的核心是想确认你有没有考虑过异常情况。你可以回答:异常包括未打卡、重复打卡、外勤未审批等情况,系统通过每日定时任务自动比对打卡流水和考勤规则,生成异常记录并推送到员工端,员工可发起补卡申请,由管理员审批修正。

6.4 “SpringBoot自动配置的原理是什么?”

这个问题跟考勤系统本身关系不大,但因为你的题目是SpringBoot开头,老师几乎必问。标准答法:SpringBoot启动时会通过@EnableAutoConfiguration注解引入AutoConfigurationImportSelector,它会读取META-INF下的spring.factoriesAutoConfiguration.imports文件,把所有候选的自动配置类加载进来,再配合@ConditionalOnClass@ConditionalOnMissingBean等条件注解按需实例化Bean。

这里建议你提前在本地写个小Demo验证一下,不然光背面试题容易被问住细节。

6.5 “项目的部署方式是怎样的?”

别小看这个问题。很多人做完系统只在IDEA里点过运行,从来没打过jar包,结果答辩时老师说“你能现场部署一下吗”就傻眼了。强烈建议你自己在本地至少执行一次:

mvn clean package -DskipTests java -jar target/attendance-system-0.0.1-SNAPSHOT.jar

如果用了Vue前端,再执行npm run build把dist目录生成出来,配一下Nginx或直接放到后端resources/static目录下供访问。整个过程走一遍,你才算真正知道这个系统怎么交付。

7. 从“交差”到“拿优”:给毕设做一点增量

最后再说一点你可能没想过的东西。每年毕业设计交上去,绝大多数人的系统都是“员工管理+打卡+请假+报表”这个四件套,老师早就审美疲劳了。如果你想拿优秀或者冲出本组,可以在这些方向上做一点点创新,投入成本不高,但很出效果。

第一个方向是统计分析可视化。用ECharts在系统首页展示出勤率趋势折线图、各部门迟到人数柱状图。视觉效果好,且前后端都只需要各花一天就能搞定。后端只要写一个返回统计数据的接口,前端用fetchaxios拿数据后渲染图表即可。

第二个方向是移动端适配。不使用小程序、App这些工作量大的方案,直接用H5把登录页和移动打卡页做好,用手机浏览器访问后端IP就能用。这也能成为答辩亮点。

第三个方向是消息提醒。可以在员工补卡通过、审批通过时向员工发邮件通知。SpringBoot自带的JavaMailSender就能实现,不需要额外引入消息队列,逻辑简单但很显眼。

回到最初的话题,考勤系统这个题目之所以经典,是因为它麻雀虽小五脏俱全,既涉及基础的CRUD,又包含审批流、时间计算、统计报表这些真实业务逻辑。把上面说到的这些吃透,你收获的绝对不止一个能过查重的毕设,还有一套完整的Web系统设计思路。这套思路以后进了公司做任何管理类系统,都能直接复用。真的把这个题目做扎实了,你回头看那些网上下载的源码包,大概率会发现它们还没你自己写的版本好用。

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

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

立即咨询