ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Vue考勤系统核心逻辑与数据库设计全拆解

SpringBoot+Vue考勤系统核心逻辑与数据库设计全拆解 考勤系统大概是计算机毕业设计里被做得最烂、也最被低估的一个选题。说它被做得烂是因为每年都有大量同学从网上拿到一套SpringBootVue 考勤管理平台源码跑通界面就算完事连考勤判定的核心逻辑在哪一行都没看过。说它被低估是因为同一个选题有人只做到“学生表和打卡表各一张、CRUD 糊弄过去”有人却能理直气壮地讲清楚权限模型、考勤规则、请假审批、统计报表这条完整链路。这篇博文就是想把后面这条链路彻底讲透。你将看到一套基于JavaMySQL的 SpringBootVue 前后端分离考勤系统从功能拆分、数据库设计、核心业务逻辑到本地部署和答辩扩展我会把实际部署和改造这类系统时踩过的坑、验证过的方案全部摆出来。不管你是做毕业设计、课程设计还是单纯想用这个项目练手学全栈这套思路都能让你的项目从“能跑”变成“能讲”。1. 为什么考勤类管理系统是毕设“安全牌”也是最容易翻车的选题不管你看多少选题推荐考勤类管理系统永远稳定地出现在“适合毕设/课设”的列表里。原因很简单技术栈覆盖均匀、业务链路完整、扩展空间充足。SpringBoot 处理后端接口和业务逻辑Vue 做前端页面与交互MySQL 存数据这三样正好覆盖大多数学校对毕设的技术考核点。而且考勤不是一个“玩具需求”——它有真实的角色体系管理员、教师、学生、有复杂的业务规则迟到早退缺勤怎么判定、请假怎么审批、有数据统计需求出勤率、缺勤次数拿它来做项目几乎能把课程里学的东西完整串一遍。但正因为热门它也最容易翻车。翻车点往往不在技术本身而在“你拿到源码后做了什么”。网上流传的考勤系统源码版本非常多有的数据库脚本写了一半有的前端依赖版本老到 npm install 直接报错有的甚至连学生签到都只是在前端写死了一条记录。如果你只是在答辩前把它跑起来、截几张图这个项目反而会给你带来麻烦——因为老师问的每一个问题都可能在源码对应的位置暴露出你根本没看过它。所以这篇博文我尽量按照“拿到一套源码之后应该如何消化它”的顺序来讲先看功能边界再看技术结构然后理解数据库和核心逻辑最后把它跑起来并做个性化扩展。这个顺序其实就是你真正需要理解一个项目的顺序。2. 功能边界拆解一套能过答辩的考勤系统该有哪些模块2.1 三种角色三种完全不同的使用视角一套合格的考勤系统一定不是“所有人都能打卡”这么简单而是围绕角色划分权限。最常见的角色划分是管理员、教师、学生三类各管各的事。角色核心职责典型页面管理员维护基础数据、配置规则、查看全局统计学生管理、教师管理、课程管理、全局考勤统计教师管理自己授课课程的考勤、审批请假我的课程、点名补签、请假审批、课程出勤报表学生上课签到、提交请假、查看个人记录今日课表、在线签到、请假申请、我的考勤这个权限模型是答辩时的重点。为什么学生看不到别人的考勤为什么教师只能操作自己名下的课程这类问题背后都是权限设计。常见的实现方案是 Spring Security JWT登录后返回角色后端接口通过注解或拦截器校验角色前端路由也做一层角色过滤。前后端双重校验既能防止直接调接口越权也能让页面不渲染无权限的入口。2.2 核心业务模块不是简单 CRUD如果把功能拆到页面级别大致有下面这些登录认证模块账号密码登录JWT 签发与校验退出登录。学生信息管理管理员维护学生的学号、姓名、班级、专业支持 Excel 批量导入。这里值得注意批量导入几乎是每套毕设系统都会用到的功能用 EasyExcel 或 POI 都能做但 EasyExcel 写起来省很多事。课程与排课管理管理员维护课程基本信息然后把课程分配给教师再把学生选课关系维护好。一门课要有上课时间星期几、第几节、上课地点、授课教师这些字段后面考勤判定都要用。在线签到学生在自己课表页看到当天课程点击签到按钮系统根据当前时间和定位判断状态。这是整个系统的核心页面。请假申请与审批学生提交请假申请选课程、填原因、选时间教师看到待审批列表通过或驳回。审批通过后请假时间段内的缺勤记录要自动修正为“请假”状态。考勤记录管理教师可以对异常记录进行补签、修改状态管理员可以看到全系统的考勤流水。统计报表按课程、班级、时间段统计出勤率、迟到率、缺勤次数用表格或图表展示支持导出 Excel。把这些模块讲清楚之后你会发现它已经覆盖了一个完整信息管理系统该有的东西认证授权、基础数据维护、业务流转、审批流、统计导出。这也是为什么考勤系统在开题、中期、答辩三个阶段都能拿出“正经功能”来汇报而不是只能演示一个登录页加一个列表页。3. 技术选型和工程结构SpringBootVue 这套黄金组合好在哪3.1 为什么是这个组合先说后端。SpringBoot 最核心的价值是“约定大于配置”内嵌 Tomcat自带起步依赖一个spring-boot-starter-web就把 Web 环境全部拉起来省掉了传统 SSM 项目里大量 XML 配置。对毕设来说这意味着你可以把精力放在业务逻辑上而不是在配置上面反复折腾。配合MyBatis Plus这样的 ORM 增强框架单表 CRUD 可以直接用 BaseMapper 里的现成方法开发速度非常快。前端选 Vue 的理由更直接组件化开发适合后台管理系统这种“页面结构类似、逻辑重复”的场景。一个学生管理页和一个教师管理页长得差不多抽成公共组件后改改字段就能复用。再加上 Element UI / Element Plus 这套组件库表格、表单、弹窗、分页全是现成的前端页面开发效率直接翻倍。我不太推荐在这个项目里硬上特别复杂的前端架构Vue Vue Router Vuex/Pinia Axios 已经足够撑起整个项目的前端部分。MySQL 则是所有组合里最没争议的。轻量、免费、学校机房一般都有现成环境网上教程也多出了问题好排查。数据库选型别整花活除非你明确知道自己在干什么否则用 MySQL 就是最稳妥的选择。3.2 工程目录结构拿到源码先看这几层一套规范的前后端分离项目后端一定是分层的。常见结构是这样的backend/ src/main/java/com/xxx/attendance/ controller/ # 接口层只做参数接收和结果封装 service/ # 业务逻辑层考勤判定、请假审批都在这里 mapper/ # 数据访问层对应 MyBatis 的 Mapper 接口 entity/ # 数据库实体类 config/ # 配置类比如跨域配置、JWT 拦截器配置 common/ # 公共类统一返回结果、异常处理、工具类 src/main/resources/ application.yml # 数据源、端口、JWT 密钥等配置 frontend/ src/ api/ # 接口请求封装按模块拆文件 views/ # 页面组件一个路由对应一个页面 components/ # 公共组件 router/ # 路由配置含角色守卫 store/ # 全局状态存用户信息和登录状态拿到源码先别急着跑按这个目录扫一遍搞清楚每个包是干什么的。面试和答辩时老师经常让你“讲讲这个项目从请求到响应的完整链路”你能顺着 Controller → Service → Mapper → MySQL 把一条签到请求讲下来就已经赢过大多数人了。3.3 前后端如何配合代理、请求封装与认证开发环境下前端起在 8080 端口后端起在 8081 端口浏览器直接跨域。最简单的解决办法是前端配代理让前端开发服务器把/api开头的请求转发给后端。这样浏览器里看到的请求都是同源的绕开跨域问题。后面部署阶段还有另一种合并部署方案我会在部署章节详细说。认证方式主流是 JWT。用户登录成功后后端生成一个带过期时间的 token前端存在 localStorage 或 Pinia/Vuex 里每次请求通过 Axios 拦截器把 token 放到 Authorization 请求头。后端写一个拦截器除了登录接口和静态资源其余接口都校验 token解析出当前用户 ID 和角色。这个机制不难但它是整个权限模型的地基建议拿到源码后第一件事就是找到这个拦截器看它放行了哪些路径、拦截了哪些路径。4. 数据库设计考勤系统的地基核心表结构和字段背后的设计逻辑4.1 网上源码常见的“一眼假”表结构很多流出来的考勤源码数据库里只有两张表一张user一张attendance。user表里塞一个role字段区分学生和教师attendance表里存学生ID、课程ID、签到时间、状态就完事了。这种表结构跑起来没问题但拿给老师看等于在脸上写着“这个系统只能做增删改查”。一个能正经答辩的考勤系统数据库至少要有这几张表-- 用户表统一登录账号 CREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role TINYINT NOT NULL COMMENT 1管理员 2教师 3学生 ); -- 学生信息表 CREATE TABLE t_student ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT, student_no VARCHAR(20) NOT NULL, name VARCHAR(50) NOT NULL, gender TINYINT, class_name VARCHAR(50), major VARCHAR(50) ); -- 教师信息表 CREATE TABLE t_teacher ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT, teacher_no VARCHAR(20) NOT NULL, name VARCHAR(50) NOT NULL, department VARCHAR(50) ); -- 课程表考勤规则所需的时间、地点参数都放在这里 CREATE TABLE t_course ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(100) NOT NULL, teacher_id BIGINT, semester VARCHAR(20), week_day TINYINT COMMENT 1-7 表示周几, start_time TIME, end_time TIME, address VARCHAR(100), latitude DECIMAL(10,6), longitude DECIMAL(10,6), sign_start_offset INT DEFAULT 15 COMMENT 课前多少分钟开放签到, late_offset INT DEFAULT 15 COMMENT 上课后多少分钟内算迟到 ); -- 学生选课关系表多对多关系 CREATE TABLE t_student_course ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT, course_id BIGINT ); -- 考勤记录表 CREATE TABLE t_attendance ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT, course_id BIGINT, att_date DATE, sign_time DATETIME, status TINYINT COMMENT 1正常 2迟到 3早退 4缺勤 5请假, sign_type TINYINT COMMENT 1定位签到 2二维码签到 3教师补签, latitude DECIMAL(10,6), longitude DECIMAL(10,6) ); -- 请假申请表 CREATE TABLE t_leave ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT, course_id BIGINT, leave_date DATE, reason VARCHAR(255), status TINYINT DEFAULT 0 COMMENT 0待审批 1通过 2驳回, create_time DATETIME, audit_time DATETIME, audit_opinion VARCHAR(255) );4.2 这些字段为什么要这样设计先看t_course表。很多人会把“上课时间”设计成两个字符串字段比如start_time 08:00也能用但没法做时间比较运算。用TIME类型LocalTime.now().isAfter(course.getStartTime())这类判断写起来非常自然。另外sign_start_offset和late_offset这两个偏移量字段是点睛之笔——不同老师对考勤宽容度不一样有的老师课前 10 分钟开放签到有的老师 15 分钟有的老师允许迟到 20 分钟。把阈值做成课程上的配置而不是写死在代码里这就是“可配置”三个字的体现答辩时能拿出来说。t_attendance表里的status字段用整数而不是字符串不是为了省存储空间而是为了统计方便。后面算考勤率的时候SUM(status 1)就能直接算正常次数一条 SQL 全搞定。如果你用字符串“正常”“迟到”那统计 SQL 就得写一堆CASE WHEN又丑又容易错。还有一个细节值得提t_attendance表里我建议冗余存一份课程名称快照或者至少存 course_id 并关联查询。考勤记录是不可变流水如果课程被管理员改名或删除历史记录里的关联会断掉。冗余存储虽然违反第三范式但在业务流水表上做适度反范式设计是实际项目里非常常见的做法也可以在论文里作为一个设计思考点写出来。t_student_course这种中间表是典型的多对多解决方案。一个学生选多门课一门课有多个学生如果没有中间表你就得在课程表里存“学生ID列表”或者在学生表里存“课程ID列表”两种都是灾难。理解了这张表也就理解了关系型数据库里最核心的建模思路。5. 核心业务逻辑考勤判定、请假审批、统计报表的实现思路5.1 考勤判定不是“打个卡”是一套时间窗口计算考勤判定的核心问题是学生点击签到的那一刻系统怎么知道他算正常、迟到还是缺勤答案是把当前时间和课程配置的时间窗口做比较。假设某节课 09:00 开始12:00 结束配置了课前 15 分钟开放签到、迟到阈值 15 分钟。那么08:45 - 08:59签到 → 正常09:00 - 09:14签到 → 迟到09:15及以后签到 → 缺勤11:50 - 12:00之间签到时还可以顺便判断早退后端伪代码大致是LocalTime now LocalTime.now(); LocalTime start course.getStartTime(); LocalTime end course.getEndTime(); if (now.isBefore(start.minusMinutes(course.getSignStartOffset()))) { return Result.error(还没到签到时间); } if (now.isBefore(start)) { status 1; // 正常 } else if (now.isBefore(start.plusMinutes(course.getLateOffset()))) { status 2; // 迟到 } else { status 4; // 缺勤 } // 早退判断如果现在处于下课之前 signStartOffset 分钟内 if (now.isAfter(end.minusMinutes(10)) now.isBefore(end)) { status 3; // 早退 }这里最容易被忽略的是“签到时间窗口关闭”的逻辑。很多简单实现只判断了“迟到”和“缺勤”的边界却没有处理“课程结束了还能不能签到”的问题。如果课程 12:00 结束学生下午 3 点还能打开页面补签那考勤就失去意义了。所以时间窗口必须做成闭合的——超出允许时间接口直接拒绝提示“签到时间已过”。另外同一门课当天不能重复签到。学生最多只能生成一条当天的考勤记录如果已经存在再请求要么直接返回已有记录要么提示“今日已签到”。这两个判断逻辑简单但特别影响系统正确性建议拿到源码后第一个验证的就是这两个点。5.2 定位签到精度不用太高但防呆必须做好定位签到的思路是前端通过浏览器navigator.geolocation获取当前经纬度传给后端后端用 Haversine 公式计算这个点和课程地点存在t_course.latitude/longitude之间的距离在设定范围内比如 200 米就允许签到。private double distance(double lat1, double lng1, double lat2, double lng2) { double radLat1 Math.toRadians(lat1); double radLat2 Math.toRadians(lat2); double a radLat1 - radLat2; double b Math.toRadians(lng1) - Math.toRadians(lng2); double s 2 * Math.asin(Math.sqrt( Math.pow(Math.sin(a / 2), 2) Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2) )); return s * 6371000; // 地球半径单位米 }实际开发时有个坑浏览器定位在http://localhost下能用但部署到服务器上如果用的是http://而没上 HTTPS浏览器会直接拒绝调用navigator.geolocation。解决办法要么给站点配 HTTPS要么用微信 JS-SDK 里的定位接口。做毕设演示时最好提前踩一下这个点别到现场才发现定位按钮点了没反应。调试阶段可以用浏览器 DevTools 里的 Sensors 面板手动模拟经纬度不用真跑到教室门口测。5.3 请假审批状态机 自动修正考勤记录请假模块的状态流转是一个典型的小型状态机。学生提交请假 → 状态为“待审批”教师审批通过 → 状态变为“通过”审批驳回 → 状态变为“驳回”。不允许从“驳回”直接跳到“通过”这种状态约束用枚举类型或带状态校验的 Service 层逻辑来实现。请假审批里最容易被忽视的是“审批通过后要修正考勤记录”。学生请假当天如果已经有缺勤记录教师通过请假申请后系统应该自动把该学生当天对应课程的考勤状态从“缺勤”修正为“请假”。这个动作可以放在审批通过的 Service 方法里统一处理。这个功能虽然代码不多但在答辩里是一个非常加分的业务闭环设计——它说明你考虑到了数据联动而不只是做一个独立的请假表单。5.4 统计报表SQL 聚合和前端图表统计报表不复杂但 SQL 写得好不好看很影响效率。按课程统计某段时间的出勤情况可以这样写SELECT course_id, DATE(att_date) AS day, COUNT(*) AS total, SUM(status 1) AS normal_count, SUM(status 2) AS late_count, SUM(status 3) AS early_count, SUM(status 4) AS absent_count, SUM(status 5) AS leave_count FROM t_attendance WHERE att_date BETWEEN #{startDate} AND #{endDate} GROUP BY course_id, DATE(att_date) ORDER BY day;MySQL 里SUM(条件)的写法可以把“等于某状态”当成 1 和 0 来累加一次查出全部状态分类不用写一堆子查询。前端拿到这个结果后用 ECharts 的折线图展示出勤率趋势、柱状图对比各课程缺勤人数就是很好的可视化页面。导出 Excel 用 EasyExcel 直接写个监听器把统计结果映射成实体列表导出即可。6. 从源码到跑通本地部署的完整链路以及最容易卡住的三类报错6.1 部署前先确认环境版本一套典型的 SpringBoot 2.x Vue 2 项目建议的本地环境是JDK 8 或 11、Maven 3.6、MySQL 5.7 或 8.0、Node 14 到 16。如果是 SpringBoot 3.x Vue 3 Element Plus 的新版组合JDK 要求 17Node 要求 16 以上。这里特别提醒一句网上很多源码是基于 SpringBoot 2.x 写的你如果非要用 JDK 17 跑大概率会遇到 javax 包名不兼容的问题SpringBoot 3 才换成 jakarta。所以拿到源码第一件事先看pom.xml里的 parent 版本再决定用哪个 JDK别一上来就java -jar。6.2 完整部署操作记录以最常见的 SpringBoot 2.x Vue 2 项目为例完整跑通一套源码的流程是创建数据库。用 Navicat 或命令行新建一个库比如attendance_db然后把源码里提供的attendance_db.sql导入。很多源码的 SQL 脚本里没带CREATE DATABASE语句需要你手动建库后再导入。改后端配置。打开application.yml把数据源的 URL、用户名、密码改成你自己的。MySQL 8 一定要在 URL 后面带上serverTimezoneAsia/Shanghai否则 JDBC 驱动会报时区错误。spring: datasource: url: jdbc:mysql://localhost:3306/attendance_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver启动后端。在项目根目录执行mvn spring-boot:run或者先mvn clean package -DskipTests打包成 jar再java -jar target/xxx.jar启动。看到 “Started Application in xx seconds” 就说明后端起来了。启动前端。进入frontend目录先npm install。如果遇到 peer 依赖冲突用npm install --legacy-peer-deps基本都能解决这是 Node 高版本和旧项目依赖冲突最常见的解法。配置前端代理。查看vue.config.js或vite.config.js确认/api代理指向后端地址// vue.config.js 示例 module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } };执行npm run dev浏览器打开http://localhost:8080用源码里提供的初始账号登录能进去、能打卡部署就成功了。6.3 最常见的三类报错对应解法第一类是数据库连不上。报Access denied for user基本就是密码错了报Unknown database就是库没建或库名不对报The server time zone value就是serverTimezone没配上。按上面三步逐一排查五分钟内能定位。第二类是端口被占用。后端默认 8080前端也默认 8080两个同时启动必然冲突。解决方法是把后端端口改成 8081或任意没被占用的端口然后前端代理指向对应端口。还有一种情况是 8080 被其他程序占了Windows 下用netstat -ano | findstr 8080找到占用进程 PID任务管理器里结束掉即可。第三类是 npm install 装到一半报错。最常见原因是 Node 版本和项目依赖不兼容。老的 Vue 2 项目在 Node 18 上经常报opensslErrorStack之类的 OpenSSL 错误要么用 nvm 切换到 Node 14 或 16要么在package.json的 scripts 里加NODE_OPTIONS--openssl-legacy-provider再重新跑。从经验上说用 nvm 管理 Node 版本是最干净的方案。7. 部署完别急着交定位签到失效、跨域拦截、路由刷新404三个坑的完整排查链路7.1 跨域拦截为什么后端配置了 CORS 还是报错开发环境下最常见的报错是 “Access to XMLHttpRequest has been blocked by CORS policy”。网上一般的说法是“在后端加一个 CORS 配置类”但真正麻烦的是这个配置到底有没有生效。完整的排查链路是这样的打开浏览器 F12 的 Network 面板看被拦截的请求。如果是跨域请求浏览器会先发一个 OPTIONS 预检请求。如果后端配置的 CORS 没有正确放行这个预检就会出现拦截报错。此时先看后端有没有类似这样的配置Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true); } }如果加了配置还是报错重点检查两件事一是项目里有没有 Spring Security因为 Security 的过滤链优先级高于 MVC 的 CORS 配置要在 Security 配置类里把 CORS 也放行二是allowedOriginPatterns和allowCredentials(true)的组合是否正确允许凭证时不能简单用allowedOrigins(*)。不过说实话开发环境我不建议依赖后端 CORS 配置来解决问题。直接在前端 devServer 配代理前端请求全走同源从根上绕过跨域简单可靠还不用动后端代码。后端 CORS 配置留到生产环境或前后端完全分离时再用。7.2 路由刷新 404尖括号里的历史模式惹的祸后端和前端都跑通后很多人会把前端用npm run build打包然后把dist文件夹的内容复制到后端src/main/resources/static目录合并成一个 jar 部署。这样做的好处是只用一台服务器、一个端口就能跑完整套系统答辩演示特别方便。但坑马上就来了点击页面内部跳转没问题一旦用户在地址栏输入子路径后按回车或者按 F5 刷新一个非首页路径页面就变 404 了。原因是 Vue Router 用了history模式路由路径是前端管理的后端静态服务器找不到对应路径的资源自然回 404。最简单的解决方法是后端加一个转发规则把所有非/api开头的请求都转发到index.htmlController public class PageForwardController { RequestMapping(value /{path:[^\\.]*}) public String forward() { return forward:/index.html; } }或者更省事前端路由改成hash模式URL 长这样http://localhost:8080/#/attendance所有路径都在 hash 里不经过后端天然不会 404。但 hash 模式 URL 不好看如果是正式部署推荐用 history 模式加后端转发做毕设演示的话两者都可以接受。7.3 考勤时间判断异常少了 8 小时和少了 15 分钟的两种尴尬有次我帮人看一个考勤系统学生明明早上 8 点 55 分签的到系统判定却是迟到。排查了大半天最后发现问题是数据库连接串里的时区没配好。MySQL 服务器默认时区是东八区但 JDBC 连接时如果没指定serverTimezone驱动会用 JVM 默认时区去解释时间两端时区不一致读出来就错乱。这里的现象和解决方法在部署章节已经说过MySQL 8 必须带serverTimezoneAsia/Shanghai。还有一个很容易被忽略的边界情况考勤接口里如果直接拿系统时间LocalTime.now()去和课程时间比较而课程时间是数据库里读出来的TIME类型两者理论上都是服务器时间没问题。但有些源码为了“图省事”把课程开始时间写成了一个全局配置比如“允许签到时间 08:45”而不是从课程表里读。一旦课程时间调整这个写死的配置就成坑了。所以拿到源码后重点检查考勤判定逻辑到底是从课程表读参数还是用了写死的值这也是你真正读懂这套系统的关键一步。8. 从能跑到能讲对源码做个性化改造让答辩不再心虚8.1 为什么一定要改造网上同质化太严重了。同一个项目源码可能班里有七八个同学都用它在做毕设导师眼睛雪亮一眼就能认出来。单纯的“跑通”只能保证不挂但不能保证拿高分。真正的加分项是你对源码做了一两个有业务意义的改造并且能清楚地讲出“为什么这么做”。这个改造不需要多宏大的规模但要有完整的故事线解决了什么问题、方案是什么、效果怎么样。8.2 低成本高回报的四个扩展方向扩展一动态二维码签到。普通二维码签到用一张静态二维码学生扫一扫就完成签到截图流传后很容易代签。改造方案是把二维码内容设计成“课程ID 当前时间戳 随机数”30 秒或 1 分钟刷新一次过期作废。学生每次打开签到时候前端向后端请求一个新的二维码内容。这个改造代码量不大但能完整讲出一个“防止代签”的业务故事。扩展二人脸识别签到。调用现成的云平台人脸识别 API百度的 AI 开放平台、腾讯云的人脸识别学生在签到时拍照上传后端做人脸比对确认是本人后才允许签到。这个方向适合想往 AI 方向靠一点的选题而且云平台 API 一般都有免费额度毕设量级完全够用。写论文时可以把“传统定位签到 人脸验证”做成双层校验作为系统的差异化亮点。扩展三数据可视化大屏。用 ECharts 做一块“考勤总览”大屏页面展示今日到课率、各学院缺勤排行、近 30 天考勤趋势。这算锦上添花的功能但视觉效果非常加分。很多同学不会做你做了就比别人多一个可以演示的页面。扩展四消息通知。请假审批通过或驳回后系统给对应学生发送站内信或邮件通知。用 Spring 自带的ApplicationEventPublisher做事件解耦即可顺便还能写一个异步处理的小案例。这个功能涉及事件驱动和异步工作量不大但论文里可以讲的东西很多。8.3 改造怎么落地怎么讲选定扩展点之后不要直接闷头写代码。先把改造涉及的数据表变更、接口设计、流程变化写成一个简短的设计说明再动手实现。答辩时不要只说“我加了人脸识别”要给一个完整的叙事考勤场景里存在代签问题 → 单纯的定位签到无法防止代签 → 我引入人脸识别 定位双重校验 → 实现效果和准确率如何。这个“问题 - 方案 - 验证”的叙事结构比堆砌一万字的功能介绍都管用。我自己带过不少用这类系统做毕设的同学最大的体会是你对项目的理解深度直接决定答辩时的自信程度。哪怕是网上拿到的源码只要你能把考勤判定那几行逻辑亲手改一遍、把数据库表关系自己画出来、把报表 SQL 读明白这个项目就已经变成你自己的了。希望这篇拆解能帮你少走一点弯路把一套平平无奇的考勤系统做成一份拿得出手的作品。
返回列表