菲暃罪霏的博客

品高工作流

导航

品高工作流 - 高级扩展教程 For Procez One Ver.4.0

一、      工作流应用架构与扩展能力

image

品高工作流的架构图

      从上图我可以看到有个绿色的工作流业务扩展层,我们今天所讲的高级扩展主要是针对这一层的环节、迁移、条件、参与者等进行扩展,文中还提及了组织关系模型的数据库实现,如果有需要,也是可以进行重新实现全新的组织架构的。

 

二、      可以扩展的内容

u  环节 (Activity)

u  迁移 (Transition)

u  条件 (Condition)

u  参与者 (Actor)

u  组织架构 (Organization)

Bingosoft.Workflow.Busines.dll所提供的现有环节、迁移、条件、参与者等不支持业务逻辑所需要的功能时,我们就需要针对业务逻辑进行扩展了,下面先列出品高工作流4.0中已有的环节、迁移、条件、参与者和组织架构实现。

注:以下没有特别注明dll的环节、迁移、条件、参与者都是Bingosoft.Workflow.Busines.dll所提供的。

 

1.       现有环节

1)       基本活动环节

2)       审批步骤

3)       代理步骤

4)       提醒环节

5)       分发部门步骤

6)       子流程环节

7)       任务分发步骤

8)       并行流程开始步骤

9)       并行流程结束步骤

10)   结束步骤

11)   脚本环节 Bingosoft.Workflow.Dynamic.dll提供)

12)   MOSS列表环节(Bingosoft.Workflow.MOSS.dll提供)

13)   MOSS文档库环节(Bingosoft.Workflow.MOSS.dll提供)

 

2.       现有迁移

1)       基本迁移

2)       可回退迁移

3)       选择迁移

4)       可排序迁移

5)       筛选迁移

 

3.       现有条件

1)       复合条件

2)       环节条件

3)       XML条件

4)       选择条件

5)       数据表条件

6)       属性条件

7)       脚本条件(Bingosoft.Workflow.Dynamic.dll提供)

 

4.       现有参与者

1)       指定用户参与者

2)       用户角色参与者

3)       流程步骤参与者

4)       流程实例参与者

5)       扩展规则求解参与者

6)       业务数据求解参与者

7)       XML参与者

8)       用户选择参与者

9)       对象参与者

10)   脚本参与者(Bingosoft.Workflow.Dynamic.dll提供)

11)   SQL参与者(Bingosoft.Workflow.Dynamic.dll提供)

 

5.       组织架构的数据库实现

Bingosoft.Workflow.Providers.DB.dll所提供的组织架构模型实现的数据库表关系图:

clip_image003

 

三、      扩展工作流的要点

1.       流程扩展的详细步骤

1)         SDK安装、数据库部署。

2)         新建Procez.One.Site工程,配置数据库连接字符串

3)         新建工作流扩展类库,引用工作流相关dllKernelBusiness及其DataAccessProvider),AssemblyInfo文件中添加以下行:

clip_image005

4)         Site工程引用扩展类库,并修改web.configFlowProvider/library节点,添加扩展dll的文件名。

5)         复制扩展dll到工作流VS插件安装目录(Bingosoft\Procez One SDK\DesignerVSIP)下,配置相应的library节点(同上)。

(注:独立设计器则需要放到Plugin\BusinessWorkflow目录下,配置PluginInfo.xml,添加扩展dll文件名)

6)         重启VisualStudio2008程序,新建工作流流程定义工程,再新建一个流程定义文件,在工具箱就可以看到我们扩展的环节和迁移,在流程定义图中的环节和迁移的属性中分别可以看到我们扩展的参与者和条件。

(注:如果有用到模拟器来模拟流程的验证,也需要将扩展dll复制到模拟器所在目录,流程定义工程的app.configFlowProvider/library节点也需要添加该扩展dll的文件名)

7)         流程定义设计部署完成后就可以使用流程模拟器来验证流程定义或者使用Procez.One.Site站点来验证新建的流程定义。

 

2.       扩展的基本规则

XML序列化要求

为了流程定义能够方便地通过Web Service或者.Net Remoting等网络技术传输,流程对象需要通过WorkflowBuilder将流程定义序列化成XML字串或文件。为了能将对象序列化,对象设计时必须要遵循.NET技术的XML Serialization的要求,要求每个扩展对象加上Serializable的属性(Attribute)

clip_image007

 

DesignAttribut属性

