人力资源管理系统项目文档
前言:
完整项目(源码+详细报告文档+操作文档)获取方式(非免费)
*文末通过QQ/WX添加小编(记得注明来意)
*WX公众号:白芝麻糊 (往期文章/回复人力资源管理系统)
![](https://i-blog.csdnimg.cn/blog_migrate/773f3d7d98d7f5b745c893b2274afba1.png)
微信公众号:白芝麻糊 |
![](https://i-blog.csdnimg.cn/blog_migrate/2d85cdeaff70e3ecb740ea4fdf83362e.png) |
创作不易,支持一波,轻松就能带回家啦
如有疑问,亦可通过QQ/WX联系小编噢(记得注明来意哈
所有文档包括sql数据库、完整报告(PDF+docx)、操作说明(md)、考勤数据导入样例(xlsx)
详细文件如下:
![图片](https://i-blog.csdnimg.cn/blog_migrate/b8fcc2c389b5b3e2055f0ea4eae2f323.png)
![图片](https://i-blog.csdnimg.cn/blog_migrate/82ca06110019fb5e5f1852a81481914e.png)
人力资源管理系统视频演示
人力资源管理系统演示-CSDN直播
2024年1月
摘 要
在如今这个人才需求量大的时代,各方企业为了永葆企业的活力与生机,在不断开拓进取的同时,又广泛纳用人才,为企业的长久发展奠定了基础。于是,各个企业与部门机构,都不可避免地会接触到人力资源管理的问题。
Hrm 是一款人力资源管理系统,其主要功能模块有员工个人信息修改、请假、员工的薪资管理、考勤管理、社保管理。其中考勤管理实现了员工考勤状态的修改与员工考勤月报表的导出,以及通过员工考勤记录的导入来实现员工考勤状态的判断。社保管理,主要实现了员工社保的计算以及明细的修改。薪资管理,实现了员工工资的调整,以及员工月工资报表的导出。
本项目采用了前后端分离的技术,前端是基于Vue+ElementUI+Axios 开发的,后端则是基于SpringBoot+MyBatisPlus+ Jwt+Mysql。本项目实现了权限菜单管理,通过员 工的权限动态渲染菜单,并动态生成路由。通过Jwttoken来判断当前登录的员工以及员工的登录状态。
关键词:人力资源管理系统,Spring Boot ,Vue,MySQL,权限管理
Abstract
In today's era of large demand for talents, in order to maintain the vitality and vigor of
the enterprise, enterprises of all parties continue to forge ahead while recruiting talents
extensively, laying the foundation for the long-term development of the enterprise. Therefore,
various enterprises and departments will inevitably come into contact with the issue of human
resource management.
Hrm is a human resource management system. Its main functional modules include
employee personal information modification, leave, employee salary management, attendance
management, and social security management. Among them, attendance management realizes
the modification of staff attendance status and the export of staff attendance monthly report,
and the judgment of staff attendance status through the import of staff attendance records.
Social security management, mainly realizes the calculation of employee social security and
the modification of details. Salary management, realizes the adjustment of employee salary,
and the export of employee monthly salary report.
This project adopts the technology of separating front and back ends. The front end is
developed based on Vue+ElementUI+Axios, and the back end is based on Spring
Boot+MyBatis Plus+Jwt+Mysql. This project implements permission menu management,
dynamically renders menus through employee permissions, and dynamically generates routes.
The currently logged in employee and the employee's login status are judged by the Jwt token.
Key Words: hrm system, Spring Boot, Vue, permission management
目录
第1章绪论
1.1选题背景
1.2 选题目的
1.3 选题意义
第2 章关键技术介绍
2.1前端技术
2.1.1 Vue
2.1.2 Axios
2.2 后端技术
2.2.1 Spring Boot
2.2.2 MyBatis Plus
2.2.3 JWT
2.2.4Swagger
第3 章系统设计
3.1功能结构设计
3.2前端设计
3.2.1 前端接口封装
3.2.2 组件封装
3.2.3 动态路由
3.3后端设计
3.3.1 全局异常处理
3.3.2 数据传输对象
3.4数据库设计
3.4.1 系统管理模块
3.4.2 权限管理模块
3.4.3 考勤管理模块
3.5 系统类结构设计
3.5.1 员工模块
3.5.2 角色模块
3.5.3 菜单模块
3.5.4 文件模块
3.5.5 考勤模块
3.5.6 薪资模块
3.5.7 社保模块
第4 章系统实现
4.1登录
4.2个人信息编辑
4.3修改密码
4.4 首页图表展示
4.5标签栏页面跳转
4.6 多条件分页查询
4.7 角色分配
4.8菜单分配
4.9 员工请假
4.10 考勤数据导入
4.11 考勤月报表导出
4.12 工资调整
4.13 月工资报表导出
第5章总结
第1章绪论
1.1选题背景
人力资源管理是企业运营中必不可少的一环,它关系到企业的前途与发展。尤其对于中小微企业来说,对企业的发展有着举足轻重的作用。随着近年来,政府对创业项目的大力扶持,我国创业型企业蓬勃发展。据统计,2019 年,我国创业企业数量已达1810万余家,占全国企业数的97%,截止2020 年,我国创业企业数量达到了2030万,同比增长10%。虽然我国创业企业的基数在不断增大,但是能够长久存活的企业却少之又少。
在创业初期,随着企业初具规模,大多数创业者开始将主要精力集中在市场调研和开发产品上,而忽略了团队的内部管理。据调查,中国企业的平均寿命是7.02 年,但70%的企业存活不超过5 年,究其原因有很多,其中最重要的一点就是,人力资源管理未能有效推动企业向前发展。
1.2 选题目的
在传统人事信息管理的模式下,各岗位的人事信息往往是独立,且需要单独分配人员进行管理,提高了维护信息的成本。由于数据互不相通,所以在进行人事调动的时候往往做了重复的工作。通过开发一款人力资源管理系统,大大减少了企业人事管理的劳动力成本,运用大数据对人力资源进行精准调控和分配。
1.3 选题意义
随着计算机技术的不断进步和现代经济的不断发展,传统的管理技术已经不能满足企业的需要,人力资源管理系统越来越受到企业的重视。
人力资源管理是企业生存发展的关键,它可以改善和加强企业的管理,企业要想进行一项生产活动和做出一些远景规划,就应该重视人力资源的分配与规划作用。通过一系列的考核、激励、打卡制度,对员工的工作积极性和业绩进行考察。使得创业者能够 对企业的发展状况有更加细致的了解,并及时做出合理的调整,充分发挥员工的潜力。
无论是企业还是小型机构中,人力资源管理都是一个永远都离不开的话题,其对公司的发展是极其有意义的。
第2 章关键技术介绍
2.1前端技术
2.1.1 Vue
Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
2.1.2 Axios
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中,用于前端向后端发起请求,它拥有全局的请求和响应的拦截,可以非常方便的处理请求异常的问题。
2.2 后端技术
2.2.1 Spring Boot
Spring Boot是Spring 项目下的子项目,旨在快速开发应用,相比于Spring ,SpringBoot 避免了繁重的xml 配置,它还采用了约定优于配置的软件设计范式,并提供了大量开箱即用的依赖模块,并且通过少量的配置,就能快速的搭建项目。
2.2.2 MyBatis Plus
MyBatis是一款优秀的持久层框架,通过XML文件或注解配置来完成实体类与数 据库之间的映射,舍弃了传统的preparedStatement 设置参数操作数据库和使用resultSet获取结果集的过程。
MyBatisPlus是由苞米豆团队开发的一款MyBatis增强工具,为简化数据库操作,提高开发效率为生。在MyBatis的基础上提供了常用的crud方法,甚至不需要配置Mapper.xml 文件都能对数据库进行基础的操作。除此之外,MyBatis Plus 还提供了自动分页、代码生成的功能,通过配置相应的模板,就能一键生成绝大部分的后端代码,真正做到了简化开发。
2.2.3 JWT
JWT 全称JSON Web Token,是目前比较流行跨域验证方案。相比于session,session生成的用户数据都会保存在服务器端,服务器只给用户的返回一个sessionId,下次访问这个网站时,通过cookie将sessionId传递给服务器,从而得到相关的用户信息。毫无疑问,在这种情况下,服务器的内存会被大大的消耗,会带来一些性能开销。若服务器突然宕机,保存在服务器的用户数据就会消失,用户再次访问服务器就会被认为是第一次登录。
而JWT是保存在浏览器本地的,当用户第一次访问服务器,并且登录成功了,服 务器会根据用户的唯一标识信息(比如id),生成一个加密的token ,并返回用户信息。只要用户每次访问服务器的时候,在请求头中携带上token,后端的拦截器获取token,验证签证信息通过之后,就允许访问。
2.2.4Swagger
Swagger 是一款用于生成Api 接口文档的工具,通过简单的注解配置,就可以将后端人员编写的接口,以文档的方式呈现。同时它还拥有简单的在线测试功能,相当于一个小型的postman。通过Swagger文档,极大地减少了前后端人员的交流成本,将前后端正真的连接起来。
第3 章系统设计
3.1功能结构设计
本系统主要分四个模块,分别是系统管理和权限管理、薪资管理、考勤管理,系统管理主要用于日常事务管理管理,权限管理,用于控制员工的访问权限,薪资管理主要是对员工的五险一金以及社保数据的修改和添加,考勤管理主要是对员工的日常打卡进行记录和统计。
![图片](https://i-blog.csdnimg.cn/blog_migrate/7f7912426e830fa00b66377952830b8a.png)
图4.1 系统功能结构图
3.2前端设计
3.2.1 前端接口封装
本项目对Axios进行了全局的封装,对前端请求和后端响应进行了统一的拦截,并进行相应的处理。前端调用的Api 都封装在src/api 模块下,进行统一的管理。
3.2.2 组件封装
为了解决代码复用的问题,通过结合Element UI。本项目对form 表单和table 数据表进行了进一步的组件封装。
3.2.3 动态路由
本项目采用了基于后端权限菜单的来实现动态路由,为了保证菜单数据的全局共享,菜单数据使用vuex来保存。当员工访路由时,通过全局路由守卫进行拦截,并向后端请求该员工的菜单数据。
3.3后端设计
3.3.1 全局异常处理
为了高效地处理异常 , 本项目对异常进行了全局的统一处理 , 使用@ControllerAdvice 声明一个全局异常处理器,并通过继承RuntimeException实现一个异常类,在需要异常处理的地方抛出自定义的异常。
3.3.2 数据传输对象
本项目中,后端响应给前端的数据都统一封装在ResponseDTO中,然后前端通过解析得到ResponseDTO的json对象。通过定义全局的业务状态码枚举类。前端通过后端响应数据的状态码来判断业务处理是否异常。
3.4数据库设计
根据《阿里开发手册》,本项目的每张表都包含id、create_time、update_time 三个字段。项目统一采用逻辑删除,每张表都包括is_deleted字段。根据实际业务需求,项 目分为系统管理、权限管理、薪资管理、考勤管理四大模块。表前缀sys_(系统管理)、 per_(权限管理)、att_(考勤管理)、sal_(工资管理)、soc_(五险一金)。
3.4.1 系统管理模块
系统管理模块主要涉及3张表,负责对员工、部门、以及被上传文件的数据信息进行保存。
![图片](https://i-blog.csdnimg.cn/blog_migrate/a273ce0c5b8b97948907d84aefb72b85.jpeg)
图4.2 系统管理模块物理模型
员工表:
员工表包含了用户的基本信息,如电话、生日、地址等。
![图片](https://i-blog.csdnimg.cn/blog_migrate/8418fc0eae1aa0ec38d0e41af942f658.png)
图4.3 员工表
部门表:
用于存放部门数据,本项目中部门只分为两级,一级部门,二级部门。parent_id 代表父级部门id,若parent_id 为0 代表一级部门。
![图片](https://i-blog.csdnimg.cn/blog_migrate/1b4e956616ed6104c1136a9b22578585.png)
图4.4 部门表
文件表:
用于存放本地上传的文件信息,其中md5 代表文件的标识,若将要上传的文件已经存在于服务器上,则不用再上传。
![图片](https://i-blog.csdnimg.cn/blog_migrate/71599c1d4d0c4721dd6b96da79e19b8d.png)
图4.5 文件表
3.4.2 权限管理模块
权限管理模块主要涉及5张表,主要对菜单数据、角色数据进行保存。
![图片](https://i-blog.csdnimg.cn/blog_migrate/5fb6738153221d8a1fd03c19ab637a5d.jpeg)
图4.6 权限管理模块物理模型
角色表:
用于存放角色数据。
![图片](https://i-blog.csdnimg.cn/blog_migrate/47fdf44d6fd904a6275ef67609515817.png)
图4.7 角色表
菜单表:
用于存放菜单数据,本项目中菜单只分为两级,一级菜单,二级菜单。其中path代表路由,parent_id 代表父级菜单的id。如果为0,则代表是一级菜单。
![图片](https://i-blog.csdnimg.cn/blog_migrate/e07573040048c22238de6e001a09fb42.png)
图4.8 菜单表
3.4.3 考勤管理模块
员工考勤模块主要涉及9张表,主要对员工的考勤数据,以及各部门的考勤规则、加班、请假、工作时间等规则信息的保存。
人力资源管理系统
![图片](https://i-blog.csdnimg.cn/blog_migrate/3b8b89e83f70037e0dbbb517d732490e.jpeg)
图4.9 考勤管理模块物理模型
考勤表:
用于保存员工每天的上下班打卡时间,并记录当前的员工考勤状态。
![图片](https://i-blog.csdnimg.cn/blog_migrate/f170f7070f6cc2f75be7f6ef63d040ca.png)
图4.10 员工考勤表
部门上班时间表:
主要保存每个部门所规定的上下班时间,用于对员工工作日当天是否迟到和旷工等状态进行判定。
人力资源管理系统
![图片](https://i-blog.csdnimg.cn/blog_migrate/9f95107b0ac79302cc9ee2539b1ac2ce.png)
图4.11 部门上班时间表
员工请假表:
主要保存员工的一些请假申请的记录,以及请假申请的审核状态。
![图片](https://i-blog.csdnimg.cn/blog_migrate/fd0971a456aa59d5dd1e063b475bb373.png)
图4.12 员工请假表
3.4.4 薪资管理模块
薪资管理模块主要涉及7张表,用于保存参保城市社保信息、员工每个的工资明细、以及考勤扣款情况。
人力资源管理系统
![图片](https://i-blog.csdnimg.cn/blog_migrate/a4e7d36b4226622b8499a6337ff9d72e.jpeg)
图4.13 薪资管理模块物理模型
工资表:
工资表主要保存每个员工每个月的工资明细,以及考勤的罚款情况。
![图片](https://i-blog.csdnimg.cn/blog_migrate/21cffdd71cf9f8a00dfcb7381ddd2727.png)
图4.14 工资表
城市社保表:
社保城市表详细记录了当地的社保的各个项目以及公积金的缴费比例。
人力资源管理系统
![图片](https://i-blog.csdnimg.cn/blog_migrate/3c0152ec838c24a6bd1dd0cd9be77336.png)
图4.15 城市社保表
员工社保表:
员工社保表保存了员工社保以及公积金的缴纳费用和明细,也包括了企业为员工缴
纳部分的明细和金额。
![图片](https://i-blog.csdnimg.cn/blog_migrate/73f102552744ea89583220275a01be6d.png)
图4.16 员工社保表
3.5 系统类结构设计
为了统一处理后端给前端的响应,本项目中后端传递给前端的数据都封装在ResponseDTO中。
3.5.1 员工模块
StaffService 主要负责对员工进行操作,而StaffRoleService用于处理员工与角色的关联业务,如获取员工角色,为员工设置角色。
![图片](https://i-blog.csdnimg.cn/blog_migrate/3a5b1873240779c3a8e7d9fe6ac167ad.jpeg)
图4.17 员工模块类图
3.5.2 角色模块
RoleService 主要负责对角色进行操作,而RoleMenuService 用于处理角色与菜单的关联业务,如获取角色所分配的菜单,为角色设置菜单。
人力资源管理系统
![图片](https://i-blog.csdnimg.cn/blog_migrate/8c2962881d7d469b5e660399d0b60f82.jpeg)
图4.18 角色模块类图
3.5.3 菜单模块
MenuService 用于处理菜单业务,Menu实体类中的parentId 标识菜单的父级菜单,children 代表当前菜单的子菜单。
![图片](https://i-blog.csdnimg.cn/blog_migrate/9d3e16af4834a103fce3a4183692d678.jpeg)
图4.19 菜单模块类图
3.5.4 文件模块
DocsService 用于处理文件业务,其中upload、download 分别为文件的上传与下载,imp、download 分别为数据的导入与导出。
![图片](https://i-blog.csdnimg.cn/blog_migrate/9fdf169b56ad7062407c1b14480963a9.jpeg)
图4.20 文件模块类图
3.5.5 考勤模块
考勤模块主要完成员工考勤状态的统计,以及月考勤报表的生成。通过导入员工的打卡时间表,完成对员工考勤状态的一个考量。
![图片](https://i-blog.csdnimg.cn/blog_migrate/76736a6bcb3c9b103ea0c7db31a1ca3c.jpeg)
图4.21 考勤模块类图
3.5.6 薪资模块
薪资模块主要完成员工工资的调整,以及员工工资月报表的导出。
![图片](https://i-blog.csdnimg.cn/blog_migrate/a5dee7caa979991e9480c864ef15f71d.jpeg)
图4.22 薪资模块类图
3.5.7 社保模块
社保模块主要是对参保地社保以及公积金的缴费税率的修改以及调整,还有个人社保信息的调整。
![图片](https://i-blog.csdnimg.cn/blog_migrate/af718d71e5650733510d03ca5a9a6191.jpeg)
图4.23 社保模块类图
第4 章系统实现
4.1登录
此模块完成了员工的登录功能,员工通过工号和密码进行登录。若员工状态异常则无法登录。
![图片](https://i-blog.csdnimg.cn/blog_migrate/eafe7556a255cc5ff24cfb4005e127da.png)
图5.1 登录页面
登录接口:
@PostMapping("/login") public ResponseDTO login(@RequestBody Staff staff) |
接口说明:
当员工填写好登录账号和密码之后,前端会提交登录表单。后端接收到数据之后,会进行数据库查询,并将最终的查询结果以状态码的形式封装在ResponseDTO之中,并返回到前端。
核心代码:
public ResponseDTO login(Staff staff) { String password = MD5Util.MD55(staff.getPassword()); StaffDeptVO staffDeptVO = this.staffMapper.findStaffInfo( staff.getCode(), password); if (staffDeptVO != null) { // 验证用户状态 if (staffDeptVO.getStatus() == 1) { String token = JWTUtil.generateToken(staffDeptVO.getId(),password); return Response.success(staffDeptVO, token); //返回员工信息和 token } return Response.error(BusinessStatusEnum.STAFF_STATUS_ERROR); } return Response.error("用户名或密码错误!"); } |
登录流程:
首先通过前端传递的账号和密码进行员工查询,之后对查询到的员工进行状态判断,如果状态正常,则将用户的信息和生成的token返回给前端 ,前端将token保存在localstorage中。
4.2个人信息编辑
此模块实现了员工个人信息的查看与修改,员工可以进行个人头像的修改。
![图片](https://i-blog.csdnimg.cn/blog_migrate/ecb0ab9b03b8ae29979ba521d5dacc90.png)
图5.2 个人信息编辑页面
编辑接口:
@PutMapping public ResponseDTO edit(@RequestBody Staff staff) |
接口说明:
员工填写的信息会通过el-form组件的data属性,将数据绑定到一个json 对象中,并通过put提交,最后后端接收数据,并完成相应员工信息的更新。
核心代码:
public ResponseDTO edit(Staff staff) { if (updateById(staff)) { return Response.success(); } return Response.error(); } |
流程:
员工填写的信息会通过el-form组件的data属性,将数据绑定到一个json 对象中,
并通过put提交,最后后端接收数据,并完成相应员工信息的更新。
4.3修改密码
此模块完成了的员工个人密码的修改,若员工修改的密码与上一次密码项目,则提示修改失败。
![图片](https://i-blog.csdnimg.cn/blog_migrate/fd976d062ed61cdf74bda7fc192d01a9.png)
图5.3 密码修改
密码检查接口:
@GetMapping("/check/{pwd}/{id}") public ResponseDTO checkPassword(@PathVariable String pwd, @PathVariable Integer id) |
接口说明:
通过前端传递的密码和id,来判断员工填写的密码是否正确。
密码修改接口:
@PutMapping("/pwd") public ResponseDTO updatePassword(@RequestBody Staff staff) |
接口说明:
为了保证密码的安全性,使用put提交。
核心代码:
public ResponseDTO checkPassword(String pwd, Integer id) { Staff staff = getById(id); if(staff != null) { if (StrUtil.isNotBlank(pwd)) { if (MD5Util.MD55(pwd).equals(staff.getPassword())) { return Response.success(); } throw new ServiceException(500,"密码错误!"); } throw new ServiceException(500,"密码不能为空!"); } throw new ServiceException(500,"此员工不存在!"); }
public ResponseDTO updatePassword(Staff staff) { staff.setPassword(MD5Util.MD55(staff.getPassword())); if(updateById(staff)){ return Response.success(); } return Response.error(); } |
流程:
当员工打开修改密码的对话框,员工需要先正确填写原来的密码,然后填入将要修改的密码。当你修改的密码与原来的密码相同时,将会提示不能使用原来的密码,并且修改密码失败。密码修改成功之后,会自动退出登录,需要重新登录。
4.4 首页图表展示
首页主要展示了当前员工的一些基本信息,以及个人在当月的考勤情况。另外显示
了系统的一些基本数据。
![图片](https://i-blog.csdnimg.cn/blog_migrate/57145bc8967671cfb6a73edcee433272.png)
图5.4 首页
图表数据接口:
@GetMapping("/staff") public ResponseDTO getStaffData() |
接口说明:
根据员工表的create_time 字段,获取最近一年内的新增员工数。
统计数据接口:
@GetMapping("/count") public ResponseDTO getCountData() |
接口说明:
统计员工总数以及状态正常的员工的数目。
核心代码:
getRegisterData().then(response => { if (response.code = 200) { const quarters = ['一季度', '二季度', '三季度', ' 四季度'] this.commonOption.xAxis.data = quarters this.commonOption.series.forEach(item => { item.data = response.data }) const registerChart = echarts.init(this.$refs.register); registerChart.setOption(this.commonOption); const commonChart = echarts.init(this.$refs.common); commonChart.setOption(this.commonOption); this.pieOption.series.forEach(item => { item.data = quarters.map((q, i) => ({ name: q, value: response.data[i] })) }) const pieChart = echarts.init(this.$refs.pie); pieChart.setOption(this.pieOption);
} else { this.$message.error("获取数据失败!") } }) |
流程:
当前端获取后端提供的数据之后,对数据项进行配置,然后将初始化好的图表挂载
到dom元素节点上。
4.5标签栏页面跳转
通过点击菜单标签,可以完成页面的跳转,以及标签的删除。
![图片](https://i-blog.csdnimg.cn/blog_migrate/6a0ffd9ac2dd21add84209da974200b8.jpeg)
图5.5 标签栏
核心代码:
ADD_TAG(state, menu) { if (menu.code !== 'home') { const result = state.tagList.findIndex( item => item.code === menu.code ) if (result === - 1) { state.tagList.push(menu) } } localStorage.removeItem("tagList") localStorage.setItem("tagList", JSON.stringify(state.tagList)) }, CLOSE_TAG(state, menu) { state.tagList = state.tagList.filter(item => item.code !== menu.code) localStorage.removeItem("tagList") localStorage.setItem("tagList", JSON.stringify(state.tagList)) } |
流程:
当每点击一次侧边栏的菜单项,就将菜单数据存储在vuex中。而且为了保证页面 刷新之后,标签栏不会消失,又将菜单数据保存在localstorage中。标签栏由vuex中存储的菜单数据动态生成。
4.6 多条件分页查询
选择不同条件,进行多条件分页查询
![图片](https://i-blog.csdnimg.cn/blog_migrate/aafb1e57bbc28d081ac0b662bfaa0768.jpeg)
图5.6 多条件分页查询
分页接口:
@PostMapping("/page") public ResponseDTO list(@RequestParam(defaultValue = " 1") Integer current, @RequestParam(defaultValue = " 10") Integer size, @RequestBody Staff staff) |
接口说明:
current 代表第几页,size 每次页面所展示的数据的个数,staff包含了多条件查询的条件。
核心代码:
public ResponseDTO list(Integer current, Integer size, Staff staff) { IPagepageConfig = new Page<>(current, size); QueryWrapperwrapper = new QueryWrapper<>(); if (staff.getName() != "" && staff.getName() != null) { wrapper.like("name", staff.getName()); } if (staff.getBirthday() != null) { wrapper.ge("birthday", staff.getBirthday()); } if (staff.getDeptId() != null) { wrapper.eq("dept_id", staff.getDeptId()); } if (staff.getStatus() != null) { wrapper.eq("status", staff.getStatus()); } IPagepage = page(pageConfig, wrapper); Map map = new HashMap(); map.put("pages", page.getPages()); map.put("total", page.getTotal()); map.put("list", page.getRecords()); return Response.success(map); } |
流程:
后端根据前端提交的查询数据,然后使用MyBatisPlus提供的分页方法进行分页,
然后将满足条件的数据返回给前端。
4.7 角色分配
此模块主要实现了为员工分配角色,一个员工可以分配多个角色。![](https://i-blog.csdnimg.cn/blog_migrate/c680b669d70193257bc2b6e1acf4de16.png)
图5.7 角色分配
角色接口:
@GetMapping("/all") public ResponseDTO findAll() |
接口说明:
获取所有角色。
员工角色接口:
@GetMapping("/role/{staffId}") public ResponseDTO getRole(@PathVariable Integer staffId) |
接口说明:
获取目前员工已拥有的角色。
设置员工角色接口:
@PostMapping("/role/{staffId}") public ResponseDTO setRole(@PathVariable Integer staffId, @RequestBody List roleIds) |
接口说明:
员工可以选择多个角色,根据员工的id 和选择的角色id,为员工设置角色。
核心代码:
public ResponseDTO setRole(Integer staffId, ListroleIds) { QueryWrapperwrapper = new QueryWrapper<>(); wrapper.eq("staff_id",staffId); Listlist = list(wrapper); for (StaffRole staffRole : list) { if (roleIds.contains(staffRole.getRoleId())){ staffRole.setStatus(1); }else{ staffRole.setStatus(0); } updateById(staffRole); } for (Integer roleId : roleIds) { StaffRole staffRole = new StaffRole(); staffRole.setStaffId(staffId); staffRole.setRoleId(roleId); staffRole.setStatus(1); QueryWrapperqueryWrapper = new QueryWrapper<>(); queryWrapper.eq("staff_id",staffId).eq("role_id",roleId); if(!saveOrUpdate(staffRole,queryWrapper)){ throw new ServiceException(500,"添加角色失败!"); } } return Response.success(); } |
流程:
当员工点击分配菜单按钮时,前端会向后端请求所有的角色数据,紧接着再查询当前员工所拥有的角色,并将对应的角色框勾选上。当员工选择好了角色并提交之后,后
端会先禁用不需要的角色,然后再添加或更新。
4.8菜单分配
此模块实现了为角色分配菜单,一个角色可以选择多个菜单。
![图片](https://i-blog.csdnimg.cn/blog_migrate/3c67c54258b743940e30714de9d0d845.jpeg)
图5.8 分配菜单
菜单接口:
@GetMapping("/all") public ResponseDTO findAll() |
接口说明:
获取所有的菜单数据。
角色菜单接口:
@GetMapping("/menu/{roleId}") public ResponseDTO getMenu(@PathVariable Integer roleId) |
接口说明:
获取角色的菜单数据。
设置角色菜单接口:
@PostMapping("/menu/{roleId}") public ResponseDTO setMenu(@PathVariable Integer roleId, @RequestBody List menuIds) |
接口说明:
根据角色id,和选择的菜单id,为角色设置菜单。
核心代码:
public ResponseDTO setMenu(Integer roleId, ListmenuIds) { QueryWrapperwrapper = new QueryWrapper<>(); wrapper.eq("role_id", roleId); Listlist = list(wrapper); for (RoleMenu roleMenu : list) { if (menuIds.contains(roleMenu.getMenuId())) { roleMenu.setStatus(1); } else { roleMenu.setStatus(0); } updateById(roleMenu); } for (Integer menuId : menuIds) { RoleMenu roleMenu = new RoleMenu(); roleMenu.setRoleId(roleId); roleMenu.setMenuId(menuId); roleMenu.setStatus(1); QueryWrapperqueryWrapper = new QueryWrapper(); queryWrapper.eq("role_id", roleId).eq("menu_id", menuId); if (!saveOrUpdate(roleMenu, queryWrapper)) { throw new ServiceException(500, "角色添加菜单失败!"); } } return Response.success(); } |
流程:
当点击分配菜单按钮,前端会向后端请求所有的菜单数据,这里只返回了父级菜单,因为子菜单都作为了父级菜单的children 属性被携带。当菜单被渲染好之后,紧接着获取当前角色的所有拥有的菜单,并将对应项勾选。提交之后,后端先将不需要的菜单禁
用,然后再重新更新或设置菜单。
4.9 员工请假
当员工填写请假的基本信息,点击确定,完成了请假申请的提交。
![图片](https://i-blog.csdnimg.cn/blog_migrate/1a1a19779ff9295323b1d57575b47eab.jpeg)
图5.9 员工请假
![图片](https://i-blog.csdnimg.cn/blog_migrate/909842f4f270ba0c3d6cdd1c1d20bde9.jpeg)
图5.10 请假记录
请假接口:
@PostMapping public ResponseDTO add(@RequestBody StaffLeave staffLeave) |
接口说明:
新增一个请假申请
请假记录接口:
@GetMapping("/staff") public ResponseDTO findByStaffId( @RequestParam(defaultValue = " 1") Integer current, @RequestParam(defaultValue = " 10") Integer size, Integer id) |
接口说明:
获取员工的请假记录
请假内容更新接口:
@PutMapping public ResponseDTO edit(@RequestBody StaffLeave staffLeave) |
接口说明:
如果请假申请审批通过,那么就将休假日的考勤状态设置为休假状态
核心代码:
public ResponseDTO edit(StaffLeave staffLeave) { if (staffLeave.getStatus() == AuditStatusEnum.APPROVE) { for (int i = 0; i < staffLeave.getDays(); i++) { Date attendanceDate = DateUtil.offsetDay( staffLeave.getStartDate(), i).toSqlDate(); if (!DateUtil.isWeekend(attendanceDate)) { Attendance attendance = new Attendance().setAttendanceDate(attendanceDate).setStaffId( staffLeave.getStaffId()).setStatus (AttendanceStatusEnum.LEAVE); QueryWrapperqueryWrapper = new QueryWrapper<>(); queryWrapper.eq("staff_id", attendance.getStaffId()
if (!this.attendanceService.saveOrUpdate(attendance, queryWrapper)) { return Response.error(); } } } } if (updateById(staffLeave)) { return Response.success(); } return Response.error(); } |
流程:
当员工提交一个请假申请之后,申请处于未审核状态,如果员工拥有未被审核的请假申请时,该员工是不能再次发起请假申请,另外员工也可以将未被审核的申请进行撤销。当申请被管理员审批通过了之后,系统就会自动地将员工休假期间的考勤状态设置为休假。
4.10 考勤数据导入
通过导入考勤数据,完成员工考勤状态的记录。
![图片](https://i-blog.csdnimg.cn/blog_migrate/4258310e77f83d04384485cc2e4ae86f.png)
图5.11 员工考勤页面![](https://i-blog.csdnimg.cn/blog_migrate/26d2d1091cc5c729942ca3ae737814e4.png)
图5.12 考勤数据导入模板
数据导入接口:
@PostMapping("/import") public ResponseDTO imp(MultipartFile file) |
接口说明:
此处只需要导入考勤数据表,系统读取数据,来对员工的考勤状态进行判断。
核心代码:
@Transactional(rollbackFor = Exception.class) public ResponseDTO imp(MultipartFile file) throws IOException { InputStream inputStream = file.getInputStream(); |
Listlist = HutoolExcelUtil.readExcel(inputStream, 1, Attendance.class); for (Attendance attendance : list) { // 判断是否是周末,如果是周末就不用记录考勤情况,如果不是周末就判断员 工是否请假 if (attendance.getStaffId() == null || attendance.getAttendanceDate() == null || DateUtil.isWeekend(attendance.getAttendanceDate()) || isLeave(attendance)) { continue; } else { WorkTime workTime = this.workTimeMapper.findDeptWorkTimeByStaffId(attendance.getStaffId()); if (isAbsenteeism(attendance, workTime)) { attendance.setStatus(AttendanceStatusEnum.ABSENTEEISM); } else if (isLate(attendance, workTime)) { attendance.setStatus(AttendanceStatusEnum.LATE); } else if (isLeaveEarly(attendance, workTime)) { attendance.setStatus(AttendanceStatusEnum.LEAVE_EARLY); } else { attendance.setStatus(AttendanceStatusEnum.NORMAL); } QueryWrapperqueryWrapper = new QueryWrapper<>(); queryWrapper.eq("staff_id", attendance.getStaffId()).eq("attendance_date", attendance.getAttendanceDate()); if (!saveOrUpdate(attendance, queryWrapper)) { throw new ServiceException(BusinessStatusEnum.DATA_IMPORT_ERROR); } } } return Response.success(); } |
流程:
当系统将考勤数据读取了之后,会根据员工的id 获取员工所在部门的上班考勤时间,然后将员工的打卡时间与部门规定的上班时间进行比对,若员工的四个打卡时间缺少一 个就认定为旷工,如果员工既迟到又早退也视为旷工。判定出员工相应的考勤状态之后,就将考勤状态更新到数据库。
4.11 考勤月报表导出
通过汇总当月员工的考勤状况得到当月的员工考勤报表。
![图片](https://i-blog.csdnimg.cn/blog_migrate/9825fd298d6978e96929f871e633321f.jpeg)
图5.13 月考勤报表导出
![图片](https://i-blog.csdnimg.cn/blog_migrate/42383a4617d8ba37d01e1a2377e18528.jpeg)
图5.14 月考勤报表
数据导出接口:
@GetMapping("/export/{month}") public ResponseDTO export(HttpServletResponse response, @PathVariable String month) |
接口说明:
根据月份,导出相应月份的考勤数据。
核心代码:
public ResponseDTO export(HttpServletResponse response, String month) throws IOException { Listlist = this.staffMapper.findAttendanceMonthVO(); for (AttendanceMonthVO attendanceMonthVO : list) {
attendanceMonthVO.setLateTimes( this.attendanceMapper.countTimes(attendanceMonthVO. getStaffId(), AttendanceStatusEnum.LATE.getCode(), month));
attendanceMonthVO.setLeaveEarlyTimes( this.attendanceMapper.countTimes(attendanceMon thVO.getStaffId(), AttendanceStatusEnum.LEAVE_EARLY.getCode(), month));
attendanceMonthVO.setAbsenteeismTimes( this.attendanceMapper.countTimes(attendanceMo nthVO.getStaffId(), AttendanceStatusEnum.ABSENTEEISM.getCode(), month)); ListleaveDateList = this.attendanceMapper.findLeaveDate(attendanceMonthVO.getStaffId(), AttendanceStatusEnum.LEAVE.getCode(), month); int count = 0; for (Date date : leaveDateList) { if (!DateUtil.isWeekend(date)) { count++; } } attendanceMonthVO.setLeaveDays(count); } String yearMonth = month.substring(0, 4) + "年" + month.substring(4) + "月"; HutoolExcelUtil.writeExcel(response, list, yearMonth + "考勤报表", AttendanceMonthVO.class); return Response.success(); } |
流程:
首先获取所有员工的基本数据信息,然后根据员工id 和月份查询员工在此月份迟到、早退、旷工的次数和休假的天数,此处休假的天数不包含周末。
4.12 工资调整
通过修改基本工资、以及生活补贴、奖金完成员工工资的基本调整。
![图片](https://i-blog.csdnimg.cn/blog_migrate/b06b47243cd3189be75c06fb9d6f1630.jpeg)
图5.15 工资调整界面
工资保存接口:
@PostMapping("/set") public ResponseDTO setSalary(@RequestBody Salary salary) |
接口说明:
当员工的基本工资信息填写完毕之后,将员工的工资数据保存到数据库。
核心代码:
public ResponseDTO setSalary(Salary salary) { QueryWrapperquery = new QueryWrapper<>(); query.eq("month", salary.getMonth()).eq("staff_id", salary.getStaffId()); if (saveOrUpdate(salary, query)) { return Response.success(); } return Response.error(); } |
流程:
当员工工资信息被提交之后,首先根据月份和员工id 查询是否有当前员工的工资信
息,如果没有就添加,有则更新。
4.13 月工资报表导出
通过汇总当月员工的考勤状况、以及社保、基本工资,得到员工当月的工资报表。
![图片](https://i-blog.csdnimg.cn/blog_migrate/93d3790450353fc66d32acee7a4662ad.jpeg)
图5.16 月工资报表导出![](https://i-blog.csdnimg.cn/blog_migrate/209bcf280cddd344df54d0bfb3caab91.png)
图5.17 月工资报表
报表导出接口:
@GetMapping("/export/{month}") public ResponseDTO export(HttpServletResponse response, @PathVariable String month) |
接口说明:
如果不选择月份,默认导出当前月份的工资报表。
核心代码:
private void setSalaryInfo(String month, Listlist) { |
for (StaffSalaryVO staffSalaryVO : list) { BigDecimal lateDeduct = BigDecimal.valueOf(this.attendanceMapper.countTimes( staffSalaryVO.getStaffId(), AttendanceStatusEnum.LATE.getCode(), month) * 50); staffSalaryVO.setLateDeduct(lateDeduct); BigDecimal leaveEarlyDeduct = BigDecimal.valueOf(this.attendanceMapper.countTimes( staffSalaryVO.getStaffId(), AttendanceStatusEnum.LEAVE_EARLY.getCode(), month) * 50); staffSalaryVO.setLeaveEarlyDeduct(leaveEarlyDeduct); BigDecimal absenteeismDeduct = BigDecimal.valueOf(this.attendanceMapper.countTimes( staffSalaryVO.getStaffId(), AttendanceStatusEnum.ABSENTEEISM.getCode(), month) * 100); staffSalaryVO.setAbsenteeismDeduct(absenteeismDeduct); ListleaveDateList = this.attendanceMapper.findLeaveDate(staffSalaryVO.getStaffId(), AttendanceStatusEnum.LEAVE.getCode(), month); int count = 0; for (Date date : leaveDateList) { if (!DateUtil.isWeekend(date)) { count++; } } BigDecimal leaveDeduct = (BigDecimal.valueOf(count * 80)); staffSalaryVO.setLeaveDeduct(leaveDeduct); QueryWrapperqueryWrapper = new QueryWrapper<>(); queryWrapper.eq("staff_id", staffSalaryVO.getStaffId()).eq("month", month); Salary one = getOne(queryWrapper); if (one != null) { staffSalaryVO.setBaseSalary(one.getBaseSalary()).setSubsidy( one.getSubsidy()) .setBonus(one.getBonus()).setRemark(one.getRemark()) .setTotalSalary(one.getBaseSalary() .add(one.getBonus()) .add(one.getSubsidy()) .subtract(lateDeduct) .subtract(leaveEarlyDeduct) .subtract(absenteeismDeduct) .subtract(leaveDeduct) .subtract(staffSalaryVO.getSocialPay()) . subtract(staffSalaryVO.getHousePay())); } } } |
流程:
当进行数据导出时,系统首先统计员工当前月的迟到、早退、旷工的次数和休假的天数,再根据罚款规则得到相应的扣款金额。
第5章总结
此项目虽完成了人力资源管理的基础功能,但由于个人原因,仍存在一些设计缺陷。
以下是对项目的一些总结。
比如在统计员工罚款时,之前打算的是通过查询员工所对应的部门的罚款规定,来确定最终的罚款金额。但是,实际上实现起来有些难度,比如需要统计员工迟到或早退多少分钟,以及多少次。之后,到了项目开发后期,考虑到员工的考勤状态一天只显示一种,所以说员工罚款的最终金额就通过迟到、早退等等的次数来决定。于是就简单的规定了迟到和早退罚款50 元/次,休假(这里不包括周末)罚款80 元/次,旷工罚款100元/次的罚款规则。
为了判断了员工是否迟到和早退,为此每个部门都设计了一个上下班时间,以此作为判断依据。因为sys_dept与att_work_time中的记录是一一对应的,所以部门的上下班时间应该设计在sys_dept 表中,不应该分开。
请假模块的数据表也存在一些问题,在设计之初考虑到请假类型有可能会扩展,于是单独设计了att_leave_type表来存放了一些请假类型。后来发现,其实可以直接在att_leave中多设计一个type 字段,用不同数字来对应不同的请假类型。另外为了希望部门可以对员工请假的类型进行限制,于是就在att_leave 表中设计了一个status 字段,用来表示部门是否启用某种请假类型。比如探亲假被禁用了,员工就不能请探亲假。后来,又添加了一个请假审批功能。发现其实大可不必使用禁用请假类型的字段,可以在请假审批处,最终通过审批人员来决定员工请假是否通过。此外还有罚款、加班模块的数据表都存在类似的表多余的情况。
另外,项目还有许多可改进的地方。比如员工请假,这里只设计了一个审批人。其 实后期可以通过结合工作流,添加多个审批人。在考勤表现处,可以对项目进行完善以 显示员工当天的一些详细情况。比如员工考勤状态是休假,就可以显示员工当天的一些 请假的详细信息。在路由设计时,起初设计了一个404页面,希望在员工登录成功之后。如果访问未被授权的路由以及不存在的路由时,就显示404页面。很可惜,尝试了很多次都失败了。
由于个人原因,项目的加班模块还未完成。综合以上几点,项目后期开发感到乏力的主要原因,是因为数据库的设计问题。之后打算对数据库重新进行设计,优化项目。
需要完整项目的可以通过以下方式获取
公众号:白芝麻糊 |
![](https://i-blog.csdnimg.cn/blog_migrate/98f1158cd5164b256a9602272be0c6a0.png) |
QQ | 微信 |
![](https://i-blog.csdnimg.cn/blog_migrate/ee6fca4d6b838440a3b7b02a6d0116db.jpeg) | ![](https://i-blog.csdnimg.cn/blog_migrate/a7efbe4a3c2c46eea6bbb131f3c410a6.jpeg) |