我们搭建框架,一方面是需要满足业务的需求、易于后期扩展,另一方面,需要减少开发人员的工作量,让他们不需要整体理解框架的情况下,照样能接入团队进行业务开发。
WWF工作流开发平台对于很多开发人员都比较陌生,如果让每一个开发人员都充分的理解WWF的定义时和运行时的机制,再接入工作流开发,不管是时间还是成本都是不能接受的。
我们需要搭建一套工作流开发框架,把WWF的定义时和运行时的逻辑都高度的抽象出来进行封装,让不同的开发者只需要关注业务部分的实现即可。
工作流插件模型设计图:
一个WWF工作流,必然有一个唯一的根活动,一般都为组合活动FlowChart,一个组合活动可以包含任何数量的组合活动或者单个活动,活动与活动之间可以通过连线建立关联,工作流运行时,从起点根据连线往下面执行活动逻辑。
WWF允许我们自定义活动,需要实现活动基娄NativeActivity,自定义活动包括自定义活动设计器以及自定义活动本身,在活动设计器中,我们可以定义双击活动设计器行为,比如双击活动设计器打开活动配置窗体,活动设计器与活动之间的关联是通过ModelItem关联起来的,即在活动的ModelItem里面可以存取活动配置信息。
我们可以在自定义活动增加一个属性,ConfigData,存储活动的配置信息,流程定义时,通过活动配置窗体配置信息,然后把配置信息序列化为字符串,保存到流程定义模版中,设计时和运行时都可以取出ConfigData信息。
流程运行时,可以把运行时信息持久化到外部存储介质,可以是数据库或者Xml,持久化时,需要指定恢复的书签值,下次运行时,传递书签值到流程引擎,就可以恢复到指定的活动。
一个活动可以定义一个插件与之对应,活动只做一些简单的配置以及对插件的封装,基本不处理具体业务,真正的业务执行交由插件来完成。业务插件也不需要关心活动是什么,以及怎么封装的,只需要执行业务逻辑,需要实现以下几部分内容:
插件需要定义插件配置窗体,双击活动时动态创建窗体,弹出配置窗体,把活动定义时上下文信息传递到配置窗体中,在配置窗体中就可以读取和写入配置信息,由框架负责存储配置信息。插件的Execute方法为运行时活动执行的方法,需要把流程的配置信息取出来做为参数传递给插件的Execute方法。CallbackExecute为恢复书签时执行的方法,工作流恢复时调用此方法。
插件执行定义时活动运行时是不知道工作流的信息的,但是有些时候,需要读取流程的信息,比如运行时工作流的参数集合,定义时流程实例的一些信息,那么这些信息都从IPluginContext插件上下文中获取。
关于ConfigData,插件框架定义的是string,插件可以定义自己的业务配置接口,定义时序列化为字符串,运行是反序列化为业务配置接口,方便业务处理。
流程发起时会创建流程实例,流程结束时,会更新流程实例状态,流程执行到每一个活动,会创建流程审批项以及流程跟踪信息,流程审批项可以定义TaskId(Guid)做为书签值,某一个用户打开任务时,执行审批,根据TaskId恢复流程运行。
自定义活动基类:
public abstract class BaseActivity : NativeActivity
{
private object wfworkflowContext;
#region Property
/// <summary>
/// 活动对应的插件
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IPlugin Plugin { get; set; }
/// <summary>
/// 活动Id
/// </summary>
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string ActivityId {
get
{
return this.Id;
}
}
/// <summary>
/// 工作流配置信息
/// </summary>
[Browsable(false)]
public string ConfigData { get; set; }
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public abstract string ActivityIcon { get; }
#endregion
protected BaseActivity()
{
Plugin = GetPlugin();
}
protected abstract IPlugin GetPlugin();
protected abstract override void Execute(NativeActivityContext context);
protected void SetTryRunParams(PluginContext pluginContext, WFWorkflowContext wfworkflowContext)
{
var unitOfWorkManager = IocManager.Instance.Resolve<IUnitOfWorkManager>();
UnitOfWorkOptions unitOfWorkOptions = new UnitOfWorkOptions();
using (var uow = unitOfWorkManager.Begin(unitOfWorkOptions))
{
var wf_WF_FlowItemRepository = IocManager.Instance.Resolve<IRepository<WF_FlowItem, Guid>>();
var wf_WF_FlowTrackItemRepository = IocManager.Instance.Resolve<IRepository<WF_FlowTrack, Guid>>();
WF_FlowItem flowItem = wf_WF_FlowItemRepository.Get(wfworkflowContext.WFActivityRunData.FlowItemId);
WF_FlowTrack flowTrack = wf_WF_FlowTrackItemRepository.GetAll()
.Where(r => (r.WF_FlowInstance_Id == flowItem.WF_FlowInstance_Id) && (r.ActivityId == Id)).OrderByDescending(r => r.TrackTime).FirstOrDefault();
if (flowTrack != null && !string.IsNullOrEmpty(flowTrack.ActivityParam))
{
pluginContext.RuntimeService.SetArgValue(flowTrack.ActivityParam, flowTrack.RouteValue);
}
uow.Complete();
}
}
}
插件基类:
public interface IPlugin
{
/// <summary>
/// Plugin上下文
/// </summary>
IPluginContext PluginContext { get; set; }
/// <summary>
/// 工作流运行时调用的方法
/// </summary>
/// <param name="configData">ConfigData</param>
/// <returns>调用反回值</returns>
bool Execute(IConfigData configData);
/// <summary>
/// 返回工作流设计时,活动弹出的窗体
/// </summary>
/// <param name="configData">ConfigData</param>
/// <param name="workflowDesignService">工作流设计时上下文</param>
/// <param name="typeFact">事实库类型</param>
/// <returns>活动弹出窗体</returns>
IConfigForm GetConfigForm(IConfigData configData, WorkflowDesignService workflowDesignService, List<TemplateModel> factModel, Guid flowDefineId);
/// <summary>
/// 恢复书签调用的回调方法
/// </summary>
/// <param name="configData">ConfigData</param>
/// <param name="wfactivityRunData">工作流运行时数据</param>
/// <returns>调用反回值</returns>
bool CallbackExecute(IConfigData configData, IWFActivityRunData wfactivityRunData);
}
|