由于扩展层有可能扩展更多的流程属性类型。而.NET XML序列化要求中是需要知道所有类型的,为了使WorkflowDesignerWorkflowFactory更加通用,这两个模块都会通过WorkflowProvider来得到所有需要XML Serialize的类型。

为了更容易标记这些类型,需要在这些类型前加上Design属性,并且设置IsWorkflowPropertytrue;

clip_image009

 

四、      扩展实例

 

1.       扩展环节(Activity

环节执行的要点:

1)         CanEnter()        环节可以进入时的准进判断

2)         Enter()           环节被触发时执行的动作处理

3)         EnterMode属性   代表流程环节迁入的模式,是需要手工激活迁入,还是自动执行进入

4)         ExitMode属性     代表流程环节迁出的模式,是需要手工激活迁出,还是自动执行迁出

5)         Execute()        环节被执行时执行的动作处理

6)         CanExit()        环节可以退出时的准退判断

7)         Exit()             环节结束执行的动作处理

 

自动跳转环节(MyResponseExtActivity

1)         描述:我是经理发起的流程申请就不需要自己再审批一次了,而且我也不想通过流程图的迁移条件来画出来。流程图如下所示:

clip_image011

流程说明:如果启动该流程是经理的话则直接跳过My审批 环节直接到备案 环节去,否则会提交到该用户的部门经理那里去审批。

 

2)         继承自审批环节(ResponseActivity

 

3)         自动跳转环节的扩展属性

bool IsSkipMyself  是否忽略本人的申请

 

4)         自动跳转环节的方法说明:

Ø  Enter()         判断申请人是不是本人,是则设置进入模式为自动进入模式。

Ø  ExitMode属性  申请人是本人则返回自动退出模式,否则返回手动退出模式。

Ø  Execute()       如果申请人是本人则跳过执行。

Ø  CanExit()       如果申请人是本人则可退出,否则不可退出。

Ø  Exit()             如果申请人是本人则跳过执行。

 

5)         重写的方法如下类详细代码中所示:

MyResponseExtActivity类的详细代码
    [Design("MyResponseExtActivity"""), Serializable]
    
public class MyResponseExtActivity : ResponseActivity
    {
        [Design(
"是否忽略本人的申请""在审批的时候,如果是本人申请的审批则跳过本环节直接进入下一个环节")]
        
public bool IsSkipMyself { getset; }

        
private bool hasMyself = false;

        
/// <summary>
        
/// 步骤被触发时执行的动作处理
        
/// </summary>
        public override void Enter(WorkflowEventArgs args)
        {
            
//判断是否忽略本人的申请
            if (IsSkipMyself)
            {
                BusinessEventArgs arg 
= new BusinessEventArgs(args);
                arg.Workflow 
= this.Owner as BusinessWorkflow;
                OrganizationProvider provider 
= OrganizationProvider.DefaultProvider;
                IUser curUser 
= provider.GetUserByID(((BusinessWorkflow)this.Owner).CreatorID);

                IList
<IParticipant> users = _participants.Resolve(arg);
                
if (users.Count < 1)
                    
throw new Exception("没找到步骤的处理人。");
                
//如果只有本人进行审批则跳过,否则执行审批环节
                if (users.Count == 1 && ((IUser)users[0]).ID == curUser.ID)
                {
                    hasMyself 
= true;
                    
//是本人则一定要设置进入模式为自动进入,不然会丢单
                    this.EnterMode = ActivityRunMode.Automatic;  
                }
                
else
                    
base.Enter(args);
            }
            
else
                
base.Enter(args);
        }

        
public override void Execute(WorkflowEventArgs e)
        {
            
//忽略本人且是自己则跳过Execute执行
            if (IsSkipMyself && hasMyself)
                
return;
            
else
                
base.Execute(e);
        }

        
public override bool CanExit(WorkflowEventArgs e)
        {
            
//忽略本人且是自己则直接允许推出
            if (IsSkipMyself && hasMyself)
                
return true;
            
else
                
return base.CanExit(e);
        }

        
/// <summary>
        
/// 步骤结束执行的动作处理
        
/// </summary>
        public override void Exit(WorkflowEventArgs args)
        {
            
//忽略本人且是自己则跳过Exit执行
            if (IsSkipMyself && hasMyself)
                
return;
            
else
                
base.Exit(args);
        }

        
public override ActivityRunMode ExitMode
        {
            
get
            {
                
//忽略本人且是自己则返回退出模式为自动退出
                if (IsSkipMyself && hasMyself)
                    
return ActivityRunMode.Automatic;
                
else
                    
return ActivityRunMode.Manual;
            }
        }
    }

 

  

