つながりを学ぶ
[ワークフロー Activiti7] 3. Activiti7 のロールバックと署名
[ワークフロー Activiti7] 4. Activiti7 の終了/終了プロセス
アクティビティ - 指定したノードにジャンプしてロールバックする
Activiti6.0 バージョンのプロセスの取り消し、ジャンプ、ロールバックなどの操作
ativiti6.0 により、プロセス ノードの自由なジャンプ、拒否/反対/前のノードへの復帰、プロセスの取り消し、ジャンプ、ロールバック、その他の操作が可能になります (一般実装、個人テストに利用可能) - Nuggets < a i=1>Activiti6.0 ソース コード分析: ノードの任意のジャンプ - 簡単な本
ActivitiワークフローコアJava APIの使用法、Activitiリターン、スキップ、強制終了実装、BpmnModelモデル操作、フローチャート実行、プロセス変数取得、候補操作
Activiti7 ワークフローが元のプロセスによって終了されない
アクティビティ学習 (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);
}