毕业设计实战:基于Spring Boot的公司日常考勤系统全栈开发

97 阅读11分钟

一、项目背景:企业考勤管理数字化的必然趋势

在现代企业管理中,员工考勤管理面临着效率低下、准确性差、流程繁琐三大核心痛点。据企业人力资源管理统计显示,2023年仍有超过40%的中小企业采用传统纸质打卡或简单Excel记录方式,导致考勤数据统计困难、人工核算易出错、请假审批流程冗长,而员工也缺乏便捷的考勤查询与申请渠道。

为破解这一困境,基于Spring Boot的公司日常考勤系统应运而生。系统以"考勤自动化、流程规范化、数据透明化"为核心目标,采用B/S架构构建一体化考勤管理平台,整合员工信息管理、考勤记录、请假审批、数据统计等核心功能,建立"管理员统筹-员工自助"的双层应用模式,推动企业考勤管理从"传统手工式"向"数字化、系统化、智能化"转型。

二、技术架构:考勤管理系统的全栈技术选型

项目围绕"稳定性优先、易维护、高适配"三大原则,选用成熟且贴合企业管理需求的技术栈:

技术模块具体工具/技术核心作用
后端框架Spring Boot 2.x快速构建高效API接口,处理考勤管理核心业务逻辑
前端技术JSP + 前端组件构建专业直观的管理界面,适配管理员与员工操作
数据库MySQL 8.0安全存储员工信息、考勤记录、请假数据等
服务器Tomcat轻量级服务器,支持JSP页面运行
开发语言Java面向对象编程,保证系统稳定性和安全性
架构模式B/S模式无需安装客户端,支持多设备访问

三、项目全流程:6步完成考勤管理系统开发

3.1 第一步:需求分析——明确系统核心价值

针对传统考勤管理的"效率低、误差大"痛点,系统聚焦"考勤自动化、审批流程化、数据可视化",明确双角色的核心需求:

3.1.1 功能性需求

  1. 双角色权限体系

    • 管理员:员工信息管理、部门管理、考勤规则设置、请假审批、考勤统计、系统公告管理;
    • 员工:个人信息查看、考勤记录查询、请假申请、密码修改、公告查看。
  2. 核心业务功能

    • 员工全生命周期管理:从入职信息录入、考勤规则分配到离职管理;
    • 考勤数据管理:上下班打卡记录、加班记录、考勤异常处理;
    • 请假审批流程:请假申请→主管审批→状态反馈→记录归档;
    • 数据统计分析:月度考勤汇总、部门考勤对比、异常考勤分析;
    • 系统管理:部门管理、权限设置、公告发布。

3.1.2 非功能性需求

  • 系统性能:支持200+员工并发操作,考勤打卡响应时间<1秒;
  • 数据安全:员工密码加密存储,考勤数据备份机制;
  • 用户体验:界面简洁直观,考勤打卡操作一步完成;
  • 可靠性:7×24小时稳定运行,考勤数据零丢失。

3.2 第二步:系统设计——构建整体架构

系统采用分层设计思想,确保各模块职责清晰、可维护性强:

3.2.1 系统总体架构

  1. 前端架构

    • 基于JSP实现页面动态渲染,结合CSS组件构建专业界面;
    • 采用Ajax实现异步数据交互,提升用户体验;
    • 响应式设计,支持PC端和移动端访问。
  2. 后端架构

    • 基于Spring Boot实现分层架构:Controller、Service、Mapper;
    • 统一异常处理机制:提供友好的错误提示信息;
    • 权限控制:通过拦截器验证用户登录状态和权限。
  3. 数据持久层

    • 采用MyBatis操作数据库,SQL与代码分离;
    • 数据库连接池优化,提高系统性能。

3.2.2 核心数据库设计

系统设计5张核心数据表,覆盖考勤管理全业务场景:

表名核心字段作用
管理员表(admin)userid、username、userpw存储管理员登录信息
员工表(employee)id、bianhao、name、loginname、loginpw、del存储员工基本信息
考勤信息表(attendance)id、yuefen、tianshu、yuangong_id记录考勤数据
请假申请表(leave_application)id、kaishishijian、jieshushijian、yuangong_id、shenhezhuangtai管理请假流程
公告信息表(notice)id、title、content、shijian存储系统公告

3.3 第三步:后端核心功能实现——Spring Boot架构

