背景
不親切なプロセス設定の問題については、国内のDingTalkがbpmnの仕様とは関係なく、プロセスモデリングモード一式を別途設計・実装しており、それを誰かが模倣してオープンソース化しました(https://github.com/StavinLi/ワークフロー -Vue3 ) の効果図は次のとおりです:
実装の一般原則は、無限にネストされた子ノードに基づいており、json データを出力し、それをバックエンドに渡します。バックエンドが解析した後、Camunda エンジンの API を呼び出します。それをプロセス モデルに変換し、永続化します。
前回の記事ではフロントエンドの統合について紹介しましたが、今回はフロントエンドからjsonデータを解析し、CamundaエンジンのAPIを呼び出してプロセスモデルに変換する方法に焦点を当てます。
カムンダ API
中心的な問題については、Camunda Model API (公式ドキュメントのアドレス: https://docs.camunda.org/manual/7.19/user-guide/model-api/bpmn ) を使用して、カスタム JSON データがプロセス モデルに変換されます。- モデル API/
公式が提供する API には 2 つのセットがあり、1 つはよく規制された標準 API、もう 1 つは Fluent API (Fluent Builder API) であることに注意してください。前者はすべての操作を完了できますが、使用が面倒ですが、後者はほとんどの機能を完了できますが、使い方は簡単です。
標準API
ここでは公式ドキュメントから例を挙げて使用方法を簡単に見ていき、後ほど実戦用の変換作業を組み合わせていきます。
上の図に示すように、ユーザー タスク リンクを 1 つだけ含む最小限のプロセスを作成します。関連する API 呼び出しは次のとおりです。
// 创建一个空白模型
BpmnModelInstance modelInstance = Bpmn.createEmptyModel();
Definitions definitions = modelInstance.newInstance(Definitions.class);
definitions.setTargetNamespace("http://camunda.org/examples");
modelInstance.setDefinitions(definitions);
// 创建流程元素
Process process = createElement(definitions, "process-with-one-task", Process.class);
// 创建开始事件,用户任务,结束事件
StartEvent startEvent = createElement(process, "start", StartEvent.class);
UserTask task1 = createElement(process, "task1", UserTask.class);
task1.setName("User Task");
EndEvent endEvent = createElement(process, "end", EndEvent.class);
// 创建各个元素之间的连接
createSequenceFlow(process, startEvent, task1);
createSequenceFlow(process, task1, endEvent);
// 验证模型并写入到文件中
Bpmn.validateModel(modelInstance);
File file = File.createTempFile("bpmn-model-api-", ".bpmn");
Bpmn.writeModelToFile(file, modelInstance);
2 つの並列タスクを含む単純なプロセスを作成する場合、関連する API 呼び出しは次のとおりです。
// create an empty model
BpmnModelInstance modelInstance = Bpmn.createEmptyModel();
Definitions definitions = modelInstance.newInstance(Definitions.class);
definitions.setTargetNamespace("http://camunda.org/examples");
modelInstance.setDefinitions(definitions);
// create elements
StartEvent startEvent = createElement(process, "start", StartEvent.class);
ParallelGateway fork = createElement(process, "fork", ParallelGateway.class);
ServiceTask task1 = createElement(process, "task1", ServiceTask.class);
task1.setName("Service Task");
UserTask task2 = createElement(process, "task2", UserTask.class);
task2.setName("User Task");
ParallelGateway join = createElement(process, "join", ParallelGateway.class);
EndEvent endEvent = createElement(process, "end", EndEvent.class);
// create flows
createSequenceFlow(process, startEvent, fork);
createSequenceFlow(process, fork, task1);
createSequenceFlow(process, fork, task2);
createSequenceFlow(process, task1, join);
createSequenceFlow(process, task2, join);
createSequenceFlow(process, join, endEvent);
// validate and write model to file
Bpmn.validateModel(modelInstance);
File file = File.createTempFile("bpmn-model-api-", ".bpmn");
Bpmn.writeModelToFile(file, modelInstance);
次のように、いくつかのツール クラス メソッドが中間に関与します。
// 创建子元素
protected <T extends BpmnModelElementInstance> T createElement(BpmnModelElementInstance parentElement, String id, Class<T> elementClass) {
T element = parentElement.getModelInstance().newInstance(elementClass);
element.setAttributeValue("id", id, true);
parentElement.addChildElement(element);
return element;
}
// 创建顺序边
public SequenceFlow createSequenceFlow(Process process, FlowNode from, FlowNode to) {
String identifier = from.getId() + "-" + to.getId();
SequenceFlow sequenceFlow = createElement(process, identifier, SequenceFlow.class);
process.addChildElement(sequenceFlow);
sequenceFlow.setSource(from);
from.getOutgoing().add(sequenceFlow);
sequenceFlow.setTarget(to);
to.getIncoming().add(sequenceFlow);
return sequenceFlow;
}
流暢なAPI
上記からわかるように、標準 API での操作は比較的面倒です。公式セカンダリパッケージのsmooth APIを見てみましょう。
最も単純なプロセスは 5 行のコードで完了します。
BpmnModelInstance modelInstance = Bpmn.createProcess()
.startEvent()
.userTask()
.endEvent()
.done();
分岐を伴う処理はわずか数10行のコードであり、標準APIに比べて大幅に簡略化されています。
BpmnModelInstance modelInstance = Bpmn.createProcess()
.startEvent()
.userTask()
.parallelGateway()
.scriptTask()
.endEvent()
.moveToLastGateway()
.serviceTask()
.endEvent()
.done();
比較的複雑なプロセスにも対応できます。
BpmnModelInstance modelInstance = Bpmn.createProcess()
.startEvent()
.userTask()
.exclusiveGateway()
.name("What to do next?")
.condition("Call an agent", "#{action = 'call'}")
.scriptTask()
.endEvent()
.moveToLastGateway()
.condition("Create a task", "#{action = 'task'}")
.serviceTask()
.endEvent()
.done();
なお、Fluent APIは完成に近づいており、すべての機能を実現できるわけではないと公式声明で明記されており、現時点でサポートできる基本要素は以下の通りです。
- プロセス
- イベントを開始します
- 専用ゲートウェイ
- パラレルゲートウェイ
- スクリプトタスク
- サービスタスク
- ユーザータスク
- シグナルイベント定義
- 終了イベント
- サブプロセス
テスト検証
次のように、Fluent API を使用して簡単なテストが実行されました。
String name = entity.getName();
// 向模型新增流程
BpmnModelInstance modelInstance = Bpmn.createProcess()
.name(name)
.executable()
.startEvent()
.userTask()
.name("Some work to do")
.endEvent()
.done();
// 发布
repositoryService.createDeployment().name(name)
.addModelInstance(name,modelInstance).deploy();
ライブラリテーブルを見ると、確かにプロセスは正常に解放されています。
小さな問題があります。生成された XML 内のプロセス名が中国語で文字化けしているため、この部分が最初に配置され、後で必要に応じて解決されます。
技術的ソリューション
実装のアイデア
上記の技術の事前調査とテスト検証に基づいて、json の変換にはシンプルでスムーズな API が推奨されますが、スムーズな API で実現できない機能が発生した場合は、標準 API を使用して実装を支援します。最初は比較的シンプルな機能開発プロセスを採用し、徐々に機能を充実させ改善していきます。
次に、Camunda モデルと API から開始して、フロントエンドから json データを処理します。処理中に、json データ構造は最適化され、同期的に再構築されます。最終的には、オープンソース プロジェクトは大きく異なるものになることが予想されます。
ユースケースを実現する
フロントエンド モデリングは次の図に示されており、開始リンクと終了リンクに加えて、3 つのリンク、2 つのユーザー タスク、および 1 つのサービス タスクがあります。
フロントエンドによって生成される JSON データ形式は次のとおりです。
{
"nodeName": "发起人",
"type": 0,
"priorityLevel": "",
"settype": "",
"selectMode": "",
"selectRange": "",
"directorLevel": "",
"examineMode": "",
"noHanderAction": "",
"examineEndDirectorLevel": "",
"ccSelfSelectFlag": "",
"conditionList": [
],
"nodeUserList": [
],
"childNode": {
"nodeName": "审核人",
"error": false,
"type": 1,
"settype": 2,
"selectMode": 0,
"selectRange": 0,
"directorLevel": 1,
"examineMode": 1,
"noHanderAction": 2,
"examineEndDirectorLevel": 0,
"childNode": {
"nodeName": "抄送人",
"type": 2,
"ccSelfSelectFlag": 1,
"childNode": null,
"nodeUserList": [
],
"error": false
},
"nodeUserList": [
]
},
"conditionNodes": [
]
}
フィールドの意味は次のとおりです。
"nodeName": "发起人",//节点名称
"type": 0,// 0 发起人 1审批 2抄送 3条件 4路由
"priorityLevel": "",// 条件优先级
"settype": "",// 审批人设置 1指定成员 2主管 4发起人自选 5发起人自己 7连续多级主管
"selectMode": "", //审批人数 1选一个人 2选多个人
"selectRange": "", //选择范围 1.全公司 2指定成员 2指定角色
"directorLevel": "", //审批终点 最高层主管数
"examineMode": "", //多人审批时采用的审批方式 1依次审批 2会签
"noHanderAction": "",//审批人为空时 1自动审批通过/不允许发起 2转交给审核管理员
"examineEndDirectorLevel": "", //审批终点 第n层主管
"ccSelfSelectFlag": "", //允许发起人自选抄送人
"conditionList": [], //当审批单同时满足以下条件时进入此流程
"nodeUserList": [], //操作人
"childNode":{
} //子节点
データ構造を解析してみると、子ノードが配列ではなくオブジェクトであることが分かり、実際のニーズに応えられない気がします 分岐があればノードは1つ、後続ノードが複数ある場合は、 、比較のために分岐のあるプロセスが構築されます。
生成されたjsonは次のとおりです。
{
"nodeName": "发起人",
"type": 0,
"priorityLevel": "",
"settype": "",
"selectMode": "",
"selectRange": "",
"directorLevel": "",
"examineMode": "",
"noHanderAction": "",
"examineEndDirectorLevel": "",
"ccSelfSelectFlag": "",
"conditionList": [
],
"nodeUserList": [
],
"childNode": {
"nodeName": "审核人",
"error": false,
"type": 1,
"settype": 2,
"selectMode": 0,
"selectRange": 0,
"directorLevel": 1,
"examineMode": 1,
"noHanderAction": 2,
"examineEndDirectorLevel": 0,
"childNode": {
"nodeName": "路由",
"type": 4,
"childNode": {
"nodeName": "抄送人",
"type": 2,
"ccSelfSelectFlag": 1,
"childNode": null,
"nodeUserList": [
],
"error": false
},
"conditionNodes": [
{
"nodeName": "条件1",
"error": true,
"type": 3,
"priorityLevel": 1,
"conditionList": [
],
"nodeUserList": [
],
"childNode": null
},
{
"nodeName": "条件2",
"type": 3,
"priorityLevel": 2,
"conditionList": [
],
"nodeUserList": [
],
"childNode": null
}
]
},
"nodeUserList": [
]
},
"conditionNodes": [
]
}
データを比較すると、フロントエンド オープンソース プロジェクトの json データ構造設計では、ブランチ ノード、各ブランチ、集計ノードを 1 つのノードとみなし、ブランチが配列としてconditionNodes 属性に入れられていることがわかります。
開発プラットフォーム情報
プラットフォーム名: One Two Three 開発プラットフォーム
紹介: エンタープライズ レベルの総合開発プラットフォーム
設計情報: csdn コラム
オープン ソース アドレス: Gitee
オープン ソース プロトコル: MIT
オープン ソースは簡単ではありません。お気に入り、いいね、コメントへようこそ。