基于Spring Boot与MyBatis构建企业级人力资源管理系统实战
2026/9/3 3:57:24 网站建设 项目流程

简介:这是一套完整的JavaWeb人力资源管理系统源码,面向Java初学者与Web开发入门者,适用于课程设计、毕业设计及中小型企业HR模块快速原型开发。系统涵盖员工管理、招聘、考勤、薪资、培训、简历审核等核心业务功能,采用JSP+Servlet+JDBC经典三层架构,便于理解MVC模式与Web应用开发全流程。压缩包共268个文件,含71个Java源文件、71个编译后Class文件、40个JSP页面、38个XML配置文件(含web.xml与数据库映射)、22个Jar依赖库,辅以CSS、JS、图片及SQL建表脚本等资源,整体大小24MB,结构清晰、模块划分明确。已有4763人学习下载,源码可直接导入Eclipse或MyEclipse运行,配套SQL脚本支持MySQL快速部署,适合边学边练、调试分析与二次开发。

1. 项目概述:从零到一构建一个企业级JavaWeb人力资源管理系统

最近在整理硬盘,翻出来一个几年前做过的企业级人力资源管理系统(HRM)的源码。这个项目当时是为一家中型制造业公司定制的,从需求对接到最终上线,前前后后折腾了小半年。今天借着这个机会,把整个系统的设计思路、技术选型、核心模块的实现,还有那些年踩过的坑,系统地梳理一遍。如果你正在学习JavaWeb,或者正打算自己动手做一个类似的管理系统来练手、甚至作为毕业设计,那这篇内容应该能给你提供一条清晰的路径和不少实用的参考。

简单来说,这个系统就是一个基于B/S架构,使用Java技术栈开发的,用于企业人力资源数字化管理的Web应用。它要解决的核心问题,就是把传统企业里那些散落在Excel表格、纸质档案甚至员工脑子里的“人”的信息和流程,搬到线上来,实现规范化、流程化和数据化。核心用户包括HR部门、各部门经理以及普通员工,覆盖了从员工入职到离职的全生命周期管理。下面,我们就一层层剥开这个系统的“外壳”,看看里面到底是怎么运作的。

2. 系统整体架构与技术选型背后的思考

做一个管理系统,技术选型是第一步,也是最容易让人纠结的一步。当时市面上可选的技术非常多,我们的选择是基于几个核心考量:团队技术栈的熟悉度、社区的活跃度与生态成熟度、项目的可维护性以及未来的扩展性。最终敲定的是一套非常经典且稳健的JavaWeb“全家桶”。

2.1 后端技术栈:为什么是Spring Boot + MyBatis?

后端我们选择了Spring Boot 2.x作为核心框架。没选传统的SSH(Struts2 + Spring + Hibernate)或者SSM(SpringMVC + Spring + MyBatis)的零散整合方式,直接上Spring Boot,图的就是一个“开箱即用”。它内嵌了Tomcat,约定大于配置,能让我们快速搭建起项目骨架,把精力集中在业务逻辑上,而不是没完没了的XML配置。这对于中小型团队或者个人开发者来说,效率提升是巨大的。

数据持久层,我们放弃了重量级的Hibernate,选择了MyBatis。这里有个很重要的权衡:HR系统的业务表结构相对固定,但查询需求极其复杂多变。比如,HR可能需要根据“入职时间在2023年、部门为技术部、学历为本科及以上、并且上月绩效考核为A的员工”这样的组合条件进行筛选。Hibernate的HQL或者Criteria API在处理这种动态多条件查询时,代码会显得比较臃肿。而MyBatis的XML映射文件,可以非常灵活地编写动态SQL(通过<if>,<choose>等标签),SQL的可读性和可控性更强,也方便我们针对复杂查询进行SQL优化。毕竟,对于管理系统,复杂列表查询的性能是用户体验的关键。

注意:选择MyBatis意味着你需要自己编写和维护更多的SQL,这对开发者的SQL功底有一定要求。但换来的是极致的灵活性和在复杂业务场景下更优的性能表现。如果你的业务非常简单,且团队更熟悉JPA的面向对象思维,那么Spring Data JPA也是一个很好的选择。

