@mldong/jeeflow 1.3.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 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
- data.jsonObject = JSON.parse(toStr(hisList[0].content));
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/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { EngineImpl, type Engine } from './engine.js';
2
2
  export { MemoryRepository } from './memory.js';
3
- export { HandlerRegistry, type IAssignmentHandler, type IDecisionHandler } from './registry.js';
3
+ export { HandlerRegistry, type IAssignmentHandler, type IDecisionHandler, type HandlerMeta } from './registry.js';
4
+ export { enumDict, enumDictKeys, type DictItem } from './metadata.js';
4
5
  export * from './model.js';
5
6
  export type { ProcessRepository, UserProvider, IDGenerator, ExpressionEvaluator } from './spi.js';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { EngineImpl } from './engine.js';
2
2
  export { MemoryRepository } from './memory.js';
3
3
  export { HandlerRegistry } from './registry.js';
4
+ export { enumDict, enumDictKeys } from './metadata.js';
4
5
  export * from './model.js';
@@ -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>): Promise<[ProcessDesign[], number]>;
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>): Promise<[ProcessSurrogate[], number]>;
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);
@@ -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
- pageCcInstances(pageNum: number, pageSize: number, actorId: string): Promise<{
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
  }>;