@mldong/jeeflow 1.5.0 → 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 +9 -1
- package/dist/facade.js +163 -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;
|
|
@@ -17,6 +19,12 @@ export declare class JeeflowFacade {
|
|
|
17
19
|
private withdraw;
|
|
18
20
|
private execute;
|
|
19
21
|
private designPage;
|
|
22
|
+
/** 修改流程设计基本信息(对齐 boot3 ProcessDesignController.update,不写设计稿快照) */
|
|
23
|
+
private designUpdate;
|
|
24
|
+
/** 更新流程设计定义(设计稿保存,issues/08):content 快照入库 + 同步基本信息 + 置未部署 */
|
|
25
|
+
private designUpdateDefine;
|
|
26
|
+
/** 重新部署流程定义(issues/08):替换最新定义内容 + 置已部署(对齐 boot3 redeploy) */
|
|
27
|
+
private designRedeploy;
|
|
20
28
|
private designDetail;
|
|
21
29
|
private designSave;
|
|
22
30
|
private surrogatePage;
|
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);
|
|
@@ -48,6 +55,8 @@ export class JeeflowFacade {
|
|
|
48
55
|
case 'processDefine/deploy':
|
|
49
56
|
case 'processDesign/deploy':
|
|
50
57
|
return this.deploy(args, action === 'processDesign/deploy');
|
|
58
|
+
case 'processDesign/redeploy':
|
|
59
|
+
return this.designRedeploy(args);
|
|
51
60
|
case 'processDefine/redeploy':
|
|
52
61
|
return this.redeploy(args);
|
|
53
62
|
case 'processDefine/remove':
|
|
@@ -72,6 +81,10 @@ export class JeeflowFacade {
|
|
|
72
81
|
return this.designDetail(args);
|
|
73
82
|
case 'processDesign/save':
|
|
74
83
|
return this.designSave(args);
|
|
84
|
+
case 'processDesign/update':
|
|
85
|
+
return this.designUpdate(args);
|
|
86
|
+
case 'processDesign/updateDefine':
|
|
87
|
+
return this.designUpdateDefine(args);
|
|
75
88
|
case 'processDesign/remove':
|
|
76
89
|
return this.ext().removeDesign(toId(args.id));
|
|
77
90
|
case 'processSurrogate/page':
|
|
@@ -125,6 +138,11 @@ export class JeeflowFacade {
|
|
|
125
138
|
for (const task of doing) {
|
|
126
139
|
await this.repo.addTaskActor(task.id, [operator]);
|
|
127
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
|
+
}
|
|
128
146
|
await this.engine.executeProcessTask(task.id, operator, flowArgs);
|
|
129
147
|
}
|
|
130
148
|
return { processInstanceId: inst.id };
|
|
@@ -247,6 +265,100 @@ export class JeeflowFacade {
|
|
|
247
265
|
const [rows, total] = await this.ext().pageDesigns(toInt(args.pageNum ?? 1), toInt(args.pageSize ?? 10), undefined, parseMQuery(args));
|
|
248
266
|
return { rows, recordCount: total };
|
|
249
267
|
}
|
|
268
|
+
/** 修改流程设计基本信息(对齐 boot3 ProcessDesignController.update,不写设计稿快照) */
|
|
269
|
+
async designUpdate(args) {
|
|
270
|
+
const ext = this.ext();
|
|
271
|
+
const design = await ext.findDesignById(toId(args.id));
|
|
272
|
+
if (!design)
|
|
273
|
+
throw new Error('流程设计不存在');
|
|
274
|
+
if (args.name != null)
|
|
275
|
+
design.name = String(args.name);
|
|
276
|
+
if (args.displayName != null)
|
|
277
|
+
design.displayName = String(args.displayName);
|
|
278
|
+
if (args.type != null)
|
|
279
|
+
design.type = String(args.type);
|
|
280
|
+
if (args.icon != null)
|
|
281
|
+
design.icon = String(args.icon);
|
|
282
|
+
if (args.remark != null)
|
|
283
|
+
design.remark = String(args.remark);
|
|
284
|
+
design.updateUser = String(args.operator ?? 'system');
|
|
285
|
+
await ext.updateDesign(design);
|
|
286
|
+
return {};
|
|
287
|
+
}
|
|
288
|
+
/** 更新流程设计定义(设计稿保存,issues/08):content 快照入库 + 同步基本信息 + 置未部署 */
|
|
289
|
+
async designUpdateDefine(args) {
|
|
290
|
+
const ext = this.ext();
|
|
291
|
+
const designId = toId(args.processDesignId);
|
|
292
|
+
const design = await ext.findDesignById(designId);
|
|
293
|
+
if (!design)
|
|
294
|
+
throw new Error('流程设计不存在');
|
|
295
|
+
if (args.content == null)
|
|
296
|
+
throw new Error('content 缺失');
|
|
297
|
+
const content = toStr(args.content);
|
|
298
|
+
// 与最新一条相同则不重复入库(对齐 boot3 updateDefine)
|
|
299
|
+
const hisList = await ext.listDesignHis(designId);
|
|
300
|
+
if (hisList.length === 0 || toStr(hisList[0].content) !== content) {
|
|
301
|
+
await ext.saveDesignHis({
|
|
302
|
+
id: 0, processDesignId: designId, content,
|
|
303
|
+
createTime: new Date(), createUser: String(args.operator ?? 'system'),
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
// 同步设计基本信息(jsonObject 里的 name/displayName/type)+ 内容变更 → 未部署
|
|
307
|
+
try {
|
|
308
|
+
const flow = JSON.parse(content);
|
|
309
|
+
if (flow?.name)
|
|
310
|
+
design.name = flow.name;
|
|
311
|
+
if (flow?.displayName)
|
|
312
|
+
design.displayName = flow.displayName;
|
|
313
|
+
if (flow?.type)
|
|
314
|
+
design.type = flow.type;
|
|
315
|
+
}
|
|
316
|
+
catch { /* ignore */ }
|
|
317
|
+
design.isDeployed = 0;
|
|
318
|
+
design.updateUser = String(args.operator ?? 'system');
|
|
319
|
+
await ext.updateDesign(design);
|
|
320
|
+
return {};
|
|
321
|
+
}
|
|
322
|
+
/** 重新部署流程定义(issues/08):替换最新定义内容 + 置已部署(对齐 boot3 redeploy) */
|
|
323
|
+
async designRedeploy(args) {
|
|
324
|
+
const ext = this.ext();
|
|
325
|
+
const designId = toId(args.id);
|
|
326
|
+
const design = await ext.findDesignById(designId);
|
|
327
|
+
if (!design)
|
|
328
|
+
throw new Error('流程设计不存在');
|
|
329
|
+
const hisList = await ext.listDesignHis(designId);
|
|
330
|
+
if (hisList.length === 0)
|
|
331
|
+
throw new Error('流程设计没有内容,无法发布');
|
|
332
|
+
const content = toStr(hisList[0].content);
|
|
333
|
+
let flow;
|
|
334
|
+
try {
|
|
335
|
+
flow = JSON.parse(content);
|
|
336
|
+
}
|
|
337
|
+
catch (e) {
|
|
338
|
+
throw new Error('流程定义 JSON 解析失败: ' + String(e));
|
|
339
|
+
}
|
|
340
|
+
if (!flow?.name)
|
|
341
|
+
throw new Error('流程定义缺少 name');
|
|
342
|
+
// 按 name 取最新定义:有则替换内容(version 不变),无则新建(对齐 boot3 redeploy)
|
|
343
|
+
const last = await this.repo.findDefineByName(flow.name);
|
|
344
|
+
let defineId;
|
|
345
|
+
if (!last) {
|
|
346
|
+
defineId = await this.saveDeployedDefine(content, args);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
last.name = flow.name;
|
|
350
|
+
last.displayName = flow.displayName ?? '';
|
|
351
|
+
last.type = flow.type ?? '';
|
|
352
|
+
last.content = content;
|
|
353
|
+
last.updateUser = String(args.operator ?? 'system');
|
|
354
|
+
await this.repo.updateDefine(last);
|
|
355
|
+
defineId = last.id;
|
|
356
|
+
}
|
|
357
|
+
design.isDeployed = 1;
|
|
358
|
+
design.updateUser = String(args.operator ?? 'system');
|
|
359
|
+
await ext.updateDesign(design);
|
|
360
|
+
return { processDefineId: defineId };
|
|
361
|
+
}
|
|
250
362
|
async designDetail(args) {
|
|
251
363
|
const ext = this.ext();
|
|
252
364
|
const design = await ext.findDesignById(toId(args.id));
|
|
@@ -307,6 +419,9 @@ export class JeeflowFacade {
|
|
|
307
419
|
if (args.remark != null)
|
|
308
420
|
found.remark = String(args.remark);
|
|
309
421
|
found.updateUser = operator;
|
|
422
|
+
// 内容快照变更 → 置为未部署(对齐 boot3 updateDefine 语义,issues/08)
|
|
423
|
+
if (args.content != null)
|
|
424
|
+
found.isDeployed = 0;
|
|
310
425
|
await ext.updateDesign(found);
|
|
311
426
|
design = found;
|
|
312
427
|
}
|
|
@@ -441,7 +556,8 @@ export class JeeflowFacade {
|
|
|
441
556
|
return his.map(t => ({
|
|
442
557
|
taskName: t.taskName, displayName: t.displayName, taskType: t.taskType ?? null,
|
|
443
558
|
performType: t.performType ?? null, taskState: t.taskState, operator: t.actorId ?? '',
|
|
444
|
-
finishTime: t.finishTime
|
|
559
|
+
finishTime: fmtTime(t.finishTime), variable: t.variables ?? {},
|
|
560
|
+
ext: t.variables ?? {}, // issues/15:前端读 ext.tf_approvalComment
|
|
445
561
|
}));
|
|
446
562
|
}
|
|
447
563
|
async getAssigneeTextData(args) {
|
|
@@ -491,6 +607,7 @@ export class JeeflowFacade {
|
|
|
491
607
|
performType: task.performType ?? null, taskState: task.taskState,
|
|
492
608
|
operator: task.actorId ?? '', formKey: task.formKey ?? '',
|
|
493
609
|
taskActorIdList: actors, executable: task.isAllowed(operator),
|
|
610
|
+
taskFormData: formDataOf(task.variables, 'tf_'), // issues/15
|
|
494
611
|
};
|
|
495
612
|
// taskModel:流程定义中对应节点
|
|
496
613
|
const inst = await this.repo.findInstanceById(task.processInstanceId);
|
|
@@ -535,13 +652,13 @@ export class JeeflowFacade {
|
|
|
535
652
|
const inst = await this.repo.findInstanceById(task.processInstanceId);
|
|
536
653
|
if (!inst)
|
|
537
654
|
throw new Error('流程实例不存在');
|
|
538
|
-
// 模型候选解析:后继任务节点的 candidateUsers 配置
|
|
655
|
+
// 模型候选解析:后继任务节点的 candidateUsers / candidateGroups 配置
|
|
539
656
|
let candidates = [];
|
|
540
657
|
const def = await this.repo.findDefineById(inst.defineId);
|
|
541
658
|
if (def) {
|
|
542
659
|
try {
|
|
543
660
|
const flow = JSON.parse(toStr(def.content));
|
|
544
|
-
candidates = this.nextTaskCandidates(flow, task.taskName);
|
|
661
|
+
candidates = await this.nextTaskCandidates(flow, task.taskName);
|
|
545
662
|
}
|
|
546
663
|
catch { /* ignore */ }
|
|
547
664
|
}
|
|
@@ -555,10 +672,10 @@ export class JeeflowFacade {
|
|
|
555
672
|
const [rows, total] = await this.userSearch(args);
|
|
556
673
|
return { rows, recordCount: total };
|
|
557
674
|
}
|
|
558
|
-
nextTaskCandidates(flow, taskName) {
|
|
675
|
+
async nextTaskCandidates(flow, taskName) {
|
|
559
676
|
const result = [];
|
|
560
677
|
const visited = new Set();
|
|
561
|
-
const collect = (node) => {
|
|
678
|
+
const collect = async (node) => {
|
|
562
679
|
const v = node.properties?.candidateUsers;
|
|
563
680
|
if (v) {
|
|
564
681
|
for (const s of String(v).split(',')) {
|
|
@@ -567,8 +684,22 @@ export class JeeflowFacade {
|
|
|
567
684
|
result.push(t);
|
|
568
685
|
}
|
|
569
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
|
+
}
|
|
570
701
|
};
|
|
571
|
-
const walk = (nodeId) => {
|
|
702
|
+
const walk = async (nodeId) => {
|
|
572
703
|
if (visited.has(nodeId))
|
|
573
704
|
return;
|
|
574
705
|
visited.add(nodeId);
|
|
@@ -579,15 +710,15 @@ export class JeeflowFacade {
|
|
|
579
710
|
if (!target)
|
|
580
711
|
continue;
|
|
581
712
|
if (target.type === 'snaker:task' || target.type === 'snaker:custom') {
|
|
582
|
-
collect(target);
|
|
713
|
+
await collect(target);
|
|
583
714
|
continue;
|
|
584
715
|
}
|
|
585
716
|
if (['snaker:fork', 'snaker:join', 'snaker:decision'].includes(target.type)) {
|
|
586
|
-
walk(target.id);
|
|
717
|
+
await walk(target.id);
|
|
587
718
|
}
|
|
588
719
|
}
|
|
589
720
|
};
|
|
590
|
-
walk(taskName);
|
|
721
|
+
await walk(taskName);
|
|
591
722
|
return result;
|
|
592
723
|
}
|
|
593
724
|
async taskAddActor(args) {
|
|
@@ -657,6 +788,7 @@ export class JeeflowFacade {
|
|
|
657
788
|
createTime: t.createTime, createUser: t.createUser,
|
|
658
789
|
updateTime: t.updateTime, updateUser: t.updateUser,
|
|
659
790
|
taskActorIdList: t.actorIds ?? [],
|
|
791
|
+
taskFormData: formDataOf(t.variables, 'tf_'), // issues/15
|
|
660
792
|
};
|
|
661
793
|
const ext = { ...(t.variables ?? {}) };
|
|
662
794
|
const doing = t.taskState === TaskState.Doing;
|
|
@@ -666,15 +798,23 @@ export class JeeflowFacade {
|
|
|
666
798
|
if (doing)
|
|
667
799
|
activeTaskList.push(vo);
|
|
668
800
|
}
|
|
669
|
-
|
|
801
|
+
const data = {
|
|
670
802
|
id: inst.id, parentId: inst.parentId, processDefineId: inst.defineId,
|
|
671
803
|
state: inst.state, parentNodeName: inst.parentNodeName,
|
|
672
804
|
businessNo: inst.businessNo, operator: inst.operator,
|
|
673
|
-
variables: inst.variables,
|
|
805
|
+
variables: inst.variables,
|
|
806
|
+
formData: formDataOf(inst.variables, 'f_'), // issues/15
|
|
807
|
+
createTime: inst.createTime, createUser: inst.createUser,
|
|
674
808
|
jsonObject: graph, // issues/05
|
|
675
809
|
tasks,
|
|
676
810
|
activeTaskList,
|
|
677
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;
|
|
678
818
|
}
|
|
679
819
|
/** 流程 JSON 中第一个任务节点 id(issues/05-4 isFirstTaskNode 用) */
|
|
680
820
|
firstTaskNodeId(graph) {
|
|
@@ -711,6 +851,17 @@ export class JeeflowFacade {
|
|
|
711
851
|
}
|
|
712
852
|
}
|
|
713
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
|
+
}
|
|
714
865
|
/** Date → 'yyyy-MM-dd HH:mm:ss'(null/undefined → null) */
|
|
715
866
|
function fmtTime(v) {
|
|
716
867
|
if (v == null)
|
|
@@ -779,6 +930,7 @@ function taskRowToMap(r) {
|
|
|
779
930
|
processDefineName: r.processDefineName, processDefineDisplayName: r.processDefineDisplayName,
|
|
780
931
|
instanceVariable: r.instanceVariable, instanceCreateTime: fmtTime(r.instanceCreateTime),
|
|
781
932
|
ext, instanceExt, version: r.defineVersion,
|
|
933
|
+
taskFormData: formDataOf(r.variables, 'tf_'), // issues/15
|
|
782
934
|
};
|
|
783
935
|
}
|
|
784
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
|
}
|