@mldong/jeeflow 1.4.0 → 1.5.1
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/dist/engine.d.ts +2 -0
- package/dist/engine.js +6 -0
- package/dist/facade.d.ts +18 -0
- package/dist/facade.js +362 -9
- package/dist/jdbc/ext.d.ts +6 -3
- package/dist/jdbc/ext.js +61 -2
- package/dist/jdbc/shared.d.ts +26 -3
- package/dist/jdbc/shared.js +201 -4
- package/dist/memory-ext.d.ts +3 -3
- package/dist/memory-ext.js +35 -4
- package/dist/memory.d.ts +24 -3
- package/dist/memory.js +182 -3
- package/dist/model.d.ts +54 -0
- package/dist/spi.d.ts +25 -4
- package/package.json +3 -1
package/dist/engine.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export declare class EngineImpl implements Engine {
|
|
|
31
31
|
setExtensions(ext: EngineExtensions): void;
|
|
32
32
|
setRegistry(reg: HandlerRegistry): void;
|
|
33
33
|
private firePre;
|
|
34
|
+
/** 表达式求值(v1.5.0,门面 highLight 决策分支过滤用) */
|
|
35
|
+
evalExpr(expr: string, vars: Record<string, any>): Promise<any>;
|
|
34
36
|
private firePost;
|
|
35
37
|
private fireEvent;
|
|
36
38
|
startProcessInstanceById(defineId: number, operator: string, args?: Record<string, any>): Promise<ProcessInstance>;
|
package/dist/engine.js
CHANGED
|
@@ -36,6 +36,12 @@ export class EngineImpl {
|
|
|
36
36
|
return false;
|
|
37
37
|
return true;
|
|
38
38
|
}
|
|
39
|
+
/** 表达式求值(v1.5.0,门面 highLight 决策分支过滤用) */
|
|
40
|
+
async evalExpr(expr, vars) {
|
|
41
|
+
if (!this.exprEval)
|
|
42
|
+
throw new Error('ExpressionEvaluator 未配置');
|
|
43
|
+
return this.exprEval.eval(expr, vars);
|
|
44
|
+
}
|
|
39
45
|
async firePost(node, inst) {
|
|
40
46
|
if (!this.ext?.interceptors)
|
|
41
47
|
return;
|
package/dist/facade.d.ts
CHANGED
|
@@ -17,6 +17,12 @@ export declare class JeeflowFacade {
|
|
|
17
17
|
private withdraw;
|
|
18
18
|
private execute;
|
|
19
19
|
private designPage;
|
|
20
|
+
/** 修改流程设计基本信息(对齐 boot3 ProcessDesignController.update,不写设计稿快照) */
|
|
21
|
+
private designUpdate;
|
|
22
|
+
/** 更新流程设计定义(设计稿保存,issues/08):content 快照入库 + 同步基本信息 + 置未部署 */
|
|
23
|
+
private designUpdateDefine;
|
|
24
|
+
/** 重新部署流程定义(issues/08):替换最新定义内容 + 置已部署(对齐 boot3 redeploy) */
|
|
25
|
+
private designRedeploy;
|
|
20
26
|
private designDetail;
|
|
21
27
|
private designSave;
|
|
22
28
|
private surrogatePage;
|
|
@@ -24,6 +30,8 @@ export declare class JeeflowFacade {
|
|
|
24
30
|
private getLastByName;
|
|
25
31
|
private highLight;
|
|
26
32
|
private collectPath;
|
|
33
|
+
/** 决策输出边表达式求值(args = 实例变量 + 决策节点前置任务变量) */
|
|
34
|
+
private evalDecisionExpr;
|
|
27
35
|
private approvalRecord;
|
|
28
36
|
private getAssigneeTextData;
|
|
29
37
|
private createCCInstance;
|
|
@@ -36,4 +44,14 @@ export declare class JeeflowFacade {
|
|
|
36
44
|
private taskAddActor;
|
|
37
45
|
private taskLatest;
|
|
38
46
|
private ext;
|
|
47
|
+
private definePage;
|
|
48
|
+
private defineDetail;
|
|
49
|
+
private instancePage;
|
|
50
|
+
private instanceDetail;
|
|
51
|
+
/** 流程 JSON 中第一个任务节点 id(issues/05-4 isFirstTaskNode 用) */
|
|
52
|
+
private firstTaskNodeId;
|
|
53
|
+
private todoList;
|
|
54
|
+
private doneList;
|
|
55
|
+
/** 定义 content 解析为 LogicFlow JSON(issues/05 jsonObject) */
|
|
56
|
+
private parseGraph;
|
|
39
57
|
}
|
package/dist/facade.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// 集成方只实现一个转发端点:把 body JSON 转成对象传入 flow(),所有流程能力按
|
|
4
4
|
// action(boot2/boot3 端点短名)路由。返回统一结构 {code, msg, data}
|
|
5
5
|
// (code=0 成功 / 99999999 失败)。操作人约定:args.operator 显式传入。
|
|
6
|
+
import { TaskState, } from './model.js';
|
|
6
7
|
// submitType 枚举(对齐 boot3)
|
|
7
8
|
const SUBMIT_APPLY = 0;
|
|
8
9
|
const SUBMIT_AGREE = 1;
|
|
@@ -37,20 +38,34 @@ export class JeeflowFacade {
|
|
|
37
38
|
}
|
|
38
39
|
async dispatch(action, args) {
|
|
39
40
|
switch (action) {
|
|
41
|
+
case 'processDefine/page':
|
|
42
|
+
return this.definePage(args);
|
|
43
|
+
case 'processDefine/detail':
|
|
44
|
+
return this.defineDetail(args);
|
|
40
45
|
case 'processDefine/startAndExecute':
|
|
41
46
|
case 'processInstance/startAndExecute':
|
|
42
47
|
return this.startAndExecute(args);
|
|
43
48
|
case 'processDefine/deploy':
|
|
44
49
|
case 'processDesign/deploy':
|
|
45
50
|
return this.deploy(args, action === 'processDesign/deploy');
|
|
51
|
+
case 'processDesign/redeploy':
|
|
52
|
+
return this.designRedeploy(args);
|
|
46
53
|
case 'processDefine/redeploy':
|
|
47
54
|
return this.redeploy(args);
|
|
48
55
|
case 'processDefine/remove':
|
|
49
56
|
return this.repo.removeDefine(toId(args.id));
|
|
50
57
|
case 'processDefine/upAndDown':
|
|
51
58
|
return this.repo.updateDefineState(toId(args.id), toInt(args.state));
|
|
59
|
+
case 'processInstance/page':
|
|
60
|
+
return this.instancePage(args);
|
|
61
|
+
case 'processInstance/detail':
|
|
62
|
+
return this.instanceDetail(args);
|
|
52
63
|
case 'processInstance/withdraw':
|
|
53
64
|
return this.withdraw(args);
|
|
65
|
+
case 'processTask/todoList':
|
|
66
|
+
return this.todoList(args);
|
|
67
|
+
case 'processTask/doneList':
|
|
68
|
+
return this.doneList(args);
|
|
54
69
|
case 'processTask/execute':
|
|
55
70
|
return this.execute(args);
|
|
56
71
|
case 'processDesign/page':
|
|
@@ -59,6 +74,10 @@ export class JeeflowFacade {
|
|
|
59
74
|
return this.designDetail(args);
|
|
60
75
|
case 'processDesign/save':
|
|
61
76
|
return this.designSave(args);
|
|
77
|
+
case 'processDesign/update':
|
|
78
|
+
return this.designUpdate(args);
|
|
79
|
+
case 'processDesign/updateDefine':
|
|
80
|
+
return this.designUpdateDefine(args);
|
|
62
81
|
case 'processDesign/remove':
|
|
63
82
|
return this.ext().removeDesign(toId(args.id));
|
|
64
83
|
case 'processSurrogate/page':
|
|
@@ -231,9 +250,103 @@ export class JeeflowFacade {
|
|
|
231
250
|
}
|
|
232
251
|
// ── 流程设计(需扩展仓储) ───────────────────────────────────────────────
|
|
233
252
|
async designPage(args) {
|
|
234
|
-
const [rows, total] = await this.ext().pageDesigns(toInt(args.pageNum ?? 1), toInt(args.pageSize ?? 10));
|
|
253
|
+
const [rows, total] = await this.ext().pageDesigns(toInt(args.pageNum ?? 1), toInt(args.pageSize ?? 10), undefined, parseMQuery(args));
|
|
235
254
|
return { rows, recordCount: total };
|
|
236
255
|
}
|
|
256
|
+
/** 修改流程设计基本信息(对齐 boot3 ProcessDesignController.update,不写设计稿快照) */
|
|
257
|
+
async designUpdate(args) {
|
|
258
|
+
const ext = this.ext();
|
|
259
|
+
const design = await ext.findDesignById(toId(args.id));
|
|
260
|
+
if (!design)
|
|
261
|
+
throw new Error('流程设计不存在');
|
|
262
|
+
if (args.name != null)
|
|
263
|
+
design.name = String(args.name);
|
|
264
|
+
if (args.displayName != null)
|
|
265
|
+
design.displayName = String(args.displayName);
|
|
266
|
+
if (args.type != null)
|
|
267
|
+
design.type = String(args.type);
|
|
268
|
+
if (args.icon != null)
|
|
269
|
+
design.icon = String(args.icon);
|
|
270
|
+
if (args.remark != null)
|
|
271
|
+
design.remark = String(args.remark);
|
|
272
|
+
design.updateUser = String(args.operator ?? 'system');
|
|
273
|
+
await ext.updateDesign(design);
|
|
274
|
+
return {};
|
|
275
|
+
}
|
|
276
|
+
/** 更新流程设计定义(设计稿保存,issues/08):content 快照入库 + 同步基本信息 + 置未部署 */
|
|
277
|
+
async designUpdateDefine(args) {
|
|
278
|
+
const ext = this.ext();
|
|
279
|
+
const designId = toId(args.processDesignId);
|
|
280
|
+
const design = await ext.findDesignById(designId);
|
|
281
|
+
if (!design)
|
|
282
|
+
throw new Error('流程设计不存在');
|
|
283
|
+
if (args.content == null)
|
|
284
|
+
throw new Error('content 缺失');
|
|
285
|
+
const content = toStr(args.content);
|
|
286
|
+
// 与最新一条相同则不重复入库(对齐 boot3 updateDefine)
|
|
287
|
+
const hisList = await ext.listDesignHis(designId);
|
|
288
|
+
if (hisList.length === 0 || toStr(hisList[0].content) !== content) {
|
|
289
|
+
await ext.saveDesignHis({
|
|
290
|
+
id: 0, processDesignId: designId, content,
|
|
291
|
+
createTime: new Date(), createUser: String(args.operator ?? 'system'),
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
// 同步设计基本信息(jsonObject 里的 name/displayName/type)+ 内容变更 → 未部署
|
|
295
|
+
try {
|
|
296
|
+
const flow = JSON.parse(content);
|
|
297
|
+
if (flow?.name)
|
|
298
|
+
design.name = flow.name;
|
|
299
|
+
if (flow?.displayName)
|
|
300
|
+
design.displayName = flow.displayName;
|
|
301
|
+
if (flow?.type)
|
|
302
|
+
design.type = flow.type;
|
|
303
|
+
}
|
|
304
|
+
catch { /* ignore */ }
|
|
305
|
+
design.isDeployed = 0;
|
|
306
|
+
design.updateUser = String(args.operator ?? 'system');
|
|
307
|
+
await ext.updateDesign(design);
|
|
308
|
+
return {};
|
|
309
|
+
}
|
|
310
|
+
/** 重新部署流程定义(issues/08):替换最新定义内容 + 置已部署(对齐 boot3 redeploy) */
|
|
311
|
+
async designRedeploy(args) {
|
|
312
|
+
const ext = this.ext();
|
|
313
|
+
const designId = toId(args.id);
|
|
314
|
+
const design = await ext.findDesignById(designId);
|
|
315
|
+
if (!design)
|
|
316
|
+
throw new Error('流程设计不存在');
|
|
317
|
+
const hisList = await ext.listDesignHis(designId);
|
|
318
|
+
if (hisList.length === 0)
|
|
319
|
+
throw new Error('流程设计没有内容,无法发布');
|
|
320
|
+
const content = toStr(hisList[0].content);
|
|
321
|
+
let flow;
|
|
322
|
+
try {
|
|
323
|
+
flow = JSON.parse(content);
|
|
324
|
+
}
|
|
325
|
+
catch (e) {
|
|
326
|
+
throw new Error('流程定义 JSON 解析失败: ' + String(e));
|
|
327
|
+
}
|
|
328
|
+
if (!flow?.name)
|
|
329
|
+
throw new Error('流程定义缺少 name');
|
|
330
|
+
// 按 name 取最新定义:有则替换内容(version 不变),无则新建(对齐 boot3 redeploy)
|
|
331
|
+
const last = await this.repo.findDefineByName(flow.name);
|
|
332
|
+
let defineId;
|
|
333
|
+
if (!last) {
|
|
334
|
+
defineId = await this.saveDeployedDefine(content, args);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
last.name = flow.name;
|
|
338
|
+
last.displayName = flow.displayName ?? '';
|
|
339
|
+
last.type = flow.type ?? '';
|
|
340
|
+
last.content = content;
|
|
341
|
+
last.updateUser = String(args.operator ?? 'system');
|
|
342
|
+
await this.repo.updateDefine(last);
|
|
343
|
+
defineId = last.id;
|
|
344
|
+
}
|
|
345
|
+
design.isDeployed = 1;
|
|
346
|
+
design.updateUser = String(args.operator ?? 'system');
|
|
347
|
+
await ext.updateDesign(design);
|
|
348
|
+
return { processDefineId: defineId };
|
|
349
|
+
}
|
|
237
350
|
async designDetail(args) {
|
|
238
351
|
const ext = this.ext();
|
|
239
352
|
const design = await ext.findDesignById(toId(args.id));
|
|
@@ -244,12 +357,25 @@ export class JeeflowFacade {
|
|
|
244
357
|
type: design.type, icon: design.icon, isDeployed: design.isDeployed, remark: design.remark,
|
|
245
358
|
};
|
|
246
359
|
const hisList = await ext.listDesignHis(design.id);
|
|
360
|
+
let jsonObject;
|
|
247
361
|
if (hisList.length > 0) {
|
|
248
362
|
try {
|
|
249
|
-
|
|
363
|
+
jsonObject = JSON.parse(toStr(hisList[0].content));
|
|
250
364
|
}
|
|
251
365
|
catch { /* ignore */ }
|
|
252
366
|
}
|
|
367
|
+
// issues/07:jsonObject 缺失基本信息时从设计表补齐(对齐 boot3 ProcessDesignServiceImpl.findById)
|
|
368
|
+
if (!jsonObject || typeof jsonObject !== 'object')
|
|
369
|
+
jsonObject = {};
|
|
370
|
+
if (!(jsonObject.name))
|
|
371
|
+
jsonObject.name = design.name;
|
|
372
|
+
if (!(jsonObject.displayName))
|
|
373
|
+
jsonObject.displayName = design.displayName;
|
|
374
|
+
if (!(jsonObject.type))
|
|
375
|
+
jsonObject.type = design.type;
|
|
376
|
+
if (!(jsonObject.processDesignId))
|
|
377
|
+
jsonObject.processDesignId = design.id;
|
|
378
|
+
data.jsonObject = jsonObject;
|
|
253
379
|
data.his = hisList;
|
|
254
380
|
return data;
|
|
255
381
|
}
|
|
@@ -281,6 +407,9 @@ export class JeeflowFacade {
|
|
|
281
407
|
if (args.remark != null)
|
|
282
408
|
found.remark = String(args.remark);
|
|
283
409
|
found.updateUser = operator;
|
|
410
|
+
// 内容快照变更 → 置为未部署(对齐 boot3 updateDefine 语义,issues/08)
|
|
411
|
+
if (args.content != null)
|
|
412
|
+
found.isDeployed = 0;
|
|
284
413
|
await ext.updateDesign(found);
|
|
285
414
|
design = found;
|
|
286
415
|
}
|
|
@@ -296,7 +425,7 @@ export class JeeflowFacade {
|
|
|
296
425
|
// ── 委托代理(需扩展仓储) ───────────────────────────────────────────────
|
|
297
426
|
async surrogatePage(args) {
|
|
298
427
|
const filters = args.operator != null ? { operator: String(args.operator) } : undefined;
|
|
299
|
-
const [rows, total] = await this.ext().pageSurrogates(toInt(args.pageNum ?? 1), toInt(args.pageSize ?? 10), filters);
|
|
428
|
+
const [rows, total] = await this.ext().pageSurrogates(toInt(args.pageNum ?? 1), toInt(args.pageSize ?? 10), filters, parseMQuery(args));
|
|
300
429
|
return { rows, recordCount: total };
|
|
301
430
|
}
|
|
302
431
|
async surrogateSave(args) {
|
|
@@ -353,26 +482,33 @@ export class JeeflowFacade {
|
|
|
353
482
|
for (const t of his)
|
|
354
483
|
if (!active.includes(t.taskName) && !history.includes(t.taskName))
|
|
355
484
|
history.push(t.taskName);
|
|
356
|
-
// 路径补全:start
|
|
485
|
+
// 路径补全:start 沿边递归(遇活跃节点停止);决策分支按表达式求值过滤(issues/06)
|
|
357
486
|
const def = await this.repo.findDefineById(inst.defineId);
|
|
358
487
|
if (def) {
|
|
359
488
|
try {
|
|
360
489
|
const flow = JSON.parse(toStr(def.content));
|
|
361
|
-
this.collectPath(flow, 'start', '', active, history, edges, new Set());
|
|
490
|
+
await this.collectPath(flow, 'start', '', active, history, edges, new Set(), inst.variables ?? {}, his);
|
|
362
491
|
}
|
|
363
492
|
catch { /* ignore */ }
|
|
364
493
|
}
|
|
365
494
|
return { activeNodeNames: active, historyNodeNames: history, historyEdgeNames: edges };
|
|
366
495
|
}
|
|
367
|
-
collectPath(flow, nodeId, edgeName, active, history, edges, visited) {
|
|
496
|
+
async collectPath(flow, nodeId, edgeName, active, history, edges, visited, vars, historyTasks) {
|
|
368
497
|
if (visited.has(nodeId))
|
|
369
498
|
return;
|
|
370
499
|
visited.add(nodeId);
|
|
371
500
|
if (edgeName && !edges.includes(edgeName))
|
|
372
501
|
edges.push(edgeName);
|
|
502
|
+
const src = (flow.nodes ?? []).find((n) => n.id === nodeId);
|
|
373
503
|
for (const e of flow.edges ?? []) {
|
|
374
504
|
if (e.sourceNodeId !== nodeId)
|
|
375
505
|
continue;
|
|
506
|
+
// 决策节点:输出边表达式求值过滤(对齐 boot3 recursionModel,issues/06)
|
|
507
|
+
if (src?.type === 'snaker:decision') {
|
|
508
|
+
const expr = e.properties?.expr;
|
|
509
|
+
if (expr && !await this.evalDecisionExpr(flow, src, expr, vars, historyTasks))
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
376
512
|
const target = (flow.nodes ?? []).find((n) => n.id === e.targetNodeId);
|
|
377
513
|
if (!target)
|
|
378
514
|
continue;
|
|
@@ -381,7 +517,25 @@ export class JeeflowFacade {
|
|
|
381
517
|
history.push(tid);
|
|
382
518
|
if (active.includes(tid))
|
|
383
519
|
continue;
|
|
384
|
-
this.collectPath(flow, tid, e.id, active, history, edges, visited);
|
|
520
|
+
await this.collectPath(flow, tid, e.id, active, history, edges, visited, vars, historyTasks);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
/** 决策输出边表达式求值(args = 实例变量 + 决策节点前置任务变量) */
|
|
524
|
+
async evalDecisionExpr(flow, decision, expr, vars, historyTasks) {
|
|
525
|
+
const args = { ...(vars ?? {}) };
|
|
526
|
+
for (const e of flow.edges ?? []) {
|
|
527
|
+
if (e.targetNodeId === decision.id) {
|
|
528
|
+
const t = (historyTasks ?? []).find((x) => x.taskName === e.sourceNodeId);
|
|
529
|
+
if (t?.variables)
|
|
530
|
+
Object.assign(args, t.variables);
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
try {
|
|
535
|
+
return Boolean(await this.engine.evalExpr(expr, args));
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
return false;
|
|
385
539
|
}
|
|
386
540
|
}
|
|
387
541
|
async approvalRecord(args) {
|
|
@@ -424,8 +578,8 @@ export class JeeflowFacade {
|
|
|
424
578
|
const pageNum = toInt(args.pageNum ?? 1);
|
|
425
579
|
const pageSize = toInt(args.pageSize ?? 10);
|
|
426
580
|
const actorId = String(args.operator ?? 'user1');
|
|
427
|
-
const { rows, total } = await this.repo.pageCcInstances(pageNum, pageSize, actorId);
|
|
428
|
-
return { rows, recordCount: total };
|
|
581
|
+
const { rows, total } = await this.repo.pageCcInstances(pageNum, pageSize, actorId, parseMQuery(args));
|
|
582
|
+
return { rows: rows.map(r => ccRowToMap(r)), recordCount: total };
|
|
429
583
|
}
|
|
430
584
|
async taskDetail(args) {
|
|
431
585
|
const taskId = toId(args.id);
|
|
@@ -446,6 +600,7 @@ export class JeeflowFacade {
|
|
|
446
600
|
if (inst) {
|
|
447
601
|
const def = await this.repo.findDefineById(inst.defineId);
|
|
448
602
|
if (def) {
|
|
603
|
+
vo.jsonObject = this.parseGraph(def.content); // issues/05
|
|
449
604
|
try {
|
|
450
605
|
const flow = JSON.parse(toStr(def.content));
|
|
451
606
|
for (const n of flow.nodes ?? []) {
|
|
@@ -558,8 +713,206 @@ export class JeeflowFacade {
|
|
|
558
713
|
throw new Error('未配置 ProcessExtRepository(扩展仓储)');
|
|
559
714
|
return this.extRepo;
|
|
560
715
|
}
|
|
716
|
+
// ═══ 基础分页/详情(v1.5.0 补齐,对齐 Java 门面)═══
|
|
717
|
+
async definePage(args) {
|
|
718
|
+
const pageNum = toInt(args.pageNum ?? 1);
|
|
719
|
+
const pageSize = toInt(args.pageSize ?? 10);
|
|
720
|
+
const { rows, total } = await this.repo.pageDefines(pageNum, pageSize, parseMQuery(args));
|
|
721
|
+
return { rows: rows.map(r => defineRowToMap(r)), recordCount: total };
|
|
722
|
+
}
|
|
723
|
+
async defineDetail(args) {
|
|
724
|
+
const id = toId(args.id);
|
|
725
|
+
const def = await this.repo.findDefineById(id);
|
|
726
|
+
if (!def)
|
|
727
|
+
throw new Error('流程定义不存在');
|
|
728
|
+
return {
|
|
729
|
+
id: def.id, name: def.name, displayName: def.displayName,
|
|
730
|
+
type: def.type, state: def.state, version: def.version,
|
|
731
|
+
jsonObject: this.parseGraph(def.content), // issues/05
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
async instancePage(args) {
|
|
735
|
+
const pageNum = toInt(args.pageNum ?? 1);
|
|
736
|
+
const pageSize = toInt(args.pageSize ?? 10);
|
|
737
|
+
const operator = String(args.operator ?? 'user1');
|
|
738
|
+
const { rows, total } = await this.repo.pageInstances(pageNum, pageSize, operator, parseMQuery(args));
|
|
739
|
+
return { rows: rows.map(r => instanceRowToMap(r)), recordCount: total };
|
|
740
|
+
}
|
|
741
|
+
async instanceDetail(args) {
|
|
742
|
+
const id = toId(args.id);
|
|
743
|
+
const inst = await this.repo.findInstanceById(id);
|
|
744
|
+
if (!inst)
|
|
745
|
+
throw new Error('流程实例不存在');
|
|
746
|
+
const def0 = await this.repo.findDefineById(inst.defineId);
|
|
747
|
+
const graph = def0 ? this.parseGraph(def0.content) : undefined;
|
|
748
|
+
// 任务列表(issues/05-4):全量 tasks + activeTaskList(仅 DOING)+ 任务行 ext/isFirstTaskNode
|
|
749
|
+
const firstTaskNodeId = this.firstTaskNodeId(graph);
|
|
750
|
+
const tasks = [];
|
|
751
|
+
const activeTaskList = [];
|
|
752
|
+
for (const t of inst.tasks ?? []) {
|
|
753
|
+
const vo = {
|
|
754
|
+
id: t.id, processInstanceId: t.processInstanceId, taskName: t.taskName,
|
|
755
|
+
displayName: t.displayName, taskType: t.taskType ?? null,
|
|
756
|
+
performType: t.performType ?? null, taskState: t.taskState,
|
|
757
|
+
operator: t.actorId ?? '', finishTime: t.finishTime,
|
|
758
|
+
expireTime: t.expireTime, formKey: t.formKey ?? '', taskParentId: t.parentTaskId ?? null,
|
|
759
|
+
variable: JSON.stringify(t.variables ?? {}),
|
|
760
|
+
createTime: t.createTime, createUser: t.createUser,
|
|
761
|
+
updateTime: t.updateTime, updateUser: t.updateUser,
|
|
762
|
+
taskActorIdList: t.actorIds ?? [],
|
|
763
|
+
};
|
|
764
|
+
const ext = { ...(t.variables ?? {}) };
|
|
765
|
+
const doing = t.taskState === TaskState.Doing;
|
|
766
|
+
ext.isFirstTaskNode = doing && t.taskName === firstTaskNodeId;
|
|
767
|
+
vo.ext = ext;
|
|
768
|
+
tasks.push(vo);
|
|
769
|
+
if (doing)
|
|
770
|
+
activeTaskList.push(vo);
|
|
771
|
+
}
|
|
772
|
+
return {
|
|
773
|
+
id: inst.id, parentId: inst.parentId, processDefineId: inst.defineId,
|
|
774
|
+
state: inst.state, parentNodeName: inst.parentNodeName,
|
|
775
|
+
businessNo: inst.businessNo, operator: inst.operator,
|
|
776
|
+
variables: inst.variables, createTime: inst.createTime, createUser: inst.createUser,
|
|
777
|
+
jsonObject: graph, // issues/05
|
|
778
|
+
tasks,
|
|
779
|
+
activeTaskList,
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
/** 流程 JSON 中第一个任务节点 id(issues/05-4 isFirstTaskNode 用) */
|
|
783
|
+
firstTaskNodeId(graph) {
|
|
784
|
+
for (const n of graph?.nodes ?? []) {
|
|
785
|
+
if (n?.type === 'snaker:task')
|
|
786
|
+
return n.id;
|
|
787
|
+
}
|
|
788
|
+
return '';
|
|
789
|
+
}
|
|
790
|
+
async todoList(args) {
|
|
791
|
+
const pageNum = toInt(args.pageNum ?? 1);
|
|
792
|
+
const pageSize = toInt(args.pageSize ?? 10);
|
|
793
|
+
const actorId = String(args.operator ?? 'user1');
|
|
794
|
+
const { rows, total } = await this.repo.pageTodoTasks(pageNum, pageSize, actorId, parseMQuery(args));
|
|
795
|
+
return { rows: rows.map(r => taskRowToMap(r)), recordCount: total };
|
|
796
|
+
}
|
|
797
|
+
async doneList(args) {
|
|
798
|
+
const pageNum = toInt(args.pageNum ?? 1);
|
|
799
|
+
const pageSize = toInt(args.pageSize ?? 10);
|
|
800
|
+
const operator = String(args.operator ?? 'user1');
|
|
801
|
+
const { rows, total } = await this.repo.pageDoneTasks(pageNum, pageSize, operator, parseMQuery(args));
|
|
802
|
+
return { rows: rows.map(r => taskRowToMap(r)), recordCount: total };
|
|
803
|
+
}
|
|
804
|
+
/** 定义 content 解析为 LogicFlow JSON(issues/05 jsonObject) */
|
|
805
|
+
parseGraph(content) {
|
|
806
|
+
try {
|
|
807
|
+
const str = typeof content === 'string' ? content : new TextDecoder().decode(content);
|
|
808
|
+
const obj = JSON.parse(str);
|
|
809
|
+
return obj && typeof obj === 'object' ? obj : undefined;
|
|
810
|
+
}
|
|
811
|
+
catch {
|
|
812
|
+
return undefined;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
// ── 行转 Map(issues/05-2 列表字段契约 + 05-3 时间格式)─────────────────────
|
|
817
|
+
/** Date → 'yyyy-MM-dd HH:mm:ss'(null/undefined → null) */
|
|
818
|
+
function fmtTime(v) {
|
|
819
|
+
if (v == null)
|
|
820
|
+
return null;
|
|
821
|
+
const p = (n) => String(n).padStart(2, '0');
|
|
822
|
+
return `${v.getFullYear()}-${p(v.getMonth() + 1)}-${p(v.getDate())} ${p(v.getHours())}:${p(v.getMinutes())}:${p(v.getSeconds())}`;
|
|
823
|
+
}
|
|
824
|
+
/** JSON 字符串 → 对象(坏 JSON / 空返回空对象) */
|
|
825
|
+
function parseVarMap(json) {
|
|
826
|
+
if (!json)
|
|
827
|
+
return {};
|
|
828
|
+
try {
|
|
829
|
+
const o = JSON.parse(json);
|
|
830
|
+
return o && typeof o === 'object' ? o : {};
|
|
831
|
+
}
|
|
832
|
+
catch {
|
|
833
|
+
return {};
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
/** 定义行:时间格式化 */
|
|
837
|
+
function defineRowToMap(r) {
|
|
838
|
+
return {
|
|
839
|
+
id: r.id, name: r.name, displayName: r.displayName, type: r.type,
|
|
840
|
+
state: r.state, version: r.version,
|
|
841
|
+
createTime: fmtTime(r.createTime), createUser: r.createUser,
|
|
842
|
+
updateTime: fmtTime(r.updateTime), updateUser: r.updateUser,
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
/** 实例行:ext(实例变量对象)+ displayName/version(定义) */
|
|
846
|
+
function instanceRowToMap(r) {
|
|
847
|
+
return {
|
|
848
|
+
id: r.id, parentId: r.parentId ?? null, processDefineId: r.defineId,
|
|
849
|
+
state: r.state, parentNodeName: r.parentNodeName, businessNo: r.businessNo,
|
|
850
|
+
operator: r.operator, expireTime: fmtTime(r.expireTime),
|
|
851
|
+
variable: r.variables, createTime: fmtTime(r.createTime), createUser: r.createUser,
|
|
852
|
+
updateTime: fmtTime(r.updateTime), updateUser: r.updateUser,
|
|
853
|
+
processDefineName: r.defineName, processDefineDisplayName: r.defineDisplayName,
|
|
854
|
+
processDefineVersion: r.defineVersion,
|
|
855
|
+
ext: r.variables, displayName: r.defineDisplayName, version: r.defineVersion,
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
/** 抄送行:ext(实例变量对象)+ displayName/version(定义) */
|
|
859
|
+
function ccRowToMap(r) {
|
|
860
|
+
return {
|
|
861
|
+
id: r.id, parentId: r.parentId ?? null, processDefineId: r.defineId,
|
|
862
|
+
state: r.state, parentNodeName: r.parentNodeName, businessNo: r.businessNo,
|
|
863
|
+
operator: r.operator, expireTime: fmtTime(r.expireTime),
|
|
864
|
+
variable: r.variables, createTime: fmtTime(r.createTime), createUser: r.createUser,
|
|
865
|
+
updateTime: fmtTime(r.updateTime), updateUser: r.updateUser,
|
|
866
|
+
processDefineName: r.defineName, processDefineDisplayName: r.defineDisplayName,
|
|
867
|
+
processDefineVersion: r.defineVersion,
|
|
868
|
+
ext: r.variables, displayName: r.defineDisplayName, version: r.defineVersion,
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
/** 任务行:ext(任务变量,空回退实例变量)+ instanceExt + version */
|
|
872
|
+
function taskRowToMap(r) {
|
|
873
|
+
const instanceExt = parseVarMap(r.instanceVariable);
|
|
874
|
+
const ext = Object.keys(r.variables ?? {}).length > 0 ? r.variables : instanceExt;
|
|
875
|
+
return {
|
|
876
|
+
id: r.id, processInstanceId: r.processInstanceId, taskName: r.taskName,
|
|
877
|
+
displayName: r.displayName, taskType: r.taskType, performType: r.performType,
|
|
878
|
+
taskState: r.taskState, operator: r.operator, finishTime: fmtTime(r.finishTime),
|
|
879
|
+
expireTime: fmtTime(r.expireTime), formKey: r.formKey, taskParentId: r.taskParentId ?? null,
|
|
880
|
+
variable: r.variables, createTime: fmtTime(r.createTime), createUser: r.createUser,
|
|
881
|
+
updateTime: fmtTime(r.updateTime), updateUser: r.updateUser,
|
|
882
|
+
processDefineName: r.processDefineName, processDefineDisplayName: r.processDefineDisplayName,
|
|
883
|
+
instanceVariable: r.instanceVariable, instanceCreateTime: fmtTime(r.instanceCreateTime),
|
|
884
|
+
ext, instanceExt, version: r.defineVersion,
|
|
885
|
+
};
|
|
561
886
|
}
|
|
562
887
|
// ── 工具 ──────────────────────────────────────────────────────────────────────
|
|
888
|
+
/** m_ 前缀查询参数解析(issues/05-5,对齐 Java JeeflowQueryParser):
|
|
889
|
+
* m_EQ_taskName → t.task_name EQ;m_pd_LIKE_displayName → pd.display_name LIKE */
|
|
890
|
+
function parseMQuery(args) {
|
|
891
|
+
const out = [];
|
|
892
|
+
for (const [key, value] of Object.entries(args)) {
|
|
893
|
+
if (!key.startsWith('m_') || value == null || value === '')
|
|
894
|
+
continue;
|
|
895
|
+
const parts = key.slice(2).split('_');
|
|
896
|
+
if (parts.length < 2)
|
|
897
|
+
continue;
|
|
898
|
+
let column;
|
|
899
|
+
let operator;
|
|
900
|
+
if (parts.length === 2) {
|
|
901
|
+
// 无别名 → 默认主表别名 t(对齐 Java,白名单列均带表别名)
|
|
902
|
+
operator = parts[0];
|
|
903
|
+
column = 't.' + toUnderscore(parts[1]);
|
|
904
|
+
}
|
|
905
|
+
else {
|
|
906
|
+
operator = parts[1];
|
|
907
|
+
column = parts[0] + '.' + toUnderscore(parts[2]);
|
|
908
|
+
}
|
|
909
|
+
out.push({ column, operator: operator.toUpperCase(), value });
|
|
910
|
+
}
|
|
911
|
+
return out;
|
|
912
|
+
}
|
|
913
|
+
function toUnderscore(camel) {
|
|
914
|
+
return camel.replace(/[A-Z]/g, c => '_' + c.toLowerCase());
|
|
915
|
+
}
|
|
563
916
|
function toStr(v) {
|
|
564
917
|
if (v == null)
|
|
565
918
|
return '';
|
package/dist/jdbc/ext.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProcessDesign, ProcessDesignHis, ProcessSurrogate } from '../model.js';
|
|
2
|
-
import type { IDGenerator, ProcessExtRepository } from '../spi.js';
|
|
2
|
+
import type { IDGenerator, ProcessExtRepository, QueryCondition } from '../spi.js';
|
|
3
3
|
import { type SqlAdapter } from './shared.js';
|
|
4
4
|
export declare class JdbcProcessExtRepository implements ProcessExtRepository {
|
|
5
5
|
private readonly adapter;
|
|
@@ -13,7 +13,10 @@ export declare class JdbcProcessExtRepository implements ProcessExtRepository {
|
|
|
13
13
|
saveDesign(d: ProcessDesign): Promise<void>;
|
|
14
14
|
updateDesign(d: ProcessDesign): Promise<void>;
|
|
15
15
|
removeDesign(id: number): Promise<void>;
|
|
16
|
-
pageDesigns(pageNum?: number, pageSize?: number, filters?: Record<string, any
|
|
16
|
+
pageDesigns(pageNum?: number, pageSize?: number, filters?: Record<string, any>, conditions?: QueryCondition[]): Promise<[ProcessDesign[], number]>;
|
|
17
|
+
private buildExtWhere;
|
|
18
|
+
private static readonly DESIGN_WHITELIST;
|
|
19
|
+
private static readonly SURROGATE_WHITELIST;
|
|
17
20
|
saveDesignHis(his: ProcessDesignHis): Promise<void>;
|
|
18
21
|
listDesignHis(designId: number): Promise<ProcessDesignHis[]>;
|
|
19
22
|
private static SURROGATE_COLS;
|
|
@@ -21,7 +24,7 @@ export declare class JdbcProcessExtRepository implements ProcessExtRepository {
|
|
|
21
24
|
saveSurrogate(s: ProcessSurrogate): Promise<void>;
|
|
22
25
|
updateSurrogate(s: ProcessSurrogate): Promise<void>;
|
|
23
26
|
removeSurrogate(id: number): Promise<void>;
|
|
24
|
-
pageSurrogates(pageNum?: number, pageSize?: number, filters?: Record<string, any
|
|
27
|
+
pageSurrogates(pageNum?: number, pageSize?: number, filters?: Record<string, any>, conditions?: QueryCondition[]): Promise<[ProcessSurrogate[], number]>;
|
|
25
28
|
getSurrogate(operator: string, processName: string, at?: Date): Promise<ProcessSurrogate | null>;
|
|
26
29
|
private querySurrogate;
|
|
27
30
|
private mapDesign;
|
package/dist/jdbc/ext.js
CHANGED
|
@@ -76,7 +76,7 @@ export class JdbcProcessExtRepository {
|
|
|
76
76
|
await this.done(conn);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
async pageDesigns(pageNum = 1, pageSize = 10, filters) {
|
|
79
|
+
async pageDesigns(pageNum = 1, pageSize = 10, filters, conditions) {
|
|
80
80
|
let sql = `SELECT ${JdbcProcessExtRepository.DESIGN_COLS} FROM wf_process_design t WHERE 1=1`;
|
|
81
81
|
let countSql = 'SELECT COUNT(*) FROM wf_process_design t WHERE 1=1';
|
|
82
82
|
const args = [];
|
|
@@ -89,6 +89,12 @@ export class JdbcProcessExtRepository {
|
|
|
89
89
|
args2.push(val);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
+
// m_ 条件(issues/05-5):LIKE/EQ 等走白名单
|
|
93
|
+
const cond = this.buildExtWhere(conditions ?? [], JdbcProcessExtRepository.DESIGN_WHITELIST);
|
|
94
|
+
sql += cond.sql;
|
|
95
|
+
countSql += cond.sql;
|
|
96
|
+
args.push(...cond.params);
|
|
97
|
+
args2.push(...cond.params);
|
|
92
98
|
const conn = await this.c();
|
|
93
99
|
try {
|
|
94
100
|
const countRow = await conn.fetchOne(this.sql(countSql), args2);
|
|
@@ -102,6 +108,53 @@ export class JdbcProcessExtRepository {
|
|
|
102
108
|
await this.done(conn);
|
|
103
109
|
}
|
|
104
110
|
}
|
|
111
|
+
// m_ 条件 WHERE 构建(issues/05-5,白名单 + 参数化)
|
|
112
|
+
buildExtWhere(conditions, whitelist) {
|
|
113
|
+
let sql = '';
|
|
114
|
+
const params = [];
|
|
115
|
+
for (const c of conditions) {
|
|
116
|
+
if (!whitelist.has(c.column))
|
|
117
|
+
continue;
|
|
118
|
+
const val = c.value;
|
|
119
|
+
if (val == null || val === '')
|
|
120
|
+
continue;
|
|
121
|
+
switch (c.operator.toUpperCase()) {
|
|
122
|
+
case 'EQ':
|
|
123
|
+
sql += ` AND ${c.column} = ?`;
|
|
124
|
+
params.push(val);
|
|
125
|
+
break;
|
|
126
|
+
case 'LIKE':
|
|
127
|
+
sql += ` AND ${c.column} LIKE ?`;
|
|
128
|
+
params.push(`%${val}%`);
|
|
129
|
+
break;
|
|
130
|
+
case 'LLIKE':
|
|
131
|
+
sql += ` AND ${c.column} LIKE ?`;
|
|
132
|
+
params.push(`%${val}`);
|
|
133
|
+
break;
|
|
134
|
+
case 'RLIKE':
|
|
135
|
+
sql += ` AND ${c.column} LIKE ?`;
|
|
136
|
+
params.push(`${val}%`);
|
|
137
|
+
break;
|
|
138
|
+
case 'IN': {
|
|
139
|
+
if (Array.isArray(val) && val.length > 0) {
|
|
140
|
+
const marks = val.map(() => '?').join(',');
|
|
141
|
+
sql += ` AND ${c.column} IN (${marks})`;
|
|
142
|
+
params.push(...val);
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return { sql, params };
|
|
149
|
+
}
|
|
150
|
+
static DESIGN_WHITELIST = new Set([
|
|
151
|
+
't.id', 't.name', 't.display_name', 't.type', 't.is_deployed', 't.remark',
|
|
152
|
+
't.create_time', 't.update_time',
|
|
153
|
+
]);
|
|
154
|
+
static SURROGATE_WHITELIST = new Set([
|
|
155
|
+
't.id', 't.process_name', 't.operator', 't.surrogate', 't.enabled',
|
|
156
|
+
't.start_time', 't.end_time', 't.create_time', 't.update_time',
|
|
157
|
+
]);
|
|
105
158
|
// ── 设计历史 ─────────────────────────────────────────────────────────────
|
|
106
159
|
async saveDesignHis(his) {
|
|
107
160
|
if (!his.id)
|
|
@@ -182,7 +235,7 @@ export class JdbcProcessExtRepository {
|
|
|
182
235
|
await this.done(conn);
|
|
183
236
|
}
|
|
184
237
|
}
|
|
185
|
-
async pageSurrogates(pageNum = 1, pageSize = 10, filters) {
|
|
238
|
+
async pageSurrogates(pageNum = 1, pageSize = 10, filters, conditions) {
|
|
186
239
|
let sql = `SELECT ${JdbcProcessExtRepository.SURROGATE_COLS} FROM wf_process_surrogate t WHERE 1=1`;
|
|
187
240
|
let countSql = 'SELECT COUNT(*) FROM wf_process_surrogate t WHERE 1=1';
|
|
188
241
|
const args = [];
|
|
@@ -195,6 +248,12 @@ export class JdbcProcessExtRepository {
|
|
|
195
248
|
args2.push(val);
|
|
196
249
|
}
|
|
197
250
|
}
|
|
251
|
+
// m_ 条件(issues/05-5)
|
|
252
|
+
const cond = this.buildExtWhere(conditions ?? [], JdbcProcessExtRepository.SURROGATE_WHITELIST);
|
|
253
|
+
sql += cond.sql;
|
|
254
|
+
countSql += cond.sql;
|
|
255
|
+
args.push(...cond.params);
|
|
256
|
+
args2.push(...cond.params);
|
|
198
257
|
const conn = await this.c();
|
|
199
258
|
try {
|
|
200
259
|
const countRow = await conn.fetchOne(this.sql(countSql), args2);
|