| | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeEducationSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeEmergencyContactSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeImportExcelVO; |
| | |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeSimpleRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeWorkHistorySaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeContractDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeEducationDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeEmergencyContactDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeWorkHistoryDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeContractMapper; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeEducationMapper; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeEmergencyContactMapper; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeMapper; |
| | |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.permission.RoleApi; |
| | | import cn.iocoder.yudao.module.system.api.permission.dto.RoleRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserCreateReqDTO; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserUpdateReqDTO; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | |
| | | @Resource |
| | | private HrmEmployeeMapper employeeMapper; |
| | | @Resource |
| | | private HrmEmployeeContractMapper contractMapper; |
| | | @Resource |
| | | private HrmEmployeeEducationMapper educationMapper; |
| | | @Resource |
| | | private HrmEmployeeWorkHistoryMapper workHistoryMapper; |
| | |
| | | @Resource |
| | | private RoleApi roleApi; |
| | | @Resource |
| | | private StorageAttachmentApi storageAttachmentApi; |
| | | @Resource |
| | | @Lazy // 自注入需延迟加载,否则 bean 被代理包装后注入原始版本导致启动失败 |
| | | private HrmEmployeeService self; |
| | | |
| | |
| | | // 1. 校验部门 |
| | | deptApi.validateDeptList(Collections.singleton(createReqVO.getDeptId())); |
| | | // 2. 校验岗位 |
| | | postApi.validPostList(Collections.singleton(createReqVO.getPostId())); |
| | | postApi.validPostList(createReqVO.getPostIds()); |
| | | // 3. 校验手机号是否已存在 |
| | | if (employeeMapper.selectByPhone(createReqVO.getPhone()) != null) { |
| | | throw exception(EMPLOYEE_PHONE_EXISTS); |
| | |
| | | userCreateReqDTO.setUsername(createReqVO.getPhone()); |
| | | userCreateReqDTO.setNickname(createReqVO.getName()); |
| | | userCreateReqDTO.setDeptId(createReqVO.getDeptId()); |
| | | userCreateReqDTO.setPostIds(Collections.singleton(createReqVO.getPostId())); |
| | | userCreateReqDTO.setPostIds(new LinkedHashSet<>(createReqVO.getPostIds())); |
| | | userCreateReqDTO.setMobile(createReqVO.getPhone()); |
| | | userCreateReqDTO.setPassword(createReqVO.getPhone()); |
| | | userCreateReqDTO.setSex(createReqVO.getGender()); |
| | |
| | | if (CollUtil.isNotEmpty(createReqVO.getRoleIds())) { |
| | | employee.setRoleIds(JSONUtil.toJsonStr(createReqVO.getRoleIds())); |
| | | } |
| | | // postIds 存储为 JSON |
| | | if (CollUtil.isNotEmpty(createReqVO.getPostIds())) { |
| | | employee.setPostIds(JSONUtil.toJsonStr(createReqVO.getPostIds())); |
| | | } |
| | | employeeMapper.insert(employee); |
| | | Long employeeId = employee.getId(); |
| | | // 绑定附件 |
| | | storageAttachmentApi.bindAttachments("file", "hrm_employee", employeeId, createReqVO.getBlobIds()); |
| | | // 7. 同步添加教育经历 |
| | | if (CollUtil.isNotEmpty(createReqVO.getEducations())) { |
| | | createReqVO.getEducations().forEach(educationVO -> { |
| | |
| | | // 校验部门 |
| | | deptApi.validateDeptList(Collections.singleton(updateReqVO.getDeptId())); |
| | | // 校验岗位 |
| | | postApi.validPostList(Collections.singleton(updateReqVO.getPostId())); |
| | | postApi.validPostList(updateReqVO.getPostIds()); |
| | | // 校验手机号是否已存在(如果手机号有变更) |
| | | if (!Objects.equals(employee.getPhone(), updateReqVO.getPhone()) |
| | | && employeeMapper.selectByPhone(updateReqVO.getPhone()) != null) { |
| | |
| | | if (CollUtil.isNotEmpty(updateReqVO.getRoleIds())) { |
| | | updateObj.setRoleIds(JSONUtil.toJsonStr(updateReqVO.getRoleIds())); |
| | | } |
| | | // postIds 存储为 JSON |
| | | if (CollUtil.isNotEmpty(updateReqVO.getPostIds())) { |
| | | updateObj.setPostIds(JSONUtil.toJsonStr(updateReqVO.getPostIds())); |
| | | } |
| | | employeeMapper.updateById(updateObj); |
| | | // 同步更新关联系统用户(岗位/部门/昵称/角色/邮箱/备注),保持与用户管理一致 |
| | | if (employee.getUserId() != null) { |
| | | AdminUserUpdateReqDTO userUpdateReqDTO = new AdminUserUpdateReqDTO(); |
| | | userUpdateReqDTO.setId(employee.getUserId()); |
| | | userUpdateReqDTO.setNickname(updateReqVO.getName()); |
| | | userUpdateReqDTO.setDeptId(updateReqVO.getDeptId()); |
| | | userUpdateReqDTO.setPostIds(new LinkedHashSet<>(updateReqVO.getPostIds())); |
| | | userUpdateReqDTO.setRoleIds(CollUtil.isEmpty(updateReqVO.getRoleIds()) ? null |
| | | : new LinkedHashSet<>(updateReqVO.getRoleIds())); |
| | | userUpdateReqDTO.setEmail(updateReqVO.getEmail()); |
| | | userUpdateReqDTO.setRemark(updateReqVO.getRemark()); |
| | | adminUserApi.updateUser(userUpdateReqDTO); |
| | | } |
| | | Long employeeId = updateReqVO.getId(); |
| | | |
| | | // 同步教育经历 |
| | |
| | | syncWorkHistoryList(employeeId, updateReqVO.getWorkHistories()); |
| | | // 同步紧急联系人 |
| | | syncEmergencyContactList(employeeId, updateReqVO.getEmergencyContacts()); |
| | | // 更新附件 |
| | | storageAttachmentApi.updateAttachments("file", "hrm_employee", employeeId, updateReqVO.getBlobIds()); |
| | | } |
| | | |
| | | private void syncEducationList(Long employeeId, List<HrmEmployeeEducationSaveReqVO> newList) { |
| | |
| | | educationMapper.deleteByEmployeeId(id); |
| | | workHistoryMapper.deleteByEmployeeId(id); |
| | | emergencyContactMapper.deleteByEmployeeId(id); |
| | | // 删除附件 |
| | | storageAttachmentApi.deleteAttachmentsByRecord("hrm_employee", id); |
| | | // 删除员工 |
| | | employeeMapper.deleteById(id); |
| | | } |
| | |
| | | respVO.setDeptName(dept.getName()); |
| | | } |
| | | } |
| | | // 填充岗位名称 |
| | | if (employee.getPostId() != null) { |
| | | Map<Long, PostRespDTO> postMap = postApi.getPostMap(Collections.singleton(employee.getPostId())); |
| | | PostRespDTO post = postMap.get(employee.getPostId()); |
| | | if (post != null) { |
| | | respVO.setPostName(post.getName()); |
| | | // 填充岗位信息 |
| | | if (StrUtil.isNotBlank(employee.getPostIds())) { |
| | | List<Long> postIds = JSONUtil.toList(employee.getPostIds(), Long.class); |
| | | respVO.setPostIds(postIds); |
| | | if (CollUtil.isNotEmpty(postIds)) { |
| | | Map<Long, PostRespDTO> postMap = postApi.getPostMap(postIds); |
| | | respVO.setPostNames(postIds.stream() |
| | | .filter(postMap::containsKey) |
| | | .map(postId -> postMap.get(postId).getName()) |
| | | .collect(Collectors.joining(","))); |
| | | } |
| | | } |
| | | // 填充角色名称 |
| | |
| | | respVO.setRoleNames(roles.stream().map(RoleRespDTO::getName).collect(Collectors.joining(","))); |
| | | } |
| | | } |
| | | // 填充附件 |
| | | respVO.setAttachmentList(storageAttachmentApi.listAttachments("hrm_employee", id)); |
| | | return respVO; |
| | | } |
| | | |
| | |
| | | |
| | | @Override |
| | | public PageResult<HrmEmployeeDO> getEmployeePage(HrmEmployeePageReqVO pageReqVO) { |
| | | if (pageReqVO.getDeptId() != null) { |
| | | // 选中部门时,连带查询其所有子部门的员工(含自身) |
| | | List<DeptRespDTO> childDepts = deptApi.getChildDeptList(pageReqVO.getDeptId()); |
| | | Set<Long> deptIds = new LinkedHashSet<>(); |
| | | deptIds.add(pageReqVO.getDeptId()); |
| | | if (CollUtil.isNotEmpty(childDepts)) { |
| | | childDepts.forEach(dept -> deptIds.add(dept.getId())); |
| | | } |
| | | return employeeMapper.selectPageByDeptIds(pageReqVO, deptIds); |
| | | } |
| | | return employeeMapper.selectPage(pageReqVO); |
| | | } |
| | | |
| | |
| | | try { |
| | | // 3.1 名称转 ID |
| | | Long deptId = parseDeptId(importEmployee.getDeptName(), deptMap); |
| | | Long postId = parsePostId(importEmployee.getPostName(), postMap); |
| | | Set<Long> postIds = parsePostIds(importEmployee.getPostName(), postMap); |
| | | Set<Long> roleIds = parseRoleIds(importEmployee.getRoleNames(), roleMap); |
| | | // 3.2 构建保存请求 |
| | | HrmEmployeeSaveReqVO saveReqVO = buildImportSaveReqVO(importEmployee, deptId, postId, roleIds); |
| | | HrmEmployeeSaveReqVO saveReqVO = buildImportSaveReqVO(importEmployee, deptId, postIds, roleIds); |
| | | // 3.3 判断存在性:按手机号 |
| | | HrmEmployeeDO existEmployee = employeeMapper.selectByPhone(importEmployee.getPhone()); |
| | | if (existEmployee == null) { |
| | |
| | | } |
| | | |
| | | private HrmEmployeeSaveReqVO buildImportSaveReqVO(HrmEmployeeImportExcelVO importEmployee, |
| | | Long deptId, Long postId, Set<Long> roleIds) { |
| | | Long deptId, Set<Long> postIds, Set<Long> roleIds) { |
| | | if (StrUtil.isBlank(importEmployee.getName())) { |
| | | throw exception(EMPLOYEE_IMPORT_FIELD_REQUIRED, "员工姓名"); |
| | | } |
| | |
| | | reqVO.setGender(importEmployee.getGender() != null ? importEmployee.getGender() : 1); // 默认男 |
| | | reqVO.setPhone(importEmployee.getPhone()); |
| | | reqVO.setDeptId(deptId); |
| | | reqVO.setPostId(postId); |
| | | reqVO.setPostIds(CollUtil.isEmpty(postIds) ? null : new ArrayList<>(postIds)); |
| | | reqVO.setRoleIds(CollUtil.isEmpty(roleIds) ? null : new ArrayList<>(roleIds)); |
| | | reqVO.setHireDate(importEmployee.getHireDate()); |
| | | reqVO.setEmployeeStatus(importEmployee.getEmployeeStatus() != null |
| | |
| | | reqVO.setEducation(importEmployee.getEducation()); |
| | | reqVO.setPoliticalStatus(importEmployee.getPoliticalStatus()); |
| | | reqVO.setRemark(importEmployee.getRemark()); |
| | | reqVO.setEmployeeCategory(importEmployee.getEmployeeCategory()); |
| | | reqVO.setBaseSalary(importEmployee.getBaseSalary()); |
| | | reqVO.setBankName(importEmployee.getBankName()); |
| | | reqVO.setBankAccount(importEmployee.getBankAccount()); |
| | | reqVO.setAddress(importEmployee.getAddress()); |
| | | return reqVO; |
| | | } |
| | | |
| | |
| | | return dept.getId(); |
| | | } |
| | | |
| | | private Long parsePostId(String postName, Map<String, PostRespDTO> postMap) { |
| | | if (StrUtil.isBlank(postName)) { |
| | | private Set<Long> parsePostIds(String postNames, Map<String, PostRespDTO> postMap) { |
| | | if (StrUtil.isBlank(postNames)) { |
| | | throw exception(EMPLOYEE_IMPORT_FIELD_REQUIRED, "岗位名称"); |
| | | } |
| | | PostRespDTO post = postMap.get(postName.trim()); |
| | | if (post == null) { |
| | | throw exception(EMPLOYEE_POST_NAME_NOT_EXISTS, postName.trim()); |
| | | Set<Long> postIds = new LinkedHashSet<>(); |
| | | for (String postName : postNames.split("[,,]")) { |
| | | String trimmed = postName.trim(); |
| | | if (StrUtil.isBlank(trimmed)) { |
| | | continue; |
| | | } |
| | | PostRespDTO post = postMap.get(trimmed); |
| | | if (post == null) { |
| | | throw exception(EMPLOYEE_POST_NAME_NOT_EXISTS, trimmed); |
| | | } |
| | | postIds.add(post.getId()); |
| | | } |
| | | return post.getId(); |
| | | if (CollUtil.isEmpty(postIds)) { |
| | | throw exception(EMPLOYEE_IMPORT_FIELD_REQUIRED, "岗位名称"); |
| | | } |
| | | return postIds; |
| | | } |
| | | |
| | | private Set<Long> parseRoleIds(String roleNames, Map<String, RoleRespDTO> roleMap) { |
| | |
| | | return roleIds; |
| | | } |
| | | |
| | | // ========== 转正 ========== |
| | | |
| | | @Override |
| | | public void regularizeEmployee(Long id) { |
| | | validateEmployee(id); |
| | | HrmEmployeeContractDO contract = findCurrentContract(id); |
| | | // 仅「劳动合同」可转正;劳务合同不转正 |
| | | if (contract == null || !Integer.valueOf(1).equals(contract.getContractType())) { |
| | | throw exception(EMPLOYEE_REGULARIZE_NEED_LABOR_CONTRACT); |
| | | } |
| | | HrmEmployeeDO updateObj = new HrmEmployeeDO(); |
| | | updateObj.setId(id); |
| | | updateObj.setEmployeeStatus(1); // 1-在职 |
| | | updateObj.setRegularDate(LocalDate.now()); |
| | | employeeMapper.updateById(updateObj); |
| | | } |
| | | |
| | | @Override |
| | | public List<HrmEmployeeDO> getRegularizeReminderList(Integer days) { |
| | | int aheadDays = days == null ? 7 : days; |
| | | LocalDate limit = LocalDate.now().plusDays(aheadDays); |
| | | // 试用中的员工 |
| | | List<HrmEmployeeDO> trials = employeeMapper.selectList( |
| | | new LambdaQueryWrapperX<HrmEmployeeDO>().eq(HrmEmployeeDO::getEmployeeStatus, 2)); |
| | | List<HrmEmployeeDO> result = new ArrayList<>(); |
| | | for (HrmEmployeeDO employee : trials) { |
| | | HrmEmployeeContractDO contract = findCurrentContract(employee.getId()); |
| | | // 仅劳动合同、且试用期即将到期 |
| | | if (contract == null || !Integer.valueOf(1).equals(contract.getContractType())) { |
| | | continue; |
| | | } |
| | | LocalDate probationEnd = contract.getProbationEndDate(); |
| | | if (probationEnd != null && !probationEnd.isAfter(limit)) { |
| | | result.add(employee); |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** 查询员工当前生效合同 */ |
| | | private HrmEmployeeContractDO findCurrentContract(Long employeeId) { |
| | | List<HrmEmployeeContractDO> list = contractMapper.selectListByEmployee(employeeId); |
| | | if (CollUtil.isEmpty(list)) { |
| | | return null; |
| | | } |
| | | for (HrmEmployeeContractDO contract : list) { |
| | | if (Boolean.TRUE.equals(contract.getIsCurrent())) { |
| | | return contract; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | } |