跳到主要内容
版本:Next

BusinessActivity

分类: 通用步骤
命名空间: CMS.Plugin.FlowManagement.Abstractions.FlowBusiness.Activitys
基类: Activity (SYC.Flow.Kernel)
模块: FlowManagement.Abstractions

概述

BusinessActivity 是 LMES 流程系统中所有业务流程节点的基类。它继承自 SYC.Flow.Kernel.Activity,提供了流程节点的基础功能和生命周期管理,包括节点进入、执行、退出的完整流程控制,以及流程上下文数据管理、异常处理、日志记录等核心功能。

所有自定义流程节点都应该继承自 BusinessActivity 或其子类(如 ActionActivity、ProcessBusinessActivity),以获得完整的流程管理能力。

业务场景

适用场景

  • 作为基类使用: 所有自定义流程节点的基类
  • 流程控制: 提供节点生命周期管理(进入、执行、退出)
  • 数据管理: 管理流程上下文数据的读写
  • 异常处理: 统一的异常处理和错误恢复机制
  • 日志记录: 标准化的日志记录功能

在系统中的作用

BusinessActivity 是整个流程系统的核心基础类,它:

  • 定义了流程节点的标准生命周期
  • 提供了流程上下文(Flow.DataItems)的访问接口
  • 实现了统一的异常处理机制
  • 支持流程事件的发布和订阅
  • 提供了扩展属性机制,支持动态配置

与其他节点的协作

  • ActionActivity: 继承自 BusinessActivity,增加了执行时机控制
  • ProcessBusinessActivity: 继承自 BusinessActivity,专门用于 MES 工序业务
  • 所有业务节点: 都直接或间接继承自 BusinessActivity

配置说明

基本配置

属性名类型必填默认值说明
TaskDelayint0Execute延时执行时间(单位:毫秒)
IsDisabledboolfalse屏蔽步骤中的业务代码 ProcessAsync(),将跳过业务继续处理
PublishFlowEventboolfalse是否发布流程事件
PublishDeliveryEventboolfalse是否发布输送事件
IsPrintConsumptionbooltrue是否打印执行耗时
StopwatchTypeStopwatchTypeCommon计时类型(Common/Start/Stop/Reset/Restart)
ExtendedPropertyBusinessPropertyCollection空集合步骤扩展属性
FlowExceptionMaxCountint10流程异常上限数量
SaveFlowInstanceboolfalse保存实例到数据库

配置项详解

TaskDelay

说明: 设置节点执行前的延时时间,单位为毫秒。在 ExecuteAsync 方法开始时会先等待指定的时间。

取值范围: 大于等于 0 的整数

注意事项:

  • 延时期间会响应流程取消请求
  • 过长的延时可能影响流程响应性能
  • 通常用于调试或特殊的时序控制场景

IsDisabled

说明: 当设置为 true 时,节点的 ProcessAsync 方法将被跳过,但节点的生命周期方法(EnterAsync、ExitAsync)仍会执行。

取值范围: true 或 false

注意事项:

  • 可用于临时禁用某个节点的业务逻辑
  • 流程上下文数据仍会正常传递
  • 可通过流程上下文动态控制:{节点名}_IsActivityDisabled(节点名为节点的 Name 属性值)

PublishFlowEvent

说明: 是否在节点执行后发布流程事件(ProcessFlowEto),其他模块可以订阅此事件。

取值范围: true 或 false

注意事项:

  • 事件包含当前流程上下文的所有数据
  • 可通过 EventSynchronous 属性控制同步或异步发布
  • 频繁发布事件可能影响性能

ExtendedProperty

说明: 扩展属性集合,允许在节点上添加自定义属性,这些属性可以写入流程上下文。

取值范围: BusinessPropertyCollection 对象

注意事项:

  • 每个扩展属性可以设置是否写入流程上下文(WriteIntoDataItems)
  • 扩展属性的值会自动添加 "_Value" 后缀作为流程上下文键
  • 支持在运行时动态配置

FlowExceptionMaxCount

说明: 流程异常次数上限,当流程异常次数超过此值时,节点将拒绝进入。

取值范围: 大于等于 -1 的整数,-1 表示不限制

注意事项:

  • 用于防止流程陷入无限异常循环
  • 异常计数是按流程名称(ProcName)统计的
  • 达到上限后需要重启流程或清除异常计数

流程上下文

输入参数

参数名类型说明
{节点名}_IsActivityDisabledbool动态控制节点是否禁用
ApplicationDataobject应用程序数据(如 ProcessModel)
自定义参数any由上游节点写入的业务数据

输出参数

参数名类型说明
扩展属性值anyExtendedProperty 中配置的属性值
自定义参数any在 ProcessAsync 中写入的业务数据

数据流转说明

  1. 读取数据: 通过 Flow.DataItems[key] 读取流程上下文数据
  2. 写入数据: 通过 Flow.DataItems[key] = value 写入流程上下文数据
  3. 辅助方法:
    • GetFlowItemValue(key): 支持点号分隔的属性路径和数组索引
    • GetVariableValue(key): 支持 {key} 格式的变量引用
    • ToFlowItemKey(key): 自动添加 "_Value" 后缀

