tuqin
5 小时以前 89071e0c09188d75c3d537f82204ae19713ac7fe
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/employee/HrmEmployeeServiceImpl.java
@@ -6,6 +6,7 @@
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;
@@ -16,10 +17,12 @@
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;
@@ -31,14 +34,18 @@
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;
@@ -66,6 +73,8 @@
    @Resource
    private HrmEmployeeMapper employeeMapper;
    @Resource
    private HrmEmployeeContractMapper contractMapper;
    @Resource
    private HrmEmployeeEducationMapper educationMapper;
    @Resource
    private HrmEmployeeWorkHistoryMapper workHistoryMapper;
@@ -82,6 +91,8 @@
    @Resource
    private RoleApi roleApi;
    @Resource
    private StorageAttachmentApi storageAttachmentApi;
    @Resource
    @Lazy // 自注入需延迟加载,否则 bean 被代理包装后注入原始版本导致启动失败
    private HrmEmployeeService self;
@@ -91,7 +102,7 @@
        // 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);
@@ -101,7 +112,7 @@
        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());
@@ -122,8 +133,14 @@
        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 -> {
@@ -167,7 +184,7 @@
        // 校验部门
        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) {
@@ -179,7 +196,24 @@
        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();
        // 同步教育经历
@@ -188,6 +222,8 @@
        syncWorkHistoryList(employeeId, updateReqVO.getWorkHistories());
        // 同步紧急联系人
        syncEmergencyContactList(employeeId, updateReqVO.getEmergencyContacts());
        // 更新附件
        storageAttachmentApi.updateAttachments("file", "hrm_employee", employeeId, updateReqVO.getBlobIds());
    }
    private void syncEducationList(Long employeeId, List<HrmEmployeeEducationSaveReqVO> newList) {
@@ -235,6 +271,8 @@
        educationMapper.deleteByEmployeeId(id);
        workHistoryMapper.deleteByEmployeeId(id);
        emergencyContactMapper.deleteByEmployeeId(id);
        // 删除附件
        storageAttachmentApi.deleteAttachmentsByRecord("hrm_employee", id);
        // 删除员工
        employeeMapper.deleteById(id);
    }
@@ -270,12 +308,16 @@
                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(",")));
            }
        }
        // 填充角色名称
@@ -285,6 +327,8 @@
                respVO.setRoleNames(roles.stream().map(RoleRespDTO::getName).collect(Collectors.joining(",")));
            }
        }
        // 填充附件
        respVO.setAttachmentList(storageAttachmentApi.listAttachments("hrm_employee", id));
        return respVO;
    }
@@ -299,6 +343,16 @@
    @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);
    }
@@ -370,10 +424,10 @@
            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) {
@@ -398,7 +452,7 @@
    }
    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, "员工姓名");
        }
@@ -413,7 +467,7 @@
        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
@@ -427,6 +481,11 @@
        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;
    }
@@ -441,15 +500,26 @@
        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) {
@@ -471,4 +541,57 @@
        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;
    }
}