アクティビティプロセスのロールバックとプロセスの終了

つながりを学ぶ

[ワークフロー Activiti7] 3. Activiti7 のロールバックと署名
[ワークフロー Activiti7] 4. Activiti7 の終了/終了プロセス

アクティビティ - 指定したノードにジャンプしてロールバックする

Activiti6.0 バージョンのプロセスの取り消し、ジャンプ、ロールバックなどの操作

ativiti6.0 により、プロセス ノードの自由なジャンプ、拒否/反対/前のノードへの復帰、プロセスの取り消し、ジャンプ、ロールバック、その他の操作が可能になります (一般実装、個人テストに利用可能) - Nuggets < a i=1>Activiti6.0 ソース コード分析: ノードの任意のジャンプ - 簡単な本

ActivitiワークフローコアJava APIの使用法、Activitiリターン、スキップ、強制終了実装、BpmnModelモデル操作、フローチャート実行、プロセス変数取得、候補操作

アクティビティのロールバックとジャンプノード

Activiti7 ワークフローが元のプロセスによって終了されない

2020-10-21アクティビティの副署名と副署名の拒否

アクティビティ学習 (22) - 通常タスクのノードジャンプ (リターン、フリージャンプ機能)

プロセスのロールバック

前のノードに戻る

フローチャート

最初から最後まで流れる、中央に 3 つのノードのみがある最も単純なフローチャート
ここに画像の説明を挿入します
次の bpmn.xml を簡単に分析します。

  • 開始タグは startEvent を使用します (ID はデフォルトの startEvent1 で、手動で指定できます)。
  • タスク ノード ラベルは userTask を使用し、各 userTask には独自の id 属性 (ID はデフォルトでランダムに生成され、手動で指定できます) と name 属性 (手動で定義) があります。
  • 2 つの userTask は、sequenceFlow タグを使用して接続されます。sequenceFlow タグには、sourceRef (ソース ノード ID) と targetRef (ターゲット ノード ID)、および独自の id 属性があります (ID はデフォルトでランダムに生成され、手動で指定できます)。
  • 終了タグは endEvent を使用します (ID はデフォルトでランダムに生成され、手動で指定できます)
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef">
  <process id="back-key" name="back-name" isExecutable="true">
    <startEvent id="start"></startEvent>
    <userTask id="userTask1" name="填写请假单" activiti:assignee="zs">
      <extensionElements>
        <modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
      </extensionElements>
    </userTask>
    <sequenceFlow id="sid-2CD9ABD5-E163-496D-8827-233545140F44" sourceRef="start" targetRef="userTask1"></sequenceFlow>
    <userTask id="userTask2" name="部门经理审批" activiti:assignee="ls">
      <extensionElements>
        <modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
      </extensionElements>
    </userTask>
    <sequenceFlow id="sid-EFF2B7AB-E8E4-4E77-A026-1A3CE1DD3BC3" sourceRef="userTask1" targetRef="userTask2"></sequenceFlow>
    <userTask id="userTask3" name="人事审批" activiti:assignee="ww">
      <extensionElements>
        <modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
      </extensionElements>
    </userTask>
    <sequenceFlow id="sid-5F827E4F-19A2-4693-8A53-32EF9E493C2C" sourceRef="userTask2" targetRef="userTask3"></sequenceFlow>
    <endEvent id="end"></endEvent>
    <sequenceFlow id="sid-A9D46FB6-2D46-47AA-A57D-EAF6495AFC2F" sourceRef="userTask3" targetRef="end"></sequenceFlow>
  </process> 
</definitions>
プロセスを開始する

上記のフローチャートをデプロイしてプロセスを開始します

/**
 * 开启流程
 */
@Test
public void test_03() {
    
    
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    RuntimeService runtimeService = processEngine.getRuntimeService();
    TaskService taskService = processEngine.getTaskService();

    HashMap<String, Object> vars = new HashMap<>();
    runtimeService.startProcessInstanceByKey("back-key", vars);

}
通常の承認

通常承認から人事承認まで

