300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 【毕业设计】基于SpringBoot+Vue+ElementUI的人力资源管理系统源码

【毕业设计】基于SpringBoot+Vue+ElementUI的人力资源管理系统源码

时间:2021-11-30 23:30:13

相关推荐

【毕业设计】基于SpringBoot+Vue+ElementUI的人力资源管理系统源码

1、前言介绍

在如今这个人才需求量大的时代,各方企业为了永葆企业的活力与生机,在不断开 拓进取的同时,又广泛纳用人才,为企业的长久发展奠定了基础。于是,各个企业与部 门机构,都不可避免地会接触到人力资源管理的问题。

Hrm 是一款人力资源管理系统,其主要功能模块有员工个人信息修改、请假、员工 的薪资管理、考勤管理、社保管理。其中考勤管理实现了员工考勤状态的修改与员工考 勤月报表的导出,以及通过员工考勤记录的导入来实现员工考勤状态的判断。社保管理, 主要实现了员工社保的计算以及明细的修改。薪资管理,实现了员工工资的调整,以及 员工月工资报表的导出。 本项目采用了前后端分离的技术,前端是基于 Vue+ElementUI+Axios 开发的,后端 则是基于 Spring Boot+MyBatis Plus+ Jwt+Mysql。本项目实现了权限菜单管理,通过员 工的权限动态渲染菜单,并动态生成路由。通过 Jwt token 来判断当前登录的员工以及 员工的登录状态。

2、主要技术

3、系统设计

3.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 张表,负责对员工、部门、以及被上传文件的数据信息进人力资源管理系统行保存。

3.4.2、权限管理模块

权限管理模块主要涉及 5 张表,主要对菜单数据、角色数据进行保存。

3.4.3、考勤管理模块

员工考勤模块主要涉及 9 张表,主要对员工的考勤数据,以及各部门的考勤规则、 加班、请假、工作时间等规则信息的保存。

3.4.4、薪资管理模块

薪资管理模块主要涉及 7 张表,用于保存参保城市社保信息、员工每个的工资明细、 以及考勤扣款情况。

4、系统实现

4.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、个人信息编辑

此模块实现了员工个人信息的查看与修改,员工可以进行个人头像的修改。

编辑接口:

@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、修改密码

此模块完成了的员工个人密码的修改,若员工修改的密码与上一次密码项目,则提 示修改失败。

密码检查接口:

@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、首页图表展示

首页主要展示了当前员工的一些基本信息,以及个人在当月的考勤情况。另外显示 了系统的一些基本数据。

图表数据接口:

@GetMapping("/staff") public ResponseDTO getStaffData()

接口说明: 根据员工表的 create_time 字段,获取最近一年内的新增员工数。 统计数据接口:

@GetMapping("/count") public ResponseDTO getCountData()

接口说明: 统计员工总数以及状态正常的员工的数目。

核心代码:

getRegisterData().then(response => {if (response.code = 200) { const quarters = ['一季度', '二季度', '三季度', '四季度'] monOption.xAxis.data = quarters monOption.series.forEach(item => { item.data = response.data }) const registerChart = echarts.init(this.$refs.register); registerChart.setOption(monOption); const commonChart = echarts.init(this.$mon);commonChart.setOption(monOption); 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、标签栏页面跳转

通过点击菜单标签,可以完成页面的跳转,以及标签的删除。

核心代码:

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、多条件分页查询

选择不同条件,进行多条件分页查询

分页接口:

@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) { IPage pageConfig = new Page<>(current, size); QueryWrapper wrapper = 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()); } IPage page = 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); }

流程: 后端根据前端提交的查询数据,然后使用 MyBatis Plus 提供的分页方法进行分页, 然后将满足条件的数据返回给前端。

4.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, List roleIds) { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("staff_id",staffId); List list = 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); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("staff_id",staffId).eq("role_id",roleId);if(!saveOrUpdate(staffRole,queryWrapper)){ throw new ServiceException(500,"添加角色失败!"); } }return Response.success(); }

流程: 当员工点击分配菜单按钮时,前端会向后端请求所有的角色数据,紧接着再查询当 前员工所拥有的角色,并将对应的角色框勾选上。当员工选择好了角色并提交之后,后 端会先禁用不需要的角色,然后再添加或更新。

4.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, List menuIds) { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("role_id", roleId); List list = 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); QueryWrapper queryWrapper = 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、员工请假

当员工填写请假的基本信息,点击确定,完成了请假申请的提交。

请假接口:

@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); QueryWrapper queryWrapper = new QueryWrapper<>();queryWrapper.eq("staff_id", attendance.getStaffId()).eq("attendance_date", attendance.getAttendanceDate()); if (!this.attendanceService.saveOrUpdate(attendance, queryWrapper)) { return Response.error(); } } } } if (updateById(staffLeave)) { return Response.success(); } return Response.error(); }

流程: 当员工提交一个请假申请之后,申请处于未审核状态,如果员工拥有未被审核的请 假申请时,该员工是不能再次发起请假申请,另外员工也可以将未被审核的申请进行撤 销。当申请被管理员审批通过了之后,系统就会自动地将员工休假期间的考勤状态设置 为休假。

4.10、考勤数据导入

通过导入考勤数据,完成员工考勤状态的记录。

数据导入接口:

@PostMapping("/import") public ResponseDTO imp(MultipartFile file)

接口说明: 此处只需要导入考勤数据表,系统读取数据,来对员工的考勤状态进行判断。 核心代码:

@Transactional(rollbackFor = Exception.class) public ResponseDTO imp(MultipartFile file) throws IOException { InputStream inputStream = file.getInputStream();List list = 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); } QueryWrapper queryWrapper = 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、考勤月报表导出

通过汇总当月员工的考勤状况得到当月的员工考勤报表。

数据导出接口:

@GetMapping("/export/{month}") public ResponseDTO export(HttpServletResponse response, @PathVariable String month)

接口说明: 根据月份,导出相应月份的考勤数据。

核心代码:

public ResponseDTO export(HttpServletResponse response, String month) throws IOException { List list = 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)); List leaveDateList = 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、工资调整

通过修改基本工资、以及生活补贴、奖金完成员工工资的基本调整。

工资保存接口:

@PostMapping("/set") public ResponseDTO setSalary(@RequestBody Salary salary)

接口说明: 当员工的基本工资信息填写完毕之后,将员工的工资数据保存到数据库。

核心代码:

public ResponseDTO setSalary(Salary salary) { QueryWrapper query = 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、月工资报表导出

通过汇总当月员工的考勤状况、以及社保、基本工资,得到员工当月的工资报表。

报表导出接口:

@GetMapping("/export/{month}") public ResponseDTO export(HttpServletResponse response, @PathVariable String month)

接口说明: 如果不选择月份,默认导出当前月份的工资报表。

核心代码:

private void setSalaryInfo(String month, List list) {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); List leaveDateList = 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); QueryWrapper queryWrapper = 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、源码获取

/download/tyxjolin/87615754

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。