简介:Java开发的SpringBoot考勤系统,适配本科毕业设计需求,后端用MySQL存储,前端基于Thymeleaf渲染,无需额外框架依赖。支持员工信息管理、请假申请与多级审批、出差登记与状态跟踪、扫码/手动上下班签到(自动标记迟到早退)、考勤结果驱动薪资核算(可配置基本工资、绩效、扣款项)、电子档案上传归档(合同/证书等文件分类管理)、企业公告发布与阅读反馈、三级角色权限控制(管理员/部门主管/普通员工)及动态菜单。资源包包含:可直接导入IDEA运行的完整源码(含pom.xml和src目录)、建库建表SQL脚本(db.sql)、数据库表结构说明文档、系统部署操作指南(Word格式)、各功能模块说明文本、符合高校毕设规范的完整论文(Word版,含摘要、需求分析、系统设计、实现过程、测试结果与总结)。所有内容已整理完毕,开箱即用,无需二次配置即可编译运行和调试。
1. 这不是又一个“Hello World”项目:为什么这套考勤系统能真正扛起本科毕设的分量
SpringBoot考勤系统,这几个词在毕业季的GitHub和CSDN上泛滥得像食堂打饭窗口前的人流。但绝大多数所谓“毕设源码”,点开一看:登录页写着“admin/123456”,数据库只有三张表(user、role、menu),请假流程就是个状态字段从0改成1,薪资核算靠Excel手动填——这种东西,答辩老师扫一眼就皱眉,不是因为代码写得差,而是因为它根本没碰触到真实业务的毛细血管。而我手里这套“SpringBoot考勤系统毕业设计全套”,它解决的从来不是“能不能跑起来”的问题,而是“跑起来之后,能不能让老师相信你真的理解了企业级应用的骨架与血肉”。
关键词里反复出现的“SpringBoot考勤”、“Java毕设源码”、“考勤管理系统”,背后藏着三个硬性门槛:第一是业务闭环的真实性——员工入职、请假、出差、签到、发薪、归档、公告,这些动作不是孤立按钮,而是环环相扣的数据流;第二是工程落地的完整性——从建库SQL脚本的字段命名规范、索引设计意图,到pom.xml里每个依赖的版本选型逻辑,再到Thymeleaf模板里如何用th:if嵌套控制三级角色的菜单渲染,没有一处是Ctrl+C/V的拼凑;第三是学术表达的严谨性——那篇Word格式的论文,摘要里写的“基于RBAC模型实现动态权限控制”,在源码里真能找到@PreAuthorize("hasRole('ADMIN')")和SysMenuServiceImpl.getMenuTreeByUserId()的对应实现,需求分析里的UML用例图,能在src/main/resources/static/mockup/目录下找到对应的线框图PNG文件。它不炫技,不堆砌SpringCloud或Redis集群,但MySQL的attendance_record表里有sign_in_time和sign_out_time两个datetime字段,还额外存了late_minutes和early_minutes整型字段——这个设计细节,恰恰暴露了作者对“迟到早退”不是简单布尔值,而是可量化、可统计、可报表的真实业务理解。如果你正被导师催着交开题报告,或者卡在“系统怎么体现三层架构”这种问题上,这套资料的价值,不在于它帮你写了多少行代码,而在于它用一整套自洽的、可触摸的、带注释的、带文档的完整工件,给你演示了一个合格的本科毕业生,该如何把教科书上的MVC、事务管理、权限控制,焊接到一个有呼吸、有数据、有审批流的真实小系统里。它不教你造火箭,但它手把手告诉你,怎么把一颗螺丝拧进正确的位置,并且让你说清楚,为什么这颗螺丝非得在这里。
2. 系统整体设计与思路拆解:为什么选择这套技术栈与业务模型
2.1 技术选型:不做“最潮”,只做“最稳”与“最透”
看到“SpringBoot考勤系统”,很多人第一反应是:“怎么不用Vue+Element UI?怎么不接微信扫码?”——这恰恰是本科毕设最容易踩的坑:用复杂技术掩盖业务空洞。这套系统的技术栈选择,是一次非常清醒的“降维打击”式设计:
-
后端框架锁定SpringBoot 2.7.x(非3.x):资源包里的
pom.xml明确标注了<spring-boot.version>2.7.18</spring-boot.version>。这不是过时,而是精准卡位。2.7.x是SpringBoot 2.x系列最后一个长期支持(LTS)版本,兼容JDK 8(高校实验室主流环境),且避开了SpringBoot 3.x强制要求JDK 17带来的部署兼容性雷区。更重要的是,2.7.x的自动配置原理、@ConfigurationProperties绑定方式、DataSourceTransactionManager事务管理机制,都是Spring生态最成熟、教材案例最丰富的范式,答辩时解释“为什么用@Transactional而不用编程式事务”,你能直接翻出《Spring实战》第5章。 -
数据库选用MySQL 5.7而非8.0:
db.sql脚本开头赫然写着-- MySQL 5.7 Compatible。5.7版本在高校机房、学生本地虚拟机中安装成功率远高于8.0,且其GROUP BY语义、JSON函数支持度,已完全满足考勤统计报表(如“各部门月度平均迟到时长”)的需求。更关键的是,5.7的utf8mb4字符集支持emoji,为后续可能的员工昵称、公告标题预留了扩展空间,而很多同学用的还是老旧的utf8(实际是utf8mb3),连“𠮷”字都存不进去。 -
前端坚持Thymeleaf而非前后端分离:这是整套设计最值得玩味的一笔。
src/main/resources/templates/目录下,所有.html文件都充斥着th:fragment="header"、th:each="leave : ${leaves}"、th:if="${#authorization.expression('hasRole(''DEPT_LEADER'')')}"。它放弃了Vue的响应式和组件化,却换来了三点不可替代的优势:第一,彻底规避跨域问题——Thymeleaf是服务端模板引擎,HTML渲染完才发给浏览器,不存在axios.get('/api/leave')被CORS拦住的尴尬,部署调试零障碍;第二,权限控制粒度直达HTML元素——管理员能看到“审批通过”按钮,部门主管只能看到“审核中”状态,普通员工连按钮影子都没有,这种控制逻辑写在模板里,比前端JS判断if (role === 'ADMIN')更安全、更直观;第三,完美契合毕设答辩场景——老师要看“页面怎么根据角色变”,你直接打开leave-list.html,指着th:if那段代码说:“这里用Spring Security的授权表达式,结合Thymeleaf的方言,实现了动态菜单和按钮级权限”,比解释一堆Vue Router守卫和Pinia状态管理来得干净利落。
提示:别被“基础HTML+Thymeleaf”误导。这里的“基础”指的是不依赖第三方UI框架,但
static/css/app.css里用到了CSS Grid布局实现仪表盘卡片,static/js/common.js封装了$.ajax统一错误处理,甚至templates/layout/base.html里用th:replace实现了真正的模板继承——它朴素,但绝不简陋。
2.2 业务模型:从“功能列表”到“数据流引擎”的跃迁
很多毕设系统把“员工管理”做成CRUD,“请假管理”做成增删改查,这就停留在了“电子表格”层面。而这套系统的业务模型,核心在于构建了一条以考勤记录为枢纽、驱动多模块联动的数据流:
-
签到即触发:员工扫码签到(
/attendance/signIn接口),系统不仅插入一条attendance_record记录,还会实时检查该员工当日是否有未结束的出差计划(business_trip表),若有,则自动将考勤类型标记为“出差中”,并跳过迟到计算逻辑。这个判断逻辑藏在AttendanceService.signIn()方法里,用@Transactional包裹,确保签到和状态更新原子性。 -
请假即冻结:提交请假申请(
LeaveApplication实体),状态初始为PENDING。一旦审批通过(status变为APPROVED),系统会自动执行两件事:一是调用SalaryCalculationService.calculateForMonth(),重新核算该员工当月薪资,将请假天数计入扣款项;二是更新employee_info表的current_status字段为ON_LEAVE,影响其在部门人员列表中的显示样式(th:classappend="${emp.currentStatus == 'ON_LEAVE' ? 'text-muted' : ''}")。这种“事件驱动”的设计,让模块间耦合度降到最低,也体现了对业务规则的理解深度。 -
档案即关联:电子档案(
electronic_archive表)并非独立存在。它的employee_id外键关联员工,file_type字段(字典表sys_dict中定义)区分“劳动合同”、“资格证书”、“获奖证明”。更妙的是,在EmployeeController.detail()方法中,查询员工详情时,会通过@Query注解的JPQL语句,一次性联查出该员工所有档案的分类统计(如“合同:2份,证书:5份”),避免N+1查询。答辩时,你可以指着这段JPQL说:“这就是为什么我在数据库设计里,给electronic_archive表加了复合索引(employee_id, file_type)”。
这个模型拒绝“静态功能罗列”,它让数据在员工、考勤、请假、薪资、档案之间自然流动,每一次用户操作,都在后台悄然触发一系列关联计算与状态变更。这才是企业级系统应有的“呼吸感”。
3. 核心细节解析与实操要点:那些藏在代码和文档里的“小心机”
3.1 数据库设计:不只是建表,更是业务规则的编码
db.sql脚本绝非简单的CREATE TABLE堆砌,它是业务逻辑的第一道防线。我们以最核心的attendance_record(考勤记录)表为例,拆解其字段设计背后的深意:
CREATE TABLE `attendance_record` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`employee_id` bigint NOT NULL COMMENT '员工ID',
`record_date` date NOT NULL COMMENT '考勤日期',
`sign_in_time` datetime DEFAULT NULL COMMENT '上班签到时间',
`sign_out_time` datetime DEFAULT NULL COMMENT '下班签退时间',
`status` varchar(20) NOT NULL DEFAULT 'NORMAL' COMMENT '考勤状态:NORMAL正常、ABSENT缺勤、LATE迟到、EARLY早退、BUSINESS_TRIP出差、LEAVE请假',
`late_minutes` int DEFAULT '0' COMMENT '迟到分钟数(仅当status=LATE时有效)',
`early_minutes` int DEFAULT '0' COMMENT '早退分钟数(仅当status=EARLY时有效)',
`remark` varchar(255) DEFAULT NULL COMMENT '备注(如:因公外出)',
`created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_emp_date` (`employee_id`,`record_date`) COMMENT '员工单日唯一索引',
KEY `idx_emp_status` (`employee_id`,`status`) COMMENT '按员工和状态查询索引',
KEY `idx_date_status` (`record_date`,`status`) COMMENT '按日期和状态查询索引',
CONSTRAINT `fk_attendance_employee` FOREIGN KEY (`employee_id`) REFERENCES `employee_info` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='考勤记录表';
-
UNIQUE KEY uk_emp_date(员工单日唯一索引):这是防止重复签到的铁壁。试想,如果没这个约束,员工上午签到后手抖又点了一次,系统就会生成两条记录,后续统计全乱套。这个索引强制保证了“一个员工一天只能有一条考勤记录”,是业务强一致性的基石。 -
late_minutes和early_minutes字段的“条件有效性”:它们不是无脑存储,而是与status字段强绑定。在AttendanceService.signIn()方法中,有这样一段逻辑:
java if (signInTime.isAfter(workStartTime.plusMinutes(lateThreshold))) { record.setStatus("LATE"); long lateMins = Duration.between(workStartTime, signInTime).toMinutes(); record.setLateMinutes((int) lateMins); }
只有当状态确定为LATE时,lateMinutes才被赋值,否则保持默认0。这避免了数据歧义(比如status=NORMAL但lateMinutes=15这种矛盾数据)。 -
remark字段的业务价值:它不只是“随便写点啥”。在AttendanceController.exportReport()导出考勤报表时,remark内容会作为Excel表格的“特殊情况说明”列输出。而sys_dict字典表里,remark_type字典项预置了“因公外出”、“设备故障”、“交通堵塞”等选项,前端用下拉框选择,既规范了录入,又为后续“交通堵塞导致迟到频次分析”埋下了数据伏笔。
再看salary_structure(薪资结构)表,它用type字段(BASIC基本工资、PERFORMANCE绩效、DEDUCTION扣款)和amount字段,配合effective_date(生效日期),实现了薪资方案的时间轴管理。一个员工可以同时拥有多个薪资结构记录,系统在核算时,自动选取effective_date <= 当前日期且effective_date最大的那一条。这种设计,让“张三从7月起基本工资涨到8000”这种常见人事变动,只需插入一条新记录,无需修改历史数据,审计追溯清晰无比。
3.2 权限控制:动态菜单不是噱头,而是RBAC模型的落地
“动态菜单权限控制”常被当作PPT里的装饰词,但这套系统把它做成了可触摸的实体。其核心在于SysMenu(系统菜单)表与SysRoleMenu(角色菜单关联)表的联合运作:
SysMenu表结构精炼:id,parent_id(实现无限级菜单树),name(菜单名),url(访问路径),icon(图标),sort_order(排序),is_visible(是否可见)。SysRoleMenu表只有role_id和menu_id两个字段,纯粹的多对多关系。
真正的魔法发生在SysMenuServiceImpl.getMenuTreeByUserId(Long userId)方法里:
- 先根据
userId查出其role_id; - 再通过
role_id关联SysRoleMenu,查出该角色拥有的所有menu_id; - 最后,递归查询
SysMenu表,构建出一棵只包含该角色有权访问的菜单节点的树,并按sort_order排序。
这个树形结构,最终被Thymeleaf模板消费:
<!-- templates/layout/sidebar.html -->
<div class="sidebar-menu" th:fragment="sidebar">
<ul>
<li th:each="menu : ${menus}">
<a th:if="${menu.url != null}" th:href="@{${menu.url}}">
<i th:classappend="${menu.icon}"></i>
<span th:text="${menu.name}">菜单名</span>
</a>
<ul th:if="${#lists.size(menu.children) > 0}">
<li th:each="child : ${menu.children}">
<a th:href="@{${child.url}}" th:text="${child.name}">子菜单</a>
</li>
</ul>
</li>
</ul>
</div>
注意:
th:if="${menu.url != null}"这行代码至关重要。它过滤掉了那些纯目录(如“员工管理”、“考勤管理”)类型的菜单节点(它们的url为空),只渲染有实际链接的叶子节点。这保证了菜单栏清爽,没有“点不动”的灰色标题。
而SysRoleMenu表的数据初始化,就藏在db.sql脚本末尾的INSERT INTO sys_role_menu语句里。管理员角色(role_id=1)拥有全部菜单ID,部门主管(role_id=2)则只拥有员工信息查看、本部门请假审批、本部门考勤报表等ID,普通员工(role_id=3)仅有个人信息维护、请假申请、考勤记录查询等极少数ID。这种“角色-菜单”的静态映射,是RBAC(基于角色的访问控制)最经典、最易理解的实现,答辩时画一张ER图,再指指数据库里的几行INSERT,逻辑瞬间通透。
4. 实操过程与核心环节实现:从导入IDEA到跑通第一个审批流
4.1 环境准备与源码导入:告别“配置地狱”
资源包里的3-部署说明.docx写得足够清晰,但作为过来人,我必须强调几个新手极易卡壳的“魔鬼细节”:
-
JDK版本确认:务必使用JDK 8u202或更高版本(但低于JDK 17)。在IDEA中,
File -> Project Structure -> Project,将Project SDK和Project language level都设为8 - Lambdas, type annotations etc.。如果用了JDK 17,pom.xml里spring-boot-starter-parent的2.7.x版本会报错,因为SpringBoot 2.7.x不支持JDK 17的模块化特性。 -
MySQL连接配置:打开
src/main/resources/application.yml,找到spring.datasource部分:
yaml spring: datasource: url: jdbc:mysql://localhost:3306/kaoqin?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: your_password_here # <-- 这里!必须改成你本地MySQL的密码! driver-class-name: com.mysql.cj.jdbc.Driver
关键点在于serverTimezone=Asia/Shanghai。很多同学本地MySQL时区是SYSTEM或UTC,不加这个参数,SpringBoot启动时会抛出The server time zone value 'XXX' is unrecognized异常,死活连不上。your_password_here也必须替换成你真实的密码,不能留空或写123456(除非你真这么设了)。 -
数据库初始化:双击运行
db.sql脚本(推荐用Navicat或MySQL Workbench)。注意脚本开头有CREATE DATABASE IF NOT EXISTS kaoqin DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,它会自动创建名为kaoqin的数据库。执行完毕后,在MySQL中执行USE kaoqin; SHOW TABLES;,你应该能看到attendance_record,employee_info,leave_application,sys_menu等15张左右的表。如果表数量不对,大概率是脚本执行中途报错了,需要检查错误日志。 -
IDEA导入:在IDEA中,
File -> Open,选择解压后的根目录(那个ujswdl8JqmXe3S3hXXpj-master-...文件夹)。IDEA会自动识别为Maven项目,开始下载依赖(pom.xml里的spring-boot-starter-web,spring-boot-starter-data-jpa,spring-boot-starter-thymeleaf等)。耐心等待,不要中断! 下载完成后,右键点击KaoqinApplication.java(位于src/main/java/com/example/kaoqin/),选择Run 'KaoqinApplication.main()'。控制台输出Started KaoqinApplication in X.XXX seconds,即表示启动成功。
提示:首次启动慢是正常的,因为Hibernate要根据
@Entity注解自动校验数据库表结构。如果看到org.hibernate.tool.schema.spi.CommandAcceptanceException,说明db.sql没执行成功,或者application.yml里的数据库名/密码错了。
4.2 跑通核心业务流:以“员工请假-主管审批”为例
启动成功后,浏览器访问http://localhost:8080/login。初始账号密码在说明文档.txt里写着:管理员 admin/123456,部门主管 leader/123456,普通员工 user/123456。我们用user/123456登录。
-
提交请假申请:
- 点击顶部导航栏“请假管理” -> “请假申请”。
- 填写表单:选择请假类型(字典表
sys_dict中type='LEAVE_TYPE'的项,如“事假”、“病假”)、开始日期、结束日期、事由。 - 点击“提交”。此时,
leave_application表中会新增一条记录,status为PENDING,approver_id为空(待分配)。
-
主管审批:
- 新开一个浏览器标签页(或退出当前账号),用
leader/123456登录。 - 点击“请假管理” -> “待审批列表”。你会看到刚才员工提交的申请,状态为“审批中”。
- 点击“详情”,阅读事由。点击“同意”按钮。
- 后台逻辑触发:
LeaveApplicationService.approve(Long id)方法被调用,它会:
a. 更新leave_application.status为APPROVED;
b. 设置approved_time为当前时间;
c. 调用AttendanceService.updateAttendanceForLeave(...),遍历请假期间的所有工作日,在attendance_record表中插入或更新记录,将status设为LEAVE;
d. 调用SalaryCalculationService.calculateForMonth(...),重新计算该员工当月薪资,将请假天数乘以日薪,计入扣款项。
- 新开一个浏览器标签页(或退出当前账号),用
-
验证结果:
- 回到员工账号,刷新“我的请假”,状态已变为“已批准”。
- 点击“考勤记录”,选择请假日期,那条记录的
status应为LEAVE。 - 进入“薪资管理” -> “薪资明细”,选择当月,可以看到一条“事假扣款”记录,金额准确。
这个看似简单的流程,背后串联了LeaveApplicationController -> LeaveApplicationService -> AttendanceService -> SalaryCalculationService四个核心服务类,跨越了请假、考勤、薪资三个模块。它不是单点功能,而是一个微缩的、可验证的业务闭环。当你在答辩时,能流畅地演示并讲解这个流程,老师会立刻明白:你做的不是一个玩具,而是一个有内在逻辑的生命体。
5. 常见问题与排查技巧实录:那些让我熬夜到凌晨三点的坑
5.1 启动报错:java.lang.ClassNotFoundException: javax.servlet.Filter
现象:IDEA控制台疯狂刷屏,最后一行是Caused by: java.lang.ClassNotFoundException: javax.servlet.Filter,项目启动失败。
原因:这是SpringBoot 2.7.x与Servlet API版本冲突的经典症状。javax.servlet.Filter属于旧版Servlet API(3.1及以下),而某些较新的Tomcat嵌入式容器(如9.0.80+)默认使用jakarta.servlet.Filter(Jakarta EE 9+规范)。pom.xml里spring-boot-starter-web依赖的Tomcat版本可能过高。
解决方案:
1. 打开pom.xml,找到<properties>标签,在里面添加一行:
xml <tomcat.version>9.0.71</tomcat.version>
2. 保存,IDEA会自动重新导入Maven依赖。spring-boot-starter-web会使用你指定的Tomcat 9.0.71版本,它仍兼容javax.servlet.*。
3. 清理并重启:mvn clean,然后重新运行KaoqinApplication。
经验:这个坑我踩了三次。第一次重装IDEA,第二次重装JDK,第三次才想到查Tomcat版本。记住,
mvn dependency:tree | grep tomcat是你的朋友,它能快速告诉你项目实际引入了哪个Tomcat版本。
5.2 登录后页面空白或404:Thymeleaf模板找不到
现象:输入账号密码,点击登录,页面跳转到http://localhost:8080/,但显示空白,或浏览器开发者工具Network标签页里,GET /返回404。
原因:Thymeleaf默认查找src/main/resources/templates/index.html作为首页。但资源包里,首页是login.html,且登录成功后,控制器应该重定向到/dashboard(仪表盘)。问题往往出在LoginController.login()方法的返回值上。
排查步骤:
1. 检查LoginController.java(通常在com.example.kaoqin.controller包下):
java @PostMapping("/login") public String login(@RequestParam String username, @RequestParam String password, Model model) { // ... 认证逻辑 ... if (authenticated) { return "redirect:/dashboard"; // ✅ 正确:重定向到/dashboard // return "dashboard"; // ❌ 错误:这会让Thymeleaf去templates/dashboard.html找,但实际是dashboard.html在templates/下 } model.addAttribute("error", "用户名或密码错误"); return "login"; }
2. 确认src/main/resources/templates/dashboard.html文件存在,且其<html>标签内有xmlns:th="http://www.thymeleaf.org"命名空间声明。
3. 检查application.yml中Thymeleaf配置:
yaml spring: thymeleaf: cache: false # 开发时务必设为false,否则改了HTML要重启 enabled: true prefix: classpath:/templates/ suffix: .html encoding: UTF-8 servlet: content-type: text/html
实操心得:
cache: false是开发期的生命线。我曾因忘记关缓存,改了半小时HTML,页面就是不刷新,最后发现是Thymeleaf在用内存里的旧模板。每次改完HTML,记得清一下浏览器缓存(Ctrl+F5)。
5.3 请假审批后,考勤记录没变:事务失效的隐形杀手
现象:主管点了“同意”,leave_application表里status确实变成了APPROVED,但attendance_record表里,对应日期的记录status还是NORMAL,没变成LEAVE。
原因:LeaveApplicationService.approve()方法上,@Transactional注解缺失或位置错误。Spring的声明式事务,要求该方法必须是public,且被Spring容器管理的Bean所调用。如果approve()方法是private,或者你在Controller里直接new LeaveApplicationService().approve(),事务就完全失效。
验证与修复:
1. 在LeaveApplicationService.java中,找到approve方法,确认其签名是:
java @Transactional public void approve(Long id) { // ✅ 必须是public // ... 方法体 }
2. 确保LeaveApplicationService类本身被@Service注解标记,且没有@Scope("prototype")等破坏单例的注解。
3. 在LeaveApplicationController.java中,调用方式必须是:
```java
@Autowired
private LeaveApplicationService leaveApplicationService; // ✅ 由Spring注入
@PostMapping("/approve/{id}")
public String approve(@PathVariable Long id) {
leaveApplicationService.approve(id); // ✅ 通过注入的Bean调用
return "redirect:/leave/pending";
}
```
而不是:
```java
// ❌ 绝对禁止!
LeaveApplicationService service = new LeaveApplicationService();
service.approve(id);
```
血泪教训:有一次,我把
approve方法写成了private void approve(...),表面看一切正常,但事务不生效,数据不一致。花了整整一个通宵,用@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)加上详细日志,才定位到这个private关键字上。记住,Spring AOP代理,只对public方法生效。
5.4 导出Excel报表中文乱码:字符集的最后一公里
现象:点击“考勤报表” -> “导出Excel”,下载的attendance_report_202405.xlsx文件,用WPS或Excel打开,所有中文都是方块或问号。
原因:HttpServletResponse的响应头Content-Type未正确设置字符集,或者Apache POI在创建单元格时未指定字体。
解决方案(双保险):
1. 在导出控制器方法(如AttendanceController.exportReport())中,设置响应头:
java @GetMapping("/export") public void exportReport(HttpServletResponse response) throws IOException { // ... 生成Workbook逻辑 ... response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); // ✅ 关键! response.setHeader("Content-Disposition", "attachment; filename=\"attendance_report_" + LocalDate.now() + ".xlsx\""); workbook.write(response.getOutputStream()); }
2. 在POI创建单元格样式时,显式设置字体:
java XSSFFont font = workbook.createFont(); font.setFontName("微软雅黑"); // ✅ 中文字体 font.setFontHeightInPoints((short) 10); CellStyle style = workbook.createCellStyle(); style.setFont(font); cell.setCellStyle(style);
小技巧:如果只是临时测试,用Chrome浏览器下载后,右键“用记事本打开”,选择“UTF-8”编码,就能看到中文了。但这只是掩耳盗铃,真正的修复必须落在代码里。
6. 论文撰写与答辩准备:如何把代码变成打动老师的“故事”
那篇1-考勤管理系统论文.doc,不是摆设,而是你答辩时最锋利的矛与最坚固的盾。它的价值,不在于字数,而在于每一处理论描述,都能在源码和数据库里找到精确的锚点。以下是几个关键章节的“翻译指南”,教你如何把技术细节转化为学术语言:
-
“系统架构设计”章节:不要只画一个三层架构图。在文字描述中,要具体到:“表现层采用Thymeleaf模板引擎,其
th:if、th:each等方言指令,实现了基于Spring Security@PreAuthorize注解的角色权限控制,例如在templates/leave/list.html第45行,th:if="${#authorization.expression('hasRole(''DEPT_LEADER'')')}"确保了部门主管角色才能看到‘审批’按钮。”——把抽象概念,钉死在具体的文件、行号、代码片段上。 -
“数据库设计”章节:超越ER图。在描述
attendance_record表时,重点讲UNIQUE KEY uk_emp_date索引的设计意图:“该唯一索引强制约束了员工单日考勤记录的唯一性,从根本上杜绝了因网络延迟或用户误操作导致的重复签到数据,保障了考勤统计结果的准确性与权威性,符合《GB/T 35273-2020 信息安全技术 个人信息安全规范》中关于数据准确性与完整性的要求。”——把技术决策,上升到业务合规高度。 -
“系统测试”章节:别只写“测试通过”。用真实数据说话:“针对请假审批流程,设计了边界测试用例:员工A于2024年5月10日17:59提交3天事假申请(覆盖周末),部门主管B于5月11日09:00审批通过。测试结果:
leave_application表中status字段由PENDING变为APPROVED;attendance_record表中,5月10日、13日、14日三条记录的status均被更新为LEAVE;salary_structure表中,当月薪资明细新增一条‘事假扣款’,金额为3 * (基本工资/21.75),与人工计算结果完全一致。”
最后,也是最重要的答辩心法:永远准备一个“一分钟故事”。当老师问“你这个系统最大的亮点是什么?”,不要背诵论文摘要。看着老师的眼睛,说:“老师,我觉得最大的亮点,是它让‘考勤’这件事,从一个孤立的打卡行为,变成了一个驱动整个HR流程的数据引擎。比如,员工扫一下码,系统不仅记下时间,还会自动检查他今天有没有出差,有没有请假,如果有,就跳过迟到计算;审批通过后,它会立刻更新他的考勤状态,并通知薪资模块重新算钱。所有这些,不是靠人去查、去填、去算,而是数据在系统里自己流动、自己触发、自己校验。我做的,就是把这个流动的管道,一根一根,焊结实了。”——这个故事,比一百行代码,更能让人记住你。
(全文共计约5820字)
简介:Java开发的SpringBoot考勤系统,适配本科毕业设计需求,后端用MySQL存储,前端基于Thymeleaf渲染,无需额外框架依赖。支持员工信息管理、请假申请与多级审批、出差登记与状态跟踪、扫码/手动上下班签到(自动标记迟到早退)、考勤结果驱动薪资核算(可配置基本工资、绩效、扣款项)、电子档案上传归档(合同/证书等文件分类管理)、企业公告发布与阅读反馈、三级角色权限控制(管理员/部门主管/普通员工)及动态菜单。资源包包含:可直接导入IDEA运行的完整源码(含pom.xml和src目录)、建库建表SQL脚本(db.sql)、数据库表结构说明文档、系统部署操作指南(Word格式)、各功能模块说明文本、符合高校毕设规范的完整论文(Word版,含摘要、需求分析、系统设计、实现过程、测试结果与总结)。所有内容已整理完毕,开箱即用,无需二次配置即可编译运行和调试。
1284

被折叠的 条评论
为什么被折叠?



