☰
SSM员工考勤管理系统设计与部署全流程详解
2026/9/30 11:17:33 网站建设 项目流程

考勤管理系统在Java课程设计和毕业设计里几乎是"永不过时"的选题。每年都能看到大量"java_ssm员工考勤管理系统"相关的需求,但这个题目其实可以做得很有深度。我手上这套基于SSM(Spring + SpringMVC + MyBatis)的员工考勤管理系统,完整跑通了从数据库设计、框架整合到功能实现和IDEA部署的全流程,这里把整个项目的设计思路、核心代码、配置细节和踩坑记录一次讲清楚,给正在做类似项目的同学一个可以直接参考的完整范本。

这个系统适合两类人:一是需要完成Java课程设计、毕业设计的学生,想找一个功能完善、逻辑清晰、能答辩讲得明白的项目;二是刚学完SSM框架、想通过一个完整项目把三大框架串起来练手的开发者。我会尽量把每一步的"为什么"也讲清楚,而不是单纯丢一堆代码让你抄。

1. 为什么考勤系统是SSM项目的最佳练手选题

很多人做课程设计时喜欢选管理系统,但容易陷入"CRUD凑页面"的尴尬。考勤系统最不一样的地方在于,它的业务规则非常明确——上下班打卡、迟到早退判断、请假审批、考勤统计,这些规则逼着你必须去思考"状态怎么管理"、"时间怎么计算"、"统计怎么汇总",而不是简单地往数据库里插数据。

1.1 这个系统的业务价值在哪

从企业实际场景看,考勤系统要解决的是三个核心问题:

  • 记录:员工每天几点来、几点走,必须有据可查。
  • 规则:什么算迟到、什么算早退、请假怎么批,这些规则要固化成程序逻辑,不能靠人肉判断。
  • 统计:月底HR要算全勤、扣款、加班补贴,统计结果必须准确且可导出。

这三个问题恰好对应了SSM框架各层要解决的典型问题:页面交互和数据展示交给SpringMVC,业务规则和事务控制交给Service层和Spring,数据存取交给MyBatis。一个项目学完,等于把SSM三个框架都用在了刀刃上。

1.2 为什么选SSM而不是Spring Boot

说实话,现在Spring Boot早就成了主流,但课程设计和毕业设计导师往往更认可SSM。原因很实际:SSM整合过程中需要手写大量配置文件(web.xml、spring.xml、spring-mvc.xml、MyBatis配置),这个过程能体现你对框架底层原理的理解,而Spring Boot的自动配置把所有环节都"藏"起来了,答辩时反而没什么可讲的。

另外,SSM项目的分层非常清晰,写出来的代码结构天然适合展开讲解。比如我自己在做功能扩展时,要加一个"加班申请"模块,从Controller到Mapper每个层动哪些文件一目了然。对于需要答辩的课程设计来说,这种结构本身就是加分项。

1.3 开发环境与版本选择

这套系统的开发环境和版本如下,虽然是两年前的组合,但胜在稳定、资料多、遇到问题随便一搜就有答案:

  • JDK 1.8(不要用太高版本,SSM经典组合对JDK 8最友好)
  • Maven 3.6+
  • Tomcat 8.5(适配JDK 8,部署配置简单)
  • MySQL 5.7(如果你本机是MySQL 8.0也行,但要注意驱动和连接参数,后文有坑)
  • IDEA 2020.3或更新版本(社区版也够用,但做Web项目建议用Ultimate版)
  • 依赖管理:Maven,统一在pom.xml里维护

这里特别提醒一下,MySQL版本差异是第一个容易踩的坑。5.7和8.0的驱动类名不同,8.0需要额外配置时区,很多同学项目明明代码没问题,却卡在"数据库连不上"这一步,多半是连接串写错了。

2. 功能模块划分:从需求到代码的分解过程

拿到需求先别急着写代码,把功能模块画清楚(哪怕只是用Word列个清单),后面开发会快很多。这套系统的功能围绕两种角色展开:管理员(也可以理解成HR或部门主管)和普通员工,登录后进入完全不同的功能界面。

