@mldong/jeeflow 1.4.0 → 1.5.0
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 +12 -0
- package/dist/facade.js +259 -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
|
@@ -24,6 +24,8 @@ export declare class JeeflowFacade {
|
|
|
24
24
|
private getLastByName;
|
|
25
25
|
private highLight;
|
|
26
26
|
private collectPath;
|
|
27
|
+
/** 决策输出边表达式求值(args = 实例变量 + 决策节点前置任务变量) */
|
|
28
|
+
private evalDecisionExpr;
|
|
27
29
|
private approvalRecord;
|
|
28
30
|
private getAssigneeTextData;
|
|
29
31
|
private createCCInstance;
|
|
@@ -36,4 +38,14 @@ export declare class JeeflowFacade {
|
|
|
36
38
|
private taskAddActor;
|
|
37
39
|
private taskLatest;
|
|
38
40
|
private ext;
|
|
41
|
+
private definePage;
|
|
42
|
+
private defineDetail;
|
|
43
|
+
private instancePage;
|
|
44
|
+
private instanceDetail;
|
|
45
|
+
/** 流程 JSON 中第一个任务节点 id(issues/05-4 isFirstTaskNode 用) */
|
|
46
|
+
private firstTaskNodeId;
|
|
47
|
+
private todoList;
|
|
48
|
+
private doneList;
|
|
49
|
+
/** 定义 content 解析为 LogicFlow JSON(issues/05 jsonObject) */
|
|
50
|
+
private parseGraph;
|
|
39
51
|
}
|
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,6 +38,10 @@ 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);
|
|
@@ -49,8 +54,16 @@ export class JeeflowFacade {
|
|
|
49
54
|
return this.repo.removeDefine(toId(args.id));
|
|
50
55
|
case 'processDefine/upAndDown':
|
|
51
56
|
return this.repo.updateDefineState(toId(args.id), toInt(args.state));
|
|
57
|
+
case 'processInstance/page':
|
|
58
|
+
return this.instancePage(args);
|
|
59
|
+
case 'processInstance/detail':
|
|
60
|
+
return this.instanceDetail(args);
|
|
52
61
|
case 'processInstance/withdraw':
|
|
53
62
|
return this.withdraw(args);
|
|
63
|
+
case 'processTask/todoList':
|
|
64
|
+
return this.todoList(args);
|
|
65
|
+
case 'processTask/doneList':
|
|
66
|
+
return this.doneList(args);
|
|
54
67
|
case 'processTask/execute':
|
|
55
68
|
return this.execute(args);
|
|
56
69
|
case 'processDesign/page':
|
|
@@ -231,7 +244,7 @@ export class JeeflowFacade {
|
|
|
231
244
|
}
|
|
232
245
|
// ── 流程设计(需扩展仓储) ───────────────────────────────────────────────
|
|
233
246
|
async designPage(args) {
|
|
234
|
-
const [rows, total] = await this.ext().pageDesigns(toInt(args.pageNum ?? 1), toInt(args.pageSize ?? 10));
|
|
247
|
+
const [rows, total] = await this.ext().pageDesigns(toInt(args.pageNum ?? 1), toInt(args.pageSize ?? 10), undefined, parseMQuery(args));
|
|
235
248
|
return { rows, recordCount: total };
|
|
236
249
|
}
|
|
237
250
|
async designDetail(args) {
|
|
@@ -244,12 +257,25 @@ export class JeeflowFacade {
|
|
|
244
257
|
type: design.type, icon: design.icon, isDeployed: design.isDeployed, remark: design.remark,
|
|
245
258
|
};
|
|
246
259
|
const hisList = await ext.listDesignHis(design.id);
|
|
260
|
+
let jsonObject;
|
|
247
261
|
if (hisList.length > 0) {
|
|
248
262
|
try {
|
|
249
|
-
|
|
263
|
+
jsonObject = JSON.parse(toStr(hisList[0].content));
|
|
250
264
|
}
|
|
251
265
|
catch { /* ignore */ }
|
|
252
266
|
}
|
|
267
|
+
// issues/07:jsonObject 缺失基本信息时从设计表补齐(对齐 boot3 ProcessDesignServiceImpl.findById)
|
|
268
|
+
if (!jsonObject || typeof jsonObject !== 'object')
|
|
269
|
+
jsonObject = {};
|
|
270
|
+
if (!(jsonObject.name))
|
|
271
|
+
jsonObject.name = design.name;
|
|
272
|
+
if (!(jsonObject.displayName))
|
|
273
|
+
jsonObject.displayName = design.displayName;
|
|
274
|
+
if (!(jsonObject.type))
|
|
275
|
+
jsonObject.type = design.type;
|
|
276
|
+
if (!(jsonObject.processDesignId))
|
|
277
|
+
jsonObject.processDesignId = design.id;
|
|
278
|
+
data.jsonObject = jsonObject;
|
|
253
279
|
data.his = hisList;
|
|
254
280
|
return data;
|
|
255
281
|
}
|
|
@@ -296,7 +322,7 @@ export class JeeflowFacade {
|
|
|
296
322
|
// ── 委托代理(需扩展仓储) ───────────────────────────────────────────────
|
|
297
323
|
async surrogatePage(args) {
|
|
298
324
|
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);
|
|
325
|
+
const [rows, total] = await this.ext().pageSurrogates(toInt(args.pageNum ?? 1), toInt(args.pageSize ?? 10), filters, parseMQuery(args));
|
|
300
326
|
return { rows, recordCount: total };
|
|
301
327
|
}
|
|
302
328
|
async surrogateSave(args) {
|
|
@@ -353,26 +379,33 @@ export class JeeflowFacade {
|
|
|
353
379
|
for (const t of his)
|
|
354
380
|
if (!active.includes(t.taskName) && !history.includes(t.taskName))
|
|
355
381
|
history.push(t.taskName);
|
|
356
|
-
// 路径补全:start
|
|
382
|
+
// 路径补全:start 沿边递归(遇活跃节点停止);决策分支按表达式求值过滤(issues/06)
|
|
357
383
|
const def = await this.repo.findDefineById(inst.defineId);
|
|
358
384
|
if (def) {
|
|
359
385
|
try {
|
|
360
386
|
const flow = JSON.parse(toStr(def.content));
|
|
361
|
-
this.collectPath(flow, 'start', '', active, history, edges, new Set());
|
|
387
|
+
await this.collectPath(flow, 'start', '', active, history, edges, new Set(), inst.variables ?? {}, his);
|
|
362
388
|
}
|
|
363
389
|
catch { /* ignore */ }
|
|
364
390
|
}
|
|
365
391
|
return { activeNodeNames: active, historyNodeNames: history, historyEdgeNames: edges };
|
|
366
392
|
}
|
|
367
|
-
collectPath(flow, nodeId, edgeName, active, history, edges, visited) {
|
|
393
|
+
async collectPath(flow, nodeId, edgeName, active, history, edges, visited, vars, historyTasks) {
|
|
368
394
|
if (visited.has(nodeId))
|
|
369
395
|
return;
|
|
370
396
|
visited.add(nodeId);
|
|
371
397
|
if (edgeName && !edges.includes(edgeName))
|
|
372
398
|
edges.push(edgeName);
|
|
399
|
+
const src = (flow.nodes ?? []).find((n) => n.id === nodeId);
|
|
373
400
|
for (const e of flow.edges ?? []) {
|
|
374
401
|
if (e.sourceNodeId !== nodeId)
|
|
375
402
|
continue;
|
|
403
|
+
// 决策节点:输出边表达式求值过滤(对齐 boot3 recursionModel,issues/06)
|
|
404
|
+
if (src?.type === 'snaker:decision') {
|
|
405
|
+
const expr = e.properties?.expr;
|
|
406
|
+
if (expr && !await this.evalDecisionExpr(flow, src, expr, vars, historyTasks))
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
376
409
|
const target = (flow.nodes ?? []).find((n) => n.id === e.targetNodeId);
|
|
377
410
|
if (!target)
|
|
378
411
|
continue;
|
|
@@ -381,7 +414,25 @@ export class JeeflowFacade {
|
|
|
381
414
|
history.push(tid);
|
|
382
415
|
if (active.includes(tid))
|
|
383
416
|
continue;
|
|
384
|
-
this.collectPath(flow, tid, e.id, active, history, edges, visited);
|
|
417
|
+
await this.collectPath(flow, tid, e.id, active, history, edges, visited, vars, historyTasks);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/** 决策输出边表达式求值(args = 实例变量 + 决策节点前置任务变量) */
|
|
421
|
+
async evalDecisionExpr(flow, decision, expr, vars, historyTasks) {
|
|
422
|
+
const args = { ...(vars ?? {}) };
|
|
423
|
+
for (const e of flow.edges ?? []) {
|
|
424
|
+
if (e.targetNodeId === decision.id) {
|
|
425
|
+
const t = (historyTasks ?? []).find((x) => x.taskName === e.sourceNodeId);
|
|
426
|
+
if (t?.variables)
|
|
427
|
+
Object.assign(args, t.variables);
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
try {
|
|
432
|
+
return Boolean(await this.engine.evalExpr(expr, args));
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
return false;
|
|
385
436
|
}
|
|
386
437
|
}
|
|
387
438
|
async approvalRecord(args) {
|
|
@@ -424,8 +475,8 @@ export class JeeflowFacade {
|
|
|
424
475
|
const pageNum = toInt(args.pageNum ?? 1);
|
|
425
476
|
const pageSize = toInt(args.pageSize ?? 10);
|
|
426
477
|
const actorId = String(args.operator ?? 'user1');
|
|
427
|
-
const { rows, total } = await this.repo.pageCcInstances(pageNum, pageSize, actorId);
|
|
428
|
-
return { rows, recordCount: total };
|
|
478
|
+
const { rows, total } = await this.repo.pageCcInstances(pageNum, pageSize, actorId, parseMQuery(args));
|
|
479
|
+
return { rows: rows.map(r => ccRowToMap(r)), recordCount: total };
|
|
429
480
|
}
|
|
430
481
|
async taskDetail(args) {
|
|
431
482
|
const taskId = toId(args.id);
|
|
@@ -446,6 +497,7 @@ export class JeeflowFacade {
|
|
|
446
497
|
if (inst) {
|
|
447
498
|
const def = await this.repo.findDefineById(inst.defineId);
|
|
448
499
|
if (def) {
|
|
500
|
+
vo.jsonObject = this.parseGraph(def.content); // issues/05
|
|
449
501
|
try {
|
|
450
502
|
const flow = JSON.parse(toStr(def.content));
|
|
451
503
|
for (const n of flow.nodes ?? []) {
|
|
@@ -558,8 +610,206 @@ export class JeeflowFacade {
|
|
|
558
610
|
throw new Error('未配置 ProcessExtRepository(扩展仓储)');
|
|
559
611
|
return this.extRepo;
|
|
560
612
|
}
|
|
613
|
+
// ═══ 基础分页/详情(v1.5.0 补齐,对齐 Java 门面)═══
|
|
614
|
+
async definePage(args) {
|
|
615
|
+
const pageNum = toInt(args.pageNum ?? 1);
|
|
616
|
+
const pageSize = toInt(args.pageSize ?? 10);
|
|
617
|
+
const { rows, total } = await this.repo.pageDefines(pageNum, pageSize, parseMQuery(args));
|
|
618
|
+
return { rows: rows.map(r => defineRowToMap(r)), recordCount: total };
|
|
619
|
+
}
|
|
620
|
+
async defineDetail(args) {
|
|
621
|
+
const id = toId(args.id);
|
|
622
|
+
const def = await this.repo.findDefineById(id);
|
|
623
|
+
if (!def)
|
|
624
|
+
throw new Error('流程定义不存在');
|
|
625
|
+
return {
|
|
626
|
+
id: def.id, name: def.name, displayName: def.displayName,
|
|
627
|
+
type: def.type, state: def.state, version: def.version,
|
|
628
|
+
jsonObject: this.parseGraph(def.content), // issues/05
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
async instancePage(args) {
|
|
632
|
+
const pageNum = toInt(args.pageNum ?? 1);
|
|
633
|
+
const pageSize = toInt(args.pageSize ?? 10);
|
|
634
|
+
const operator = String(args.operator ?? 'user1');
|
|
635
|
+
const { rows, total } = await this.repo.pageInstances(pageNum, pageSize, operator, parseMQuery(args));
|
|
636
|
+
return { rows: rows.map(r => instanceRowToMap(r)), recordCount: total };
|
|
637
|
+
}
|
|
638
|
+
async instanceDetail(args) {
|
|
639
|
+
const id = toId(args.id);
|
|
640
|
+
const inst = await this.repo.findInstanceById(id);
|
|
641
|
+
if (!inst)
|
|
642
|
+
throw new Error('流程实例不存在');
|
|
643
|
+
const def0 = await this.repo.findDefineById(inst.defineId);
|
|
644
|
+
const graph = def0 ? this.parseGraph(def0.content) : undefined;
|
|
645
|
+
// 任务列表(issues/05-4):全量 tasks + activeTaskList(仅 DOING)+ 任务行 ext/isFirstTaskNode
|
|
646
|
+
const firstTaskNodeId = this.firstTaskNodeId(graph);
|
|
647
|
+
const tasks = [];
|
|
648
|
+
const activeTaskList = [];
|
|
649
|
+
for (const t of inst.tasks ?? []) {
|
|
650
|
+
const vo = {
|
|
651
|
+
id: t.id, processInstanceId: t.processInstanceId, taskName: t.taskName,
|
|
652
|
+
displayName: t.displayName, taskType: t.taskType ?? null,
|
|
653
|
+
performType: t.performType ?? null, taskState: t.taskState,
|
|
654
|
+
operator: t.actorId ?? '', finishTime: t.finishTime,
|
|
655
|
+
expireTime: t.expireTime, formKey: t.formKey ?? '', taskParentId: t.parentTaskId ?? null,
|
|
656
|
+
variable: JSON.stringify(t.variables ?? {}),
|
|
657
|
+
createTime: t.createTime, createUser: t.createUser,
|
|
658
|
+
updateTime: t.updateTime, updateUser: t.updateUser,
|
|
659
|
+
taskActorIdList: t.actorIds ?? [],
|
|
660
|
+
};
|
|
661
|
+
const ext = { ...(t.variables ?? {}) };
|
|
662
|
+
const doing = t.taskState === TaskState.Doing;
|
|
663
|
+
ext.isFirstTaskNode = doing && t.taskName === firstTaskNodeId;
|
|
664
|
+
vo.ext = ext;
|
|
665
|
+
tasks.push(vo);
|
|
666
|
+
if (doing)
|
|
667
|
+
activeTaskList.push(vo);
|
|
668
|
+
}
|
|
669
|
+
return {
|
|
670
|
+
id: inst.id, parentId: inst.parentId, processDefineId: inst.defineId,
|
|
671
|
+
state: inst.state, parentNodeName: inst.parentNodeName,
|
|
672
|
+
businessNo: inst.businessNo, operator: inst.operator,
|
|
673
|
+
variables: inst.variables, createTime: inst.createTime, createUser: inst.createUser,
|
|
674
|
+
jsonObject: graph, // issues/05
|
|
675
|
+
tasks,
|
|
676
|
+
activeTaskList,
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
/** 流程 JSON 中第一个任务节点 id(issues/05-4 isFirstTaskNode 用) */
|
|
680
|
+
firstTaskNodeId(graph) {
|
|
681
|
+
for (const n of graph?.nodes ?? []) {
|
|
682
|
+
if (n?.type === 'snaker:task')
|
|
683
|
+
return n.id;
|
|
684
|
+
}
|
|
685
|
+
return '';
|
|
686
|
+
}
|
|
687
|
+
async todoList(args) {
|
|
688
|
+
const pageNum = toInt(args.pageNum ?? 1);
|
|
689
|
+
const pageSize = toInt(args.pageSize ?? 10);
|
|
690
|
+
const actorId = String(args.operator ?? 'user1');
|
|
691
|
+
const { rows, total } = await this.repo.pageTodoTasks(pageNum, pageSize, actorId, parseMQuery(args));
|
|
692
|
+
return { rows: rows.map(r => taskRowToMap(r)), recordCount: total };
|
|
693
|
+
}
|
|
694
|
+
async doneList(args) {
|
|
695
|
+
const pageNum = toInt(args.pageNum ?? 1);
|
|
696
|
+
const pageSize = toInt(args.pageSize ?? 10);
|
|
697
|
+
const operator = String(args.operator ?? 'user1');
|
|
698
|
+
const { rows, total } = await this.repo.pageDoneTasks(pageNum, pageSize, operator, parseMQuery(args));
|
|
699
|
+
return { rows: rows.map(r => taskRowToMap(r)), recordCount: total };
|
|
700
|
+
}
|
|
701
|
+
/** 定义 content 解析为 LogicFlow JSON(issues/05 jsonObject) */
|
|
702
|
+
parseGraph(content) {
|
|
703
|
+
try {
|
|
704
|
+
const str = typeof content === 'string' ? content : new TextDecoder().decode(content);
|
|
705
|
+
const obj = JSON.parse(str);
|
|
706
|
+
return obj && typeof obj === 'object' ? obj : undefined;
|
|
707
|
+
}
|
|
708
|
+
catch {
|
|
709
|
+
return undefined;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
// ── 行转 Map(issues/05-2 列表字段契约 + 05-3 时间格式)─────────────────────
|
|
714
|
+
/** Date → 'yyyy-MM-dd HH:mm:ss'(null/undefined → null) */
|
|
715
|
+
function fmtTime(v) {
|
|
716
|
+
if (v == null)
|
|
717
|
+
return null;
|
|
718
|
+
const p = (n) => String(n).padStart(2, '0');
|
|
719
|
+
return `${v.getFullYear()}-${p(v.getMonth() + 1)}-${p(v.getDate())} ${p(v.getHours())}:${p(v.getMinutes())}:${p(v.getSeconds())}`;
|
|
720
|
+
}
|
|
721
|
+
/** JSON 字符串 → 对象(坏 JSON / 空返回空对象) */
|
|
722
|
+
function parseVarMap(json) {
|
|
723
|
+
if (!json)
|
|
724
|
+
return {};
|
|
725
|
+
try {
|
|
726
|
+
const o = JSON.parse(json);
|
|
727
|
+
return o && typeof o === 'object' ? o : {};
|
|
728
|
+
}
|
|
729
|
+
catch {
|
|
730
|
+
return {};
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
/** 定义行:时间格式化 */
|
|
734
|
+
function defineRowToMap(r) {
|
|
735
|
+
return {
|
|
736
|
+
id: r.id, name: r.name, displayName: r.displayName, type: r.type,
|
|
737
|
+
state: r.state, version: r.version,
|
|
738
|
+
createTime: fmtTime(r.createTime), createUser: r.createUser,
|
|
739
|
+
updateTime: fmtTime(r.updateTime), updateUser: r.updateUser,
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
/** 实例行:ext(实例变量对象)+ displayName/version(定义) */
|
|
743
|
+
function instanceRowToMap(r) {
|
|
744
|
+
return {
|
|
745
|
+
id: r.id, parentId: r.parentId ?? null, processDefineId: r.defineId,
|
|
746
|
+
state: r.state, parentNodeName: r.parentNodeName, businessNo: r.businessNo,
|
|
747
|
+
operator: r.operator, expireTime: fmtTime(r.expireTime),
|
|
748
|
+
variable: r.variables, createTime: fmtTime(r.createTime), createUser: r.createUser,
|
|
749
|
+
updateTime: fmtTime(r.updateTime), updateUser: r.updateUser,
|
|
750
|
+
processDefineName: r.defineName, processDefineDisplayName: r.defineDisplayName,
|
|
751
|
+
processDefineVersion: r.defineVersion,
|
|
752
|
+
ext: r.variables, displayName: r.defineDisplayName, version: r.defineVersion,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
/** 抄送行:ext(实例变量对象)+ displayName/version(定义) */
|
|
756
|
+
function ccRowToMap(r) {
|
|
757
|
+
return {
|
|
758
|
+
id: r.id, parentId: r.parentId ?? null, processDefineId: r.defineId,
|
|
759
|
+
state: r.state, parentNodeName: r.parentNodeName, businessNo: r.businessNo,
|
|
760
|
+
operator: r.operator, expireTime: fmtTime(r.expireTime),
|
|
761
|
+
variable: r.variables, createTime: fmtTime(r.createTime), createUser: r.createUser,
|
|
762
|
+
updateTime: fmtTime(r.updateTime), updateUser: r.updateUser,
|
|
763
|
+
processDefineName: r.defineName, processDefineDisplayName: r.defineDisplayName,
|
|
764
|
+
processDefineVersion: r.defineVersion,
|
|
765
|
+
ext: r.variables, displayName: r.defineDisplayName, version: r.defineVersion,
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
/** 任务行:ext(任务变量,空回退实例变量)+ instanceExt + version */
|
|
769
|
+
function taskRowToMap(r) {
|
|
770
|
+
const instanceExt = parseVarMap(r.instanceVariable);
|
|
771
|
+
const ext = Object.keys(r.variables ?? {}).length > 0 ? r.variables : instanceExt;
|
|
772
|
+
return {
|
|
773
|
+
id: r.id, processInstanceId: r.processInstanceId, taskName: r.taskName,
|
|
774
|
+
displayName: r.displayName, taskType: r.taskType, performType: r.performType,
|
|
775
|
+
taskState: r.taskState, operator: r.operator, finishTime: fmtTime(r.finishTime),
|
|
776
|
+
expireTime: fmtTime(r.expireTime), formKey: r.formKey, taskParentId: r.taskParentId ?? null,
|
|
777
|
+
variable: r.variables, createTime: fmtTime(r.createTime), createUser: r.createUser,
|
|
778
|
+
updateTime: fmtTime(r.updateTime), updateUser: r.updateUser,
|
|
779
|
+
processDefineName: r.processDefineName, processDefineDisplayName: r.processDefineDisplayName,
|
|
780
|
+
instanceVariable: r.instanceVariable, instanceCreateTime: fmtTime(r.instanceCreateTime),
|
|
781
|
+
ext, instanceExt, version: r.defineVersion,
|
|
782
|
+
};
|
|
561
783
|
}
|
|
562
784
|
// ── 工具 ──────────────────────────────────────────────────────────────────────
|
|
785
|
+
/** m_ 前缀查询参数解析(issues/05-5,对齐 Java JeeflowQueryParser):
|
|
786
|
+
* m_EQ_taskName → t.task_name EQ;m_pd_LIKE_displayName → pd.display_name LIKE */
|
|
787
|
+
function parseMQuery(args) {
|
|
788
|
+
const out = [];
|
|
789
|
+
for (const [key, value] of Object.entries(args)) {
|
|
790
|
+
if (!key.startsWith('m_') || value == null || value === '')
|
|
791
|
+
continue;
|
|
792
|
+
const parts = key.slice(2).split('_');
|
|
793
|
+
if (parts.length < 2)
|
|
794
|
+
continue;
|
|
795
|
+
let column;
|
|
796
|
+
let operator;
|
|
797
|
+
if (parts.length === 2) {
|
|
798
|
+
// 无别名 → 默认主表别名 t(对齐 Java,白名单列均带表别名)
|
|
799
|
+
operator = parts[0];
|
|
800
|
+
column = 't.' + toUnderscore(parts[1]);
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
operator = parts[1];
|
|
804
|
+
column = parts[0] + '.' + toUnderscore(parts[2]);
|
|
805
|
+
}
|
|
806
|
+
out.push({ column, operator: operator.toUpperCase(), value });
|
|
807
|
+
}
|
|
808
|
+
return out;
|
|
809
|
+
}
|
|
810
|
+
function toUnderscore(camel) {
|
|
811
|
+
return camel.replace(/[A-Z]/g, c => '_' + c.toLowerCase());
|
|
812
|
+
}
|
|
563
813
|
function toStr(v) {
|
|
564
814
|
if (v == null)
|
|
565
815
|
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);
|
package/dist/jdbc/shared.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ProcessInstance, ProcessTask, type ProcessDefine, type CcInstanceRow } from '../model.js';
|
|
2
|
-
import type { IDGenerator, ProcessRepository } from '../spi.js';
|
|
1
|
+
import { ProcessInstance, ProcessTask, type ProcessDefine, type CcInstanceRow, type DefineRow, type InstanceRow, type TaskRow } from '../model.js';
|
|
2
|
+
import type { IDGenerator, ProcessRepository, QueryCondition } from '../spi.js';
|
|
3
3
|
/** 默认 ID 生成器:时间戳毫秒 + 同毫秒递增序号(对齐 Java nextId 默认实现) */
|
|
4
4
|
export declare class TsIDGenerator implements IDGenerator {
|
|
5
5
|
private last;
|
|
@@ -63,7 +63,30 @@ export declare class JdbcRepository implements ProcessRepository {
|
|
|
63
63
|
removeTaskActor(taskId: number, actors: string[]): Promise<void>;
|
|
64
64
|
createCcInstance(instanceId: number, creator: string, ...actorIds: string[]): Promise<void>;
|
|
65
65
|
updateCcStatus(instanceId: number, actorId: string): Promise<void>;
|
|
66
|
-
|
|
66
|
+
pageDefines(pageNum: number, pageSize: number, conditions?: QueryCondition[]): Promise<{
|
|
67
|
+
rows: DefineRow[];
|
|
68
|
+
total: number;
|
|
69
|
+
}>;
|
|
70
|
+
pageInstances(pageNum: number, pageSize: number, operator: string, conditions?: QueryCondition[]): Promise<{
|
|
71
|
+
rows: InstanceRow[];
|
|
72
|
+
total: number;
|
|
73
|
+
}>;
|
|
74
|
+
pageTodoTasks(pageNum: number, pageSize: number, actorId: string, conditions?: QueryCondition[]): Promise<{
|
|
75
|
+
rows: TaskRow[];
|
|
76
|
+
total: number;
|
|
77
|
+
}>;
|
|
78
|
+
pageDoneTasks(pageNum: number, pageSize: number, operator: string, conditions?: QueryCondition[]): Promise<{
|
|
79
|
+
rows: TaskRow[];
|
|
80
|
+
total: number;
|
|
81
|
+
}>;
|
|
82
|
+
private pageTasks;
|
|
83
|
+
private mapInstanceRow;
|
|
84
|
+
protected buildWhere(conditions: QueryCondition[], whitelist: Set<string>): {
|
|
85
|
+
sql: string;
|
|
86
|
+
params: any[];
|
|
87
|
+
};
|
|
88
|
+
private mapTaskRow;
|
|
89
|
+
pageCcInstances(pageNum: number, pageSize: number, actorId: string, conditions?: QueryCondition[]): Promise<{
|
|
67
90
|
rows: CcInstanceRow[];
|
|
68
91
|
total: number;
|
|
69
92
|
}>;
|
package/dist/jdbc/shared.js
CHANGED
|
@@ -11,6 +11,30 @@
|
|
|
11
11
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
12
12
|
import { ProcessInstance, ProcessTask } from '../model.js';
|
|
13
13
|
import { TaskState } from '../model.js';
|
|
14
|
+
// ═══ 列白名单(issues/05-5,与 mldong-boot2 别名一致) ═══
|
|
15
|
+
const TASK_WHITELIST = new Set([
|
|
16
|
+
't.id', 't.task_name', 't.display_name', 't.task_type', 't.perform_type', 't.task_state',
|
|
17
|
+
't.operator', 't.form_key', 't.create_time', 't.finish_time', 't.expire_time',
|
|
18
|
+
't.process_instance_id', 't.task_parent_id', 't.variable',
|
|
19
|
+
'pi.id', 'pi.business_no', 'pi.operator', 'pi.create_time', 'pi.state',
|
|
20
|
+
'pd.name', 'pd.display_name', 'pd.type',
|
|
21
|
+
'pta.actor_id', 'pta.process_task_id',
|
|
22
|
+
]);
|
|
23
|
+
const INSTANCE_WHITELIST = new Set([
|
|
24
|
+
't.id', 't.parent_id', 't.process_define_id', 't.state', 't.business_no',
|
|
25
|
+
't.operator', 't.create_time', 't.expire_time', 't.variable',
|
|
26
|
+
'pd.name', 'pd.display_name', 'pd.type', 'pd.version',
|
|
27
|
+
]);
|
|
28
|
+
const CC_WHITELIST = new Set([
|
|
29
|
+
't.id', 't.process_define_id', 't.state', 't.business_no', 't.operator',
|
|
30
|
+
't.create_time', 't.variable',
|
|
31
|
+
'pd.name', 'pd.display_name', 'pd.type', 'pd.version',
|
|
32
|
+
'cc.actor_id', 'cc.state',
|
|
33
|
+
]);
|
|
34
|
+
const DEFINE_WHITELIST = new Set([
|
|
35
|
+
't.id', 't.name', 't.display_name', 't.type', 't.state', 't.version',
|
|
36
|
+
't.create_time', 't.update_time',
|
|
37
|
+
]);
|
|
14
38
|
// 当前异步上下文绑定的事务连接
|
|
15
39
|
const txStore = new AsyncLocalStorage();
|
|
16
40
|
/** 默认 ID 生成器:时间戳毫秒 + 同毫秒递增序号(对齐 Java nextId 默认实现) */
|
|
@@ -399,20 +423,193 @@ export class JdbcRepository {
|
|
|
399
423
|
await this.done(conn);
|
|
400
424
|
}
|
|
401
425
|
}
|
|
426
|
+
// ── 核心表分页(v1.5.0,对齐 Java pageDefines/pageInstances/pageTodoTasks/pageDoneTasks)──
|
|
427
|
+
async pageDefines(pageNum, pageSize, conditions) {
|
|
428
|
+
const cond = this.buildWhere(conditions ?? [], DEFINE_WHITELIST);
|
|
429
|
+
const where = ' FROM wf_process_define t WHERE 1=1' + cond.sql;
|
|
430
|
+
const conn = await this.c();
|
|
431
|
+
try {
|
|
432
|
+
const countRow = await conn.fetchOne(this.sql('SELECT COUNT(*) ' + where), cond.params);
|
|
433
|
+
const total = Number(Object.values(countRow)[0] ?? 0);
|
|
434
|
+
const rows = await conn.fetchAll(this.sql('SELECT id, name, display_name, type, state, version, create_time, create_user, update_time, update_user' +
|
|
435
|
+
where + ' ORDER BY t.id DESC LIMIT ? OFFSET ?'), [...cond.params, pageSize, (pageNum - 1) * pageSize]);
|
|
436
|
+
return {
|
|
437
|
+
rows: rows.map(r => ({
|
|
438
|
+
id: Number(r.id), name: r.name, displayName: r.display_name, type: r.type,
|
|
439
|
+
state: Number(r.state), version: Number(r.version),
|
|
440
|
+
createTime: r.create_time, createUser: r.create_user ?? '',
|
|
441
|
+
updateTime: r.update_time, updateUser: r.update_user ?? '',
|
|
442
|
+
})),
|
|
443
|
+
total,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
finally {
|
|
447
|
+
await this.done(conn);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
async pageInstances(pageNum, pageSize, operator, conditions) {
|
|
451
|
+
const cond = this.buildWhere(conditions ?? [], INSTANCE_WHITELIST);
|
|
452
|
+
const where = ' FROM wf_process_instance t' +
|
|
453
|
+
' LEFT JOIN wf_process_define pd ON t.process_define_id = pd.id' +
|
|
454
|
+
' WHERE t.operator = ?' + cond.sql;
|
|
455
|
+
const conn = await this.c();
|
|
456
|
+
try {
|
|
457
|
+
const countRow = await conn.fetchOne(this.sql('SELECT COUNT(*) ' + where), [operator, ...cond.params]);
|
|
458
|
+
const total = Number(Object.values(countRow)[0] ?? 0);
|
|
459
|
+
const cols = 't.id, t.parent_id, t.process_define_id, t.state, t.parent_node_name, t.business_no,' +
|
|
460
|
+
' t.operator, t.expire_time, t.variable, t.create_time, t.create_user, t.update_time, t.update_user,' +
|
|
461
|
+
' pd.name, pd.display_name, pd.version';
|
|
462
|
+
const rows = await conn.fetchAll(this.sql(`SELECT ${cols}${where} ORDER BY t.id DESC LIMIT ? OFFSET ?`), [operator, ...cond.params, pageSize, (pageNum - 1) * pageSize]);
|
|
463
|
+
return { rows: rows.map(r => this.mapInstanceRow(r)), total };
|
|
464
|
+
}
|
|
465
|
+
finally {
|
|
466
|
+
await this.done(conn);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
async pageTodoTasks(pageNum, pageSize, actorId, conditions) {
|
|
470
|
+
return this.pageTasks(pageNum, pageSize, false, actorId, conditions);
|
|
471
|
+
}
|
|
472
|
+
async pageDoneTasks(pageNum, pageSize, operator, conditions) {
|
|
473
|
+
return this.pageTasks(pageNum, pageSize, true, operator, conditions);
|
|
474
|
+
}
|
|
475
|
+
async pageTasks(pageNum, pageSize, done, filter, conditions) {
|
|
476
|
+
const cond = this.buildWhere(conditions ?? [], TASK_WHITELIST);
|
|
477
|
+
const where = ' FROM wf_process_task t' +
|
|
478
|
+
' LEFT JOIN wf_process_instance pi ON t.process_instance_id = pi.id' +
|
|
479
|
+
' LEFT JOIN wf_process_define pd ON pi.process_define_id = pd.id' +
|
|
480
|
+
' LEFT JOIN wf_process_task_actor pta ON t.id = pta.process_task_id' +
|
|
481
|
+
(done ? ' WHERE t.task_state <> 10 AND t.operator = ?' : ' WHERE t.task_state = 10 AND pta.actor_id = ?') + cond.sql;
|
|
482
|
+
const conn = await this.c();
|
|
483
|
+
try {
|
|
484
|
+
const countRow = await conn.fetchOne(this.sql('SELECT COUNT(DISTINCT t.id) ' + where), [filter, ...cond.params]);
|
|
485
|
+
const total = Number(Object.values(countRow)[0] ?? 0);
|
|
486
|
+
const cols = 'DISTINCT t.id, t.process_instance_id, t.task_name, t.display_name, t.task_type, t.perform_type,' +
|
|
487
|
+
' t.task_state, t.operator, t.finish_time, t.expire_time, t.form_key, t.task_parent_id, t.variable,' +
|
|
488
|
+
' t.create_time, t.create_user, t.update_time, t.update_user,' +
|
|
489
|
+
' pd.name, pd.display_name, pd.version AS process_define_version,' +
|
|
490
|
+
' pi.variable AS instance_variable, pi.create_time AS instance_create_time';
|
|
491
|
+
const rows = await conn.fetchAll(this.sql(`SELECT ${cols}${where} ORDER BY t.id DESC LIMIT ? OFFSET ?`), [filter, ...cond.params, pageSize, (pageNum - 1) * pageSize]);
|
|
492
|
+
return { rows: rows.map(r => this.mapTaskRow(r)), total };
|
|
493
|
+
}
|
|
494
|
+
finally {
|
|
495
|
+
await this.done(conn);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
mapInstanceRow(r) {
|
|
499
|
+
let variables = {};
|
|
500
|
+
if (r.variable) {
|
|
501
|
+
try {
|
|
502
|
+
variables = JSON.parse(r.variable);
|
|
503
|
+
}
|
|
504
|
+
catch { /* 忽略坏 JSON */ }
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
id: Number(r.id), parentId: r.parent_id != null ? Number(r.parent_id) : undefined,
|
|
508
|
+
defineId: Number(r.process_define_id), state: r.state,
|
|
509
|
+
parentNodeName: r.parent_node_name ?? '', businessNo: r.business_no ?? '', operator: r.operator ?? '',
|
|
510
|
+
expireTime: r.expire_time ?? undefined, variables,
|
|
511
|
+
createTime: r.create_time, createUser: r.create_user ?? '',
|
|
512
|
+
updateTime: r.update_time, updateUser: r.update_user ?? '',
|
|
513
|
+
defineName: r.name ?? '', defineDisplayName: r.display_name ?? '',
|
|
514
|
+
defineVersion: Number(r.version ?? 0),
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
// ═══ m_ 条件 WHERE 构建(issues/05-5,白名单 + 参数化,对齐 Java buildWhere) ═══
|
|
518
|
+
buildWhere(conditions, whitelist) {
|
|
519
|
+
let sql = '';
|
|
520
|
+
const params = [];
|
|
521
|
+
for (const c of conditions) {
|
|
522
|
+
if (!whitelist.has(c.column))
|
|
523
|
+
continue; // 不在白名单,丢弃
|
|
524
|
+
const val = c.value;
|
|
525
|
+
if (val == null || val === '')
|
|
526
|
+
continue;
|
|
527
|
+
switch (c.operator.toUpperCase()) {
|
|
528
|
+
case 'EQ':
|
|
529
|
+
sql += ` AND ${c.column} = ?`;
|
|
530
|
+
params.push(val);
|
|
531
|
+
break;
|
|
532
|
+
case 'NE':
|
|
533
|
+
sql += ` AND ${c.column} <> ?`;
|
|
534
|
+
params.push(val);
|
|
535
|
+
break;
|
|
536
|
+
case 'LIKE':
|
|
537
|
+
sql += ` AND ${c.column} LIKE ?`;
|
|
538
|
+
params.push(`%${val}%`);
|
|
539
|
+
break;
|
|
540
|
+
case 'LLIKE':
|
|
541
|
+
sql += ` AND ${c.column} LIKE ?`;
|
|
542
|
+
params.push(`%${val}`);
|
|
543
|
+
break;
|
|
544
|
+
case 'RLIKE':
|
|
545
|
+
sql += ` AND ${c.column} LIKE ?`;
|
|
546
|
+
params.push(`${val}%`);
|
|
547
|
+
break;
|
|
548
|
+
case 'GT':
|
|
549
|
+
sql += ` AND ${c.column} > ?`;
|
|
550
|
+
params.push(val);
|
|
551
|
+
break;
|
|
552
|
+
case 'GE':
|
|
553
|
+
sql += ` AND ${c.column} >= ?`;
|
|
554
|
+
params.push(val);
|
|
555
|
+
break;
|
|
556
|
+
case 'LT':
|
|
557
|
+
sql += ` AND ${c.column} < ?`;
|
|
558
|
+
params.push(val);
|
|
559
|
+
break;
|
|
560
|
+
case 'LE':
|
|
561
|
+
sql += ` AND ${c.column} <= ?`;
|
|
562
|
+
params.push(val);
|
|
563
|
+
break;
|
|
564
|
+
case 'IN':
|
|
565
|
+
case 'NIN': {
|
|
566
|
+
if (Array.isArray(val) && val.length > 0) {
|
|
567
|
+
const marks = val.map(() => '?').join(',');
|
|
568
|
+
sql += ` AND ${c.column} ${c.operator.toUpperCase() === 'IN' ? 'IN' : 'NOT IN'} (${marks})`;
|
|
569
|
+
params.push(...val);
|
|
570
|
+
}
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return { sql, params };
|
|
576
|
+
}
|
|
577
|
+
mapTaskRow(r) {
|
|
578
|
+
let variables = {};
|
|
579
|
+
if (r.variable) {
|
|
580
|
+
try {
|
|
581
|
+
variables = JSON.parse(r.variable);
|
|
582
|
+
}
|
|
583
|
+
catch { /* 忽略坏 JSON */ }
|
|
584
|
+
}
|
|
585
|
+
return {
|
|
586
|
+
id: Number(r.id), processInstanceId: Number(r.process_instance_id), taskName: r.task_name,
|
|
587
|
+
displayName: r.display_name, taskType: Number(r.task_type), performType: Number(r.perform_type),
|
|
588
|
+
taskState: r.task_state, operator: r.operator ?? '', finishTime: r.finish_time ?? undefined,
|
|
589
|
+
expireTime: r.expire_time ?? undefined, formKey: r.form_key ?? '',
|
|
590
|
+
taskParentId: r.task_parent_id != null ? Number(r.task_parent_id) : undefined,
|
|
591
|
+
variables, createTime: r.create_time, createUser: r.create_user ?? '',
|
|
592
|
+
updateTime: r.update_time, updateUser: r.update_user ?? '',
|
|
593
|
+
processDefineName: r.name ?? '', processDefineDisplayName: r.display_name ?? '',
|
|
594
|
+
defineVersion: Number(r.process_define_version ?? 0),
|
|
595
|
+
instanceVariable: r.instance_variable ?? '', instanceCreateTime: r.instance_create_time,
|
|
596
|
+
};
|
|
597
|
+
}
|
|
402
598
|
// pageCcInstances 我的抄送分页(v1.3.0):cc 表 join 实例 + 定义,按抄送人过滤(对齐 Java pageCcInstances)
|
|
403
|
-
async pageCcInstances(pageNum, pageSize, actorId) {
|
|
599
|
+
async pageCcInstances(pageNum, pageSize, actorId, conditions) {
|
|
600
|
+
const cond = this.buildWhere(conditions ?? [], CC_WHITELIST);
|
|
404
601
|
const where = ' FROM wf_process_instance t' +
|
|
405
602
|
' LEFT JOIN wf_process_define pd ON t.process_define_id = pd.id' +
|
|
406
603
|
' LEFT JOIN wf_process_cc_instance cc ON t.id = cc.process_instance_id' +
|
|
407
|
-
' WHERE cc.actor_id = ?';
|
|
604
|
+
' WHERE cc.actor_id = ?' + cond.sql;
|
|
408
605
|
const cols = 't.id, t.parent_id, t.process_define_id, t.state, t.parent_node_name, t.business_no,' +
|
|
409
606
|
' t.operator, t.expire_time, t.variable, t.create_time, t.create_user, t.update_time, t.update_user,' +
|
|
410
607
|
' pd.name, pd.display_name, pd.version';
|
|
411
608
|
const conn = await this.c();
|
|
412
609
|
try {
|
|
413
|
-
const countRow = await conn.fetchOne(this.sql('SELECT COUNT(*) ' + where), [actorId]);
|
|
610
|
+
const countRow = await conn.fetchOne(this.sql('SELECT COUNT(*) ' + where), [actorId, ...cond.params]);
|
|
414
611
|
const total = Number(Object.values(countRow)[0] ?? 0);
|
|
415
|
-
const rows = await conn.fetchAll(this.sql(`SELECT ${cols}${where} ORDER BY t.id ASC LIMIT ? OFFSET ?`), [actorId, pageSize, (pageNum - 1) * pageSize]);
|
|
612
|
+
const rows = await conn.fetchAll(this.sql(`SELECT ${cols}${where} ORDER BY t.id ASC LIMIT ? OFFSET ?`), [actorId, ...cond.params, pageSize, (pageNum - 1) * pageSize]);
|
|
416
613
|
return {
|
|
417
614
|
rows: rows.map(r => this.mapCcRow(r)),
|
|
418
615
|
total,
|
package/dist/memory-ext.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProcessDesign, ProcessDesignHis, ProcessSurrogate } from './model.js';
|
|
2
|
-
import type { ProcessExtRepository } from './spi.js';
|
|
2
|
+
import type { ProcessExtRepository, QueryCondition } from './spi.js';
|
|
3
3
|
export declare class MemoryExtRepository implements ProcessExtRepository {
|
|
4
4
|
private designs;
|
|
5
5
|
private designHis;
|
|
@@ -9,13 +9,13 @@ export declare class MemoryExtRepository implements ProcessExtRepository {
|
|
|
9
9
|
saveDesign(d: ProcessDesign): Promise<void>;
|
|
10
10
|
updateDesign(d: ProcessDesign): Promise<void>;
|
|
11
11
|
removeDesign(id: number): Promise<void>;
|
|
12
|
-
pageDesigns(_pageNum?: number, _pageSize?: number, _filters?: Record<string, any
|
|
12
|
+
pageDesigns(_pageNum?: number, _pageSize?: number, _filters?: Record<string, any>, conditions?: QueryCondition[]): Promise<[ProcessDesign[], number]>;
|
|
13
13
|
saveDesignHis(his: ProcessDesignHis): Promise<void>;
|
|
14
14
|
listDesignHis(designId: number): Promise<ProcessDesignHis[]>;
|
|
15
15
|
findSurrogateById(id: number): Promise<ProcessSurrogate | null>;
|
|
16
16
|
saveSurrogate(s: ProcessSurrogate): Promise<void>;
|
|
17
17
|
updateSurrogate(s: ProcessSurrogate): Promise<void>;
|
|
18
18
|
removeSurrogate(id: number): Promise<void>;
|
|
19
|
-
pageSurrogates(_pageNum?: number, _pageSize?: number,
|
|
19
|
+
pageSurrogates(_pageNum?: number, _pageSize?: number, filters?: Record<string, any>, conditions?: QueryCondition[]): Promise<[ProcessSurrogate[], number]>;
|
|
20
20
|
getSurrogate(operator: string, processName: string, at?: Date): Promise<ProcessSurrogate | null>;
|
|
21
21
|
}
|
package/dist/memory-ext.js
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
// 扩展仓储内存实现(v1.1.0,测试/演示用)
|
|
2
|
+
import { matchConditions } from './memory.js';
|
|
3
|
+
// ═══ 条件匹配基建(issues/05-5) ═══
|
|
4
|
+
const DESIGN_FIELDS = {
|
|
5
|
+
't.id': 'id', 't.name': 'name', 't.display_name': 'displayName', 't.type': 'type',
|
|
6
|
+
't.is_deployed': 'isDeployed', 't.remark': 'remark',
|
|
7
|
+
't.create_time': 'createTime', 't.update_time': 'updateTime',
|
|
8
|
+
};
|
|
9
|
+
const SURROGATE_FIELDS = {
|
|
10
|
+
't.id': 'id', 't.process_name': 'processName', 't.operator': 'operator',
|
|
11
|
+
't.surrogate': 'surrogate', 't.enabled': 'enabled',
|
|
12
|
+
't.start_time': 'startTime', 't.end_time': 'endTime',
|
|
13
|
+
't.create_time': 'createTime', 't.update_time': 'updateTime',
|
|
14
|
+
};
|
|
15
|
+
function pickFields(row, map) {
|
|
16
|
+
const fields = {};
|
|
17
|
+
for (const [col, key] of Object.entries(map)) {
|
|
18
|
+
fields[col] = row[key];
|
|
19
|
+
}
|
|
20
|
+
return fields;
|
|
21
|
+
}
|
|
2
22
|
export class MemoryExtRepository {
|
|
3
23
|
designs = new Map();
|
|
4
24
|
designHis = new Map();
|
|
@@ -24,8 +44,9 @@ export class MemoryExtRepository {
|
|
|
24
44
|
this.designs.delete(id);
|
|
25
45
|
this.designHis.delete(id);
|
|
26
46
|
}
|
|
27
|
-
async pageDesigns(_pageNum = 1, _pageSize = 10, _filters) {
|
|
28
|
-
|
|
47
|
+
async pageDesigns(_pageNum = 1, _pageSize = 10, _filters, conditions) {
|
|
48
|
+
const rows = [...this.designs.values()].filter(d => matchConditions(conditions, pickFields(d, DESIGN_FIELDS)));
|
|
49
|
+
return [rows, rows.length];
|
|
29
50
|
}
|
|
30
51
|
// ── 设计历史 ──
|
|
31
52
|
async saveDesignHis(his) {
|
|
@@ -61,8 +82,18 @@ export class MemoryExtRepository {
|
|
|
61
82
|
async removeSurrogate(id) {
|
|
62
83
|
this.surrogates.delete(id);
|
|
63
84
|
}
|
|
64
|
-
async pageSurrogates(_pageNum = 1, _pageSize = 10,
|
|
65
|
-
|
|
85
|
+
async pageSurrogates(_pageNum = 1, _pageSize = 10, filters, conditions) {
|
|
86
|
+
const rows = [...this.surrogates.values()].filter(s => {
|
|
87
|
+
for (const [col, val] of Object.entries(filters ?? {})) {
|
|
88
|
+
if (val == null || val === '')
|
|
89
|
+
continue;
|
|
90
|
+
const k = col === 'process_name' ? 'processName' : col;
|
|
91
|
+
if (String(s[k]) !== String(val))
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return matchConditions(conditions, pickFields(s, SURROGATE_FIELDS));
|
|
95
|
+
});
|
|
96
|
+
return [rows, rows.length];
|
|
66
97
|
}
|
|
67
98
|
async getSurrogate(operator, processName, at = new Date()) {
|
|
68
99
|
let fallback = null;
|
package/dist/memory.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import type { CcInstanceRow, ProcessDefine } from './model.js';
|
|
1
|
+
import type { CcInstanceRow, DefineRow, InstanceRow, TaskRow, ProcessDefine } from './model.js';
|
|
2
2
|
import { type ProcessInstance, type ProcessTask } from './model.js';
|
|
3
|
-
import type { ProcessRepository } from './spi.js';
|
|
3
|
+
import type { ProcessRepository, QueryCondition } from './spi.js';
|
|
4
|
+
/** 条件全匹配(操作符对齐 JDBC buildWhere;列不在字段中则跳过) */
|
|
5
|
+
export declare function matchConditions(conditions: QueryCondition[] | undefined, fields: Record<string, any>): boolean;
|
|
6
|
+
/** EQ:值或集合包含判断(pta.actor_id/cc.actor_id 为数组) */
|
|
7
|
+
export declare function eqValue(v: any, expect: any): boolean;
|
|
4
8
|
export declare class MemoryRepository implements ProcessRepository {
|
|
5
9
|
private defines;
|
|
6
10
|
private instances;
|
|
@@ -29,7 +33,24 @@ export declare class MemoryRepository implements ProcessRepository {
|
|
|
29
33
|
removeTaskActor(taskId: number, actors: string[]): Promise<void>;
|
|
30
34
|
createCcInstance(instanceId: number, _creator: string, ...actorIds: string[]): Promise<void>;
|
|
31
35
|
updateCcStatus(_instanceId: number, _actorId: string): Promise<void>;
|
|
32
|
-
|
|
36
|
+
pageDefines(pageNum?: number, pageSize?: number, conditions?: QueryCondition[]): Promise<{
|
|
37
|
+
rows: DefineRow[];
|
|
38
|
+
total: number;
|
|
39
|
+
}>;
|
|
40
|
+
pageInstances(pageNum: number | undefined, pageSize: number | undefined, operator: string, conditions?: QueryCondition[]): Promise<{
|
|
41
|
+
rows: InstanceRow[];
|
|
42
|
+
total: number;
|
|
43
|
+
}>;
|
|
44
|
+
pageTodoTasks(pageNum: number | undefined, pageSize: number | undefined, actorId: string, conditions?: QueryCondition[]): Promise<{
|
|
45
|
+
rows: TaskRow[];
|
|
46
|
+
total: number;
|
|
47
|
+
}>;
|
|
48
|
+
pageDoneTasks(pageNum: number | undefined, pageSize: number | undefined, operator: string, conditions?: QueryCondition[]): Promise<{
|
|
49
|
+
rows: TaskRow[];
|
|
50
|
+
total: number;
|
|
51
|
+
}>;
|
|
52
|
+
private taskRow;
|
|
53
|
+
pageCcInstances(pageNum: number | undefined, pageSize: number | undefined, actorId: string, conditions?: QueryCondition[]): Promise<{
|
|
33
54
|
rows: CcInstanceRow[];
|
|
34
55
|
total: number;
|
|
35
56
|
}>;
|
package/dist/memory.js
CHANGED
|
@@ -1,4 +1,97 @@
|
|
|
1
|
+
import { TaskState } from './model.js';
|
|
1
2
|
import { cloneInstance, cloneTask } from './model.js';
|
|
3
|
+
// ═══ 条件匹配基建(issues/05-5,对齐 JDBC 白名单语义) ═══
|
|
4
|
+
// 行字段映射(列名 → 行属性,白名单列均可匹配)
|
|
5
|
+
const TASK_FIELDS = {
|
|
6
|
+
't.id': 'id', 't.task_name': 'taskName', 't.display_name': 'displayName',
|
|
7
|
+
't.task_type': 'taskType', 't.perform_type': 'performType', 't.task_state': 'taskState',
|
|
8
|
+
't.operator': 'operator', 't.form_key': 'formKey', 't.create_time': 'createTime',
|
|
9
|
+
't.finish_time': 'finishTime', 't.expire_time': 'expireTime',
|
|
10
|
+
't.process_instance_id': 'processInstanceId', 't.task_parent_id': 'taskParentId',
|
|
11
|
+
'pd.name': 'processDefineName', 'pd.display_name': 'processDefineDisplayName',
|
|
12
|
+
'pd.version': 'defineVersion',
|
|
13
|
+
};
|
|
14
|
+
const INSTANCE_FIELDS = {
|
|
15
|
+
't.id': 'id', 't.parent_id': 'parentId', 't.process_define_id': 'defineId',
|
|
16
|
+
't.state': 'state', 't.parent_node_name': 'parentNodeName', 't.business_no': 'businessNo',
|
|
17
|
+
't.operator': 'operator', 't.expire_time': 'expireTime', 't.create_time': 'createTime',
|
|
18
|
+
'pd.name': 'defineName', 'pd.display_name': 'defineDisplayName', 'pd.version': 'defineVersion',
|
|
19
|
+
};
|
|
20
|
+
const DEFINE_FIELDS = {
|
|
21
|
+
't.id': 'id', 't.name': 'name', 't.display_name': 'displayName', 't.type': 'type',
|
|
22
|
+
't.state': 'state', 't.version': 'version', 't.create_time': 'createTime',
|
|
23
|
+
't.update_time': 'updateTime',
|
|
24
|
+
};
|
|
25
|
+
/** 行字段提取(列名 → 值) */
|
|
26
|
+
function pickFields(row, map) {
|
|
27
|
+
const fields = {};
|
|
28
|
+
for (const [col, key] of Object.entries(map)) {
|
|
29
|
+
fields[col] = row[key];
|
|
30
|
+
}
|
|
31
|
+
return fields;
|
|
32
|
+
}
|
|
33
|
+
/** 条件全匹配(操作符对齐 JDBC buildWhere;列不在字段中则跳过) */
|
|
34
|
+
export function matchConditions(conditions, fields) {
|
|
35
|
+
for (const c of conditions ?? []) {
|
|
36
|
+
const v = fields[c.column];
|
|
37
|
+
const expect = c.value;
|
|
38
|
+
if (v == null || expect == null)
|
|
39
|
+
continue;
|
|
40
|
+
switch (c.operator.toUpperCase()) {
|
|
41
|
+
case 'EQ':
|
|
42
|
+
if (!eqValue(v, expect))
|
|
43
|
+
return false;
|
|
44
|
+
break;
|
|
45
|
+
case 'NE':
|
|
46
|
+
if (eqValue(v, expect))
|
|
47
|
+
return false;
|
|
48
|
+
break;
|
|
49
|
+
case 'LIKE':
|
|
50
|
+
if (!String(v).includes(String(expect)))
|
|
51
|
+
return false;
|
|
52
|
+
break;
|
|
53
|
+
case 'LLIKE':
|
|
54
|
+
if (!String(v).endsWith(String(expect)))
|
|
55
|
+
return false;
|
|
56
|
+
break;
|
|
57
|
+
case 'RLIKE':
|
|
58
|
+
if (!String(v).startsWith(String(expect)))
|
|
59
|
+
return false;
|
|
60
|
+
break;
|
|
61
|
+
case 'GT':
|
|
62
|
+
if (Number(v) <= Number(expect))
|
|
63
|
+
return false;
|
|
64
|
+
break;
|
|
65
|
+
case 'GE':
|
|
66
|
+
if (Number(v) < Number(expect))
|
|
67
|
+
return false;
|
|
68
|
+
break;
|
|
69
|
+
case 'LT':
|
|
70
|
+
if (Number(v) >= Number(expect))
|
|
71
|
+
return false;
|
|
72
|
+
break;
|
|
73
|
+
case 'LE':
|
|
74
|
+
if (Number(v) > Number(expect))
|
|
75
|
+
return false;
|
|
76
|
+
break;
|
|
77
|
+
case 'IN':
|
|
78
|
+
if (!Array.isArray(expect) || !expect.includes(v))
|
|
79
|
+
return false;
|
|
80
|
+
break;
|
|
81
|
+
case 'NIN':
|
|
82
|
+
if (Array.isArray(expect) && expect.includes(v))
|
|
83
|
+
return false;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
/** EQ:值或集合包含判断(pta.actor_id/cc.actor_id 为数组) */
|
|
90
|
+
export function eqValue(v, expect) {
|
|
91
|
+
if (Array.isArray(v))
|
|
92
|
+
return v.includes(expect);
|
|
93
|
+
return String(v) === String(expect);
|
|
94
|
+
}
|
|
2
95
|
export class MemoryRepository {
|
|
3
96
|
defines = new Map();
|
|
4
97
|
instances = new Map();
|
|
@@ -162,8 +255,90 @@ export class MemoryRepository {
|
|
|
162
255
|
this.ccInstances.set(instanceId, existing);
|
|
163
256
|
}
|
|
164
257
|
async updateCcStatus(_instanceId, _actorId) { }
|
|
258
|
+
// ── 核心表分页(v1.5.0)──
|
|
259
|
+
async pageDefines(pageNum = 1, pageSize = 10, conditions) {
|
|
260
|
+
const rows = [...this.defines.values()].map(d => ({
|
|
261
|
+
id: d.id, name: d.name, displayName: d.displayName, type: d.type,
|
|
262
|
+
state: d.state, version: d.version,
|
|
263
|
+
createTime: d.createTime, createUser: d.createUser,
|
|
264
|
+
updateTime: d.updateTime, updateUser: d.updateUser,
|
|
265
|
+
})).filter(r => matchConditions(conditions, pickFields(r, DEFINE_FIELDS)));
|
|
266
|
+
const total = rows.length;
|
|
267
|
+
const start = (pageNum - 1) * pageSize;
|
|
268
|
+
return { rows: rows.slice(start, start + pageSize), total };
|
|
269
|
+
}
|
|
270
|
+
async pageInstances(pageNum = 1, pageSize = 10, operator, conditions) {
|
|
271
|
+
const rows = [];
|
|
272
|
+
for (const inst of this.instances.values()) {
|
|
273
|
+
if (operator && inst.operator !== operator)
|
|
274
|
+
continue;
|
|
275
|
+
const def = this.defines.get(inst.defineId);
|
|
276
|
+
const r = {
|
|
277
|
+
id: inst.id, parentId: inst.parentId, defineId: inst.defineId, state: inst.state,
|
|
278
|
+
parentNodeName: inst.parentNodeName, businessNo: inst.businessNo, operator: inst.operator,
|
|
279
|
+
expireTime: inst.expireTime, variables: { ...inst.variables },
|
|
280
|
+
createTime: inst.createTime, createUser: inst.createUser,
|
|
281
|
+
updateTime: inst.updateTime, updateUser: inst.updateUser,
|
|
282
|
+
defineName: def?.name ?? '', defineDisplayName: def?.displayName ?? '',
|
|
283
|
+
defineVersion: def?.version ?? 0,
|
|
284
|
+
};
|
|
285
|
+
if (matchConditions(conditions, pickFields(r, INSTANCE_FIELDS)))
|
|
286
|
+
rows.push(r);
|
|
287
|
+
}
|
|
288
|
+
const total = rows.length;
|
|
289
|
+
const start = (pageNum - 1) * pageSize;
|
|
290
|
+
return { rows: rows.slice(start, start + pageSize), total };
|
|
291
|
+
}
|
|
292
|
+
async pageTodoTasks(pageNum = 1, pageSize = 10, actorId, conditions) {
|
|
293
|
+
const rows = [];
|
|
294
|
+
for (const t of this.tasks.values()) {
|
|
295
|
+
if (t.taskState !== TaskState.Doing)
|
|
296
|
+
continue;
|
|
297
|
+
if (actorId && !(this.actors.get(t.id) ?? []).includes(actorId))
|
|
298
|
+
continue;
|
|
299
|
+
const r = this.taskRow(t);
|
|
300
|
+
const fields = pickFields(r, TASK_FIELDS);
|
|
301
|
+
fields['pta.actor_id'] = this.actors.get(t.id) ?? [];
|
|
302
|
+
if (matchConditions(conditions, fields))
|
|
303
|
+
rows.push(r);
|
|
304
|
+
}
|
|
305
|
+
const total = rows.length;
|
|
306
|
+
const start = (pageNum - 1) * pageSize;
|
|
307
|
+
return { rows: rows.slice(start, start + pageSize), total };
|
|
308
|
+
}
|
|
309
|
+
async pageDoneTasks(pageNum = 1, pageSize = 10, operator, conditions) {
|
|
310
|
+
const rows = [];
|
|
311
|
+
for (const t of this.tasks.values()) {
|
|
312
|
+
if (t.taskState === TaskState.Doing)
|
|
313
|
+
continue;
|
|
314
|
+
if (operator && t.actorId !== operator)
|
|
315
|
+
continue;
|
|
316
|
+
const r = this.taskRow(t);
|
|
317
|
+
if (matchConditions(conditions, pickFields(r, TASK_FIELDS)))
|
|
318
|
+
rows.push(r);
|
|
319
|
+
}
|
|
320
|
+
const total = rows.length;
|
|
321
|
+
const start = (pageNum - 1) * pageSize;
|
|
322
|
+
return { rows: rows.slice(start, start + pageSize), total };
|
|
323
|
+
}
|
|
324
|
+
taskRow(t) {
|
|
325
|
+
const inst = this.instances.get(t.processInstanceId);
|
|
326
|
+
const def = inst ? this.defines.get(inst.defineId) : undefined;
|
|
327
|
+
return {
|
|
328
|
+
id: t.id, processInstanceId: t.processInstanceId, taskName: t.taskName,
|
|
329
|
+
displayName: t.displayName, taskType: t.taskType, performType: t.performType,
|
|
330
|
+
taskState: t.taskState, operator: t.actorId ?? '', finishTime: t.finishTime,
|
|
331
|
+
expireTime: t.expireTime, formKey: t.formKey ?? '', taskParentId: t.parentTaskId,
|
|
332
|
+
variables: { ...t.variables }, createTime: t.createTime, createUser: t.createUser,
|
|
333
|
+
updateTime: t.updateTime, updateUser: t.updateUser,
|
|
334
|
+
processDefineName: def?.name ?? '', processDefineDisplayName: def?.displayName ?? '',
|
|
335
|
+
defineVersion: def?.version ?? 0,
|
|
336
|
+
instanceVariable: inst ? JSON.stringify(inst.variables ?? {}) : '',
|
|
337
|
+
instanceCreateTime: inst?.createTime ?? t.createTime,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
165
340
|
// pageCcInstances 我的抄送分页(v1.3.0):按抄送人 actorId 过滤,join 实例 + 定义
|
|
166
|
-
async pageCcInstances(pageNum = 1, pageSize = 10, actorId) {
|
|
341
|
+
async pageCcInstances(pageNum = 1, pageSize = 10, actorId, conditions) {
|
|
167
342
|
const rows = [];
|
|
168
343
|
for (const [instId, actors] of this.ccInstances) {
|
|
169
344
|
if (actorId && !actors.includes(actorId))
|
|
@@ -172,7 +347,7 @@ export class MemoryRepository {
|
|
|
172
347
|
if (!inst)
|
|
173
348
|
continue;
|
|
174
349
|
const def = this.defines.get(inst.defineId);
|
|
175
|
-
|
|
350
|
+
const r = {
|
|
176
351
|
id: inst.id, parentId: inst.parentId, defineId: inst.defineId, state: inst.state,
|
|
177
352
|
parentNodeName: inst.parentNodeName, businessNo: inst.businessNo, operator: inst.operator,
|
|
178
353
|
expireTime: inst.expireTime, variables: { ...inst.variables },
|
|
@@ -180,7 +355,11 @@ export class MemoryRepository {
|
|
|
180
355
|
updateTime: inst.updateTime, updateUser: inst.updateUser,
|
|
181
356
|
defineName: def?.name ?? '', defineDisplayName: def?.displayName ?? '',
|
|
182
357
|
defineVersion: def?.version ?? 0,
|
|
183
|
-
}
|
|
358
|
+
};
|
|
359
|
+
const fields = pickFields(r, INSTANCE_FIELDS);
|
|
360
|
+
fields['cc.actor_id'] = actors;
|
|
361
|
+
if (matchConditions(conditions, fields))
|
|
362
|
+
rows.push(r);
|
|
184
363
|
}
|
|
185
364
|
const total = rows.length;
|
|
186
365
|
const start = (pageNum - 1) * pageSize;
|
package/dist/model.d.ts
CHANGED
|
@@ -225,3 +225,57 @@ export interface CcInstanceRow {
|
|
|
225
225
|
defineDisplayName: string;
|
|
226
226
|
defineVersion: number;
|
|
227
227
|
}
|
|
228
|
+
export interface DefineRow {
|
|
229
|
+
id: number;
|
|
230
|
+
name: string;
|
|
231
|
+
displayName: string;
|
|
232
|
+
type: string;
|
|
233
|
+
state: number;
|
|
234
|
+
version: number;
|
|
235
|
+
createTime: Date;
|
|
236
|
+
createUser: string;
|
|
237
|
+
updateTime: Date;
|
|
238
|
+
updateUser: string;
|
|
239
|
+
}
|
|
240
|
+
export interface InstanceRow {
|
|
241
|
+
id: number;
|
|
242
|
+
parentId?: number;
|
|
243
|
+
defineId: number;
|
|
244
|
+
state: InstanceState;
|
|
245
|
+
parentNodeName: string;
|
|
246
|
+
businessNo: string;
|
|
247
|
+
operator: string;
|
|
248
|
+
expireTime?: Date;
|
|
249
|
+
variables: Record<string, any>;
|
|
250
|
+
createTime: Date;
|
|
251
|
+
createUser: string;
|
|
252
|
+
updateTime: Date;
|
|
253
|
+
updateUser: string;
|
|
254
|
+
defineName: string;
|
|
255
|
+
defineDisplayName: string;
|
|
256
|
+
defineVersion: number;
|
|
257
|
+
}
|
|
258
|
+
export interface TaskRow {
|
|
259
|
+
id: number;
|
|
260
|
+
processInstanceId: number;
|
|
261
|
+
taskName: string;
|
|
262
|
+
displayName: string;
|
|
263
|
+
taskType: number;
|
|
264
|
+
performType: number;
|
|
265
|
+
taskState: TaskState;
|
|
266
|
+
operator: string;
|
|
267
|
+
finishTime?: Date;
|
|
268
|
+
expireTime?: Date;
|
|
269
|
+
formKey: string;
|
|
270
|
+
taskParentId?: number;
|
|
271
|
+
variables: Record<string, any>;
|
|
272
|
+
createTime: Date;
|
|
273
|
+
createUser: string;
|
|
274
|
+
updateTime: Date;
|
|
275
|
+
updateUser: string;
|
|
276
|
+
processDefineName: string;
|
|
277
|
+
processDefineDisplayName: string;
|
|
278
|
+
defineVersion: number;
|
|
279
|
+
instanceVariable: string;
|
|
280
|
+
instanceCreateTime: Date;
|
|
281
|
+
}
|
package/dist/spi.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type { CcInstanceRow, ProcessDefine, ProcessDesign, ProcessDesignHis, ProcessInstance, ProcessSurrogate, ProcessTask, UserInfo } from './model.js';
|
|
1
|
+
import type { CcInstanceRow, DefineRow, InstanceRow, TaskRow, ProcessDefine, ProcessDesign, ProcessDesignHis, ProcessInstance, ProcessSurrogate, ProcessTask, UserInfo } from './model.js';
|
|
2
|
+
export interface QueryCondition {
|
|
3
|
+
column: string;
|
|
4
|
+
operator: string;
|
|
5
|
+
value: any;
|
|
6
|
+
}
|
|
2
7
|
export interface ProcessRepository {
|
|
3
8
|
findDefineById(id: number): Promise<ProcessDefine | null>;
|
|
4
9
|
findDefineByName(name: string): Promise<ProcessDefine | null>;
|
|
@@ -20,10 +25,26 @@ export interface ProcessRepository {
|
|
|
20
25
|
removeTaskActor(taskId: number, actors: string[]): Promise<void>;
|
|
21
26
|
createCcInstance(instanceId: number, creator: string, ...actorIds: string[]): Promise<void>;
|
|
22
27
|
updateCcStatus(instanceId: number, actorId: string): Promise<void>;
|
|
23
|
-
pageCcInstances(pageNum: number, pageSize: number, actorId: string): Promise<{
|
|
28
|
+
pageCcInstances(pageNum: number, pageSize: number, actorId: string, conditions?: QueryCondition[]): Promise<{
|
|
24
29
|
rows: CcInstanceRow[];
|
|
25
30
|
total: number;
|
|
26
31
|
}>;
|
|
32
|
+
pageDefines(pageNum: number, pageSize: number, conditions?: QueryCondition[]): Promise<{
|
|
33
|
+
rows: DefineRow[];
|
|
34
|
+
total: number;
|
|
35
|
+
}>;
|
|
36
|
+
pageInstances(pageNum: number, pageSize: number, operator: string, conditions?: QueryCondition[]): Promise<{
|
|
37
|
+
rows: InstanceRow[];
|
|
38
|
+
total: number;
|
|
39
|
+
}>;
|
|
40
|
+
pageTodoTasks(pageNum: number, pageSize: number, actorId: string, conditions?: QueryCondition[]): Promise<{
|
|
41
|
+
rows: TaskRow[];
|
|
42
|
+
total: number;
|
|
43
|
+
}>;
|
|
44
|
+
pageDoneTasks(pageNum: number, pageSize: number, operator: string, conditions?: QueryCondition[]): Promise<{
|
|
45
|
+
rows: TaskRow[];
|
|
46
|
+
total: number;
|
|
47
|
+
}>;
|
|
27
48
|
}
|
|
28
49
|
export interface UserProvider {
|
|
29
50
|
getUser(userId: string): Promise<UserInfo | null>;
|
|
@@ -39,13 +60,13 @@ export interface ProcessExtRepository {
|
|
|
39
60
|
saveDesign(d: ProcessDesign): Promise<void>;
|
|
40
61
|
updateDesign(d: ProcessDesign): Promise<void>;
|
|
41
62
|
removeDesign(id: number): Promise<void>;
|
|
42
|
-
pageDesigns(pageNum?: number, pageSize?: number, filters?: Record<string, any
|
|
63
|
+
pageDesigns(pageNum?: number, pageSize?: number, filters?: Record<string, any>, conditions?: QueryCondition[]): Promise<[ProcessDesign[], number]>;
|
|
43
64
|
saveDesignHis(his: ProcessDesignHis): Promise<void>;
|
|
44
65
|
listDesignHis(designId: number): Promise<ProcessDesignHis[]>;
|
|
45
66
|
findSurrogateById(id: number): Promise<ProcessSurrogate | null>;
|
|
46
67
|
saveSurrogate(s: ProcessSurrogate): Promise<void>;
|
|
47
68
|
updateSurrogate(s: ProcessSurrogate): Promise<void>;
|
|
48
69
|
removeSurrogate(id: number): Promise<void>;
|
|
49
|
-
pageSurrogates(pageNum?: number, pageSize?: number, filters?: Record<string, any
|
|
70
|
+
pageSurrogates(pageNum?: number, pageSize?: number, filters?: Record<string, any>, conditions?: QueryCondition[]): Promise<[ProcessSurrogate[], number]>;
|
|
50
71
|
getSurrogate(operator: string, processName: string, at?: Date): Promise<ProcessSurrogate | null>;
|
|
51
72
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mldong/jeeflow",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "jeeflow workflow engine — Node.js / TypeScript implementation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
],
|
|
30
30
|
"license": "Apache-2.0",
|
|
31
31
|
"devDependencies": {
|
|
32
|
+
"@types/express": "^4.17.25",
|
|
32
33
|
"@types/pg": "^8.20.3",
|
|
34
|
+
"express": "^4.22.2",
|
|
33
35
|
"tsx": "^4.19.0",
|
|
34
36
|
"typescript": "^5.5.0"
|
|
35
37
|
},
|