import { NfProcess } from "./entity/nfprocess"; import { NFTaskNode } from "./node/tasknode"; import { NfNode } from "./entity/nfnode"; import { NFError } from "./error"; import { NFNodeManager } from "./nodemanager"; import { INFNextParam, INFWaitState, NFNodeType, NFWaitKind } from "./types"; import { NFInclusiveNode } from "./node/inclusivenode"; import { NFParallelNode } from "./node/parallelnode"; import { NFUserTaskNode } from "./node/usertasknode"; import { NFEventBus } from "./eventbus"; import { NFWaitNode } from "./node/waitnode"; import { NFSubProcessNode } from "./node/subprocessnode"; import { EntityManager, getEntityManager, Query } from "relaen"; import { NFTaskUtil } from "./taskutil"; import { NFUserManager } from "./usermanager"; import { INFProcessDefCfg, NFNodeCfg } from "./types"; /** * 流程运行时实例:变量、等待、人工任务推进与生命周期。 */ export class NFProcess{ private static readonly GATEWAY_KEY = '__nf_gateway__'; private static readonly WAIT_KEY = '__nf_wait__'; private taskIds:string[] = []; private params:any = {}; private gatewayState:Record = {}; /** 等待中的节点状态 nodeId -> waitState */ private waitState:Record = {}; public entity:NfProcess; public nodeManager:NFNodeManager; public userId:number; /** * 创建流程运行时。 * * @param cfg - 节点数组,或带 `name` / `nodes` 的流程定义对象 * @param entity - 可选的持久化实体(测试可用内存 mock) * @throws `1000` 当无法从 `cfg` 解析出 nodes */ constructor(cfg:any,entity?:NfProcess){ this.entity = entity; this.nodeManager = new NFNodeManager(this, NFProcess.resolveNodes(cfg)); } /** * 从流程定义对象或节点数组中取出 `nodes`。 * * @param cfg - 节点数组或 `{ name, nodes }` * @returns 节点配置数组 * @throws `1000` 当既不是数组也不含 `nodes` */ static resolveNodes(cfg:NFNodeCfg[]|INFProcessDefCfg|any):NFNodeCfg[]{ if(Array.isArray(cfg)){ return cfg; } if(cfg && Array.isArray(cfg.nodes)){ return cfg.nodes; } throw new NFError("1000"); } /** * 从实体恢复变量、待办、当前节点、网关与等待态,并尝试唤醒到期等待。 * * @returns 无实体时直接返回 */ async restore(){ if(!this.entity){ return; } if(this.entity.variables){ try{ const data = JSON.parse(this.entity.variables); if(data && typeof data === 'object'){ this.gatewayState = data[NFProcess.GATEWAY_KEY] || {}; this.waitState = data[NFProcess.WAIT_KEY] || {}; delete data[NFProcess.GATEWAY_KEY]; delete data[NFProcess.WAIT_KEY]; this.params = data; } }catch(e){ this.params = {}; this.gatewayState = {}; this.waitState = {}; } } if(this.entity.taskIds){ try{ this.taskIds = JSON.parse(this.entity.taskIds) || []; }catch(e){ this.taskIds = []; } } if(this.entity.currentId){ const cur = this.nodeManager.getNodeById(this.entity.currentId); if(cur){ this.nodeManager.setCurrentNode(cur); } }else if(this.taskIds.length > 0){ const cur = this.nodeManager.getNodeById(this.taskIds[this.taskIds.length - 1]); if(cur){ this.nodeManager.setCurrentNode(cur); } } this.applyGatewayState(); // 恢复后尝试推进已到期的等待 await this.resumeWaits(); } /** * 读取流程变量。 * * @param key - 变量名;缺省返回全部变量对象 * @returns 指定值,或整个参数对象 */ getParam(key?:string){ if(!this.params){ return; } if(key){ return this.params[key]; } return this.params; } /** * 写入单个流程变量并持久化。 * * @param key - 变量名 * @param value - 变量值 */ async setParam(key:string,value:any){ if(!key){ return; } this.params[key] = value; await this.persistVariables(); } /** * 合并写入多个流程变量并持久化(供网关 `cond` 使用)。 * * @param params - 变量对象 */ async setParams(params:Record){ if(!params || typeof params !== 'object'){ return; } Object.assign(this.params, params); await this.persistVariables(); } /** * 将流程变量、网关状态、等待态写入实体 `variables`。 */ private async persistVariables(){ if(!this.entity){ return; } const data = Object.assign({}, this.params); if(this.gatewayState && Object.keys(this.gatewayState).length > 0){ data[NFProcess.GATEWAY_KEY] = this.gatewayState; } if(this.waitState && Object.keys(this.waitState).length > 0){ data[NFProcess.WAIT_KEY] = this.waitState; } this.entity.variables = JSON.stringify(data); await this.entity.save(); } /** * 保存网关节点运行时状态(汇聚计数等)。 * * @param nodeId - 网关定义 id * @param state - 可序列化状态 */ async saveGatewayNodeState(nodeId:string,state:any){ this.gatewayState[nodeId] = state; await this.persistVariables(); } /** * 清除指定网关的运行时状态。 * * @param nodeId - 网关定义 id */ async clearGatewayNodeState(nodeId:string){ if(this.gatewayState[nodeId] !== undefined){ delete this.gatewayState[nodeId]; await this.persistVariables(); } } /** * 将已持久化的网关状态套回并行 / 包容节点。 */ private applyGatewayState(){ for(let id of Object.keys(this.gatewayState)){ const node = this.nodeManager.getNodeById(id); const state = this.gatewayState[id]; if(!node || !state){ continue; } if(node instanceof NFInclusiveNode){ node.restoreState(state); }else if(node instanceof NFParallelNode){ node.restoreState(state); } } } /** * 登记等待态(定时 / 消息 / 信号 / 子流程)。 * * @param state - 等待状态 */ async addWait(state:INFWaitState){ this.waitState[state.nodeId] = state; this.entity.currentId = state.nodeId; this.nodeManager.setCurrentNode(this.nodeManager.getNodeById(state.nodeId)); await this.persistVariables(); } /** * 移除指定节点的等待态。 * * @param nodeId - 节点定义 id */ async removeWait(nodeId:string){ if(this.waitState[nodeId]){ delete this.waitState[nodeId]; await this.persistVariables(); } } /** * 获取单个等待态。 * * @param nodeId - 节点定义 id * @returns 等待状态;不存在时为 `undefined` */ getWait(nodeId:string):INFWaitState{ return this.waitState[nodeId]; } /** * 当前全部等待态。 * * @returns 等待状态数组 */ getWaits():INFWaitState[]{ return Object.values(this.waitState); } /** * 投递消息,唤醒对应 `wait`(`waitKind=message`)节点。 * * @param messageName - 消息名 * @param payload - 写入流程变量的载荷 * @returns 是否成功唤醒 * @throws `1400` 当没有节点在等待该消息 */ async message(messageName:string,payload?:Record){ const wait = Object.values(this.waitState).find( w => w.kind === NFWaitKind.MESSAGE && w.messageName === messageName ); if(!wait){ throw new NFError("1400",[messageName]); } if(payload){ await this.setParams(payload); } return await this.resumeWaitNode(wait.nodeId); } /** * 投递信号,唤醒对应 `wait`(`waitKind=signal`)节点。 * * @param signalName - 信号名 * @param payload - 写入流程变量的载荷 * @returns 是否成功唤醒 * @throws `1401` 当没有节点在等待该信号 */ async signal(signalName:string,payload?:Record){ const wait = Object.values(this.waitState).find( w => w.kind === NFWaitKind.SIGNAL && w.signalName === signalName ); if(!wait){ throw new NFError("1401",[signalName]); } if(payload){ await this.setParams(payload); } return await this.resumeWaitNode(wait.nodeId); } /** * 检查并恢复已到期定时器、已结束的子流程。 */ async resumeWaits(){ const now = Date.now(); const waits = Object.values(this.waitState); for(let w of waits){ if(w.kind === NFWaitKind.TIMER && w.dueTime != null && now >= w.dueTime){ await this.resumeWaitNode(w.nodeId); }else if(w.kind === 'subprocess'){ const node = this.nodeManager.getNodeById(w.nodeId); if(node instanceof NFSubProcessNode){ await node.resume(); } } } } /** * 唤醒单个等待 / 子流程节点并继续出口。 * * @param nodeId - 节点定义 id * @returns 是否完成唤醒 */ private async resumeWaitNode(nodeId:string){ const node = this.nodeManager.getNodeById(nodeId); if(!node){ return false; } await NFEventBus.emit('wait.resume',{ process:this, nodeId }); if(node instanceof NFWaitNode){ await node.resume(); return true; } if(node instanceof NFSubProcessNode){ return await node.resume(); } return false; } /** * 启动流程,从开始节点沿顺序流执行直到停下(人工任务 / 等待 / 结束)。 * * @throws `1001` 已结束 * @throws `1002` 已关闭 * @throws `1003` 无开始节点 */ async start(){ if(this.entity.endTime){ throw new NFError("1001"); } if(this.entity.deleteTime){ throw new NFError("1002"); } const node = this.nodeManager.getNodeByType(NFNodeType.START); if(!node){ throw new NFError("1003"); } this.entity.startTime = new Date().getTime(); await this.entity.save(); await NFEventBus.emit('process.start',{ process:this }); try{ await node.run(); }catch(e){ await NFEventBus.emit('process.error',{ process:this, error:e }); throw e; } } /** * 正常结束流程并清理运行态。 */ async end(){ this.entity.endTime = new Date().getTime(); if(this.entity.startTime){ this.entity.handleTime = this.entity.endTime - this.entity.startTime; } await this.clearRuntimeState(); await this.entity.save(); await NFEventBus.emit('process.end',{ process:this }); } /** * 强制关闭流程:结束未完成节点并清理待办。 * * @param reason - 关闭原因 * @returns 是否执行了关闭;已关闭时为 `false` */ async close(reason?:string){ if(this.entity.deleteTime){ return false; } if(this.entity.endTime){ this.entity.deleteReason = reason; this.entity.deleteTime = new Date().getTime(); await this.entity.save(); await NFEventBus.emit('process.close',{ process:this, reason }); return true; } const now = new Date().getTime(); this.entity.deleteTime = now; this.entity.deleteReason = reason; const openNodes = await NfNode.findMany({ nfProcess:this.entity.processId, endTime:null }); if(openNodes && openNodes.length > 0){ for(let n of openNodes){ n.endTime = now; if(n.startTime){ n.waitTime = now - n.startTime; } n.reason = reason || 'process closed'; await n.save(); } } await this.clearRuntimeState(); await this.entity.save(); await NFEventBus.emit('process.close',{ process:this, reason }); return true; } /** * 清空待办 id、网关与等待态(保留业务变量)。 */ private async clearRuntimeState(){ this.taskIds = []; this.gatewayState = {}; this.waitState = {}; this.entity.taskIds = '[]'; this.entity.currentId = null; const data = Object.assign({}, this.params); this.entity.variables = JSON.stringify(data); } /** * 登记当前人工任务并触发 `task.create`。 * * @param node - 人工 / 任务节点 */ async addTaskNode(node:NFTaskNode){ if(this.taskIds.indexOf(node.id) === -1){ this.taskIds.push(node.id); } this.entity.currentId = node.id; this.nodeManager.setCurrentNode(node); await this.updateTaskIds(); await NFEventBus.emit('task.create',{ process:this, node }); } /** * 从待办列表移除已完成任务定义 id。 * * @param nodeId - 节点定义 id */ async removeTaskNode(nodeId:string){ const ind = this.taskIds.indexOf(nodeId); if(ind !== -1){ this.taskIds.splice(ind,1); } this.entity.currentId = this.taskIds.length > 0 ? this.taskIds[this.taskIds.length - 1] : null; await this.updateTaskIds(); } /** * 推进当前节点。 * * 人工任务需 `userId`(校验办理权限后 `finish`);等待节点可省略 `userId`。 * `agree` / `reason` 只写入任务记录;网关条件请用 `cfg.variables` 或 {@link setParams}。 * * @param cfg - 办理参数 * @throws `1001` 已结束 * @throws `1002` 已关闭 * @throws `1004` 无待处理节点 * @throws `1206` 办理人工任务未提供 `userId` */ async next(cfg?:INFNextParam){ if(this.entity.endTime){ throw new NFError("1001"); } if(this.entity.deleteTime){ throw new NFError("1002"); } const nodeId = cfg?.id || this.entity.currentId || (this.taskIds.length > 0 ? this.taskIds[this.taskIds.length - 1] : null); if(!nodeId){ throw new NFError("1004"); } const node = this.nodeManager.getNodeById(nodeId); if(!node){ throw new NFError("1004"); } // 显式流程变量(与 agree 办理结果分离) if(cfg?.variables){ await this.setParams(cfg.variables); } const isUserTask = node instanceof NFUserTaskNode || node instanceof NFTaskNode; if(isUserTask){ if(cfg?.userId == null){ throw new NFError("1206"); } await this.assertTaskPermission(nodeId, cfg.userId); await (node).finish(cfg); await this.removeTaskNode(nodeId); await NFEventBus.emit('task.complete',{ process:this, node, cfg }); }else if(this.getWait(nodeId) && (node instanceof NFWaitNode || node instanceof NFSubProcessNode)){ try{ await this.resumeWaitNode(nodeId); }catch(e){ await NFEventBus.emit('process.error',{ process:this, error:e }); throw e; } return; } this.nodeManager.setCurrentNode(node); const seq = this.nodeManager.getSequenceNode(node.id); if(!seq){ throw new NFError("1100"); } try{ await seq.run(); }catch(e){ await NFEventBus.emit('process.error',{ process:this, error:e }); throw e; } } /** * 校验用户是否有权办理指定定义节点上的活动任务。 * * @param defId - 节点定义 id * @param userId - 办理人用户 id * @throws `1200` 无活动任务 * @throws `1205` 不是办理人 * @throws `1203` 不在候选人 / 候选组中 */ private async assertTaskPermission(defId:string,userId:number){ const em:EntityManager = await getEntityManager(); const query:Query = em.createQuery(NfNode.name); const node = await query.select("*") .where({ nfProcess:this.entity.processId, defId, endTime:null }) .orderBy({ nodeId:'desc' }) .getResult(); await em.close(); if(!node){ throw new NFError("1200"); } if(node.assignee != null){ if(Number(node.assignee) !== Number(userId)){ throw new NFError("1205"); } }else{ const groups = await NFUserManager.getGroupIdsByUserId(Number(userId)); if(!NFTaskUtil.isCandidate(node, Number(userId), groups)){ throw new NFError("1203"); } } } /** * 将当前待办定义 id 列表写入实体。 * * @param clear - 为 `true` 时先清空待办 */ async updateTaskIds(clear?:boolean){ if(clear){ this.taskIds = []; } this.entity.taskIds = JSON.stringify(this.taskIds); await this.entity.save(); } /** * 获取本实例节点办理轨迹(按开始时间、节点 id)。 * * @returns 历史节点列表 */ async getHistory():Promise{ if(!this.entity?.processId){ return []; } const em:EntityManager = await getEntityManager(); const query:Query = em.createQuery(NfNode.name); const nodes = await query.select(['*','nfUser.*']) .where({ nfProcess:this.entity.processId }) .orderBy({ startTime:'asc', nodeId:'asc' }) .getResultList(); await em.close(); return nodes || []; } /** * 按实例 id 查询全部节点及附件。 * * @param procId - 实例 id * @returns 节点实体列表 */ async getAllNodes(procId:number){ const nodes:NfNode[] = await NfNode.findMany({nfProcess:procId}); for(let n of nodes){ await n.getNfResources(); } return nodes; } }