@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.
@@ -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,
@@ -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>): Promise<[ProcessDesign[], number]>;
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, _filters?: Record<string, any>): Promise<[ProcessSurrogate[], 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
  }
@@ -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
- return [[...this.designs.values()], this.designs.size];
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, _filters) {
65
- return [[...this.surrogates.values()], this.surrogates.size];
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
- pageCcInstances(pageNum: number | undefined, pageSize: number | undefined, actorId: string): Promise<{
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
- rows.push({
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;
@@ -0,0 +1,9 @@
1
+ /** 单个字典项 */
2
+ export interface DictItem {
3
+ value: string;
4
+ label: string;
5
+ }
6
+ /** 内置枚举字典 key 清单(对齐 boot3 字典 key,存量前端零改动) */
7
+ export declare function enumDictKeys(): string[];
8
+ /** 按 key 取字典([{value, label}]),未知 key 返回空列表 */
9
+ export declare function enumDict(key: string): DictItem[];
@@ -0,0 +1,41 @@
1
+ // 引擎元数据能力(v1.4.0,issues/04)——内置状态枚举字典
2
+ //
3
+ // key 对齐 boot3 字典(wf_process_define_state 等),value/label 与 Java enums 完全一致,
4
+ // 杜绝集成方重复定义导致的值漂移。
5
+ // 内置字典表(值顺序与 Java enums 声明顺序一致)
6
+ const DICTS = {
7
+ wf_process_define_state: [
8
+ { value: '0', label: '禁用' }, { value: '1', label: '启用' },
9
+ ],
10
+ wf_process_instance_state: [
11
+ { value: '10', label: '进行中' }, { value: '20', label: '已完成' }, { value: '30', label: '已撤回' },
12
+ { value: '40', label: '强行终止' }, { value: '45', label: '已拒绝' }, { value: '50', label: '挂起' },
13
+ { value: '99', label: '已废弃' },
14
+ ],
15
+ wf_process_submit_type: [
16
+ { value: '0', label: '发起申请' }, { value: '1', label: '同意申请' }, { value: '2', label: '拒绝申请' },
17
+ { value: '3', label: '退回上一步' }, { value: '4', label: '跳转' }, { value: '5', label: '重新提交' },
18
+ { value: '6', label: '退回发起人' }, { value: '20', label: '拒绝申请' },
19
+ ],
20
+ wf_process_task_state: [
21
+ { value: '10', label: '进行中' }, { value: '20', label: '已完成' }, { value: '30', label: '已撤回' },
22
+ { value: '40', label: '强行终止' }, { value: '50', label: '挂起' }, { value: '99', label: '已废弃' },
23
+ ],
24
+ wf_process_task_type: [
25
+ { value: '0', label: '主办' }, { value: '1', label: '协办' }, { value: '2', label: '记录' },
26
+ ],
27
+ wf_process_task_perform_type: [
28
+ { value: '0', label: '普通参与' }, { value: '1', label: '会签参与' },
29
+ ],
30
+ wf_countersign_type: [
31
+ { value: '0', label: '并行会签' }, { value: '1', label: '串行会签' },
32
+ ],
33
+ };
34
+ /** 内置枚举字典 key 清单(对齐 boot3 字典 key,存量前端零改动) */
35
+ export function enumDictKeys() {
36
+ return Object.keys(DICTS);
37
+ }
38
+ /** 按 key 取字典([{value, label}]),未知 key 返回空列表 */
39
+ export function enumDict(key) {
40
+ return [...(DICTS[key] ?? [])];
41
+ }