业务逻辑说明

处理流程

BusinessActivity 的完整生命周期包括以下步骤:

  1. CanEnterAsync: 判断是否可以进入节点

    • 检查流程异常次数是否超限
    • 检查流程是否被取消
    • 检查流程状态是否正常
  2. EnterAsync: 进入节点

    • 创建工作项(FlowItem)
    • 记录开始时间
    • 更新当前任务信息
    • 记录进入日志
  3. ExecuteAsync: 执行节点

    • 执行延时(如果配置了 TaskDelay)
    • 调用 ProcessAsync 执行业务逻辑
    • 发布流程事件(如果启用)
    • 处理扩展属性
  4. ProcessAsync: 业务逻辑处理

    • 默认实现为空,由子类重写
  5. CanExitAsync: 判断是否可以退出节点

    • 默认返回 true
  6. ExitAsync: 退出节点

    • 记录结束时间
    • 保存流程实例(如果启用)
    • 打印执行耗时
    • 处理计时器操作

流程图

[CanEnterAsync] 
↓ (true)
[EnterAsync]

[ExecuteAsync]

[ProcessAsync] (业务逻辑)

[CanExitAsync]
↓ (true)
[ExitAsync]

[下一个节点]

依赖服务

服务接口用途说明
ILmesMetrics性能指标收集可选,用于收集流程执行指标
ILocalEventBus事件总线用于发布流程事件和输送事件
Flow.ServiceProvider服务提供者访问依赖注入容器中的服务

异常处理

异常类型处理方式说明
BusinessException标记流程状态为 UnexpectedProcessAsync 中的业务异常
TaskCanceledException记录错误日志流程被取消
OperationCanceledException记录错误日志操作被取消
其他异常记录异常日志并标记流程状态未预期的异常

异常处理机制:

  • ProcessAsync 中的异常会被捕获并包装为 BusinessException
  • 异常会增加流程异常计数
  • 达到异常上限后节点将拒绝进入
  • 异常会触发流程重置(ResetEventAsync)

日志记录

BusinessActivity 记录以下关键日志:

  • 进入节点: Enter,TaskID={taskId}
  • 执行延时: 延时期间的异常
  • 业务异常: ProcessAsync 中的异常详情
  • 流程事件: 发布流程事件的开始和结束
  • 退出节点: Exit,耗时={milliseconds}毫秒
  • 超时警告: 当执行时间超过 TimeOut 时记录

使用示例

基本示例

// 最简单的自定义节点
[Serializable]
[Design("简单节点", "简单的业务节点示例", Sort = 1)]
[Category("自定义")]
public class SimpleActivity : BusinessActivity
{
public override async Task ProcessAsync(ProcessflowEventArgs args)
{
// 读取输入数据
var input = Flow.DataItems["InputData"]?.ToString();

// 处理业务逻辑
var result = $"Processed: {input}";

// 写入输出数据
Flow.DataItems["OutputData"] = result;

// 记录日志
Flow.Logger.LogMessage($"处理完成: {result}", Name);

await base.ProcessAsync(args);
}
}

高级示例

// 带配置属性和异常处理的节点
[Serializable]
[Design("高级节点", "带配置的业务节点示例", Sort = 2)]
[Category("自定义")]
public class AdvancedActivity : BusinessActivity
{
[Design("配置项1", "第一个配置项", Sort = 1)]
[Category("配置")]
[DataMember]
public string Config1 { get; set; }

[Design("配置项2", "第二个配置项", Sort = 2)]
[Category("配置")]
[DataMember]
[Required]
public int Config2 { get; set; }

public override List<FlowItemKey> FlowItemKeys => new()
{
new FlowItemKey("Input_Value", "输入参数", "输入参数说明", typeof(string)),
new FlowItemKey("Output_Value", "输出参数", "输出参数说明", typeof(string))
};

public override async Task ProcessAsync(ProcessflowEventArgs args)
{
try
{
// 读取输入
var input = GetFlowItemValue("Input")?.ToString();
if (string.IsNullOrEmpty(input))
{
Flow.Logger.LogWarningMessage("输入参数为空", Name);
return;
}

// 使用配置项
Flow.Logger.LogMessage($"Config1={Config1}, Config2={Config2}", Name);

// 调用外部服务
var service = Flow.ServiceProvider.GetRequiredService<IMyService>();
var result = await service.ProcessAsync(input, Config2);

// 写入输出
Flow.DataItems["Output_Value"] = result;

Flow.Logger.LogMessage($"处理成功: {result}", Name);
}
catch (Exception ex)
{
Flow.Logger.LogExceptionMessage(ex, Name);
throw;
}

await base.ProcessAsync(args);
}
}

完整流程示例