基于Spring Boot框架实现系统核心业务逻辑,重点突破"考勤管理""请假审批""数据统计"三大核心场景:

3.3.1 考勤管理功能实现

@RestController
@RequestMapping("/api/attendance")
public class AttendanceController {
    
    @Autowired
    private AttendanceService attendanceService;
    
    /**
     * 员工打卡
     */
    @PostMapping("/clock")
    public ResponseEntity<?> clockIn(@RequestParam String employeeId,
                                    @RequestParam String type,
                                    @RequestParam String timestamp) {
        try {
            // 参数校验
            if (StringUtils.isEmpty(employeeId) || StringUtils.isEmpty(type)) {
                return ResponseEntity.badRequest().body("员工ID和打卡类型不能为空");
            }
            
            // 执行打卡
            AttendanceRecord record = attendanceService.clockIn(employeeId, type, timestamp);
            return ResponseEntity.ok("打卡成功");
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("打卡失败:" + e.getMessage());
        }
    }
    
    /**
     * 查询员工考勤记录
     */
    @GetMapping("/records")
    public ResponseEntity<?> getAttendanceRecords(@RequestParam String employeeId,
                                                @RequestParam String month) {
        try {
            List<AttendanceRecordVO> records = attendanceService.getAttendanceRecords(employeeId, month);
            return ResponseEntity.ok(records);
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("查询考勤记录失败:" + e.getMessage());
        }
    }
}

@Service
@Transactional
public class AttendanceServiceImpl implements AttendanceService {
    
    @Autowired
    private AttendanceMapper attendanceMapper;
    
    @Autowired
    private EmployeeMapper employeeMapper;
    
    @Override
    public AttendanceRecord clockIn(String employeeId, String type, String timestamp) {
        // 1. 验证员工是否存在
        Employee employee = employeeMapper.selectByPrimaryKey(employeeId);
        if (employee == null) {
            throw new RuntimeException("员工不存在");
        }
        
        // 2. 检查是否已打卡
        AttendanceRecordExample example = new AttendanceRecordExample();
        example.createCriteria()
            .andEmployeeIdEqualTo(employeeId)
            .andRecordDateEqualTo(getCurrentDate())
            .andRecordTypeEqualTo(type);
        if (attendanceMapper.countByExample(example) > 0) {
            throw new RuntimeException("今日已打卡");
        }
        
        // 3. 创建考勤记录
        AttendanceRecord record = new AttendanceRecord();
        record.setEmployeeId(employeeId);
        record.setRecordType(type);
        record.setRecordTime(timestamp);
        record.setRecordDate(getCurrentDate());
        record.setStatus("正常");
        record.setCreateTime(new Date());
        
        // 4. 保存考勤记录
        attendanceMapper.insert(record);
        return record;
    }
    
    @Override
    public List<AttendanceRecordVO> getAttendanceRecords(String employeeId, String month) {
        // 查询指定月份的考勤记录
        AttendanceRecordExample example = new AttendanceRecordExample();
        example.createCriteria()
            .andEmployeeIdEqualTo(employeeId)
            .andRecordDateLike(month + "%");
        example.setOrderByClause("record_date DESC, record_time DESC");
        
        List<AttendanceRecord> records = attendanceMapper.selectByExample(example);
        
        // 转换为VO对象
        return records.stream()
            .map(record -> {
                AttendanceRecordVO vo = new AttendanceRecordVO();
                BeanUtils.copyProperties(record, vo);
                return vo;
            }).collect(Collectors.toList());
    }
    
    private String getCurrentDate() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(new Date());
    }
}

3.3.2 请假审批功能实现

@RestController
@RequestMapping("/api/leave")
public class LeaveController {
    
    @Autowired
    private LeaveService leaveService;
    
    /**
     * 员工提交请假申请
     */
    @PostMapping("/apply")
    public ResponseEntity<?> applyLeave(@RequestBody LeaveApplicationDTO leaveDTO,
                                       @RequestHeader("employeeId") String employeeId) {
        try {
            // 参数校验
            if (StringUtils.isEmpty(leaveDTO.getKaishishijian()) || 
                StringUtils.isEmpty(leaveDTO.getJieshushijian()) ||
                StringUtils.isEmpty(leaveDTO.getBeizhu())) {
                return ResponseEntity.badRequest().body("开始时间、结束时间、备注不能为空");
            }
            
            // 提交请假申请
            LeaveApplication application = leaveService.applyLeave(leaveDTO, employeeId);
            return ResponseEntity.ok("请假申请提交成功,等待审批");
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("请假申请提交失败:" + e.getMessage());
        }
    }
    
