Spring Boot人力资源管理系统毕业设计拆解:数据库权限与薪资核心 做Java毕业设计每年都有大量同学选人力资源管理系统这个题目原因很现实业务场景清晰、技术栈成熟从Spring Boot后端到Vue前端再到MySQL存储一整套下来能把大学四年学的东西串起来。但正因为做的人多评委老师一眼就能看出你是认真做了还是糊弄的。今天我把这个选题从需求分析、数据库设计、核心实现到上线部署完整拆一遍。这篇文章不是代码逐行注释而是告诉你每一步为什么这么选、有哪些坑以及怎么在答辩现场把项目讲出亮点。写这套系统的目标很明确面向中小企业的HR日常管理把员工信息、考勤、请假、薪资、招聘这些核心业务线上化。技术组合选的是Spring Boot MyBatis Plus Spring Security JWT MySQL Redis前端用Vue 2 Element UI。整个系统前后端分离符合当前企业主流开发模式。如果你正在准备毕设或者是想通过项目实战入行Java的同学这篇文章可以帮你在动手前把整体思路理清楚。1. 项目整体设计与思路拆解1.1 技术选型为什么是Spring Boot这套组合每年都有人纠结技术栈我见过用SSHStrutsSpringHibernate的老项目也见过硬上Spring Cloud微服务的。先说结论毕业设计最稳妥的选型就是Spring Boot单体应用。Spring Boot从2016年发布到现在已经把约定优于配置贯彻到了极致。内嵌Tomcat不用再打war包部署starter依赖一键引入自动配置帮你处理了大部分繁琐的bean装配。这意味着你把精力放在写业务代码上而不是和XML配置文件搏斗。企业里Java后端岗位明确要求Spring Boot的占了大多数用这套做毕设简历上也有说服力。那为什么不建议用Spring Cloud微服务人力资源管理系统本身的业务量级单体架构完全扛得住。一旦拆成微服务服务发现、配置中心、分布式事务、链路追踪这些概念你答辩的时候解释不清楚就成了减分项。评委问一句你这个服务拆分之后数据一致性怎么保证答不上来还不如不做。架构选型的核心原则是能简则简不要为了炫技引入自己hold不住的东西。完整的技术栈清单如下技术用途选型原因Spring Boot 2.7后端主框架生态成熟企业主流MyBatis PlusORM框架单表CRUD零SQL节省编码时间Spring Security JWT认证与授权基于角色的RBAC权限模型MySQL 8.0持久化存储开源免费教程丰富Redis缓存与验证码提升性能支持token注销Vue 2 Element UI前端框架上手快后台管理界面组件齐全EasyExcelExcel导入导出阿里开源低内存API简单这套组合兼顾了实用性和学习价值每个团队成员也都能快速上手。1.2 功能模块划分先跑通主链路再补全支线毕设失败的常见原因是一开始就把系统想得很大画了十几个模块的气泡图建了三十多张表最后做不完。人力资源管理系统听起来是个大概念但落到实际操作层面核心链路就一条员工入职 - 录入员工信息 - 分配部门岗位 - 日常考勤打卡 - 请假加班审批 - 月末薪资核算 - 汇总统计顺着这条链路我最终把系统切成了六个模块系统管理登录认证、用户角色、菜单权限。没有权限控制的后台管理系统就是裸奔这是地基模块。员工管理员工信息增删改查、离职调岗、合同管理、Excel批量导入导出。部门管理部门树形结构、岗位信息、员工与部门的关联关系。考勤管理模拟打卡、补卡审核、请假申请与审批、加班申请、月度考勤汇总。薪资管理薪资结构配置、工资条自动生成、历史薪资记录、Excel导出。招聘管理招聘职位发布、简历投递记录、面试流程管理。这样的划分不是拍脑袋它跟着HR真实的工作节奏走。员工入职是起点后续每一天的考勤数据积累月末汇聚到薪资模块完成核算。答辩的时候你把这个主链路讲清楚评委就知道你是理解业务的而不是只写了一堆管理系统的CRUD。每个模块内部的子功能也分优先级。员工管理、考勤、薪资是必须完整做出来的招聘管理做简化版即可比如职位发布和简历列表不做复杂的在线笔试面试流程。时间不够的时候砍功能不要砍链路宁可只做四个完整模块也不要十二个模块个个都是半成品。1.3 权限模型RBAC不只是在表里加个role字段很多人一提权限就是在用户表加一个role字段角色名写死成admin和user然后在代码里if(admin.equals())写一堆判断。这样不是不能跑但换一个角色就要改代码而且越到后期越难维护。我在这套系统里用的是标准的RBACRole-Based Access Control模型分为三层用户、角色、权限。为什么用角色做中间层因为真实场景里人事专员和人事经理的权限高度重叠只是少数操作不同。如果给用户直接绑权限每新增一个员工管理员都要从头配一遍菜单和按钮。改成角色之后定义好专员角色该有的权限新员工只需要绑定角色就自动继承。这个项目里我把权限粒度做到了按钮级别。不只是薪资管理这个菜单谁能看还包括删除员工按钮只有经理角色可用。RBAC模型落到功能上的具体表现是不同角色登录后左侧菜单不同同一个页面没有权限的角色看不到操作按钮。这两点也是答辩时的加分项。RBAC实现的三张核心关联表用户-角色关联表、角色-菜单关联表、用户-菜单关联表用于存储单独授权的例外情况。Spring Security的PreAuthorize注解可以直接在接口方法上做权限校验配合前端的动态路由渲染控制链路完整。2. 数据库设计与核心业务算法2.1 表结构规划一次设计到位后面少掉头发数据库设计是整系统质量的地基我在编码前画了两天E-R图最终产出16张表按业务域可以分成四组权限组sys_user用户表、sys_role角色表、sys_menu菜单表、sys_user_role用户角色关联表、sys_role_menu角色菜单关联表组织与员工组dept部门表、position岗位表、employee员工信息表、contract合同表考勤组attendance考勤记录表、leave_record请假记录表、overtime_record加班记录表薪资招聘组salary_config薪资配置表、salary_record工资条表、recruit_position招聘职位表、resume简历表有人问过一个问题用户表和员工表为什么不合并因为它们代表的实体不一样。用户表是可以登录系统的人包含账号密码状态员工表是公司人事档案里的员工包含姓名、部门、岗位、入职日期。一个用户可能对公司员工HR本人也是员工也可能对应外部人员比如外聘面试官。合并的话两个维度的字段互相纠缠后续扩展很痛苦。这个职责分离的原则对所有表设计都适用。2.2 员工与部门表的核心字段细节员工表不是简单地把身份证号、手机号、姓名堆上去就完事。有几个字段在设计时就要想清楚employee_no员工工号全局唯一。这不仅是业务标识也可以作为登录账号的候选。我用的是EMP 日期 序号的生成规则比如EMP20250612001。status在职状态用枚举值管理1在职、2离职、3停薪留职。不要直接用字符串在职存入数据库维护起来极其痛苦。dept_id部门外键。注意这里不能存部门名称一旦部门改名所有历史数据全乱套。存ID才是正确姿势查询时再join部门表。entry_date和leave_date入职日期和离职日期。带薪期计算、工龄工资全部依赖这两列。部门表用了自关联树结构parent_id指向自身主键支持多级部门嵌套。配合前端递归组件渲染部门树增删部门都方便。这里有个非常实际的教训parent_id字段一定记得加索引。我第一版没加测试数据量到两万条时长时部门树的查询速度明显变慢加了索引后恢复到毫秒级。数据量小没问题但养成好习惯不亏。2.3 考勤模块类型字段设计让统计不再纠结考勤模块是整个系统里业务逻辑密度最高的地方。刚开始设计的时候我犯过一个典型的错误attendance表只存打卡时间等到月末汇总时再实时判断每个员工每天是正常、迟到、早退还是缺勤。后来发现这种做法有较大隐患当天的考勤状态无法实时展示而且月末计算一次要全表扫描大量数据。优化后的方案是在考勤记录表里增加一个attendance_type字段每天打卡后立即判定结果并落库类型包括正常出勤、迟到、早退、缺勤、请假、加班。当天的判定规则如下上班时间标准为09:00下班标准为18:00晚于09:00打卡记为迟到早于18:00打卡记为早退全天无任何打卡记为缺勤已有审批通过的请假单则标记为请假月末汇总统计只需对这个字段做分组聚合效率高逻辑清晰HR当天就能看到员工考勤异常。考勤数据从哪里来这个问题也很多人纠结。真实企业会用考勤机硬件但毕设环境没法外接设备。我用了模拟打卡方案员工在系统内点击打卡按钮提交当前时间作为打卡记录HR也可以在后台手动补录某位员工的打卡数据模拟考勤机接口。如果评委问及真实考勤机可以说已预留考勤数据对接的抽象接口只要实现一个对接类即可。这样设计既保证演示效果也保留了扩展性。考勤与请假的联动规则如果员工某天有审批通过的请假记录考勤汇总时此日直接计为请假不再判为缺勤。这个规则在汇总统计前统一执行而不是人工改数据。请假时长的计算按工作日自然日处理最小单位为0.5天支持半天假。2.4 薪资核算策略模式解决不同计算口径薪资模块的算法不算难难的是正确性和可追溯性。基本核算公式应发工资 基本工资 岗位工资 绩效奖金 加班工资 - 请假扣款实发工资 应发工资 - 五险一金个人部分 - 个税其中加班工资和请假扣款要从考勤模块取数加班工资 加班时长 × 时薪 × 加班倍率工作日1.5倍、休息日2倍、法定节假日3倍请假扣款 请假天数 × 日薪这里藏着一个容易踩的坑日薪的计算口径。按劳动法推荐口径是21.75天365天减104个休息日再除以12个月但很多中小企业实际沿用20.83天或按当月实际出勤天数来算。两种口径算下来金额会有差异。我在代码里把它抽象成了策略接口public interface SalaryPolicy { /** * 计算日薪 */ BigDecimal calculateDailySalary(BigDecimal monthlySalary); /** * 计算加班工资 */ BigDecimal calculateOvertimeSalary(BigDecimal base, BigDecimal hours, BigDecimal rate); }提供了标准实现和按实际出勤天数实现后台通过配置切换使用哪种口径。这种策略模式的使用答辩时专门拿出来讲能让评委看到你有软件工程设计的意识。薪资记录生成时必须做快照当月工资条保存的不仅是金额还包括计算依据——用到的薪资配置版本、参与的考勤记录ID列表、日薪口径。因为薪资配置会变比如下月调薪如果工资条不存快照历史查询时看到的可能都是被新配置覆盖的数据就乱套了。3. 实操过程与核心环节实现3.1 环境准备与工程初始化先把环境统一了不统一后面全是麻烦JDK 1.8Spring Boot 2.x对JDK8支持最稳不要一上来就用JDK 17编译期各种坑会让你想砸电脑Maven 3.6本地仓库配置阿里云镜像依赖下载速度立翻数倍MySQL 8.0库表字符集统一为utf8mb4不然emoji、生僻字存不进去Redis 6.x本机安装或用Docker起一个容器都行Node.js 14前端Vue项目编译需要IDE后端用IDEA前端用VSCode各干各的活儿后端工程初始化我用Spring Initializr生成基础骨架勾选Web、MySQL Driver、Lombok、Redis等依赖后续再手动加MyBatis Plus和JWT相关库。包名用com.demo.hrms工程名hrms规范清晰后面写代码不容易凌乱。模块架构上强烈建议单模块多包结构controller、service、mapper、entity、dto、config、security、util。有些同学看到大厂开源项目都是多模块就跟着学hrms-common、hrms-system、hrms-api各自分开毕业设计完全没必要。多模块解决的是多团队协作和代码复用单项目单体架构搞多模块反而增加各模块间相互import的混乱局面答辩也讲不出实际收益。3.2 JWT认证链路从登录到接口鉴权系统登录认证链路设计如下用户提交用户名密码 - 后端校验 - 生成JWT返回前端 - 前端存入localStorage - 后续请求header携带Authorization: Bearer token- 后端过滤器解析token并构建安全上下文。我自定义了JwtAuthenticationFilter继承OncePerRequestFilter核心代码逻辑Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String header request.getHeader(Authorization); if (StringUtils.hasText(header) header.startsWith(Bearer )) { String token header.substring(7); try { Claims claims Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(token) .getBody(); String username claims.get(username, String.class); // 从Redis校验token是否有效登录状态 String cachedToken redisUtil.get(token: username); if (username ! null cachedToken ! null cachedToken.equals(token)) { UserDetails userDetails userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authentication.setDetails( new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } } catch (Exception e) { // token非法或过期直接返回401阻止请求进入Controller response.setStatus(HttpStatus.UNAUTHORIZED.value()); return; } } filterChain.doFilter(request, response); }JWT默认有效期设2小时但要注意JWT本身无状态服务端不能主动吊销。解决方法是登录时把token写入Rediskey为token:username用户注销时删除该key。过滤器每次都从Redis里检查token在Redis中不存在或值不一致就拦截。这样既保留了JWT的便捷又获得了服务端吊销能力。接口鉴权用Spring Security的PreAuthorize注解方法级控制PreAuthorize(hasAuthority(employee:delete)) DeleteMapping(/{id}) public Result deleteEmployee(PathVariable Long id) { employeeService.deleteById(id); return Result.ok(); }同时在配置类中开启方法安全校验EnableGlobalMethodSecurity(prePostEnabled true)。3.3 员工批量导入Excel处理不是简单地读个文件HR系统里最常见的实操场景之一就是HR拿着Excel表导入大批员工数据。EasyExcel是阿里开源的低内存Excel处理库API简洁对毕设来说完全够用。处理批量导入的核心不只是把Excel读进数据库而是校验机制。每行数据按规则校验姓名为空则报错手机号正则校验^1[3-9]\d{9}$身份证号格式合法18位末位可为X部门名称必须存在于系统部门表中岗位名称必须存在校验失败的行要单独记录错误原因全部处理完后统一返回错误报告。不能只报导入失败要告诉用户具体哪一行哪个字段有问题。这样导入功能才有实际可用性。正常流程是提供模板下载 - 用户填好模板 - 上传 - 系统逐行校验 - 显示成功条数 错误报告下载。3.4 前端联调与接口对接前端用Vue2 Element UI核心操作点两个axios全局封装和路由守卫。axios拦截器需要统一做token注入和401处理。用户登录后token存入localStorage每次请求由拦截器自动加到header上不用每个页面手动写一遍service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) service.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } Message.error(error.response?.data?.message || 请求失败) return Promise.reject(error) } )路由守卫做页面级准入除了/login其余页面未登录一律重定向到登录页路由meta里配置roles字段做一部分页面的角色准入控制。前端权限只是体验优化真正的安全校验永远在后端接口层两者同时存在才是完整方案。树形部门组件是另一个值得注意的点。Vue递归组件渲染多级部门树组件内部调用自身实现子节点展开。配合后端已经组装好的树形结构数据直接绑定渲染即可。组装树形数据时注意保证数据是有向无环图避免递归死循环导致栈溢出。3.5 演示数据准备让系统打开就有内容毕设演示效果占的印象分远超想象。如果打开系统空空如也评委点一下员工管理看不到人再点薪资管理什么都没有整个项目质量感瞬间拉低。我在项目里做了数据生成器当员工表为空时启动阶段自动插入演示数据。5个部门、30位员工、每位员工最近3个月的考勤记录、请假记录、加班记录、12月工资条。演示时随便点哪个模块都有数据可看。工资生成还有一个细节把工资计算的中间过程字段也写入工资条明细。比如这位员工本月出勤22天、请假1.5天、加班4小时、加班工资是多少、请假扣款是多少每一项都有来源。演示时被问到这个人的工资是怎么算出来的直接打工资明细页面从考勤数据推导到每一项金额讲清楚计算路径。这种透明度很高的演示方式比讲一百句系统功能强大都有用。4. 常见问题与排查技巧实录4.1 环境类问题速查表这一部分的问题其实都不是代码逻辑问题而是环境没有弄对但排查起来特别费时间。我把常见问题整理成一张速查表问题现象可能原因解决方案启动报Failed to configure a DataSource数据库连接信息问题或MySQL没启动检查application.yml配置确认MySQL服务已启动登录时验证码接口报错Redis未启动或端口被占用启动Redis服务检查配置的端口是否正确前端请求跨域报错后端未配置CORSSecurityConfig中放行OPTIONS预检请求配置跨域过滤器JWT解析报签名错误前后端密钥不一致统一前后端的secretKey配置重新登录生成新token中文乱码数据库字符集不是utf8mb4建库时指定utf8mb4连接URL加characterEncodingutf-8上传的Excel读取中文乱码文件编码或EasyExcel读表头映射问题确保模板文件为xlsx格式DTO字段名与表头注解一致本地可以跑换电脑跑就报错环境版本不一致写README.txt记录JDK、Maven、MySQL、Redis版本以及数据库初始化SQL执行步骤如果你用的是MySQL 8.0容易踩到的一个坑是时区问题。连接URL如果没加serverTimezoneAsia/Shanghai日期字段会在午夜附近错位一天考勤数据日期就不对了。这个错误很隐蔽不仔细比对日期根本发现不了。4.2 业务逻辑中容易忽略的三个坑第一是跨月数据归属。员工1月31日提交了2月2日的请假申请如果汇总1月考勤时按申请创建日期统计这条请假就会被错误算进1月。正确做法是考勤统计始终以记录实际归属日期为准而不是创建日期。这个原则在项目注释里写明之后没有再犯。第二是请假与缺勤的冲突。员工请假当天没打卡系统不能同时标记缺勤和请假两条记录。考勤汇总模块要对这两种情况做联动存在审批通过请假记录的日期考勤状态直接标记为请假没有请假也没有考勤的日期标记为缺勤。第三是员工离职后的薪资。离职员工在当月仍有部分工资需要计算不能直接从员工表删除而应该将状态改为离职薪资模块仍要把他纳入当月核算。所以员工管理只做离职操作不做物理删除除非是误录入的数据。这三类问题本质都是业务状态的流转边界没有事先定义清楚。在设计阶段把状态机图画出来在职-请假-离职等后面编码就会顺畅很多。4.3 答辩前的自检与演练正式答辩前至少做三轮完整地走流程测试。第一轮测试业务bug看核心链路能不能跑通第二轮测交互体验页面跳转、按钮响应是否顺畅第三轮测演示流畅度所有操作是不是一遍就能成功不能出现演示现场临时找数据的情况。答辩备几张关键图系统架构图、数据库E-R图、权限模型图、核心业务时序图。不用画得多漂亮关键是能顺着图讲清业务。讲项目的时候重点是业务链路设计方案不是操作手册。评委想听的是你为什么这么设计、遇到过什么重大问题、怎么解决的。把开发日志里的踩坑记录整理成2-3个项目难点与解决方案这是答辩中最有说服力的部分。写在最后的一点经验带过几轮毕设项目之后我最大的体会是人力资源管理系统这个选题核心不在写代码而在把业务理清楚。技术栈只要会Spring Boot基本操作就能跑通真正区分优良中差的是数据库设计是否合理、业务状态流转是否严谨、考勤薪资逻辑是否经得起追问。时间规划上建议按四周推进第一周做设计表结构、接口定义、页面原型第二周完成后端核心链路登录、员工、考勤、薪资第三周做前端对接和权限完善第四周集中测试、补数据、准备答辩材料。每天两三个小时强度完全可控。最后分享一个很实用的小习惯开发过程中写一个简单的开发日志不用长每天记几条今天完成什么、遇到什么问题、怎么解决。这个日志对整理思路很有帮助更重要的是答辩时你手里全是真实的技术决策记录项目讲解会比空谈功能扎实得多。如果你也正在做这个选题希望这篇拆解能让你少走几个坑。系统跑得通、逻辑讲得清、数据填得真做到这三点答辩基本就稳了。