
简介这套基于SSMSpringSpringMVCMyBatis、MySQL与JSP技术栈的公司员工考勤管理系统是面向毕业设计或课程设计的完整可运行源码包适合计算机相关专业学生学习和二次开发。系统按一般员工、部门经理、系统管理员三类角色划分权限覆盖个人资料管理、上班时间公告、请假管理、出差管理、差费报销、日常出勤与考勤管理等核心业务并延伸出系统用户管理、部门管理、员工管理以及员工统计、请假统计等功能模块。登录模块完成角色认证请假及出差设置子模块支持管理员统一管理出勤子模块则对经理与员工进行整体考勤归集。压缩包为zip格式约29.96MB内含可运行源码、SQL数据库脚本与配套文档便于导入部署、理解表结构关系并快速启动项目。目前已有1847人学习下载尤其适合正在筹备SSM课题、需要参考完整考勤业务闭环与权限设计方案的读者。1. 一套 SSM 员工考勤系统源码该怎么判断它值不值得跑在资源检索平台上以“公司员工考勤管理系统”为关键词翻出来的可运行源码包最常见的就是标题里这套组合SSMSpringSpringMVCMyBatis、MySQL、JSP附带 SQL 文件和文档。这类项目长期有热度因为很多人要的不是演示 Demo而是一个能改造成内部工具的底座——打卡、请假、出勤统计在小团队里是真的要用的。拿到源码后多数人第一步就跑偏了急着启动 Tomcat 看登录页却没先检查数据库脚本和配置。这篇文章从技术栈拆解、数据库初始化、本地部署到高频问题排查把这条链路完整走一遍。新手能照着跑起来老手也能从参数和数据设计里看到可复用的判断标准。2. 功能模块与三层架构这套 SSM 组合的骨架到底怎么搭2.1 五个核心业务模块拆解一个典型的 SSM 版公司员工考勤管理系统业务范围大致可以切分成五块。员工与组织管理维护部门、职位、员工基础信息包括工号、姓名、手机号、入职状态。这个模块通常是最先做的因为考勤记录和请假审批都要关联到员工表。考勤登记员工上下班打卡系统记录签到签退时间并据此计算迟到、早退、旷工等标记。请假审批员工提交请假单事假、病假、年假直属上级审批审批结果回写到请假记录。考勤统计按月度汇总每个员工的出勤天数、请假天数、迟到次数、旷工次数生成报表。这也是整个系统价值最高的模块管理者真正看的是统计结果不是单条打卡流水。系统管理用户登录、菜单权限、字典数据维护。很多源码包这部分做得比较薄只有一张用户表和角色字段拿到手后通常要自己补权限拦截逻辑。第一次打开源码时建议不要急着点运行按钮先打开 web.xml 和 spring-mvc.xml 看一眼。web.xml 里注册的 DispatcherServlet 路径、监听器加载的 Spring 上下文文件决定了整个请求从浏览器到后端类的走向。能在代码里画出“JSP 提交 → Controller 接收 → Service 处理 → Mapper 查库 → 返回视图”这条链路后面改任何一个功能都会心里有底。2.2 Controller、Service、Mapper 的职责边界与事务陷阱SSM 架构本身不复杂但职责边界一旦模糊项目就会变成谁也不敢动的毛线团。我一般用三条硬规则来约束代码结构Controller 只做参数接收和视图转发不写任何业务判断也不直接调用 Mapper。Service 承载全部业务逻辑比如迟到判定、请假时长计算、审批状态流转。Mapper 接口加 XML 文件只负责 SQL一条 SQL 对应一个方法不做跨表拼接的复杂运算。实际部署时最容易踩到的坑是事务失效。考勤打卡的写入、请假审批的状态更新都必须在 Service 方法上标注 Transactional。有些同学图省事在 Controller 里直接用 new 关键字创建 Service 对象再去调方法结果 Spring 的 AOP 代理完全没生效数据库出现异常时数据就停在中间状态。正确的做法是全程依赖 Autowired 注入让 Spring 接管对象的生命周期。这个细节在源码里看不出来但只要你改了代码就很容易亲手把它弄坏。2.3 为什么这套项目继续用 SSM 而不是 Spring Boot新项目几乎默认 Spring Boot可这类考勤系统源码依然坚持 SSM JSP主要有两个现实原因。一是教学与课设需求。Spring Boot 把大量配置自动化了学生反而看不懂组件之间的装配过程。SSM 的 web.xml、applicationContext.xml、spring-mvc.xml 虽然繁琐但每个配置项都对应一个真实的框架概念适合做课程设计的底板。二是改造成本。如果把一个 SSM JSP 项目迁到 Spring Boot涉及 JSP 视图解析器、Tomcat JSP 编译、spring-webmvc 版本协调还有 web.xml 中过滤器与监听器的迁移映射。对于一个小团队的内部考勤工具来说迁移本身不产生业务价值反而会引入一批新 bug。我见过有人花了两周时间做这种迁移最后页面渲染还是有问题又退回原样。所以除非有明确的架构升级要求否则拿到这套源码直接改功能比推倒重来划算得多。3. 数据库脚本与初始化SQL 文件才是真正决定能不能跑的部分3.1 核心业务表结构设计考勤系统的数据库设计并不复杂但三张核心表的关联关系必须清晰员工表、考勤记录表、请假表。下面是我在部署这类项目时习惯使用的简化结构基本能覆盖源码包中最常见的设计。员工表 employeeCREATE TABLE employee ( id INT PRIMARY KEY AUTO_INCREMENT, emp_no VARCHAR(20) NOT NULL UNIQUE COMMENT 员工工号, dept_id INT COMMENT 所属部门ID, name VARCHAR(50) NOT NULL COMMENT 姓名, phone VARCHAR(20) COMMENT 手机号, email VARCHAR(50) COMMENT 邮箱, status TINYINT DEFAULT 1 COMMENT 1在职 0离职, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;考勤记录表 attendance_recordCREATE TABLE attendance_record ( id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT NOT NULL COMMENT 员工ID, work_date DATE NOT NULL COMMENT 出勤日期, sign_in_time DATETIME COMMENT 签到时间, sign_out_time DATETIME COMMENT 签退时间, late_flag TINYINT DEFAULT 0 COMMENT 1迟到 0正常, leave_early_flag TINYINT DEFAULT 0 COMMENT 1早退 0正常, UNIQUE KEY uk_emp_date (emp_id, work_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;请假表 leave_requestCREATE TABLE leave_request ( id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT NOT NULL COMMENT 申请人ID, leave_type VARCHAR(20) COMMENT 请假类型事假/病假/年假, start_time DATETIME NOT NULL COMMENT 开始时间, end_time DATETIME NOT NULL COMMENT 结束时间, reason VARCHAR(500) COMMENT 请假事由, status TINYINT DEFAULT 0 COMMENT 0待审批 1已通过 2已驳回, approver_id INT COMMENT 审批人ID, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有几个参数细节值得说明。工号 emp_no 用 UNIQUE 约束避免重复分配考勤记录用 (emp_id, work_date) 组成联合唯一索引确保同一个员工同一天不会插入两条签到记录这是防止打卡数据刷重的关键。请假表的 status 字段用 TINYINT 而非 VARCHAR一方面节省存储另一方面在 SQL 里写 status 1 比 status approved 更高效。很多源码包没有建立外键我建议你也别建因为考勤系统后期会做历史数据归档外键会拖累批量删除和导入操作。关联关系靠 SQL 查询时的 JOIN 维护就足够了。3.2 初始化数据与默认账号说明SQL 文件里通常会包含一部分初始化数据比如部门表的基础数据、一个管理员账号、几个演示员工和几条考勤记录。导入前要重点关注两张表的内容。sys_user 或者 user 表里的账号密码。很多项目默认密码是 123456 或者 admin存的是 MD5 值。拿到源码后第一步不是改代码而是改密码。你可以直接在数据库里把默认账号的密码更新为自己的 MD5 值。employee 表和 department 表的基础数据。这些数据如果和你的组织架构对不上后续生成的考勤报表就没法看。建议先清空业务表再按自己的组织架构重新插入。重置业务数据的常用 SQLSET FOREIGN_KEY_CHECKS 0; TRUNCATE TABLE attendance_record; TRUNCATE TABLE leave_request; TRUNCATE TABLE employee; SET FOREIGN_KEY_CHECKS 1;如果数据库里存在外键约束TRUNCATE 会被拒绝所以先关闭外键检查清完再打开。这个操作只重置业务数据不会动 sys_user 等系统表。注意执行前一定要备份原数据库不要问我为什么强调这一句——我见过有人把整年的考勤数据一条 TRUNCATE 清掉又没有备份最后只能对着 binlog 后悔。3.3 SQL 文件导入方法与验证导入 SQL 的方式有两种按你的使用习惯选一种即可。第一种命令行直接导入。mysql -u root -p attendance.sqlEnter password 后输入数据库密码。如果 SQL 文件里已经包含 CREATE DATABASE 语句导入会自动创建数据库如果没有你需要先手动创建一个库再执行 USE 切换。mysql -u root -p CREATE DATABASE attendance DEFAULT CHARACTER SET utf8mb4; USE attendance; SOURCE /path/to/attendance.sql;第二种使用图形化客户端导入。这种方式更容易看到导入过程中的报错信息。导入完成后做三步验证SHOW TABLES; SELECT COUNT(*) FROM employee; SELECT * FROM sys_user;执行 SHOW TABLES 确认表数量符合文档描述统计 employee 表行数确认初始化数据是否完整查询 sys_user 表找到默认账号并记下它的密码字段值后续登录要用。如果表结构和预期不同优先检查 SQL 文件头部是否有版本注释不同源码包的表名设计差异很大。4. 本地部署与运行从环境准备到浏览器出现登录页4.1 环境版本搭配这个项目的技术栈决定了它不能像 Spring Boot 一样无脑运行。环境版本选不好启动阶段就会卡住。我常用的搭配如下表所示组件建议版本说明JDK1.8大多数 SSM 老项目基于 JDK 8 编译高于此版本可能遇到 JSP 编译兼容问题Tomcat8.5对 JSP 支持稳定和 JDK 8 配合良好MySQL5.7 或 8.05.7 兼容性最好8.0 需要调整 JDBC 驱动和连接参数Maven3.6 以上用于编译和打包版本过新不影响这个项目如果你之前机器上装的是 JDK 11 以上跑这类项目有时会遇到启动失败报错指向 JSP 编译或反射调用。最快的解决办法不是升级项目而是安装一个 JDK 8 并切换 IDE 的项目 SDK。Tomcat 和 JDK 版本不匹配的报错信息往往不是直接说版本问题而是抛 ClassNotFoundException 或 UnsupportedClassVersionError后面避坑章节会细说。4.2 项目导入与关键配置修改拿到源码包后我先解压用开发工具导入为 Maven 项目。导入完成后不要急着点运行按钮先找到配置文件把环境变量改好。SSM 项目里数据库连接配置一般放在 jdbc.properties 或 db.properties内容类似下面这样jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/attendance?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456配置项说明driverMySQL 5.7 用 com.mysql.jdbc.Driver如果连的是 MySQL 8.0必须改成 com.mysql.cj.jdbc.Driver否则驱动类加载失败。url 里的 characterEncodingutf8 要保留它和页面乱码问题直接相关。serverTimezoneAsia/Shanghai 是给 MySQL 8.0 用的不加的话会在查询时间字段时抛异常提示 Server returns invalid timezone。修改完配置打开 spring-mvc.xml确认视图解析器的前缀和后缀。常见配置是把 JSP 文件放在 /WEB-INF/views/ 下bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views/ / property namesuffix value.jsp / /bean这个配置的意思是Controller return login 时Spring 会去找 /WEB-INF/views/login.jsp。如果你把 JSP 放在了别的目录或者去掉后缀运行时会一直报 404。我一般会逐一打开几个 JSP 文件确认路径是否和视图解析器匹配因为源码包在传播过程中经常有人改动目录结构导致页面找不到。4.3 编译、启动与日志观察在 IDE 里选择 Tomcat Server 方式运行或者用 Maven 命令启动。两种方式我都试过给出一条命令行方案mvn clean package -DskipTests mvn tomcat7:run第一条命令清理编译产物并打 war 包跳过测试能减少打包时间第二条命令借助 Maven 内置的 Tomcat 插件启动应用。如果你的 pom.xml 配置了其他插件使用 IDE 的 Run 按钮更稳妥。启动后观察控制台日志看到类似下面的输出才算真正启动成功INFO: Deploying web application directory ... INFO: Development mode is enabled.出现Deploying web application表示 Tomcat 在加载项目不是最终状态。要等看到项目 context 对应的路径再打开浏览器。访问地址一般是 http://localhost:8080/ 项目名/。打开后页面会跳转到登录页输入初始化数据里的默认账号密码。如果启动过程中报错优先看控制台最底部的 Caused by 部分而不是最上方的异常信息。SSM 项目的报错链很长真正的根因通常在最后几行。我见过有人盯着最上面一行异常查了一小时最后发现只是数据库密码写错了。5. 避坑部署 SSM 考勤系统最容易翻车的五个场景5.1 数据库连接失败时区错误和驱动类找不到现象启动时抛异常日志里出现The server time zone value 中国标准时间 is unrecognized或ClassNotFoundException: com.mysql.jdbc.Driver。原因MySQL 8.0 对时区参数更严格且新的驱动类路径改为 com.mysql.cj.jdbc.Driver。源码包里的配置文件多半是旧版驱动路径。解决把 jdbc.driver 改成 com.mysql.cj.jdbc.Driverurl 末尾加上serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue。allowPublicKeyRetrieval 这个参数在 MySQL 8.0 下经常被漏掉不加的话连接时偶尔报 Public Key Retrieval is not allowed。5.2 页面中文乱码前端页面和数据库编码不一致现象登录界面中文正常但查询出来的员工姓名、部门名称显示成问号或者乱码。原因JSP 文件编码、MySQL 表字符集、数据库连接参数三者不一致。JSP 声明了 UTF-8但表是 latin1或者连接参数里没加 characterEncodingutf8都会出问题。解决检查 JSP 头部有没有contentTypetext/html; charsetUTF-8确认建表语句里 CHARSETutf8mb4保证 jdbc.url 里携带characterEncodingutf8。三处统一后重新启动再查一次数据即可。5.3 Tomcat 启动闪退或端口被占用现象点击启动后控制台很快消失或者报Port 8080 was already in use。原因前面启动的 Tomcat 进程没有完全退出或者 IDE 中启动过一次但未停止另一个原因是你之前部署过其他项目占用了同一端口。解决先杀掉残留 Java 进程。Windows 下用命令查看端口占用netstat -ano | findstr 8080 taskkill /PID 端口号 /F然后重新启动。如果端口必须换成别的在 Tomcat 配置里修改 server.xml 的 Connector 端口同时在 IDE 的启动配置里保持一致。改端口后访问地址也要变别照旧访问 8080。5.4 登录后页面 404 或静态资源加载失败现象登录成功跳转到首页但页面样式全丢或者点菜单跳到 404。原因Spring MVC 把静态资源的请求拦截了。DispatcherServlet 拦截了 /css/、/js/、/images/ 等路径但项目里没有配置放行这些资源。解决在 spring-mvc.xml 里加入静态资源放行配置mvc:default-servlet-handler /或者更明确地配置 resource 映射mvc:resources mapping/static/** location/static/ /配置完清理浏览器缓存再刷新。如果 404 只出现在某个菜单上优先检查 Controller 的 RequestMapping 值和 JSP 侧跳转路径是否一致路径大小写不一致在这种老项目里也很常见。5.5 打卡数据统计不对跨天和时区问题现象员工在 23:58 打卡系统记录为第二天日期统计月度出勤时天数持续不准确。原因考勤记录的时间字段用的是 DATETIME与业务日期 work_date 分开存储但打卡写入逻辑里直接用当前日期作为 work_date忽略了凌晨打卡跨天的场景。解决在 Service 层做一次时间校正。如果打卡时间在 00:00–06:00 之间work_date 取前一天。这里给出一个简化处理示例Date now new Date(); Calendar c Calendar.getInstance(); c.setTime(now); int hour c.get(Calendar.HOUR_OF_DAY); if (hour 6) { c.add(Calendar.DAY_OF_MONTH, -1); } Date workDate c.getTime();逻辑说明凌晨 0 点到 6 点的打卡记录归属到上一个工作日避免把晚班员工的考勤算到错误的日期。小时阈值可以根据公司排班习惯调整比如三班倒企业可以改成 5 点。这个逻辑修改只需要改 Service 层不需要动页面和数据库结构。6. 进阶给考勤系统加一个月度统计的自动汇总任务项目跑起来后你会很快发现一个可复用的痛点管理员需要每月一号手动点一个按钮去生成上个月的统计报表哪天忘记点数据就空着或延迟。这个场景最适合加一个定时任务把整个统计动作自动化。常见的做法是用 Spring 自带的 Scheduled不引入额外依赖。先把这个能力在 spring-mvc.xml 里打开task:annotation-driven schedulertaskScheduler / task:scheduler idtaskScheduler pool-size2 /然后在 Service 实现类里新增一个汇总方法并加上调度注解Component public class AttendanceSummaryJob { Autowired private AttendanceStatsService statsService; Scheduled(cron 0 0 2 1 * ?) public void generateMonthlySummary() { // 执行上个月的汇总统计并写入 summary 表 statsService.generateLastMonthSummary(); } }参数说明cron 表达式0 0 2 1 * ?表示每月 1 日凌晨 2 点执行一次此时刚好避开打卡高峰期。如果你希望当月最后一天晚上执行可以改成0 0 1 L * ?L 在 Quartz 中表示当月最后一天。这里有个隐藏坑定时任务必须保证幂等——如果 2 点执行失败你手动补跑了一次就可能生成两条汇总记录。解决办法是在数据库里建一个唯一索引字段组合为 emp_id summary_month插入时使用INSERT INTO summary (emp_id, summary_month, ...) VALUES (...) ON DUPLICATE KEY UPDATE ...这样重复执行只会更新数据不会插入新行。加上这个功能后整个系统就从“能演示的考勤小程序”变成了“月初不用人操心”的可用工具。当初我给自己公司的考勤系统加这个任务时遇到过两个问题一是 Cron 表达式写错导致任务没触发排查方法很简单——去看看 Tomcat 日志定时任务启动时会打印一行调度信息没看到就是配置没生效二是 “L” 字符在 Spring 的 Cron 里不是所有语义都支持如果你的调度器是基于 Spring 自身而非 Quartz不要用 L直接用每月 1 号执行更稳。希望这套流程和参数经验能帮到你至少别在数据库清数据和时区这类问题上再花一个晚上。本文还有配套的精品资源点击获取