    /**
     * 管理员审批请假
     */
    @PostMapping("/approve")
    public ResponseEntity<?> approveLeave(@RequestBody LeaveApproveDTO approveDTO,
                                         @RequestHeader("adminId") String adminId) {
        try {
            // 参数校验
            if (approveDTO.getId() == null || StringUtils.isEmpty(approveDTO.getShenhezhuangtai())) {
                return ResponseEntity.badRequest().body("申请ID和审批状态不能为空");
            }
            
            // 审批请假
            LeaveApplication application = leaveService.approveLeave(approveDTO);
            return ResponseEntity.ok("请假审批完成");
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("请假审批失败:" + e.getMessage());
        }
    }
}

@Service
@Transactional
public class LeaveServiceImpl implements LeaveService {
    
    @Autowired
    private LeaveApplicationMapper leaveMapper;
    
    @Override
    public LeaveApplication applyLeave(LeaveApplicationDTO dto, String employeeId) {
        // 1. 构建请假申请实体
        LeaveApplication application = new LeaveApplication();
        application.setEmployeeId(employeeId);
        application.setKaishishijian(dto.getKaishishijian());
        application.setJieshushijian(dto.getJieshushijian());
        application.setBeizhu(dto.getBeizhu());
        application.setShenhezhuangtai("待审批");
        application.setCreateTime(new Date());
        
        // 2. 计算请假天数
        int leaveDays = calculateLeaveDays(dto.getKaishishijian(), dto.getJieshushijian());
        application.setLeaveDays(leaveDays);
        
        // 3. 保存请假申请
        leaveMapper.insert(application);
        return application;
    }
    
    @Override
    public LeaveApplication approveLeave(LeaveApproveDTO dto) {
        // 1. 查询请假申请
        LeaveApplication application = leaveMapper.selectByPrimaryKey(dto.getId());
        if (application == null) {
            throw new RuntimeException("请假申请不存在");
        }
        
        // 2. 更新审批状态
        application.setShenhezhuangtai(dto.getShenhezhuangtai());
        application.setHuifuxinxi(dto.getHuifuxinxi());
        application.setApproveTime(new Date());
        
        // 3. 更新数据库
        leaveMapper.updateByPrimaryKey(application);
        return application;
    }
    
    /**
     * 计算请假天数
     */
    private int calculateLeaveDays(String startTime, String endTime) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date start = sdf.parse(startTime);
            Date end = sdf.parse(endTime);
            
            long diff = end.getTime() - start.getTime();
            return (int) (diff / (1000 * 60 * 60 * 24)) + 1;
        } catch (Exception e) {
            throw new RuntimeException("日期格式错误");
        }
    }
}

3.3.3 数据统计功能实现

@RestController
@RequestMapping("/api/statistics")
public class StatisticsController {
    
    @Autowired
    private StatisticsService statisticsService;
    
    /**
     * 获取部门考勤统计
     */
    @GetMapping("/department")
    public ResponseEntity<?> getDepartmentStatistics(@RequestParam String department,
                                                   @RequestParam String month) {
        try {
            DepartmentStatsVO stats = statisticsService.getDepartmentStatistics(department, month);
            return ResponseEntity.ok(stats);
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("获取部门统计失败:" + e.getMessage());
        }
    }
    
    /**
     * 获取员工考勤明细
     */
    @GetMapping("/employee/detail")
    public ResponseEntity<?> getEmployeeAttendanceDetail(@RequestParam String employeeId,
                                                       @RequestParam String month) {
        try {
            EmployeeAttendanceDetailVO detail = statisticsService.getEmployeeAttendanceDetail(employeeId, month);
            return ResponseEntity.ok(detail);
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("获取员工考勤明细失败:" + e.getMessage());
        }
    }
}

3.4 第四步:前端界面实现——企业管理风格平台

基于JSP构建前端界面,贴合企业管理系统的"专业、简洁、高效"需求:

