274 lines
10 KiB
C#
274 lines
10 KiB
C#
|
|
using MainShell.Common;
|
|||
|
|
using MainShell.ProcessResult;
|
|||
|
|
using MW.WorkFlow;
|
|||
|
|
using Stylet;
|
|||
|
|
using System;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
|
|||
|
|
namespace MainShell.Process
|
|||
|
|
{
|
|||
|
|
public class WorkflowRunCompletedEventArgs : EventArgs
|
|||
|
|
{
|
|||
|
|
public WorkflowState FinalState { get; }
|
|||
|
|
public string Error { get; }
|
|||
|
|
public string FailureMessage { get; }
|
|||
|
|
public MessageKey FailureMessageKey { get; }
|
|||
|
|
public object[] FailureMessageArguments { get; }
|
|||
|
|
|
|||
|
|
public WorkflowRunCompletedEventArgs(WorkflowState finalState, string error, string failureMessage, MessageKey failureMessageKey, object[] failureMessageArguments)
|
|||
|
|
{
|
|||
|
|
FinalState = finalState;
|
|||
|
|
Error = error;
|
|||
|
|
FailureMessage = failureMessage;
|
|||
|
|
FailureMessageKey = failureMessageKey;
|
|||
|
|
FailureMessageArguments = failureMessageArguments ?? Array.Empty<object>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 封装工作流的启动、停止和状态管理,用于手动流程的复用。
|
|||
|
|
/// </summary>
|
|||
|
|
public class
|
|||
|
|
WorkflowRunner : PropertyChangedBase, IDisposable
|
|||
|
|
{
|
|||
|
|
private WorkflowEngine _workflowEngine;
|
|||
|
|
private bool _isRunning;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 流程是否正在运行
|
|||
|
|
/// </summary>
|
|||
|
|
public bool IsRunning
|
|||
|
|
{
|
|||
|
|
get { return _isRunning; }
|
|||
|
|
private set { SetAndNotify(ref _isRunning, value); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public event EventHandler<WorkflowRunCompletedEventArgs> RunCompleted;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 运行单个 Activity(自动包装为单步工作流)
|
|||
|
|
/// </summary>
|
|||
|
|
public Task RunActivityAsync(IActivity activity, WorkflowContext context)
|
|||
|
|
{
|
|||
|
|
if (activity == null) throw new ArgumentNullException(nameof(activity));
|
|||
|
|
|
|||
|
|
// 自动构建单步工作流定义
|
|||
|
|
var stepId = "SingleStep_" + Guid.NewGuid().ToString("N");
|
|||
|
|
var step = new WorkflowStep(stepId, activity);
|
|||
|
|
var definition = new WorkflowDefinition(stepId);
|
|||
|
|
definition.AddStep(step);
|
|||
|
|
|
|||
|
|
return RunAsync(definition, context);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public Task<WorkflowRunCompletedEventArgs> RunActivityWithResultAsync(IActivity activity, WorkflowContext context)
|
|||
|
|
{
|
|||
|
|
if (activity == null) throw new ArgumentNullException(nameof(activity));
|
|||
|
|
|
|||
|
|
var stepId = "SingleStep_" + Guid.NewGuid().ToString("N");
|
|||
|
|
var step = new WorkflowStep(stepId, activity);
|
|||
|
|
var definition = new WorkflowDefinition(stepId);
|
|||
|
|
definition.AddStep(step);
|
|||
|
|
|
|||
|
|
return RunWithResultAsync(definition, context, null);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 运行完整的工作流定义
|
|||
|
|
/// </summary>
|
|||
|
|
public async Task RunAsync(WorkflowDefinition definition, WorkflowContext context)
|
|||
|
|
{
|
|||
|
|
await RunWithResultAsync(definition, context, null);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public Task<WorkflowRunCompletedEventArgs> RunWithResultAsync(WorkflowDefinition definition, WorkflowContext context)
|
|||
|
|
{
|
|||
|
|
return RunWithResultAsync(definition, context, null);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 运行完整的工作流定义,并支持从指定步骤启动。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="definition">工作流定义</param>
|
|||
|
|
/// <param name="context">工作流上下文</param>
|
|||
|
|
/// <param name="startStepId">可选:指定起始步骤ID;为 null/空时从初始步骤启动</param>
|
|||
|
|
public async Task RunAsync(WorkflowDefinition definition, WorkflowContext context, string startStepId)
|
|||
|
|
{
|
|||
|
|
await RunWithResultAsync(definition, context, startStepId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task<WorkflowRunCompletedEventArgs> RunWithResultAsync(WorkflowDefinition definition, WorkflowContext context, string startStepId)
|
|||
|
|
{
|
|||
|
|
if (definition == null) throw new ArgumentNullException(nameof(definition));
|
|||
|
|
if (context == null) throw new ArgumentNullException(nameof(context));
|
|||
|
|
|
|||
|
|
if (IsRunning)
|
|||
|
|
{
|
|||
|
|
throw new InvalidOperationException("当前已有流程正在运行,无法重复启动。");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!string.IsNullOrWhiteSpace(startStepId) && !definition.GetAllStepIds().Contains(startStepId))
|
|||
|
|
{
|
|||
|
|
throw new ArgumentException($"指定的起始步骤ID '{startStepId}' 不存在于工作流定义中。", nameof(startStepId));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var resolvedStartStepId = ResolveStartStepId(definition, context, startStepId);
|
|||
|
|
|
|||
|
|
var errorStr = string.Empty;
|
|||
|
|
WorkflowState finalState = WorkflowState.Created;
|
|||
|
|
MessageKey failureMessageKey = MessageKey.None;
|
|||
|
|
object[] failureMessageArguments = Array.Empty<object>();
|
|||
|
|
string failureMessage = string.Empty;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
IsRunning = true;
|
|||
|
|
_workflowEngine = new WorkflowEngine(definition, context);
|
|||
|
|
_workflowEngine.Start(resolvedStartStepId);
|
|||
|
|
|
|||
|
|
// 异步轮询等待工作流结束
|
|||
|
|
// 这样可以不阻塞 UI 线程,同时保持 IsRunning 状态正确
|
|||
|
|
while (_workflowEngine != null && _workflowEngine.IsWorkflowRunning)
|
|||
|
|
{
|
|||
|
|
await Task.Delay(100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 捕获最终状态(Dispose 之前)
|
|||
|
|
if (_workflowEngine != null)
|
|||
|
|
{
|
|||
|
|
finalState = _workflowEngine.CurrentState;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
failureMessageKey = ResolveFailureMessageKey(context);
|
|||
|
|
failureMessageArguments = ResolveFailureMessageArguments(context);
|
|||
|
|
failureMessage = ResolveFailureMessage(context, failureMessageKey, failureMessageArguments);
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
// 无论正常结束、异常还是被取消,都确保清理资源和复位状态
|
|||
|
|
errorStr = _workflowEngine?.LastException?.ToString();
|
|||
|
|
IsRunning = false;
|
|||
|
|
_workflowEngine?.Dispose();
|
|||
|
|
_workflowEngine = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var completedArgs = new WorkflowRunCompletedEventArgs(finalState, errorStr, failureMessage, failureMessageKey, failureMessageArguments);
|
|||
|
|
RunCompleted?.Invoke(this, completedArgs);
|
|||
|
|
|
|||
|
|
return completedArgs;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 从指定步骤启动工作流(语义化封装)。
|
|||
|
|
/// </summary>
|
|||
|
|
public Task RunFromStepAsync(WorkflowDefinition definition, WorkflowContext context, string startStepId)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrWhiteSpace(startStepId))
|
|||
|
|
throw new ArgumentNullException(nameof(startStepId));
|
|||
|
|
|
|||
|
|
return RunAsync(definition, context, startStepId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public Task<WorkflowRunCompletedEventArgs> RunFromStepWithResultAsync(WorkflowDefinition definition, WorkflowContext context, string startStepId)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrWhiteSpace(startStepId))
|
|||
|
|
throw new ArgumentNullException(nameof(startStepId));
|
|||
|
|
|
|||
|
|
return RunWithResultAsync(definition, context, startStepId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private string ResolveStartStepId(WorkflowDefinition definition, WorkflowContext context, string startStepId)
|
|||
|
|
{
|
|||
|
|
if (!string.IsNullOrWhiteSpace(startStepId))
|
|||
|
|
{
|
|||
|
|
return startStepId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!context.TryGetData<string>(WorkflowContextKeys.WorkflowName, out var workflowName) ||
|
|||
|
|
string.IsNullOrWhiteSpace(workflowName) ||
|
|||
|
|
!context.TryGetData<ProcessResultManager>(WorkflowContextKeys.ProcessResultManager, out var resultManager) ||
|
|||
|
|
resultManager == null ||
|
|||
|
|
!resultManager.TryGetResumeStepId(workflowName, out var resumeStepId) ||
|
|||
|
|
!definition.ContainsStep(resumeStepId))
|
|||
|
|
{
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return resumeStepId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private MessageKey ResolveFailureMessageKey(WorkflowContext context)
|
|||
|
|
{
|
|||
|
|
MessageKey failureMessageKey;
|
|||
|
|
if (context.TryGetData<MessageKey>(WorkflowContextKeys.WorkflowFailureMessageKey, out failureMessageKey))
|
|||
|
|
{
|
|||
|
|
return failureMessageKey;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return MessageKey.None;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private object[] ResolveFailureMessageArguments(WorkflowContext context)
|
|||
|
|
{
|
|||
|
|
object[] failureMessageArguments;
|
|||
|
|
if (context.TryGetData<object[]>(WorkflowContextKeys.WorkflowFailureMessageArguments, out failureMessageArguments) &&
|
|||
|
|
failureMessageArguments != null)
|
|||
|
|
{
|
|||
|
|
return failureMessageArguments;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return Array.Empty<object>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private string ResolveFailureMessage(WorkflowContext context, MessageKey failureMessageKey, object[] failureMessageArguments)
|
|||
|
|
{
|
|||
|
|
if (failureMessageKey != MessageKey.None)
|
|||
|
|
{
|
|||
|
|
return LanguageResourceHelper.Format(failureMessageKey, failureMessageArguments);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
string failureMessage;
|
|||
|
|
if (context.TryGetData<string>(WorkflowContextKeys.WorkflowFailureMessage, out failureMessage) &&
|
|||
|
|
!string.IsNullOrWhiteSpace(failureMessage))
|
|||
|
|
{
|
|||
|
|
return failureMessage;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return string.Empty;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 请求停止当前流程
|
|||
|
|
/// </summary>
|
|||
|
|
public async Task StopAsync()
|
|||
|
|
{
|
|||
|
|
if (_workflowEngine != null && _workflowEngine.IsWorkflowRunning)
|
|||
|
|
{
|
|||
|
|
await _workflowEngine.StopAsync();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Pause()
|
|||
|
|
{
|
|||
|
|
if (_workflowEngine != null && _workflowEngine.IsWorkflowRunning)
|
|||
|
|
{
|
|||
|
|
_workflowEngine.Pause();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Resume()
|
|||
|
|
{
|
|||
|
|
if (_workflowEngine != null && _workflowEngine.IsWorkflowRunning)
|
|||
|
|
{
|
|||
|
|
_workflowEngine.Resume();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Dispose()
|
|||
|
|
{
|
|||
|
|
_workflowEngine?.Dispose();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|