@mldong/jeeflow 1.8.12 → 1.8.14
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/README.md +80 -80
- package/dist/engine.d.ts +6 -1
- package/dist/engine.js +28 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/persist.d.ts +4 -0
- package/dist/persist.js +11 -0
- package/dist/registry.d.ts +13 -5
- package/dist/registry.js +31 -16
- package/package.json +47 -47
package/README.md
CHANGED
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
# jeeflow · Node.js / TypeScript
|
|
2
|
-
|
|
3
|
-
[jeeflow](https://jeeflow-doc.mldong.com) 引擎规范的 **TypeScript 实现**。仅依赖 `node:*` 标准库,零外部依赖(引擎核心)。
|
|
4
|
-
|
|
5
|
-
> **v1.1.0**:新增管理扩展(流程设计/历史/委托 + `ProcessExtRepository`)与统一门面
|
|
6
|
-
> `JeeflowFacade.flow(action, args)`;assignee 变量解析与 `flow.auto`/`flow.admin` 系统代执行对齐 boot2/boot3。
|
|
7
|
-
|
|
8
|
-
```bash
|
|
9
|
-
npm install @mldong/jeeflow # 等发布到 npm 后
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
## 快速开始
|
|
13
|
-
|
|
14
|
-
```ts
|
|
15
|
-
import { EngineImpl, MemoryRepository } from '@mldong/jeeflow'
|
|
16
|
-
|
|
17
|
-
const repo = new MemoryRepository()
|
|
18
|
-
const engine = new EngineImpl(repo)
|
|
19
|
-
|
|
20
|
-
const def = {
|
|
21
|
-
id: '1', name: 'leave', displayName: '请假审批',
|
|
22
|
-
content: JSON.stringify({ nodes: [...], edges: [...] })
|
|
23
|
-
}
|
|
24
|
-
repo.addDefine(def)
|
|
25
|
-
|
|
26
|
-
const inst = await engine.startProcessInstanceById(def.id, '张三')
|
|
27
|
-
const tasks = await repo.findDoingTasks(inst.id)
|
|
28
|
-
await engine.executeProcessTask(tasks[0].id, 'leader')
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
> ⚠️ **id 全程 string(issue 38 E9)**:引擎内部与 API 契约的 id(流程定义/实例/任务/设计)一律为**字符串**。
|
|
32
|
-
> Java 雪花 id(>2^53)超出 JS 安全整数,`Number()` 转换必丢精度;字符串可无损承载,保证四语言同库共享流程。
|
|
33
|
-
|
|
34
|
-
## JDBC(MySQL/PostgreSQL)
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
import mysql from 'mysql2/promise'
|
|
38
|
-
import { JdbcRepository, TsIDGenerator, MysqlAdapter } from '@mldong/jeeflow/jdbc'
|
|
39
|
-
|
|
40
|
-
// ⚠️ 必须配置 bigNumberStrings(issue 38 E9):BIGINT 列以 string 返回,
|
|
41
|
-
// 否则 mysql2 默认转 number,Java 雪花 id(>2^53)在驱动层就已丢精度
|
|
42
|
-
const pool = mysql.createPool({
|
|
43
|
-
host: 'localhost', user: 'root', password: '***', database: 'wf',
|
|
44
|
-
supportBigNumbers: true,
|
|
45
|
-
bigNumberStrings: true,
|
|
46
|
-
})
|
|
47
|
-
const repo = new JdbcRepository(new MysqlAdapter(pool), new TsIDGenerator())
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
- MySQL 连接池必须带 `supportBigNumbers: true, bigNumberStrings: true`;PostgreSQL 的 int8 默认即字符串,无此要求
|
|
51
|
-
- 引擎侧已做驱动兜底:`rowId()` 把驱动返回的 number/string 统一归一化为 string,未开 `bigNumberStrings` 时小 id(Node 自建)仍可用
|
|
52
|
-
|
|
53
|
-
## 运行演示
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
git clone https://github.com/mldong/jeeflow-node
|
|
57
|
-
cd jeeflow-node
|
|
58
|
-
npm install
|
|
59
|
-
npm run demo # http://localhost:8082
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## 目录
|
|
63
|
-
|
|
64
|
-
| 路径 | 说明 |
|
|
65
|
-
|------|------|
|
|
66
|
-
| `src/engine.ts` | 引擎核心 |
|
|
67
|
-
| `src/model.ts` | 域类型 |
|
|
68
|
-
| `src/spi.ts` | SPI 接口 |
|
|
69
|
-
| `src/memory.ts` | 内存仓储 |
|
|
70
|
-
| `src/jdbc/` | JDBC 多库实现(shared 核心 + mysql/postgres 适配器) |
|
|
71
|
-
| `demo/main.ts` | Express 演示 |
|
|
72
|
-
| `__tests__/` | 9 项合规测试 |
|
|
73
|
-
|
|
74
|
-
## 节点支持
|
|
75
|
-
|
|
76
|
-
对齐 [SPEC.md](https://jeeflow-doc.mldong.com) v1.0——与 Java/Go 版一致。
|
|
77
|
-
|
|
78
|
-
## License
|
|
79
|
-
|
|
80
|
-
Apache-2.0
|
|
1
|
+
# jeeflow · Node.js / TypeScript
|
|
2
|
+
|
|
3
|
+
[jeeflow](https://jeeflow-doc.mldong.com) 引擎规范的 **TypeScript 实现**。仅依赖 `node:*` 标准库,零外部依赖(引擎核心)。
|
|
4
|
+
|
|
5
|
+
> **v1.1.0**:新增管理扩展(流程设计/历史/委托 + `ProcessExtRepository`)与统一门面
|
|
6
|
+
> `JeeflowFacade.flow(action, args)`;assignee 变量解析与 `flow.auto`/`flow.admin` 系统代执行对齐 boot2/boot3。
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @mldong/jeeflow # 等发布到 npm 后
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 快速开始
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { EngineImpl, MemoryRepository } from '@mldong/jeeflow'
|
|
16
|
+
|
|
17
|
+
const repo = new MemoryRepository()
|
|
18
|
+
const engine = new EngineImpl(repo)
|
|
19
|
+
|
|
20
|
+
const def = {
|
|
21
|
+
id: '1', name: 'leave', displayName: '请假审批',
|
|
22
|
+
content: JSON.stringify({ nodes: [...], edges: [...] })
|
|
23
|
+
}
|
|
24
|
+
repo.addDefine(def)
|
|
25
|
+
|
|
26
|
+
const inst = await engine.startProcessInstanceById(def.id, '张三')
|
|
27
|
+
const tasks = await repo.findDoingTasks(inst.id)
|
|
28
|
+
await engine.executeProcessTask(tasks[0].id, 'leader')
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
> ⚠️ **id 全程 string(issue 38 E9)**:引擎内部与 API 契约的 id(流程定义/实例/任务/设计)一律为**字符串**。
|
|
32
|
+
> Java 雪花 id(>2^53)超出 JS 安全整数,`Number()` 转换必丢精度;字符串可无损承载,保证四语言同库共享流程。
|
|
33
|
+
|
|
34
|
+
## JDBC(MySQL/PostgreSQL)
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import mysql from 'mysql2/promise'
|
|
38
|
+
import { JdbcRepository, TsIDGenerator, MysqlAdapter } from '@mldong/jeeflow/jdbc'
|
|
39
|
+
|
|
40
|
+
// ⚠️ 必须配置 bigNumberStrings(issue 38 E9):BIGINT 列以 string 返回,
|
|
41
|
+
// 否则 mysql2 默认转 number,Java 雪花 id(>2^53)在驱动层就已丢精度
|
|
42
|
+
const pool = mysql.createPool({
|
|
43
|
+
host: 'localhost', user: 'root', password: '***', database: 'wf',
|
|
44
|
+
supportBigNumbers: true,
|
|
45
|
+
bigNumberStrings: true,
|
|
46
|
+
})
|
|
47
|
+
const repo = new JdbcRepository(new MysqlAdapter(pool), new TsIDGenerator())
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- MySQL 连接池必须带 `supportBigNumbers: true, bigNumberStrings: true`;PostgreSQL 的 int8 默认即字符串,无此要求
|
|
51
|
+
- 引擎侧已做驱动兜底:`rowId()` 把驱动返回的 number/string 统一归一化为 string,未开 `bigNumberStrings` 时小 id(Node 自建)仍可用
|
|
52
|
+
|
|
53
|
+
## 运行演示
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/mldong/jeeflow-node
|
|
57
|
+
cd jeeflow-node
|
|
58
|
+
npm install
|
|
59
|
+
npm run demo # http://localhost:8082
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 目录
|
|
63
|
+
|
|
64
|
+
| 路径 | 说明 |
|
|
65
|
+
|------|------|
|
|
66
|
+
| `src/engine.ts` | 引擎核心 |
|
|
67
|
+
| `src/model.ts` | 域类型 |
|
|
68
|
+
| `src/spi.ts` | SPI 接口 |
|
|
69
|
+
| `src/memory.ts` | 内存仓储 |
|
|
70
|
+
| `src/jdbc/` | JDBC 多库实现(shared 核心 + mysql/postgres 适配器) |
|
|
71
|
+
| `demo/main.ts` | Express 演示 |
|
|
72
|
+
| `__tests__/` | 9 项合规测试 |
|
|
73
|
+
|
|
74
|
+
## 节点支持
|
|
75
|
+
|
|
76
|
+
对齐 [SPEC.md](https://jeeflow-doc.mldong.com) v1.0——与 Java/Go 版一致。
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
Apache-2.0
|
package/dist/engine.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare const KeyNextNodeOperator = "tf_nextNodeOperator";
|
|
|
14
14
|
export declare const KeyProcessStartNextNodeOperator = "f_nextNodeOperator";
|
|
15
15
|
export declare const KeyAutoExecute = "flow.auto";
|
|
16
16
|
export declare const KeyAdminID = "flow.admin";
|
|
17
|
+
export declare const KeyAutoGenTitle = "autoGenTitle";
|
|
17
18
|
export interface Engine {
|
|
18
19
|
startProcessInstanceById(defineId: string, operator: string, args?: Record<string, any>): Promise<ProcessInstance>;
|
|
19
20
|
executeProcessTask(taskId: string, operator: string, args?: Record<string, any>): Promise<ProcessInstance>;
|
|
@@ -34,7 +35,9 @@ export declare class EngineImpl implements Engine {
|
|
|
34
35
|
private interceptorCache;
|
|
35
36
|
/** 定义级拦截器解析(issue 34,对齐 Java 模型级 postInterceptors):
|
|
36
37
|
* 流程定义顶层 postInterceptors 声明 → 按名从 interceptorRegistry 取(未声明该流程不触发);
|
|
37
|
-
* 未声明 → 回落引擎级列表(向后兼容)。结果按 defineId 缓存。
|
|
38
|
+
* 未声明 → 回落引擎级列表(向后兼容)。结果按 defineId 缓存。
|
|
39
|
+
* issues/60:解析与校验分离——定义读取/JSON 解析失败回落引擎级(现状语义),
|
|
40
|
+
* 声明中存在未注册名时抛错(不静默跳过),且错误不写缓存保证持续报错。 */
|
|
38
41
|
private resolveInterceptors;
|
|
39
42
|
private firePre;
|
|
40
43
|
/** 表达式求值(v1.5.0,门面 highLight 决策分支过滤用) */
|
|
@@ -53,6 +56,8 @@ export declare class EngineImpl implements Engine {
|
|
|
53
56
|
private resolveActors;
|
|
54
57
|
private isAllowed;
|
|
55
58
|
private addUserInfo;
|
|
59
|
+
/** issue 29:自动生成标题(对齐 boot3 FlowUtil.addAutoGenTitle) */
|
|
60
|
+
private addAutoGenTitle;
|
|
56
61
|
/** 用户提供者访问(issue 41 补强:nodeProgress 姓名解析用) */
|
|
57
62
|
getUserProvider(): UserProvider | undefined;
|
|
58
63
|
private nextId;
|
package/dist/engine.js
CHANGED
|
@@ -15,6 +15,8 @@ export const KeyProcessStartNextNodeOperator = 'f_nextNodeOperator';
|
|
|
15
15
|
// v1.0.1:系统代执行 / 超级管理员(对齐 boot3 FlowConst)
|
|
16
16
|
export const KeyAutoExecute = 'flow.auto';
|
|
17
17
|
export const KeyAdminID = 'flow.admin';
|
|
18
|
+
// issue 29:自动生成标题(对齐 boot3 FlowConst.AUTO_GEN_TITLE)
|
|
19
|
+
export const KeyAutoGenTitle = 'autoGenTitle';
|
|
18
20
|
export class EngineImpl {
|
|
19
21
|
repo;
|
|
20
22
|
userProv;
|
|
@@ -36,7 +38,9 @@ export class EngineImpl {
|
|
|
36
38
|
interceptorCache = new Map();
|
|
37
39
|
/** 定义级拦截器解析(issue 34,对齐 Java 模型级 postInterceptors):
|
|
38
40
|
* 流程定义顶层 postInterceptors 声明 → 按名从 interceptorRegistry 取(未声明该流程不触发);
|
|
39
|
-
* 未声明 → 回落引擎级列表(向后兼容)。结果按 defineId 缓存。
|
|
41
|
+
* 未声明 → 回落引擎级列表(向后兼容)。结果按 defineId 缓存。
|
|
42
|
+
* issues/60:解析与校验分离——定义读取/JSON 解析失败回落引擎级(现状语义),
|
|
43
|
+
* 声明中存在未注册名时抛错(不静默跳过),且错误不写缓存保证持续报错。 */
|
|
40
44
|
async resolveInterceptors(inst) {
|
|
41
45
|
if (!this.ext)
|
|
42
46
|
return [];
|
|
@@ -47,23 +51,27 @@ export class EngineImpl {
|
|
|
47
51
|
if (cached)
|
|
48
52
|
return cached;
|
|
49
53
|
let list = this.ext.interceptors ?? [];
|
|
54
|
+
let declared = '';
|
|
50
55
|
try {
|
|
51
56
|
const def = await this.repo.findDefineById(defineId);
|
|
52
57
|
if (def) {
|
|
53
58
|
const content = typeof def.content === 'string' ? def.content : new TextDecoder().decode(def.content);
|
|
54
59
|
const meta = JSON.parse(content);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
declared = String(meta.postInterceptors ?? '').trim();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch { /* 定义读取/JSON 解析失败回落引擎级 */ }
|
|
64
|
+
if (declared) {
|
|
65
|
+
list = [];
|
|
66
|
+
for (const name of declared.split(',').map(n => n.trim())) {
|
|
67
|
+
if (!name)
|
|
68
|
+
continue;
|
|
69
|
+
const ic = this.ext.interceptorRegistry?.[name];
|
|
70
|
+
if (!ic)
|
|
71
|
+
throw new Error(`postInterceptors 声明的拦截器未注册: ${name}`);
|
|
72
|
+
list.push(ic);
|
|
64
73
|
}
|
|
65
74
|
}
|
|
66
|
-
catch { /* 解析失败回落引擎级 */ }
|
|
67
75
|
this.interceptorCache.set(defineId, list);
|
|
68
76
|
return list;
|
|
69
77
|
}
|
|
@@ -103,6 +111,7 @@ export class EngineImpl {
|
|
|
103
111
|
const flow = JSON.parse(content);
|
|
104
112
|
const vars = { ...args };
|
|
105
113
|
await this.addUserInfo(operator, vars);
|
|
114
|
+
this.addAutoGenTitle(def.displayName, vars);
|
|
106
115
|
const now = new Date();
|
|
107
116
|
// 聚合根工厂创建实例
|
|
108
117
|
const inst = ProcessInstance.create(this.nextId(), defineId, operator, vars, now);
|
|
@@ -470,6 +479,14 @@ export class EngineImpl {
|
|
|
470
479
|
if (u.postName)
|
|
471
480
|
vars[KeyPostName] = u.postName;
|
|
472
481
|
}
|
|
482
|
+
/** issue 29:自动生成标题(对齐 boot3 FlowUtil.addAutoGenTitle) */
|
|
483
|
+
addAutoGenTitle(displayName, vars) {
|
|
484
|
+
const realName = vars[KeyRealName] || '';
|
|
485
|
+
const now = new Date();
|
|
486
|
+
const pad = (n) => n.toString().padStart(2, '0');
|
|
487
|
+
const timeStr = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}`;
|
|
488
|
+
vars[KeyAutoGenTitle] = `${realName}的${displayName}-${timeStr}`;
|
|
489
|
+
}
|
|
473
490
|
/** 用户提供者访问(issue 41 补强:nodeProgress 姓名解析用) */
|
|
474
491
|
getUserProvider() {
|
|
475
492
|
return this.userProv;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export { EngineImpl, type Engine } from './engine.js';
|
|
2
2
|
export { MemoryRepository } from './memory.js';
|
|
3
|
-
export { HandlerRegistry, type IAssignmentHandler, type IDecisionHandler, type HandlerMeta } from './registry.js';
|
|
3
|
+
export { HandlerRegistry, type IAssignmentHandler, type IDecisionHandler, type HandlerMeta, type HandlerType } from './registry.js';
|
|
4
4
|
export { enumDict, enumDictKeys, type DictItem } from './metadata.js';
|
|
5
5
|
export * from './model.js';
|
|
6
6
|
export type { ProcessRepository, UserProvider, OrgUserProvider, IDGenerator, ExpressionEvaluator } from './spi.js';
|
|
7
|
-
export { SqliteDynamicTableWriter, PersistPostInterceptor, type DynamicTableWriter, type DefineLoader, } from './persist.js';
|
|
7
|
+
export { SqliteDynamicTableWriter, PersistPostInterceptor, registerPersistMeta, type DynamicTableWriter, type DefineLoader, } from './persist.js';
|
|
8
8
|
export { registerBuiltinAssignments, OperatorAssignmentHandler, FormFieldAssigneeHandler, DeptLeaderAssignmentHandler, DeptMainLeaderAssignmentHandler, ApplicantDeptLeaderAssignmentHandler, ApplicantDeptMainLeaderAssignmentHandler, TaskRoleAssigneeHandler, HANDLER_OPERATOR_ASSIGNMENT, HANDLER_FORM_FIELD_ASSIGNEE, HANDLER_DEPT_LEADER, HANDLER_DEPT_MAIN_LEADER, HANDLER_APPLICANT_DEPT_LEADER, HANDLER_APPLICANT_DEPT_MAIN_LEADER, HANDLER_TASK_ROLE_ASSIGNEE, } from './builtin.js';
|
|
9
9
|
export { JeeflowFacade } from './facade.js';
|
|
10
10
|
export type { EngineExtensions, FlowInterceptor, ProcessEventListener, ProcessEvent, AssignmentHandler, DecisionHandler } from './extensions.js';
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export { MemoryRepository } from './memory.js';
|
|
|
3
3
|
export { HandlerRegistry } from './registry.js';
|
|
4
4
|
export { enumDict, enumDictKeys } from './metadata.js';
|
|
5
5
|
export * from './model.js';
|
|
6
|
-
export { SqliteDynamicTableWriter, PersistPostInterceptor, } from './persist.js';
|
|
6
|
+
export { SqliteDynamicTableWriter, PersistPostInterceptor, registerPersistMeta, } from './persist.js';
|
|
7
7
|
export { registerBuiltinAssignments, OperatorAssignmentHandler, FormFieldAssigneeHandler, DeptLeaderAssignmentHandler, DeptMainLeaderAssignmentHandler, ApplicantDeptLeaderAssignmentHandler, ApplicantDeptMainLeaderAssignmentHandler, TaskRoleAssigneeHandler, HANDLER_OPERATOR_ASSIGNMENT, HANDLER_FORM_FIELD_ASSIGNEE, HANDLER_DEPT_LEADER, HANDLER_DEPT_MAIN_LEADER, HANDLER_APPLICANT_DEPT_LEADER, HANDLER_APPLICANT_DEPT_MAIN_LEADER, HANDLER_TASK_ROLE_ASSIGNEE, } from './builtin.js';
|
|
8
8
|
// issues/35:包导出面补齐——门面/扩展类型/扩展仓储(集成方组装完整引擎链)
|
|
9
9
|
export { JeeflowFacade } from './facade.js';
|
package/dist/persist.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DatabaseSync } from 'node:sqlite';
|
|
2
2
|
import { type ProcessInstance, type ProcessDefine, type FlowNode } from './model.js';
|
|
3
3
|
import type { FlowInterceptor } from './extensions.js';
|
|
4
|
+
import type { HandlerRegistry } from './registry.js';
|
|
4
5
|
/**
|
|
5
6
|
* 动态表写入组件接口——引擎无关,四语言契约一致(Java/Go/Python/Node)
|
|
6
7
|
*
|
|
@@ -111,3 +112,6 @@ export declare class PersistPostInterceptor implements FlowInterceptor {
|
|
|
111
112
|
/** 流程上下文字段(蛇形列名约定,与 writer 系统字段一致) */
|
|
112
113
|
private fillContext;
|
|
113
114
|
}
|
|
115
|
+
/** 将 PersistPostInterceptor 元数据注册进处理器注册中心(SPI 字典源),
|
|
116
|
+
* 集成方在实例组装处调用一次即可,保证"字典项 ⟺ 实例"同步(避免各端写死注册遗漏/名不一致)。 */
|
|
117
|
+
export declare function registerPersistMeta(registry: HandlerRegistry): void;
|
package/dist/persist.js
CHANGED
|
@@ -362,3 +362,14 @@ export class PersistPostInterceptor {
|
|
|
362
362
|
data['apply_dept_id'] ??= inst.variables[KeyDeptID] ?? null;
|
|
363
363
|
}
|
|
364
364
|
}
|
|
365
|
+
// ─── 注册助手(issues/60)──────────────────────────────────────────────────────
|
|
366
|
+
/** 将 PersistPostInterceptor 元数据注册进处理器注册中心(SPI 字典源),
|
|
367
|
+
* 集成方在实例组装处调用一次即可,保证"字典项 ⟺ 实例"同步(避免各端写死注册遗漏/名不一致)。 */
|
|
368
|
+
export function registerPersistMeta(registry) {
|
|
369
|
+
registry.registerMeta('FlowInterceptor', {
|
|
370
|
+
name: 'com.mldong.jeeflow.persist.interceptor.PersistPostInterceptor',
|
|
371
|
+
displayName: '业务数据自动入库',
|
|
372
|
+
order: 0,
|
|
373
|
+
group: 'post',
|
|
374
|
+
});
|
|
375
|
+
}
|
package/dist/registry.d.ts
CHANGED
|
@@ -8,8 +8,12 @@ export interface IAssignmentHandler {
|
|
|
8
8
|
export interface IDecisionHandler {
|
|
9
9
|
decide(node: FlowNode, inst: ProcessInstance, vars: Record<string, any>): string | Promise<string>;
|
|
10
10
|
}
|
|
11
|
+
/** 处理器类型名(对齐 Java/go/python HandlerMeta.type,四语言通用) */
|
|
12
|
+
export type HandlerType = 'AssignmentHandler' | 'CandidateHandler' | 'FlowInterceptor' | 'DecisionHandler';
|
|
11
13
|
/** 处理器元数据(v1.4.0,SPI 实现清单字典源) */
|
|
12
14
|
export interface HandlerMeta {
|
|
15
|
+
/** 处理器类型(内置 AssignmentHandler 元数据自动携带;老数据缺省按 AssignmentHandler 归类) */
|
|
16
|
+
type?: HandlerType;
|
|
13
17
|
/** 处理器标识:节点配置的 handlerName(与字典 value 一致) */
|
|
14
18
|
name: string;
|
|
15
19
|
/** 显示名(字典 label) */
|
|
@@ -28,14 +32,18 @@ export declare class HandlerRegistry {
|
|
|
28
32
|
private metas;
|
|
29
33
|
/** 构造即内置 7 个通用 AssignmentHandler 元数据(v1.6.0 issues/16,注册名与 Java 类全限定名一致) */
|
|
30
34
|
constructor();
|
|
31
|
-
registerAssignment(name: string, handler: IAssignmentHandler, meta?: Omit<HandlerMeta, 'name'>): void;
|
|
32
|
-
registerDecision(name: string, handler: IDecisionHandler, meta?: Omit<HandlerMeta, 'name'>): void;
|
|
35
|
+
registerAssignment(name: string, handler: IAssignmentHandler, meta?: Omit<HandlerMeta, 'name' | 'type'>): void;
|
|
36
|
+
registerDecision(name: string, handler: IDecisionHandler, meta?: Omit<HandlerMeta, 'name' | 'type'>): void;
|
|
37
|
+
/** 注册处理器元数据(不绑定运行时实现,对齐 Java HandlerRegistry.register;
|
|
38
|
+
* 拦截器/候选类清单等无运行时注册表的类型用) */
|
|
39
|
+
registerMeta(type: HandlerType, meta: Omit<HandlerMeta, 'type'>): void;
|
|
33
40
|
resolveAssignment(name: string): IAssignmentHandler | undefined;
|
|
34
41
|
resolveDecision(name: string): IDecisionHandler | undefined;
|
|
35
|
-
/**
|
|
36
|
-
|
|
42
|
+
/** 按处理器类型列出可用实现的元数据(按 order 升序;
|
|
43
|
+
* AssignmentHandler/DecisionHandler 并集运行时注册名,其余类型只列显式元数据) */
|
|
44
|
+
listHandlers(typeName: HandlerType): HandlerMeta[];
|
|
37
45
|
/** 按处理器类型 + 分组列出(拦截器 pre/post) */
|
|
38
|
-
listHandlersGroup(typeName:
|
|
46
|
+
listHandlersGroup(typeName: HandlerType, group: string): HandlerMeta[];
|
|
39
47
|
/** 已注册的处理器名称清单(含未带元数据的) */
|
|
40
48
|
listHandlerNames(): string[];
|
|
41
49
|
}
|
package/dist/registry.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/** 内置通用 AssignmentHandler 元数据(v1.6.0 issues/16,注册名与 Java 类全限定名一致,四语言通用) */
|
|
2
2
|
export const BUILTIN_ASSIGNMENT_METAS = [
|
|
3
|
-
{ name: 'com.mldong.jeeflow.interceptor.impl.OperatorAssignmentHandler', displayName: '流程发起人', order: -9999 },
|
|
4
|
-
{ name: 'com.mldong.jeeflow.interceptor.impl.OrgUserAssignmentHandlers$ApplicantDeptLeaderAssignmentHandler', displayName: '发起人所属部门经理', order: 10 },
|
|
5
|
-
{ name: 'com.mldong.jeeflow.interceptor.impl.OrgUserAssignmentHandlers$ApplicantDeptMainLeaderAssignmentHandler', displayName: '发起人所属部门分管领导', order: 20 },
|
|
6
|
-
{ name: 'com.mldong.jeeflow.interceptor.impl.OrgUserAssignmentHandlers$DeptLeaderAssignmentHandler', displayName: '当前用户所属部门经理', order: 30 },
|
|
7
|
-
{ name: 'com.mldong.jeeflow.interceptor.impl.OrgUserAssignmentHandlers$DeptMainLeaderAssignmentHandler', displayName: '当前用户所属部门分管领导', order: 40 },
|
|
8
|
-
{ name: 'com.mldong.jeeflow.interceptor.impl.FormFieldAssigneeHandler', displayName: '根据表单字段值分配参与者', order: 50 },
|
|
9
|
-
{ name: 'com.mldong.jeeflow.interceptor.impl.OrgUserAssignmentHandlers$TaskRoleAssigneeHandler', displayName: '根据任务节点唯一编码关联角色分配参与者', order: 60 },
|
|
3
|
+
{ type: 'AssignmentHandler', name: 'com.mldong.jeeflow.interceptor.impl.OperatorAssignmentHandler', displayName: '流程发起人', order: -9999 },
|
|
4
|
+
{ type: 'AssignmentHandler', name: 'com.mldong.jeeflow.interceptor.impl.OrgUserAssignmentHandlers$ApplicantDeptLeaderAssignmentHandler', displayName: '发起人所属部门经理', order: 10 },
|
|
5
|
+
{ type: 'AssignmentHandler', name: 'com.mldong.jeeflow.interceptor.impl.OrgUserAssignmentHandlers$ApplicantDeptMainLeaderAssignmentHandler', displayName: '发起人所属部门分管领导', order: 20 },
|
|
6
|
+
{ type: 'AssignmentHandler', name: 'com.mldong.jeeflow.interceptor.impl.OrgUserAssignmentHandlers$DeptLeaderAssignmentHandler', displayName: '当前用户所属部门经理', order: 30 },
|
|
7
|
+
{ type: 'AssignmentHandler', name: 'com.mldong.jeeflow.interceptor.impl.OrgUserAssignmentHandlers$DeptMainLeaderAssignmentHandler', displayName: '当前用户所属部门分管领导', order: 40 },
|
|
8
|
+
{ type: 'AssignmentHandler', name: 'com.mldong.jeeflow.interceptor.impl.FormFieldAssigneeHandler', displayName: '根据表单字段值分配参与者', order: 50 },
|
|
9
|
+
{ type: 'AssignmentHandler', name: 'com.mldong.jeeflow.interceptor.impl.OrgUserAssignmentHandlers$TaskRoleAssigneeHandler', displayName: '根据任务节点唯一编码关联角色分配参与者', order: 60 },
|
|
10
10
|
];
|
|
11
11
|
/** 处理器注册表——按名称注册/解析 + 元数据清单(对标 Spring IoC / Java HandlerRegistry) */
|
|
12
12
|
export class HandlerRegistry {
|
|
@@ -22,12 +22,17 @@ export class HandlerRegistry {
|
|
|
22
22
|
registerAssignment(name, handler, meta) {
|
|
23
23
|
this.assignments.set(name, handler);
|
|
24
24
|
if (meta)
|
|
25
|
-
this.metas.set(name, { name, ...meta });
|
|
25
|
+
this.metas.set(name, { type: 'AssignmentHandler', name, ...meta });
|
|
26
26
|
}
|
|
27
27
|
registerDecision(name, handler, meta) {
|
|
28
28
|
this.decisions.set(name, handler);
|
|
29
29
|
if (meta)
|
|
30
|
-
this.metas.set(name, { name, ...meta });
|
|
30
|
+
this.metas.set(name, { type: 'DecisionHandler', name, ...meta });
|
|
31
|
+
}
|
|
32
|
+
/** 注册处理器元数据(不绑定运行时实现,对齐 Java HandlerRegistry.register;
|
|
33
|
+
* 拦截器/候选类清单等无运行时注册表的类型用) */
|
|
34
|
+
registerMeta(type, meta) {
|
|
35
|
+
this.metas.set(meta.name, { type, ...meta });
|
|
31
36
|
}
|
|
32
37
|
resolveAssignment(name) {
|
|
33
38
|
return this.assignments.get(name);
|
|
@@ -36,14 +41,24 @@ export class HandlerRegistry {
|
|
|
36
41
|
return this.decisions.get(name);
|
|
37
42
|
}
|
|
38
43
|
// ── SPI 实现清单(v1.4.0)──
|
|
39
|
-
/**
|
|
44
|
+
/** 按处理器类型列出可用实现的元数据(按 order 升序;
|
|
45
|
+
* AssignmentHandler/DecisionHandler 并集运行时注册名,其余类型只列显式元数据) */
|
|
40
46
|
listHandlers(typeName) {
|
|
41
|
-
const names =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
const names = new Set();
|
|
48
|
+
if (typeName === 'AssignmentHandler') {
|
|
49
|
+
for (const n of this.assignments.keys())
|
|
50
|
+
names.add(n);
|
|
51
|
+
}
|
|
52
|
+
else if (typeName === 'DecisionHandler') {
|
|
53
|
+
for (const n of this.decisions.keys())
|
|
54
|
+
names.add(n);
|
|
55
|
+
}
|
|
56
|
+
for (const [n, m] of this.metas) {
|
|
57
|
+
// 老数据无 type 时按 AssignmentHandler 归类(与历史 listHandlers 行为一致)
|
|
58
|
+
if ((m.type ?? 'AssignmentHandler') === typeName)
|
|
59
|
+
names.add(n);
|
|
60
|
+
}
|
|
61
|
+
return [...names]
|
|
47
62
|
.map(n => this.metas.get(n) ?? { name: n })
|
|
48
63
|
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
49
64
|
}
|
package/package.json
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@mldong/jeeflow",
|
|
3
|
-
"version": "1.8.
|
|
4
|
-
"description": "jeeflow workflow engine — Node.js / TypeScript implementation",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./dist/index.js",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": "./dist/index.js",
|
|
10
|
-
"./engine": "./dist/engine.js",
|
|
11
|
-
"./model": "./dist/model.js",
|
|
12
|
-
"./spi": "./dist/spi.js",
|
|
13
|
-
"./memory": "./dist/memory.js",
|
|
14
|
-
"./jdbc": "./dist/jdbc/index.js",
|
|
15
|
-
"./mysql": "./dist/jdbc/mysql.js",
|
|
16
|
-
"./postgres": "./dist/jdbc/postgres.js",
|
|
17
|
-
"./jdbc/ext": "./dist/jdbc/ext.js"
|
|
18
|
-
},
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "tsc",
|
|
21
|
-
"test": "node --import tsx __tests__/spec.test.ts",
|
|
22
|
-
"demo": "node --import tsx demo/main.ts",
|
|
23
|
-
"prepublishOnly": "npm run build"
|
|
24
|
-
},
|
|
25
|
-
"keywords": [
|
|
26
|
-
"workflow",
|
|
27
|
-
"engine",
|
|
28
|
-
"jeeflow",
|
|
29
|
-
"typescript"
|
|
30
|
-
],
|
|
31
|
-
"license": "Apache-2.0",
|
|
32
|
-
"devDependencies": {
|
|
33
|
-
"@types/express": "^4.17.25",
|
|
34
|
-
"@types/pg": "^8.20.3",
|
|
35
|
-
"express": "^4.22.2",
|
|
36
|
-
"tsx": "^4.19.0",
|
|
37
|
-
"typescript": "^5.5.0"
|
|
38
|
-
},
|
|
39
|
-
"optionalDependencies": {
|
|
40
|
-
"mysql2": "^3.11.0",
|
|
41
|
-
"pg": "^8.13.0"
|
|
42
|
-
},
|
|
43
|
-
"files": [
|
|
44
|
-
"dist",
|
|
45
|
-
"LICENSE",
|
|
46
|
-
"README.md"
|
|
47
|
-
]
|
|
1
|
+
{
|
|
2
|
+
"name": "@mldong/jeeflow",
|
|
3
|
+
"version": "1.8.14",
|
|
4
|
+
"description": "jeeflow workflow engine — Node.js / TypeScript implementation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/index.js",
|
|
10
|
+
"./engine": "./dist/engine.js",
|
|
11
|
+
"./model": "./dist/model.js",
|
|
12
|
+
"./spi": "./dist/spi.js",
|
|
13
|
+
"./memory": "./dist/memory.js",
|
|
14
|
+
"./jdbc": "./dist/jdbc/index.js",
|
|
15
|
+
"./mysql": "./dist/jdbc/mysql.js",
|
|
16
|
+
"./postgres": "./dist/jdbc/postgres.js",
|
|
17
|
+
"./jdbc/ext": "./dist/jdbc/ext.js"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"test": "node --import tsx __tests__/spec.test.ts",
|
|
22
|
+
"demo": "node --import tsx demo/main.ts",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"workflow",
|
|
27
|
+
"engine",
|
|
28
|
+
"jeeflow",
|
|
29
|
+
"typescript"
|
|
30
|
+
],
|
|
31
|
+
"license": "Apache-2.0",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/express": "^4.17.25",
|
|
34
|
+
"@types/pg": "^8.20.3",
|
|
35
|
+
"express": "^4.22.2",
|
|
36
|
+
"tsx": "^4.19.0",
|
|
37
|
+
"typescript": "^5.5.0"
|
|
38
|
+
},
|
|
39
|
+
"optionalDependencies": {
|
|
40
|
+
"mysql2": "^3.11.0",
|
|
41
|
+
"pg": "^8.13.0"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"README.md"
|
|
47
|
+
]
|
|
48
48
|
}
|