/**
  * 完成任务
  */
 @Test
 public void test_04() {
    
    
     ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
     RuntimeService runtimeService = processEngine.getRuntimeService();
     TaskService taskService = processEngine.getTaskService();

     Task task = taskService.createTaskQuery().processInstanceId("2501").singleResult();

     taskService.complete(task.getId());
 }

この時点でのテーブルact_ru_executionは次のとおりです (サブ実行インスタンス ID: 2502 の act_id が userTask3 であり、この userTask3 が現在のアクティビティ ID であることがわかります)この実行インスタンスの userTask ラベル ID):
ここに画像の説明を挿入します
テーブル act_ru_task は次のとおりです (現在のタスクの task_def_key が ID であることがわかります) userTask タグの ここに画像の説明を挿入します
テーブルact_hi_taskinst は次のとおりです (履歴タスクの task_def_key が userTask タグの ID であることがわかります。 は次のとおりです (履歴アクティビティ インスタンスの act_id であることがわかります)は userTask タグの ID です): テーブルact_hi_taskinst テーブルの id_ は taskId です):
ここに画像の説明を挿入します
act_hi_actinst
ここに画像の説明を挿入します

このとき、指定された履歴プロセス インスタンスに対応するタスクをクエリします。

/*
    查询指定历史流程实例所对应的任务
 */
@Test
public void testHistoryTaskInstance() {
    
    

    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    HistoryService historyService = engine.getHistoryService();
    TaskService taskService = engine.getTaskService();

    // 查询指定历史流程实例所对应的所有任务
    List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery()
            .processInstanceId("2501")
            .orderByHistoricTaskInstanceStartTime()
            .asc()
            .list();

    HashMap<String, Object> taskId2CommentMap = new HashMap<>();
    historicTaskInstanceList.stream().forEach(hisTaskInst -> {
    
    
        List<Comment> taskComments = taskService.getTaskComments(hisTaskInst.getId());
        if (!CollectionUtils.isEmpty(taskComments)) {
    
    
            taskId2CommentMap.put(hisTaskInst.getId(), taskComments.get(0).getFullMessage());
        }
    });

    for (HistoricTaskInstance historicTaskInstance : historicTaskInstanceList) {
    
    
        log.info("历史任务实例id: {}", historicTaskInstance.getId());
        log.info("历史任务名: {}", historicTaskInstance.getName());
        log.info("任务办理人: {}", historicTaskInstance.getAssignee());
        log.info("历史任务开始时间: {}", DateUtils.fmtDate(historicTaskInstance.getStartTime()));
        log.info("历史任务结束时间: {}", DateUtils.fmtDate(historicTaskInstance.getEndTime()));
        if (taskId2CommentMap.get(historicTaskInstance.getId()) != null) {
    
    
            log.info("审批意见: {}", taskId2CommentMap.get(historicTaskInstance.getId()));
        }
        log.info("---------------------------------------------");
    }
}

出力は次のとおりです。

历史任务实例id: 2505
历史任务名: 填写请假单
任务办理人: zs
历史任务开始时间: 2023-11-25 23:27:59
历史任务结束时间: 2023-11-25 23:28:33
---------------------------------------------
历史任务实例id: 5002
历史任务名: 部门经理审批
任务办理人: ls
历史任务开始时间: 2023-11-25 23:28:33
历史任务结束时间: 2023-11-25 23:28:42
---------------------------------------------
历史任务实例id: 7502
历史任务名: 人事审批
任务办理人: ww
历史任务开始时间: 2023-11-25 23:28:42
历史任务结束时间: 
---------------------------------------------

このとき、指定された履歴プロセス インスタンスに対応するタスクをクエリします。

/*
    查询指定历史流程实例所对应的所有活动实例
 */
@Test
public void testHistoryActivityInstance() {
    
    
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    HistoryService historyService = engine.getHistoryService();

    // 查询指定历史流程实例所对应的所有活动实例
    List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
            .processInstanceId("2501")
            .orderByHistoricActivityInstanceStartTime()
            .asc()
            .list();

    for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
    
    
        log.info("历史活动实例id: {}", historicActivityInstance.getId());
        log.info("历史活动名: {}", historicActivityInstance.getActivityName());
        log.info("历史活动类型: {}", historicActivityInstance.getActivityType());
        log.info("任务活动办理人: {}", historicActivityInstance.getAssignee());
        log.info("历史活动开始时间: {}", DateUtils.fmtDate(historicActivityInstance.getStartTime()));
        log.info("历史活动结束时间: {}", DateUtils.fmtDate(historicActivityInstance.getEndTime()));
        log.info("---------------------------------------------");
    }

}