2.2 前端技术栈:从JSP到前后端分离的演进

这个项目最初期用的是JSP + JSTL + Bootstrap这套经典组合。JSP直接在后端渲染页面,开发速度快,对于表单提交、页面跳转这类传统操作非常顺手。Bootstrap则能快速搭建出风格统一、响应式的管理界面,大大减少了前端UI的工作量。

但是,随着模块增多,尤其是涉及到更多动态交互(如无刷新数据加载、复杂表单验证)时,JSP混合Java代码和HTML的模式就显得有些力不从心,代码可维护性下降。因此,在项目后期重构部分模块时,我们引入了Vue.js作为前端框架,尝试了前后端分离的架构。后端Spring Boot只提供RESTful API,前端通过Vue进行组件化开发。这样做的好处是前后端职责清晰,并行开发效率高,前端用户体验也更流畅。不过,这也带来了额外的学习成本和部署复杂度。

对于初学者或毕业设计,我建议可以从JSP+Bootstrap开始,先把核心业务跑通,理解整个MVC的流程。有精力再去探索前后端分离,这是一个很自然的技术演进路径。

2.3 其他关键组件

  • 权限控制:Apache Shiro。相比Spring Security,Shiro的API更直观易懂,配置也相对简单。对于HRM这种权限模型(用户-角色-权限)清晰的项目,Shiro完全够用,可以精细控制到菜单、按钮甚至API接口的访问。
  • 数据库:MySQL 5.7。开源、稳定、生态完善,是中型管理系统的首选。我们使用了InnoDB存储引擎,并对核心表(如员工表、考勤记录表)建立了合适的索引。
  • 项目构建与管理:Maven。依赖管理、项目构建、打包部署,一条龙服务。
  • 缓存:Redis。用于缓存一些不常变动但访问频繁的数据,如部门树形结构、系统配置项、用户会话信息等,显著减轻数据库压力。

3. 核心功能模块设计与实现拆解

一个完整的HRMS,其核心是围绕“人”的“进、管、出”流程。我们的系统主要包含了以下六大模块,每个模块的设计都有其特定的业务考量。

3.1 组织架构与员工信息管理:系统的基石

这是整个系统的数据核心,设计的好坏直接影响到其他所有模块。

数据库设计要点: 员工表(employee)的设计至关重要。除了基本信息(工号、姓名、性别、身份证号、联系方式),关键字段包括:

  • dept_id(部门ID):关联部门表。
  • position_id(岗位ID):关联岗位表。这里将部门和岗位分开,是因为一个部门下可能有多个岗位,且员工岗位调动可能不涉及部门变动。
  • hire_date(入职日期)、status(员工状态:试用、正式、离职等)。status字段用于软删除和状态流转控制。
  • 敏感信息如身份证号、银行卡号,在数据库存储时应进行加密处理。

部门树形结构实现: 部门通常具有层级关系(如公司->事业部->部门->小组)。我们在部门表(department)中设计了一个parent_id字段指向父部门ID,顶级部门的parent_id为0或NULL。在Java后端,我们通过递归或一次查询后内存组装的方式,构建出完整的部门树形结构,提供给前端以树形控件展示。

实操心得:处理树形数据时,避免在循环中频繁查询数据库。最佳实践是一次性查询出所有部门数据到内存(List),然后通过Map(以id为key,部门对象为value)和递归算法在内存中构建树。这样可以减少数据库查询次数,性能提升非常明显。

3.2 招聘管理:流程化与状态驱动

招聘模块本质上是一个状态机驱动的流程管理系统。我们设计了一个招聘需求表(recruitment_request)和一个候选人表(candidate)。

候选人有一个stage字段,表示当前所处阶段,如:“简历筛选”、“初试”、“复试”、“HR面试”、“发放Offer”、“已入职”、“已淘汰”。每个状态的变更,都通过一个独立的操作(如“安排面试”、“录入面试结果”、“发送Offer”)来触发,并记录操作人和时间到流程日志表。

