spritekuang

导航

企业级工作流解决方案(十七)--工作流--工作流插件模型

  我们搭建框架,一方面是需要满足业务的需求、易于后期扩展,另一方面,需要减少开发人员的工作量,让他们不需要整体理解框架的情况下,照样能接入团队进行业务开发。

  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);
    }

 

posted on 2019-05-12 17:00  spritekuang  阅读(1185)  评论(2编辑  收藏  举报