2.1 员工端功能清单

员工是这个系统最高频的使用者,每天都要打开来打卡。员工端的功能我整理成一张表:

功能说明核心难点
登录注册员工工号+密码登录,新员工可注册密码加密存储
上下班打卡点击按钮记录打卡时间迟到/早退判断
查看考勤记录按日期查看自己的打卡历史日期范围查询
请假申请提交请假单,填写类型、时间、事由状态流转设计
查看请假记录查看自己所有请假记录及审批状态状态筛选
个人信息维护修改手机号、邮箱、密码数据校验

2.2 管理员端功能清单

管理员的职责比员工复杂一个量级,既要管人,又要管数据:

  • 部门管理:部门增删改查,删除前要检查该部门下是否有员工。
  • 员工管理:对员工信息的增删改查,重置密码,离职状态标记。
  • 考勤记录管理:按部门、日期范围、姓名筛选考勤记录,补卡审批。
  • 考勤统计:按月/日汇总部门出勤、迟到、早退、缺勤人数。
  • 请假审批:审核员工提交的请假申请,批准或驳回。
  • 公告管理:发布放假通知、作息时间调整等公告。

2.3 业务流程中的关键设计:考勤状态怎么定义

打卡记录的核心是状态字段的设计,这直接关系到SQL难度和统计逻辑。我把每次打卡拆成两条记录:早上一次、晚上一次,每条记录包含"时间"和"结果状态"。状态我用字典常量管理:

  • 上班打卡状态:正常(NORMAL)、迟到(LATE)、缺卡(MISSING)
  • 下班打卡状态:正常(NORMAL)、早退(EARLY)、缺卡(MISSING)

这里有个容易被忽略的设计点:缺卡和未打卡不能混为一谈。缺卡是当天没有打上班卡或下班卡,而未打卡是当天完全没有任何记录。统计时这两种情况算法完全不同,缺卡要单独标记出来,不能当成旷工处理。

3. 数据库表结构设计:这个系统的地基

SSM项目的数据库设计直接决定后面MyBatis写SQL的难度。表设计得烂,后面每个查询都要写一堆绕来绕去的关联;设计得好,统计功能就是几条简单SQL的事。这套系统一共设计了6张核心表,每一张都仔细斟酌过。

3.1 员工表(employee)

员工表是系统的用户表,用于登录认证和基础信息展示:

