yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmResignationApplicationServiceImpl.java
@@ -2,6 +2,7 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
@@ -12,11 +13,13 @@
import cn.iocoder.yudao.module.hrm.controller.admin.approval.vo.HrmResignationApplicationPageReqVO;
import cn.iocoder.yudao.module.hrm.controller.admin.approval.vo.HrmResignationApplicationRespVO;
import cn.iocoder.yudao.module.hrm.controller.admin.approval.vo.HrmResignationApplicationSaveReqVO;
import cn.iocoder.yudao.module.hrm.controller.admin.approval.vo.HrmResignationHandoverReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.approval.HrmResignationApplicationDO;
import cn.iocoder.yudao.module.hrm.dal.mysql.approval.HrmResignationApplicationMapper;
import cn.iocoder.yudao.module.hrm.dal.redis.HrmNoRedisDAO;
import cn.iocoder.yudao.module.hrm.enums.HrmAuditStatusEnum;
import cn.iocoder.yudao.module.hrm.enums.HrmResignationTypeEnum;
import cn.iocoder.yudao.module.hrm.service.handover.HrmUserHandoverService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
@@ -31,6 +34,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -46,7 +50,7 @@
/**
 * 离职申请 Service 实现类
 *
 * @author 芋道源码
 * @author 超级管理员
 */
@Service
@Validated
@@ -73,6 +77,8 @@
    private StorageAttachmentApi storageAttachmentApi;
    @Resource
    private BpmProcessDefinitionService bpmProcessDefinitionService;
    @Resource
    private HrmUserHandoverService handoverService;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -89,12 +95,17 @@
        String no = generateUniqueNo();
        AdminUserRespDTO user = adminUserApi.getUser(userId);
        if (user == null) {
            // 传入的可能不是系统用户ID(例如员工ID),回退到当前登录用户并重新查询,避免后续 NPE
            userId = getCurrentUserId();
            user = adminUserApi.getUser(userId);
        }
        HrmResignationApplicationDO application = BeanUtils.toBean(createReqVO, HrmResignationApplicationDO.class);
        application.setNo(no);
        application.setUserId(userId);
        application.setDeptId(createReqVO.getDeptId() != null ? createReqVO.getDeptId() : user.getDeptId());
        // 部门:优先取请求参数,其次取账号所属部门,最后兜底 0(该列 NOT NULL)
        Long deptId = createReqVO.getDeptId() != null ? createReqVO.getDeptId()
                : (user != null ? user.getDeptId() : null);
        application.setDeptId(deptId != null ? deptId : 0L);
        application.setStatus(HrmAuditStatusEnum.DRAFT.getStatus());
        application.setHandoverStatus(HANDOVER_STATUS_NOT_STARTED);
        resignationApplicationMapper.insert(application);
@@ -123,11 +134,7 @@
        }
        HrmResignationApplicationDO updateObj = BeanUtils.toBean(updateReqVO, HrmResignationApplicationDO.class);
        resignationApplicationMapper.updateById(updateObj);
        if (updateReqVO.getBlobIds() != null) {
            storageAttachmentApi.deleteAttachmentsByRecord("hrm_resignation_application", updateReqVO.getId());
            storageAttachmentApi.bindAttachments("file", "hrm_resignation_application",
                    updateReqVO.getId(), updateReqVO.getBlobIds());
        }
        storageAttachmentApi.updateAttachments("file", "hrm_resignation_application", updateReqVO.getId(), updateReqVO.getBlobIds());
    }
    @Override
@@ -245,17 +252,40 @@
            throw exception(RESIGNATION_APPLICATION_SUBMIT_FAIL_NOT_DRAFT);
        }
        validateApproveCategoryAndProcessDefinition();
        ProcessDefinition processDefinition = bpmProcessDefinitionService.getActiveProcessDefinition(processDefinitionKey);
        // 未指定流程定义 Key 时,自动取「离职审批」分类下最新的活跃流程,避免前端未传 key 直接报系统异常
        String key = resolveProcessDefinitionKey(processDefinitionKey);
        ProcessDefinition processDefinition = bpmProcessDefinitionService.getActiveProcessDefinition(key);
        if (processDefinition == null) {
            throw exception(RESIGNATION_APPLICATION_BPM_PROCESS_DEFINITION_NOT_EXISTS);
        }
        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
                .setProcessDefinitionKey(processDefinitionKey).setBusinessKey(String.valueOf(id)));
                .setProcessDefinitionKey(key).setBusinessKey(String.valueOf(id)));
        resignationApplicationMapper.updateById(HrmResignationApplicationDO.builder()
                .id(id)
                .processInstanceId(processInstanceId)
                .status(HrmAuditStatusEnum.PROCESS.getStatus())
                .build());
    }
    /**
     * 解析流程定义 Key:优先使用调用方传入值,为空时取「离职审批」分类下最新的活跃流程
     */
    private String resolveProcessDefinitionKey(String processDefinitionKey) {
        if (StrUtil.isNotBlank(processDefinitionKey)) {
            return processDefinitionKey.trim();
        }
        List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
                .getProcessDefinitionInfoListByCategory(APPROVE_CATEGORY_CODE);
        if (CollUtil.isEmpty(definitionInfoList)) {
            throw exception(RESIGNATION_APPLICATION_BPM_PROCESS_DEFINITION_NOT_EXISTS);
        }
        Set<String> processDefinitionIds = convertSet(definitionInfoList, BpmProcessDefinitionInfoDO::getProcessDefinitionId);
        List<ProcessDefinition> processDefinitions = bpmProcessDefinitionService.getProcessDefinitionList(processDefinitionIds);
        return processDefinitions.stream()
                .filter(pd -> !pd.isSuspended())
                .max(Comparator.comparingInt(ProcessDefinition::getVersion))
                .map(ProcessDefinition::getKey)
                .orElseThrow(() -> exception(RESIGNATION_APPLICATION_BPM_PROCESS_DEFINITION_NOT_EXISTS));
    }
    private void validateApproveCategoryAndProcessDefinition() {
@@ -323,4 +353,22 @@
        return resignationApplicationMapper.selectByProcessInstanceId(processInstanceId);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateHandoverStatus(HrmResignationHandoverReqVO reqVO) {
        HrmResignationApplicationDO application = validateResignationApplicationExists(reqVO.getId());
        resignationApplicationMapper.updateById(HrmResignationApplicationDO.builder()
                .id(reqVO.getId())
                .handoverStatus(reqVO.getHandoverStatus())
                .build());
        // 交接完成时,创建用户数据交接映射
        if (Objects.equals(reqVO.getHandoverStatus(), HANDOVER_STATUS_COMPLETED)
                && application.getHandoverUserId() != null) {
            handoverService.createHandover(
                    application.getUserId(),
                    application.getHandoverUserId(),
                    application.getId());
        }
    }
}