@mldong/jeeflow 1.5.1 → 1.6.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/builtin.d.ts +59 -0
- package/dist/builtin.js +135 -0
- package/dist/engine.d.ts +1 -0
- package/dist/engine.js +5 -3
- package/dist/facade.d.ts +3 -1
- package/dist/facade.js +60 -11
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/registry.d.ts +2 -1
- package/dist/spi.d.ts +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { FlowNode, ProcessInstance } from './model.js';
|
|
2
|
+
import type { IAssignmentHandler } from './registry.js';
|
|
3
|
+
import type { OrgUserProvider, UserProvider } from './spi.js';
|
|
4
|
+
export declare const HANDLER_OPERATOR_ASSIGNMENT = "com.mldong.jeeflow.interceptor.impl.OperatorAssignmentHandler";
|
|
5
|
+
export declare const HANDLER_FORM_FIELD_ASSIGNEE = "com.mldong.jeeflow.interceptor.impl.FormFieldAssigneeHandler";
|
|
6
|
+
export declare const HANDLER_DEPT_LEADER: string;
|
|
7
|
+
export declare const HANDLER_DEPT_MAIN_LEADER: string;
|
|
8
|
+
export declare const HANDLER_APPLICANT_DEPT_LEADER: string;
|
|
9
|
+
export declare const HANDLER_APPLICANT_DEPT_MAIN_LEADER: string;
|
|
10
|
+
export declare const HANDLER_TASK_ROLE_ASSIGNEE: string;
|
|
11
|
+
/** 流程发起人(兜底 "apply.operator") */
|
|
12
|
+
export declare class OperatorAssignmentHandler implements IAssignmentHandler {
|
|
13
|
+
assign(_node: FlowNode, inst: ProcessInstance | null, _operator: string): Promise<string[]>;
|
|
14
|
+
}
|
|
15
|
+
/** 按表单字段值分配参与者:精确匹配 node.id → vars 字段;_数字 后缀去后缀再匹配。 */
|
|
16
|
+
export declare class FormFieldAssigneeHandler implements IAssignmentHandler {
|
|
17
|
+
assign(node: FlowNode, inst: ProcessInstance | null, _operator: string): Promise<string[]>;
|
|
18
|
+
private findFieldValue;
|
|
19
|
+
private collect;
|
|
20
|
+
private add;
|
|
21
|
+
}
|
|
22
|
+
/** 组织维度 handler 公共依赖 */
|
|
23
|
+
declare class OrgBase {
|
|
24
|
+
protected userProv?: UserProvider | undefined;
|
|
25
|
+
protected orgProv?: OrgUserProvider | undefined;
|
|
26
|
+
constructor(userProv?: UserProvider | undefined, orgProv?: OrgUserProvider | undefined);
|
|
27
|
+
protected byDept(deptId: string, main: boolean): Promise<string[]>;
|
|
28
|
+
protected deptIdOf(userId: string): Promise<string>;
|
|
29
|
+
}
|
|
30
|
+
/** 当前用户(任务操作人)部门领导 */
|
|
31
|
+
export declare class DeptLeaderAssignmentHandler extends OrgBase implements IAssignmentHandler {
|
|
32
|
+
assign(_node: FlowNode, _inst: ProcessInstance | null, operator: string): Promise<string[]>;
|
|
33
|
+
}
|
|
34
|
+
/** 当前用户(任务操作人)部门分管领导 */
|
|
35
|
+
export declare class DeptMainLeaderAssignmentHandler extends OrgBase implements IAssignmentHandler {
|
|
36
|
+
assign(_node: FlowNode, _inst: ProcessInstance | null, operator: string): Promise<string[]>;
|
|
37
|
+
}
|
|
38
|
+
/** 发起人部门领导 */
|
|
39
|
+
export declare class ApplicantDeptLeaderAssignmentHandler extends OrgBase implements IAssignmentHandler {
|
|
40
|
+
assign(_node: FlowNode, inst: ProcessInstance | null, _operator: string): Promise<string[]>;
|
|
41
|
+
}
|
|
42
|
+
/** 发起人部门分管领导 */
|
|
43
|
+
export declare class ApplicantDeptMainLeaderAssignmentHandler extends OrgBase implements IAssignmentHandler {
|
|
44
|
+
assign(_node: FlowNode, inst: ProcessInstance | null, _operator: string): Promise<string[]>;
|
|
45
|
+
}
|
|
46
|
+
/** 任务节点唯一编码关联角色(roleCode = 节点 id) */
|
|
47
|
+
export declare class TaskRoleAssigneeHandler implements IAssignmentHandler {
|
|
48
|
+
private orgProv?;
|
|
49
|
+
constructor(orgProv?: OrgUserProvider | undefined);
|
|
50
|
+
assign(node: FlowNode, _inst: ProcessInstance | null, _operator: string): Promise<string[]>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 注册内置通用参与者处理器到注册表(组织维度 handler 依赖 userProv/orgProv)。
|
|
54
|
+
* 与 HandlerRegistry.registerAssignment 组合使用。
|
|
55
|
+
*/
|
|
56
|
+
export declare function registerBuiltinAssignments(reg: {
|
|
57
|
+
registerAssignment(name: string, handler: IAssignmentHandler): void;
|
|
58
|
+
}, userProv?: UserProvider, orgProv?: OrgUserProvider): void;
|
|
59
|
+
export {};
|
package/dist/builtin.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// ─── 内置通用参与者处理器(issues/16)───────────────────────────────────────────
|
|
2
|
+
// 注册名与 Java 类全限定名一致,跨语言流程 JSON 通用(前端设计器配置天然兼容)。
|
|
3
|
+
// OperatorAssignmentHandler / FormFieldAssigneeHandler 为纯引擎语义,零外部依赖;
|
|
4
|
+
// 组织维度 handler 通过 OrgUserProvider SPI 取数据,业务方只实现数据接口。
|
|
5
|
+
export const HANDLER_OPERATOR_ASSIGNMENT = 'com.mldong.jeeflow.interceptor.impl.OperatorAssignmentHandler';
|
|
6
|
+
export const HANDLER_FORM_FIELD_ASSIGNEE = 'com.mldong.jeeflow.interceptor.impl.FormFieldAssigneeHandler';
|
|
7
|
+
const ORG_HANDLERS_PREFIX = 'com.mldong.jeeflow.interceptor.impl.OrgUserAssignmentHandlers$';
|
|
8
|
+
export const HANDLER_DEPT_LEADER = ORG_HANDLERS_PREFIX + 'DeptLeaderAssignmentHandler';
|
|
9
|
+
export const HANDLER_DEPT_MAIN_LEADER = ORG_HANDLERS_PREFIX + 'DeptMainLeaderAssignmentHandler';
|
|
10
|
+
export const HANDLER_APPLICANT_DEPT_LEADER = ORG_HANDLERS_PREFIX + 'ApplicantDeptLeaderAssignmentHandler';
|
|
11
|
+
export const HANDLER_APPLICANT_DEPT_MAIN_LEADER = ORG_HANDLERS_PREFIX + 'ApplicantDeptMainLeaderAssignmentHandler';
|
|
12
|
+
export const HANDLER_TASK_ROLE_ASSIGNEE = ORG_HANDLERS_PREFIX + 'TaskRoleAssigneeHandler';
|
|
13
|
+
// 表单字段编号后缀正则(task_01 → task)
|
|
14
|
+
const NUMBER_SUFFIX_PATTERN = /^(.+?)_(\d+)$/;
|
|
15
|
+
// ─── 纯引擎语义 ────────────────────────────────────────────────────────────────
|
|
16
|
+
/** 流程发起人(兜底 "apply.operator") */
|
|
17
|
+
export class OperatorAssignmentHandler {
|
|
18
|
+
async assign(_node, inst, _operator) {
|
|
19
|
+
if (inst?.operator)
|
|
20
|
+
return [inst.operator];
|
|
21
|
+
return ['apply.operator'];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** 按表单字段值分配参与者:精确匹配 node.id → vars 字段;_数字 后缀去后缀再匹配。 */
|
|
25
|
+
export class FormFieldAssigneeHandler {
|
|
26
|
+
async assign(node, inst, _operator) {
|
|
27
|
+
if (!inst || !node)
|
|
28
|
+
return [];
|
|
29
|
+
const value = this.findFieldValue(inst.variables, node.id);
|
|
30
|
+
if (value == null)
|
|
31
|
+
return [];
|
|
32
|
+
return this.collect(value);
|
|
33
|
+
}
|
|
34
|
+
findFieldValue(variables, fieldName) {
|
|
35
|
+
if (fieldName in variables)
|
|
36
|
+
return variables[fieldName];
|
|
37
|
+
const m = NUMBER_SUFFIX_PATTERN.exec(fieldName);
|
|
38
|
+
if (m && m[1] in variables)
|
|
39
|
+
return variables[m[1]];
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
collect(value) {
|
|
43
|
+
const ids = [];
|
|
44
|
+
if (Array.isArray(value)) {
|
|
45
|
+
for (const item of value)
|
|
46
|
+
this.add(ids, String(item));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.add(ids, String(value));
|
|
50
|
+
}
|
|
51
|
+
return ids;
|
|
52
|
+
}
|
|
53
|
+
add(ids, token) {
|
|
54
|
+
for (const s of token.split(',')) {
|
|
55
|
+
const t = s.trim();
|
|
56
|
+
if (t && !ids.includes(t))
|
|
57
|
+
ids.push(t);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// ─── 组织维度(OrgUserProvider SPI)───────────────────────────────────────────
|
|
62
|
+
/** 组织维度 handler 公共依赖 */
|
|
63
|
+
class OrgBase {
|
|
64
|
+
userProv;
|
|
65
|
+
orgProv;
|
|
66
|
+
constructor(userProv, orgProv) {
|
|
67
|
+
this.userProv = userProv;
|
|
68
|
+
this.orgProv = orgProv;
|
|
69
|
+
}
|
|
70
|
+
async byDept(deptId, main) {
|
|
71
|
+
if (!deptId || !this.orgProv)
|
|
72
|
+
return [];
|
|
73
|
+
return main ? await this.orgProv.findDeptMainLeaders(deptId) ?? [] : await this.orgProv.findDeptLeaders(deptId) ?? [];
|
|
74
|
+
}
|
|
75
|
+
async deptIdOf(userId) {
|
|
76
|
+
if (!userId || !this.userProv)
|
|
77
|
+
return '';
|
|
78
|
+
const u = await this.userProv.getUser(userId);
|
|
79
|
+
return u?.deptId ?? '';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** 当前用户(任务操作人)部门领导 */
|
|
83
|
+
export class DeptLeaderAssignmentHandler extends OrgBase {
|
|
84
|
+
async assign(_node, _inst, operator) {
|
|
85
|
+
return this.byDept(await this.deptIdOf(operator), false);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** 当前用户(任务操作人)部门分管领导 */
|
|
89
|
+
export class DeptMainLeaderAssignmentHandler extends OrgBase {
|
|
90
|
+
async assign(_node, _inst, operator) {
|
|
91
|
+
return this.byDept(await this.deptIdOf(operator), true);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** 发起人部门领导 */
|
|
95
|
+
export class ApplicantDeptLeaderAssignmentHandler extends OrgBase {
|
|
96
|
+
async assign(_node, inst, _operator) {
|
|
97
|
+
if (!inst)
|
|
98
|
+
return [];
|
|
99
|
+
return this.byDept(await this.deptIdOf(inst.operator), false);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** 发起人部门分管领导 */
|
|
103
|
+
export class ApplicantDeptMainLeaderAssignmentHandler extends OrgBase {
|
|
104
|
+
async assign(_node, inst, _operator) {
|
|
105
|
+
if (!inst)
|
|
106
|
+
return [];
|
|
107
|
+
return this.byDept(await this.deptIdOf(inst.operator), true);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** 任务节点唯一编码关联角色(roleCode = 节点 id) */
|
|
111
|
+
export class TaskRoleAssigneeHandler {
|
|
112
|
+
orgProv;
|
|
113
|
+
constructor(orgProv) {
|
|
114
|
+
this.orgProv = orgProv;
|
|
115
|
+
}
|
|
116
|
+
async assign(node, _inst, _operator) {
|
|
117
|
+
if (!node || !this.orgProv)
|
|
118
|
+
return [];
|
|
119
|
+
return await this.orgProv.findByRole(node.id) ?? [];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// ─── 注册 ──────────────────────────────────────────────────────────────────────
|
|
123
|
+
/**
|
|
124
|
+
* 注册内置通用参与者处理器到注册表(组织维度 handler 依赖 userProv/orgProv)。
|
|
125
|
+
* 与 HandlerRegistry.registerAssignment 组合使用。
|
|
126
|
+
*/
|
|
127
|
+
export function registerBuiltinAssignments(reg, userProv, orgProv) {
|
|
128
|
+
reg.registerAssignment(HANDLER_OPERATOR_ASSIGNMENT, new OperatorAssignmentHandler());
|
|
129
|
+
reg.registerAssignment(HANDLER_FORM_FIELD_ASSIGNEE, new FormFieldAssigneeHandler());
|
|
130
|
+
reg.registerAssignment(HANDLER_DEPT_LEADER, new DeptLeaderAssignmentHandler(userProv, orgProv));
|
|
131
|
+
reg.registerAssignment(HANDLER_DEPT_MAIN_LEADER, new DeptMainLeaderAssignmentHandler(userProv, orgProv));
|
|
132
|
+
reg.registerAssignment(HANDLER_APPLICANT_DEPT_LEADER, new ApplicantDeptLeaderAssignmentHandler(userProv, orgProv));
|
|
133
|
+
reg.registerAssignment(HANDLER_APPLICANT_DEPT_MAIN_LEADER, new ApplicantDeptMainLeaderAssignmentHandler(userProv, orgProv));
|
|
134
|
+
reg.registerAssignment(HANDLER_TASK_ROLE_ASSIGNEE, new TaskRoleAssigneeHandler(orgProv));
|
|
135
|
+
}
|
package/dist/engine.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare const KeyDeptName = "u_deptName";
|
|
|
11
11
|
export declare const KeyPostID = "u_postId";
|
|
12
12
|
export declare const KeyPostName = "u_postName";
|
|
13
13
|
export declare const KeyNextNodeOperator = "tf_nextNodeOperator";
|
|
14
|
+
export declare const KeyProcessStartNextNodeOperator = "f_nextNodeOperator";
|
|
14
15
|
export declare const KeyAutoExecute = "flow.auto";
|
|
15
16
|
export declare const KeyAdminID = "flow.admin";
|
|
16
17
|
export interface Engine {
|
package/dist/engine.js
CHANGED
|
@@ -10,6 +10,8 @@ export const KeyPostID = 'u_postId';
|
|
|
10
10
|
export const KeyPostName = 'u_postName';
|
|
11
11
|
// v1.0.1:下一节点处理人(对齐 boot3 tf_nextNodeOperator)
|
|
12
12
|
export const KeyNextNodeOperator = 'tf_nextNodeOperator';
|
|
13
|
+
// v1.6.0:流程启动时预指派人(对齐 boot3 f_nextNodeOperator)——startAndExecute 时转换为 tf_
|
|
14
|
+
export const KeyProcessStartNextNodeOperator = 'f_nextNodeOperator';
|
|
13
15
|
// v1.0.1:系统代执行 / 超级管理员(对齐 boot3 FlowConst)
|
|
14
16
|
export const KeyAutoExecute = 'flow.auto';
|
|
15
17
|
export const KeyAdminID = 'flow.admin';
|
|
@@ -303,7 +305,7 @@ export class EngineImpl {
|
|
|
303
305
|
}
|
|
304
306
|
}
|
|
305
307
|
async createTask(node, inst, operator, vars) {
|
|
306
|
-
const actors = await this.resolveActors(node, inst, vars);
|
|
308
|
+
const actors = await this.resolveActors(node, inst, operator, vars);
|
|
307
309
|
if (!actors.length)
|
|
308
310
|
return;
|
|
309
311
|
const performType = parseInt(String(node.properties?.performType ?? '0'));
|
|
@@ -338,14 +340,14 @@ export class EngineImpl {
|
|
|
338
340
|
nt.actorIds = actors;
|
|
339
341
|
await this.repo.saveTask(nt);
|
|
340
342
|
}
|
|
341
|
-
async resolveActors(node, inst, vars) {
|
|
343
|
+
async resolveActors(node, inst, operator, vars) {
|
|
342
344
|
// 1a. Registry 按名称解析(推荐)
|
|
343
345
|
if (this.registry) {
|
|
344
346
|
const handlerName = node.properties?.assignmentHandler ?? '';
|
|
345
347
|
if (handlerName) {
|
|
346
348
|
const h = this.registry.resolveAssignment(handlerName);
|
|
347
349
|
if (h)
|
|
348
|
-
return await h.assign(node, inst);
|
|
350
|
+
return await h.assign(node, inst, operator);
|
|
349
351
|
}
|
|
350
352
|
}
|
|
351
353
|
// 1b. Extensions 兼容
|
package/dist/facade.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ProcessExtRepository, ProcessRepository } from './spi.js';
|
|
1
|
+
import type { OrgUserProvider, ProcessExtRepository, ProcessRepository } from './spi.js';
|
|
2
2
|
import type { EngineImpl } from './engine.js';
|
|
3
3
|
export type UserSearch = (query: Record<string, any>) => Promise<[Record<string, any>[], number]> | [Record<string, any>[], number];
|
|
4
4
|
export declare class JeeflowFacade {
|
|
@@ -6,8 +6,10 @@ export declare class JeeflowFacade {
|
|
|
6
6
|
private readonly repo;
|
|
7
7
|
private readonly extRepo?;
|
|
8
8
|
private userSearch?;
|
|
9
|
+
private orgProv?;
|
|
9
10
|
constructor(engine: EngineImpl, repo: ProcessRepository, extRepo?: ProcessExtRepository | undefined);
|
|
10
11
|
setUserSearch(fn: UserSearch): this;
|
|
12
|
+
setOrgProvider(orgProv: OrgUserProvider): this;
|
|
11
13
|
flow(action: string, args?: Record<string, any>): Promise<Record<string, any>>;
|
|
12
14
|
private dispatch;
|
|
13
15
|
private startAndExecute;
|
package/dist/facade.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// action(boot2/boot3 端点短名)路由。返回统一结构 {code, msg, data}
|
|
5
5
|
// (code=0 成功 / 99999999 失败)。操作人约定:args.operator 显式传入。
|
|
6
6
|
import { TaskState, } from './model.js';
|
|
7
|
+
import { KeyNextNodeOperator, KeyProcessStartNextNodeOperator } from './engine.js';
|
|
7
8
|
// submitType 枚举(对齐 boot3)
|
|
8
9
|
const SUBMIT_APPLY = 0;
|
|
9
10
|
const SUBMIT_AGREE = 1;
|
|
@@ -17,6 +18,7 @@ export class JeeflowFacade {
|
|
|
17
18
|
repo;
|
|
18
19
|
extRepo;
|
|
19
20
|
userSearch;
|
|
21
|
+
orgProv;
|
|
20
22
|
constructor(engine, repo, extRepo) {
|
|
21
23
|
this.engine = engine;
|
|
22
24
|
this.repo = repo;
|
|
@@ -27,6 +29,11 @@ export class JeeflowFacade {
|
|
|
27
29
|
this.userSearch = fn;
|
|
28
30
|
return this;
|
|
29
31
|
}
|
|
32
|
+
// 注入组织用户提供者(candidatePage candidateGroups 角色取人,v1.6.0)
|
|
33
|
+
setOrgProvider(orgProv) {
|
|
34
|
+
this.orgProv = orgProv;
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
30
37
|
async flow(action, args = {}) {
|
|
31
38
|
try {
|
|
32
39
|
const data = await this.dispatch(action, args);
|
|
@@ -131,6 +138,11 @@ export class JeeflowFacade {
|
|
|
131
138
|
for (const task of doing) {
|
|
132
139
|
await this.repo.addTaskActor(task.id, [operator]);
|
|
133
140
|
flowArgs.submitType = SUBMIT_APPLY;
|
|
141
|
+
// 对齐 boot3:f_nextNodeOperator(发起时预指派人)→ tf_nextNodeOperator(引擎执行参数)
|
|
142
|
+
const startNextOp = flowArgs[KeyProcessStartNextNodeOperator];
|
|
143
|
+
if (startNextOp != null && String(startNextOp) !== '') {
|
|
144
|
+
flowArgs[KeyNextNodeOperator] = startNextOp;
|
|
145
|
+
}
|
|
134
146
|
await this.engine.executeProcessTask(task.id, operator, flowArgs);
|
|
135
147
|
}
|
|
136
148
|
return { processInstanceId: inst.id };
|
|
@@ -544,7 +556,8 @@ export class JeeflowFacade {
|
|
|
544
556
|
return his.map(t => ({
|
|
545
557
|
taskName: t.taskName, displayName: t.displayName, taskType: t.taskType ?? null,
|
|
546
558
|
performType: t.performType ?? null, taskState: t.taskState, operator: t.actorId ?? '',
|
|
547
|
-
finishTime: t.finishTime
|
|
559
|
+
finishTime: fmtTime(t.finishTime), variable: t.variables ?? {},
|
|
560
|
+
ext: t.variables ?? {}, // issues/15:前端读 ext.tf_approvalComment
|
|
548
561
|
}));
|
|
549
562
|
}
|
|
550
563
|
async getAssigneeTextData(args) {
|
|
@@ -594,6 +607,7 @@ export class JeeflowFacade {
|
|
|
594
607
|
performType: task.performType ?? null, taskState: task.taskState,
|
|
595
608
|
operator: task.actorId ?? '', formKey: task.formKey ?? '',
|
|
596
609
|
taskActorIdList: actors, executable: task.isAllowed(operator),
|
|
610
|
+
taskFormData: formDataOf(task.variables, 'tf_'), // issues/15
|
|
597
611
|
};
|
|
598
612
|
// taskModel:流程定义中对应节点
|
|
599
613
|
const inst = await this.repo.findInstanceById(task.processInstanceId);
|
|
@@ -638,13 +652,13 @@ export class JeeflowFacade {
|
|
|
638
652
|
const inst = await this.repo.findInstanceById(task.processInstanceId);
|
|
639
653
|
if (!inst)
|
|
640
654
|
throw new Error('流程实例不存在');
|
|
641
|
-
// 模型候选解析:后继任务节点的 candidateUsers 配置
|
|
655
|
+
// 模型候选解析:后继任务节点的 candidateUsers / candidateGroups 配置
|
|
642
656
|
let candidates = [];
|
|
643
657
|
const def = await this.repo.findDefineById(inst.defineId);
|
|
644
658
|
if (def) {
|
|
645
659
|
try {
|
|
646
660
|
const flow = JSON.parse(toStr(def.content));
|
|
647
|
-
candidates = this.nextTaskCandidates(flow, task.taskName);
|
|
661
|
+
candidates = await this.nextTaskCandidates(flow, task.taskName);
|
|
648
662
|
}
|
|
649
663
|
catch { /* ignore */ }
|
|
650
664
|
}
|
|
@@ -658,10 +672,10 @@ export class JeeflowFacade {
|
|
|
658
672
|
const [rows, total] = await this.userSearch(args);
|
|
659
673
|
return { rows, recordCount: total };
|
|
660
674
|
}
|
|
661
|
-
nextTaskCandidates(flow, taskName) {
|
|
675
|
+
async nextTaskCandidates(flow, taskName) {
|
|
662
676
|
const result = [];
|
|
663
677
|
const visited = new Set();
|
|
664
|
-
const collect = (node) => {
|
|
678
|
+
const collect = async (node) => {
|
|
665
679
|
const v = node.properties?.candidateUsers;
|
|
666
680
|
if (v) {
|
|
667
681
|
for (const s of String(v).split(',')) {
|
|
@@ -670,8 +684,22 @@ export class JeeflowFacade {
|
|
|
670
684
|
result.push(t);
|
|
671
685
|
}
|
|
672
686
|
}
|
|
687
|
+
// candidateGroups:按角色取人(v1.6.0,对齐 boot4 GlobalCandidateHandler)
|
|
688
|
+
const g = node.properties?.candidateGroups;
|
|
689
|
+
if (g && this.orgProv) {
|
|
690
|
+
for (const rc of String(g).split(',')) {
|
|
691
|
+
const role = rc.trim();
|
|
692
|
+
if (!role)
|
|
693
|
+
continue;
|
|
694
|
+
const ids = await this.orgProv.findByRole(role);
|
|
695
|
+
for (const uid of ids ?? []) {
|
|
696
|
+
if (uid && !result.includes(uid))
|
|
697
|
+
result.push(uid);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
673
701
|
};
|
|
674
|
-
const walk = (nodeId) => {
|
|
702
|
+
const walk = async (nodeId) => {
|
|
675
703
|
if (visited.has(nodeId))
|
|
676
704
|
return;
|
|
677
705
|
visited.add(nodeId);
|
|
@@ -682,15 +710,15 @@ export class JeeflowFacade {
|
|
|
682
710
|
if (!target)
|
|
683
711
|
continue;
|
|
684
712
|
if (target.type === 'snaker:task' || target.type === 'snaker:custom') {
|
|
685
|
-
collect(target);
|
|
713
|
+
await collect(target);
|
|
686
714
|
continue;
|
|
687
715
|
}
|
|
688
716
|
if (['snaker:fork', 'snaker:join', 'snaker:decision'].includes(target.type)) {
|
|
689
|
-
walk(target.id);
|
|
717
|
+
await walk(target.id);
|
|
690
718
|
}
|
|
691
719
|
}
|
|
692
720
|
};
|
|
693
|
-
walk(taskName);
|
|
721
|
+
await walk(taskName);
|
|
694
722
|
return result;
|
|
695
723
|
}
|
|
696
724
|
async taskAddActor(args) {
|
|
@@ -760,6 +788,7 @@ export class JeeflowFacade {
|
|
|
760
788
|
createTime: t.createTime, createUser: t.createUser,
|
|
761
789
|
updateTime: t.updateTime, updateUser: t.updateUser,
|
|
762
790
|
taskActorIdList: t.actorIds ?? [],
|
|
791
|
+
taskFormData: formDataOf(t.variables, 'tf_'), // issues/15
|
|
763
792
|
};
|
|
764
793
|
const ext = { ...(t.variables ?? {}) };
|
|
765
794
|
const doing = t.taskState === TaskState.Doing;
|
|
@@ -769,15 +798,23 @@ export class JeeflowFacade {
|
|
|
769
798
|
if (doing)
|
|
770
799
|
activeTaskList.push(vo);
|
|
771
800
|
}
|
|
772
|
-
|
|
801
|
+
const data = {
|
|
773
802
|
id: inst.id, parentId: inst.parentId, processDefineId: inst.defineId,
|
|
774
803
|
state: inst.state, parentNodeName: inst.parentNodeName,
|
|
775
804
|
businessNo: inst.businessNo, operator: inst.operator,
|
|
776
|
-
variables: inst.variables,
|
|
805
|
+
variables: inst.variables,
|
|
806
|
+
formData: formDataOf(inst.variables, 'f_'), // issues/15
|
|
807
|
+
createTime: inst.createTime, createUser: inst.createUser,
|
|
777
808
|
jsonObject: graph, // issues/05
|
|
778
809
|
tasks,
|
|
779
810
|
activeTaskList,
|
|
780
811
|
};
|
|
812
|
+
if (def0) {
|
|
813
|
+
data.displayName = def0.displayName; // issues/15
|
|
814
|
+
data.name = def0.name;
|
|
815
|
+
data.version = def0.version;
|
|
816
|
+
}
|
|
817
|
+
return data;
|
|
781
818
|
}
|
|
782
819
|
/** 流程 JSON 中第一个任务节点 id(issues/05-4 isFirstTaskNode 用) */
|
|
783
820
|
firstTaskNodeId(graph) {
|
|
@@ -814,6 +851,17 @@ export class JeeflowFacade {
|
|
|
814
851
|
}
|
|
815
852
|
}
|
|
816
853
|
// ── 行转 Map(issues/05-2 列表字段契约 + 05-3 时间格式)─────────────────────
|
|
854
|
+
/** issues/15:取 vars 中 prefix 前缀字段,输出「带前缀 + 去前缀副本」(对齐 boot3 getFormData) */
|
|
855
|
+
function formDataOf(vars, prefix) {
|
|
856
|
+
const out = {};
|
|
857
|
+
for (const [k, v] of Object.entries(vars ?? {})) {
|
|
858
|
+
if (k.startsWith(prefix)) {
|
|
859
|
+
out[k] = v;
|
|
860
|
+
out[k.slice(prefix.length)] = v;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return out;
|
|
864
|
+
}
|
|
817
865
|
/** Date → 'yyyy-MM-dd HH:mm:ss'(null/undefined → null) */
|
|
818
866
|
function fmtTime(v) {
|
|
819
867
|
if (v == null)
|
|
@@ -882,6 +930,7 @@ function taskRowToMap(r) {
|
|
|
882
930
|
processDefineName: r.processDefineName, processDefineDisplayName: r.processDefineDisplayName,
|
|
883
931
|
instanceVariable: r.instanceVariable, instanceCreateTime: fmtTime(r.instanceCreateTime),
|
|
884
932
|
ext, instanceExt, version: r.defineVersion,
|
|
933
|
+
taskFormData: formDataOf(r.variables, 'tf_'), // issues/15
|
|
885
934
|
};
|
|
886
935
|
}
|
|
887
936
|
// ── 工具 ──────────────────────────────────────────────────────────────────────
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { MemoryRepository } from './memory.js';
|
|
|
3
3
|
export { HandlerRegistry, type IAssignmentHandler, type IDecisionHandler, type HandlerMeta } from './registry.js';
|
|
4
4
|
export { enumDict, enumDictKeys, type DictItem } from './metadata.js';
|
|
5
5
|
export * from './model.js';
|
|
6
|
-
export type { ProcessRepository, UserProvider, IDGenerator, ExpressionEvaluator } from './spi.js';
|
|
6
|
+
export type { ProcessRepository, UserProvider, OrgUserProvider, IDGenerator, ExpressionEvaluator } from './spi.js';
|
|
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';
|
package/dist/index.js
CHANGED
|
@@ -3,3 +3,4 @@ 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 { 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';
|
package/dist/registry.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { FlowNode, ProcessInstance } from './model.js';
|
|
2
2
|
/** 参与者指派处理器接口——对标 Java AssignmentHandler */
|
|
3
3
|
export interface IAssignmentHandler {
|
|
4
|
-
|
|
4
|
+
/** 返回参与者列表(operator: 当前任务操作人,issues/16 对齐 Java Execution.getOperator) */
|
|
5
|
+
assign(node: FlowNode, inst: ProcessInstance, operator: string): string[] | Promise<string[]>;
|
|
5
6
|
}
|
|
6
7
|
/** 决策处理器接口——对标 Java DecisionHandler */
|
|
7
8
|
export interface IDecisionHandler {
|
package/dist/spi.d.ts
CHANGED
|
@@ -49,6 +49,14 @@ export interface ProcessRepository {
|
|
|
49
49
|
export interface UserProvider {
|
|
50
50
|
getUser(userId: string): Promise<UserInfo | null>;
|
|
51
51
|
}
|
|
52
|
+
export interface OrgUserProvider {
|
|
53
|
+
/** 部门领导(deptId → 领导 userId 列表) */
|
|
54
|
+
findDeptLeaders(deptId: string): Promise<string[]>;
|
|
55
|
+
/** 部门分管领导(deptId → 分管领导 userId 列表) */
|
|
56
|
+
findDeptMainLeaders(deptId: string): Promise<string[]>;
|
|
57
|
+
/** 按角色取人(roleCode → userId 列表) */
|
|
58
|
+
findByRole(roleCode: string): Promise<string[]>;
|
|
59
|
+
}
|
|
52
60
|
export interface IDGenerator {
|
|
53
61
|
nextId(): number;
|
|
54
62
|
}
|