@flowlab/all
Version:
A cool library focusing on handling various flows
183 lines (165 loc) • 6.66 kB
text/typescript
import { IStepConfig, StepType, IWorkflowContext, NodeStatus } from '../types/index.js';
import { Step } from './step.js';
import { v4 as uuidv4 } from 'uuid';
/**
* MARK: 工作流定义
* @class WorkflowDefinition
* @description 定义一个工作流的结构和步骤
*/
export class WorkflowDefinition {
public readonly id: string; // 工作流定义的唯一 ID
public readonly name?: string; // 工作流名称 (可选)
public readonly description?: string; // 工作流描述 (可选)
private steps: Map<string, Step> = new Map(); // 存储所有步骤,以 ID 为键
private startStepId?: string; // 工作流的起始步骤 ID
constructor(id: string, name?: string, description?: string) {
this.id = id || uuidv4(); // 如果未提供 ID,则生成一个
this.name = name;
this.description = description;
}
/**
* MARK: addStep
* 添加一个顺序执行的任务节点步骤
* @param config - 步骤配置 (必须包含 id, type=TASK, nodeId)
* @returns 返回 WorkflowDefinition 实例,支持链式调用
* @throws 如果步骤 ID 重复,则抛出错误
*/
addStep(config: Omit<IStepConfig, 'type'> & { nodeId: string }): this {
const fullConfig: IStepConfig = { ...config, type: StepType.TASK };
return this.addStepInternal(fullConfig);
}
/**
* MARK: 添加条件步骤
* 添加一个条件分支步骤
* @param config - 步骤配置 (必须包含 id, type=CONDITION, condition, branches)
* @returns 返回 WorkflowDefinition 实例,支持链式调用
*/
addConditionStep(config: Omit<IStepConfig, 'type'> & { condition: (context: IWorkflowContext) => boolean | string; branches: { [key: string]: string } }): this {
const fullConfig: IStepConfig = { ...config, type: StepType.CONDITION };
return this.addStepInternal(fullConfig);
}
/**
* MARK: 添加并行步骤
* 添加一个并行执行步骤
* @param config - 步骤配置 (必须包含 id, type=PARALLEL, parallelSteps)
* @returns 返回 WorkflowDefinition 实例,支持链式调用
*/
addParallelStep(config: Omit<IStepConfig, 'type'> & { parallelSteps: IStepConfig[] }): this {
const fullConfig: IStepConfig = { ...config, type: StepType.PARALLEL };
// 可以在这里递归验证 parallelSteps 内部的配置
return this.addStepInternal(fullConfig);
}
/**
* MARK: 添加子工作流
* 添加一个子工作流步骤
* @param config - 步骤配置 (必须包含 id, type=SUB_WORKFLOW, subWorkflowId)
* @returns 返回 WorkflowDefinition 实例,支持链式调用
*/
addSubWorkflowStep(config: Omit<IStepConfig, 'type'> & { subWorkflowId: string }): this {
const fullConfig: IStepConfig = { ...config, type: StepType.SUB_WORKFLOW };
return this.addStepInternal(fullConfig);
}
/**
* MARK: 添加事件触发
* 添加一个事件触发步骤
* @param config - 步骤配置 (必须包含 id, type=EVENT_TRIGGER, event)
* @returns 返回 WorkflowDefinition 实例,支持链式调用
*/
addEventTriggerStep(config: Omit<IStepConfig, 'type'> & { event: string }): this {
const fullConfig: IStepConfig = { ...config, type: StepType.EVENT_TRIGGER };
return this.addStepInternal(fullConfig);
}
/**
* MARK: 添加事件监听
* 添加一个事件监听步骤 (通常作为起始步骤或并行分支)
* @param config - 步骤配置 (必须包含 id, type=EVENT_LISTENER, event)
* @returns 返回 WorkflowDefinition 实例,支持链式调用
*/
addEventListenerStep(config: Omit<IStepConfig, 'type'> & { event: string }): this {
const fullConfig: IStepConfig = { ...config, type: StepType.EVENT_LISTENER };
return this.addStepInternal(fullConfig);
}
/**
* MARK: addStepInternal
* NOTE: 内部方法,用于添加步骤并进行基本验证
* @param config - 完整的步骤配置
* @returns 返回 WorkflowDefinition 实例
*/
addStepInternal(config: IStepConfig): this {
if (this.steps.has(config.id)) {
throw new Error(`工作流 "${this.id}" 中已存在 ID 为 "${config.id}" 的步骤。`);
}
const step = new Step(config);
this.steps.set(step.id, step);
// 如果是第一个添加的步骤,则设为起始步骤 (除非是事件监听器)
if (!this.startStepId && step.type !== StepType.EVENT_LISTENER) {
this.startStepId = step.id;
}
return this;
}
/**
* MARK: setStartStep
* 设置工作流的起始步骤 ID
* @param stepId - 起始步骤的 ID
* @returns 返回 WorkflowDefinition 实例
* @throws 如果步骤 ID 不存在,则抛出错误
*/
setStartStep(stepId: string): this {
if (!this.steps.has(stepId)) {
throw new Error(`工作流 "${this.id}" 中不存在 ID 为 "${stepId}" 的步骤。`);
}
this.startStepId = stepId;
return this;
}
/**
* MARK: getStartStepId
* 获取起始步骤 ID
* @returns 起始步骤 ID
* @throws 如果未设置起始步骤,则抛出错误
*/
getStartStepId(): string {
if (!this.startStepId) {
throw new Error(`工作流 "${this.id}" 未设置起始步骤。`);
}
return this.startStepId;
}
/**
* MARK: getStepById
* 根据 ID 获取步骤实例
* @param stepId - 步骤 ID
* @returns 返回步骤实例,如果未找到则返回 undefined
*/
getStep(stepId: string): Step | undefined {
return this.steps.get(stepId);
}
/**
* MARK: getSteps
* 获取所有步骤的 Map
*/
getSteps(): Map<string, Step> {
return this.steps;
}
/**
* MARK: validate
* 验证工作流定义的有效性 (例如,检查链接是否断开,是否存在循环等)
* @returns 如果有效则返回 true,否则返回 false 或抛出错误
*/
validate(): boolean {
if (!this.startStepId) {
console.error(`[Workflow: ${this.id}] 验证失败:未设置起始步骤。`);
return false;
}
if (!this.steps.has(this.startStepId)) {
console.error(`[Workflow: ${this.id}] 验证失败:起始步骤 "${this.startStepId}" 不存在。`);
return false;
}
// TODO: 实现更复杂的验证逻辑
// 1. 检查所有 nextStepId, branch targets, subWorkflowId 是否指向存在的步骤/工作流
// 2. 检查是否存在无法到达的步骤
// 3. (可选) 检测简单循环 (复杂的循环检测可能需要图算法)
// 4. 检查并行步骤的配置是否正确
// 5. 检查 TASK 步骤是否关联了已注册的 nodeId
console.log(`[Workflow: ${this.id}] 验证通过 (基本检查)。`);
return true;
}
}