添加 MX-PD-盘古 项目文件

将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
Shi.Ji
2026-05-18 11:43:09 +08:00
parent 03632a379d
commit e31d3560bb
739 changed files with 99783 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MW.WorkFlow
{
public class ActivityControl
{
public CancellationToken CancellationToken { get; }
private readonly Func<Task> _pauseCheck;
public ActivityControl(CancellationToken cancellationToken, Func<Task> pauseCheck)
{
CancellationToken = cancellationToken;
_pauseCheck = pauseCheck ?? throw new ArgumentNullException(nameof(pauseCheck));
}
/// <summary>
/// 检查是否有取消请求,如果有则抛出 OperationCanceledException。
/// </summary>
public void ThrowIfCancellationRequested()
{
CancellationToken.ThrowIfCancellationRequested();
}
/// <summary>
/// 检查流程是否处于暂停状态,如果是则等待直到恢复。
/// 同时响应取消请求。
/// </summary>
public Task CheckPauseAsync() => _pauseCheck();
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MW.WorkFlow
{
public enum ActivityResult
{
Success,
Failure,
Canceled,
}
}

View File

@@ -0,0 +1,111 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MW.WorkFlow
{
/// <summary>
/// 一个异步事件同步原语,类似于 ManualResetEvent但能够传递数据。
/// 可以等待事件发生并获取数据,也可以将事件设置为已发生状态并附带数据。
/// 默认情况下,事件在 Set 后保持已设置状态,直到 Reset 被调用。
/// </summary>
/// <typeparam name="T">事件发生时要传递的数据类型。</typeparam>
public class AsyncEvent<T>
{
// 使用 volatile 确保 _tcs 的读写操作对所有线程可见
private volatile TaskCompletionSource<T> _tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly object _lock = new object(); // 用于保护 _tcs 的替换操作
/// <summary>
/// 获取一个值,指示事件是否已设置。
/// </summary>
public bool IsSet => _tcs.Task.IsCompleted;
/// <summary>
/// 等待事件被设置为已发生状态,并获取传递的数据。
/// 如果事件已经设置,则立即返回数据。
/// </summary>
/// <param name="cancellationToken">用于取消等待的令牌。</param>
/// <returns>事件发生时传递的数据。</returns>
/// <exception cref="OperationCanceledException">如果在等待过程中取消令牌被请求。</exception>
public async Task<T> WaitAsync(CancellationToken cancellationToken = default)
{
Task<T> currentTask;
lock (_lock)
{
currentTask = _tcs.Task;
}
// 如果已经设置,直接返回结果
if (currentTask.IsCompleted)
{
return await currentTask;
}
// 注册取消回调,确保在取消时 tcs 被取消
CancellationTokenRegistration? registration = null;
if (cancellationToken.CanBeCanceled)
{
// 注意:这里需要确保在 TCS 完成或取消后,回调能被注销,避免资源泄露。
// 更好的做法是使用 CancellationToken.Register(Action) 返回的 IDisposable
// 但考虑到 TaskCompletionSource 的生命周期,通常当 Task 完成或取消时,
// 注册的回调也会在适当时候被清理。
registration = cancellationToken.Register(() =>
{
// 使用 TrySetCanceled 避免在 Task 已经完成时抛出异常
_tcs.TrySetCanceled(cancellationToken);
});
}
try
{
return await currentTask;
}
finally
{
// 确保在等待结束后注销取消回调
registration?.Dispose();
}
}
/// <summary>
/// 将事件设置为已发生状态,并传递指定的数据。
/// 如果事件已经设置,此调用将被忽略。
/// </summary>
/// <param name="data">要传递的数据。</param>
/// <returns>如果事件成功设置为已发生状态,则为 true否则为 false。</returns>
public bool Set(T data)
{
// TrySetResult 是线程安全的,如果已经设置则返回 false
// RunContinuationsAsynchronously 选项确保回调在线程池上运行不会阻塞Set的调用者
bool result = _tcs.TrySetResult(data);
if (result)
{
Console.WriteLine($"[AsyncEvent] Event set with data: {data}");
}
return result;
}
/// <summary>
/// 将事件重置为未设置状态。
/// 允许事件再次被等待。
/// </summary>
public void Reset()
{
lock (_lock)
{
// 如果当前 TaskCompletionSource 尚未完成,则不应该替换它
// 因为这可能导致正在等待的 Task 永远不会完成
if (_tcs.Task.IsCompleted)
{
_tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
Console.WriteLine("[AsyncEvent] Event reset.");
}
else
{
Console.WriteLine("[AsyncEvent] Event is not yet completed, cannot reset.");
}
}
}
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MW.WorkFlow
{
public interface IActivity
{
string Name { get; }
// Task<ActivityResult> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken, Func<Task> pauseCheck);
Task<ActivityResult> ExecuteAsync(WorkflowContext context, ActivityControl activityControl);
}
}

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D155CFA8-7C12-49F0-B31B-A25327A81E04}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Mw.WorkFlow</RootNamespace>
<AssemblyName>Mw.WorkFlow</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ActivityControl.cs" />
<Compile Include="ActivityResult.cs" />
<Compile Include="AsyncEvent.cs" />
<Compile Include="IActivity.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WorkflowContext.cs" />
<Compile Include="WorkflowDefinition.cs" />
<Compile Include="WorkflowEngine.cs" />
<Compile Include="WorkflowState.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,33 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("Mw.WorkFlow")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HP Inc.")]
[assembly: AssemblyProduct("Mw.WorkFlow")]
[assembly: AssemblyCopyright("Copyright © HP Inc. 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("d155cfa8-7c12-49f0-b31b-a25327a81e04")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MW.WorkFlow
{
public class WorkflowContext
{
private const string RouteKeyPrefix = "Route:";
private readonly ConcurrentDictionary<string, object> _data = new ConcurrentDictionary<string, object>();
public object this[string key]
{
get
{
if (_data.TryGetValue(key, out var value))
{
return value;
}
throw new KeyNotFoundException($"Key '{key}' not found in WorkflowContext.");
}
set
{
_data[key] = value;
}
}
public void SetData<T>(string key, T value)
{
_data[key] = value;
}
public T GetData<T>(string key)
{
if (_data.TryGetValue(key, out var value) && value is T typedValue)
{
return typedValue;
}
throw new KeyNotFoundException($"Key '{key}' not found or of incorrect type.");
}
public bool TryGetData<T>(string key, out T value)
{
if (_data.TryGetValue(key, out var obj) && obj is T typedValue)
{
value = typedValue;
return true;
}
value = default;
return false;
}
public bool ContainsKey(string key)
{
return _data.ContainsKey(key);
}
public void SetRoute<T>(string routeKey, T value)
{
SetData(BuildRouteKey(routeKey), value);
}
public T GetRoute<T>(string routeKey)
{
return GetData<T>(BuildRouteKey(routeKey));
}
public bool TryGetRoute<T>(string routeKey, out T value)
{
return TryGetData(BuildRouteKey(routeKey), out value);
}
public bool ContainsRoute(string routeKey)
{
return ContainsKey(BuildRouteKey(routeKey));
}
public void Clear() { _data.Clear(); }
public Dictionary<string, object> ToDictionary()
{
return new Dictionary<string, object>(_data);
}
private static string BuildRouteKey(string routeKey)
{
if (string.IsNullOrWhiteSpace(routeKey))
{
throw new ArgumentNullException(nameof(routeKey));
}
return RouteKeyPrefix + routeKey;
}
}
}

View File

@@ -0,0 +1,291 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MW.WorkFlow
{
/// <summary>
/// 定义工作流的结构,包含所有活动及其跳转规则。
/// </summary>
public class WorkflowDefinition
{
private readonly Dictionary<string, WorkflowStep> _steps;
public string InitialStepId { get; private set; }
public WorkflowDefinition(string initialStepId)
{
InitialStepId = initialStepId ?? throw new ArgumentNullException(nameof(initialStepId));
_steps = new Dictionary<string, WorkflowStep>();
}
/// <summary>
/// 添加一个工作流步骤到定义中。
/// </summary>
/// <param name="step">要添加的步骤。</param>
public void AddStep(WorkflowStep step)
{
if (step == null) throw new ArgumentNullException(nameof(step));
if (_steps.ContainsKey(step.Id))
{
throw new ArgumentException($"工作流步骤ID '{step.Id}' 已存在。");
}
_steps.Add(step.Id, step);
}
/// <summary>
/// 根据ID获取工作流步骤。
/// </summary>
/// <param name="stepId">步骤的唯一ID。</param>
/// <returns>对应的 WorkflowStep。</returns>
public WorkflowStep GetStep(string stepId)
{
if (_steps.TryGetValue(stepId, out var step))
{
return step;
}
throw new ArgumentException($"未找到ID为 '{stepId}' 的工作流步骤。");
}
public bool ContainsStep(string stepId)
{
return !string.IsNullOrWhiteSpace(stepId) && _steps.ContainsKey(stepId);
}
/// <summary>
/// 获取所有步骤的ID列表。
/// </summary>
public IEnumerable<string> GetAllStepIds() => _steps.Keys;
}
/// <summary>
/// 表示工作流中的一个步骤,包含一个活动和可能的条件跳转。
/// </summary>
public class WorkflowStep
{
public string Id { get; } // 步骤的唯一标识符
public IActivity Activity { get; } // 该步骤要执行的活动
public string FlowName { get; private set; } // 所属流程名称,用于界面显示和断点定位
public string NextStepId { get; private set; } // 默认的下一个步骤ID
public List<JumpCondition> JumpConditions { get; } // 条件跳转列表
public WorkflowStep(string id, IActivity activity)
{
Id = id ?? throw new ArgumentNullException(nameof(id));
Activity = activity ?? throw new ArgumentNullException(nameof(activity));
JumpConditions = new List<JumpCondition>();
}
/// <summary>
/// 设置默认的下一个步骤ID。
/// </summary>
public WorkflowStep WithNextStep(string nextStepId)
{
NextStepId = nextStepId;
return this;
}
public WorkflowStep WithFlowName(string flowName)
{
FlowName = flowName;
return this;
}
/// <summary>
/// 添加一个条件跳转。
/// </summary>
/// <param name="condition">跳转条件。</param>
/// <param name="targetStepId">满足条件时要跳转到的目标步骤ID。</param>
public WorkflowStep AddJumpCondition(Func<WorkflowContext, ActivityResult, bool> condition, string targetStepId)
{
JumpConditions.Add(new JumpCondition(condition, targetStepId));
return this;
}
public WorkflowStep AddDynamicJumpCondition(Func<WorkflowContext, ActivityResult, bool> condition, Func<WorkflowContext, ActivityResult, string> targetStepResolver)
{
JumpConditions.Add(new JumpCondition(condition, targetStepResolver));
return this;
}
public WorkflowStep AddJumpOnContextValue<T>(string contextKey, T expectedValue, string targetStepId)
{
if (string.IsNullOrWhiteSpace(contextKey)) throw new ArgumentNullException(nameof(contextKey));
return AddJumpCondition(
(ctx, result) => ctx != null &&
ctx.TryGetData<T>(contextKey, out var actualValue) &&
EqualityComparer<T>.Default.Equals(actualValue, expectedValue),
targetStepId);
}
public WorkflowStep AddJumpOnContextValueOnSuccess<T>(string contextKey, T expectedValue, string targetStepId)
{
if (string.IsNullOrWhiteSpace(contextKey)) throw new ArgumentNullException(nameof(contextKey));
return AddJumpCondition(
(ctx, result) => result == ActivityResult.Success &&
ctx != null &&
ctx.TryGetData<T>(contextKey, out var actualValue) &&
EqualityComparer<T>.Default.Equals(actualValue, expectedValue),
targetStepId);
}
public WorkflowStep AddJumpOnRouteValue<T>(string routeKey, T expectedValue, string targetStepId)
{
if (string.IsNullOrWhiteSpace(routeKey)) throw new ArgumentNullException(nameof(routeKey));
return AddJumpCondition(
(ctx, result) => ctx != null &&
ctx.TryGetRoute<T>(routeKey, out var actualValue) &&
EqualityComparer<T>.Default.Equals(actualValue, expectedValue),
targetStepId);
}
public WorkflowStep AddJumpOnRouteValueOnSuccess<T>(string routeKey, T expectedValue, string targetStepId)
{
if (string.IsNullOrWhiteSpace(routeKey)) throw new ArgumentNullException(nameof(routeKey));
return AddJumpCondition(
(ctx, result) => result == ActivityResult.Success &&
ctx != null &&
ctx.TryGetRoute<T>(routeKey, out var actualValue) &&
EqualityComparer<T>.Default.Equals(actualValue, expectedValue),
targetStepId);
}
public WorkflowStep AddRouteBranchOnSuccess<T>(string routeKey, IDictionary<T, string> routeTargets)
{
if (string.IsNullOrWhiteSpace(routeKey)) throw new ArgumentNullException(nameof(routeKey));
if (routeTargets == null) throw new ArgumentNullException(nameof(routeTargets));
return AddDynamicJumpCondition(
(ctx, result) => result == ActivityResult.Success && TryResolveRouteBranchTarget(ctx, routeKey, routeTargets, out var _),
(ctx, result) => ResolveRouteBranchTarget(ctx, routeKey, routeTargets));
}
public WorkflowStep AddRouteBranch<T>(ActivityResult expectedResult, string routeKey, IDictionary<T, string> routeTargets)
{
if (string.IsNullOrWhiteSpace(routeKey)) throw new ArgumentNullException(nameof(routeKey));
if (routeTargets == null) throw new ArgumentNullException(nameof(routeTargets));
return AddDynamicJumpCondition(
(ctx, result) => result == expectedResult && TryResolveRouteBranchTarget(ctx, routeKey, routeTargets, out var _),
(ctx, result) => ResolveRouteBranchTarget(ctx, routeKey, routeTargets));
}
public WorkflowStep AddRouteJump(string routeContextKey = "WorkflowRoutes")
{
return AddDynamicJumpCondition(
(ctx, result) => TryResolveRouteTarget(ctx, routeContextKey, out var _),
(ctx, result) => ResolveRouteTarget(ctx, routeContextKey));
}
public WorkflowStep AddRouteJumpOnSuccess(string routeContextKey = "WorkflowRoutes")
{
return AddDynamicJumpCondition(
(ctx, result) => result == ActivityResult.Success && TryResolveRouteTarget(ctx, routeContextKey, out var _),
(ctx, result) => ResolveRouteTarget(ctx, routeContextKey));
}
// 可以添加更多方便的条件方法,例如:
public WorkflowStep AddJumpOnSuccess(string targetStepId)
{
return AddJumpCondition((ctx, result) => result == ActivityResult.Success, targetStepId);
}
public WorkflowStep AddJumpOnFailure(string targetStepId)
{
return AddJumpCondition((ctx, result) => result == ActivityResult.Failure, targetStepId);
}
public WorkflowStep AddJumpOnCanceled(string targetStepId)
{
return AddJumpCondition((ctx, result) => result == ActivityResult.Canceled, targetStepId);
}
private bool TryResolveRouteTarget(WorkflowContext context, string routeContextKey, out string targetStepId)
{
if (context != null &&
context.TryGetData<IDictionary<string, string>>(routeContextKey, out var routes) &&
routes != null &&
routes.TryGetValue(Id, out targetStepId) &&
!string.IsNullOrWhiteSpace(targetStepId))
{
return true;
}
targetStepId = null;
return false;
}
private string ResolveRouteTarget(WorkflowContext context, string routeContextKey)
{
if (TryResolveRouteTarget(context, routeContextKey, out var targetStepId))
{
return targetStepId;
}
throw new InvalidOperationException($"步骤 '{Id}' 未找到有效的路由目标。上下文键: '{routeContextKey}'。");
}
private bool TryResolveRouteBranchTarget<T>(WorkflowContext context, string routeKey, IDictionary<T, string> routeTargets, out string targetStepId)
{
if (context != null &&
context.TryGetRoute<T>(routeKey, out var routeValue) &&
routeTargets != null &&
routeTargets.TryGetValue(routeValue, out targetStepId) &&
!string.IsNullOrWhiteSpace(targetStepId))
{
return true;
}
targetStepId = null;
return false;
}
private string ResolveRouteBranchTarget<T>(WorkflowContext context, string routeKey, IDictionary<T, string> routeTargets)
{
if (TryResolveRouteBranchTarget(context, routeKey, routeTargets, out var targetStepId))
{
return targetStepId;
}
throw new InvalidOperationException($"步骤 '{Id}' 未找到匹配的路由分支。路由键: '{routeKey}'。");
}
}
/// <summary>
/// 定义一个条件跳转规则。
/// </summary>
public class JumpCondition
{
public Func<WorkflowContext, ActivityResult, bool> Condition { get; } // 判断是否跳转的条件
public string TargetStepId { get; } // 满足条件时跳转到的目标步骤ID
private readonly Func<WorkflowContext, ActivityResult, string> _targetStepResolver;
public JumpCondition(Func<WorkflowContext, ActivityResult, bool> condition, string targetStepId)
: this(condition, (ctx, result) => targetStepId)
{
TargetStepId = targetStepId ?? throw new ArgumentNullException(nameof(targetStepId));
}
public JumpCondition(Func<WorkflowContext, ActivityResult, bool> condition, Func<WorkflowContext, ActivityResult, string> targetStepResolver)
{
Condition = condition ?? throw new ArgumentNullException(nameof(condition));
_targetStepResolver = targetStepResolver ?? throw new ArgumentNullException(nameof(targetStepResolver));
}
public string ResolveTargetStepId(WorkflowContext context, ActivityResult result)
{
var targetStepId = _targetStepResolver(context, result);
if (string.IsNullOrWhiteSpace(targetStepId))
{
throw new InvalidOperationException("跳转目标步骤ID不能为空。");
}
return targetStepId;
}
}
}

View File

@@ -0,0 +1,529 @@
using MW.WorkFlow;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MW.WorkFlow
{
/// <summary>
/// 负责执行工作流活动,并支持暂停、恢复、停止、重启和跳转,基于工作流定义和取消功能。
/// </summary>
public class WorkflowEngine : IDisposable
{
private const string WorkflowNameContextKey = "WorkflowName";
private const string ParentActivityNameContextKey = "ParentActivityName";
private const string CurrentStepIdContextKey = "CurrentStepId";
private const string NextStepIdContextKey = "NextStepId";
private const string WorkflowRuntimeTrackerContextKey = "WorkflowRuntimeTracker";
private const string WorkflowFailureMessageContextKey = "WorkflowFailureMessage";
private const string WorkflowFailureMessageKeyContextKey = "WorkflowFailureMessageKey";
private const string WorkflowFailureMessageArgumentsContextKey = "WorkflowFailureMessageArguments";
private readonly WorkflowDefinition _workflowDefinition;
private readonly WorkflowContext _context;
private string _currentStepId;
private volatile bool _isPausedRequested; // 外部请求暂停的标志
private TaskCompletionSource<bool> _currentPauseSignal; // 当前的暂停信号
private CancellationTokenSource _workflowCancellationTokenSource;
private Task _workflowExecutionTask;
private bool disposedValue;
private readonly object _stateLock = new object(); // 用于保护共享状态的锁
// 当前工作流的状态
public WorkflowState CurrentState { get; private set; } = WorkflowState.Created;
public bool IsWorkflowPaused => _isPausedRequested;
public bool IsWorkflowFinished => _currentStepId == null;
public bool IsWorkflowRunning => _workflowExecutionTask != null && !_workflowExecutionTask.IsCompleted;
public string CurrentStepId => _currentStepId;
public Exception LastException { get; private set; }
public WorkflowEngine(WorkflowDefinition workflowDefinition, WorkflowContext context)
{
_workflowDefinition = workflowDefinition ?? throw new ArgumentNullException(nameof(workflowDefinition));
_context = context ?? throw new ArgumentNullException(nameof(context));
_currentStepId = workflowDefinition.InitialStepId;
_isPausedRequested = false;
_currentPauseSignal = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
_currentPauseSignal.SetResult(true); // 初始时已经完成
}
// 启动工作流的执行,可选参数允许指定起始步骤
public void Start(string startStepId = null)
{
lock (_stateLock)
{
if (IsWorkflowRunning)
{
Debug.WriteLine("[WorkflowEngine] 工作流已经在运行中,无法重复启动。");
return;
}
// 核心修改逻辑:处理起始步骤
if (!string.IsNullOrEmpty(startStepId))
{
// 验证步骤是否存在
if (!_workflowDefinition.ContainsStep(startStepId))
{
throw new ArgumentException($"指定的起始步骤ID '{startStepId}' 不存在于工作流定义中。", nameof(startStepId));
}
_currentStepId = startStepId;
}
else if (_currentStepId == null)
{
// 如果未指定且当前没有步骤(例如上次运行已结束),重置为初始步骤
_currentStepId = _workflowDefinition.InitialStepId;
}
LastException = null;
UpdateWorkflowState(WorkflowState.Running);
_isPausedRequested = false;
_workflowCancellationTokenSource?.Dispose();
_workflowCancellationTokenSource = new CancellationTokenSource();
// 确保 _currentPauseSignal 处于已完成状态,以便工作流立即开始
if (!_currentPauseSignal.Task.IsCompleted)
{
_currentPauseSignal.TrySetResult(true);
}
// 使用 Task.Run(ExecuteWorkflowInternalAsync) 避免嵌套 Task<Task>
_workflowExecutionTask = Task.Run(ExecuteWorkflowInternalAsync);
// 注册仅在发生未处理异常时的续体以记录异常,避免未观察异常
_workflowExecutionTask.ContinueWith(t =>
{
Debug.WriteLine($"[WorkflowEngine] 工作流任务发生未处理异常: {t.Exception?.Flatten().InnerException}");
}, TaskContinuationOptions.OnlyOnFaulted);
Debug.WriteLine($"[WorkflowEngine] 工作流已启动,从步骤 '{_currentStepId}' 开始。");
}
}
// 异步执行工作流中的活动
private async Task ExecuteWorkflowInternalAsync()
{
Debug.WriteLine("\n[WorkflowEngine] 工作流执行开始。");
CancellationToken cancellationToken = _workflowCancellationTokenSource.Token;
Func<Task> pauseCheck = async () =>
{
cancellationToken.ThrowIfCancellationRequested();
if (_isPausedRequested)
{
Debug.WriteLine("[WorkflowEngine] 工作流已暂停,等待恢复...");
var pauseTask = _currentPauseSignal.Task;
var cancelTask = Task.Delay(Timeout.Infinite, cancellationToken);
var finished = await Task.WhenAny(pauseTask, cancelTask).ConfigureAwait(false);
if (finished == cancelTask)
{
// 如果取消任务先完成,则抛出取消
cancellationToken.ThrowIfCancellationRequested();
}
// 等待 pauseTask 以传播其可能的异常/取消
await pauseTask.ConfigureAwait(false);
Debug.WriteLine("[WorkflowEngine] 工作流已恢复。");
}
};
ActivityControl activityControl = new ActivityControl(cancellationToken, pauseCheck);
WorkflowStep currentStep = null;
try
{
while (_currentStepId != null)
{
cancellationToken.ThrowIfCancellationRequested();
await pauseCheck().ConfigureAwait(false);
try
{
currentStep = _workflowDefinition.GetStep(_currentStepId);
}
catch (ArgumentException ex)
{
Debug.WriteLine($"[WorkflowEngine] 错误无法找到步骤ID '{_currentStepId}'。工作流终止。原因: {ex.Message}");
HandleWorkflowFault(ex, currentStep);
_currentStepId = null;
break;
}
IActivity currentActivity = currentStep.Activity;
_context.SetData(WorkflowFailureMessageContextKey, string.Empty);
_context.SetData<object>(WorkflowFailureMessageKeyContextKey, null);
_context.SetData<object[]>(WorkflowFailureMessageArgumentsContextKey, null);
UpdateExecutionContext(currentStep);
TrackExecutionPointer(currentStep, currentStep.Id);
Debug.WriteLine($"\n[WorkflowEngine] 执行步骤: {currentStep.Id}, 活动: {currentActivity.Name}");
ActivityResult result = ActivityResult.Failure;
try
{
result = await currentActivity.ExecuteAsync(_context, activityControl).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
Debug.WriteLine($"[WorkflowEngine] 活动 '{currentActivity.Name}' 被取消。");
result = ActivityResult.Canceled;
if (CurrentState != WorkflowState.Stopped)
{
UpdateWorkflowState(WorkflowState.Canceled);
}
break;
}
catch (Exception ex)
{
Debug.WriteLine($"[WorkflowEngine] 活动 '{currentActivity.Name}' 执行时发生未处理的异常: {ex.Message}");
result = ActivityResult.Failure;
HandleWorkflowFault(ex, currentStep);
break;
}
Debug.WriteLine($"[WorkflowEngine] 步骤 '{currentStep.Id}' 完成,结果: {result}");
// 如果状态已变为 Faulted (可能在上面的 catch 中没 break 住,或者其他逻辑导致),双重保险停止
if (CurrentState == WorkflowState.Faulted || CurrentState == WorkflowState.Canceled)
{
break;
}
if (result == ActivityResult.Failure)
{
string failureMessage = ResolveWorkflowFailureMessage(currentActivity.Name);
Debug.WriteLine($"[WorkflowEngine] {failureMessage}");
HandleWorkflowFailure(failureMessage, currentStep);
break;
}
string nextStepCandidateId = null;
foreach (var condition in currentStep.JumpConditions)
{
if (condition.Condition(_context, result))
{
nextStepCandidateId = condition.ResolveTargetStepId(_context, result);
Debug.WriteLine($"[WorkflowEngine] 条件跳转满足,跳转到步骤 '{nextStepCandidateId}'。");
break;
}
}
if (nextStepCandidateId == null && !string.IsNullOrEmpty(currentStep.NextStepId))
{
nextStepCandidateId = currentStep.NextStepId;
Debug.WriteLine($"[WorkflowEngine] 默认跳转,跳转到步骤 '{nextStepCandidateId}'。");
}
if (!string.IsNullOrWhiteSpace(nextStepCandidateId) && !_workflowDefinition.ContainsStep(nextStepCandidateId))
{
throw new InvalidOperationException($"步骤 '{currentStep.Id}' 解析出的目标步骤 '{nextStepCandidateId}' 不存在于工作流定义中。");
}
TrackExecutionPointer(currentStep, nextStepCandidateId);
lock (_stateLock)
{
if (_currentStepId == currentStep.Id)
{
_currentStepId = nextStepCandidateId;
}
else
{
Debug.WriteLine($"[WorkflowEngine] 外部已修改当前步骤,跳过内部跳转逻辑。");
}
}
if (_currentStepId == null)
{
UpdateWorkflowState(WorkflowState.Completed);
Debug.WriteLine($"[WorkflowEngine] 工作流已完成。");
}
}
}
catch (OperationCanceledException)
{
Debug.WriteLine("[WorkflowEngine] 工作流执行被取消。");
if (CurrentState != WorkflowState.Stopped)
{
UpdateWorkflowState(WorkflowState.Canceled);
}
}
catch (Exception ex)
{
Debug.WriteLine($"[WorkflowEngine] 工作流执行过程中发生未处理的异常: {ex.Message}");
HandleWorkflowFault(ex, currentStep);
}
finally
{
Debug.WriteLine("[WorkflowEngine] 工作流执行结束。");
lock (_stateLock)
{
_workflowExecutionTask = null;
_currentStepId = null;
_workflowCancellationTokenSource?.Dispose();
_workflowCancellationTokenSource = null;
}
}
}
// 更新工作流状态,并记录日志
private void UpdateWorkflowState(WorkflowState newState)
{
if (CurrentState != newState)
{
CurrentState = newState;
Debug.WriteLine($"[WorkflowEngine] 工作流状态已更改: {CurrentState}");
}
}
private void HandleWorkflowFault(Exception ex, WorkflowStep currentStep)
{
LastException = ex;
UpdateWorkflowState(WorkflowState.Faulted);
if (_context.TryGetData<IWorkflowRuntimeTracker>(WorkflowRuntimeTrackerContextKey, out var tracker) && tracker != null)
{
_context.TryGetData<string>(WorkflowNameContextKey, out var workflowName);
_context.TryGetData<string>(ParentActivityNameContextKey, out var flowNameFromContext);
_context.TryGetData<string>(CurrentStepIdContextKey, out var currentStepIdFromContext);
tracker.ReportWorkflowFault(new WorkflowFaultInfo
{
WorkflowName = workflowName,
FlowName = currentStep?.FlowName ?? flowNameFromContext,
ActivityName = currentStep?.Activity?.Name,
CurrentStepId = currentStep?.Id ?? currentStepIdFromContext ?? _currentStepId,
ErrorMessage = ex?.Message,
OccurredAt = DateTime.Now
});
}
}
private void HandleWorkflowFailure(string errorMessage, WorkflowStep currentStep)
{
LastException = new InvalidOperationException(errorMessage);
UpdateWorkflowState(WorkflowState.Faulted);
if (_context.TryGetData<IWorkflowRuntimeTracker>(WorkflowRuntimeTrackerContextKey, out var tracker) && tracker != null)
{
_context.TryGetData<string>(WorkflowNameContextKey, out var workflowName);
_context.TryGetData<string>(ParentActivityNameContextKey, out var flowNameFromContext);
_context.TryGetData<string>(CurrentStepIdContextKey, out var currentStepIdFromContext);
tracker.ReportWorkflowFault(new WorkflowFaultInfo
{
WorkflowName = workflowName,
FlowName = currentStep?.FlowName ?? flowNameFromContext,
ActivityName = currentStep?.Activity?.Name,
CurrentStepId = currentStep?.Id ?? currentStepIdFromContext ?? _currentStepId,
ErrorMessage = errorMessage,
OccurredAt = DateTime.Now
});
}
}
private string ResolveWorkflowFailureMessage(string activityName)
{
string failureMessage;
if (_context.TryGetData<string>(WorkflowFailureMessageContextKey, out failureMessage) &&
!string.IsNullOrWhiteSpace(failureMessage))
{
return failureMessage;
}
return $"活动 '{activityName}' 返回 Failure工作流按失败结束。";
}
private void UpdateExecutionContext(WorkflowStep currentStep)
{
_context.SetData(CurrentStepIdContextKey, currentStep.Id);
_context.SetData(NextStepIdContextKey, currentStep.Id);
_context.SetData(ParentActivityNameContextKey, currentStep.FlowName ?? string.Empty);
}
private void TrackExecutionPointer(WorkflowStep currentStep, string nextStepId)
{
_context.SetData(NextStepIdContextKey, nextStepId);
if (_context.TryGetData<IWorkflowRuntimeTracker>(WorkflowRuntimeTrackerContextKey, out var tracker) && tracker != null)
{
_context.TryGetData<string>(WorkflowNameContextKey, out var workflowName);
tracker.UpdateExecutionPointer(new WorkflowExecutionPointer
{
WorkflowName = workflowName,
FlowName = currentStep?.FlowName,
ActivityName = currentStep?.Activity?.Name,
CurrentStepId = currentStep?.Id,
NextStepId = nextStepId,
UpdatedAt = DateTime.Now
});
}
}
// 暂停工作流
public void Pause()
{
lock (_stateLock)
{
if (!IsWorkflowRunning)
{
Debug.WriteLine("[WorkflowEngine] 工作流未运行,无法暂停。");
return;
}
if (_isPausedRequested)
{
Debug.WriteLine("[WorkflowEngine] 工作流已处于暂停状态。");
return;
}
UpdateWorkflowState(WorkflowState.Paused);
_isPausedRequested = true;
if (_currentPauseSignal.Task.IsCompleted)
{
_currentPauseSignal = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
}
Debug.WriteLine("[WorkflowEngine] 工作流已请求暂停。");
}
}
// 恢复工作流
public void Resume()
{
lock (_stateLock)
{
if (_isPausedRequested)
{
_currentPauseSignal.TrySetResult(true);
_isPausedRequested = false;
UpdateWorkflowState(WorkflowState.Running);
Debug.WriteLine("[WorkflowEngine] 工作流已恢复。");
}
else
{
Debug.WriteLine("[WorkflowEngine] 工作流未暂停,无需恢复。");
}
}
}
// 停止工作流
public async Task StopAsync()
{
Task taskToAwait = null;
lock (_stateLock)
{
if (!IsWorkflowRunning)
{
Debug.WriteLine("[WorkflowEngine] 工作流未运行,无法停止。");
return;
}
if (_workflowCancellationTokenSource?.IsCancellationRequested == true)
{
taskToAwait = _workflowExecutionTask;
}
else
{
Debug.WriteLine("[WorkflowEngine] 已请求停止工作流。");
UpdateWorkflowState(WorkflowState.Stopped);
_workflowCancellationTokenSource?.Cancel();
if (_isPausedRequested)
{
Resume();
}
taskToAwait = _workflowExecutionTask;
}
}
if (taskToAwait != null)
{
try
{
await taskToAwait.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
Debug.WriteLine("[WorkflowEngine] 工作流任务被取消。");
}
catch (Exception ex)
{
Debug.WriteLine($"[WorkflowEngine] 停止工作流时发生异常: {ex.Message}");
}
}
lock (_stateLock)
{
_workflowExecutionTask = null;
_currentStepId = null;
_workflowCancellationTokenSource?.Dispose();
_workflowCancellationTokenSource = null;
_isPausedRequested = false;
Debug.WriteLine("[WorkflowEngine] 工作流已停止。");
}
}
// 重启工作流
public async Task RestartAsync(string startFromStepId = null, bool clearContext = true)
{
await StopAsync().ConfigureAwait(false);
lock (_stateLock)
{
if (clearContext)
{
_context.Clear();
}
_currentStepId = startFromStepId ?? _workflowDefinition.InitialStepId;
}
Start();
}
// 跳转到指定步骤
public void JumpTo(string stepId)
{
lock (_stateLock)
{
if (!_workflowDefinition.GetAllStepIds().Contains(stepId))
{
throw new ArgumentException($"步骤ID '{stepId}' 不存在。", nameof(stepId));
}
_currentStepId = stepId;
var step = _workflowDefinition.GetStep(stepId);
UpdateExecutionContext(step);
TrackExecutionPointer(step, stepId);
Debug.WriteLine($"[WorkflowEngine] 跳转到步骤 '{stepId}'。");
if (IsWorkflowPaused)
{
Resume();
}
}
}
// 将异步 Dispose 改为同步;在 .NET Framework 中不要在 Dispose 中使用 async void/Task 未等待
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
if (IsWorkflowRunning)
{
// 同步等待 StopAsync 完成以确保资源清理
StopAsync().GetAwaiter().GetResult();
}
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MW.WorkFlow
{
public enum WorkflowState
{
Created, // 工作流刚创建
Running, // 工作流正在运行
Paused, // 工作流已暂停
Stopped, // 工作流已停止
Completed, // 工作流已完成
Canceled, // 工作流已取消
Faulted // 工作流执行时发生异常
}
public class WorkflowExecutionPointer
{
public string WorkflowName { get; set; }
public string FlowName { get; set; }
public string ActivityName { get; set; }
public string CurrentStepId { get; set; }
public string NextStepId { get; set; }
public DateTime UpdatedAt { get; set; }
}
public class WorkflowFaultInfo
{
public string WorkflowName { get; set; }
public string FlowName { get; set; }
public string ActivityName { get; set; }
public string CurrentStepId { get; set; }
public string ErrorMessage { get; set; }
public DateTime OccurredAt { get; set; }
}
public interface IWorkflowRuntimeTracker
{
void UpdateExecutionPointer(WorkflowExecutionPointer executionPointer);
void ReportWorkflowFault(WorkflowFaultInfo faultInfo);
}
}