2.       扩展迁移(Transition

扩展迁移要点:

Ø  加入自定义属性

 

例如:

1.         可排序迁移的 public uint SortIndex { get; set; }      

在选择下一个发送步骤时的次序  

 

2.         可回退迁移的 public bool AllowBackward { get; set; }   

是否允许回退 

 

3.         筛选迁移的   public string TransitionCode { get; set; }   

该迁移的标识,可用于筛选

 

Demo程序中实现了一个组合的迁移,既可排序,又可以允许回退的迁移,只需要增加SortIndexAllowBackward这两个属性,然后重写了IComparable接口的CompareTo方法

重写迁移的比较CompareTo函数代码
    #region IComparable Members
    
/// <summary>
    
/// 比较
    
/// </summary>
    
/// <param name="obj"></param>
    
/// <returns></returns>
    public int CompareTo(object obj)
    {
        SortableTransition objSortableNode 
= obj as SortableTransition;
        
if (null == objSortableNode)
            
return 1;

        
if (SortIndex > objSortableNode.SortIndex)
            
return 1;
        
else if (SortIndex == objSortableNode.SortIndex)
            
return 0;
        
else
            
return -1;
    }
    
#endregion

  

3.       扩展条件(Condition

扩展条件要点:

      Ø  重写Eval()方法

 

调用外部dll方法的条件MyDllCondition

1)         描述:我项目的条件计算很特别,我要调用一个外系统的接口来计算才能得出迁移条件是否成立。

clip_image013

流程说明:在启动 审批、备案 的迁移中有两个相反的条件,该条件回去调用外部dll类中的一个方法,方法的中会判断当前用户名是不是系统管理员,如果是则直接迁移到备案 环节,如果不是则会去审批 环节让该用户的部门经理去审批。

 

2)         继承自业务条件基类(BusinessCondition),重写的方法代码如下: 

重写条件的计算函数代码
    /// <summary>
    
///求解条件
    
/// </summary>
    
/// <param name="args"></param>
    
/// <returns></returns>
    public override ConditionResult Eval(WorkflowEventArgs args)
    {
        
try
        {
            handler.DllPath 
= this.DllPath;
            handler.TypeName 
= this.TypeName;
            handler.Method 
= this.Method;
            handler.ParameterMap 
= this.ParameterMap;

            
bool result = handler.Execute(args);
            
if (result)
                
return NOTOperator(ConditionResult.ConditionTrue);
            
else
                
return NOTOperator(ConditionResult.ConditionFalse);

        }
        
catch (Exception ex)
        {
            
throw new Exception(String.Format("dll[local:{0}]中的{1}.{2}方法调用异常", handler.DllPath, handler.TypeName, handler.Method), ex);
        }
    }

  

3)         调用外部dll方法条件的扩展属性

Ø  string   DllPath       dll文件的完整路径

Ø  string   TypeName     类名    Demo中设置为:MyDllFunction.DllFunc 表示调用命名空间MyDllFunction下的DllFunc类中的方法。

Ø  string   Method       方法名称     Demo中设置为:“IsAdmin”,会调用public bool IsAdmin(string userName)这个方法。

Ø  string[]  ParameterMap 参数匹配

将工作流的参数和dll中方法的输入参数进行匹配。在运行时将流程参数作为调用参数传入。

 

4)         AssemblyActionHandler handler

              调用外部dll的封装类,继承自ActionHandler 

AssemblyActionHandler类代码
    /// <summary>
    
///类型处理,通过反射调用对应的类指定的方法
    
/// </summary>
    public class AssemblyActionHandler : ActionHandler
    {
        
#region AssemblyActionHandler Members

        
/// <summary>
        
/// Dll文件的完整路径
        
/// </summary>
        public string DllPath { getset; }
        
/// <summary>
        
///类名
        
/// </summary>
        public string TypeName { getset; }
        
/// <summary>
        
///方法名称
        
/// </summary>
        public string Method { getset; }
        
/// <summary>
        
///参数匹配
        
///将工作流的参数和dll中方法的输入参数进行匹配。在运行时将流程参数作为调用参数传入。
        
/// </summary>
        public string[] ParameterMap { getset; }

        
/// <summary>
        
///执行
        
/// </summary>
        
/// <param name="args"></param>
        
/// <returns></returns>
        public override bool Execute(WorkflowEventArgs args)
        {
            
try
            {
                Assembly asm 
= Assembly.LoadFrom(DllPath);
                Type type 
= asm.GetType(TypeName, truetrue);
                
object newObj = type.InvokeMember("", BindingFlags.CreateInstance
                                                , 
nullnullnull);
                
object[] parameters = prepareParameter(args);
                
return (bool)type.InvokeMember(Method, BindingFlags.InvokeMethod
                                                , 
null, newObj, parameters);
            }
            
catch (Exception ex)
            {
                
throw new Exception("执行dll中的方法过程中发生异常", ex);
            }
        }

        
private object[] prepareParameter(WorkflowEventArgs args)
        {
            
if (ParameterMap.Length < 1return null;

            
object[] result = new object[ParameterMap.Length];
            
for (int i = 0; i < ParameterMap.Length; i++)
            {
                result[i] 
= args.DataItems[ParameterMap[i]];
            }
            
return result;
        }
        
#endregion
    }

  

5)         MyDllFunction工程为一个简单实现的外部方法类,实现了一个IsAdmin方法,如果是当前用户是管理员则条件成立,内部代码如下:

