我們搭建框架,一方面是需要滿足業務的需求、易於后期擴展,另一方面,需要減少開發人員的工作量,讓他們不需要整體理解框架的情況下,照樣能接入團隊進行業務開發。
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); }