package org.dromara.workflow.utils; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.dromara.common.core.domain.dto.BusinessInstanceDTO; import org.dromara.common.core.domain.dto.RoleDTO; import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.service.UserService; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.mail.utils.MailUtils; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.websocket.dto.WebSocketMessageDto; import org.dromara.common.websocket.utils.WebSocketUtils; import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.enums.BusinessStatusEnum; import org.dromara.workflow.common.enums.MessageTypeEnum; import org.dromara.workflow.common.enums.TaskStatusEnum; import org.dromara.workflow.domain.ActHiProcinst; import org.dromara.workflow.domain.ActHiTaskinst; import org.dromara.workflow.domain.vo.MultiInstanceVo; import org.dromara.workflow.domain.vo.ParticipantVo; import org.dromara.workflow.flowable.cmd.UpdateHiTaskInstCmd; import org.dromara.workflow.mapper.ActHiTaskinstMapper; import org.dromara.workflow.service.IActHiProcinstService; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.FlowNode; import org.flowable.common.engine.api.delegate.Expression; import org.flowable.engine.ProcessEngine; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; import org.flowable.identitylink.api.history.HistoricIdentityLink; import org.flowable.task.api.Task; import org.flowable.task.api.TaskQuery; import org.flowable.task.api.history.HistoricTaskInstance; import org.flowable.task.service.impl.persistence.entity.TaskEntity; import java.util.*; import static org.dromara.workflow.common.constant.FlowConstant.BUSINESS_INSTANCE_DTO; /** * 工作流工具 * * @author may */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class WorkflowUtils { private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class); private static final UserService USER_SERVICE = SpringUtils.getBean(UserService.class); private static final IActHiProcinstService ACT_HI_PROCINST_SERVICE = SpringUtils.getBean(IActHiProcinstService.class); private static final ActHiTaskinstMapper ACT_HI_TASKINST_MAPPER = SpringUtils.getBean(ActHiTaskinstMapper.class); /** * 创建一个新任务 * * @param currentTask 参数 */ public static TaskEntity createNewTask(Task currentTask) { TaskEntity task = null; if (ObjectUtil.isNotEmpty(currentTask)) { task = (TaskEntity) PROCESS_ENGINE.getTaskService().newTask(); task.setCategory(currentTask.getCategory()); task.setDescription(currentTask.getDescription()); task.setAssignee(currentTask.getAssignee()); task.setName(currentTask.getName()); task.setProcessDefinitionId(currentTask.getProcessDefinitionId()); task.setProcessInstanceId(currentTask.getProcessInstanceId()); task.setTaskDefinitionKey(currentTask.getTaskDefinitionKey()); task.setPriority(currentTask.getPriority()); task.setCreateTime(new Date()); task.setTenantId(TenantHelper.getTenantId()); PROCESS_ENGINE.getTaskService().saveTask(task); } if (ObjectUtil.isNotNull(task)) { UpdateHiTaskInstCmd updateHiTaskInstCmd = new UpdateHiTaskInstCmd(Collections.singletonList(task.getId()), task.getProcessDefinitionId(), task.getProcessInstanceId()); PROCESS_ENGINE.getManagementService().executeCommand(updateHiTaskInstCmd); } return task; } /** * 抄送任务 * * @param parentTaskList 父级任务 * @param userIds 人员id */ public static void createCopyTask(List parentTaskList, List userIds) { List list = new ArrayList<>(); String tenantId = TenantHelper.getTenantId(); for (Task parentTask : parentTaskList) { for (Long userId : userIds) { TaskEntity newTask = (TaskEntity) PROCESS_ENGINE.getTaskService().newTask(); newTask.setParentTaskId(parentTask.getId()); newTask.setAssignee(userId.toString()); newTask.setName("【抄送】-" + parentTask.getName()); newTask.setProcessDefinitionId(parentTask.getProcessDefinitionId()); newTask.setProcessInstanceId(parentTask.getProcessInstanceId()); newTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey()); newTask.setTenantId(tenantId); list.add(newTask); } } PROCESS_ENGINE.getTaskService().bulkSaveTasks(list); if (CollUtil.isNotEmpty(list) && CollUtil.isNotEmpty(parentTaskList)) { String processInstanceId = parentTaskList.get(0).getProcessInstanceId(); String processDefinitionId = parentTaskList.get(0).getProcessDefinitionId(); List taskIds = StreamUtils.toList(list, Task::getId); ActHiTaskinst actHiTaskinst = new ActHiTaskinst(); actHiTaskinst.setProcDefId(processDefinitionId); actHiTaskinst.setProcInstId(processInstanceId); actHiTaskinst.setScopeType(TaskStatusEnum.COPY.getStatus()); actHiTaskinst.setTenantId(tenantId); LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.in(ActHiTaskinst::getId, taskIds); ACT_HI_TASKINST_MAPPER.update(actHiTaskinst, updateWrapper); for (Task task : list) { PROCESS_ENGINE.getTaskService().addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.COPY.getStatus(), StrUtil.EMPTY); } } } /** * 获取当前任务参与者 * * @param taskId 任务id */ public static ParticipantVo getCurrentTaskParticipant(String taskId) { ParticipantVo participantVo = new ParticipantVo(); List linksForTask = PROCESS_ENGINE.getHistoryService().getHistoricIdentityLinksForTask(taskId); Task task = QueryUtils.taskQuery().taskId(taskId).singleResult(); if (task != null && CollUtil.isNotEmpty(linksForTask)) { List groupList = StreamUtils.filter(linksForTask, e -> StringUtils.isNotBlank(e.getGroupId())); if (CollUtil.isNotEmpty(groupList)) { List groupIds = StreamUtils.toList(groupList, e -> Long.valueOf(e.getGroupId())); List userIds = USER_SERVICE.selectUserIdsByRoleIds(groupIds); if (CollUtil.isNotEmpty(userIds)) { participantVo.setGroupIds(groupIds); List userList = USER_SERVICE.selectListByIds(userIds); if (CollUtil.isNotEmpty(userList)) { List userIdList = StreamUtils.toList(userList, UserDTO::getUserId); List nickNames = StreamUtils.toList(userList, UserDTO::getNickName); participantVo.setCandidate(userIdList); participantVo.setCandidateName(nickNames); participantVo.setClaim(!StringUtils.isBlank(task.getAssignee())); } } } else { List candidateList = StreamUtils.filter(linksForTask, e -> FlowConstant.CANDIDATE.equals(e.getType())); List userIdList = new ArrayList<>(); for (HistoricIdentityLink historicIdentityLink : linksForTask) { try { userIdList.add(Long.valueOf(historicIdentityLink.getUserId())); } catch (NumberFormatException ignored) { } } List userList = USER_SERVICE.selectListByIds(userIdList); if (CollUtil.isNotEmpty(userList)) { List userIds = StreamUtils.toList(userList, UserDTO::getUserId); List nickNames = StreamUtils.toList(userList, UserDTO::getNickName); participantVo.setCandidate(userIds); participantVo.setCandidateName(nickNames); // 判断当前任务是否具有多个办理人 if (CollUtil.isNotEmpty(candidateList) && candidateList.size() > 1) { // 如果 assignee 存在,则设置当前任务已经被认领 participantVo.setClaim(StringUtils.isNotBlank(task.getAssignee())); } } } } return participantVo; } /** * 判断当前节点是否为会签节点 * * @param processDefinitionId 流程定义id * @param taskDefinitionKey 流程定义id */ public static MultiInstanceVo isMultiInstance(String processDefinitionId, String taskDefinitionKey) { BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId); FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey); MultiInstanceVo multiInstanceVo = new MultiInstanceVo(); //判断是否为并行会签节点 if (flowNode.getBehavior() instanceof ParallelMultiInstanceBehavior behavior && behavior.getCollectionExpression() != null) { Expression collectionExpression = behavior.getCollectionExpression(); String assigneeList = collectionExpression.getExpressionText(); String assignee = behavior.getCollectionElementVariable(); multiInstanceVo.setType(behavior); multiInstanceVo.setAssignee(assignee); multiInstanceVo.setAssigneeList(assigneeList); return multiInstanceVo; //判断是否为串行会签节点 } else if (flowNode.getBehavior() instanceof SequentialMultiInstanceBehavior behavior && behavior.getCollectionExpression() != null) { Expression collectionExpression = behavior.getCollectionExpression(); String assigneeList = collectionExpression.getExpressionText(); String assignee = behavior.getCollectionElementVariable(); multiInstanceVo.setType(behavior); multiInstanceVo.setAssignee(assignee); multiInstanceVo.setAssigneeList(assigneeList); return multiInstanceVo; } return null; } /** * 获取当前流程状态 * * @param taskId 任务id */ public static String getBusinessStatusByTaskId(String taskId) { HistoricTaskInstance historicTaskInstance = QueryUtils.hisTaskInstanceQuery().taskId(taskId).singleResult(); HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery(historicTaskInstance.getProcessInstanceId()).singleResult(); return historicProcessInstance.getBusinessStatus(); } /** * 获取当前流程状态 * * @param businessKey 业务id */ public static String getBusinessStatus(String businessKey) { HistoricProcessInstance historicProcessInstance = QueryUtils.hisBusinessKeyQuery(businessKey).singleResult(); return historicProcessInstance.getBusinessStatus(); } /** * 设置流程实例对象 * * @param obj 业务对象 * @param businessKey 业务id */ public static void setBusinessInstanceDTO(Object obj, String businessKey) { if (StringUtils.isBlank(businessKey) || obj == null) { return; } ActHiProcinst actHiProcinst = ACT_HI_PROCINST_SERVICE.selectByBusinessKey(businessKey); if (actHiProcinst == null) { BusinessInstanceDTO businessInstanceDTO = new BusinessInstanceDTO(); businessInstanceDTO.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus()); ReflectUtils.invokeSetter(obj, BUSINESS_INSTANCE_DTO, businessInstanceDTO); return; } BusinessInstanceDTO businessInstanceDTO = BeanUtil.toBean(actHiProcinst, BusinessInstanceDTO.class); businessInstanceDTO.setBusinessStatusName(BusinessStatusEnum.findByStatus(businessInstanceDTO.getBusinessStatus())); businessInstanceDTO.setProcessDefinitionId(actHiProcinst.getProcDefId()); ReflectUtils.invokeSetter(obj, BUSINESS_INSTANCE_DTO, businessInstanceDTO); } /** * 设置流程实例对象 * * @param obj 业务对象 * @param idList 业务id * @param fieldName 主键属性名称 */ public static void setBusinessInstanceListDTO(Object obj, List idList, String fieldName) { if (CollUtil.isEmpty(idList) || obj == null) { return; } List actHiProcinstList = ACT_HI_PROCINST_SERVICE.selectByBusinessKeyIn(idList); if (obj instanceof Collection collection) { for (Object o : collection) { String fieldValue = ReflectUtils.invokeGetter(o, fieldName).toString(); if (CollUtil.isEmpty(actHiProcinstList)) { BusinessInstanceDTO businessInstanceDTO = new BusinessInstanceDTO(); businessInstanceDTO.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus()); businessInstanceDTO.setBusinessStatusName(BusinessStatusEnum.findByStatus(businessInstanceDTO.getBusinessStatus())); ReflectUtils.invokeSetter(o, BUSINESS_INSTANCE_DTO, businessInstanceDTO); } else { ActHiProcinst actHiProcinst = actHiProcinstList.stream().filter(e -> e.getBusinessKey().equals(fieldValue)).findFirst().orElse(null); if (ObjectUtil.isNotEmpty(actHiProcinst)) { BusinessInstanceDTO businessInstanceDTO = BeanUtil.toBean(actHiProcinst, BusinessInstanceDTO.class); businessInstanceDTO.setBusinessStatusName(BusinessStatusEnum.findByStatus(businessInstanceDTO.getBusinessStatus())); businessInstanceDTO.setProcessDefinitionId(actHiProcinst.getProcDefId()); ReflectUtils.invokeSetter(o, BUSINESS_INSTANCE_DTO, businessInstanceDTO); } else { BusinessInstanceDTO businessInstanceDTO = new BusinessInstanceDTO(); businessInstanceDTO.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus()); businessInstanceDTO.setBusinessStatusName(BusinessStatusEnum.findByStatus(businessInstanceDTO.getBusinessStatus())); ReflectUtils.invokeSetter(o, BUSINESS_INSTANCE_DTO, businessInstanceDTO); } } } } } /** * 发送消息 * * @param list 任务 * @param name 流程名称 * @param messageType 消息类型 * @param message 消息内容,为空则发送默认配置的消息内容 */ public static void sendMessage(List list, String name, List messageType, String message) { Set userIds = new HashSet<>(); if (StringUtils.isBlank(message)) { message = "有新的【" + name + "】单据已经提交至您的待办,请您及时处理。"; } for (Task t : list) { ParticipantVo taskParticipant = WorkflowUtils.getCurrentTaskParticipant(t.getId()); if (CollUtil.isNotEmpty(taskParticipant.getGroupIds())) { List userIdList = USER_SERVICE.selectUserIdsByRoleIds(taskParticipant.getGroupIds()); if (CollUtil.isNotEmpty(userIdList)) { userIds.addAll(userIdList); } } List candidate = taskParticipant.getCandidate(); if (CollUtil.isNotEmpty(candidate)) { userIds.addAll(candidate); } } if (CollUtil.isNotEmpty(userIds)) { List userList = USER_SERVICE.selectListByIds(new ArrayList<>(userIds)); for (String code : messageType) { MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code); if (ObjectUtil.isNotEmpty(messageTypeEnum)) { switch (messageTypeEnum) { case SYSTEM_MESSAGE: WebSocketMessageDto dto = new WebSocketMessageDto(); dto.setSessionKeys(new ArrayList<>(userIds)); dto.setMessage(message); WebSocketUtils.publishMessage(dto); break; case EMAIL_MESSAGE: MailUtils.sendText(StreamUtils.join(userList, UserDTO::getEmail), "单据审批提醒", message); break; case SMS_MESSAGE: //todo 短信发送 break; } } } } } /** * 根据任务id查询 当前用户的任务,检查 当前人员 是否是该 taskId 的办理人 * * @param taskId 任务id * @return 结果 */ public static Task getTaskByCurrentUser(String taskId) { TaskQuery taskQuery = QueryUtils.taskQuery(); taskQuery.taskId(taskId).taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId())); List roles = LoginHelper.getLoginUser().getRoles(); if (CollUtil.isNotEmpty(roles)) { List groupIds = StreamUtils.toList(roles, e -> String.valueOf(e.getRoleId())); taskQuery.taskCandidateGroupIn(groupIds); } return taskQuery.singleResult(); } }