3.4.1 核心界面设计

  1. 登录界面:支持管理员/员工双角色登录,输入账号密码验证权限;
  2. 管理员后台
    • 首页:展示系统核心数据(员工总数、今日打卡数、待审批申请数);
    • 员工管理:员工信息录入、查询、修改、删除;
    • 考勤管理:考勤记录查看、异常处理、数据统计;
    • 请假审批:待审批列表、审批操作、历史记录;
    • 部门管理:部门信息维护;
    • 公告管理:系统公告发布;
  3. 员工界面
    • 个人中心:个人信息查看、密码修改;
    • 考勤打卡:上下班打卡、打卡记录查询;
    • 请假申请:请假申请提交、申请状态查询;
    • 公告查看:系统公告浏览。 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

3.4.2 设计亮点

  • 专业界面设计:采用蓝色系配色,符合企业管理软件专业形象;
  • 操作流程优化:考勤打卡一键完成,请假申请三步提交;
  • 数据可视化:考勤数据图表展示,便于管理者快速了解情况;
  • 权限控制严格:管理员与员工功能完全分离,确保数据安全。

3.5 第五步:系统测试——确保系统稳定性

通过多维度测试验证系统功能完整性和稳定性:

3.5.1 功能测试

测试场景测试用例预期结果实际结果
员工打卡员工执行上下班打卡打卡成功,记录保存功能正常
请假审批员工提交请假,管理员审批流程完整,状态更新正确流程正确
考勤统计查询部门月度考勤统计数据准确显示统计准确
权限验证员工尝试访问管理功能提示无权限访问拦截成功

3.5.2 性能测试

  • 并发测试:模拟100名员工同时打卡,响应时间<1秒;
  • 数据加载测试:加载1000条考勤记录,分页显示流畅;
  • 稳定性测试:连续运行72小时,无内存泄漏和系统崩溃。

3.5.3 安全性测试

测试项测试方法预期结果实际结果
SQL注入输入SQL特殊字符系统过滤,无异常防护有效
越权访问员工尝试审批请假返回权限不足拦截成功
数据加密查看数据库密码字段密文存储加密有效

3.6 第六步:问题排查与优化——提升系统体验

开发过程中遇到的核心问题及解决方案:

  1. 问题:考勤数据统计不准确
    解决方案:优化统计算法,增加数据校验机制,确保统计结果准确;

  2. 问题:并发打卡数据冲突
    解决方案:使用数据库事务和乐观锁机制,避免数据重复插入;

  3. 问题:请假天数计算错误
    解决方案:完善日期计算逻辑,考虑节假日和工作日区分;

  4. 问题:系统响应速度慢
    解决方案:对常用查询建立数据库索引,优化SQL语句性能。

四、毕业设计复盘:考勤管理系统开发实践总结

4.1 开发过程中的技术挑战

  1. 考勤业务逻辑复杂性:需要处理正常考勤、迟到、早退、加班等多种情况;
  2. 数据一致性要求:考勤统计、薪资计算等对数据准确性要求极高;
  3. 并发处理能力:上下班高峰期大量员工同时打卡,需要保证系统稳定性;
  4. 权限管理精细度:不同角色对数据的查看和操作权限需要精细控制。

4.2 给后续开发者的建议

  1. 业务理解深入:充分理解企业考勤管理的实际业务流程;
  2. 数据模型设计:合理设计数据库表结构,考虑查询效率和扩展性;
  3. 异常处理完善:对各类异常情况要有充分的处理和提示;
  4. 用户体验优化:从用户角度出发,简化操作流程;
  5. 测试全面性:重点测试核心业务功能,确保系统稳定可靠。

五、项目资源与发展展望

5.1 项目核心资源

本项目提供完整的开发与部署资料:

  • 后端源码:完整的Spring Boot项目,包含所有业务逻辑;
  • 前端页面:JSP页面文件及静态资源;
  • 数据库脚本:MySQL建表语句和测试数据;
  • 部署文档:详细的环境配置和部署步骤;
  • 使用手册:管理员和员工的操作指南。

5.2 系统扩展方向

  1. 移动端应用:开发APP或小程序,支持移动打卡;
  2. 生物识别:集成人脸识别或指纹打卡功能;
  3. 智能排班:增加智能排班系统,自动生成排班计划;
  4. 薪资集成:与薪资系统对接,自动计算考勤相关薪资;
  5. 数据分析:集成BI工具,提供深度考勤数据分析。

如果本文对您的Spring Boot学习、考勤管理系统毕业设计有帮助,欢迎点赞 + 收藏 + 关注,后续会分享更多企业管理系统实战案例!