外部方法类代码
    using System;
    
using System.Reflection;

    
namespace MyDllFunction
    {
        
public class DllFunc : MarshalByRefObject  //, IRunAction
        {
            
public bool IsAdmin(string userName)
            {
                
if (userName == "系统管理员")
                    
return true;
                
else
                    
return false;
            }

            
#region IRunAction 成员

            
public object Eval(params object[] objs)
            {
                
throw new NotImplementedException();
            }

            
public object ProxyInvoke(string method, object[] objs)
            {
                
object o = this.GetType().InvokeMember(method, BindingFlags.InvokeMethod, nullthis, objs);
                
return o;
            }

            
#endregion
        }
    }

  

4.       扩展参与者(Actor

Linq对象参与者(MyLinqExtActor

1)       描述:通过参数传入一个Linq对象,从该对象中获取参与者的ID或者LoginID来找到相应的参与者。

clip_image015

流程说明:在数据库中的TaskUserTable表中设置了每个环节的一个或多个参与者的LoginID,流转到领导审批 或者助理处理 环节时,会连接到数据库中去获取当前环节的参与者。

 

2)       继承自Actor

重写参与者的计算函数代码
    public override IList<IParticipant> Resolve(WorkflowEventArgs args)
    {
        IList
<IParticipant> result = new List<IParticipant>();
        BusinessWorkflow workflow 
= ((BusinessEventArgs)args).Workflow;
        IQueryable
<string> track = workflow.DataItems[_parameter] as IQueryable<string>;
        OrganizationProvider provider 
= OrganizationProvider.DefaultProvider;

        var users 
= from t in track
                    select t;
        
if (users.Count() > 0)
        {
            
foreach (string strID in users)
            {
                
string strGUIDPattern = "^\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}$";
                System.Text.RegularExpressions.Regex objGuidRegex 
= new System.Text.RegularExpressions.Regex(strGUIDPattern);
                IUser user;
                
if (objGuidRegex.IsMatch(strID))
                    user 
= provider.GetUserByID(new Guid(strID));
                
else
                    user 
= provider.GetUserByLoginID(strID);
                
//避免重复加入
                if (!result.Contains(user))
                    result.Add(user);
            }
        }
        
return result;
    }

  

3)       Linq对象参与者的属性

    string Parameter 参数名    Demo中设置为:"LinqData",与下面代码的this.Parameters.Add("LinqData", lingObjs)参数索引名相等。

 

4)       在表单Sample.ascx(位置:\Workflow\UserControls\Biz\Sample.ascx)的后台代码的BeforeDataSave函数中新增参与者计算所需要的Linq对象,这里我们简单查询了几个参与者的loginID,生成了IQueryable<string>对象,调用this.Parameters.Add("LinqData", lingObjs);在AffairDetail.aspx的提交函数中,会将这个对象发送给工作流引擎进行参与者计算,从而实现了一个简单Linq对象的参与者。

BeforeDataSave函数中新增的代码
    protected override bool BeforeDataSave(InfoWeb.Sample.Test_Holiday obj)
    {
        
if (ActionType == ItemAction.AddNew)
        {
            obj.HolidayID 
= Guid.NewGuid();
            
if (obj.StartDate != null && obj.EndDate != null)
                obj.Days 
= (short)(WorkDaysProvider.DefaultProvider.GetSpan((DateTime)obj.StartDate, (DateTime)obj.EndDate).Days + 1);
        }

        
string[] strs = new string[] { this.CurrentUser.ID.ToString(), "baochao""lij" };
        var lingObjs 
= (from t in strs
                        select t).AsQueryable();
        
this.Parameters.Add("LinqData", lingObjs);

        
this.Parameters.Add("CurUserName"this.WorkflowData.CreatorName);

        
return base.BeforeDataSave(obj);
    }

 

 

