• Activiti7工作流引擎:节点动态跳转


    Activiti7中并没有实现从一个UserTask跳转到另一个UserTask,要实现节点之间自由跳转,需要通过自定义命令来实现。

    Activiti7实现主要使用了命令模式(Command)和责任链模式(Intercepter)。

    • 命令模式:主要是将每个操作封装成一个命令。如:
      • DeployCmd:部署操作。
      • CompleteTaskCmd:审批操作。
      • DeleteTaskCmd:删除操作。
      • AddCommentCmd:添加审批意见操作。
    • 责任链模式:每个拦截器AbstractCommandInterceptor有一个next属性指向下一个拦截器。
      • LogIntercepter:第一个拦截器 first
      • SpringTransactionIntercepter:Spring事务拦截器。
      • CommandContextIntercepter:命令上下文拦截器。
      • CommandInvoker:最后一个拦截器,用于执行前面的各种Cmd命令。

    一:application.yml

    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT
        username: root
        password: root123
        driver-class-name: com.mysql.cj.jdbc.Driver
    ## Mybatis 配置
    mybatis:
      typeAliasesPackage: com.example.demo.entity
      mapperLocations: classpath:mapper/*.xml
      configuration:
        map-underscore-to-camel-case: true
    
    # 打印mybatis中的sql语句
    logging:
        level:
          org.activiti.engine.impl.persistence.entity: debug
    
    
    activiti:
      #1.flase: 默认值。activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常
      #2.true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建
      #3.create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
      #4.drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
      database-schema-update: true
      #检测历史表是否存在 activiti7默认没有开启数据库历史记录 启动数据库历史记录
      db-history-used: true
      #记录历史等级 可配置的历史级别有none, activity, audit, full
      history-level: full
      #校验流程文件,默认校验resources下的processes文件夹里的流程文件
      check-process-definitions: false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    二:bpmn

    在这里插入图片描述

    
    <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1692856575168" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
      <process id="leave" isClosed="false" isExecutable="true" name="请假流程" processType="None">
        <startEvent id="start" name="StartEvent"/>
        <userTask activiti:exclusive="true" id="leads" name="组长审批"/>
        <userTask activiti:exclusive="true" id="manager" name="经理审批"/>
        <sequenceFlow id="_5" sourceRef="start" targetRef="leads"/>
        <sequenceFlow id="_6" sourceRef="leads" targetRef="manager"/>
        <exclusiveGateway gatewayDirection="Unspecified" id="_7" name="ExclusiveGateway"/>
        <sequenceFlow id="_8" sourceRef="manager" targetRef="_7"/>
        <userTask activiti:exclusive="true" id="gm" name="总经理审批"/>
        <userTask activiti:exclusive="true" id="dgm" name="副总经理审批"/>
        <sequenceFlow id="_11" sourceRef="_7" targetRef="gm">
          <conditionExpression xsi:type="tFormalExpression">
            
          conditionExpression>
        sequenceFlow>
        <sequenceFlow id="_12" sourceRef="_7" targetRef="dgm">
          <conditionExpression xsi:type="tFormalExpression">
            
          conditionExpression>
        sequenceFlow>
        <userTask activiti:exclusive="true" id="hr" name="人事审批"/>
        <sequenceFlow id="_2" sourceRef="gm" targetRef="hr"/>
        <sequenceFlow id="_3" sourceRef="dgm" targetRef="hr"/>
        <endEvent id="end" name="EndEvent"/>
        <sequenceFlow id="_9" sourceRef="hr" targetRef="end"/>
      process>
      <bpmndi:BPMNDiagram documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
        <bpmndi:BPMNPlane bpmnElement="leave">
          <bpmndi:BPMNShape bpmnElement="start" id="Shape-start">
            <omgdc:Bounds height="32.0" width="32.0" x="60.0" y="160.0"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNShape>
          <bpmndi:BPMNShape bpmnElement="leads" id="Shape-leads">
            <omgdc:Bounds height="55.0" width="85.0" x="170.0" y="150.0"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNShape>
          <bpmndi:BPMNShape bpmnElement="manager" id="Shape-manager">
            <omgdc:Bounds height="55.0" width="85.0" x="315.0" y="150.0"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNShape>
          <bpmndi:BPMNShape bpmnElement="_7" id="Shape-_7" isMarkerVisible="false">
            <omgdc:Bounds height="32.0" width="32.0" x="480.0" y="160.0"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNShape>
          <bpmndi:BPMNShape bpmnElement="gm" id="Shape-gm">
            <omgdc:Bounds height="55.0" width="85.0" x="620.0" y="95.0"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNShape>
          <bpmndi:BPMNShape bpmnElement="dgm" id="Shape-dgm">
            <omgdc:Bounds height="55.0" width="85.0" x="620.0" y="220.0"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNShape>
          <bpmndi:BPMNShape bpmnElement="hr" id="Shape-hr">
            <omgdc:Bounds height="55.0" width="85.0" x="815.0" y="170.0"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNShape>
          <bpmndi:BPMNShape bpmnElement="end" id="Shape-end">
            <omgdc:Bounds height="32.0" width="32.0" x="960.0" y="180.0"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNShape>
          <bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_7" targetElement="dgm">
            <omgdi:waypoint x="512.0" y="176.0"/>
            <omgdi:waypoint x="620.0" y="247.5"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge bpmnElement="_2" id="BPMNEdge__2" sourceElement="gm" targetElement="hr">
            <omgdi:waypoint x="705.0" y="122.5"/>
            <omgdi:waypoint x="815.0" y="197.5"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge bpmnElement="_3" id="BPMNEdge__3" sourceElement="dgm" targetElement="hr">
            <omgdi:waypoint x="705.0" y="247.5"/>
            <omgdi:waypoint x="815.0" y="197.5"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge bpmnElement="_5" id="BPMNEdge__5" sourceElement="start" targetElement="leads">
            <omgdi:waypoint x="92.0" y="176.0"/>
            <omgdi:waypoint x="170.0" y="177.5"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="leads" targetElement="manager">
            <omgdi:waypoint x="255.0" y="177.5"/>
            <omgdi:waypoint x="315.0" y="177.5"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="manager" targetElement="_7">
            <omgdi:waypoint x="400.0" y="177.5"/>
            <omgdi:waypoint x="480.0" y="176.0"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge bpmnElement="_9" id="BPMNEdge__9" sourceElement="hr" targetElement="end">
            <omgdi:waypoint x="900.0" y="197.5"/>
            <omgdi:waypoint x="960.0" y="196.0"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="_7" targetElement="gm">
            <omgdi:waypoint x="512.0" y="176.0"/>
            <omgdi:waypoint x="620.0" y="122.5"/>
            <bpmndi:BPMNLabel>
              <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            bpmndi:BPMNLabel>
          bpmndi:BPMNEdge>
        bpmndi:BPMNPlane>
      bpmndi:BPMNDiagram>
    definitions>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139

    三:自定义跳转命令

    DynamicJumpCmd

    /**
     * 节点动态跳转.
     */
    @Slf4j
    @AllArgsConstructor
    public class DynamicJumpCmd implements Command<Void> {
    
        /** 流程实例id */
        private String processInstanceId;
    
        /** 从哪个节点 */
        private String fromActivityId;
    
        /** 跳转到那个节点 */
        private String toActivityId;
    
    
        @Override
        public Void execute(CommandContext commandContext) {
            log.info("node jump: processInstanceId={}, fromActivityId={}, toActivityId={}", processInstanceId, fromActivityId, toActivityId);
            // 0.参数合法性检查
            ExecutionEntityManager executionEntityManager = commandContext.getExecutionEntityManager();
            ExecutionEntity executionEntity = executionEntityManager.findById(processInstanceId);
            Assert.isTrue(executionEntity != null, "processInstanceId is not exist");
            BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(executionEntity.getProcessDefinitionId());
            FlowElement fromFlowElement = bpmnModel.getFlowElement(fromActivityId);
            Assert.isTrue(fromFlowElement != null, "fromActivityId is not exist");
            FlowElement toFlowElement = bpmnModel.getFlowElement(toActivityId);
    
            // 1.找出子执行实例fromActivityId,并删除该执行实例
            List<ExecutionEntity> childList = executionEntityManager.findChildExecutionsByProcessInstanceId(processInstanceId);
            ExecutionEntity currentChildExecutionEntity = null;
            for (ExecutionEntity child : childList) {
                if (child.getCurrentActivityId().equals(fromActivityId)) {
                    currentChildExecutionEntity = child;
                    break;
                }
            }
            executionEntityManager.deleteExecutionAndRelatedData(currentChildExecutionEntity, "任务动态跳转");
    
            // 2.创建新的执行实例,并绑定到toActivityId
            ExecutionEntity childExecution = executionEntityManager.createChildExecution(executionEntity);
            childExecution.setCurrentFlowElement(toFlowElement);
    
            // 执行流程操作
            Context.getAgenda().planContinueProcessOperation(childExecution);
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    DynamicJumpService

    @Service
    public class DynamicJumpService {
    
        @Autowired
        protected ManagementService managementService;
    
        public void jumpTask(String processInstanceId, String fromActivityId, String toActivityId) {
            DynamicJumpCmd dynamicJumpCmd = new DynamicJumpCmd(processInstanceId, fromActivityId, toActivityId);
            managementService.executeCommand(dynamicJumpCmd);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    @RequestMapping("/jump")
    public void jump(String processInstanceId, String from, String to) {
        dynamicJumpService.jumpTask(processInstanceId, from, to);
    }
    
    @RequestMapping("/complete")
    public void complete(String processInstanceId, String assignee) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        Task task = taskService.createTaskQuery().processDefinitionKey("leave")
                .processInstanceId(processInstanceId)
                .singleResult();
    
        taskService.setAssignee(task.getId(), assignee);
        taskService.complete(task.getId());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    四:Test

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    • insert into ACT_RU_EXECUTION
    • insert into ACT_RU_TASK
    • update ACT_RU_EXECUTION:更新子执行实例
    • delete from ACT_RU_TASK
    • delete from ACT_RU_EXECUTION

    在这里插入图片描述

    insert into ACT_RU_EXECUTION (ID_, REV_, PROC_INST_ID_, BUSINESS_KEY_, PROC_DEF_ID_, ACT_ID_, IS_ACTIVE_, IS_CONCURRENT_, IS_SCOPE_,IS_EVENT_SCOPE_, IS_MI_ROOT_, PARENT_ID_, SUPER_EXEC_, ROOT_PROC_INST_ID_, SUSPENSION_STATE_, TENANT_ID_, NAME_, START_TIME_, START_USER_ID_, IS_COUNT_ENABLED_, EVT_SUBSCR_COUNT_, TASK_COUNT_, JOB_COUNT_, TIMER_JOB_COUNT_, SUSP_JOB_COUNT_, DEADLETTER_JOB_COUNT_, VAR_COUNT_, ID_LINK_COUNT_, APP_VERSION_) values ( ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
    2d360fca-4c7b-11ee-88f4-7221aa3b96b2(String), 0930c9a5-4c79-11ee-88f4-7221aa3b96b2(String), null, leave:1:37059261-4246-11ee-b072-8251588a22ea(String), hr(String), true(Boolean), false(Boolean), false(Boolean), false(Boolean), false(Boolean), 0930c9a5-4c79-11ee-88f4-7221aa3b96b2(String), null, 0930c9a5-4c79-11ee-88f4-7221aa3b96b2(String), 1(Integer), (String), null, 2023-09-06 14:04:01.751(Timestamp), null, false(Boolean), 0(Integer), 0(Integer), 0(Integer), 0(Integer), 0(Integer), 0(Integer), 0(Integer), 0(Integer), null
    
    
    
    insert into ACT_RU_TASK (ID_, REV_, NAME_, BUSINESS_KEY_, PARENT_TASK_ID_, DESCRIPTION_, PRIORITY_, CREATE_TIME_, OWNER_, ASSIGNEE_, DELEGATION_, EXECUTION_ID_, PROC_INST_ID_, PROC_DEF_ID_, TASK_DEF_KEY_, DUE_DATE_, CATEGORY_, SUSPENSION_STATE_, TENANT_ID_, FORM_KEY_, CLAIM_TIME_, APP_VERSION_) values (?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
    53d7968b-4c7b-11ee-88f4-7221aa3b96b2(String), 人事审批(String), null, null, null, 50(Integer), 2023-09-06 14:05:06.638(Timestamp), null, null, null, 2d360fca-4c7b-11ee-88f4-7221aa3b96b2(String), 0930c9a5-4c79-11ee-88f4-7221aa3b96b2(String), leave:1:37059261-4246-11ee-b072-8251588a22ea(String), hr(String), null, null, 1(Integer), (String), null, null, null
    
    update ACT_RU_EXECUTION set REV_ = 3, ACT_ID_ = ‘manager’  where ID_ = '0932ec86-4c79-11ee-88f4-7221aa3b96b2' and REV_ = 2;
    
    delete from ACT_RU_TASK where ID_ = '0970dfe9-4c79-11ee-88f4-7221aa3b96b2' and REV_ = 1;
    
    delete from ACT_RU_EXECUTION where ID_ = '0932ec86-4c79-11ee-88f4-7221aa3b96b2' and REV_ = 3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    五:继续审批

    节点跳转之后继续审批一下,验证节点跳转是否有问题。
    在这里插入图片描述

  • 相关阅读:
    强化学习环境报错解决
    双Buff加持!无GPU畅玩ControlNet Union,一个模型搞定10+图像控制
    【JVM基础】虚拟机栈
    【vue3】10.跟着官网学习vue3-表单输入绑定,双向绑定,封装input组件,封装ant-design-vue的input组件
    JS 这次真的可以禁止常量修改了!
    【Linux】多线程基础
    在 uni-app 中选中奇偶子元素
    Gerrit | 重磅! 2.x 版本升级到 3.x 版本----转
    OpenGL编程学习笔记——glBegin
    『heqingchun-ubuntu系统下安装nvidia显卡驱动3种方法』
  • 原文地址:https://blog.csdn.net/vbirdbest/article/details/132714353