关键实现:我们使用了枚举(Enum)来定义所有可能的状态和操作,并在服务层编写状态转换的逻辑。例如,从“初试”到“复试”,必须满足“初试结果”为“通过”且由有权限的人执行操作。这种设计保证了流程的规范性和数据的可追溯性。

3.3 考勤与薪酬核算:规则与计算的复杂性

这是业务逻辑最复杂的模块之一。

考勤子系统

  1. 规则定义:需要设计考勤规则表,定义上班时间、下班时间、迟到早退的容忍分钟数、是否弹性工作制等。
  2. 数据采集:与考勤机对接(通过TCP/IP或数据库直连),定时任务每天同步原始打卡记录到attendance_record表。
  3. 异常处理:系统根据规则自动判断每条记录的异常状态(正常、迟到、早退、缺卡)。对于“缺卡”,需要提供补卡申请流程,由上级审批后更新状态。
  4. 统计报表:按月统计每个员工的出勤天数、各种异常次数,为薪酬核算提供依据。

薪酬核算子系统

  1. 薪资结构:设计薪资项表(salary_item),如“基本工资”、“岗位津贴”、“绩效奖金”、“社保个人部分”、“个税”等,并标识其为“加项”或“减项”。
  2. 薪资模板:为每个岗位或员工绑定一个薪资模板,模板定义了其包含的薪资项及计算规则(固定值、公式计算等)。
  3. 核算流程:每月在固定时间点(如5号),系统启动核算任务。任务会:
    • 获取员工当月考勤统计结果。
    • 获取员工的薪资模板。
    • 根据模板中的公式(如基本工资 - (迟到次数 * 罚款单价))动态计算每个薪资项。
    • 调用税务计算接口(或内置规则)计算个人所得税。
    • 汇总所有加项和减项,生成最终应发工资和实发工资,存入payroll表。
  4. 审批与发放:生成的工资单需要经过财务或HR负责人审批确认后,才能锁定数据,并进入银行代发流程。

踩坑记录:薪资计算涉及钱,必须保证幂等性。即同一员工、同一月份,多次触发核算,结果必须一致且不会重复生成记录。我们的做法是在核算前检查payroll表是否已存在该员工该月的记录,如果存在且已锁定,则跳过或报错。计算过程所有用到的变量(如考勤数据)必须在计算开始时一次性取好,避免在计算过程中数据被修改。

3.4 绩效管理:目标与评估的闭环

我们采用相对常见的KPI(关键绩效指标)模式。

  1. 周期与模板:定义绩效周期(季度、年度),并为不同岗位设置绩效评估模板,模板中包含若干考核项(KPI)及其权重、评分标准。
  2. 目标设定(Goal Setting):周期初,员工与上级共同在系统中设定本期的绩效目标(关联到具体的KPI项)。
  3. 自评与上级评:周期末,员工先进行自评,然后上级进行评分,并填写评语。系统支持多级评审(如上级的上级确认)。
  4. 结果校准与反馈:HR或部门负责人可能对评分进行校准(如强制分布)。最终结果与员工确认后,归档并作为奖金、晋升的依据。

技术实现关键点:这个模块涉及大量的表单动态渲染和审批流。我们使用了一个工作流引擎的轻量级实现(也可以自己基于状态机设计),来驱动“目标制定->提交->自评->上级评->校准->确认”这个流程。每个节点的操作权限和可见字段都需要精细控制。

3.5 培训与发展与员工自助

  • 培训管理:管理培训课程、讲师、资源,员工可报名参加,系统记录培训结果。
  • 员工自助平台:这是提升员工体验的关键。员工可以在此查看自己的档案、工资条、考勤明细,提交请假、报销、补卡等各类申请,查看公司公告。这极大地减少了HR的日常事务性工作。

请假流程示例

  1. 员工填写请假单(类型、起止时间、事由)。
  2. 提交后,根据请假天数和公司规则,自动路由给相应审批人(如直接上级、部门总监)。
  3. 审批人收到待办任务(系统消息或邮件通知),进行审批。
  4. 审批通过后,状态更新,并自动同步到考勤预期数据中。
  5. 员工和HR均可查看流程状态。

4. 数据库设计与核心表结构解析

数据库设计是系统的骨架。这里列举几个最核心的表及其关联,让你感受一下设计思路。

表关系核心思想:员工(employee)是中心实体,通过外键与部门(department)、岗位(position)关联。各种业务单据(如请假单leave_application、报销单expense_report)都以employee_id关联到具体员工,并包含自己的业务状态和审批流信息。

部分核心表字段说明

表名关键字段说明与设计理由
employeeid,emp_no(工号),name,dept_id,position_id,hire_date,statusemp_no唯一索引。status用于软删除(0离职/1在职)。
departmentid,name,parent_id,leader_id(负责人)parent_id构建树形结构。leader_id关联employee.id
attendance_recordid,employee_id,check_date,check_in_time,check_out_time,status(employee_id, check_date)建立联合唯一索引,防止重复打卡记录。
leave_applicationid,employee_id,leave_type,start_time,end_time,duration,status,approver_idstatus跟踪流程(提交中/审批中/已批准/已驳回)。duration单位可以是天或小时,需统一。
payrollid,employee_id,pay_period(发薪月份),base_salary,bonus,tax,net_salary,is_confirmed(employee_id, pay_period)唯一索引,确保幂等性。is_confirmed标记是否已审批锁定。

索引策略

  • 主键:所有表使用自增整数id作为主键。
  • 外键索引:在所有作为外键的字段上建立索引,如employee.dept_id,leave_application.employee_id
  • 查询优化索引:在经常用于WHERE条件、ORDER BYJOIN的字段上建立索引。例如,attendance_record表的(employee_id, check_date)联合索引,对于查询某个员工某个月的考勤记录速度极快。
  • 唯一索引:保证业务唯一性的字段组合,如employee.emp_no,payroll.(employee_id, pay_period)

5. 权限系统设计与Shiro整合实战

没有权限控制的管理系统就是“裸奔”。我们采用经典的RBAC(基于角色的访问控制)模型:用户 -> 角色 -> 权限。

数据库设计

  • sys_user: 系统用户表,与employee表一对一或关联。
  • sys_role: 角色表。
  • sys_permission: 权限表,存储权限标识符(如employee:view,salary:approve)和对应的资源(菜单URL、按钮ID、API路径)。
  • user_role,role_permission: 用户-角色、角色-权限的关联表。

与Shiro整合的关键步骤

  1. 自定义Realm:这是Shiro的核心,你需要继承AuthorizingRealm类,重写doGetAuthenticationInfo(认证)和doGetAuthorizationInfo(授权)方法。在这里,你从数据库中查询用户的角色和权限信息,并封装成Shiro能识别的对象(如SimpleAuthenticationInfo,SimpleAuthorizationInfo)。
  2. 权限标识符:我们将权限细化为三级:模块:操作:资源,例如hrm:employee:view(查看员工)、hrm:salary:export(导出工资单)。这样在控制层方法或页面上,可以使用注解@RequiresPermissions("hrm:employee:view")进行声明式控制。
  3. 动态菜单:用户登录后,根据其拥有的权限标识符,过滤出有权限访问的菜单项,动态生成左侧导航菜单。这通常在前端或后端模板中完成。
  4. 按钮级控制:在JSP或Thymeleaf模板中,可以使用Shiro标签<shiro:hasPermission name="hrm:salary:export">来控制一个导出按钮是否显示。

注意事项:Shiro的权限缓存非常重要。每次授权都查数据库是无法接受的。务必在Realm中配置合理的缓存策略(如使用Ehcache或Redis)。同时,当用户的权限被管理员修改后,需要手动清除该用户的授权缓存,否则会出现权限更新延迟的问题。

6. 典型业务场景代码实现剖析

光讲理论不够,我们看两个典型业务的后端代码实现片段。

6.1 复杂条件分页查询员工列表

这是HR最常用的功能。前端传递一堆可能为空的查询条件(姓名、部门、状态等),后端需要动态拼接SQL。

Controller层