出力は次のとおりです。

历史活动实例id: 2503
历史活动名: null
历史活动类型: startEvent
任务活动办理人: null
历史活动开始时间: 2023-11-25 23:27:59
历史活动结束时间: 2023-11-25 23:27:59
---------------------------------------------
历史活动实例id: 2504
历史活动名: 填写请假单
历史活动类型: userTask
任务活动办理人: zs
历史活动开始时间: 2023-11-25 23:27:59
历史活动结束时间: 2023-11-25 23:28:33
---------------------------------------------
历史活动实例id: 5001
历史活动名: 部门经理审批
历史活动类型: userTask
任务活动办理人: ls
历史活动开始时间: 2023-11-25 23:28:33
历史活动结束时间: 2023-11-25 23:28:42
---------------------------------------------
历史活动实例id: 7501
历史活动名: 人事审批
历史活动类型: userTask
任务活动办理人: ww
历史活动开始时间: 2023-11-25 23:28:42
历史活动结束时间: 
---------------------------------------------
プロセスのロールバック

フォールバックの考え方は次のとおりです:动态更改节点的流向。水に出会ったらまず橋を架け、川を渡るときは最後に橋を壊します。

まず、アクティビティ プロセス要素の一般的な構造を簡単に理解しましょう。

  • FlowElement はフローチャート内の要素で、FlowNode流程节点SequenceFlow顺序流 に分かれています。
    • そのうちFlowNode分为3种类型:事件(Event)、网关(Gateway)、活动(Activity) は bpmn グラフの各ノードに対応し、2 つの属性が FlowNoe クラスで定義されています: incomingFlows と < a i=3> (すべての SequenceFlow タイプ、bpmn ダイアグラムのプロセス行に対応)outgoingFlows
    • そのうちSequenceFlow顺序流は、sourceRef と targetRef を通じて 2 つの接続されたノードの ID を記録します

ここに画像の説明を挿入します
以下は比較的完全な図です (わかりやすくするために、多くのサブカテゴリは削除されています)。
ここに画像の説明を挿入します

具体的な操作は以下の通りです。

  • 現在のノードに関する情報を取得します
  • 現在のノードの前のノードの情報を取得します
  • 現在のノードのフロー方向を保存します
  • 現在のノードから前のノードを指す新しいフロー方向を作成します。
  • 現在のノードのフロー方向を、上で新しく作成したフロー方向に設定します。
  • 現在のノードがタスクを完了します
  • 現在のノードのフロー方向を復元します
  • 前のノードのエグゼキュータを取得する
  • 前のノードの担当者を前の実行者に設定します
@Test
public void back() throws Exception {
    
    
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = processEngine.getTaskService();
    Task task = taskService.createTaskQuery().processInstanceId("2501").singleResult();

    backProcess(task);
}

/**
 * 驳回 / 回退
 * 按照这种方法,可以回退至任意节点
 * @param task
 * @throws Exception
 */