步骤参与者(MyActivityExtActor

1)       描述:在数据库的一个表中配置了每个步骤所对应的参与者,通过数据库连接来获取到相应的参与者

clip_image017

流程说明:启动之后可以选择下一步环节,分别可以进入领导审批 环节和助理处理 环节,在数据库中分别设置了这两个环节的参与者,领导审批 环节的参与者为baochao和baok这两个人,助理处理 环节则是lij这个参与者。

 

2)       数据库配置步骤参与者属性如下:

Ø  string TaskName     步骤名称

Ø  string TableName    数据库表名

Ø  string ColumnName  数据库表的字段名

 

3)       数据库中TaskUserTable表的对应测试数据

image

 

4)       继承自Actor

重写参与者的计算函数代码
    public override IList<IParticipant> Resolve(WorkflowEventArgs args)
    {
        List
<IParticipant> result = new List<IParticipant>();

        
using (SqlConnection connection = new SqlConnection(connectionString))
        {
            SqlDataAdapter adapter 
= new SqlDataAdapter();
            
string strSelect = String.Format("select {0} from {1} where TaskName = '{2}'", ColumnName, TableName, TaskName);
            adapter.SelectCommand 
= new SqlCommand(strSelect, connection);
            DataSet ds 
= new DataSet();
            ds.Tables.Add(TableName);
            adapter.Fill(ds, TableName);

            DataTable dt 
= ds.Tables[TableName];
            
foreach (DataRow dr in dt.Rows)
            {
                
string[] actors = dr[ColumnName].ToString().Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                
foreach (string actor in actors)
                {
                    
if (!String.IsNullOrEmpty(actor))
                    {
                        IUser user 
= OrganizationProvider.DefaultProvider.GetUserByLoginID(actor);
                        
if (user != null)
                            result.Add(user);

                    }
                }
            }
        }

        
return result;
    }

  

5.       表单扩展事件

1)       描述:我的表单在某个环节完成之后需要发送短信。

2)       表单类继承自IDocForm接口,可以重写以下函数:

Ø  OnActivityEnter (object sender, EventArgs e)

工作流环节进入时执行的方法

 

Ø  OnActivityExecute (object sender, EventArgs e)

工作流执行时执行的方法

 

Ø  OnActivityExit (object sender, EventArgs e)

工作流环节退出后执行的方法,SenderActivity

 

3)       本例我们只需要重写OnActivityExit即可,重写的方法代码:

重写OnActivityExit代码
     public override void OnActivityExit (object sender, EventArgs e)
    {
        AgentActivity acti 
= sender as AgentActivity;
        
if (acti == null || acti.Name != "Broadcasting SMS"return;
        SurveyNotiseAccess access 
= new SurveyNotiseAccess();
        SurveyDB db 
= new SurveyDB();
        OSS_Survey survey 
= GetBindingData(db);
        
foreach (SurveyCustomer sc in survey.SurveyCustomers)
        {
            access.InsertUpdateSurveyEmail(survey.SurveyID, sc.Customer.Email, 
0);
            
string responseCode;
            responseCode 
= EnDeCode.Encode(survey.SurveyID.ToString() + "&" + sc.Customer.Email);
            
string content = survey.OSS_SurveyEmails.EmailContent + string.Format("<{0}>", responseCode);
            DistributeSMS(sc.Customer.Mobile, content);
        }
        survey.IsPublish 
= true;
        db.SubmitChanges();
    } 

  

6.Demo附件及相关参考

本文Demo文件下载:WorkflowExtDemoFor4.0.rar 

注:本Demo删除了P1.Site站点工程和数据库工程,请参照工作流快速指引进行数据库部署、新建及配置好P1.Site Web工程进行流程测试。

 

品高工作流下载试用地址:http://www.cnblogs.com/briankfc/archive/2010/07/30/1788818.html

品高工作流Procez One V4.0最新功能全览:http://www.cnblogs.com/briankfc/archive/2010/07/29/1787783.html

品高工作流原理介绍:http://www.cnblogs.com/briankfc/archive/2010/07/28/1787205.html

品高工作流Procez One的快速入门:http://www.cnblogs.com/firewing/tag/Quick+Start/

 

posted on 2010-08-02 11:14  菲暃罪霏  阅读(751)  评论(0编辑  收藏  举报