资讯动态

SpringBoot+Vue+MySQL考勤系统毕业设计:从需求分析到部署全流程

发布时间:2026/10/21 13:28:52 来源:尧图企业网站定制
“这套源码能跑起来是你毕业设计能不能拿优秀最关键的一步但它还远不是全部。”这句话我每年都会跟接手“SpringBootVueMySQL 公司日常考勤系统”这个题目的学生说一遍。考勤系统在毕业设计里属于那种看起来“烂大街”、实际上最稳妥也最好讲出彩的选题。它不像电商系统那样业务链路长、模块又多又杂也不像人工智能方向那样需要厚实的算法功底。它的业务边界非常清楚——打卡、请假、加班、统计、审批刚好覆盖了一个前后端分离项目该有的所有核心知识点权限控制、时间计算、数据关联、报表展示。更关键的是它有一个天然的业务闭环员工在手机或电脑上打一次卡数据落库管理员在后台看到一个统计结果中间这一整条链路都是你可以展开讲的“干货”。这篇博文我打算把当初带学生做这套系统的完整思路、每一个核心模块的实现细节、数据库设计时的坑、论文写作时怎么把代码翻译成“学术话术”以及最后部署时最容易翻车的几个点全部摊开讲一遍。文章比较长但每一段都是实操过的东西不是纸上谈兵。1. 项目整体设计与选题价值分析1.1 为什么考勤系统是毕业设计的“安全牌”和“加分牌”很多学生选题时纠结题目太简单怕答辩没东西讲题目太难又怕做不完。考勤系统恰好卡在中间偏上的位置——它属于“管理系统”大类但业务复杂度比图书管理、学生信息管理高不少。“安全牌”的意思是它不会跑偏。考勤系统的业务规则在任何公司都是存在的你随便问一个身边的人“公司怎么考勤”他都能给你说出一套流程早上打卡、下班打卡、迟到了要记录、请假要走审批流程。这种业务是大家都能理解的答辩时老师不需要你做太多背景铺垫你也不用担心评委听不懂你在做什么。“加分牌”的意思是它有技术亮点可以挖。比如考勤状态的计算逻辑不是简单的增删改查而是要考虑“迟到”“早退”“缺卡”“请假抵消”这几种情况的交叉再比如月度统计报表要处理跨天、跨月的数据边界管理员审批模块涉及多角色权限流转。这些细节做好了答辩时讲出来是有技术含金量的。从工作量预估来说一个人独立完成这套系统集中精力做三到四周是合理的。前端页面大概十来个后端接口二十到三十个数据库表六到八张。这个体量不会让你做到最后疲于奔命也不会显得太单薄。1.2 角色划分与核心需求拆解考勤系统一般分两个角色管理员和普通员工。有的设计里还会拆出“部门主管”用于一级审批但毕设建议控制在两个角色否则权限逻辑会复杂不少时间不够的话会拖垮进度。员工端的需求很直观上下班打卡记录打卡时间自动判断是否迟到、早退个人考勤查询查看某一天、某个月的打卡记录和考勤状态请假申请填请假类型事假、病假、年假、起止时间、事由提交后等待管理员审批加班申请提交加班时间段和加班事由便于后续算调休或补贴个人信息维护修改手机号、邮箱、头像等基础资料管理员端的需求员工管理新增员工、禁用账号、重置密码、调整部门部门管理维护组织架构考勤规则配置设置上下班时间、迟到时间阈值、午休时间是否忽略等考勤记录管理查看所有员工的打卡明细按条件筛选手动修正异常记录请假审批同意或驳回请假申请加班审批同上统计报表按月份统计员工出勤天数、迟到次数、请假时长支持导出把这些需求列出来之后你其实已经拿到了一张非常清晰的功能清单。后面画用例图、建表、写接口全部围绕这张清单展开不会乱。1.3 工作量评估与里程碑规划我建议把整个项目拆成五个里程碑需求分析和数据库设计2~3天画出用例图、ER图建好所有表结构后端基础框架搭建2天SpringBoot项目初始化MyBatis-Plus整合通用返回结果封装JWT工具类核心接口开发7~10天先做登录和员工管理再做打卡、请假、加班最后做统计报表前端页面开发7~10天Vue项目初始化路由和状态管理页面按模块逐个实现联调、部署、论文5~7天前后端联调修bug打包部署边写论文边截图这个排期比较保守中间的缓冲期可以用来处理意想不到的问题。很多学生喜欢先写论文再补代码我强烈不建议。代码跑通了论文只是把做过的事情复述一遍代码没跑通论文怎么写都是空中楼阁。2. 核心技术栈选型背后的逻辑2.1 SpringBoot为什么是它而不是SSM老一代的SSMSpringSpringMVCMyBatis项目需要写大量的XML配置数据源、事务、扫描路径、视图解析器每一项都要手动配。SpringBoot把这些全部自动化了依赖管理用Starter内嵌Tomcat一个main方法就能启动整个项目。对于毕业设计来说省下来的时间应该用在业务逻辑上而不是消耗在配置排错上。SpringBoot还有一个好处是它的生态太成熟了。你遇到任何问题搜索引擎里几乎都有现成的答案。这一点在赶进度的时候尤其重要。2.2 Vue组件化开发让后台管理页面不再痛苦选Vue主要有三个原因第一Vue的模板语法简单直接if、for、model这些指令很好上手第二配合Element UI组件库表格、表单、弹窗、日期选择器都是现成的写一个后台管理页面就像拼积木第三前后端分离的开发模式下前端只负责调接口渲染数据逻辑清晰调试方便。Vue 2配Element UI的生态最稳定Vue 3配Element Plus更现代。如果是从零开始学我建议直接用Vue 3 Element PlusHook的写法更简洁而且Stars多、文档全。但要注意Vue 3的生态里有些老教程不适用遇到问题先在Element Plus官方文档里找答案。2.3 MySQL中小型系统的稳妥选择选择MySQL不用多解释免费、轻量、资料多。考勤系统的数据量级远没有到需要分布式数据库的程度单机MySQL配合InnoDB事务完全能满足并发打卡的需求。有一个细节值得说一下表字段的字符集要统一用utf8mb4不是utf8。utf8mb4是UTF-8的完整实现支持存储emoji表情理论上有更好的扩展性。很多人建表时图省事不指定字符集等到导入数据时发现中文乱码到处排查半天其实根源在这里。2.4 其他工具的补充说明MyBatis-Plus比原生MyBatis省事得多CRUD方法都有现成的分页插件一配就能用。条件构造器QueryWrapper写动态SQL非常方便Redis可以做缓存或者存验证码但考勤系统非必须。如果时间充裕加一个“当日内打卡状态缓存”的场景讲讲也算亮点JWT做登录状态的令牌比Session更适合前后端分离EasyExcel导出月度考勤报表用比POI写起来简单很多几行代码就能把List转换成Excel技术选型这一块在论文里要单独写一章但不需要洋洋洒洒重点讲清楚“为什么这么选”结合项目规模和数据特点说明即可。3. 系统功能模块与数据库设计3.1 数据库表结构全景考勤系统的核心表一共六张下面这个列表是经过实际项目验证的结构系统用户表sys_userid主键自增username登录名唯一索引passwordBCrypt加密后的密码real_name真实姓名dept_id关联部门表role角色1管理员、2员工phone、email联系方式avatar头像地址status账号状态1启用、0禁用create_time创建时间部门表sys_deptid、dept_name、create_time考勤规则表attend_ruleid、rule_namework_start_time上班时间如09:00work_end_time下班时间如18:00late_threshold迟到判定分钟数比如上班时间后5分钟内不算迟到leave_threshold早退判定分钟数is_workday是否工作日简单设计可只存周一到周五的规则考勤记录表attend_recordid、user_id、work_date上班日期check_in_time上班打卡时间check_out_time下班打卡时间status考勤状态0正常、1迟到、2早退、3缺卡remark备注管理员修正时使用唯一索引user_id, work_date避免一个人一天打多次正常卡请假申请表attend_leaveid、user_id、leave_type1事假、2病假、3年假、4调休start_time、end_time起止时间leave_days请假天数由后端根据起止时间自动计算reason请假事由status审批状态0待审批、1通过、2驳回approve_remark审批意见create_time加班申请表attend_overtimeid、user_id、overtime_date、start_time、end_time、hours、reason、status、create_time这六张表设计的核心思想是“业务数据与流程数据分离”。用户表和部门表是基础数据规则表是配置记录表是实际产生的考勤流水。请假和加班是独立的流程表它们的状态流转不干扰主考勤记录的生成。3.2 数据库设计容易踩的坑第一个坑是打卡记录表没有唯一约束。看似只是多打几次卡的问题实际上会导致统计报表里出现重复数据一个员工某天有两条打卡记录月度汇总时就乱了。解决办法就是建表时给user_id, work_date加上联合唯一索引。如果确实需要支持一天多次打卡比如中午外出下午回来再打一次那就要拆分成“打卡流水表”和“每日考勤汇总表”两张表流水表存原始打卡记录汇总表存状态。毕业设计用前者就足够了。第二个坑是时间字段的类型选择。打卡时间建议用DATETIME不要用TIMESTAMP。DATETIME的范围更大而且不涉及时区转换问题对于考勤这种需要精确展示“某年某月某日几点几分”的场景更直接。请假起止时间同理。第三个坑是外键约束。很多教科书会教你建外键但实际开发中尤其是这种中小型项目我建议不要用物理外键而是用逻辑外键在JAVA代码层面通过user_id去联表查询数据库层面只保留索引。原因很简单物理外键在删除部门或员工时会受到约束限制操作起来束手束脚而逻辑外键灵活得多你要删除哪个数据只需在代码里写清楚规则。3.3 核心表设计的“为什么要这么设计”以考勤规则表为例。我遇到过很多学生项目把上下班时间直接写死在代码里比如一个全局变量“正常上班时间为9点”。这样做的后果是要改成8点上班必须改代码重新部署。而设计成数据库表后管理员在后台就能改配置再配合缓存或者每次查询时读取就实现了“规则可配置化”。这个亮点在论文里可以写进“系统的可扩展性设计”章节是很加分的。再比如请假天数的计算。请假表里有start_time和end_time如果只让用户填日期如2024-05-06到2024-05-08那请假天数的计算逻辑是(end - start) 1天。但如果是精确到时分如05-06 09:00到05-08 18:00那就要考虑跨天和半天的场景。毕设建议按日期级别处理减少很多边界判断。4. 核心功能实现要点与代码思路4.1 登录鉴权JWT让前后端身份打通前后端分离项目里登录状态不能靠Session因为前端和后端可能不在同一个域名下Cookie跨域麻烦而且前后端分离本来就应该各自独立。JWT的机制是用户登录成功后后端生成一个签名字符串返回给前端前端把这个Token存到本地localStorage或sessionStorage后续每次请求都把它放在请求头里。后端的处理流程// 登录成功后生成token String token JWT.create() .withClaim(userId, user.getId()) .withClaim(role, user.getRole()) .withExpiresAt(new Date(System.currentTimeMillis() 1000 * 60 * 60 * 24)) .sign(Algorithm.HMAC256(SECRET_KEY));前端在axios请求拦截器里统一加上Tokenservice.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config })后端写一个拦截器拦截需要登录的接口校验Token是否有效。校验不通过就返回401前端收到401后自动跳转登录页。这里有一个很常见的坑Token里存了userId和role前端页面路由也做了角色判断但后端接口没有做权限校验。比如普通员工直接访问管理员的接口地址就能绕过前端限制。严格的做法是后端定义一个自定义注解比如RequireRole(admin)在拦截器里读Token中的role做判断。这虽然麻烦一点但写进论文里是一个安全亮点的体现。4.2 打卡模块考勤状态的计算逻辑打卡是考勤系统最核心的模块也是编码中最需要考虑边界情况的部分。数据库设计里attend_record表会在打卡时先查“今天是否已有记录”没有就插入一天的第一条打卡上班打卡有且check_out_time为空就更新为下班打卡。这里有一个问题如果员工上午打了上班卡中午12点又打了一次卡他本意是“我打完卡出去吃饭”但系统会把这次当作下班卡记进去。所以好的做法是分两个接口打上班卡和打下班卡前端页面上提供两个按钮。后端逻辑如下PostMapping(/checkIn) public Result checkIn(RequestAttribute Long userId) { LocalDate today LocalDate.now(); AttendRecord record recordMapper.selectOne(new QueryWrapperAttendRecord() .eq(user_id, userId) .eq(work_date, today)); if (record ! null record.getCheckInTime() ! null) { return Result.error(今日已打卡); } LocalTime now LocalTime.now(); AttendRule rule ruleMapper.selectOne(new QueryWrapperAttendRule() .eq(is_workday, 1)); String status 0; // 默认正常 if (now.isAfter(rule.getWorkStartTime().plusMinutes(rule.getLateThreshold()))) { status 1; // 迟到 } // 插入或更新记录 return Result.ok(); }下班打卡的逻辑类似判断当前时间是否早于下班时间减去早退阈值。还要注意一种情况如果员工早上忘记打卡直接打下班卡那上班打卡时间是空的。此时需要在打卡逻辑里做特殊处理——下班打卡时检查check_in_time是否为空为空则补一条状态为“缺卡上班”。状态计算总结场景考勤状态上班打卡时间在迟到阈值之前正常上班打卡时间超过迟到阈值迟到下班打卡时间早于早退阈值早退只打了上班卡没有下班卡缺卡下班只打了下班卡没有上班卡缺卡上班 正常下班全天没有任何打卡缺卡这里要注意时间比较时要统一用LocalTime不要用字符串比较更不要用Date去比较时分秒否则很容易出现“9:00:00”和“09:00:00”格式不同的坑。4.3 请假与加班审批流的表设计思路请假和加班本质上是同一种流程用户提交申请管理员审批数据状态流转。所以它们的表结构高度相似代码实现也可以复用一套模板。后端审批接口PutMapping(/approve/{id}) public Result approve(PathVariable Long id, RequestParam Integer status, RequestParam String remark) { AttendLeave leave leaveMapper.selectById(id); if (leave null) { return Result.error(申请不存在); } leave.setStatus(status); leave.setApproveRemark(remark); leaveMapper.updateById(leave); return Result.ok(); }这里有一个值得注意的细节审批通过后要不要自动修改考勤记录比如员工请了5月6日下午的病假那5月6日的考勤状态就不应该算“早退”或者“缺卡”。这是一个非常常见的业务延伸需求。毕业设计里如果做出来是一个明显的加分项。实现方式可以这样在审批通过的逻辑里遍历该员工请假期间的工作日查询每天是否有考勤记录。如果有且当天状态是“迟到”或“早退”且时间落在请假范围内就把状态改成“请假”如果没有记录则插入一条状态为“请假”的记录。这个逻辑不复杂但需要细心处理日期循环。写完之后务必用边界情况测试一遍请假跨月、请假跨周末、请假半天。4.4 统计报表如何把零散记录变成有价值的汇总月度统计是考勤系统里另一个核心点也是答辩时老师最爱问的部分。统计模块一般接受一个月份参数如2024-05返回每位员工的汇总信息public class AttendSummaryVO { private Long userId; private String realName; private int workDays; // 应出勤天数 private int actualDays; // 实际出勤天数 private int lateCount; // 迟到次数 private int earlyLeaveCount; // 早退次数 private int absentCount; // 缺卡次数 private double leaveHours; // 请假总时长小时 private double overtimeHours; // 加班总时长 }实现思路是先把当月每天的考勤记录批量查出来在内存中做统计不要逐条访问数据库。数据量不大内存统计是最高效的。统计的另一个重点是Excel导出。用EasyExcel导出时在列表数据上直接映射即可EasyExcel.write(outputStream, AttendSummaryVO.class).sheet(考勤统计).doWrite(list);这里的坑是字段映射的注解要写对日期格式要加DateTimeFormat数字要保留小数位。导出后发现Excel打不开十有八九是数据类型或者列顺序映射出了问题。4.5 前端页面Vue组件与状态的配合前端我建议采用这样的目录结构src/ api/ // axios请求封装 views/ login.vue layout.vue // 后台布局 employee/ index.vue // 员工列表 profile.vue // 个人信息 attendance/ check.vue // 打卡页面 history.vue // 考勤记录 summary.vue // 月度统计 leave/ apply.vue // 请假申请 myLeave.vue // 我的请假 approve.vue // 管理员审批 overtime/ apply.vue // 加班申请 approve.vue // 管理员审批 router/index.js // 路由配置 store/ // Pinia/Vuex状态管理打卡页面是最有意思的。用Element Plus的统计组件展示当前时间每秒刷新一次。页面放两个大按钮一个“上班打卡”一个“下班打卡”打卡成功后立刻显示今天的打卡时间和考勤状态。这个交互虽然简单但对于毕业设计来说视觉效果好演示时很有感染力。状态管理里就存三样东西用户信息、Token、角色。进入路由前先判断是否有Token没有就跳登录页。路由守卫里判断目标页面需要的角色有管理员权限的页面如员工管理只有管理员能访问。5. 论文写作与项目文档组织5.1 论文结构的标准模板考勤系统这一类管理系统论文结构高度统一学校一般也会给模板。核心章节如下第一章 绪论项目背景、国内外研究现状、选题意义、论文组织结构第二章 相关技术介绍SpringBoot、Vue、MySQL、MyBatis-Plus、JWT每个小节介绍一项技术第三章 系统需求分析可行性分析、功能需求分析、用例图、非功能需求第四章 系统设计总体架构图、功能模块设计、数据库设计ER图加表结构说明、接口设计第五章 系统实现分模块截图加核心代码逐段说明实现过程第六章 系统测试测试环境、测试用例表、测试结果分析第七章 总结与展望总结得失5.2 如何把代码翻译成论文语言很多学生论文写得像代码注释这是大忌。论文里贴代码段没问题但必须用文字讲清楚“这段代码解决了什么问题为什么用这种方式解决”。举个例子不要在论文里写“我用QueryWrapper查出了全部数据”而是应该写“考勤记录查询模块中考虑到现实场景中管理员常常需要按照日期范围、员工姓名、考勤状态等多个条件组合筛选数据系统使用MyBatis-Plus提供的条件构造器动态拼接查询条件避免了手动编写多条SQL语句的繁琐过程提高了代码的可维护性。”论文里所有的功能描述都应该遵循这个公式业务背景 实现手段 带来的价值。5.3 截图技巧和注意事项系统实现那一章需要大量截图质量好的截图需要提前准备截图要清晰分辨率高调整好浏览器缩放比推荐100%或125%不要缩放得奇奇怪怪每个页面都要有合理的展示数据。员工列表要有七八条数据状态有启用有禁用同一页面的不同状态要分别截图。比如考勤记录页面既要有“正常”的记录又要有“迟到”“早退”的示例演示截图时把当前用户的登录窗口隐藏掉避免出现暴露密码之类的敏感信息更节省时间的方法是先把所有功能跑起来专门花半天按照“登录→员工管理→考勤打卡→请假→审批→统计报表”的顺序把所有需要用的数据准备齐全一次性截图。不要写完一页功能截一张那样效率太低前面截的图还不一定能用。5.4 测试章节的写法测试这一章经常被忽视但答辩时被问的概率很高。不要只写“登录测试成功”要写成测试用例的形式测试编号测试内容操作步骤预期结果实际结果TC-01员工登录输入正确用户名密码点击登录登录成功跳转首页通过TC-02管理员登录输入错误密码提示密码错误通过TC-03上班打卡在迟到阈值前打卡状态为正常通过TC-04上班打卡超过迟到阈值打卡状态为迟到通过TC-05请假审批管理员通过请假申请请假状态变为通过通过写清楚步骤和预期结果比泛泛而谈“系统运行良好”有说服力得多。测试用例表也是工作量的一种体现老师看得到你做过相关实验。6. 部署实战与常见问题排查6.1 本地开发环境的搭建开发环境建议统一版本避免版本不一致导致的奇怪问题JDK 1.8Maven 3.6Node.js 16MySQL 5.7或8.0Redis可选SpringBoot的后端项目里application.yml配置server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/attendance_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl mapper-locations: classpath*:mapper/*.xml特别注意serverTimezone如果不加这个参数MySQL和Java的时区不一致会导致时间晚8小时打卡记录全面错乱。这是最典型的部署坑十个项目里有三个会碰到。6.2 前端项目的启动前端启动前先安装依赖npm install如果网络问题装得慢可以换成淘宝镜像源。启动开发环境npm run serve前端开发时和后端联调要先在vue.config.js里配置代理const { defineConfig } require(vue/cli-service) module.exports defineConfig({ devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, ws: true } } } })配置代理后前端请求 /api/login 会被转发到后端的 8080 端口。否则跨域问题会让你联调阶段寸步难行。6.3 生产环境的打包部署后端打包mvn clean package -DskipTests打包成功后target目录下会生成一个Jar包运行java -jar attendance-system.jar前端打包npm run build生产环境如果不用Nginx最简单的做法是将dist目录里的静态文件放到Nginx或Tomcat里部署。用Nginx时同时做反向代理将API请求转发到后端server { listen 80; server_name localhost; location / { root /opt/attendance/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }try_files这一句的作用是前端是Vue的history路由模式刷新深链页面时请求后端找不到对应路径就统一返回index.html由前端路由接管。如果不加这一句刷新/employee页面会直接404。上传服务器时要先把数据库脚本在服务器MySQL上执行一遍把初始管理员账号创建好。同时记得给MySQL创建独立用户不要直接用root权限越少越安全。6.4 常见问题排查速查表这段时间带学生排坑我整理了一张速查表遇到问题先对照着查故障现象可能原因解决办法前端请求接口报404代理没配或后端没启动检查vue.config.js代理和8080端口前端请求接口报CORSNginx未配置反向代理检查nginx location /api配置中文显示乱码MySQL连接字符集不对URL加characterEncodingutf8查询结果时间相差8小时无serverTimezone参数配置serverTimezoneAsia/Shanghai打包Jar后启动报错数据库连接失败检查服务器MySQL是否允许远程连接打卡按钮点了没反应Token过期或请求头没带检查拦截器返回的401响应上传图片预览不了静态资源路径不对后端配置WebMvcConfigurer映射审批状态不更新前端传了status但后端没接收检查参数名和注解是否匹配6.5 答辩演示时的部署建议答辩现场最常见的翻车场景是网络不稳定本地数据库没启动或者前端打包文件路径不对。我的建议是答辩前一天做一次完整的“断电演练”——关闭所有开发工具断开网络从零开始按部署文档操作一遍启动MySQL、导入或连接数据库、运行后端Jar、启动前端静态服务。能用一台电脑完成就绝不依赖网络。如果时间实在紧张至少准备一台装好环境的备用电脑以及一份直接在服务器上运行的可执行Jar包。把部署步骤压缩到一个脚本文件里答辩前双击就能恢复环境。这些细节看着琐碎但关系到你台上十分钟的稳定发挥。个人经验做完这套系统你带走的不仅仅是“毕设通过”带过好几届学生做这个题目我的体会是考勤系统的难点从来不是某一个技术而是把“时间处理”、“状态判断”、“审批流”这些零散琐碎的点在一个清晰的架构里组织起来。你掌握的其实是一套完整的“业务系统开发方法论”——从分析需求、设计表结构到实现接口、联调页面再到大包部署、撰写文档。这一步走通之后你会发现后面再看别的管理系统无论是仓库管理、订单管理还是OA系统框架都是一样的差异只在于业务规则。最后分享一个小技巧在论文致谢前的“总结”章节里不要写“系统还有不足”这种套话。要写成“未来可以从以下方向进一步完善”然后列出三个具体的扩展点——智能识别迟到早退趋势、移动端小程序打卡、基于LBS的定位考勤。这代表你在做系统时想过它的演进方向而不是只把它当成拿来应付评分的作业。如果这篇长文能帮你少熬夜排几个bug或者让选题前那个犹豫不决的你下定了决心那它就没白写。做毕设最怕的不是题难是心不定。把需求拆开、每个模块吃掉、一步一步来这套考勤系统一定能在你手里跑起来。

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

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

免费获取报价 →
↑