首页 / 资讯中心 / 文章详情

SpringBoot人事管理系统毕业设计实战:技术选型、核心模块与避坑指南

SpringBoot人事管理系统毕业设计实战:技术选型、核心模块与避坑指南 ★ FEATURED ARTICLE
简介这份资源是面向计算机与软件工程专业学生的毕业设计完整方案围绕基于SpringBoot的人事管理系统展开适合需要完成毕设或学习企业级Java开发的学习者。系统采用前后端分离架构后端以Spring Boot为核心结合Spring Security实现认证与权限控制MyBatis完成持久层操作MySQL负责数据存储整体遵循B/S模式与MVVM设计。功能覆盖员工信息管理、考勤打卡与请假审批、薪资自动计算与个税核算、基于角色的权限控制以及员工分布、考勤趋势、薪资结构等图表化统计分析并运用单例、工厂、观察者等设计模式优化代码结构。资源包共305个文件包含126个Java源码、32个JavaScript脚本、27个XML配置、28张PNG图片及SQL脚本、说明文档、论文文档等压缩包约90.97MB源码与论文齐备涵盖需求分析、系统设计、技术实现与测试部署全过程。目前已有60人学习下载后可直接部署运行便于二次开发与定制是掌握SpringBoot企业级应用开发的实用参考。1. 从一份能跑起来的 SpringBoot 人事管理系统说起为什么它成了毕业设计里最稳的选题每年到了做课程设计或毕业设计的节点总有一批人卡在选题上。想做点有业务厚度的怕技术栈太杂收不了尾想做纯增删改查又担心答辩时被问“这跟练手项目有什么区别”。SpringBoot 人事管理系统恰好卡在一个很舒服的位置业务边界清晰模块划分天然技术栈主流且资料密集源码和论文两条线都能落地。它解决的核心问题就一个——把员工从入职到离职的全生命周期数据管起来同时让考勤、薪资、请假、部门调动这些高频动作有据可查。适合谁适合已经学过 Java Web、能看懂 Controller-Service-Mapper 三层结构、但还没独立搭过一个完整业务系统的人。也适合那些想拿一份结构规整的源码当底子再往上叠自己业务理解的人。这个方向不新但每年都有人靠它把答辩稳稳过掉原因后面几章会拆开讲。2. 技术选型与项目骨架为什么这套组合能扛住答辩追问2.1 为什么是 SpringBoot 而不是传统 SSM传统 SSM 要配 web.xml、applicationContext.xml、spring-mvc.xml光配置文件就能劝退一半人。SpringBoot 的核心价值在于自动配置和起步依赖把原来散落在多个 XML 里的东西收进一个 application.yml再通过 starter 把常用依赖打包。对人事管理系统这种模块数量中等、业务逻辑不算复杂的项目来说SpringBoot 能让开发者把精力放在业务代码上而不是环境搭建上。选版本时有个血泪经验别追最新。热搜里“springboot版本太高”是真实痛点高版本对 JDK 要求苛刻部分旧教程里的依赖坐标也对不上。我一般会选 2.7.x 这条线JDK 用 8 或 11MyBatis-Plus 用 3.5.xMySQL 驱动用 8.0.x。这套组合在各类教程、问答和源码里出现频率最高遇到问题一搜就有答案。如果学校要求 JDK 17那就上 SpringBoot 3.x但要注意 javax 包名全部换成了 jakarta老代码直接拷会编译不过。2.2 项目结构怎么分层才不乱一个能讲清楚的人事管理系统包结构应该让人一眼看懂职责边界。常见做法是按功能模块 分层混合切分com.hrms ├── common // 通用返回体、异常、工具类 ├── config // 拦截器、跨域、MyBatis-Plus 配置 ├── controller // 接口入口 ├── service │ └── impl ├── mapper // 数据访问 ├── entity // 数据库映射对象 ├── dto // 入参对象 └── vo // 出参对象entity 和数据库表一一对应dto 接前端传进来的参数vo 负责返回给前端展示。很多新手直接把 entity 当入参出参用结果一改表结构前端就崩答辩时被问“为什么员工密码会返回给前端”就尴尬了。分层不是形式主义是为了让每层只关心自己的事。2.3 依赖清单与最小可运行配置下面这份 pom 依赖是经过多个项目验证的最小集合能覆盖人事管理系统 90% 的功能dependencies !-- Web 层提供 REST 接口和内嵌 Tomcat -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 数据访问MyBatis-Plus 比原生 MyBatis 少写大量 XML -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency !-- MySQL 驱动 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- 简化实体类代码 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- 参数校验 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency /dependencies逻辑说明web starter 负责接口和容器mybatis-plus starter 负责数据访问并自带分页插件validation 负责 NotNull、Size 这类注解校验。参数上MyBatis-Plus 版本要和 SpringBoot 版本匹配2.7.x 的 SpringBoot 配 3.5.x 的 MyBatis-Plus 是稳的。如果启动报 “Invalid bound statement”八成是 mapper 扫描路径没配在启动类上加 MapperScan(“com.hrms.mapper”) 即可。2.4 数据库表设计五张核心表撑起整个系统人事管理系统的表不用多但关系要清楚。核心五张表员工表 employee、部门表 department、考勤表 attendance、请假表 leave_record、薪资表 salary。员工表通过 department_id 关联部门考勤和请假通过 employee_id 关联员工薪资通过 employee_id 和月份定位。CREATE TABLE employee ( id BIGINT PRIMARY KEY AUTO_INCREMENT, emp_no VARCHAR(20) NOT NULL UNIQUE COMMENT 工号, name VARCHAR(50) NOT NULL, gender TINYINT DEFAULT 1 COMMENT 1男 2女, phone VARCHAR(20), department_id BIGINT, hire_date DATE, status TINYINT DEFAULT 1 COMMENT 1在职 0离职, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT 员工表;工号加唯一索引是必须的否则重复录入查起来很麻烦。status 字段用逻辑删除而不是物理删除离职员工的历史考勤和薪资记录还要保留这是人事系统的业务底线。department_id 上建普通索引按部门筛选员工是高频操作。3. 核心模块落地从登录鉴权到考勤统计的完整实现3.1 登录鉴权JWT 拦截器的最小闭环人事系统的入口是登录登录之后所有接口都要带身份。常见做法是 JWT 拦截器不引入 Spring Security 那套重家伙对毕业设计来说够用且好讲。// JwtUtil.java public class JwtUtil { private static final String SECRET hrms-secret-key-2024; private static final long EXPIRE 24 * 60 * 60 * 1000L; // 生成 token把用户 id 和角色塞进 payload public static String createToken(Long userId, String role) { return Jwts.builder() .claim(userId, userId) .claim(role, role) .setExpiration(new Date(System.currentTimeMillis() EXPIRE)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } // 解析 token失败会抛异常由拦截器统一处理 public static Claims parse(String token) { return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); } }逻辑说明createToken 把 userId 和 role 写进 payload前端每次请求放在 Header 的 Authorization 里。parse 方法在拦截器里调用解析失败直接返回 401。参数上SECRET 不要硬编码在代码里放到 application.yml 用 Value 注入答辩时被问“密钥泄露怎么办”能答上来。过期时间设 24 小时是折中太长不安全太短前端要频繁重登。拦截器注册时要注意放行登录接口和静态资源registry.addInterceptor(new AuthInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /css/**, /js/**);3.2 员工管理分页查询与条件筛选员工列表是使用频率最高的页面分页和条件筛选必须做对。MyBatis-Plus 的分页插件能省掉手写 limit 的麻烦。// EmployeeServiceImpl.java public PageEmployeeVO pageQuery(EmployeeQuery query) { PageEmployee page new Page(query.getPageNum(), query.getPageSize()); LambdaQueryWrapperEmployee wrapper new LambdaQueryWrapper(); // 姓名模糊查工号精确查部门精确查 wrapper.like(StringUtils.hasText(query.getName()), Employee::getName, query.getName()) .eq(StringUtils.hasText(query.getEmpNo()), Employee::getEmpNo, query.getEmpNo()) .eq(query.getDepartmentId() ! null, Employee::getDepartmentId, query.getDepartmentId()) .eq(Employee::getStatus, 1) .orderByDesc(Employee::getCreateTime); return employeeMapper.selectPage(page, wrapper); }逻辑说明LambdaQueryWrapper 的条件方法第一个参数是布尔值只有为 true 时才拼接该条件这样一条链就能处理“有就查、没有就忽略”的动态查询。参数上pageNum 从 1 开始pageSize 默认 10前端传之前要做边界校验防止有人传 pageSize100000 把数据库拖垮。orderByDesc 按创建时间倒序新入职的员工排前面符合使用习惯。分页插件需要在配置类里注册否则 selectPage 不生效返回的是全量数据Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }3.3 考勤统计按月汇总的 SQL 与接口设计考勤模块的难点不在录入在统计。每个月要算出每个员工的出勤天数、迟到次数、请假天数。用一条 SQL 在数据库层聚合比在 Java 里循环效率高得多。SELECT e.id, e.name, COUNT(CASE WHEN a.status 1 THEN 1 END) AS normal_days, COUNT(CASE WHEN a.status 2 THEN 1 END) AS late_times, COUNT(CASE WHEN a.status 3 THEN 1 END) AS absent_days FROM employee e LEFT JOIN attendance a ON e.id a.employee_id AND DATE_FORMAT(a.attend_date, %Y-%m) #{month} WHERE e.status 1 GROUP BY e.id, e.name;逻辑说明LEFT JOIN 保证没有考勤记录的员工也出现在结果里否则新员工会被漏掉。DATE_FORMAT 把日期转成年月做匹配条件写在 ON 里而不是 WHERE 里这是 LEFT JOIN 的关键区别——写在 WHERE 里会让左连接退化成内连接。参数 month 格式是 “2024-06”前端传之前用正则校验一下防止 SQL 注入。接口层把结果封装成 VO 返回前端用 ECharts 画柱状图。如果数据量大可以在 attendance 表的 attend_date 字段上建索引按月查询走索引扫描。3.4 请假审批状态机与并发处理请假流程是“员工提交 → 主管审批 → 状态更新”本质是一个状态机。状态值0 待审批、1 已通过、2 已驳回。审批接口要做两件事校验当前状态是否允许审批、更新状态并记录审批人和时间。Transactional public void approve(Long leaveId, Integer status, Long approverId) { LeaveRecord record leaveMapper.selectById(leaveId); // 只有待审批状态才能被审批防止重复操作 if (record null || record.getStatus() ! 0) { throw new BizException(该申请已处理或不存在); } record.setStatus(status); record.setApproverId(approverId); record.setApproveTime(LocalDateTime.now()); // 乐观锁带上原状态作为条件更新行数为 0 说明被并发修改 int rows leaveMapper.update(record, new LambdaUpdateWrapperLeaveRecord() .eq(LeaveRecord::getId, leaveId) .eq(LeaveRecord::getStatus, 0)); if (rows 0) { throw new BizException(操作冲突请刷新后重试); } }逻辑说明Transactional 保证状态更新和后续操作在一个事务里。乐观锁通过 update 时带 status0 条件实现两个主管同时点审批只有一个能成功另一个收到冲突提示。参数上approverId 从 JWT 里取不要从前端传否则可以伪造审批人。这个并发场景在答辩时是加分项能讲清楚为什么需要乐观锁而不是 synchronized。4. 避坑与排查那些让项目跑不起来的常见问题4.1 启动报 “Failed to configure a DataSource”现象项目一启动就报错提示找不到数据源。原因通常是 application.yml 里数据库配置没写全或者用了多环境配置但没激活对应 profile。解决检查 spring.datasource.url、username、password、driver-class-name 四项是否齐全url 里要带时区和编码参数比如jdbc:mysql://localhost:3306/hrms?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai。如果用了 application-dev.yml确认 spring.profiles.activedev 已配置。4.2 前端请求跨域被浏览器拦截现象Postman 能调通浏览器里报 CORS 错误。原因前后端分离部署时端口不同浏览器同源策略拦截。解决加一个全局跨域配置实现 WebMvcConfigurer 的 addCorsMappings 方法允许指定来源、方法和头。注意 allowCredentials 为 true 时allowedOrigins 不能用 “*”要写具体域名否则浏览器会拒绝。4.3 MyBatis-Plus 分页返回全部数据现象调分页接口返回的 total 是对的但 records 里是全部数据。原因分页插件没注册或者注册了但版本和 MyBatis-Plus 不匹配。解决确认配置类里有 MybatisPlusInterceptor 的 Bean并且加了 PaginationInnerInterceptor。如果用的是老版本分页插件类名是 PaginationInterceptor别搞混。4.4 时间字段前后端差 8 小时现象数据库存的是正确时间前端显示少了 8 小时。原因Jackson 序列化时区没配默认用 UTC。解决在 application.yml 里加spring.jackson.time-zoneGMT8和spring.jackson.date-formatyyyy-MM-dd HH:mm:ss。如果用的是 LocalDateTime还要确认 MySQL 连接串里的 serverTimezone 设对了。4.5 打包成 jar 后访问 404现象IDEA 里跑得好好的mvn package 之后 java -jar 启动接口全 404。原因静态资源没被打进 jar或者 Controller 没被扫描到。解决确认静态文件放在 src/main/resources/static 下启动类在根包下且加了 SpringBootApplication。如果启动类在子包里要手动指定 ComponentScan 的 basePackages。5. 论文与源码的配合怎么让答辩老师觉得你真正做了这个系统5.1 论文框架怎么搭才不空论文最怕写成产品说明书。常见做法是按“绪论 → 需求分析 → 系统设计 → 系统实现 → 系统测试 → 总结”六章走但每章要有自己的分析痕迹。需求分析里画用例图把员工、主管、管理员三个角色的操作边界画清楚系统设计里画 E-R 图和架构图说明为什么选三层架构而不是微服务实现章节贴关键代码但代码前后要有设计思路的说明不能只贴代码。测试章节列一张测试用例表把正常流程和异常流程都覆盖到比如“输入不存在的工号查询”应该返回什么。5.2 源码怎么整理才经得起追问源码不是能跑就行。答辩老师常问的几个点为什么用逻辑删除、JWT 过期怎么处理、分页插件原理是什么、并发审批怎么保证。整理源码时把每个模块的核心方法加上注释注释写“为什么这么做”而不是“这行做了什么”。比如乐观锁那段注释写“防止两个主管同时审批同一条请假记录”比写“更新请假状态”有价值得多。另外把 application.yml 里的敏感信息抽成占位符展示时用假值这是基本的安全意识。5.3 一个能加分的验证技巧用接口文档反向检查功能覆盖写完接口后用 Knife4j 或 Swagger 生成接口文档然后对着需求分析里的用例逐条核对。比如“员工离职后不能再登录”就去测登录接口传离职员工账号是否返回错误“请假天数不能超过年假余额”就去测边界值。这个动作能发现很多“代码写了但逻辑没闭环”的问题。我一般会在答辩前把接口文档打印出来每个接口旁边手写测试结果老师看到这个会认为你是真的跑过而不是抄的。5.4 我踩过的坑与留下的习惯第一次做这个系统时我把所有业务逻辑写在 Controller 里Service 层只是透传结果改一个校验规则要翻五个文件。后来养成习惯Controller 只做参数接收和结果返回Service 写业务规则Mapper 只做数据访问。另一个坑是数据库字段用驼峰命名MySQL 在 Linux 下默认大小写敏感本地 Windows 跑得好好的部署到服务器就报表不存在。现在建表一律用下划线命名实体类用驼峰靠 MyBatis-Plus 的 map-underscore-to-camel-case 自动映射。这些习惯不写在论文里但能让代码在答辩现场少出洋相。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站