public void backProcess(Task task) throws Exception {
    
    
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    HistoryService historyService = processEngine.getHistoryService();
    RepositoryService repositoryService = processEngine.getRepositoryService();
    TaskService taskService = processEngine.getTaskService();

    String processInstanceId = task.getProcessInstanceId();

    // 获取所有历史任务(按创建时间降序)
    List<HistoricTaskInstance> hisTaskList = historyService
            .createHistoricTaskInstanceQuery()
            .processInstanceId(processInstanceId)
            .orderByTaskCreateTime()
            .desc()
            .list();

    // 获取当前流程实例的所有的历史活动实例
    List<HistoricActivityInstance> hisActivityList = historyService
            .createHistoricActivityInstanceQuery()
            .processInstanceId(processInstanceId).list();

    // 如果历史活动实例为空 或者 数量才1个 说明不存在或者才刚开启流程
    if (CollectionUtils.isEmpty(hisTaskList) || hisTaskList.size() < 2) {
    
    
        return;
    }

    // 当前任务(通过历史任务按时间倒序,查询第1个)
    HistoricTaskInstance currentTask = hisTaskList.get(0);
    // 前一个任务(通过历史任务按时间倒序,查询第2个)
    HistoricTaskInstance lastTask = hisTaskList.get(1);
    
    //  当前活动(遍历当前流程实例的所有的历史活动实例, 根据当前任务的id(就是taskId)与历史活动实例的taskId相等, 找到对应的历史活动实例)
    HistoricActivityInstance currentActivity = hisActivityList.stream().filter(e -> currentTask.getId().equals(e.getTaskId())).collect(Collectors.toList()).get(0);
    //  前一个活动(遍历当前流程实例的所有的历史活动实例, 根据前一个任务的id(就是taskId)与历史活动实例的taskId相等, 找到对应的历史活动实例)
    HistoricActivityInstance lastActivity = hisActivityList.stream().filter(e -> lastTask.getId().equals(e.getTaskId())).collect(Collectors.toList()).get(0);

    // 使用repositoryService, 根据流程定义id获取 【bpmn模型对象】
    BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());

    //  获取前一个活动节点(从bpmn模型对象中,根据活动实例的活动id(就是标签的id属性)找到FlowNode。所以活动实例其实就是把taskId和活动id给结合起来了)
    FlowNode lastFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(lastActivity.getActivityId());
    //  获取当前活动节点(从bpmn模型对象中,根据活动实例的活动id(就是标签的id属性)找到FlowNode。所以活动实例其实就是把taskId和活动id给结合起来了)
    FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivity.getActivityId());

    //  临时保存当前活动的原始方向(这说明每1个FlowNode都包含1个SequenceFlow的outgoingFlows集合)
    List<SequenceFlow> originalSequenceFlowList = new ArrayList<>();
    originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
    //  清理活动方向
    currentFlowNode.getOutgoingFlows().clear();

    //  建立新方向
    SequenceFlow newSequenceFlow = new SequenceFlow();
    newSequenceFlow.setId("newSequenceFlowId");
    newSequenceFlow.setSourceFlowElement(currentFlowNode); // 当前flowNode
    newSequenceFlow.setTargetFlowElement(lastFlowNode);    // 上一个flowNode
    List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
    newSequenceFlowList.add(newSequenceFlow);
    //  当前节点指向新的方向
    currentFlowNode.setOutgoingFlows(newSequenceFlowList);

    taskService.addComment(task.getId(), task.getProcessInstanceId(), "部门经理意见未填写, 回退至上一节点");

    //  完成当前任务(将会沿着新给定的方向流转到指定的节点)
    taskService.complete(task.getId());

    //  重新查询当前任务
    Task nextTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
    if (null != nextTask) {
    
    
        // 因为现在已经退回到上一个节点了, 这里从历史任务中获取上1个节点的任务负责人, 设置到当前任务中
        taskService.setAssignee(nextTask.getId(), lastTask.getAssignee());
    }

    //  恢复原始方向
    currentFlowNode.setOutgoingFlows(originalSequenceFlowList);
}

この時点でのテーブルact_ru_executionは次のとおりです (現在の実行インスタンスの act_id が userTask2 であることがわかり、前のノードに戻ったことを示しています)。< /span>
ここに画像の説明を挿入します

テーブルact_ru_task は次のとおりです (現在のタスクは前のノードにロールバックされています)。
ここに画像の説明を挿入します

act_hi_taskinst如下:
ここに画像の説明を挿入します

act_hi_actinst如下:
ここに画像の説明を挿入します

承認を完了する

最後に、さらに 2 つの承認を完了すると、プロセス全体が正常に完了します。

現時点では、テーブルact_ru_executionデータは空です (プロセスは終了しています)

テーブルact_ru_taskデータは空です (プロセスが終了しました)

act_hi_taskinst如下:
ここに画像の説明を挿入します