CREATE TABLE `employee` ( `id` INT NOT NULL AUTO_INCREMENT, `emp_no` VARCHAR(20) NOT NULL COMMENT '工号,登录账号', `password` VARCHAR(64) NOT NULL COMMENT '密码,MD5加密', `emp_name` VARCHAR(30) NOT NULL COMMENT '姓名', `gender` CHAR(1) DEFAULT '男' COMMENT '性别', `department_id` INT DEFAULT NULL COMMENT '所属部门ID', `position` VARCHAR(50) DEFAULT NULL COMMENT '职位', `phone` VARCHAR(20) DEFAULT NULL, `email` VARCHAR(50) DEFAULT NULL, `entry_date` DATE DEFAULT NULL COMMENT '入职日期', `status` INT DEFAULT 1 COMMENT '1在职 0离职', PRIMARY KEY (`id`), UNIQUE KEY `uk_emp_no` (`emp_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

工号emp_no务必加唯一索引。员工离职时做逻辑删除(status置0),而不是物理删除,这样历史考勤数据才能保留。

3.2 部门表(department)

CREATE TABLE `department` ( `id` INT NOT NULL AUTO_INCREMENT, `dept_name` VARCHAR(50) NOT NULL COMMENT '部门名称', `manager_id` INT DEFAULT NULL COMMENT '负责人员工ID', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3.3 考勤记录表(attendance)

这是整个系统最核心的表,需要同时支持按日查询、按人统计、按部门汇总,所以字段尽量单独拆开:

CREATE TABLE `attendance` ( `id` INT NOT NULL AUTO_INCREMENT, `emp_id` INT NOT NULL COMMENT '员工ID', `att_date` DATE NOT NULL COMMENT '打卡日期', `check_in_time` DATETIME DEFAULT NULL COMMENT '上班打卡时间', `check_out_time` DATETIME DEFAULT NULL COMMENT '下班打卡时间', `in_status` VARCHAR(20) DEFAULT 'MISSING' COMMENT '上班状态', `out_status` VARCHAR(20) DEFAULT 'MISSING' COMMENT '下班状态', `late_minutes` INT DEFAULT 0 COMMENT '迟到分钟数', `early_minutes` INT DEFAULT 0 COMMENT '早退分钟数', `work_type` VARCHAR(10) DEFAULT 'NORMAL' COMMENT '工作日/休息日/节假日', PRIMARY KEY (`id`), KEY `idx_emp_id_date` (`emp_id`, `att_date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

注意联合索引(emp_id, att_date)。考勤查询最频繁的场景就是"查某个人某段时间的记录",这个联合索引能让查询速度提升非常明显。

3.4 请假表(leave_request)

CREATE TABLE `leave_request` ( `id` INT NOT NULL AUTO_INCREMENT, `emp_id` INT NOT NULL, `leave_type` VARCHAR(20) NOT NULL COMMENT '事假/病假/年假/调休', `start_time` DATETIME NOT NULL, `end_time` DATETIME NOT NULL, `reason` VARCHAR(500) DEFAULT NULL, `status` INT DEFAULT 0 COMMENT '0待审批 1已批准 2已驳回', `apply_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `approve_time` DATETIME DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_emp_id` (`emp_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3.5 管理员表和公告表

管理员独立建表admin_user,字段就是id、登录名、密码、姓名。注意不要把管理员和员工混在一张用户表里,因为课程设计答辩时,导师很容易问"这两种角色的权限边界是怎么控制的"——分开设计可以在代码层面用不同表查询不同角色,讲起来很干净。

4. SSM框架整合:配置文件里的关键细节

很多同学搭SSM时,配置都是复制粘贴的,出了错根本不知道是哪里的问题。这里我把这套项目里的核心配置逐步拆开讲,每个文件负责什么,为什么要这样写。

4.1 pom.xml:依赖版本要统一

SSM项目最容易出现的异常是"ClassNotFoundException"或"AbstractMethodError",多半是依赖版本冲突。我建议直接固定一套经过验证的版本组合:

<properties> <spring.version>5.3.9</spring.version> <mybatis.version>3.5.9</mybatis.version> </properties> <!-- 核心依赖:spring-context, spring-webmvc, spring-jdbc, mybatis, mybatis-spring --> <!-- mybatis-spring版本必须和Spring版本匹配,这里用2.0.6 --> <!-- 数据库:mysql-connector-java 8.0.29(对应MySQL 8.0) --> <!-- 连接池:druid 1.2.8 --> <!-- JSON工具:jackson-databind 2.12.4 --> <!-- 分页插件:pagehelper 5.3.0 --> <!-- 日志:slf4j-api + logback-classic -->

版本匹配规则是:Spring 5.x配mybatis-spring 2.x,这个不能乱配。Spring 4.x配mybatis-spring 1.x,如果配错会在启动时报NoSuchBeanDefinitionException,但日志又不明显,排查起来非常头疼。

4.2 web.xml:监听器、分发器、乱码过滤器

SSM是Java Web项目,第一步配置web.xml。核心三件事:启动Spring容器、启动SpringMVC分发器、统一编码。

<!-- 启动Spring容器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-context.xml</param-value> </context-param> <!-- SpringMVC 前端控制器 --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 乱码过滤器 --> <filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

乱码过滤器一定要放在所有过滤器的第一个位置。这个顺序有时候书上不讲,但实际部署时如果后面再加了登录拦截器,过滤顺序错了就会出现"中文参数到Controller变成??"的情况。

4.3 spring-context.xml:数据源、事务、扫描

这个文件管理的是Spring核心容器,包括DataSource、事务管理器、Service层扫描等:

<!-- 读取数据库配置文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="typeAliasesPackage" value="com.example.attendance.entity"/> <property name="mapperLocations" value="classpath:mapper/*.xml"/> <!-- 这个配置非常重要,下节详细说 --> <property name="configuration"> <bean class="org.apache.ibatis.session.Configuration"> <property name="mapUnderscoreToCamelCase" value="true"/> </bean> </property> </bean> <!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- Service层扫描 --> <context:component-scan base-package="com.example.attendance.service"/>

4.4 一个能救命的配置:mapUnderscoreToCamelCase

数据库字段是emp_no、check_in_time这种下划线风格,Java实体类是empNo、checkInTime,如果不开mapUnderscoreToCamelCase,MyBatis查询结果根本映射不上,所有字段都是null。这个配置必须在SqlSessionFactory里设置,很多教程漏掉这一步,导致很多人以为MyBatis的resultType映射有问题,其实只是驼峰映射没打开。

4.5 spring-mvc.xml:注解驱动、视图解析器、静态资源

Controller层配置相对简单,但有几个容易忽略的点:

<context:component-scan base-package="com.example.attendance.controller"/>

mvc:annotation-driven/

mvc:default-servlet-handler/

这里mvc:default-servlet-handler特别重要。前端页面引用了CSS、JS、图片等静态资源,如果不加这一行,所有静态资源请求都会被DispatcherServlet拦截,页面样式全丢。加了之后Spring会把静态资源请求交还给容器默认的Servlet处理。

5. 核心功能代码实现:登录、打卡、统计

配置搭好之后就进入正题。我把这套系统里最有代表性的四个功能拿出来,完整讲一遍代码实现思路。

5.1 登录和角色识别

登录功能本身不难,关键点是登录成功后怎么区分员工和管理员。我的做法是登录时查两张表,哪张表有记录就按哪个角色处理,然后在Session里存一个loginType字段:

// 登录时判断角色 Employee emp = employeeService.login(empNo, password); if (emp != null) { session.setAttribute("loginUser", emp); session.setAttribute("loginType", "EMPLOYEE"); return "redirect:/employee/index"; } Admin admin = adminService.login(adminNo, password); if (admin != null) { session.setAttribute("loginUser", admin); session.setAttribute("loginType", "ADMIN"); return "redirect:/admin/index"; }

拦截器里校验Session的loginType,就能控制页面访问权限。这里密码建议用MD5加盐,虽然MD5不算安全,但课程设计完全够用,答辩时还能讲出"为了安全我做了MD5加密"这个点。

5.2 打卡逻辑:状态判断和并发安全

打卡是考勤系统的灵魂。员工点一下"上班打卡"按钮,后端大致要做这样几件事:

  1. 判断今天是否已经打上班卡(同一人同一天只能有一条记录)。
  2. 获取当前系统时间,和规定的上班时间比较。
  3. 判断状态是正常还是迟到,计算迟到分钟数。
  4. 写入或更新考勤记录。

核心代码逻辑:

// 上班打卡 public boolean checkIn(Employee emp) { Date now = new Date(); Date today = DateUtils.truncate(now, Calendar.DATE); Attendance record = attendanceMapper.selectByEmpAndDate(emp.getId(), today); if (record == null) { // 首次打卡,创建记录 record = new Attendance(); record.setEmpId(emp.getId()); record.setAttDate(today); record.setCheckInTime(now); // 9:00后算迟到 if (now.after(workStartTime)) { record.setInStatus("LATE"); record.setLateMinutes((now.getTime() - workStartTime.getTime()) / 60000); } else { record.setInStatus("NORMAL"); } attendanceMapper.insert(record); } else if (record.getCheckOutTime() == null) { // 上班卡已经打过,打下班卡 // 18:00前算早退 } else { // 一天已经打过两次卡 return false; } return true; }

这里有几个细节值得留意。同一人一天只能有一条记录的约束要在Service层提前判断,不要只依赖数据库,否则并发点两个按钮就会出现两条打卡记录。另外,上班时间和下班时间建议放在配置表或常量类里,不要硬编码在业务代码中,后面改夏令时或调整作息只需要改一处配置。

5.3 考勤统计:一条SQL还是多条SQL

考勤统计通常是答辩时最容易被问到的点。很多人的实现方式是写循环:遍历员工,每个员工查一次考勤表,再汇总——这在小数据量下没问题,但逻辑完全经不起推敲。

我采用的是分组统计SQL,比如统计每天每条记录的打卡情况:

SELECT att_date, COUNT(CASE WHEN in_status = 'NORMAL' THEN 1 END) AS normal_count, COUNT(CASE WHEN in_status = 'LATE' THEN 1 END) AS late_count, COUNT(CASE WHEN in_status = 'MISSING' THEN 1 END) AS missing_count FROM attendance GROUP BY att_date ORDER BY att_date DESC

如果是按月汇总每个员工的出勤情况,就按员工分组:

SELECT emp_id, COUNT(CASE WHEN in_status = 'NORMAL' THEN 1 END) AS normal_days, COUNT(CASE WHEN in_status = 'LATE' THEN 1 END) AS late_days, COUNT(CASE WHEN out_status = 'EARLY' THEN 1 END) AS early_days FROM attendance WHERE att_date BETWEEN #{startDate} AND #{endDate} GROUP BY emp_id

这种写法只需要一条SQL、一次数据库查询,MyBatis映射到List后,前端用表格展示即可。而且这种SQL在答辩时讲起来非常有说服力——"我用CASE WHEN配合GROUP BY对考勤状态做了聚合统计,避免了循环查询数据库的性能问题"。

5.4 请假审批:状态机和操作权限

请假模块的核心是状态机。状态只有三个:待审批、已批准、已驳回。我严格控制了状态流转的规则:

  • 员工提交请假:状态从无到"待审批",此时可以撤销。
  • 管理员审批通过:状态从"待审批"到"已批准"。
  • 管理员审批驳回:状态从"待审批"到"已驳回"。
  • 已批准和已驳回不允许再做任何操作,不能重复审批。

这个规则在Service层用if判断控制,而不是在Controller层写业务逻辑。举个例子,管理员驳回时要注意:员工重新提交的请假单会生成一条新的记录,不会复用旧记录,避免审批历史出现歧义。这个点在答辩时也挺加分的。

6. IDEA导入与Tomcat部署:最容易翻车的环节

代码写得再好,部署不成功也白搭。SSM项目的部署有几个高频踩坑点,我一个个说,都是我实际趟过的。

6.1 IDEA导入Maven项目的正确姿势

拿到源码后(无论从网上下载的还是自己写的),IDEA导入步骤是:

  1. File → New → Project from Existing Sources。
  2. 选择项目根目录下的pom.xml。
  3. 选择"Import as Maven Project"。
  4. 等待Maven下载依赖(第一次大概5-10分钟,取决于网络速度)。

这里最容易出的问题:Maven下载慢或下载失败。解决方案是修改Maven镜像源为阿里云公共仓库:

<mirror> <id>aliyun</id> <mirrorOf>central</mirrorOf> <url>https://maven.aliyun.com/repository/public</url> </mirror>

6.2 配置Tomcat时常见的问题

配置Run → Edit Configurations → Tomcat Server → Local,在"Deployment"标签页里把项目以war exploded方式添加,这样改代码后重启更快,也方便热部署。Application context建议设为/attendance,不要用根路径,后面访问就是http://localhost:8080/attendance。

部署环节最常见的三个错误:

404问题:多半是Artifact没有添加成功,或Application context配错了。打开http://localhost:8080确认Tomcat本身能访问,再看项目路径。

ClassNotFoundException:org.springframework.web.context.ContextLoaderListener:说明Tomcat运行时没有加载到项目的Maven依赖。右键项目 →Open Module Settings→Artifacts,把lib目录里的依赖包加进来。这个操作在IDEA 2020之后被很多人忽略,段位高一点的工程师建议养成每次导入项目后检查Artifacts的习惯。

数据库连接失败:报Access denied for user 'root'@'localhost'时,检查jdbc.properties里的用户名、密码,尤其注意MySQL 8.0默认的认证插件是caching_sha2_password,而低版本连接驱动不支持这个认证方式,需要改URL和驱动。

6.3 MySQL 8.0的连接配置坑

如果是MySQL 8.0,jdbc.properties要这样写:

jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/attendance_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4 jdbc.username=root jdbc.password=你的密码

注意驱动类名是com.mysql.cj.jdbc.Driver,不再是com.mysql.jdbc.Driver(后者在8.0下会报Driver has not been revoked这类错误)。serverTimezone必须写,否则报一堆时区异常。characterEncoding统一用utf8mb4,能覆盖全部中文和表情符号。

6.4 页面静态资源丢失和AJAX乱码

部署成功后打开页面样式全丢,九成是spring-mvc.xml里没加<mvc:default-servlet-handler/>。另外JSP页面引用CSS时,路径要带上${pageContext.request.contextPath},不然换一台机器部署后路径就写死了。

AJAX请求返回中文乱码,除了web.xml里配了CharacterEncodingFilter之外,还要在spring-mvc.xml里配置消息转换器强制使用UTF-8。实际上我们Controller返回的往往是JSON,乱码的根源在于Response的Content-Type里没有charset,用@RequestMapping(produces = "application/json;charset=UTF-8")可以逐个接口解决,生产级做法则是统一配置StringHttpMessageConverter的编码。

7. 给答辩加分的扩展点与优化方向

核心功能做完之后,这套系统已经算是一个完整的SSM课程设计项目。但如果你想在答辩时拿更高分,或者上线给真实小团队用,下面这几个方向非常值得投入精力。它们的技术难度和现有系统架构完全兼容,不会伤筋动骨。

7.1 让考勤数据"看得见":接入ECharts统计图表

考勤统计列表做得再漂亮,也不如一个图表直观。ECharts(Apache ECharts)可以通过CDN方式引入,和后端完全解耦,不需要改任何Java代码。你只需要增加一个接口返回统计数据:

@ResponseBody @RequestMapping("/admin/chartData") public Map<String, Object> chartData(String month) { List<Map<String, Object>> data = attendanceService.getMonthlySummary(month); Map<String, Object> result = new HashMap<>(); result.put("code", 0); result.put("data", data); return result; }

前端通过jQuery的$.ajax拿到数据后,用ECharts画一个部门出勤对比的柱状图和一个迟到原因的饼图。答辩时打开页面,数据一张图表直接呈现出来,导师的观感立刻不一样。这个改动的投入产出比极高,强烈建议做。

7.2 定时任务:每天自动"锁卡"并生成缺勤标记

打卡时间不是实时判断的吗?为什么还需要定时任务?因为真实场景是"忘了打卡":员工上班忘了点按钮,到下午才想起来,上班卡就是空的。如果只靠实时判断,这条记录就永远缺了上班卡。

合理的设计是写一个定时任务,每天凌晨自动扫描前一天的考勤记录,把打卡状态为空的记录标记为缺卡。SSM里用Spring Task就能实现,在spring-context.xml里开启注解定时:

<task:annotation-driven scheduler="taskScheduler"/> <task:scheduler id="taskScheduler" pool-size="5"/>

然后写一个定时Job方法:

@Component public class AttendanceCheckTask { @Scheduled(cron = "0 30 1 * * ?") public void autoCheckMissingCards() { // 找到昨天还没有打卡状态的记录 // 把 in_status 或 out_status 为 NULL 的记录补成 MISSING } }

这个功能在答辩时是实打实的业务亮点,一举说明你考虑到了真实考勤场景中的边界情况,而不只是做一个"能打卡"的demo。

7.3 对接企业微信或钉钉的提醒功能

如果使用了别人封装好的SDK,接一个"考勤异常通知"功能是可行的:当员工打卡成功或出现迟到记录时,通过Webhook机器人推送到企业微信群或钉钉群。技术本质就是发一个HTTP POST请求,带着封装好的JSON消息体。这个功能适合作为项目展示时的"锦上添花",能明显体现出你的架构思维——考勤系统不只是企业内部页面,还可以和办公协同工具联动。

7.4 安全层面还能补什么

课程设计阶段,大家普遍不重视安全,但这是一个可以在答辩时展示思考的维度:

  • 密码加密:至少用MD5加盐,讲清楚MD5不是加密算法而是哈希算法,以及它为什么不安全、为什么后续要考虑BCrypt。
  • SQL注入防护:MyBatis的#{}预编译机制本身就防注入,但如果你在XML里用${}拼接字段(比如动态排序列名),就一定要做白名单校验。
  • Session过期处理:在拦截器里统一校验Session是否过期,过期后跳转登录页并提示重新登录。
  • 统一异常处理:用@ControllerAdvice和@ExceptionHandler做全局异常拦截,不让500错误页面裸奔。

这些在代码里改动成本极低,但答辩时每一句都经得起追问。

8. 项目运行自查清单与常见异常表

最后给你一份自查清单和异常对照表,不管你是要验收代码还是要调试这套系统,按这个顺序检查能省下很多排查时间。

先是一份启动自查清单:

  1. 确认数据库已创建,编码为utf8mb4,SQL脚本执行成功,表名、字段名和代码里一致。
  2. 确认jdbc.properties用户名密码正确,URL符合本机MySQL版本。
  3. 确认IDEA里Maven配置使用本地仓库,且依赖无红色报错。
  4. 确认Artifacts里已包含所有依赖lib。
  5. 确认启动前没有其他进程占用8080端口。
  6. 启动后看IDEA的Run窗口有没有Initializing Spring root WebApplicationContext以及Tomcat started日志。
  7. 浏览器访问http://localhost:8080/attendance,先看登录页,再逐条测试打卡、统计、审批。

常见异常对照表:

现象可能原因处理方式
项目启动报404Artifacts没配好 / context path不对重新配置Tomcat Deployment
Tomcat启动成功但访问空白Spring容器初始化失败日志没看清查看catalina日志,看有没有Bean创建失败的堆栈
页面中文全是??数据库连接URL少了characterEncodingjdbc.url加上characterEncoding=utf8mb4
数据库查出来字段全是nullmapUnderscoreToCamelCase没开启在SqlSessionFactory里开启驼峰映射
提示Column emp_no not foundMyBatis的SQL里没写正确的列名检查mapper XML中的SQL和表结构是否一致
报Access denied for user数据库用户名密码错误核对jdbc.properties并确认MySQL服务正常
登录成功但页面没有样式缺少default-servlet-handlerspring-mvc.xml加mvc:default-servlet-handler
页面跳转时报500大概率是Controller里返回的JSP路径不对检查Controller return的字符串和WEB-INF/jsp下的文件是否匹配

9. 最后再说点个人经验

做完这个项目,我最大的一个体会是:**SSM框架项目的复杂度控制,关键在于把业务规则想清楚后再动手。**如果一上来就写代码,很容易把考勤逻辑写成一团乱麻——按钮点了就插一条记录,统计就是循环查表,最后代码堆了三层,自己都讲不清楚。

如果你正在做类似的课程设计或毕业设计,我建议按这套思路来推进:先把考勤状态定义清楚(迟到、早退、缺卡、请假),再把一个员工从入职到离职会经历的所有考勤动作列一遍,最后才是建表、搭框架、写代码。这套系统里我用红色作为页面主色调,其他视觉风格你也可以按自己喜好替换,跟核心逻辑完全无关。希望这篇拆解能让你少走一些弯路,如果你在搭SSM环境、部署Tomcat时卡住了,回头再多看几遍第6章的内容——那些坑基本都是每个做SSM项目的人都会遇到的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询