@RestController @RequestMapping("/api/employee") public class EmployeeController { @Autowired private EmployeeService employeeService; @GetMapping("/list") public PageResult<EmployeeVO> listEmployees(EmployeeQueryDTO queryDTO) { // queryDTO 包含了 pageNum, pageSize, name, deptId, status 等字段 return employeeService.queryEmployeeList(queryDTO); } }

Service层与Mapper动态SQL: 在EmployeeMapper.xml中,我们利用MyBatis的动态SQL功能:

<select id="selectEmployeeList" parameterType="EmployeeQueryDTO" resultMap="EmployeeResult"> SELECT e.*, d.name as dept_name, p.name as position_name FROM employee e LEFT JOIN department d ON e.dept_id = d.id LEFT JOIN position p ON e.position_id = p.id WHERE e.status != 'DELETED' <!-- 软删除过滤 --> <if test="name != null and name != ''"> AND e.name LIKE CONCAT('%', #{name}, '%') </if> <if test="deptId != null"> AND e.dept_id = #{deptId} </if> <if test="status != null and status != ''"> AND e.status = #{status} </if> <!-- 可以添加更多条件,如入职时间范围 --> <if test="hireDateStart != null"> AND e.hire_date >= #{hireDateStart} </if> <if test="hireDateEnd != null"> AND e.hire_date <= #{hireDateEnd} </if> ORDER BY e.id DESC </select>

PageHelper是一个常用的分页插件,在Service方法中调用PageHelper.startPage(queryDTO.getPageNum(), queryDTO.getPageSize())后,下一次的Mapper查询就会自动进行物理分页。

6.2 发起请假申请与审批流转

这是一个典型的工作流场景。

1. 创建请假单(Controller):

@PostMapping("/leave/apply") public Result applyLeave(@RequestBody LeaveApplicationDTO dto, @CurrentUser SysUser user) { // 1. 数据校验(时间是否合法、假期余额是否足够等) // 2. 设置申请人ID、初始状态为“提交中” dto.setApplicantId(user.getEmployeeId()); dto.setStatus(LeaveStatus.SUBMITTED.getCode()); // 3. 调用服务层 leaveApplicationService.createApplication(dto); return Result.success("请假申请提交成功,等待审批"); }

2. 服务层创建并启动流程(Service):

@Override @Transactional // 保证创建单据和启动流程在一个事务里 public void createApplication(LeaveApplicationDTO dto) { // 1. DTO 转 Entity,并保存到数据库 LeaveApplication entity = convertToEntity(dto); leaveApplicationMapper.insert(entity); // 2. 根据请假规则,确定审批人(可能是直接上级,或更高级别领导) Long approverId = findApproverByRule(entity.getApplicantId(), entity.getDuration()); // 3. 更新单据的当前审批人,状态改为“审批中” entity.setApproverId(approverId); entity.setStatus(LeaveStatus.UNDER_REVIEW.getCode()); updateById(entity); // 4. 发送通知(系统消息、邮件、钉钉/企业微信)给审批人 notificationService.sendLeaveApprovalNotification(approverId, entity.getId()); }

3. 审批操作(Service):

@Override @Transactional public void approveApplication(Long applicationId, Long approverId, boolean approved, String comment) { LeaveApplication application = getById(applicationId); // 1. 校验:单据是否存在、状态是否为“审批中”、当前用户是否为审批人 validateApprovalPermission(application, approverId); // 2. 更新单据状态和审批意见 if (approved) { application.setStatus(LeaveStatus.APPROVED.getCode()); // 可能触发后续动作:扣减假期余额、同步至考勤预期 deductLeaveBalance(application.getApplicantId(), application.getLeaveType(), application.getDuration()); syncToAttendance(application); } else { application.setStatus(LeaveStatus.REJECTED.getCode()); } application.setApprovalComment(comment); application.setApprovalTime(new Date()); updateById(application); // 3. 通知申请人审批结果 notificationService.sendLeaveResultNotification(application.getApplicantId(), approved); }

7. 部署、运维与性能优化经验谈

项目开发完,部署上线才是真正的开始。

7.1 部署实践

我们采用最经典的Linux + Nginx + Tomcat + MySQL架构。

  • Linux服务器:使用CentOS 7,配置好防火墙、时区、Java环境。
  • Nginx:作为反向代理和静态资源服务器。将前端的HTML/CSS/JS文件放在Nginx下,提高访问速度。同时,将/api/路径的请求代理到后端的Tomcat。Nginx还能轻松配置负载均衡、SSL证书(HTTPS)、压缩传输等。
  • Tomcat:使用Spring Boot内嵌的Tomcat,通过java -jar your-application.jar --server.port=8080启动。生产环境建议使用nohup或配置为systemd服务,保证进程稳定运行。
  • MySQL:单独部署在一台服务器上,定期进行备份(mysqldump + binlog)。务必修改默认端口和root密码,创建专属的、权限受限的数据库用户给应用使用。

7.2 性能优化点

  1. 数据库层面
    • 索引:如前所述,分析慢查询日志,为高频查询条件建立合适的索引。但也要避免过度索引,影响写性能。
    • SQL优化:避免SELECT *,只取需要的字段。多表关联时,注意关联字段是否有索引。复杂查询考虑是否能用子查询或JOIN优化。
    • 连接池:使用HikariCP或Druid等高性能连接池,并合理配置最大最小连接数、超时时间。
  2. 应用层面
    • 缓存:使用Redis缓存热点数据。例如,部门树、数据字典(如请假类型)、用户权限信息。注意设置合理的过期时间,并处理好缓存与数据库的一致性。
    • 异步处理:对于耗时的操作,如发送批量邮件、生成复杂的统计报表,不要阻塞主请求线程。可以使用Spring的@Async注解,或者集成消息队列(如RabbitMQ)进行异步解耦。
    • JVM调优:根据服务器内存大小,调整Spring Boot应用的JVM参数,如堆内存大小(-Xms,-Xmx)、垃圾回收器等。
  3. 前端层面
    • 压缩JS、CSS、图片资源。
    • 利用浏览器缓存。
    • 对于数据量大的表格,采用分页或虚拟滚动,不要一次性加载所有数据。

7.3 常见问题与排查技巧

  1. 应用启动失败,端口被占用

    • netstat -tlnp | grep 8080查找占用8080端口的进程。
    • kill -9 <PID>结束该进程,或修改应用的server.port配置。
  2. 数据库连接池耗尽

    • 现象:应用日志中出现Cannot get connection from pool或大量超时错误。
    • 排查:首先检查数据库服务是否正常,网络是否通畅。然后,检查应用连接池配置是否合理(最大连接数是否太小)。最后,通过Druid监控或日志,分析是否有SQL执行过慢导致连接被长时间占用,或者代码中是否有忘记关闭数据库连接的情况(如未在finally块中关闭ResultSet, Statement, Connection)。
  3. 前端页面访问慢

    • 打开浏览器开发者工具(F12)的Network面板,查看哪个资源加载耗时最长。
    • 如果是API接口慢,查看后端应用日志和数据库慢查询日志。
    • 如果是静态资源(如图片、JS)慢,检查Nginx配置和服务器带宽。
  4. 权限配置了但不生效

    • 检查Shiro的过滤器链配置是否正确,请求路径是否被正确拦截。
    • 检查自定义Realm中,用户的权限信息是否被正确查询和返回。
    • 最重要的一点:检查Shiro的权限缓存。修改用户权限后,尝试退出重新登录,或者手动清除该用户的授权缓存(securityManager.logout()或直接操作缓存管理器)。

这个项目做下来,最大的体会是,开发一个管理系统,技术实现只是基础,更重要的是对业务逻辑的理解和抽象能力。如何将HR繁杂的线下流程,精准地转化为清晰的数据模型和状态机,是成败的关键。代码的健壮性、异常处理、日志记录,这些在练手时容易忽略的细节,在生产环境中却至关重要。建议你在实现每个功能时,多思考边界情况:如果员工离职了,他的历史数据怎么处理?如果审批人不在岗,流程如何流转?这些思考,会让你的系统从一个“玩具”升级为一个真正可用的“工具”。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询