做毕设选型的时候我见过太多同学在“电商系统”“图书管理系统”“宿舍管理系统”这几个老掉牙题目里反复横跳真到了答辩台上评审老师听第一句就能猜到后面的所有模块。相比之下基于SpringBoot的人力资源管理系统是个很聪明的选择业务域足够完整组织架构、员工档案、考勤、薪资、招聘、培训全都能覆盖技术栈主流SpringBoot MyBatis-Plus Redis Vue 这一套说出去都有分量而且源码、配套文档、部署说明、讲解材料一应俱全无论你是拿来当毕设交差还是作为第一个完整项目写进简历性价比都非常高。这篇文章我就以这套人力资源管理系统为线索把项目从定位、技术选型、数据库设计到核心模块实现、部署上线、常见问题排查完整地拆开讲一遍。项目源码自带lw论文和部署文档我也会结合我多年带项目的经验告诉你怎么把这些配套材料用起来让整个交付物看起来专业、完整、能打。1. 项目定位与技术选型思路1.1 为什么人力资源管理系统值得做很多人一听到“人力资源管理系统”第一反应是“太常见了”但恰恰是“常见”这个属性让它成为最稳妥的选题。冷门题目虽然看着炫酷但答辩时老师一问业务流程、二问数据流转、三问权限控制你答不上来就是硬伤而HR系统不一样它的问题是“太常见所以每个人都懂”但每个模块拆开来都有足够的技术深度经得起追问。选择这个题目的直接收益有三个业务需求明确且闭环从员工入职建档、考勤打卡、请假审批到月底算薪、生成报表整个流程串起来就是一个完整的业务闭环。这比零散的“XX管理系统”更有逻辑性。技术点覆盖全面CRUD只是基础它还天然涉及权限角色、审批流、文件导入导出、定时任务、数据统计等进阶功能每一个都能作为亮点写进论文和答辩PPT。边界可控不会失控不像电商系统那样要面对订单超卖、支付回调、分库分表这些魔鬼细节HR系统的核心逻辑清晰一个人完全能在两三周内从零跑到上线。在功能边界上我给这套系统的定位是“麻雀虽小五脏俱全”。管理端包含部门管理、员工管理、考勤管理、薪资管理、招聘管理和培训公告角色上区分管理员、HR专员、普通员工三类权限用Spring Security JWT来控制。核心闭环就一句话员工档案是基础考勤和薪资是业务核心招聘培训是扩展公告日志是加分项。不要贪多求全把每一个模块做透比堆十个半成品功能强得多。1.2 SpringBoot怎么做选型更合理关于SpringBoot本身这里多说几句。项目采用的是SpringBoot 2.7.x这个版本而不是更激进的SpringBoot 3.x。选2.7.x的原因非常实际大多数教学资料、网上教程、旧版本中间件都基于Java 8 javax命名空间而这个版本恰好是2.x系列的最后一个稳定大版本既能用上SpringBoot 2.x的成熟生态又避开了3.x从javax切换到jakarta带来的兼容性坑。如果你不想在“怎么把guava换成新坐标”“MyBatis-Plus版本又冲突了”这类问题上浪费一整天直接锁死2.7.18就好。配套技术栈这样搭配是比较稳的组合持久层MyBatis-Plus 3.5.x。自动CRUD、分页插件、条件构造器写起来非常顺手尤其适合快速出活。权限认证Spring Security JWT Redis。JWT做无状态登录Redis存token黑名单和验证码权限注解控制接口访问。数据库MySQL 8.x。存储引擎默认InnoDB字符集统一utf8mb4。前端若走前后端分离路线选用Vue3 Element-Plus Axios若不想拆前后端SpringBoot直接集成Thymeleaf模板引擎也可以。这套项目源码里我建议以后端为主前端按需选择。这套组合选型的核心逻辑是“短时间内能交付 面试/答辩有得聊”每个组件都是工业界被验证过的标准方案出了问题随手就能搜到解决方案不存在选型上的冷门风险。2. 系统架构与功能模块拆解2.1 分层架构与代码组织整个后端工程采用经典的三层架构Controller层负责接收HTTP请求和参数校验Service层处理业务逻辑和事务Mapper层DAO层负责数据库交互。如果项目规模再大一点可以在这个基础上抽出DTO/VO来隔离实体和数据传输对象避免把数据库表结构直接暴露给前端。com.example.hrms ├── controller # 接口层按业务域分包 ├── service # 业务层接口 实现类 ├── mapper # 数据访问层MyBatis-Plus的BaseMapper ├── entity # 数据库实体 ├── dto # 入参对象 ├── vo # 出参对象 ├── config # 配置类Security、Redis、MybatisPlus等 ├── utils # 工具类JwtUtil、ExcelUtil等 ├── aspect # AOP切面操作日志、接口日志 └── exception # 全局异常处理这种分包方式好理解也方便论文里画架构图。有一点我要特别强调Controller里绝对不能写业务逻辑所有业务判断都要下沉到Service这样事务注解才有效代码才好测试答辩的时候讲“我的代码分层清晰、职责分明”才有底气。2.2 六大核心业务模块部门与员工管理是系统的基石也是重点演示CRUD功底的部分。部门表用父子结构支持多级部门树员工表关联部门、岗位、入职日期列表查询支持姓名、部门、入职时间范围等多条件组合用MyBatis-Plus的LambdaQueryWrapper轻松搞定。员工状态要有“在职/离职”两种离职员工不能直接物理删除要走逻辑删除标记。考勤模块的玩法是每天上下班打卡后端收到打卡请求后判断当前时间与规定的上班时间比如9:00做对比自动标记正常/迟到/缺卡状态。月底汇总异常考勤供HR参考。这个模块最好配合一个定时任务每天晚上自动补扫一遍当天未打卡的员工记录生成异常提醒。薪资模块的逻辑是典型的状态机先是薪资草稿HR录入基本工资、绩效、补助、加班费系统自动扣减社保公积金和个人所得税计算实发工资确认无误后标记为已确认最后发放完成。每月工资数据要留历史存档不能覆盖上个月的记录。招聘模块管理职位发布和简历筛选核心是“职位-简历”的一对多关系每个职位有招聘人数上限投递简历后会经历待筛选、邀面试、已通过、已淘汰这些状态流转。这个模块展示了关联表设计和状态流转控制也是一个很好的答辩话题点。培训与公告属于扩展模块培训模块管理培训计划、时间、地点、讲师和参与员工名单公告模块发布公司通知支持置顶和按时间倒序展示。这两个模块简单但完整填充了系统的功能面不至于让整个系统看起来“只有增删改查”。2.3 权限控制设计权限上我采用“RBAC”基于角色的访问控制模型三类角色天然对应不同的菜单和数据权限管理员全部权限包括部门管理、员工管理、薪资管理、系统日志。HR专员员工管理、考勤管理、招聘管理、薪资管理但不能删部门、不能改系统配置。普通员工只能看自己的档案、打自己的卡、提交请假申请、查自己的工资条。具体落地就是数据库里建sys_user、sys_role、sys_menu、user_role、role_menu这几张标准RBAC表后端用Spring Security 自定义注解比如PreAuthorize(hasRole(ADMIN))在接口方法上声明权限要求。前后端配合前端根据返回的用户角色动态渲染能看到的菜单按钮后端再校验一次接口权限双保险。这里必须强调一个原则——前端隐藏按钮只是体验优化真正的鉴权永远在服务端做。3. 数据库设计与核心表结构3.1 表设计总览数据库是这个项目的“地基”地基不打牢后面所有模块都歪。我通常建议核心主表控制在10张以内宁可字段多一点也不要拆出几十张表把自己绕晕。这套系统的核心表大致如下表名用途关键字段说明sys_user系统用户登录账号username、passwordBCrypt加密、user_typesys_role角色表role_code、role_namesys_menu菜单/权限表parent_id、menu_name、permsdept部门表parent_id树形结构、dept_name、leader_idemployee员工档案表emp_no工号唯一、name、dept_id、position、entry_dateattendance考勤记录表emp_id、attendance_date、clock_in_time、clock_out_time、statusleave请假申请表emp_id、leave_type、start_time、end_time、approve_statussalary薪资表emp_id、salary_month、basic_salary、bonus、actual_salary、statusjob_post招聘职位表post_name、dept_id、headcount、current_count、statusresume简历表job_post_id、name、phone、resume_path、statussys_log操作日志表username、operation、method、params、ip、time3.2 员工表和考勤表的设计细节员工表是核心中的核心设计时有一个很关键的原则主键用自增id但业务标识用工号emp_no唯一索引。因为员工可能离职后重新入职也可能身份证号重号现实中不常见但理论存在自增id和工号分离后面所有业务表都用emp_id做逻辑关联避免动员工基本数据时牵一发动全身。员工表里的id_card身份证号字段属于敏感信息我建议在入库前用AES或国密SM4做加密查询时按需解密并且只有管理员权限能明文查看。数据库层面还要设置逻辑删除标志deleted0正常1已删除所有列表查询默认过滤deleted0防止误删数据无法恢复。考勤表的设计要区分两个维度打卡原始记录和日汇总状态。简单一点的做法是每天每个员工只生成一条记录上下班时间是两个可空字段严格一点的做法是建一张打卡流水表每次打卡一行再用定时任务汇总生成日考勤记录。毕业设计推荐第一种逻辑清晰、报表好出如果想把技术含量拉高再考虑第二种方案也不迟。salary_month字段要存成2025-06这种格式用varchar7就够不要用DATETIME因为工资月份本身没有“日”的概念。所有涉及金额的字段一律DECIMAL(10,2)绝不能用FLOAT或DOUBLE否则算工资会出现0.10.20.30000000000000004这种事故。3.3 数据库设计的三个通用注意事项第一字符集必须utf8mb4原因很简单员工名字、简历里可能出现emoji、生僻字utf8mb3存不下。建库语句统一写成CREATE DATABASE hrms DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;第二所有表都加上create_time、update_time、deleted三个通用字段并让MyBatis-Plus的自动填充功能在插入和更新时自动写入时间不要手动维护省心且统一。第三外键约束能不用就不用逻辑层面去控制关联关系。数据库外键在分库分表、批量导入、系统扩展时全是障碍通过Service层代码保证数据完整性的做法比数据库硬约束更利于项目演进。4. 核心功能实现与实操步骤4.1 登录认证与JWT的完整实现登录模块虽然看起来简单但它串起了Spring Security、Redis、JWT三样东西是整套系统里含金量最高的部分之一。整体流程是用户提交用户名密码后端验证成功后生成JWT返回前端前端后续请求在请求头里带上Authorization: Bearer token后端拦截器解析token拿到用户信息后放行。具体实现分四步第一步密码加密。注册或初始化用户时密码用BCrypt加密存储。Spring Security自带的BCryptPasswordEncoder就够了它是加盐哈希同一个密码每次加密结果都不同暴力破解成本极高。登录时用encoder.matches(rawPassword, encodedPassword)校验。第二步生成JWT。引入jjwt依赖设计一个JwtUtil工具类核心是生成token和解析token// 生成token设置subject为用户名加入用户id、角色等claims设置过期时间 String token Jwts.builder() .setSubject(username) .claim(userId, user.getId()) .claim(role, user.getRoleCode()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() 2 * 60 * 60 * 1000)) // 2小时过期 .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact();第三步写拦截器。定义一个JwtAuthenticationFilter继承OncePerRequestFilter在每次请求时取出token校验通过后把userId放到ThreadLocal或SecurityContext中供后续业务代码获取当前登录人。第四步Redis管黑名单。用户退出登录时token还没到过期时间如果不处理任何人都能拿着旧token继续访问。做法是退出时把token丢进Redis黑名单并设置与token一致的过期时间拦截器里先查Redis存在就直接拒绝。验证码也建议放Redis5分钟过期前后端分离环境下验证码不能用Session这个方案是最稳的。4.2 员工管理的CRUD与动态分页查询员工管理是整个系统出镜率最高的模块也是答辩时最容易演示的部分。它的实现要点就是“多条件动态查询 分页 排序”MyBatis-Plus的LambdaQueryWrapper写起来非常优雅public PageResultEmployeeVO pageEmployees(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(query.getDeptId() ! null, Employee::getDeptId, query.getDeptId()) .ge(query.getEntryStart() ! null, Employee::getEntryDate, query.getEntryStart()) .le(query.getEntryEnd() ! null, Employee::getEntryDate, query.getEntryEnd()) .eq(Employee::getDeleted, 0) .orderByDesc(Employee::getCreateTime); PageEmployee result employeeMapper.selectPage(page, wrapper); return convertToPageResult(result); }这段代码里有几个细节值得说。第一每个条件都要先判断前端传没传值避免拼出“查询全部”的SQL比如用户没选部门时deptId是null这时绝不能把“等于null”拼进where条件。第二入职时间范围用ge和le两个条件闭合日期范围查询这是标配。第三deleted0条件必须手动加上MyBatis-Plus的逻辑删除只对按id操作生效自定义查询时如果没配全局逻辑删除非常容易把已删除数据查出来。新增和编辑员工时要注意工号唯一性校验先查emp_no是否已存在存在就抛业务异常。工号可以按照部门编号序号自动生成或者手动录入时加个前后端双重校验。删除员工时用逻辑删除但在删除前要先检查员工有没有关联的未结清薪资记录否则会出现“人没了工资表还在”的脏数据问题。4.3 考勤打卡与月底汇总的实现细节打卡功能的难点不在写入而在状态判定和边界时间处理。假设公司规定9:00上班、18:00下班。我建议的规则是上班卡可提前最多60分钟打卡8:00到9:00之间打上班卡都算正常超过9:00算迟到。下班卡在18:00之后打卡算正常17:00到18:00之间打下班卡算早退打了上班卡没打下班卡算缺卡。未打卡的系统不自动补卡需要走补卡申请由HR审核。后端核心代码逻辑大致如下public Result clockIn(Long empId) { LocalDate today LocalDate.now(); LocalDateTime now LocalDateTime.now(); Attendance record attendanceMapper.selectOne( new LambdaQueryWrapperAttendance() .eq(Attendance::getEmpId, empId) .eq(Attendance::getAttendanceDate, today)); if (record null) { record new Attendance(); record.setEmpId(empId); record.setAttendanceDate(today); } // 判断是上班卡还是下班卡 LocalTime startLimit LocalTime.of(8, 0); LocalTime endLimit LocalTime.of(18, 0); if (record.getClockInTime() null) { record.setClockInTime(now.toLocalTime()); record.setStatus(now.toLocalTime().isAfter(endLimit) ? ABSENT : NORMAL); // 如果没到上班时间就打上班卡算正常 // 超过上班时间算迟到 } else { record.setClockOutTime(now.toLocalTime()); // 判断早退 } attendanceMapper.insertOrUpdate(record); return Result.success(); }注意这里有一个非常容易踩的坑判断迟到时不要用now.toLocalTime().isAfter(startLimit)直接判因为如果有人在8:30打上班卡这个条件是true但9:00上班允许早到8:30打卡应该算正常。正确做法是用实际打卡时间对比上班时间和下班时间窗口大于8:00且小于等于9:00都算正常在9:00之后才算迟到判断逻辑里对早退同理。月底汇总建议用SQL或Service批量更新把所有员工某月每天的考勤状态聚合出一个“出勤天数、迟到次数、缺卡次数”的月度汇总表供薪资模块参考。4.4 薪资计算与Excel导出薪资计算是整个系统里最能体现“业务深度”的模块没必要做到财务软件的复杂度但状态流和计算规则要清晰。我采用的规则是应发工资 基本工资 绩效奖金 岗位补贴 加班工资 社保公积金 应发工资 × 固定比例比如社保10.5%、公积金7% 个人所得税 阶梯简化计算不超过5000不扣5000~8000按3%扣 实发工资 应发工资 - 社保公积金 - 个人所得税这里的比例和个税算法适用于毕设演示真实企业计算要按当地社保基数和个人所得税法的累计预扣法算但代码结构和常量配置的方式完全一样把比例常量放到application.yml或数据库配置表里演示的时候改配置就能调整非常方便。工资数据生成后导出Excel是展示给答辩老师看的典型亮点。我推荐用EasyExcel阿里开源的相比POI原生API它内存占用小、写起来简单、支持模板填充。员工花名册和工资表导出先用ExcelProperty注解定义表头再用EasyExcel.write(outputStream, 实体类.class).sheet(工资表).doWrite(dataList)几行代码就能搞定。导入员工Excel时注意在导入前校验表头是否符合预期数据里有没有空行日期字段格式是不是标准这些细节少一个就等着导入完数据乱成一锅粥。4.5 日志与报表答辩环节的加分项此前的模块都算是“必备项”接下来这两个功能是“加分项”但成本不高非常建议加进去。操作日志用AOP切面实现定义Log注解标记在Controller方法上切面在方法执行前后自动记录操作人、操作类型、方法名、参数、IP、耗时。切面里把参数序列化成JSON超过一定长度的截断敏感字段如密码做脱敏。这个功能既能证明你掌握AOP又能在论文里占一节答辩老师问“你怎么实现日志功能的”你可以讲得头头是道。统计报表可以针对三个点部门员工数分布柱状图、月度考勤异常统计折线图、招聘漏斗各状态简历数量。后端只需要提供聚合查询接口前端用ECharts渲染图表不需要引入重型报表引擎。数据聚合用SQL的 Group By 就能解决比如统计各部门人数SELECT dept_id, COUNT(*) AS cnt FROM employee WHERE deleted 0 GROUP BY dept_id;5. 部署上线与配套交付物使用5.1 本地环境准备与项目启动拿到源码后第一步不是急着运行而是把环境对齐。推荐版本组合JDK 1.8、Maven 3.6、MySQL 5.7或8.0、Redis 6.x。如果你用的是IDEA直接导入项目等Maven下载依赖即可如果依赖下载很慢在Maven的settings.xml里配置阿里云镜像这是我最常帮人解决的问题。启动后端流程分四步在MySQL里创建数据库执行项目根目录的sql/hrms.sql脚本初始化数据表和基础数据默认管理员账号、部门、菜单。修改application.yml把数据库地址、账号、密码Redis地址密码改成你自己的环境配置。切换到SpringBoot主启动类所在目录执行mvn spring-boot:run或者直接在IDEA里运行主类。看到Started HrmsApplication日志后用接口测试工具推荐Postman或Apifox访问http://localhost:8080/api/login用默认管理员账号登录验证token能正常返回。如果项目是前后端分离结构前端还要单独启动npm install安装依赖然后npm run serve启动开发服务器比如http://localhost:3000把前端代理配置指向http://localhost:8080。遇到CORS跨域问题优先在后端配置跨域过滤器而不是生成环境用代理解决。5.2 打包部署到服务器本地跑通只是“能运行”要演示或部署必须打包到服务器上。后端打包命令mvn clean package -DskipTests打出的jar包在target目录下文件名类似hrms-0.0.1-SNAPSHOT.jar。放到服务器上运行建议用后台方式nohup java -jar hrms-0.0.1-SNAPSHOT.jar --server.port8080 app.log 21 几个实用参数--spring.profiles.activeprod可以切换不同环境的配置文件-Xms256m -Xmx512m限制JVM内存避免小服务器被撑爆。如果前端也打包好了dist目录部署到Nginx并配置反向代理把/api请求转发到后端jar的8080端口location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }部署文档里这些内容通常都会写但真正执行时最容易出问题的不是命令本身而是端口没开、防火墙没放行、数据库IP配置成了localhost服务器上应该是内网IP或127.0.0.1、Redis没设置密码导致连接被拒。我建议部署时先在服务器上curl http://127.0.0.1:8080/api/xxx自测能通再考虑外部访问。5.3 源码、论文lw、部署文档和讲解材料怎么配合使用这套项目交付物里有源码、lw论文/设计文档、部署文档和讲解材料很多人拿到手就懵了不知道怎么高效利用。我给的顺序建议是第一遍以部署文档为主先把项目跑起来看到登录页面和默认数据对系统有个直观感受。第二遍以源码为主重点看登录模块和员工管理模块的代码结构对照部署文档里的目录说明搞清楚每个包是干什么的。第三遍再看论文的章节结构论文一般包含绪论、需求分析、系统设计、数据库设计、系统实现、系统测试这几个大章节论文里的时序图、用例图、ER图其实就是你答辩时要讲的核心素材把论文和代码对应起来讲起来就有底气。讲解材料一般是录屏视频或PPT适合在答辩前几天快速过一遍主要学“讲解的话术”和“演示的路径”比如先演示登录、再演示员工添加、然后演示考勤打卡、最后演示工资导出这条演示主线逻辑非常顺自己也照着演两遍。6. 常见问题与排查技巧实录6.1 高频报错与解决方案速查开发过程中一定会遇到各种报错我把这几年带项目最常见的几个问题列成一张速查表照着排查能省一晚上时间报错现象可能原因解决方案ClassNotFoundException: javax.servlet.FilterSpringBoot 3.x项目用了旧版的拦截器依赖锁定SpringBoot 2.7.x版本或改用jakarta命名空间启动失败端口被占用8080端口已被其他程序占用lsof -i:8080找到PID后kill或--server.port8081换端口Public Key Retrieval is not allowed连接MySQL 8时未允许公钥检索数据库URL加allowPublicKeyRetrievaltrueuseSSLfalseAccess denied for user rootlocalhost数据库密码错误或未授权检查application.yml密码或MySQL执行授权SQLRedis连接超时Redis未启动或密码不对确保Redis服务启动确认密码配置一致Query报错Unknown column xxx实体类字段和数据库字段映射错误检查TableField注解和驼峰映射配置中文乱码数据库字符集不是utf8mb4建库时指定utf8mb4连接URL加characterEncodingutf8Thymeleaf模板找不到页面模板路径配置错误检查controller返回的view name与templates目录结构对齐6.2 数据库时区与字符集两大坑数据库问题里时区问题是仅次于依赖冲突的“新手杀手”。MySQL连接串上一定加上spring.datasource.urljdbc:mysql://localhost:3306/hrms?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue不配serverTimezone旧驱动可能报The server time zone value йʱ is unrecognized新驱动虽然默认UTC能连上但你插入的create_time和本地差8个小时。配了Asia/Shanghai就一劳永逸这个字符串是能被驱动的时区数据库识别的标准ID。字符集问题是隐蔽的数据库是utf8mb4表是utf8mb4但如果你在Navicat里手动插入中文正常程序写入后读出来乱码那就要检查Spring Boot的数据库连接URL里的characterEncodingutf8是否加上以及操作系统环境变量里的LANG是否为UTF-8。三层都对了中文显示就稳了。6.3 打包后接口404的排查思路很多同学本地跑接口一切正常打包部署后访问却404排查顺序我建议固定为先看jar包有没有起来ps -ef | grep java再看访问路径前缀对不对后端接口通常都在/api前缀下Nginx转发规则里有没有带上最后看前端打包出的index.html里接口地址是不是写死了本地路径比如localhost:8080/api应该改为相对路径/api或由Nginx配置环境变量注入。有一种很隐蔽的情况SpringBoot打出的jar包里带有src/main/resources/static下的前端资源如果里面也放了一套index.htmlNginx同时也在8080端口上接管了静态资源冲突就会导致访问根路径出现奇怪的404或白屏。解决办法就是要么纯Nginx托管前端要么SpringBoot托管前端不要混着来。6.4 答辩演示时最容易翻车的三个演示场景演示环节翻车技术问题占一半环境问题占一半。第一个常见翻车是演示时突然Redis崩溃或内存不足导致登录接口直接报错建议提前把-Xms256m -Xmx512m配上Redis做一次完整重启再开始讲。第二个翻车是演示考勤打卡时当前时间不对系统按真实时间判断迟到早退而你想演示“正常打卡”却显示迟到。最好在演示前把考勤时间做成可配置或者数据库里预先插入几条不同状态的考勤记录演示时只展示汇总结果而不现场打卡。第三个翻车是Excel导出时文件下载不下来原因多半是Nginx没配置proxy_buffering off或者后端返回头里没有正确的Content-Disposition提前测一遍下载就能避免全场尴尬。7. 这套项目还能往哪些方向扩展如果你答辩完还有精力或者想让这个项目在简历上更有分量扩展方向很多而且都是顺着现有代码自然生长的。第一个方向是消息通知请假审批通过后给员工发系统消息或邮件这个用SpringBoot的JavaMailSender就能实现审批流就完整了。第二个方向是定时任务每月1号自动生成上个月的薪资草稿每天凌晨自动统计前一天考勤异常用Scheduled注解加几行配置就能跑起来。第三个方向是文件存储把员工头像、简历附件放到MinIO对象存储本地文件系统的硬编码路径会越来越不好维护MinIO的Java SDK集成也很简单这类改造能体现你对生产环境的理解。最后一个建议是做性能优化比如给查询频繁的接口加Redis缓存、给大列表接口做深分页优化、用Async异步处理Excel导入。这些优化不用全做挑两个做透写在论文的“系统优化”章节答辩老师一定会觉得你考虑得比别人深入。整套项目做下来我最深的感触是技术栈不在多而在用得明白。SpringBoot这套体系单独看每个知识点都不难难的是把它们串成一个完整可运行的系统。把登录、权限、分页、上传、部署这些散落的点全部打通之后你再看其他SpringBoot项目基本就是换一层皮的问题了。