资讯动态

SpringBoot2+Vue3考勤系统源码拆解:从环境搭建到二开实战

发布时间:2026/10/2 2:52:41 来源:尧图企业网站定制
说实话市面上的考勤系统源码我见过太多种从SSH框架时代一直到现在的前后端分离标题里写着SpringBoot2Vue3MyBatis-PlusMySQL8.0的一套比一套整齐。但真正下载下来能跑通的人十个里顶多三四个卡住的点几乎都集中在数据库连接、版本不匹配、前后端端口对接这几处。今天我就以这套大学生考勤系统为样本把这类项目从需求拆解到代码结构、从环境搭建到二开思路完整过一遍至少让你拿到手之后不会两眼一抹黑。1. 考勤系统这类源码的需求边界它到底在解决什么问题1.1 最小可用考勤系统的功能闭环很多人拿到考勤系统源码第一反应是去看登录页好不好看、页面多不多这是完全错误的切入点。考勤系统的核心不是页面而是业务闭环。完整的学生考勤业务必须能把从数据维护到统计导出这条链路串起来人员数据维护 → 考勤事件产生 → 异常考勤识别 → 统计分析导出也就是先得有学生、班级、课程、教师这些基础数据然后才有某学生某节课打卡这个事件再根据打卡时间和课程时间判断出正常、迟到、早退、缺勤、请假这些状态最后才能按班级、按日期、按课程维度汇总出出勤统计。如果你打开的源码里只有一张student表加一张attendance表那后面所有的统计功能都会非常别扭因为班级、课程、教师、课表这几个维度没有拆开查不了三班这周英语课的出勤率这种最常见的需求。我拿到这类项目第一步永远是翻数据库脚本看有没有class_info、course、schedule、leave_record这些关联表。表拆得清楚说明作者是真的理解考勤业务而不是只会做增删改查。这点也直接决定了你后面二开时的工作量大小。1.2 从标题里的技术栈能反推出什么标题不是随便写的每个词背后都有讲究。SpringBoot2意味着后端大概率跑在JDK8或JDK11上不用面对JDK17之后那些模块化带来的坑Maven依赖也好找。Vue3说明前端用组合式API组件复用方式从options API换成了setup函数加ref/reactiveVue3Element Plus这个组合现在资料也是最多的。MyBatis-Plus说明单表CRUD交给了BaseMapper代码量比原生MyBatis少了一大截代价是有一些SQL需要自己兜底。MySQL8.0则暗示数据库默认字符集是utf8mb4认证插件默认是caching_sha2_password连接串上如果不做配置会踩到老驱动不兼容的坑。这几个技术点拼起来这套系统的可运行门槛其实不高只要环境版本在合理区间内启动过程应该是顺滑的。怕就怕不看版本直接跑报错之后才开始怀疑人生。所以我建议读源码之前先对着技术栈把环境检查一遍。2. 后端代码骨架SpringBoot2和MyBatis-Plus是怎么把考勤业务拆开的2.1 Controller、Service、Mapper的标准职责划分打开后端工程先别急着跑看包结构。合格的考勤系统源码包名一般是com.xxx.attendance下面按模块拆包controller、service、mapper、entity、config、common、utils。如果整个项目所有类都堆在几个包里不用怀疑这就是典型的课程设计随手作品后面改起来会非常痛苦。Controller层应该很薄只做三件事接收参数、调用Service、返回统一结果。很多源码里有个Result或R类里面是code、message、data三个字段Controller返回这个对象前端axios响应拦截器统一判断code200再渲染数据。这个设计看着简单但它会让前后端联调舒服很多。Service层是业务逻辑的主战场。以考勤打卡为例基本流程是接收学生ID和课程ID → 判断当前时间是否在合法打卡区间 → 查有没有重复记录 → 插入记录 → 更新统计结果。这里最值得学的是先查后插的防重思路。很多烂代码是直接insert一条考勤记录学生同一天同一节课能点十几次打卡全留下记录统计直接炸掉。2.2 MyBatis-Plus帮你扛了哪些活哪些活它扛不了用mybatis-plus-boot-starter搭配SpringBoot2你会发现单表操作真的可以不写SQL。BaseMapper自带的selectById、selectPage、insert、updateById覆盖了考勤系统里绝大部分基础CRUD。配合LambdaQueryWrapper条件查询也安全很多列名是编译期校验不容易出现老MyBatis里手写字符串拼错列名的问题。举个例子按班级ID查学生列表直接LambdaQueryWrapperStudent wrapper new LambdaQueryWrapper(); wrapper.eq(Student::getClassId, classId) .eq(Student::getStatus, 1) .orderByAsc(Student::getStudentNo); ListStudent students studentMapper.selectList(wrapper);不用写XML不用拼接字符串可读性也好。这套写法的确是查单表的利器。但MyBatis-Plus不是万能的。跨表统计、分组聚合这类场景用它做起来会非常绕。比如某个学生本月迟到次数要关联student、course、attendance_record还要做COUNT和分组。这种SQL就别硬用Wrapper拼了直接写在Mapper接口上或者XML里Select(SELECT COUNT(*) FROM attendance_record r WHERE r.stu_id #{stuId} AND r.status 2 AND r.attendance_date BETWEEN #{startDate} AND #{endDate}) int countLate(Param(stuId) Long stuId, Param(startDate) String startDate, Param(endDate) String endDate);我看到有些改写源码的人为了全程不用写SQL把复杂的联表查询硬拆成多次单表查询再在Java内存里做关联。数据量小的时候确实能跑数据一多就慢得离谱。正确的原则是单表CRUD交给MyBatis-Plus跨表统计交给原生SQL两者配合才健康。还有两个分页相关的点尤其重要必须配置MybatisPlusInterceptor和PaginationInnerInterceptor否则selectPage返回的total永远是0看起来像是数据分页失效批量插入考勤记录时JDBC连接串上加上rewriteBatchedStatementstrue批量插入的性能才能真正释放。考勤记录表的数据量虽然不大但学期末一键导入几千条时还是能感受到明显区别的。2.3 JWT登录鉴权的轻量实现方式考勤系统的角色一般就三种管理员、教师、学生。用Spring Security全套配置在这个体量下有点杀鸡用牛刀多数手写源码的惯用方案是JWT加自定义过滤器。流程非常好理解登录成功 → 后端生成一个JWT令牌里面塞入userId和role→ 前端把令牌存在本地后续请求在Header里带Authorization: Bearer xxx→ 后端过滤器解析令牌放行请求进入Controller。这里有个新手最喜欢犯的错误只校验令牌能不能解析不校验用户还在不在。更好的做法是在过滤器里用userId回查一下数据库或者查缓存账号被禁用或删除时立刻拦截。这个细节在答辩时是加分项面试时也能当成亮点讲。角色权限也要注意。管理员的接口是不能让学生直接调的。最笨的写法是每个接口里自己写if(role.equals(admin))代码会很冗余。稍微像样的做法是自定义一个RequireRole注解配合拦截器统一处理。我在改这类源码时一定会确认角色校验不在前端路由层就结束而是后端也有一道闸门否则学生把本地存储的role字段改成teacher就能绕过权限这是低级漏洞。3. Vue3前端工程页面组织与后端对接的实操细节3.1 前端目录结构怎么排才算清晰Vue3项目现在大多用Vite创建目录一般是src/views、src/api、src/router、src/store、src/utils。考勤系统页面不算多核心也就登录页、首页工作台、学生管理、班级管理、考勤打卡、考勤统计、请假审批这几块。模块划分上有一个常见毛病把考勤打卡和考勤统计塞到同一个页面里。看着方便其实后患无穷。打卡页面是高频率实时操作统计页面是低频率复杂查询两者的权限、缓存策略、组件复用方式都不一样强行揉在一起代码会越来越臃肿。分开之后你还可以把统计页面单独做成教师端或管理员的专属接口。权限路由这里我比较推荐用动态路由而不是纯meta控制。登录后从后端拉取当前用户的角色和菜单权限根据权限列表动态addRoute。管理员和学生打开同一个系统左侧菜单完全不同学生根本看不到管理页入口。这在答辩时被问如何防止学生访问管理员页面的概率几乎是百分之百动态路由后端角色校验是一个能展开讲的完整答案。3.2 axios封装与开发阶段跨域处理前端和后端对接质量好不好看src/api目录就很清楚。标准做法是按模块拆分文件auth.js管登录、student.js管学生、attendance.js管考勤、stats.js管统计。每个文件里的函数返回值是Promise页面组件里只负责await和渲染不直接拼URL。请求拦截器要做两件事从store或localStorage拿token放到请求头在响应拦截器里统一处理业务码。如果后端返回的code表示登录过期应该清掉本地token并跳转登录页而不是只弹一个报错让用户干瞪眼。开发阶段的跨域问题我在跑源码时见得特别多。前后端分离项目最佳实践是在vite.config.js里配置server.proxy把/api代理到http://localhost:8080。这样后端Controller不需要加一堆CrossOrigin注解前端开发和线上部署都干净。如果你拿到手的是在Controller上到处写跨域的版本建议改成代理方式因为上线后前后端多半是通过Nginx同域代理的早期依赖CrossOrigin会留下安全面和后期改造面。一个最简配置长这样// vite.config.js export default defineConfig({ server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })3.3 打卡按钮的状态设计与本地时间陷阱打卡功能是考勤系统里交互要求最高的模块。按钮就一个但状态必须分清楚未到打卡时间、可打卡、已打卡、请假。前端拿到当前课程信息后要同时判断课程时间和已存在记录。如果后端做了重复校验前端也要把已打卡状态显示出来否则学生以为打卡成功实际上请求被后端拒绝了体验很差。这里有个必须强调的坑不要用前端本地时间去判断是否在可打卡区间。学生电脑时间快了五分钟前端校验直接放行后端按服务器时间校验又拒绝两边一打架就是一堆我明明打卡了为什么不在的投诉。最稳的做法是打卡接口不依赖前端时间而是由后端返回当前服务器时间再把是否在打卡区间的判断也放在后端完成。前端只需要展示结果时间对齐的问题就不用反复纠结。4. MySQL8.0建库建表和连接配置的坑4.1 考勤核心表结构与索引设计我见过的考勤源码里表设计最常犯的错误是该拆的表不拆该建的索引不建。比如课程和班级混在一张表里课表上周一第3节直接写成字符串后期统计的时候简直烧脑。合理的表结构大致是这样几个层次student学生主表student_no唯一索引class_id普通索引class_info班级表包含专业、年级course课程表关联教师IDschedule课表关联班级、课程、教师、星期几、第几节attendance_record考勤记录表包含学生ID、课程ID、日期、节次、状态leave_record请假表包含学生ID、开始时间、结束时间、原因、审批状态。其中最关键的一条attendance_record表一定要建联合唯一索引ALTER TABLE attendance_record ADD UNIQUE KEY uk_stu_course_date (stu_id, course_id, attendance_date);这个索引是从数据库层面保证不会插入重复考勤比依赖Java代码的先查后插更硬。你可能会问代码里也有去重逻辑为什么还要建索引因为代码去重是串行的并发场景下两个请求同时通过检查就会插入两条重复记录。唯一索引一建重复的那条直接报错再由代码捕获异常转化成提示逻辑反而简单了。索引不是越多越好考勤系统数据量没那么夸张两三个联合索引完全够用。给所有表都加上create_time、update_time两个审计字段就够了这能在排查数据问题时多留一条线索。4.2 MySQL8.0连接串的三大高频错误MySQL8.0默认认证插件是caching_sha2_password这就导致老版本的数据库驱动第一次连接时会报Public Key Retrieval is not allowed。解决办法是在JDBC连接串上显式加allowPublicKeyRetrievaltrue或者useSSLfalse。这个报错在搜索栏里出现频率极高凡是MySQL8.0几乎都遇到过也不算冷门。时区问题是另一个典型的隐性坑。启动项目后你会发现数据库存的时间和本地时间差了8小时或16小时。原因就是连接串没指定时区驱动取了服务器默认时区。我的写法统一用这一串url: jdbc:mysql://localhost:3306/attendance?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueuseSSLfalse把serverTimezone写明白之后时区问题基本绝迹。还有一个容易忽略的坑MySQL8.0默认字符集虽然是utf8mb4但你建库时不显式指定某些环境依然会掉回utf8mb3或者其他乱码状态。尤其是考勤表里如果有学生姓名含生僻字或符号导入时就可能出现乱码。建库语句建议固定写成CREATE DATABASE attendance DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;顺手把表和字段的字符集也统一了后面导入导出Excel才不会出现中文变问号的情况。5. 跑通这套源码的完整步骤与排错顺序5.1 环境版本核对与启动顺序拿到源码第一件事不要双击启动先把环境版本核对一遍。版本核对完基本已经避掉一半坑。组件推荐版本备注JDK8或11SpringBoot2对JDK8支持最稳JDK17需谨慎Maven3.6.3以上配置阿里云镜像依赖下载会快很多Node.js16或18Vite3/Vite4需要14.18建议16.19MySQL8.0.x使用Navicat或命令行导入SQL脚本前端包管理npm/pnpm老旧依赖冲突时优先切pnpm启动顺序也固定先启动数据库导入SQL脚本再启动后端看到Tomcat started on port 8080最后启动前端Vite默认端口一般为5173。浏览器访问5173登录管理员账号先别急着欣赏页面直接打开考勤记录模块看一眼确认数据是通的再去做其他验证。如果是后端启动失败看日志是最快路径。日志里没有明显的SQL异常就去检查application.yml。很多源码把数据库密码写成root或者123456你本机的密码不一致启动照样报错。5.2 四个高频报错的完整排查链路我按出现频率把常见的报错排了个序供你参考第一个是数据库Access denied。报错长这样Access denied for user rootlocalhost。不要马上去改代码先去命令行做一次最原始的验证mysql -uroot -p如果这个能登录成功那就是application.yml里账号密码、库名、端口有问题如果命令行本身就登录失败那是MySQL的用户密码或权限配置有问题。按这个顺序判断几分钟就能定位。第二个是分页查询total为0。列表页有数据但分页总数不对九成是没配置MyBatis-Plus分页插件。查找有没有一个MybatisPlusConfig配置类里面有没有注册MybatisPlusInterceptor并添加PaginationInnerInterceptor。缺了这个selectPage方法是不会真正执行COUNT和LIMIT的返回的total永远是0。第三个是前端npm run dev启动失败。一般有两种情况Node版本过高导致依赖编译失败或者旧依赖锁文件与当前包管理器版本不兼容。最简单的方法是删除node_modules和package-lock.json换pnpm重新安装。旧项目尽量别用node-sass统一改成sass否则编译时会报Binding错误这已经是Vue3项目的常识了。第四个是跨域导致登录请求发不出。前端报CORS或Request failed后端日志却很干净大概率是代理没生效。先确认vite.config.js里proxy目标指向是否正确再看请求路径是否以/api开头。后端接口如果本来就没有前缀代理也要做路径重写前端调用时带上/apivite代理再把/api剥离转发。这个配置逻辑想清楚后跨域问题就只是配置细节了。调试前端接口还有一个土办法用浏览器的Network面板先确认请求有没有发出去再用命令行工具直接对着后端接口打一次比如curl http://localhost:8080/api/student/list。后端接口通了问题就只在前端代理或封装层不会两边互相甩锅。6. 二开建议从能跑到能答辩如何进阶6.1 考勤去重与异常复核模块一个能应付答辩的考勤系统光有CRUD是不够的。我的第一个二开建议是补上异常考勤复核功能管理员进入一个待审核列表把系统判定为迟到、早退、缺勤的记录逐条复核可以核准或驳回复核状态写进新字段。这个功能会让你涉及到状态机设计、操作日志、表结构变更都是真实业务里常见的能力答辩时也有内容可讲。防重逻辑则用一个唯一索引加INSERT ... ON DUPLICATE KEY UPDATE就能处理干净不用写复杂业务代码。学生端打卡时如果重复提交数据库报唯一键冲突后端捕获后返回您已完成打卡逻辑闭环。6.2 统计报表从表格到图表考勤系统的统计模块大多停留在表格。你可以引入ECharts做按周、按月的出勤率趋势图后端提供按时间聚合好的数据接口前端画图。关键点在于聚合统计的SQL要写好GROUP BY和DATE_FORMAT能解决大部分按月按周统计的需求别把整表返回给前端让浏览器算——数据量小看不出来数据一多就卡。一个有用的统计SQL片段可以长这样SELECT DATE_FORMAT(attendance_date, %Y-%m) AS month, COUNT(*) AS total_count, SUM(status 1) AS normal_count FROM attendance_record WHERE course_id #{courseId} GROUP BY DATE_FORMAT(attendance_date, %Y-%m);先让统计接口返回聚合好的数据前端再用ECharts渲染折线图或柱状图这个方向做出来视觉冲击力强答辩效果也很好。6.3 多端入口设计与打包部署顺序另一个有价值的二开方向是把系统拆成管理端和学生端两个入口。学生端在手机上访问扫码打卡页面管理端在PC上维护数据。前后端分离的项目天然支持这种拆分后端接口不用大改前端加一套移动端页面就行。部署顺序可以总结成固定套路后端mvn clean package生成jar包java -jar运行前端npm run build生成dist目录交给Nginx托管静态文件。Nginx配置里做一层转发把/api开头的请求代理到后端端口前端静态页面和API请求就在同一个域下了。这一套走通之后代码才算真正交付而不仅仅是本地开发环境能跑。最后说一句我自己一直在坚持的习惯拿到任何源码不管功能多少第一件事是沿着核心业务走一条完整链路从登录到打卡从统计到导出逐段验证。链路走通再研究细节感觉会和只看教程完全不同。这个考勤系统如果能把人员管理—打卡—统计—权限这条主线吃透你对SpringBoot2Vue3这套组合的把握基本上就达到能独立干活的程度了。

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

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

免费获取报价 →
↑