@mldong/jeeflow 1.0.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/LICENSE +201 -0
- package/README.md +55 -0
- package/dist/engine.d.ts +46 -0
- package/dist/engine.js +400 -0
- package/dist/extensions.d.ts +40 -0
- package/dist/extensions.js +8 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/jdbc/index.d.ts +3 -0
- package/dist/jdbc/index.js +7 -0
- package/dist/jdbc/mysql.d.ts +19 -0
- package/dist/jdbc/mysql.js +41 -0
- package/dist/jdbc/postgres.d.ts +19 -0
- package/dist/jdbc/postgres.js +45 -0
- package/dist/jdbc/shared.d.ts +59 -0
- package/dist/jdbc/shared.js +317 -0
- package/dist/memory.d.ts +29 -0
- package/dist/memory.js +127 -0
- package/dist/model.d.ts +137 -0
- package/dist/model.js +170 -0
- package/dist/registry.d.ts +18 -0
- package/dist/registry.js +17 -0
- package/dist/spi.d.ts +27 -0
- package/dist/spi.js +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
// 共享 JDBC 仓储核心——SQL 逻辑与数据库无关。
|
|
2
|
+
//
|
|
3
|
+
// 设计(多数据库维护策略):
|
|
4
|
+
// - 本文件是**唯一维护点**:15 个仓储方法的 SQL 逻辑、行映射、ID 生成
|
|
5
|
+
// - SQL 占位符统一使用 `?`,由各数据库适配器(SqlAdapter)转换为自家风格
|
|
6
|
+
// (MySQL `?` 原生 / PostgreSQL `$n`)
|
|
7
|
+
// - 事务(spec §7.4):withTx 用 AsyncLocalStorage 绑定当前异步上下文的事务连接
|
|
8
|
+
//
|
|
9
|
+
// 新增数据库 = 写一个适配器(约 80 行):实现 SqlAdapter + 连接包装
|
|
10
|
+
// (execute/fetchOne/fetchAll/begin/commit/rollback)。参考 mysql.ts / postgres.ts。
|
|
11
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
12
|
+
import { ProcessInstance, ProcessTask } from '../model.js';
|
|
13
|
+
import { TaskState } from '../model.js';
|
|
14
|
+
// 当前异步上下文绑定的事务连接
|
|
15
|
+
const txStore = new AsyncLocalStorage();
|
|
16
|
+
/** 默认 ID 生成器:时间戳毫秒 + 同毫秒递增序号(对齐 Java nextId 默认实现) */
|
|
17
|
+
export class TsIDGenerator {
|
|
18
|
+
last = 0;
|
|
19
|
+
seq = 0;
|
|
20
|
+
nextId() {
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
if (now === this.last) {
|
|
23
|
+
this.seq++;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.last = now;
|
|
27
|
+
this.seq = 0;
|
|
28
|
+
}
|
|
29
|
+
return now * 1000 + this.seq;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** 把核心 SQL 的统一 `?` 占位符转换为适配器风格 */
|
|
33
|
+
export function convertPlaceholder(sql, style) {
|
|
34
|
+
if (style === '$n') {
|
|
35
|
+
let i = 0;
|
|
36
|
+
return sql.replace(/\?/g, () => `$${++i}`);
|
|
37
|
+
}
|
|
38
|
+
return sql; // '?' 原生
|
|
39
|
+
}
|
|
40
|
+
/** 生成 n 个 `?` 占位符(用于 IN 列表) */
|
|
41
|
+
export function repeatPh(n) {
|
|
42
|
+
return Array.from({ length: n }, () => '?').join(',');
|
|
43
|
+
}
|
|
44
|
+
export class JdbcRepository {
|
|
45
|
+
adapter;
|
|
46
|
+
idGen;
|
|
47
|
+
constructor(adapter, idGen = new TsIDGenerator()) {
|
|
48
|
+
this.adapter = adapter;
|
|
49
|
+
this.idGen = idGen;
|
|
50
|
+
}
|
|
51
|
+
sql(s) {
|
|
52
|
+
return convertPlaceholder(s, this.adapter.placeholder);
|
|
53
|
+
}
|
|
54
|
+
// ── 事务(spec §7.4:AsyncLocalStorage 绑定连接)─────────────────────────
|
|
55
|
+
async withTx(fn) {
|
|
56
|
+
const conn = await this.adapter.acquire();
|
|
57
|
+
try {
|
|
58
|
+
await conn.begin();
|
|
59
|
+
try {
|
|
60
|
+
const result = await txStore.run(conn, fn);
|
|
61
|
+
await conn.commit();
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
await conn.rollback();
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
await this.adapter.release(conn);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** 返回当前连接:有事务绑定用事务连接,否则从适配器获取 */
|
|
74
|
+
async c() {
|
|
75
|
+
return txStore.getStore() ?? (await this.adapter.acquire());
|
|
76
|
+
}
|
|
77
|
+
/** 归还非事务连接(事务连接由 withTx 统一释放) */
|
|
78
|
+
async done(conn) {
|
|
79
|
+
if (!txStore.getStore())
|
|
80
|
+
await this.adapter.release(conn);
|
|
81
|
+
}
|
|
82
|
+
// ── ProcessDefine ─────────────────────────────────────────────────────────
|
|
83
|
+
async findDefineById(id) {
|
|
84
|
+
const conn = await this.c();
|
|
85
|
+
try {
|
|
86
|
+
const row = await conn.fetchOne(this.sql('SELECT id, name, display_name, type, state, content, version, ' +
|
|
87
|
+
'create_time, create_user, update_time, update_user FROM wf_process_define WHERE id = ?'), [id]);
|
|
88
|
+
if (!row)
|
|
89
|
+
return null;
|
|
90
|
+
return {
|
|
91
|
+
id: row.id, name: row.name, displayName: row.display_name, type: row.type,
|
|
92
|
+
state: row.state,
|
|
93
|
+
content: row.content ? Buffer.from(row.content).toString('utf8') : '',
|
|
94
|
+
version: row.version, createTime: row.create_time, createUser: row.create_user,
|
|
95
|
+
updateTime: row.update_time, updateUser: row.update_user,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
await this.done(conn);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// ── ProcessInstance ───────────────────────────────────────────────────────
|
|
103
|
+
static INSTANCE_COLS = 'id, parent_id, process_define_id, state, parent_node_name, business_no, ' +
|
|
104
|
+
'operator, expire_time, variable, create_time, create_user, update_time, update_user';
|
|
105
|
+
async findInstanceById(id) {
|
|
106
|
+
const conn = await this.c();
|
|
107
|
+
try {
|
|
108
|
+
const row = await conn.fetchOne(this.sql(`SELECT ${JdbcRepository.INSTANCE_COLS} FROM wf_process_instance WHERE id = ?`), [id]);
|
|
109
|
+
if (!row)
|
|
110
|
+
return null;
|
|
111
|
+
const inst = new ProcessInstance({
|
|
112
|
+
id: row.id, parentId: row.parent_id, defineId: row.process_define_id,
|
|
113
|
+
state: row.state, parentNodeName: row.parent_node_name, businessNo: row.business_no,
|
|
114
|
+
operator: row.operator, expireTime: row.expire_time,
|
|
115
|
+
createTime: row.create_time, createUser: row.create_user,
|
|
116
|
+
updateTime: row.update_time, updateUser: row.update_user,
|
|
117
|
+
tasks: [],
|
|
118
|
+
});
|
|
119
|
+
inst.variables = row.variable ? JSON.parse(row.variable) : {};
|
|
120
|
+
return inst;
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
await this.done(conn);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async saveInstance(inst) {
|
|
127
|
+
const conn = await this.c();
|
|
128
|
+
try {
|
|
129
|
+
await conn.execute(this.sql('INSERT INTO wf_process_instance (id, parent_id, process_define_id, state, ' +
|
|
130
|
+
'parent_node_name, business_no, operator, expire_time, variable, ' +
|
|
131
|
+
'create_time, create_user, update_time, update_user) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)'), [inst.id, inst.parentId ?? null, inst.defineId, inst.state, inst.parentNodeName ?? '',
|
|
132
|
+
inst.businessNo ?? '', inst.operator, inst.expireTime ?? null,
|
|
133
|
+
JSON.stringify(inst.variables ?? {}), inst.createTime, inst.createUser,
|
|
134
|
+
inst.updateTime, inst.updateUser]);
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
await this.done(conn);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async updateInstance(inst) {
|
|
141
|
+
const conn = await this.c();
|
|
142
|
+
try {
|
|
143
|
+
await conn.execute(this.sql('UPDATE wf_process_instance SET state=?, parent_node_name=?, business_no=?, ' +
|
|
144
|
+
'operator=?, expire_time=?, variable=?, update_time=?, update_user=? WHERE id=?'), [inst.state, inst.parentNodeName ?? '', inst.businessNo ?? '', inst.operator,
|
|
145
|
+
inst.expireTime ?? null, JSON.stringify(inst.variables ?? {}),
|
|
146
|
+
inst.updateTime, inst.updateUser, inst.id]);
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
await this.done(conn);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// ── ProcessTask ───────────────────────────────────────────────────────────
|
|
153
|
+
static TASK_COLS = 'id, process_instance_id, task_name, display_name, task_type, perform_type, ' +
|
|
154
|
+
'task_state, operator, finish_time, expire_time, form_key, task_parent_id, ' +
|
|
155
|
+
'variable, create_time, create_user, update_time, update_user';
|
|
156
|
+
async findTaskById(taskId) {
|
|
157
|
+
const conn = await this.c();
|
|
158
|
+
try {
|
|
159
|
+
const row = await conn.fetchOne(this.sql(`SELECT ${JdbcRepository.TASK_COLS} FROM wf_process_task WHERE id = ?`), [taskId]);
|
|
160
|
+
if (!row)
|
|
161
|
+
return null;
|
|
162
|
+
const task = this.mapTask(row);
|
|
163
|
+
task.actorIds = await this.findTaskActors(taskId);
|
|
164
|
+
return task;
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
await this.done(conn);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async saveTask(task) {
|
|
171
|
+
const conn = await this.c();
|
|
172
|
+
try {
|
|
173
|
+
await conn.execute(this.sql('INSERT INTO wf_process_task (id, process_instance_id, task_name, display_name, ' +
|
|
174
|
+
'task_type, perform_type, task_state, operator, finish_time, expire_time, form_key, ' +
|
|
175
|
+
'task_parent_id, variable, create_time, create_user, update_time, update_user) ' +
|
|
176
|
+
'VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)'), [task.id, task.processInstanceId, task.taskName, task.displayName, task.taskType ?? 0,
|
|
177
|
+
task.performType ?? 0, task.taskState, task.actorId ?? '', task.finishTime ?? null,
|
|
178
|
+
task.expireTime ?? null, task.formKey ?? '', task.parentTaskId ?? null,
|
|
179
|
+
JSON.stringify(task.variables ?? {}), task.createTime, task.createUser,
|
|
180
|
+
task.updateTime, task.updateUser]);
|
|
181
|
+
await this.replaceTaskActors(conn, task.id, task.actorIds ?? []);
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
await this.done(conn);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async updateTask(task) {
|
|
188
|
+
const conn = await this.c();
|
|
189
|
+
try {
|
|
190
|
+
await conn.execute(this.sql('UPDATE wf_process_task SET task_state=?, operator=?, finish_time=?, expire_time=?, ' +
|
|
191
|
+
'variable=?, update_time=?, update_user=? WHERE id=?'), [task.taskState, task.actorId ?? '', task.finishTime ?? null, task.expireTime ?? null,
|
|
192
|
+
JSON.stringify(task.variables ?? {}), task.updateTime, task.updateUser, task.id]);
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
await this.done(conn);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async findTasksByState(instanceId, state, taskNames) {
|
|
199
|
+
let sql = `SELECT ${JdbcRepository.TASK_COLS} FROM wf_process_task WHERE process_instance_id = ?`;
|
|
200
|
+
const args = [instanceId];
|
|
201
|
+
if (state !== null) {
|
|
202
|
+
sql += ' AND task_state = ?';
|
|
203
|
+
args.push(state);
|
|
204
|
+
}
|
|
205
|
+
if (taskNames && taskNames.length > 0) {
|
|
206
|
+
sql += ` AND task_name IN (${repeatPh(taskNames.length)})`;
|
|
207
|
+
args.push(...taskNames);
|
|
208
|
+
}
|
|
209
|
+
sql += ' ORDER BY id ASC';
|
|
210
|
+
const conn = await this.c();
|
|
211
|
+
try {
|
|
212
|
+
const rows = await conn.fetchAll(this.sql(sql), args);
|
|
213
|
+
const tasks = rows.map(r => this.mapTask(r));
|
|
214
|
+
if (tasks.length > 0) {
|
|
215
|
+
const ids = tasks.map(t => t.id);
|
|
216
|
+
const actorRows = await conn.fetchAll(this.sql(`SELECT process_task_id, actor_id FROM wf_process_task_actor WHERE process_task_id IN (${repeatPh(ids.length)}) ORDER BY id ASC`), ids);
|
|
217
|
+
for (const t of tasks)
|
|
218
|
+
t.actorIds = [];
|
|
219
|
+
for (const r of actorRows) {
|
|
220
|
+
const t = tasks.find(x => x.id === r.process_task_id);
|
|
221
|
+
if (t)
|
|
222
|
+
t.actorIds.push(r.actor_id);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return tasks;
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
await this.done(conn);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
async findDoingTasks(instanceId, taskNames) {
|
|
232
|
+
return this.findTasksByState(instanceId, TaskState.Doing, taskNames);
|
|
233
|
+
}
|
|
234
|
+
async findDoneTasks(instanceId, taskNames) {
|
|
235
|
+
return this.findTasksByState(instanceId, TaskState.Done, taskNames);
|
|
236
|
+
}
|
|
237
|
+
async findHistoryTasks(instanceId) {
|
|
238
|
+
return this.findTasksByState(instanceId, null);
|
|
239
|
+
}
|
|
240
|
+
mapTask(row) {
|
|
241
|
+
const task = new ProcessTask({
|
|
242
|
+
id: row.id, processInstanceId: row.process_instance_id, taskName: row.task_name,
|
|
243
|
+
displayName: row.display_name, taskType: row.task_type, performType: row.perform_type,
|
|
244
|
+
taskState: row.task_state, actorId: row.operator, finishTime: row.finish_time,
|
|
245
|
+
expireTime: row.expire_time, formKey: row.form_key, parentTaskId: row.task_parent_id,
|
|
246
|
+
createTime: row.create_time, createUser: row.create_user,
|
|
247
|
+
updateTime: row.update_time, updateUser: row.update_user,
|
|
248
|
+
});
|
|
249
|
+
task.variables = row.variable ? JSON.parse(row.variable) : {};
|
|
250
|
+
task.actorIds = [];
|
|
251
|
+
return task;
|
|
252
|
+
}
|
|
253
|
+
// ── TaskActor ─────────────────────────────────────────────────────────────
|
|
254
|
+
async replaceTaskActors(conn, taskId, actors) {
|
|
255
|
+
await conn.execute(this.sql('DELETE FROM wf_process_task_actor WHERE process_task_id = ?'), [taskId]);
|
|
256
|
+
await this.insertTaskActors(conn, taskId, actors);
|
|
257
|
+
}
|
|
258
|
+
async insertTaskActors(conn, taskId, actors) {
|
|
259
|
+
const now = new Date();
|
|
260
|
+
for (const a of actors) {
|
|
261
|
+
await conn.execute(this.sql('INSERT INTO wf_process_task_actor (id, process_task_id, actor_id, create_time, create_user) VALUES (?,?,?,?,?)'), [this.idGen.nextId(), taskId, a, now, 'jeeflow']);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
async findTaskActors(taskId) {
|
|
265
|
+
const conn = await this.c();
|
|
266
|
+
try {
|
|
267
|
+
const rows = await conn.fetchAll(this.sql('SELECT actor_id FROM wf_process_task_actor WHERE process_task_id = ? ORDER BY id ASC'), [taskId]);
|
|
268
|
+
return rows.map(r => r.actor_id);
|
|
269
|
+
}
|
|
270
|
+
finally {
|
|
271
|
+
await this.done(conn);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async addTaskActor(taskId, actors) {
|
|
275
|
+
const conn = await this.c();
|
|
276
|
+
try {
|
|
277
|
+
await this.insertTaskActors(conn, taskId, actors);
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
await this.done(conn);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async removeTaskActor(taskId, actors) {
|
|
284
|
+
if (actors.length === 0)
|
|
285
|
+
return;
|
|
286
|
+
const conn = await this.c();
|
|
287
|
+
try {
|
|
288
|
+
await conn.execute(this.sql(`DELETE FROM wf_process_task_actor WHERE process_task_id = ? AND actor_id IN (${repeatPh(actors.length)})`), [taskId, ...actors]);
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
await this.done(conn);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// ── CcInstance(抄送)─────────────────────────────────────────────────────
|
|
295
|
+
async createCcInstance(instanceId, creator, ...actorIds) {
|
|
296
|
+
const conn = await this.c();
|
|
297
|
+
try {
|
|
298
|
+
const now = new Date();
|
|
299
|
+
for (const actorId of actorIds) {
|
|
300
|
+
await conn.execute(this.sql('INSERT INTO wf_process_cc_instance (id, process_instance_id, actor_id, state, ' +
|
|
301
|
+
'create_time, create_user, update_time, update_user) VALUES (?,?,?,0,?,?,?,?)'), [this.idGen.nextId(), instanceId, actorId, now, creator, now, creator]);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
finally {
|
|
305
|
+
await this.done(conn);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
async updateCcStatus(instanceId, actorId) {
|
|
309
|
+
const conn = await this.c();
|
|
310
|
+
try {
|
|
311
|
+
await conn.execute(this.sql('UPDATE wf_process_cc_instance SET state=1, update_time=? WHERE process_instance_id=? AND actor_id=?'), [new Date(), instanceId, actorId]);
|
|
312
|
+
}
|
|
313
|
+
finally {
|
|
314
|
+
await this.done(conn);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
package/dist/memory.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ProcessDefine } from './model.js';
|
|
2
|
+
import { type ProcessInstance, type ProcessTask } from './model.js';
|
|
3
|
+
import type { ProcessRepository } from './spi.js';
|
|
4
|
+
export declare class MemoryRepository implements ProcessRepository {
|
|
5
|
+
private defines;
|
|
6
|
+
private instances;
|
|
7
|
+
private tasks;
|
|
8
|
+
private actors;
|
|
9
|
+
private seq;
|
|
10
|
+
addDefine(def: ProcessDefine): void;
|
|
11
|
+
findDefineById(id: number): Promise<ProcessDefine | null>;
|
|
12
|
+
saveInstance(inst: ProcessInstance): Promise<void>;
|
|
13
|
+
updateInstance(inst: ProcessInstance): Promise<void>;
|
|
14
|
+
findInstanceById(id: number): Promise<ProcessInstance | null>;
|
|
15
|
+
findTaskById(id: number): Promise<ProcessTask | null>;
|
|
16
|
+
saveTask(task: ProcessTask): Promise<void>;
|
|
17
|
+
updateTask(task: ProcessTask): Promise<void>;
|
|
18
|
+
findDoingTasks(instanceId: number, taskNames?: string[]): Promise<ProcessTask[]>;
|
|
19
|
+
findDoneTasks(instanceId: number, _taskNames?: string[]): Promise<ProcessTask[]>;
|
|
20
|
+
findHistoryTasks(instanceId: number): Promise<ProcessTask[]>;
|
|
21
|
+
findTaskActors(taskId: number): Promise<string[]>;
|
|
22
|
+
addTaskActor(taskId: number, actors: string[]): Promise<void>;
|
|
23
|
+
removeTaskActor(taskId: number, actors: string[]): Promise<void>;
|
|
24
|
+
createCcInstance(..._args: any[]): Promise<void>;
|
|
25
|
+
updateCcStatus(..._args: any[]): Promise<void>;
|
|
26
|
+
allDefines(): ProcessDefine[];
|
|
27
|
+
allInstances(): ProcessInstance[];
|
|
28
|
+
allTasks(): ProcessTask[];
|
|
29
|
+
}
|
package/dist/memory.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { cloneInstance, cloneTask } from './model.js';
|
|
2
|
+
export class MemoryRepository {
|
|
3
|
+
defines = new Map();
|
|
4
|
+
instances = new Map();
|
|
5
|
+
tasks = new Map();
|
|
6
|
+
actors = new Map();
|
|
7
|
+
seq = 1;
|
|
8
|
+
addDefine(def) {
|
|
9
|
+
if (!def.id)
|
|
10
|
+
def.id = this.seq++;
|
|
11
|
+
this.defines.set(def.id, def);
|
|
12
|
+
}
|
|
13
|
+
async findDefineById(id) { return this.defines.get(id) ?? null; }
|
|
14
|
+
async saveInstance(inst) {
|
|
15
|
+
if (!inst.id)
|
|
16
|
+
inst.id = this.seq++;
|
|
17
|
+
const cp = cloneInstance(inst);
|
|
18
|
+
cp.tasks = [];
|
|
19
|
+
this.instances.set(inst.id, cp);
|
|
20
|
+
}
|
|
21
|
+
async updateInstance(inst) {
|
|
22
|
+
const cp = cloneInstance(inst);
|
|
23
|
+
cp.tasks = [];
|
|
24
|
+
this.instances.set(inst.id, cp);
|
|
25
|
+
}
|
|
26
|
+
async findInstanceById(id) {
|
|
27
|
+
const inst = this.instances.get(id);
|
|
28
|
+
if (!inst)
|
|
29
|
+
return null;
|
|
30
|
+
const cp = cloneInstance(inst);
|
|
31
|
+
cp.tasks = [];
|
|
32
|
+
for (const t of this.tasks.values()) {
|
|
33
|
+
if (t.processInstanceId === id) {
|
|
34
|
+
const tc = cloneTask(t);
|
|
35
|
+
tc.actorIds = this.actors.get(t.id) ?? t.actorIds;
|
|
36
|
+
cp.tasks.push(tc);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return cp;
|
|
40
|
+
}
|
|
41
|
+
async findTaskById(id) {
|
|
42
|
+
const t = this.tasks.get(id);
|
|
43
|
+
if (!t)
|
|
44
|
+
return null;
|
|
45
|
+
const cp = cloneTask(t);
|
|
46
|
+
cp.actorIds = this.actors.get(id) ?? t.actorIds;
|
|
47
|
+
return cp;
|
|
48
|
+
}
|
|
49
|
+
async saveTask(task) {
|
|
50
|
+
if (!task.id)
|
|
51
|
+
task.id = this.seq++;
|
|
52
|
+
const cp = cloneTask(task);
|
|
53
|
+
cp.actorIds = [];
|
|
54
|
+
this.tasks.set(task.id, cp);
|
|
55
|
+
if (task.actorIds.length)
|
|
56
|
+
this.actors.set(task.id, [...task.actorIds]);
|
|
57
|
+
}
|
|
58
|
+
async updateTask(task) {
|
|
59
|
+
const cp = cloneTask(task);
|
|
60
|
+
cp.actorIds = [];
|
|
61
|
+
this.tasks.set(task.id, cp);
|
|
62
|
+
if (task.actorIds.length)
|
|
63
|
+
this.actors.set(task.id, [...task.actorIds]);
|
|
64
|
+
}
|
|
65
|
+
async findDoingTasks(instanceId, taskNames) {
|
|
66
|
+
const result = [];
|
|
67
|
+
for (const t of this.tasks.values()) {
|
|
68
|
+
if (t.processInstanceId === instanceId && t.taskState === 10) {
|
|
69
|
+
if (taskNames?.length && !taskNames.includes(t.taskName))
|
|
70
|
+
continue;
|
|
71
|
+
const cp = cloneTask(t);
|
|
72
|
+
cp.actorIds = this.actors.get(t.id) ?? t.actorIds;
|
|
73
|
+
result.push(cp);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
async findDoneTasks(instanceId, _taskNames) {
|
|
79
|
+
const result = [];
|
|
80
|
+
for (const t of this.tasks.values()) {
|
|
81
|
+
if (t.processInstanceId === instanceId && t.taskState === 20) {
|
|
82
|
+
const cp = cloneTask(t);
|
|
83
|
+
cp.actorIds = this.actors.get(t.id) ?? t.actorIds;
|
|
84
|
+
result.push(cp);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
async findHistoryTasks(instanceId) {
|
|
90
|
+
const result = [];
|
|
91
|
+
for (const t of this.tasks.values()) {
|
|
92
|
+
if (t.processInstanceId === instanceId) {
|
|
93
|
+
const cp = cloneTask(t);
|
|
94
|
+
cp.actorIds = this.actors.get(t.id) ?? t.actorIds;
|
|
95
|
+
result.push(cp);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
async findTaskActors(taskId) { return this.actors.get(taskId) ?? []; }
|
|
101
|
+
async addTaskActor(taskId, actors) {
|
|
102
|
+
const existing = this.actors.get(taskId) ?? [];
|
|
103
|
+
const seen = new Set(existing);
|
|
104
|
+
for (const a of actors) {
|
|
105
|
+
if (!seen.has(a)) {
|
|
106
|
+
existing.push(a);
|
|
107
|
+
seen.add(a);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
this.actors.set(taskId, existing);
|
|
111
|
+
}
|
|
112
|
+
async removeTaskActor(taskId, actors) {
|
|
113
|
+
const remove = new Set(actors);
|
|
114
|
+
this.actors.set(taskId, (this.actors.get(taskId) ?? []).filter(a => !remove.has(a)));
|
|
115
|
+
}
|
|
116
|
+
async createCcInstance(..._args) { }
|
|
117
|
+
async updateCcStatus(..._args) { }
|
|
118
|
+
allDefines() { return [...this.defines.values()]; }
|
|
119
|
+
allInstances() { return [...this.instances.values()]; }
|
|
120
|
+
allTasks() {
|
|
121
|
+
return [...this.tasks.values()].map(t => {
|
|
122
|
+
const cp = cloneTask(t);
|
|
123
|
+
cp.actorIds = this.actors.get(t.id) ?? t.actorIds;
|
|
124
|
+
return cp;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
export interface FlowModel {
|
|
2
|
+
name: string;
|
|
3
|
+
displayName: string;
|
|
4
|
+
type: string;
|
|
5
|
+
nodes: FlowNode[];
|
|
6
|
+
edges: FlowEdge[];
|
|
7
|
+
}
|
|
8
|
+
export interface FlowNode {
|
|
9
|
+
id: string;
|
|
10
|
+
type: string;
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
properties: Record<string, any>;
|
|
14
|
+
text: {
|
|
15
|
+
value: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface FlowEdge {
|
|
19
|
+
id: string;
|
|
20
|
+
sourceNodeId: string;
|
|
21
|
+
targetNodeId: string;
|
|
22
|
+
properties: Record<string, any>;
|
|
23
|
+
text?: {
|
|
24
|
+
value: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export declare const TypeStart = "snaker:start";
|
|
28
|
+
export declare const TypeEnd = "snaker:end";
|
|
29
|
+
export declare const TypeTask = "snaker:task";
|
|
30
|
+
export declare const TypeDecision = "snaker:decision";
|
|
31
|
+
export declare const TypeFork = "snaker:fork";
|
|
32
|
+
export declare const TypeJoin = "snaker:join";
|
|
33
|
+
export declare const TypeCustom = "snaker:custom";
|
|
34
|
+
export interface ProcessDefine {
|
|
35
|
+
id: number;
|
|
36
|
+
name: string;
|
|
37
|
+
displayName: string;
|
|
38
|
+
type: string;
|
|
39
|
+
state: number;
|
|
40
|
+
content: Uint8Array | string;
|
|
41
|
+
version: number;
|
|
42
|
+
createTime: Date;
|
|
43
|
+
createUser: string;
|
|
44
|
+
updateTime: Date;
|
|
45
|
+
updateUser: string;
|
|
46
|
+
}
|
|
47
|
+
export declare enum InstanceState {
|
|
48
|
+
Doing = 10,
|
|
49
|
+
Done = 20,
|
|
50
|
+
Reject = 45
|
|
51
|
+
}
|
|
52
|
+
export declare enum TaskState {
|
|
53
|
+
Doing = 10,
|
|
54
|
+
Done = 20,
|
|
55
|
+
Abandoned = 99
|
|
56
|
+
}
|
|
57
|
+
export declare const BusinessNoKey = "BUSINESS_NO";
|
|
58
|
+
export declare class ProcessInstance {
|
|
59
|
+
id: number;
|
|
60
|
+
parentId?: number;
|
|
61
|
+
defineId: number;
|
|
62
|
+
state: InstanceState;
|
|
63
|
+
parentNodeName: string;
|
|
64
|
+
businessNo: string;
|
|
65
|
+
operator: string;
|
|
66
|
+
expireTime?: Date;
|
|
67
|
+
variables: Record<string, any>;
|
|
68
|
+
tasks: ProcessTask[];
|
|
69
|
+
createTime: Date;
|
|
70
|
+
createUser: string;
|
|
71
|
+
updateTime: Date;
|
|
72
|
+
updateUser: string;
|
|
73
|
+
constructor(data: any);
|
|
74
|
+
/** 工厂——创建流程实例 */
|
|
75
|
+
static create(id: number, defineId: number, operator: string, vars: Record<string, any>, now: Date): ProcessInstance;
|
|
76
|
+
/** 完成任务(子实体状态转换 + 实例变量合并) */
|
|
77
|
+
completeTask(task: ProcessTask, operator: string, vars: Record<string, any>, now: Date): void;
|
|
78
|
+
/** 废弃单个任务 */
|
|
79
|
+
abandonTask(task: ProcessTask, now: Date): void;
|
|
80
|
+
/** 废弃所有进行中任务,返回被废弃列表(供调用方持久化) */
|
|
81
|
+
abandonAllDoing(now: Date): ProcessTask[];
|
|
82
|
+
/** 流程完成 */
|
|
83
|
+
finish(now: Date): void;
|
|
84
|
+
/** 驳回流程 */
|
|
85
|
+
reject(now: Date): void;
|
|
86
|
+
/** 追加变量 */
|
|
87
|
+
addVariable(vars: Record<string, any>): void;
|
|
88
|
+
/** 获取进行中任务 */
|
|
89
|
+
getDoingTasks(): ProcessTask[];
|
|
90
|
+
/** 获取已完成任务 */
|
|
91
|
+
getDoneTasks(): ProcessTask[];
|
|
92
|
+
/** 所有任务是否都已完成(join 合并判断) */
|
|
93
|
+
isAllTasksFinished(): boolean;
|
|
94
|
+
/** 创建任务(子实体工厂) */
|
|
95
|
+
createTask(id: number, taskName: string, displayName: string, actor: string, operator: string, formKey: string, now: Date): ProcessTask;
|
|
96
|
+
}
|
|
97
|
+
export declare class ProcessTask {
|
|
98
|
+
id: number;
|
|
99
|
+
processInstanceId: number;
|
|
100
|
+
taskName: string;
|
|
101
|
+
displayName: string;
|
|
102
|
+
taskType: number;
|
|
103
|
+
performType: number;
|
|
104
|
+
taskState: TaskState;
|
|
105
|
+
actorId: string;
|
|
106
|
+
actorIds: string[];
|
|
107
|
+
finishTime?: Date;
|
|
108
|
+
expireTime?: Date;
|
|
109
|
+
formKey: string;
|
|
110
|
+
parentTaskId?: number;
|
|
111
|
+
variables: Record<string, any>;
|
|
112
|
+
createTime: Date;
|
|
113
|
+
createUser: string;
|
|
114
|
+
updateTime: Date;
|
|
115
|
+
updateUser: string;
|
|
116
|
+
constructor(data: any);
|
|
117
|
+
/** 完成任务 */
|
|
118
|
+
finish(operator: string, vars: Record<string, any>, now: Date): void;
|
|
119
|
+
/** 废弃任务 */
|
|
120
|
+
abandon(now: Date): void;
|
|
121
|
+
/** 是否进行中 */
|
|
122
|
+
isDoing(): boolean;
|
|
123
|
+
/** 是否已完成 */
|
|
124
|
+
isFinished(): boolean;
|
|
125
|
+
/** 操作人是否有权限处理 */
|
|
126
|
+
isAllowed(operator: string): boolean;
|
|
127
|
+
}
|
|
128
|
+
export declare function cloneInstance(inst: ProcessInstance): ProcessInstance;
|
|
129
|
+
export declare function cloneTask(task: ProcessTask): ProcessTask;
|
|
130
|
+
export interface UserInfo {
|
|
131
|
+
userId: string;
|
|
132
|
+
realName: string;
|
|
133
|
+
deptId?: string;
|
|
134
|
+
deptName?: string;
|
|
135
|
+
postId?: string;
|
|
136
|
+
postName?: string;
|
|
137
|
+
}
|