@mldong/jeeflow 1.8.2 → 1.8.4
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 +5 -0
- package/dist/engine.js +43 -5
- package/dist/extensions.d.ts +2 -0
- package/dist/facade.d.ts +15 -0
- package/dist/facade.js +113 -6
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/package.json +4 -3
package/dist/engine.d.ts
CHANGED
|
@@ -31,6 +31,11 @@ export declare class EngineImpl implements Engine {
|
|
|
31
31
|
constructor(repo: ProcessRepository, userProv?: UserProvider | undefined, idGen?: IDGenerator | undefined, exprEval?: ExpressionEvaluator | undefined);
|
|
32
32
|
setExtensions(ext: EngineExtensions): void;
|
|
33
33
|
setRegistry(reg: HandlerRegistry): void;
|
|
34
|
+
private interceptorCache;
|
|
35
|
+
/** 定义级拦截器解析(issue 34,对齐 Java 模型级 postInterceptors):
|
|
36
|
+
* 流程定义顶层 postInterceptors 声明 → 按名从 interceptorRegistry 取(未声明该流程不触发);
|
|
37
|
+
* 未声明 → 回落引擎级列表(向后兼容)。结果按 defineId 缓存。 */
|
|
38
|
+
private resolveInterceptors;
|
|
34
39
|
private firePre;
|
|
35
40
|
/** 表达式求值(v1.5.0,门面 highLight 决策分支过滤用) */
|
|
36
41
|
evalExpr(expr: string, vars: Record<string, any>): Promise<any>;
|
package/dist/engine.js
CHANGED
|
@@ -28,12 +28,50 @@ export class EngineImpl {
|
|
|
28
28
|
this.idGen = idGen;
|
|
29
29
|
this.exprEval = exprEval;
|
|
30
30
|
}
|
|
31
|
-
setExtensions(ext) {
|
|
31
|
+
setExtensions(ext) {
|
|
32
|
+
this.ext = ext;
|
|
33
|
+
this.interceptorCache = new Map();
|
|
34
|
+
}
|
|
32
35
|
setRegistry(reg) { this.registry = reg; }
|
|
36
|
+
interceptorCache = new Map();
|
|
37
|
+
/** 定义级拦截器解析(issue 34,对齐 Java 模型级 postInterceptors):
|
|
38
|
+
* 流程定义顶层 postInterceptors 声明 → 按名从 interceptorRegistry 取(未声明该流程不触发);
|
|
39
|
+
* 未声明 → 回落引擎级列表(向后兼容)。结果按 defineId 缓存。 */
|
|
40
|
+
async resolveInterceptors(inst) {
|
|
41
|
+
if (!this.ext)
|
|
42
|
+
return [];
|
|
43
|
+
const defineId = inst.defineId;
|
|
44
|
+
if (defineId == null)
|
|
45
|
+
return this.ext.interceptors ?? [];
|
|
46
|
+
const cached = this.interceptorCache.get(defineId);
|
|
47
|
+
if (cached)
|
|
48
|
+
return cached;
|
|
49
|
+
let list = this.ext.interceptors ?? [];
|
|
50
|
+
try {
|
|
51
|
+
const def = await this.repo.findDefineById(defineId);
|
|
52
|
+
if (def) {
|
|
53
|
+
const content = typeof def.content === 'string' ? def.content : new TextDecoder().decode(def.content);
|
|
54
|
+
const meta = JSON.parse(content);
|
|
55
|
+
const declared = String(meta.postInterceptors ?? '').trim();
|
|
56
|
+
if (declared) {
|
|
57
|
+
list = [];
|
|
58
|
+
for (const name of declared.split(',').map(n => n.trim())) {
|
|
59
|
+
const ic = this.ext.interceptorRegistry?.[name];
|
|
60
|
+
if (name && ic)
|
|
61
|
+
list.push(ic);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch { /* 解析失败回落引擎级 */ }
|
|
67
|
+
this.interceptorCache.set(defineId, list);
|
|
68
|
+
return list;
|
|
69
|
+
}
|
|
33
70
|
async firePre(node, inst) {
|
|
34
|
-
if (!this.ext?.interceptors)
|
|
71
|
+
if (!this.ext?.interceptors && !this.ext?.interceptorRegistry)
|
|
35
72
|
return true;
|
|
36
|
-
|
|
73
|
+
const list = await this.resolveInterceptors(inst);
|
|
74
|
+
for (const ic of [...list].sort((a, b) => a.order - b.order))
|
|
37
75
|
if (!(await ic.preHandle(node, inst)))
|
|
38
76
|
return false;
|
|
39
77
|
return true;
|
|
@@ -45,9 +83,9 @@ export class EngineImpl {
|
|
|
45
83
|
return this.exprEval.eval(expr, vars);
|
|
46
84
|
}
|
|
47
85
|
async firePost(node, inst) {
|
|
48
|
-
if (!this.ext?.interceptors)
|
|
86
|
+
if (!this.ext?.interceptors && !this.ext?.interceptorRegistry)
|
|
49
87
|
return;
|
|
50
|
-
for (const ic of this.
|
|
88
|
+
for (const ic of await this.resolveInterceptors(inst))
|
|
51
89
|
await ic.postHandle(node, inst);
|
|
52
90
|
}
|
|
53
91
|
async fireEvent(evt) {
|
package/dist/extensions.d.ts
CHANGED
|
@@ -34,6 +34,8 @@ export interface ProcessEvent {
|
|
|
34
34
|
export type ProcessEventListener = (event: ProcessEvent) => void | Promise<void>;
|
|
35
35
|
export interface EngineExtensions {
|
|
36
36
|
interceptors?: FlowInterceptor[];
|
|
37
|
+
/** 定义级拦截器注册表(issue 34):名字 → 实例;流程定义顶层 postInterceptors 按名解析 */
|
|
38
|
+
interceptorRegistry?: Record<string, FlowInterceptor>;
|
|
37
39
|
assignmentHandler?: AssignmentHandler;
|
|
38
40
|
decisionHandler?: DecisionHandler;
|
|
39
41
|
listeners?: ProcessEventListener[];
|
package/dist/facade.d.ts
CHANGED
|
@@ -7,7 +7,12 @@ export declare class JeeflowFacade {
|
|
|
7
7
|
private readonly extRepo?;
|
|
8
8
|
private userSearch?;
|
|
9
9
|
private orgProv?;
|
|
10
|
+
private metaReader?;
|
|
10
11
|
constructor(engine: EngineImpl, repo: ProcessRepository, extRepo?: ProcessExtRepository | undefined);
|
|
12
|
+
/** 注入业务数据读取器(issue 30):需有 readByProcessInstance(tableName, processInstanceId) */
|
|
13
|
+
setMetaReader(reader: {
|
|
14
|
+
readByProcessInstance(tableName: string, processInstanceId: unknown): unknown;
|
|
15
|
+
}): this;
|
|
11
16
|
setUserSearch(fn: UserSearch): this;
|
|
12
17
|
setOrgProvider(orgProv: OrgUserProvider): this;
|
|
13
18
|
flow(action: string, args?: Record<string, any>): Promise<Record<string, any>>;
|
|
@@ -23,6 +28,16 @@ export declare class JeeflowFacade {
|
|
|
23
28
|
private designUpdate;
|
|
24
29
|
/** 更新流程设计定义(设计稿保存,issues/08):content 快照入库 + 同步基本信息 + 置未部署 */
|
|
25
30
|
private designUpdateDefine;
|
|
31
|
+
/** 删除设计稿(issues/28:兼容 {ids} 批量与单 {id}) */
|
|
32
|
+
private designRemove;
|
|
33
|
+
/** 删除定义(issues/28:兼容 {ids} 批量与单 {id}) */
|
|
34
|
+
private defineRemove;
|
|
35
|
+
/** 启用/停用(issues/28:兼容 {ids, opType} 批量;opType/state 二选一) */
|
|
36
|
+
private defineUpAndDown;
|
|
37
|
+
/** 按类型分组列出流程设计(issue 30,对齐 Java issues/28):不依赖框架字典 */
|
|
38
|
+
private designListByType;
|
|
39
|
+
/** 按流程实例回显业务数据(issue 30,对齐 Java issues/28):metaReader 注入式,未注入清晰报错 */
|
|
40
|
+
private bizData;
|
|
26
41
|
/** 重新部署流程定义(issues/08):替换最新定义内容 + 置已部署(对齐 boot3 redeploy) */
|
|
27
42
|
private designRedeploy;
|
|
28
43
|
private designDetail;
|
package/dist/facade.js
CHANGED
|
@@ -19,11 +19,17 @@ export class JeeflowFacade {
|
|
|
19
19
|
extRepo;
|
|
20
20
|
userSearch;
|
|
21
21
|
orgProv;
|
|
22
|
+
metaReader;
|
|
22
23
|
constructor(engine, repo, extRepo) {
|
|
23
24
|
this.engine = engine;
|
|
24
25
|
this.repo = repo;
|
|
25
26
|
this.extRepo = extRepo;
|
|
26
27
|
}
|
|
28
|
+
/** 注入业务数据读取器(issue 30):需有 readByProcessInstance(tableName, processInstanceId) */
|
|
29
|
+
setMetaReader(reader) {
|
|
30
|
+
this.metaReader = reader;
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
27
33
|
// 注入用户搜索钩子(candidatePage 无模型候选时的用户分页搜索)
|
|
28
34
|
setUserSearch(fn) {
|
|
29
35
|
this.userSearch = fn;
|
|
@@ -60,9 +66,9 @@ export class JeeflowFacade {
|
|
|
60
66
|
case 'processDefine/redeploy':
|
|
61
67
|
return this.redeploy(args);
|
|
62
68
|
case 'processDefine/remove':
|
|
63
|
-
return this.
|
|
69
|
+
return this.defineRemove(args);
|
|
64
70
|
case 'processDefine/upAndDown':
|
|
65
|
-
return this.
|
|
71
|
+
return this.defineUpAndDown(args);
|
|
66
72
|
case 'processInstance/page':
|
|
67
73
|
return this.instancePage(args);
|
|
68
74
|
case 'processInstance/detail':
|
|
@@ -86,7 +92,11 @@ export class JeeflowFacade {
|
|
|
86
92
|
case 'processDesign/updateDefine':
|
|
87
93
|
return this.designUpdateDefine(args);
|
|
88
94
|
case 'processDesign/remove':
|
|
89
|
-
return this.
|
|
95
|
+
return this.designRemove(args);
|
|
96
|
+
case 'processDesign/listByType':
|
|
97
|
+
return this.designListByType(args);
|
|
98
|
+
case 'processInstance/bizData':
|
|
99
|
+
return this.bizData(args);
|
|
90
100
|
case 'processSurrogate/page':
|
|
91
101
|
return this.surrogatePage(args);
|
|
92
102
|
case 'processSurrogate/save':
|
|
@@ -292,9 +302,19 @@ export class JeeflowFacade {
|
|
|
292
302
|
const design = await ext.findDesignById(designId);
|
|
293
303
|
if (!design)
|
|
294
304
|
throw new Error('流程设计不存在');
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
305
|
+
// issues/31:兼容 boot3 顶层 JSON(无 content 字段)——非保留字段序列化为内容快照
|
|
306
|
+
let content = args.content;
|
|
307
|
+
if (content == null) {
|
|
308
|
+
const copy = {};
|
|
309
|
+
for (const [k, v] of Object.entries(args)) {
|
|
310
|
+
if (k !== 'processDesignId' && k !== 'operator')
|
|
311
|
+
copy[k] = v;
|
|
312
|
+
}
|
|
313
|
+
if (Object.keys(copy).length === 0)
|
|
314
|
+
throw new Error('content 缺失');
|
|
315
|
+
content = JSON.stringify(copy);
|
|
316
|
+
}
|
|
317
|
+
content = toStr(content);
|
|
298
318
|
// 与最新一条相同则不重复入库(对齐 boot3 updateDefine)
|
|
299
319
|
const hisList = await ext.listDesignHis(designId);
|
|
300
320
|
if (hisList.length === 0 || toStr(hisList[0].content) !== content) {
|
|
@@ -319,6 +339,93 @@ export class JeeflowFacade {
|
|
|
319
339
|
await ext.updateDesign(design);
|
|
320
340
|
return {};
|
|
321
341
|
}
|
|
342
|
+
/** 删除设计稿(issues/28:兼容 {ids} 批量与单 {id}) */
|
|
343
|
+
async designRemove(args) {
|
|
344
|
+
const ext = this.ext();
|
|
345
|
+
if (Array.isArray(args.ids)) {
|
|
346
|
+
for (const id of args.ids)
|
|
347
|
+
await ext.removeDesign(toId(id));
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
await ext.removeDesign(toId(args.id));
|
|
351
|
+
}
|
|
352
|
+
return {};
|
|
353
|
+
}
|
|
354
|
+
/** 删除定义(issues/28:兼容 {ids} 批量与单 {id}) */
|
|
355
|
+
async defineRemove(args) {
|
|
356
|
+
if (Array.isArray(args.ids)) {
|
|
357
|
+
for (const id of args.ids)
|
|
358
|
+
await this.repo.removeDefine(toId(id));
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
await this.repo.removeDefine(toId(args.id));
|
|
362
|
+
}
|
|
363
|
+
return {};
|
|
364
|
+
}
|
|
365
|
+
/** 启用/停用(issues/28:兼容 {ids, opType} 批量;opType/state 二选一) */
|
|
366
|
+
async defineUpAndDown(args) {
|
|
367
|
+
const state = toInt(args.opType ?? args.state);
|
|
368
|
+
if (Array.isArray(args.ids)) {
|
|
369
|
+
for (const id of args.ids)
|
|
370
|
+
await this.repo.updateDefineState(toId(id), state);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
await this.repo.updateDefineState(toId(args.id), state);
|
|
374
|
+
}
|
|
375
|
+
return {};
|
|
376
|
+
}
|
|
377
|
+
/** 按类型分组列出流程设计(issue 30,对齐 Java issues/28):不依赖框架字典 */
|
|
378
|
+
async designListByType(args) {
|
|
379
|
+
const ext = this.ext();
|
|
380
|
+
const pageNum = args.pageNum != null ? toInt(args.pageNum) : 1;
|
|
381
|
+
const pageSize = args.pageSize != null ? toInt(args.pageSize) : 10000;
|
|
382
|
+
const [rows] = await ext.pageDesigns(pageNum, pageSize, []);
|
|
383
|
+
// 每 name 最新 define(version 最大)
|
|
384
|
+
const { rows: defRows } = await this.repo.pageDefines(1, 10000, []);
|
|
385
|
+
const latestByName = new Map();
|
|
386
|
+
for (const r of defRows) {
|
|
387
|
+
const prev = latestByName.get(r.name);
|
|
388
|
+
if (!prev || r.version > prev.version)
|
|
389
|
+
latestByName.set(r.name, r);
|
|
390
|
+
}
|
|
391
|
+
const groups = {};
|
|
392
|
+
for (const d of rows) {
|
|
393
|
+
const key = d.type || '';
|
|
394
|
+
(groups[key] ??= []).push({
|
|
395
|
+
processDesignId: d.id,
|
|
396
|
+
name: d.name,
|
|
397
|
+
displayName: d.displayName,
|
|
398
|
+
icon: d.icon,
|
|
399
|
+
remark: d.remark,
|
|
400
|
+
processDefineId: latestByName.get(d.name)?.id ?? null,
|
|
401
|
+
processDefineState: latestByName.get(d.name)?.state ?? null,
|
|
402
|
+
jsonObject: this.parseGraph((await ext.listDesignHis(d.id))[0]?.content ?? ''),
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
return groups;
|
|
406
|
+
}
|
|
407
|
+
/** 按流程实例回显业务数据(issue 30,对齐 Java issues/28):metaReader 注入式,未注入清晰报错 */
|
|
408
|
+
async bizData(args) {
|
|
409
|
+
const instanceId = toId(args.processInstanceId ?? args.id);
|
|
410
|
+
const inst = await this.repo.findInstanceById(instanceId);
|
|
411
|
+
if (!inst)
|
|
412
|
+
throw new Error('流程实例不存在');
|
|
413
|
+
const def = await this.repo.findDefineById(inst.defineId);
|
|
414
|
+
if (!def)
|
|
415
|
+
throw new Error('流程定义不存在');
|
|
416
|
+
const content = typeof def.content === 'string' ? def.content : new TextDecoder().decode(def.content);
|
|
417
|
+
let tableName = null;
|
|
418
|
+
try {
|
|
419
|
+
const meta = JSON.parse(content);
|
|
420
|
+
tableName = String(meta.relTableName ?? '').trim() || String(meta.name ?? '').trim() || null;
|
|
421
|
+
}
|
|
422
|
+
catch { /* ignore */ }
|
|
423
|
+
if (!tableName)
|
|
424
|
+
throw new Error('流程定义未配置 relTableName');
|
|
425
|
+
if (!this.metaReader)
|
|
426
|
+
throw new Error('业务数据读取器未注册(facade.setMetaReader(MetaTableReader(...)),需引入 jeeflow.meta)');
|
|
427
|
+
return this.metaReader.readByProcessInstance(tableName, instanceId);
|
|
428
|
+
}
|
|
322
429
|
/** 重新部署流程定义(issues/08):替换最新定义内容 + 置已部署(对齐 boot3 redeploy) */
|
|
323
430
|
async designRedeploy(args) {
|
|
324
431
|
const ext = this.ext();
|
package/dist/index.d.ts
CHANGED
|
@@ -6,3 +6,6 @@ export * from './model.js';
|
|
|
6
6
|
export type { ProcessRepository, UserProvider, OrgUserProvider, IDGenerator, ExpressionEvaluator } from './spi.js';
|
|
7
7
|
export { SqliteDynamicTableWriter, PersistPostInterceptor, type DynamicTableWriter, type DefineLoader, } from './persist.js';
|
|
8
8
|
export { registerBuiltinAssignments, OperatorAssignmentHandler, FormFieldAssigneeHandler, DeptLeaderAssignmentHandler, DeptMainLeaderAssignmentHandler, ApplicantDeptLeaderAssignmentHandler, ApplicantDeptMainLeaderAssignmentHandler, TaskRoleAssigneeHandler, HANDLER_OPERATOR_ASSIGNMENT, HANDLER_FORM_FIELD_ASSIGNEE, HANDLER_DEPT_LEADER, HANDLER_DEPT_MAIN_LEADER, HANDLER_APPLICANT_DEPT_LEADER, HANDLER_APPLICANT_DEPT_MAIN_LEADER, HANDLER_TASK_ROLE_ASSIGNEE, } from './builtin.js';
|
|
9
|
+
export { JeeflowFacade } from './facade.js';
|
|
10
|
+
export type { EngineExtensions, FlowInterceptor, ProcessEventListener, ProcessEvent, AssignmentHandler, DecisionHandler } from './extensions.js';
|
|
11
|
+
export { JdbcProcessExtRepository } from './jdbc/ext.js';
|
package/dist/index.js
CHANGED
|
@@ -5,3 +5,6 @@ export { enumDict, enumDictKeys } from './metadata.js';
|
|
|
5
5
|
export * from './model.js';
|
|
6
6
|
export { SqliteDynamicTableWriter, PersistPostInterceptor, } from './persist.js';
|
|
7
7
|
export { registerBuiltinAssignments, OperatorAssignmentHandler, FormFieldAssigneeHandler, DeptLeaderAssignmentHandler, DeptMainLeaderAssignmentHandler, ApplicantDeptLeaderAssignmentHandler, ApplicantDeptMainLeaderAssignmentHandler, TaskRoleAssigneeHandler, HANDLER_OPERATOR_ASSIGNMENT, HANDLER_FORM_FIELD_ASSIGNEE, HANDLER_DEPT_LEADER, HANDLER_DEPT_MAIN_LEADER, HANDLER_APPLICANT_DEPT_LEADER, HANDLER_APPLICANT_DEPT_MAIN_LEADER, HANDLER_TASK_ROLE_ASSIGNEE, } from './builtin.js';
|
|
8
|
+
// issues/35:包导出面补齐——门面/扩展类型/扩展仓储(集成方组装完整引擎链)
|
|
9
|
+
export { JeeflowFacade } from './facade.js';
|
|
10
|
+
export { JdbcProcessExtRepository } from './jdbc/ext.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mldong/jeeflow",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.4",
|
|
4
4
|
"description": "jeeflow workflow engine — Node.js / TypeScript implementation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"./memory": "./dist/memory.js",
|
|
14
14
|
"./jdbc": "./dist/jdbc/index.js",
|
|
15
15
|
"./mysql": "./dist/jdbc/mysql.js",
|
|
16
|
-
"./postgres": "./dist/jdbc/postgres.js"
|
|
16
|
+
"./postgres": "./dist/jdbc/postgres.js",
|
|
17
|
+
"./jdbc/ext": "./dist/jdbc/ext.js"
|
|
17
18
|
},
|
|
18
19
|
"scripts": {
|
|
19
20
|
"build": "tsc",
|
|
@@ -44,4 +45,4 @@
|
|
|
44
45
|
"LICENSE",
|
|
45
46
|
"README.md"
|
|
46
47
|
]
|
|
47
|
-
}
|
|
48
|
+
}
|