1. 项目概述
考勤管理是企业日常运营中不可或缺的一环,传统的人工考勤方式效率低下且容易出错。这个基于SpringBoot的考勤管理系统正是为了解决这些问题而设计的。系统采用B/S架构,实现了员工信息管理、考勤记录、请假审批、数据统计等核心功能模块。
我在开发这个系统时,特别注重实际业务场景的适配性。比如针对制造业常见的三班倒考勤需求,系统支持灵活设置班次规则;针对外勤人员,开发了基于地理位置的移动端签到功能。整个系统采用前后端分离架构,前端使用Vue.js+ElementUI,后端基于SpringBoot+MyBatisPlus,数据库选用MySQL 8.0。
2. 技术架构解析
2.1 后端技术栈
SpringBoot 2.7作为基础框架,其自动配置特性大大简化了项目搭建过程。我特别使用了以下关键配置:
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class }) public class AttendanceApplication { public static void main(String[] args) { SpringApplication.run(AttendanceApplication.class, args); } }这种排除自动配置的方式,是为了实现多数据源的灵活切换。系统支持同时连接考勤机数据库和业务数据库,通过自定义的DynamicDataSource类实现数据源路由:
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceType(); } }2.2 前端技术方案
Vue3组合式API的使用让前端代码更易维护。考勤统计模块使用了ECharts实现可视化展示,关键配置如下:
const initChart = () => { const chartDom = document.getElementById('attendance-chart'); const myChart = echarts.init(chartDom); const option = { tooltip: { trigger: 'axis' }, legend: { data: ['正常', '迟到', '早退', '缺勤'] }, xAxis: { type: 'category', data: dates }, yAxis: { type: 'value' }, series: [ { name: '正常', type: 'bar', data: normalData }, { name: '迟到', type: 'bar', data: lateData } ] }; myChart.setOption(option); };2.3 数据库设计
数据库设计遵循第三范式,主要表结构包括:
- 员工表(employee): 存储员工基本信息
- 考勤记录表(attendance): 记录每日考勤状态
- 请假表(leave_application): 管理请假审批流程
- 部门表(department): 组织结构管理
特别设计了考勤规则表(attendance_rule)来支持灵活的考勤策略:
CREATE TABLE `attendance_rule` ( `id` bigint NOT NULL AUTO_INCREMENT, `rule_name` varchar(50) NOT NULL COMMENT '规则名称', `work_start_time` time NOT NULL COMMENT '上班时间', `work_end_time` time NOT NULL COMMENT '下班时间', `late_minutes` int DEFAULT '30' COMMENT '迟到分钟数阈值', `early_minutes` int DEFAULT '30' COMMENT '早退分钟数阈值', `flexible_time` int DEFAULT '0' COMMENT '弹性时间(分钟)', `department_id` bigint DEFAULT NULL COMMENT '适用部门', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;3. 核心功能实现
3.1 考勤打卡流程
系统支持多种打卡方式:
- Web端打卡:适用于办公室人员
- 移动端GPS打卡:适用于外勤人员
- 考勤机对接:通过TCP协议与中控考勤机通信
打卡业务逻辑处理流程:
public AttendanceRecord handleCheckIn(CheckInDTO dto) { // 1. 验证员工状态 Employee employee = employeeService.getById(dto.getEmployeeId()); if (employee == null || employee.getStatus() != 1) { throw new BusinessException("员工不存在或已离职"); } // 2. 获取当日考勤规则 AttendanceRule rule = ruleService.getRuleByEmployee(employee.getId()); // 3. 计算考勤状态 AttendanceStatus status = calculateStatus(dto.getCheckTime(), rule); // 4. 保存记录 AttendanceRecord record = new AttendanceRecord(); record.setEmployeeId(employee.getId()); record.setCheckTime(dto.getCheckTime()); record.setStatus(status.getValue()); record.setLocation(dto.getLocation()); attendanceMapper.insert(record); return record; }3.2 请假审批工作流
使用Activiti引擎实现多级审批流程,关键配置:
<process id="leaveApproval" name="请假审批流程"> <startEvent id="startEvent"/> <userTask id="deptLeaderAudit" name="部门领导审批"/> <userTask id="hrAudit" name="人事审批" activiti:candidateGroups="hr"/> <exclusiveGateway id="exclusiveGw"/> <endEvent id="endEvent"/> <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="deptLeaderAudit"/> <sequenceFlow id="flow2" sourceRef="deptLeaderAudit" targetRef="hrAudit"/> <sequenceFlow id="flow3" sourceRef="hrAudit" targetRef="exclusiveGw"/> <sequenceFlow id="flow4" sourceRef="exclusiveGw" targetRef="endEvent"> <conditionExpression xsi:type="tFormalExpression"> <![CDATA[${approved}]]> </conditionExpression> </sequenceFlow> </process>3.3 考勤统计报表
使用JasperReport生成PDF报表,核心SQL:
SELECT e.employee_name, d.department_name, COUNT(CASE WHEN a.status = 1 THEN 1 END) AS normal_days, COUNT(CASE WHEN a.status = 2 THEN 1 END) AS late_days, COUNT(CASE WHEN a.status = 3 THEN 1 END) AS early_days, COUNT(CASE WHEN a.status = 4 THEN 1 END) AS absent_days FROM attendance a JOIN employee e ON a.employee_id = e.id JOIN department d ON e.department_id = d.id WHERE a.check_time BETWEEN :startDate AND :endDate GROUP BY e.employee_name, d.department_name4. 系统部署方案
4.1 开发环境配置
推荐使用以下开发环境:
- JDK 17
- IntelliJ IDEA 2023+
- Node.js 16+
- MySQL 8.0
- Redis 6.2
Maven依赖管理特别注意这些核心依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter</artifactId> <version>7.1.0.M6</version> </dependency>4.2 生产环境部署
采用Docker容器化部署方案,docker-compose.yml关键配置:
version: '3' services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} volumes: - ./mysql/data:/var/lib/mysql ports: - "3306:3306" redis: image: redis:6.2-alpine ports: - "6379:6379" app: build: . ports: - "8080:8080" depends_on: - mysql - redis environment: - SPRING_PROFILES_ACTIVE=prodNginx配置示例,实现前后端分离部署:
server { listen 80; server_name attendance.example.com; location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://app:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }5. 开发经验与优化建议
5.1 性能优化实践
- 考勤数据缓存策略:
@Cacheable(value = "attendance", key = "#employeeId+'-'+#date") public AttendanceRecord getDailyRecord(Long employeeId, LocalDate date) { return attendanceMapper.selectByEmployeeAndDate(employeeId, date); }- 批量导入优化:使用MyBatis的批量插入功能
@Transactional public void batchImport(List<AttendanceRecord> records) { SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); AttendanceMapper mapper = session.getMapper(AttendanceMapper.class); records.forEach(mapper::insert); session.commit(); session.close(); }5.2 安全防护措施
- JWT认证实现:
public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); claims.put("roles", userDetails.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.toList())); return Jwts.builder() .setClaims(claims) .setSubject(userDetails.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + expiration)) .signWith(SignatureAlgorithm.HS512, secret) .compact(); }- SQL注入防护:始终使用预编译语句
<select id="selectByCondition" resultType="AttendanceRecord"> SELECT * FROM attendance WHERE employee_id = #{employeeId} <if test="startDate != null"> AND check_time >= #{startDate} </if> <if test="endDate != null"> AND check_time <= #{endDate} </if> </select>5.3 常见问题排查
- 考勤机连接超时问题:
- 检查网络连通性:telnet 考勤机IP 端口
- 确认考勤机协议版本
- 验证心跳包发送间隔(建议30秒)
- 移动端定位不准解决方案:
- 增加GPS信号强度检测
- 使用高德/百度地图API进行坐标纠偏
- 设置合理的位置误差范围(建议300米内)
- 并发打卡冲突处理:
@Transactional(isolation = Isolation.SERIALIZABLE) public AttendanceRecord handleConcurrentCheckIn(CheckInDTO dto) { // 检查是否已打卡 AttendanceRecord existing = getDailyRecord(dto.getEmployeeId(), LocalDate.now()); if (existing != null) { throw new BusinessException("今日已打卡"); } return handleCheckIn(dto); }6. 论文文档要点
论文文档包含以下核心章节:
- 系统需求分析
- 技术方案选型
- 系统架构设计
- 数据库设计
- 核心功能实现
- 系统测试方案
- 部署与维护指南
特别在系统测试章节,提供了详细的测试用例:
- 考勤打卡功能测试用例表
- 请假审批流程测试场景
- 性能压力测试结果(支持500并发用户)
- 安全测试报告(包括OWASP Top 10防护措施)
论文最后附有完整的系统界面截图,展示主要功能页面:
- 登录界面
- 员工考勤看板
- 请假申请表单
- 考勤统计报表
- 系统管理后台