import { NfDefProcess } from "./entity/nfdefprocess"; import { NfProcess } from "./entity/nfprocess"; import { NfNode } from "./entity/nfnode"; import { NFProcess } from "./process"; import { EntityManager, getEntityManager, Query } from "relaen"; import { NFError } from "./error"; import { NFTaskService } from "./taskservice"; import { NfUser } from "./entity/nfuser"; import { INFProcessDefCfg, NFDefStatus } from "./types"; /** * 流程引擎:定义、发布、创建实例与查询。 */ export class NFEngine{ /** * 语言,默认 `en`(对应 `message_en` / `message_zh`)。 */ public static language:String = 'en'; /** * 定义并保存流程(默认 `published`,版本自动 +1)。 * * `cfg` 必须包含非空 `name` 与非空 `nodes` 数组;整份配置写入 `cfgStr`。 * * @param cfg - 流程定义 * @returns 已保存的定义实体 * @throws `1005` 当缺少 `name` 或 `nodes` */ public static async defineProcess(cfg:INFProcessDefCfg){ this.assertProcessDef(cfg); const status = (cfg.status || NFDefStatus.PUBLISHED) as string; const name = cfg.name.trim(); let ver = cfg.version; if(ver == null){ ver = await this.nextVersion(name); } const defP:NfDefProcess = new NfDefProcess(); defP.defName = name; defP.createTime = new Date().getTime(); defP.updTime = defP.createTime; defP.dueTime = cfg.dueTime; defP.ver = ver; defP.defType = status; defP.cfgStr = JSON.stringify({ ...cfg, name }); await defP.save(); return defP; } /** * 校验流程定义是否包含必填项 `name`、`nodes`。 * * @param cfg - 待校验的定义对象 * @throws `1005` 当 `name` 为空或 `nodes` 不是非空数组 */ public static assertProcessDef(cfg:INFProcessDefCfg){ if(!cfg || typeof cfg.name !== 'string' || !cfg.name.trim()){ throw new NFError("1005"); } if(!Array.isArray(cfg.nodes) || cfg.nodes.length === 0){ throw new NFError("1005"); } } /** * 将指定草稿版本发布为 `published`。 * * @param defName - 定义名 * @param version - 版本号;缺省为该名称最新版本 * @returns 更新后的定义实体 * @throws `1000` 当定义不存在 */ public static async publishProcess(defName:string,version?:number){ const defP = await this.findDefinition(defName, version, false); if(!defP){ throw new NFError("1000"); } defP.defType = NFDefStatus.PUBLISHED; defP.updTime = new Date().getTime(); await defP.save(); return defP; } /** * 列出流程定义,按名称升序、版本降序。 * * @param defName - 可选,按定义名过滤 * @returns 定义列表,无记录时为空数组 */ public static async listDefinitions(defName?:string):Promise{ const em:EntityManager = await getEntityManager(); const query:Query = em.createQuery(NfDefProcess.name); let q = query.select('*').orderBy({ defName:'asc', ver:'desc' }); if(defName){ q = q.where({ defName }); } const rows = await q.getResultList(); await em.close(); return rows || []; } /** * 计算指定定义名的下一版本号。 * * @param defName - 定义名 * @returns 已有版本最大值 +1;尚无定义时为 `1` */ public static async nextVersion(defName:string):Promise{ const list = await this.listDefinitions(defName); if(!list.length){ return 1; } return (list[0].ver || 0) + 1; } /** * 查找流程定义。 * * @param defName - 定义名 * @param version - 指定版本;缺省取最新 * @param publishedOnly - 为 `true` 时只查已发布版本,默认 `true` * @returns 定义实体;未找到时为空 */ public static async findDefinition(defName:string,version?:number,publishedOnly:boolean=true):Promise{ const em:EntityManager = await getEntityManager(); const query:Query = em.createQuery(NfDefProcess.name); const where:any = { defName }; if(version != null){ where.ver = version; } if(publishedOnly){ where.defType = NFDefStatus.PUBLISHED; } let q = query.select('*').where(where); if(version == null){ q = q.orderBy({ ver:'desc' }); } const defP = await q.getResult(); await em.close(); return defP; } /** * 按已发布定义创建流程实例(不自动 `start`)。 * * @param defName - 定义名 * @param instName - 实例显示名 * @param userId - 发起人用户 id * @param version - 定义版本;缺省用最新 `published` * @returns 流程实例;定义不存在时为空 */ public static async createProcess(defName:string,instName:string,userId?:number,version?:number):Promise{ const defP = await this.findDefinition(defName, version, true); if(!defP || !defP.cfgStr){ // 兼容旧数据:defType 为空时按名称取最新 const fallback = version == null ? await NfDefProcess.findOne({ defName }) : await this.findDefinition(defName, version, false); if(!fallback?.cfgStr){ return; } return this.createFromDef(fallback, instName, userId); } return this.createFromDef(defP, instName, userId); } /** * 解析定义 JSON,取出节点数组。 * * 兼容旧数据:`cfgStr` 可为节点数组,或带 `name`/`nodes` 的对象。 * * @param cfgStr - 定义 JSON 字符串 * @returns 节点配置数组 * @throws `1000` 当 JSON 无效或缺少 nodes */ private static parseDefNodes(cfgStr:string){ let cfg; try{ cfg = JSON.parse(cfgStr); }catch(e){ throw new NFError("1000"); } return NFProcess.resolveNodes(cfg); } /** * 由定义实体创建运行时流程(只把 `nodes` 交给节点管理器)。 * * @param defP - 流程定义实体 * @param instName - 实例名 * @param userId - 发起人用户 id * @returns 已持久化实体的流程实例 * @throws `1000` 当定义配置无法解析 */ private static async createFromDef(defP:NfDefProcess,instName:string,userId?:number):Promise{ const nodes = this.parseDefNodes(defP.cfgStr); const process = new NFProcess(nodes); process.entity = await this.saveInstance(defP,instName,userId); return process; } /** * 强制关闭流程实例并清理待办 / 网关状态。 * * @param processId - 实例 id * @param reason - 关闭原因 * @returns 是否执行了关闭;实例不存在时为 `false` */ public static async closeProcess(processId:number,reason?:string){ const process = await this.getInstance(processId); if(!process){ return false; } return await process.close(reason); } /** * 按实例 id 加载流程并恢复运行态。 * * @param procId - 实例 id * @returns 流程实例;不存在时为空 * @throws `1000` 当定义配置无法解析 */ public static async getInstance(procId:number):Promise{ const em:EntityManager = await getEntityManager(); const query:Query = em.createQuery(NfProcess.name); const proc:NfProcess = await query.select(['*','nfUser.userId','nfDefProcess.*']).where({processId:procId}).getResult(); if(!proc){ await em.close(); return; } let nodes; try{ nodes = this.parseDefNodes(proc.nfDefProcess.cfgStr); }catch(e){ await em.close(); throw e instanceof NFError ? e : new NFError("1000"); } const process = new NFProcess(nodes); process.entity = proc; await process.restore(); await em.close(); return process; } /** * 查询实例的节点办理轨迹。 * * @param processId - 实例 id * @returns 历史节点列表;实例不存在时为空数组 */ public static async getHistory(processId:number):Promise{ const process = await this.getInstance(processId); if(!process){ return []; } return process.getHistory(); } /** * 持久化新建的流程实例行。 * * @param defProc - 所属定义 * @param name - 实例名 * @param userId - 发起人用户 id * @returns 已保存的实例实体 */ public static async saveInstance(defProc:NfDefProcess,name:string,userId:number){ let proc:NfProcess = new NfProcess(); proc.createTime = new Date().getTime(); proc.processName = name; proc.nfUser = await NfUser.find(userId); if(defProc.dueTime){ proc.dueTime = proc.createTime + defProc.dueTime; } proc.nfDefProcess = defProc; proc = await proc.save(); return proc; } /** * 获取任务服务实例。 * * @returns 任务服务 */ public static getTaskService(){ return new NFTaskService(); } }