| | |
| | | import cn.hutool.core.lang.Assert; |
| | | import cn.hutool.core.util.*; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| | | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| | | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| | |
| | | import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; |
| | | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; |
| | | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| | | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; |
| | | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; |
| | | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNodeTask; |
| | | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; |
| | | import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; |
| | | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| | | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| | | import cn.iocoder.yudao.module.bpm.dal.redis.BpmProcessIdRedisDAO; |
| | | import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants; |
| | | import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; |
| | | import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum; |
| | | import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeTypeEnum; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmAttachmentTypeEnum; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; |
| | |
| | | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; |
| | | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; |
| | | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; |
| | | import cn.iocoder.yudao.module.bpm.service.attach.BpmBusinessAttachService; |
| | | import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService; |
| | | import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; |
| | | import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; |
| | | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| | |
| | | import org.flowable.engine.runtime.Execution; |
| | | import org.flowable.engine.runtime.ProcessInstance; |
| | | import org.flowable.engine.runtime.ProcessInstanceBuilder; |
| | | import org.flowable.engine.task.Attachment; |
| | | import org.flowable.task.api.Task; |
| | | import org.flowable.task.api.history.HistoricTaskInstance; |
| | | import org.springframework.context.annotation.Lazy; |
| | |
| | | * <p> |
| | | * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 |
| | | * |
| | | * @author 芋道源码 |
| | | * @author 超级管理员 |
| | | */ |
| | | @Service |
| | | @Validated |
| | |
| | | |
| | | @Resource |
| | | private BpmProcessIdRedisDAO processIdRedisDAO; |
| | | |
| | | @Resource |
| | | private BpmBusinessAttachService businessAttachService; |
| | | @Resource |
| | | private BpmFormService formService; |
| | | |
| | | // ========== Query 查询相关方法 ========== |
| | | |
| | |
| | | processVariables.putAll(reqVO.getProcessVariables()); |
| | | } |
| | | |
| | | // 3. 获取下一个将要执行的节点集合 |
| | | // 3.1 获取下一个将要执行的节点集合 |
| | | FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); |
| | | List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); |
| | | // 仅仅获取 UserTask 节点 TODO add from jason:如果网关节点和网关节点相连,获取下个 UserTask. 貌似有点不准。 |
| | | List<FlowNode> nextUserTaskList = CollectionUtils.filterList(nextFlowNodes, node -> node instanceof UserTask); |
| | | // 3.2 获取 UserTask 节点 |
| | | List<UserTask> nextUserTaskList = BpmnModelUtils.getNextUserTasks(flowElement, bpmnModel, processVariables); |
| | | List<ActivityNode> nextActivityNodes = convertList(nextUserTaskList, node -> new ActivityNode().setId(node.getId()) |
| | | .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) |
| | | .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) |
| | |
| | | Map<String, String> formFieldsPermission = getFormFieldsPermission(bpmnModel, reqVO.getActivityId(), taskId); |
| | | |
| | | // 3. 拼接数据 |
| | | return BpmProcessInstanceConvert.INSTANCE.buildApprovalDetail(bpmnModel, processDefinition, |
| | | processDefinitionInfo, processInstance, |
| | | BpmApprovalDetailRespVO detailRespVO = BpmProcessInstanceConvert.INSTANCE.buildApprovalDetail(bpmnModel, |
| | | processDefinition, processDefinitionInfo, processInstance, |
| | | processInstanceStatus, approveNodes, todoTask, formFieldsPermission, userMap, deptMap); |
| | | |
| | | // 3.1 普通表单兜底:历史部署 / 配置导入的数据,bpm_process_definition_info 可能没有 formConf、formFields, |
| | | // 此时按 formId 回读 bpm_form,避免审批详情页表单区域空白 |
| | | fillNormalForm(detailRespVO.getProcessDefinition(), processDefinitionInfo); |
| | | |
| | | // 4. 填充业务附件配置,实现审批详情自动展示外部业务信息 |
| | | businessAttachService.fillBusinessForm(detailRespVO.getProcessDefinition(), processDefinition.getKey(), |
| | | processInstance != null ? processInstance.getBusinessKey() : null); |
| | | return detailRespVO; |
| | | } |
| | | |
| | | /** |
| | | * 普通表单兜底填充:历史部署或配置导入的流程定义,可能只有 formId 而缺失 formConf、formFields, |
| | | * 导致审批详情页的表单区域空白。这里按 formId 回读 bpm_form 补齐。 |
| | | */ |
| | | private void fillNormalForm(BpmProcessDefinitionRespVO definition, BpmProcessDefinitionInfoDO processDefinitionInfo) { |
| | | if (definition == null || !Objects.equals(definition.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) { |
| | | return; |
| | | } |
| | | Long formId = definition.getFormId() != null ? definition.getFormId() |
| | | : (processDefinitionInfo != null ? processDefinitionInfo.getFormId() : null); |
| | | if (formId == null |
| | | || (StrUtil.isNotBlank(definition.getFormConf()) && CollUtil.isNotEmpty(definition.getFormFields()))) { |
| | | return; |
| | | } |
| | | BpmFormDO form = formService.getForm(formId); |
| | | if (form != null) { |
| | | definition.setFormConf(form.getConf()).setFormFields(form.getFields()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | // 遍历 tasks 列表,只处理已结束的 UserTask |
| | | // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities 的话,它无法成为一个节点 |
| | | List<HistoricTaskInstance> endTasks = filterList(tasks, task -> task.getEndTime() != null); |
| | | // 获取已完成节点的附件 |
| | | Set<String> endTaskIds = convertSet(endTasks, HistoricTaskInstance::getId); |
| | | List<Attachment> attachments = taskService.getAttachments(historicProcessInstance.getId(), endTaskIds, BpmAttachmentTypeEnum.TASK_ATTACHMENT); |
| | | Map<String, List<Attachment>> taskAttachmentMap = convertMultiMap(attachments, Attachment::getTaskId); |
| | | List<ActivityNode> approvalNodes = convertList(endTasks, task -> { |
| | | FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); |
| | | List<Attachment> taskAttachments = taskAttachmentMap.get(task.getId()); |
| | | ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName()) |
| | | .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) |
| | | ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() |
| | |
| | | .setStatus(getEndActivityNodeStatus(task)) |
| | | .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) |
| | | .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) |
| | | .setTasks(singletonList(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task))); |
| | | .setTasks(singletonList(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task, taskAttachments))); |
| | | // 如果是取消状态,则跳过 |
| | | if (BpmTaskStatusEnum.isCancelStatus(activityNode.getStatus())) { |
| | | return null; |
| | |
| | | if (task == null) { |
| | | continue; |
| | | } |
| | | activityNode.getTasks().add(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task)); |
| | | activityNode.getTasks().add(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task, null)); |
| | | // 加签子任务,需要过滤掉已经完成的加签子任务 |
| | | List<HistoricTaskInstance> childrenTasks = filterList( |
| | | taskService.getAllChildrenTaskListByParentTaskId(activity.getTaskId(), tasks), |
| | | childTask -> childTask.getEndTime() == null); |
| | | if (CollUtil.isNotEmpty(childrenTasks)) { |
| | | activityNode.getTasks().addAll( |
| | | convertList(childrenTasks, BpmProcessInstanceConvert.INSTANCE::buildApprovalTaskInfo)); |
| | | convertList(childrenTasks, item->BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(item, null))); |
| | | } |
| | | } |
| | | // 处理每个任务的 candidateUsers 属性:如果是依次审批,需要预测它的后续审批人。因为 Task 是审批完一个,创建一个新的 Task |
| | |
| | | } |
| | | |
| | | // 3. 发送流程实例的状态事件 |
| | | // 带上流程分类:自定义流程模型的 key 可能是随机值(如 C000_abc.),业务监听器需要按分类兜底匹配 |
| | | ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(instance.getProcessDefinitionId()); |
| | | processInstanceEventPublisher.sendProcessInstanceResultEvent( |
| | | BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status, reason)); |
| | | BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, |
| | | processDefinition != null ? processDefinition.getCategory() : null, status, reason)); |
| | | |
| | | // 4. 流程后置通知 |
| | | if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { |