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
配置说明
基本配置
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| TaskDelay | int | 否 | 0 | Execute延时执行时间(单位:毫秒) |
| IsDisabled | bool | 否 | false | 屏蔽步骤中的业务代码 ProcessAsync(),将跳过业务继续处理 |
| PublishFlowEvent | bool | 否 | false | 是否发布流程事件 |
| PublishDeliveryEvent | bool | 否 | false | 是否发布输送事件 |
| IsPrintConsumption | bool | 否 | true | 是否打印执行耗时 |
| StopwatchType | StopwatchType | 否 | Common | 计时类型(Common/Start/Stop/Reset/Restart) |
| ExtendedProperty | BusinessPropertyCollection | 否 | 空集合 | 步骤扩展属性 |
| FlowExceptionMaxCount | int | 否 | 10 | 流程异常上限数量 |
| SaveFlowInstance | bool | 否 | false | 保存实例到数据库 |
配置项详解
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)统计的
- 达到上限后需要重启流程或清除异常计数
流程上下文
输入参数
| 参数名 | 类型 | 说明 |
|---|---|---|
| {节点名}_IsActivityDisabled | bool | 动态控制节点是否禁用 |
| ApplicationData | object | 应用程序数据(如 ProcessModel) |
| 自定义参数 | any | 由上游节点写入的业务数据 |
输出参数
| 参数名 | 类型 | 说明 |
|---|---|---|
| 扩展属性值 | any | ExtendedProperty 中配置的属性值 |
| 自定义参数 | any | 在 ProcessAsync 中写入的业务数据 |
数据流转说明
- 读取数据: 通过
Flow.DataItems[key]读取流程上下文数据 - 写入数据: 通过
Flow.DataItems[key] = value写入流程上下文数据 - 辅助方法:
GetFlowItemValue(key): 支持点号分隔的属性路径和数组索引GetVariableValue(key): 支持{key}格式的变量引用ToFlowItemKey(key): 自动添加 "_Value" 后缀
业务逻辑说明
处理流程
BusinessActivity 的完整生命周期包括以下步骤:
-
CanEnterAsync: 判断是否可以进入节点
- 检查流程异常次数是否超限
- 检查流程是否被取消
- 检查流程状态是否正常
-
EnterAsync: 进入节点
- 创建工作项(FlowItem)
- 记录开始时间
- 更新当前任务信息
- 记录进入日志
-
ExecuteAsync: 执行节点
- 执行延时(如果配置了 TaskDelay)
- 调用 ProcessAsync 执行业务逻辑
- 发布流程事件(如果启用)
- 处理扩展属性
-
ProcessAsync: 业务逻辑处理
- 默认实现为空,由子类重写
-
CanExitAsync: 判断是否可以退出节点
- 默认返回 true
-
ExitAsync: 退出节点
- 记录结束时间
- 保存流程实例(如果启用)
- 打印执行耗时
- 处理计时器操作
流程图
[CanEnterAsync]
↓ (true)
[EnterAsync]
↓
[ExecuteAsync]
↓
[ProcessAsync] (业务逻辑)
↓
[CanExitAsync]
↓ (true)
[ExitAsync]
↓
[ 下一个节点]
依赖服务
| 服务接口 | 用途 | 说明 |
|---|---|---|
| ILmesMetrics | 性能指标收集 | 可选,用于收集流程执行指标 |
| ILocalEventBus | 事件总线 | 用于发布流程事件和输送事件 |
| Flow.ServiceProvider | 服务提供者 | 访问依赖注入容器中的服务 |
异常处理
| 异常类型 | 处理方式 | 说明 |
|---|---|---|
| BusinessException | 标记流程状态为 Unexpected | ProcessAsync 中的业务异常 |
| 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 | 验证节点配置 | 需要自定义配置验证逻辑 |
自定义节点开发步骤
- 创建节点类
[Serializable]
[Design("节点名称", "节点描述", Sort = 排序号)]
[Category("分类名称")]
public class MyCustomActivity : BusinessActivity
{
// 实现代码
}
- 添加配置属性
[Design("属性名称", "属性描述", Sort = 排序号)]
[Category("分类名称")]
[DataMember]
[Required] // 可选:标记为必填
public string MyProperty { get; set; }
- 定义流程上下文键
public override List<FlowItemKey> FlowItemKeys => new()
{
new FlowItemKey("MyInput_Value", "输入", "输入说明", typeof(string)),
new FlowItemKey("MyOutput_Value", "输出", "输出说明", typeof(string))
};
- 实现业务逻辑
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);
}
注册和集成
-
编译节点:
- 将自定义节点编译为 DLL
- 确保引用了必要的依赖包
-
部署节点:
- 将 DLL 放到 LMES 的插件目录
- 或者直接集成到主项目中
-
配置节点:
- 在流程设计器中可以看到新节点
- 通过 Design 特性配置的信息会显示在设计器中
-
测试节点:
- 创建测试流程
- 运行流程验证节点功能
- 检查日志输出
注意事项
- ⚠️ 异步方法: 所有生命周期方法都是异步的,必须正确使用 async/await
- ⚠️ 异常处理: ProcessAsync 中的异常会被自动捕获,但仍应该做好异常处理
- ⚠️ 流程取消: 长时间运行的操作应该检查
Flow.CancellationTokenSource.Token - ⚠️ 线程安全: 节点可能在多线程环境中执行,注意线程安全
- ⚠️ 性能考虑: 避免在 ProcessAsync 中执行耗时的同步操作
- 💡 最佳实践:
- 使用 GetFlowItemValue 而不是直接访问 Flow.DataItems,以支持复杂路径
- 充分利用日志记录,便于问题排查
- 合理使用扩展属性,提高节点的灵活性
- 在 ProcessAsync 开始时验证输入参数
相关节点
- ActionActivity: 继承自 BusinessActivity,增加了执行时机控制
- EndActivity: 流程结束节点
- ParallelStart: 并行流程开始节点
- ParallelEnd: 并行流程结束节点
- SubflowActivity: 子流程节点
常见问题
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-28 | 1.0 | 初始版本 |
本文档最后更新时间: 2025-11-28