act_hi_actinst如下:
ここに画像の説明を挿入します

最初のタスクに戻る

操作方法は上記とほぼ同じですが、bpmnModelに基づいてFlowNodeを取得する際に、履歴タスクインスタンスのtaskDefinitionKey(act_hi_taskintテーブルのtask_def_keyフィールドに相当)に基づいて取得する点が異なります。実際にはタグの ID (履歴) アクティビティ インスタンスの activityId (act_hi_actinst テーブルの act_id フィールドに対応)

/**
 * 跳到最开始的任务节点(直接打回)
 * @param task 当前任务
 */
public void jumpToStart(Task task) {
    
    
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    HistoryService historyService = processEngine.getHistoryService();
    RepositoryService repositoryService = processEngine.getRepositoryService();
    TaskService taskService = processEngine.getTaskService();
 
    String processInstanceId = task.getProcessInstanceId();
 
    //  获取所有历史任务(按创建时间升序)
    List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
            .processInstanceId(processInstanceId)
            .orderByTaskCreateTime()
            .asc()
            .list();
 
    if (CollectionUtils.isEmpty(hisTaskList) || hisTaskList.size() < 2) {
    
    
        return;
    }
 
    //  第一个任务
    HistoricTaskInstance startTask = hisTaskList.get(0);
    //  当前任务
    HistoricTaskInstance currentTask = hisTaskList.get(hisTaskList.size() - 1);
 
    BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
 
    //  获取第一个活动节点
    FlowNode startFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(startTask.getTaskDefinitionKey());
    //  获取当前活动节点
    FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentTask.getTaskDefinitionKey());
 
    //  临时保存当前活动的原始方向
    List<SequenceFlow> originalSequenceFlowList = new ArrayList<>();
    originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
    //  清理活动方向
    currentFlowNode.getOutgoingFlows().clear();
 
    //  建立新方向
    SequenceFlow newSequenceFlow = new SequenceFlow();
    newSequenceFlow.setId("newSequenceFlowId");
    newSequenceFlow.setSourceFlowElement(currentFlowNode);
    newSequenceFlow.setTargetFlowElement(startFlowNode);
    List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
    newSequenceFlowList.add(newSequenceFlow);
    //  当前节点指向新的方向
    currentFlowNode.setOutgoingFlows(newSequenceFlowList);
 
    //  完成当前任务
    taskService.complete(task.getId());
 
    //  重新查询当前任务
    Task nextTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
    if (null != nextTask) {
    
    
        taskService.setAssignee(nextTask.getId(), startTask.getAssignee());
    }
 
    //  恢复原始方向
    currentFlowNode.setOutgoingFlows(originalSequenceFlowList);
}

プロセスの終了/終了

アイデア: ロールバックと同じアイデアで、現在のノードから最後のセクション (EndEvent) に直接ジャンプします。

/**
 * 结束任务
 * @param taskId    当前任务ID
 */
public void endTask(String taskId) {
    
    

    //  当前任务
    Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
 
    BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
    List endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
    FlowNode endFlowNode = endEventList.get(0);
    FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
 
    //  临时保存当前活动的原始方向
    List originalSequenceFlowList = new ArrayList<>();
    originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
    //  清理活动方向
    currentFlowNode.getOutgoingFlows().clear();
 
    // 建立新方向
    SequenceFlow newSequenceFlow = new SequenceFlow();
    newSequenceFlow.setId("newSequenceFlowId");
    newSequenceFlow.setSourceFlowElement(currentFlowNode);
    newSequenceFlow.setTargetFlowElement(endFlowNode);
    List newSequenceFlowList = new ArrayList<>();
    newSequenceFlowList.add(newSequenceFlow);
    
    // 当前节点指向新的方向
    currentFlowNode.setOutgoingFlows(newSequenceFlowList);
 
    // 完成当前任务
    taskService.complete(task.getId());
 
    // 可以不用恢复原始方向,不影响其它的流程
    currentFlowNode.setOutgoingFlows(originalSequenceFlowList);
}

おすすめ

転載: blog.csdn.net/qq_16992475/article/details/134621427