{
"Name": "示例流程",
"Activities": [
{
"Type": "SimpleActivity",
"Name": "开始节点",
"Alias": "Start",
"ExtendedProperty": [
{
"Name": "InputData",
"Value": "初始数据",
"WriteIntoDataItems": true
}
]
},
{
"Type": "AdvancedActivity",
"Name": "处理节点",
"Alias": "Process",
"Config1": "配置值1",
"Config2": 100,
"TaskDelay": 1000,
"IsPrintConsumption": true
},
{
"Type": "EndActivity",
"Name": "结束节点",
"Alias": "End"
}
],
"Transitions": [
{
"From": "Start",
"To": "Process"
},
{
"From": "Process",
"To": "End"
}
]
}

扩展开发指南

继承层次

Activity (SYC.Flow.Kernel)
└── BusinessActivity
├── ActionActivity (动作节点基类)
│ ├── ApiCallActivity
│ ├── MethodCallActivity
│ └── ScriptCallActivity
├── ProcessBusinessActivity (工序节点基类)
│ ├── GenerateCodeActivity
│ ├── MaterialDetectionActivity
│ └── PrintLabelActivity
└── 其他自定义节点

可重写方法

方法名用途何时重写
CanEnterAsync判断是否可以进入节点需要自定义进入条件(如检查前置条件)
EnterAsync进入节点时执行需要初始化操作(如初始化变量、创建资源)
ProcessAsync核心业务逻辑必须重写,实现具体的业务功能
CanExitAsync判断是否可以退出节点需要自定义退出条件(如验证处理结果)
ExitAsync退出节点时执行需要清理操作(如释放资源、保存状态)
Validate验证节点配置需要自定义配置验证逻辑

自定义节点开发步骤

  1. 创建节点类
[Serializable]
[Design("节点名称", "节点描述", Sort = 排序号)]
[Category("分类名称")]
public class MyCustomActivity : BusinessActivity
{
// 实现代码
}
  1. 添加配置属性
[Design("属性名称", "属性描述", Sort = 排序号)]
[Category("分类名称")]
[DataMember]
[Required] // 可选:标记为必填
public string MyProperty { get; set; }
  1. 定义流程上下文键
public override List<FlowItemKey> FlowItemKeys => new()
{
new FlowItemKey("MyInput_Value", "输入", "输入说明", typeof(string)),
new FlowItemKey("MyOutput_Value", "输出", "输出说明", typeof(string))
};
  1. 实现业务逻辑
public override async Task ProcessAsync(ProcessflowEventArgs args)
{
// 1. 读取输入
var input = GetFlowItemValue("MyInput")?.ToString();

// 2. 处理业务
var result = await DoBusinessLogic(input);

// 3. 写入输出
Flow.DataItems["MyOutput_Value"] = result;

// 4. 记录日志
Flow.Logger.LogMessage($"处理完成", Name);

await base.ProcessAsync(args);
}

注册和集成

  1. 编译节点:

    • 将自定义节点编译为 DLL
    • 确保引用了必要的依赖包
  2. 部署节点:

    • 将 DLL 放到 LMES 的插件目录
    • 或者直接集成到主项目中
  3. 配置节点:

    • 在流程设计器中可以看到新节点
    • 通过 Design 特性配置的信息会显示在设计器中
  4. 测试节点:

    • 创建测试流程
    • 运行流程验证节点功能
    • 检查日志输出

注意事项

  • ⚠️ 异步方法: 所有生命周期方法都是异步的,必须正确使用 async/await
  • ⚠️ 异常处理: ProcessAsync 中的异常会被自动捕获,但仍应该做好异常处理
  • ⚠️ 流程取消: 长时间运行的操作应该检查 Flow.CancellationTokenSource.Token
  • ⚠️ 线程安全: 节点可能在多线程环境中执行,注意线程安全
  • ⚠️ 性能考虑: 避免在 ProcessAsync 中执行耗时的同步操作
  • 💡 最佳实践:
    • 使用 GetFlowItemValue 而不是直接访问 Flow.DataItems,以支持复杂路径
    • 充分利用日志记录,便于问题排查
    • 合理使用扩展属性,提高节点的灵活性
    • 在 ProcessAsync 开始时验证输入参数

相关节点

常见问题

Q1: 如何在节点间传递数据?

A: 使用流程上下文(Flow.DataItems):

// 写入数据
Flow.DataItems["MyKey_Value"] = myValue;

// 读取数据
var value = Flow.DataItems["MyKey_Value"];

Q2: 如何访问依赖注入的服务?

A: 使用 Flow.ServiceProvider:

var myService = Flow.ServiceProvider.GetRequiredService<IMyService>();

Q3: 如何处理节点执行失败?

A: 在 ProcessAsync 中抛出异常,系统会自动处理:

if (!success)
{
throw new BusinessException("MyNode", "处理失败的原因");
}

Q4: 如何动态禁用节点?

A: 设置流程上下文中的禁用标志:

Flow.DataItems["MyNode_IsActivityDisabled"] = true;

更新历史

日期版本说明
2025-11-281.0初始版本

本文档最后更新时间: 2025-11-28