@logicflow/engine 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +3 -15
- package/cjs/FlowModel.js +57 -21
- package/cjs/Scheduler.js +21 -48
- package/cjs/recorder/index.js +7 -12
- package/es/FlowModel.d.ts +50 -10
- package/es/FlowModel.js +57 -21
- package/es/Scheduler.d.ts +18 -7
- package/es/Scheduler.js +21 -48
- package/es/recorder/index.js +7 -12
- package/lib/main.js +1 -1
- package/package.json +1 -1
- package/types/EventEmitter.d.ts +0 -7
- package/types/FlowModel.d.ts +0 -104
- package/types/Scheduler.d.ts +0 -51
- package/types/constant/LogCode.d.ts +0 -12
- package/types/constant/constant.d.ts +0 -14
- package/types/expression/browserVm.d.ts +0 -2
- package/types/expression/index.d.ts +0 -2
- package/types/expression/nodeVm.d.ts +0 -2
- package/types/index.d.ts +0 -47
- package/types/nodes/BaseNode.d.ts +0 -109
- package/types/nodes/StartNode.d.ts +0 -5
- package/types/nodes/TaskNode.d.ts +0 -5
- package/types/recorder/index.d.ts +0 -9
- package/types/util/ID.d.ts +0 -2
- package/types/util/global.d.ts +0 -5
- package/types/util/storage.d.ts +0 -6
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## 0.0.2 (2023-08-10)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **engine:** consistent expression evaluation in browser and nodejs ([8f24045](https://github.com/didi/LogicFlow/commit/8f240451dce588b6fa21e639c79e1a0782937ff9))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* add support for specifying the start node and multiple executions ([113ad88](https://github.com/didi/LogicFlow/commit/113ad880cdb564431933eeeed3b661c50fca2a9c))
|
|
17
|
+
* added expression evaluation functionality for Node.js and browser ([a7759f6](https://github.com/didi/LogicFlow/commit/a7759f69d4f7b294397c8502da654ea7c729d930))
|
|
18
|
+
* create logicflow eninge ([c7d80f4](https://github.com/didi/LogicFlow/commit/c7d80f4b4c19cf82af9be49dd8fd44433327db58))
|
|
19
|
+
* **engie:** support parallel execution within workflows ([8e17ea6](https://github.com/didi/LogicFlow/commit/8e17ea614c5c3567e532a67240cf8e0e9110b2bf))
|
|
20
|
+
* **engine:** add comments and sync storage execution ([6fa0904](https://github.com/didi/LogicFlow/commit/6fa0904ddee88254d4af5c246ff0247c988dfdd2))
|
|
21
|
+
* **engine:** add the ability to pause and resume workflows ([7c4e385](https://github.com/didi/LogicFlow/commit/7c4e3855ad0a7af4121de6552be61f690b4e0e6c))
|
|
22
|
+
* **engine:** added workflow scheduling feature ([c2a7044](https://github.com/didi/LogicFlow/commit/c2a704449772445387f324924491f15e526dfc4e))
|
|
23
|
+
* implemented execution record query functionality ([d73aa46](https://github.com/didi/LogicFlow/commit/d73aa46675f35bae5362e6024232d44cbfe5bcaa))
|
package/README.md
CHANGED
|
@@ -4,19 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## 使用方式
|
|
6
6
|
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const flowModel = new LogicFlowEngine({
|
|
11
|
-
graphData: {
|
|
12
|
-
nodes: [],
|
|
13
|
-
edges: [],
|
|
14
|
-
},
|
|
15
|
-
global: {
|
|
16
|
-
// 全局数据
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
flowModel.execute();
|
|
21
|
-
|
|
7
|
+
```shell
|
|
8
|
+
npm test
|
|
22
9
|
```
|
|
10
|
+
|
package/cjs/FlowModel.js
CHANGED
|
@@ -94,9 +94,44 @@ var FlowModel = /** @class */ (function () {
|
|
|
94
94
|
FlowModel.prototype.setStartNodeType = function (startNodeType) {
|
|
95
95
|
this.startNodeType = startNodeType;
|
|
96
96
|
};
|
|
97
|
+
/**
|
|
98
|
+
* 解析LogicFlow图数据,将nodes和edges转换成节点格式。
|
|
99
|
+
* 例如:
|
|
100
|
+
* graphData: {
|
|
101
|
+
* nodes: [
|
|
102
|
+
* { id: 'node1', type: 'StartNode', properties: {} },
|
|
103
|
+
* { id: 'node2', type: 'TaskNode', properties: {} },
|
|
104
|
+
* ],
|
|
105
|
+
* edges: [
|
|
106
|
+
* { id: 'edge1', sourceNodeId: 'node1', targetNodeId: 'node2', properties: {} },
|
|
107
|
+
* ]
|
|
108
|
+
* }
|
|
109
|
+
* 转换成:
|
|
110
|
+
* nodeConfigMap: {
|
|
111
|
+
* node1: {
|
|
112
|
+
* id: 'node1',
|
|
113
|
+
* type: 'StartNode',
|
|
114
|
+
* properties: {},
|
|
115
|
+
* incoming: [],
|
|
116
|
+
* outgoing: [{ id: 'edge1', properties: {}, target: 'node2' }]
|
|
117
|
+
* },
|
|
118
|
+
* node2: {
|
|
119
|
+
* id: 'node2',
|
|
120
|
+
* type: 'TaskNode',
|
|
121
|
+
* properties: {},
|
|
122
|
+
* incoming: [{ id: 'edge1', properties: {}, source: 'node1' }],
|
|
123
|
+
* outgoing: [],
|
|
124
|
+
* }
|
|
125
|
+
* }
|
|
126
|
+
* 此格式方便后续执行时,根据节点id快速找到节点和执行初始化节点模型。
|
|
127
|
+
* 同时此方法还会找到所有的开始节点,方便后续执行时,从开始节点开始执行。
|
|
128
|
+
* @param graphData 流程图数据
|
|
129
|
+
*/
|
|
97
130
|
FlowModel.prototype.load = function (graphData) {
|
|
98
131
|
var _this = this;
|
|
99
132
|
var _a = graphData.nodes, nodes = _a === void 0 ? [] : _a, _b = graphData.edges, edges = _b === void 0 ? [] : _b;
|
|
133
|
+
this.startNodes = [];
|
|
134
|
+
this.nodeConfigMap = new Map();
|
|
100
135
|
nodes.forEach(function (node) {
|
|
101
136
|
if (_this.nodeModelMap.has(node.type)) {
|
|
102
137
|
var nodeConfig = {
|
|
@@ -135,11 +170,10 @@ var FlowModel = /** @class */ (function () {
|
|
|
135
170
|
});
|
|
136
171
|
};
|
|
137
172
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* 例如:
|
|
173
|
+
* 执行流程, 每次执行都会生成一个唯一的executionId,用于区分不同的执行。
|
|
174
|
+
* 同一次执行,这次执行内部的节点执行顺序为并行。内部并行是为了避免异步节点阻塞其他节点的执行。
|
|
175
|
+
* 多次执行,多次执行之间为串行,这里选择串行的原因是避免多次执行之间的数据冲突。
|
|
176
|
+
* example:
|
|
143
177
|
* 一个流程存在着两个开始节点,A和B,A和B的下一个节点都是C,C的下两个节点是D和E。
|
|
144
178
|
* 外部分别触发了A和B的执行,那么A和B的执行是串行的(也就是需要A执行完成后再执行B),但是D和E的执行是并行的。
|
|
145
179
|
* 如果希望A和B的执行是并行的,就不能使用同一个流程模型执行,应该初始化两个。
|
|
@@ -171,7 +205,8 @@ var FlowModel = /** @class */ (function () {
|
|
|
171
205
|
});
|
|
172
206
|
};
|
|
173
207
|
/**
|
|
174
|
-
*
|
|
208
|
+
* 创建节点实例, 每个节点实例都会有一个唯一的taskId。
|
|
209
|
+
* 通过executionId、nodeId、taskId可以唯一确定一个节点的某一次执行。
|
|
175
210
|
* @param nodeId 节点Id
|
|
176
211
|
* @returns 节点示例
|
|
177
212
|
*/
|
|
@@ -191,11 +226,11 @@ var FlowModel = /** @class */ (function () {
|
|
|
191
226
|
FlowModel.prototype.updateGlobalData = function (data) {
|
|
192
227
|
this.globalData = __assign(__assign({}, this.globalData), data);
|
|
193
228
|
};
|
|
229
|
+
/**
|
|
230
|
+
* 在执行完成后,通知外部此次执行完成。
|
|
231
|
+
* 如果还存在待执行的任务,那么继续执行。
|
|
232
|
+
*/
|
|
194
233
|
FlowModel.prototype.onTaskFinished = function (result) {
|
|
195
|
-
var executionId = result.executionId;
|
|
196
|
-
if (executionId !== this.executionId) {
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
234
|
var callback = this.executingInstance.callback;
|
|
200
235
|
if (callback) {
|
|
201
236
|
callback(result);
|
|
@@ -208,26 +243,28 @@ var FlowModel = /** @class */ (function () {
|
|
|
208
243
|
this.isRunning = false;
|
|
209
244
|
}
|
|
210
245
|
};
|
|
246
|
+
/**
|
|
247
|
+
* 从待执行队列中取出需要执行的内容。
|
|
248
|
+
* 会依次判断是否有taskId、nodeId、executionId。
|
|
249
|
+
* 若存在taskId,那么表示恢复执行。
|
|
250
|
+
* 若存在nodeId,那么表示从指定节点开始执行。
|
|
251
|
+
* 若都不存在,那么新建一个executionId,从开始节点开始执行。
|
|
252
|
+
*/
|
|
211
253
|
FlowModel.prototype.createExecution = function () {
|
|
212
254
|
var _this = this;
|
|
213
255
|
var execParams = this.executeQueue.shift();
|
|
214
256
|
this.executingInstance = execParams;
|
|
215
|
-
if (execParams.executionId) {
|
|
216
|
-
this.executionId = execParams.executionId;
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
this.executionId = ID_1.createExecId();
|
|
220
|
-
}
|
|
221
257
|
// 如果有taskId,那么表示恢复执行
|
|
222
|
-
if (execParams.taskId) {
|
|
258
|
+
if (execParams.taskId && execParams.executionId && execParams.nodeId) {
|
|
223
259
|
this.scheduler.resume({
|
|
224
|
-
executionId:
|
|
260
|
+
executionId: execParams.executionId,
|
|
225
261
|
taskId: execParams.taskId,
|
|
226
262
|
nodeId: execParams.nodeId,
|
|
227
263
|
data: execParams.data,
|
|
228
264
|
});
|
|
229
265
|
return;
|
|
230
266
|
}
|
|
267
|
+
var executionId = execParams.executionId || ID_1.createExecId();
|
|
231
268
|
if (execParams.nodeId) {
|
|
232
269
|
var nodeConfig = this.nodeConfigMap.get(execParams.nodeId);
|
|
233
270
|
if (!nodeConfig) {
|
|
@@ -238,13 +275,12 @@ var FlowModel = /** @class */ (function () {
|
|
|
238
275
|
}
|
|
239
276
|
this.startNodes.forEach(function (startNode) {
|
|
240
277
|
_this.scheduler.addTask({
|
|
241
|
-
executionId:
|
|
278
|
+
executionId: executionId,
|
|
242
279
|
nodeId: startNode.id,
|
|
243
280
|
});
|
|
244
|
-
// 所有的开始节点都执行
|
|
245
281
|
});
|
|
246
282
|
this.scheduler.run({
|
|
247
|
-
executionId:
|
|
283
|
+
executionId: executionId,
|
|
248
284
|
});
|
|
249
285
|
};
|
|
250
286
|
return FlowModel;
|
package/cjs/Scheduler.js
CHANGED
|
@@ -75,7 +75,6 @@ var Scheduler = /** @class */ (function (_super) {
|
|
|
75
75
|
_this.taskRunningMap = new Map();
|
|
76
76
|
_this.flowModel = config.flowModel;
|
|
77
77
|
_this.recorder = config.recorder;
|
|
78
|
-
_this.currentTask = null;
|
|
79
78
|
return _this;
|
|
80
79
|
}
|
|
81
80
|
/**
|
|
@@ -143,10 +142,6 @@ var Scheduler = /** @class */ (function (_super) {
|
|
|
143
142
|
});
|
|
144
143
|
});
|
|
145
144
|
};
|
|
146
|
-
// 流程执行过程中出错,停止执行
|
|
147
|
-
Scheduler.prototype.stop = function (data) {
|
|
148
|
-
console.log('stop', data);
|
|
149
|
-
};
|
|
150
145
|
Scheduler.prototype.pushTaskToRunningMap = function (taskParam) {
|
|
151
146
|
var executionId = taskParam.executionId, taskId = taskParam.taskId;
|
|
152
147
|
if (!this.taskRunningMap.has(executionId)) {
|
|
@@ -224,53 +219,31 @@ var Scheduler = /** @class */ (function (_super) {
|
|
|
224
219
|
});
|
|
225
220
|
};
|
|
226
221
|
Scheduler.prototype.next = function (data) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
_this.addTask({
|
|
235
|
-
executionId: data.executionId,
|
|
236
|
-
nodeId: item.target,
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
return [4 /*yield*/, this.saveTaskResult(data)];
|
|
241
|
-
case 1:
|
|
242
|
-
_a.sent();
|
|
243
|
-
this.removeTaskFromRunningMap(data);
|
|
244
|
-
this.run({
|
|
245
|
-
executionId: data.executionId,
|
|
246
|
-
nodeId: data.nodeId,
|
|
247
|
-
taskId: data.taskId,
|
|
248
|
-
});
|
|
249
|
-
return [2 /*return*/];
|
|
250
|
-
}
|
|
222
|
+
var _this = this;
|
|
223
|
+
if (data.outgoing && data.outgoing.length > 0) {
|
|
224
|
+
data.outgoing.forEach(function (item) {
|
|
225
|
+
_this.addTask({
|
|
226
|
+
executionId: data.executionId,
|
|
227
|
+
nodeId: item.target,
|
|
228
|
+
});
|
|
251
229
|
});
|
|
230
|
+
}
|
|
231
|
+
this.saveTaskResult(data);
|
|
232
|
+
this.removeTaskFromRunningMap(data);
|
|
233
|
+
this.run({
|
|
234
|
+
executionId: data.executionId,
|
|
235
|
+
nodeId: data.nodeId,
|
|
236
|
+
taskId: data.taskId,
|
|
252
237
|
});
|
|
253
238
|
};
|
|
254
|
-
/**
|
|
255
|
-
* 为了防止多次添加导致
|
|
256
|
-
*/
|
|
257
239
|
Scheduler.prototype.saveTaskResult = function (data) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
nodeType: data.nodeType,
|
|
266
|
-
timestamp: Date.now(),
|
|
267
|
-
properties: data.properties,
|
|
268
|
-
})];
|
|
269
|
-
case 1:
|
|
270
|
-
_a.sent();
|
|
271
|
-
return [2 /*return*/];
|
|
272
|
-
}
|
|
273
|
-
});
|
|
240
|
+
this.recorder.addTask({
|
|
241
|
+
executionId: data.executionId,
|
|
242
|
+
taskId: data.taskId,
|
|
243
|
+
nodeId: data.nodeId,
|
|
244
|
+
nodeType: data.nodeType,
|
|
245
|
+
timestamp: Date.now(),
|
|
246
|
+
properties: data.properties,
|
|
274
247
|
});
|
|
275
248
|
};
|
|
276
249
|
return Scheduler;
|
package/cjs/recorder/index.js
CHANGED
|
@@ -56,19 +56,14 @@ var Recorder = /** @class */ (function () {
|
|
|
56
56
|
return __awaiter(this, void 0, void 0, function () {
|
|
57
57
|
var executionId, taskId, instanceData;
|
|
58
58
|
return __generator(this, function (_a) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
case 1:
|
|
64
|
-
instanceData = _a.sent();
|
|
65
|
-
if (!instanceData) {
|
|
66
|
-
this.pushExecution(executionId);
|
|
67
|
-
}
|
|
68
|
-
this.pushTaskToExecution(executionId, taskId);
|
|
69
|
-
storage_1.default.setItem(taskId, task);
|
|
70
|
-
return [2 /*return*/];
|
|
59
|
+
executionId = task.executionId, taskId = task.taskId;
|
|
60
|
+
instanceData = this.getExecutionTasks(executionId);
|
|
61
|
+
if (!instanceData) {
|
|
62
|
+
this.pushExecution(executionId);
|
|
71
63
|
}
|
|
64
|
+
this.pushTaskToExecution(executionId, taskId);
|
|
65
|
+
storage_1.default.setItem(taskId, task);
|
|
66
|
+
return [2 /*return*/];
|
|
72
67
|
});
|
|
73
68
|
});
|
|
74
69
|
};
|
package/es/FlowModel.d.ts
CHANGED
|
@@ -20,10 +20,6 @@ export default class FlowModel {
|
|
|
20
20
|
* 流程支持的节点类型
|
|
21
21
|
*/
|
|
22
22
|
nodeModelMap: Map<string, NodeConstructor>;
|
|
23
|
-
/**
|
|
24
|
-
* 每一次执行流程都会生成一个唯一的executionId。
|
|
25
|
-
*/
|
|
26
|
-
executionId: string;
|
|
27
23
|
/**
|
|
28
24
|
* 调度器,用于调度节点的执行。
|
|
29
25
|
*/
|
|
@@ -76,13 +72,45 @@ export default class FlowModel {
|
|
|
76
72
|
startNodeType?: string;
|
|
77
73
|
});
|
|
78
74
|
setStartNodeType(startNodeType: any): void;
|
|
79
|
-
load(graphData: any): void;
|
|
80
75
|
/**
|
|
81
|
-
*
|
|
82
|
-
* 同一次执行,这次执行内部的节点执行顺序为并行。
|
|
83
|
-
* 多次执行,多次执行之间为串行。
|
|
84
|
-
* 允许一个流程多次执行,效率更高。
|
|
76
|
+
* 解析LogicFlow图数据,将nodes和edges转换成节点格式。
|
|
85
77
|
* 例如:
|
|
78
|
+
* graphData: {
|
|
79
|
+
* nodes: [
|
|
80
|
+
* { id: 'node1', type: 'StartNode', properties: {} },
|
|
81
|
+
* { id: 'node2', type: 'TaskNode', properties: {} },
|
|
82
|
+
* ],
|
|
83
|
+
* edges: [
|
|
84
|
+
* { id: 'edge1', sourceNodeId: 'node1', targetNodeId: 'node2', properties: {} },
|
|
85
|
+
* ]
|
|
86
|
+
* }
|
|
87
|
+
* 转换成:
|
|
88
|
+
* nodeConfigMap: {
|
|
89
|
+
* node1: {
|
|
90
|
+
* id: 'node1',
|
|
91
|
+
* type: 'StartNode',
|
|
92
|
+
* properties: {},
|
|
93
|
+
* incoming: [],
|
|
94
|
+
* outgoing: [{ id: 'edge1', properties: {}, target: 'node2' }]
|
|
95
|
+
* },
|
|
96
|
+
* node2: {
|
|
97
|
+
* id: 'node2',
|
|
98
|
+
* type: 'TaskNode',
|
|
99
|
+
* properties: {},
|
|
100
|
+
* incoming: [{ id: 'edge1', properties: {}, source: 'node1' }],
|
|
101
|
+
* outgoing: [],
|
|
102
|
+
* }
|
|
103
|
+
* }
|
|
104
|
+
* 此格式方便后续执行时,根据节点id快速找到节点和执行初始化节点模型。
|
|
105
|
+
* 同时此方法还会找到所有的开始节点,方便后续执行时,从开始节点开始执行。
|
|
106
|
+
* @param graphData 流程图数据
|
|
107
|
+
*/
|
|
108
|
+
load(graphData: any): void;
|
|
109
|
+
/**
|
|
110
|
+
* 执行流程, 每次执行都会生成一个唯一的executionId,用于区分不同的执行。
|
|
111
|
+
* 同一次执行,这次执行内部的节点执行顺序为并行。内部并行是为了避免异步节点阻塞其他节点的执行。
|
|
112
|
+
* 多次执行,多次执行之间为串行,这里选择串行的原因是避免多次执行之间的数据冲突。
|
|
113
|
+
* example:
|
|
86
114
|
* 一个流程存在着两个开始节点,A和B,A和B的下一个节点都是C,C的下两个节点是D和E。
|
|
87
115
|
* 外部分别触发了A和B的执行,那么A和B的执行是串行的(也就是需要A执行完成后再执行B),但是D和E的执行是并行的。
|
|
88
116
|
* 如果希望A和B的执行是并行的,就不能使用同一个流程模型执行,应该初始化两个。
|
|
@@ -90,7 +118,8 @@ export default class FlowModel {
|
|
|
90
118
|
execute(params: ExecParams): Promise<void>;
|
|
91
119
|
resume(params: ExecParams): Promise<void>;
|
|
92
120
|
/**
|
|
93
|
-
*
|
|
121
|
+
* 创建节点实例, 每个节点实例都会有一个唯一的taskId。
|
|
122
|
+
* 通过executionId、nodeId、taskId可以唯一确定一个节点的某一次执行。
|
|
94
123
|
* @param nodeId 节点Id
|
|
95
124
|
* @returns 节点示例
|
|
96
125
|
*/
|
|
@@ -99,6 +128,17 @@ export default class FlowModel {
|
|
|
99
128
|
* 更新流程全局数据
|
|
100
129
|
*/
|
|
101
130
|
updateGlobalData(data: any): void;
|
|
131
|
+
/**
|
|
132
|
+
* 在执行完成后,通知外部此次执行完成。
|
|
133
|
+
* 如果还存在待执行的任务,那么继续执行。
|
|
134
|
+
*/
|
|
102
135
|
private onTaskFinished;
|
|
136
|
+
/**
|
|
137
|
+
* 从待执行队列中取出需要执行的内容。
|
|
138
|
+
* 会依次判断是否有taskId、nodeId、executionId。
|
|
139
|
+
* 若存在taskId,那么表示恢复执行。
|
|
140
|
+
* 若存在nodeId,那么表示从指定节点开始执行。
|
|
141
|
+
* 若都不存在,那么新建一个executionId,从开始节点开始执行。
|
|
142
|
+
*/
|
|
103
143
|
private createExecution;
|
|
104
144
|
}
|
package/es/FlowModel.js
CHANGED
|
@@ -92,9 +92,44 @@ var FlowModel = /** @class */ (function () {
|
|
|
92
92
|
FlowModel.prototype.setStartNodeType = function (startNodeType) {
|
|
93
93
|
this.startNodeType = startNodeType;
|
|
94
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* 解析LogicFlow图数据,将nodes和edges转换成节点格式。
|
|
97
|
+
* 例如:
|
|
98
|
+
* graphData: {
|
|
99
|
+
* nodes: [
|
|
100
|
+
* { id: 'node1', type: 'StartNode', properties: {} },
|
|
101
|
+
* { id: 'node2', type: 'TaskNode', properties: {} },
|
|
102
|
+
* ],
|
|
103
|
+
* edges: [
|
|
104
|
+
* { id: 'edge1', sourceNodeId: 'node1', targetNodeId: 'node2', properties: {} },
|
|
105
|
+
* ]
|
|
106
|
+
* }
|
|
107
|
+
* 转换成:
|
|
108
|
+
* nodeConfigMap: {
|
|
109
|
+
* node1: {
|
|
110
|
+
* id: 'node1',
|
|
111
|
+
* type: 'StartNode',
|
|
112
|
+
* properties: {},
|
|
113
|
+
* incoming: [],
|
|
114
|
+
* outgoing: [{ id: 'edge1', properties: {}, target: 'node2' }]
|
|
115
|
+
* },
|
|
116
|
+
* node2: {
|
|
117
|
+
* id: 'node2',
|
|
118
|
+
* type: 'TaskNode',
|
|
119
|
+
* properties: {},
|
|
120
|
+
* incoming: [{ id: 'edge1', properties: {}, source: 'node1' }],
|
|
121
|
+
* outgoing: [],
|
|
122
|
+
* }
|
|
123
|
+
* }
|
|
124
|
+
* 此格式方便后续执行时,根据节点id快速找到节点和执行初始化节点模型。
|
|
125
|
+
* 同时此方法还会找到所有的开始节点,方便后续执行时,从开始节点开始执行。
|
|
126
|
+
* @param graphData 流程图数据
|
|
127
|
+
*/
|
|
95
128
|
FlowModel.prototype.load = function (graphData) {
|
|
96
129
|
var _this = this;
|
|
97
130
|
var _a = graphData.nodes, nodes = _a === void 0 ? [] : _a, _b = graphData.edges, edges = _b === void 0 ? [] : _b;
|
|
131
|
+
this.startNodes = [];
|
|
132
|
+
this.nodeConfigMap = new Map();
|
|
98
133
|
nodes.forEach(function (node) {
|
|
99
134
|
if (_this.nodeModelMap.has(node.type)) {
|
|
100
135
|
var nodeConfig = {
|
|
@@ -133,11 +168,10 @@ var FlowModel = /** @class */ (function () {
|
|
|
133
168
|
});
|
|
134
169
|
};
|
|
135
170
|
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* 例如:
|
|
171
|
+
* 执行流程, 每次执行都会生成一个唯一的executionId,用于区分不同的执行。
|
|
172
|
+
* 同一次执行,这次执行内部的节点执行顺序为并行。内部并行是为了避免异步节点阻塞其他节点的执行。
|
|
173
|
+
* 多次执行,多次执行之间为串行,这里选择串行的原因是避免多次执行之间的数据冲突。
|
|
174
|
+
* example:
|
|
141
175
|
* 一个流程存在着两个开始节点,A和B,A和B的下一个节点都是C,C的下两个节点是D和E。
|
|
142
176
|
* 外部分别触发了A和B的执行,那么A和B的执行是串行的(也就是需要A执行完成后再执行B),但是D和E的执行是并行的。
|
|
143
177
|
* 如果希望A和B的执行是并行的,就不能使用同一个流程模型执行,应该初始化两个。
|
|
@@ -169,7 +203,8 @@ var FlowModel = /** @class */ (function () {
|
|
|
169
203
|
});
|
|
170
204
|
};
|
|
171
205
|
/**
|
|
172
|
-
*
|
|
206
|
+
* 创建节点实例, 每个节点实例都会有一个唯一的taskId。
|
|
207
|
+
* 通过executionId、nodeId、taskId可以唯一确定一个节点的某一次执行。
|
|
173
208
|
* @param nodeId 节点Id
|
|
174
209
|
* @returns 节点示例
|
|
175
210
|
*/
|
|
@@ -189,11 +224,11 @@ var FlowModel = /** @class */ (function () {
|
|
|
189
224
|
FlowModel.prototype.updateGlobalData = function (data) {
|
|
190
225
|
this.globalData = __assign(__assign({}, this.globalData), data);
|
|
191
226
|
};
|
|
227
|
+
/**
|
|
228
|
+
* 在执行完成后,通知外部此次执行完成。
|
|
229
|
+
* 如果还存在待执行的任务,那么继续执行。
|
|
230
|
+
*/
|
|
192
231
|
FlowModel.prototype.onTaskFinished = function (result) {
|
|
193
|
-
var executionId = result.executionId;
|
|
194
|
-
if (executionId !== this.executionId) {
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
232
|
var callback = this.executingInstance.callback;
|
|
198
233
|
if (callback) {
|
|
199
234
|
callback(result);
|
|
@@ -206,26 +241,28 @@ var FlowModel = /** @class */ (function () {
|
|
|
206
241
|
this.isRunning = false;
|
|
207
242
|
}
|
|
208
243
|
};
|
|
244
|
+
/**
|
|
245
|
+
* 从待执行队列中取出需要执行的内容。
|
|
246
|
+
* 会依次判断是否有taskId、nodeId、executionId。
|
|
247
|
+
* 若存在taskId,那么表示恢复执行。
|
|
248
|
+
* 若存在nodeId,那么表示从指定节点开始执行。
|
|
249
|
+
* 若都不存在,那么新建一个executionId,从开始节点开始执行。
|
|
250
|
+
*/
|
|
209
251
|
FlowModel.prototype.createExecution = function () {
|
|
210
252
|
var _this = this;
|
|
211
253
|
var execParams = this.executeQueue.shift();
|
|
212
254
|
this.executingInstance = execParams;
|
|
213
|
-
if (execParams.executionId) {
|
|
214
|
-
this.executionId = execParams.executionId;
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
this.executionId = createExecId();
|
|
218
|
-
}
|
|
219
255
|
// 如果有taskId,那么表示恢复执行
|
|
220
|
-
if (execParams.taskId) {
|
|
256
|
+
if (execParams.taskId && execParams.executionId && execParams.nodeId) {
|
|
221
257
|
this.scheduler.resume({
|
|
222
|
-
executionId:
|
|
258
|
+
executionId: execParams.executionId,
|
|
223
259
|
taskId: execParams.taskId,
|
|
224
260
|
nodeId: execParams.nodeId,
|
|
225
261
|
data: execParams.data,
|
|
226
262
|
});
|
|
227
263
|
return;
|
|
228
264
|
}
|
|
265
|
+
var executionId = execParams.executionId || createExecId();
|
|
229
266
|
if (execParams.nodeId) {
|
|
230
267
|
var nodeConfig = this.nodeConfigMap.get(execParams.nodeId);
|
|
231
268
|
if (!nodeConfig) {
|
|
@@ -236,13 +273,12 @@ var FlowModel = /** @class */ (function () {
|
|
|
236
273
|
}
|
|
237
274
|
this.startNodes.forEach(function (startNode) {
|
|
238
275
|
_this.scheduler.addTask({
|
|
239
|
-
executionId:
|
|
276
|
+
executionId: executionId,
|
|
240
277
|
nodeId: startNode.id,
|
|
241
278
|
});
|
|
242
|
-
// 所有的开始节点都执行
|
|
243
279
|
});
|
|
244
280
|
this.scheduler.run({
|
|
245
|
-
executionId:
|
|
281
|
+
executionId: executionId,
|
|
246
282
|
});
|
|
247
283
|
};
|
|
248
284
|
return FlowModel;
|
package/es/Scheduler.d.ts
CHANGED
|
@@ -3,16 +3,31 @@ import type { TaskParam, NodeParam, ResumeParam } from './types.d';
|
|
|
3
3
|
import type FlowModel from './FlowModel';
|
|
4
4
|
import type Recorder from './recorder';
|
|
5
5
|
declare type TaskParamMap = Map<string, TaskParam>;
|
|
6
|
+
declare type ExecutionId = string;
|
|
6
7
|
/**
|
|
7
8
|
* 调度器
|
|
8
9
|
* 通过一个队列维护需要执行的节点,一个集合维护正在执行的节点
|
|
9
10
|
*/
|
|
10
11
|
export default class Scheduler extends EventEmitter {
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* 当前需要执行的节点队列
|
|
14
|
+
*/
|
|
15
|
+
nodeQueueMap: Map<ExecutionId, NodeParam[]>;
|
|
16
|
+
/**
|
|
17
|
+
* 当前正在执行的节点集合
|
|
18
|
+
* 在每个节点执行完成后,会从集合中删除。
|
|
19
|
+
* 同时会判断此集合中是否还存在和此节点相同的executionId,如果不存在,说明此流程已经执行完成。
|
|
20
|
+
*/
|
|
21
|
+
taskRunningMap: Map<ExecutionId, TaskParamMap>;
|
|
22
|
+
/**
|
|
23
|
+
* 流程模型,用于创建节点模型。
|
|
24
|
+
*/
|
|
13
25
|
flowModel: FlowModel;
|
|
26
|
+
/**
|
|
27
|
+
* 执行记录存储器
|
|
28
|
+
* 用于存储节点执行的结果。
|
|
29
|
+
*/
|
|
14
30
|
recorder: Recorder;
|
|
15
|
-
currentTask: TaskParam | null;
|
|
16
31
|
constructor(config: any);
|
|
17
32
|
/**
|
|
18
33
|
* 添加一个任务到队列中。
|
|
@@ -36,16 +51,12 @@ export default class Scheduler extends EventEmitter {
|
|
|
36
51
|
* 可以自定义节点手动实现流程中断,然后通过此方法恢复流程的执行。
|
|
37
52
|
*/
|
|
38
53
|
resume(resumeParam: ResumeParam): Promise<void>;
|
|
39
|
-
stop(data: any): void;
|
|
40
54
|
private pushTaskToRunningMap;
|
|
41
55
|
private removeTaskFromRunningMap;
|
|
42
56
|
private hasRunningTask;
|
|
43
57
|
private exec;
|
|
44
58
|
private interrupted;
|
|
45
59
|
private next;
|
|
46
|
-
/**
|
|
47
|
-
* 为了防止多次添加导致
|
|
48
|
-
*/
|
|
49
60
|
private saveTaskResult;
|
|
50
61
|
}
|
|
51
62
|
export {};
|