收藏 分销(赏)

web工作流管理系统开发36-40.doc

上传人:xrp****65 文档编号:6662914 上传时间:2024-12-19 格式:DOC 页数:22 大小:296.89KB 下载积分:10 金币
下载 相关 举报
web工作流管理系统开发36-40.doc_第1页
第1页 / 共22页
web工作流管理系统开发36-40.doc_第2页
第2页 / 共22页


点击查看更多>>
资源描述
三十六 工作流软件中的那些人 如果我们把预定义的流程比喻成一条从起点到终点的公交线路,那么流程定义中的步骤,就表示车站。汽车到站后人员有上车下车等行为,这就可以认为是流程中的动作。流程定义完后,就按照预定义好的线路往下走,当然业务流程的规则是比公交线路更复杂。   业务流程的流转离不开人员的参与,工作流软件的定义就是:自动运作的业务过程部分或整体,表现为参与者对文件、信息或任务按照规程采取行动,并令其在参与者之间传递。 在工作流软件中,有步骤的所有者,动作的执行人,任务的参与者,工单的执行人等等很多关于人的概念。   下面一一解释工作流软件产品中的关于人的概念: 业务流程建模的创建人: 标识由谁创建的,对流程建模文件的一个辅助说明,主要存放在 流程建模的定义表中。   业务流程建模的修改人: 标识修改人,刚创建的时候,可以就是创建人,辅助查找以后由谁修改过。   业务流程实例的创建人: 业务流程建模后,就需要按照这个预定义的过程,启动流程实例了。一般来说,每个流程实例的创建人,都是属于流程建模时初始化动作的可执行人。   业务流程实例的完成人: 流程实例运转到结束前,最后一个步骤的执行人,就是流程实例的完成人。 步骤的执行人: 当流程实例完成一个步骤后,即这个步骤成为历史步骤后,就一定会产生一个此步骤的执行人,谁来完成的此步骤,谁就是此步骤的执行人。步骤的执行人,是一个具体的人,不会是一个对人员的分类等的概念。   步骤的所有者: 当业务流程在建模的时候,动作的结果导向另外一个步骤,在进入此步骤之前,给此步骤设置 了一个步骤的所有者,那么这个步骤就有所有者了。当流程实例运行到此步骤的时候,这个步骤的所有者就有值了。步骤所有者可以是一个具体的人,也可以是一个 变量(当流程实例运行到此步骤之前,给这个变量赋值,到达的时候,能获取到变量的值,否则,步骤所有者就会是空)。步骤的所有者只能是一个人,不会是一个 对人员的分类(例如角色,用户组等)。 历史步骤的执行人: 和步骤执行人一样,步骤执行完成后,均成为历史步骤,每个历史步骤都一定会 有一个步骤的执行人。常常用变量mostRecentCaller来表示,并辅助一个前置函数,将指定历史步骤的执行人,保存到 mostRecentCaller变量中。mostRecentCaller是临时变量,注意要在流程的一次流转中,前面环节赋值,后面的环节才能获取到 值。   注:历史步骤的执行人,可用于当流程再次返回到此步骤后,仍然交给原来的步骤执行人再次执行,例如,谁填写的报销单审核不通过,打回给原来的填写人重新填写。或者后面的步骤执行人需要是以前步骤的执行人。历史步骤的执行人和历史步骤的所有者均可作此运用。   历史步骤的所有者: 首先要此步骤在流程建模的时候,动作结果导向此步骤后,设置了此步骤的所有者。当这 个步骤成为历史步骤后,获取此步骤的的所有者,就是历史步骤的所有者。常常用mostRecentOwner变量来表示,和 mostRecentCaller一样,是临时变量,需要在流程的一次流转中,前面的环节利用前置函数给mostRecentOwner变量赋值,后面的 环节才能获取到此值。和mostRecentCaller的区别是,每个历史步骤,不一定会有历史步骤的所有者(需要设置了才会 有),mostRecentCaller是一定会有的。   当前执行者: 就是执行流程的操作人。在流程建模的时候,可以用caller变量来表示,caller变 量也是临时变量,在流程的一次流转前面环节给caller变量赋值,后面的环节就可以获取caller变量的值来使用。给caller变量赋值,用 将当前执行者保存到变量caller  这个前置函数。   动作的执行人: 就是当前步骤的当前可执行动作的执行人。动作是否可执行,有条件可以设置,当流程实例在运行时,当前执行者符合当前步骤的当前动作的条件,此动作就是当前可执行的动作。动作的条件,可以是限制人,也可以是业务规则的限制。   动作的可执行人: 当流程建模时,动作的条件设置,是一个人,或者是一个角色等,那么这个人,或者是有这个角色的所有人 都是此动作的可执行人。   任务的创建人: 当流程实例运转的时候,会根据节点的设置产生任务记录,谁执行的流程,产生的任务记录,任务的创建人就是谁。   任务的分配人: 在eworkflow工作流软件中,任务的分配人通常就是指任务的创建人。   任务的参与人: 顾名思义,就是可以看到此任务并参与执行任务的人。在eworkflow工作流软件中,任务的参与人,不一定就是任务的最后完成人。   任务的签收人: 这是针对竞争型任务设置的,当产生竞争型任务时,谁先签收了此任务,谁就是任务的签收人,其它人就不能再执行此任务了。   任务工单的执行人: 在工作流软件中,一条任务信息,可能会产生多个任务工单。   任务工单的完成人: 谁完成的任务工单,谁就是任务工单的完成人。一条任务工单,只会有一个完成人。   任务的完成人: 因为任务会产生多条派发的工单,所以任务工单的完成人,也就是任务的完成人(任务的完成人可能是多个)。   任务的抄送人 抄送给某些用户,任务的抄送人只能查看任务,不能执行任务。   .... 随着工作流软件系统的不断升级,工作流软件的那些人,还会继续的添加...... 三十七 自由流的实现(续) 工作流系统的自由流实现了不按照流程预定义的轨迹流转,即需要在节点之间任意的跳转。上 一篇自由流的实现主要说明了跳转规律,如果是单节点间的跳转,只要有当前的节点,以及跳转到的节点,就ok了。但是当当前节点和任意跳转到的节点之间有分 支并发线路后,就不是简单的跳转了,当分支套分支,分支主干等之间的跳转就更复杂了。 如果是这种串行路由,就很简单了。 但是这样的多路分支,分支嵌套分支的,就很复杂了。 因此在实现的时候,必需找出,当前节点到跳转到的节点之间的轨迹线路。又因为分支之间是可以嵌套的,所以必需用递归来遍历这之间的轨迹。 由当前节点开始,查找这个节点的动作结果节点是否为跳转到的节点,如果是,则找到之间的轨迹,返回。 如果不是,则将当前这个节点的动作结果节点作为当前节点,继续调用这个函数,查找,直到找到后退出。 因为流程定义的轨迹,可以是循环的轨迹,即一个节点的动作结果可以返回到前面的节点,如: 当返回到前面的节点时,用递归调用的话,就永远也退不出循环了。因此,将处理过的节点放到traceNodeList中,每次调用递归函数前,均判断一下,此节点是否在traceNodeList中,如果在,就不用处理了。不在的继续处理。 eworkflow自定义工作流系统有.net版和java版,两个版本的实现过程完全一样,只是一个是c#语言的一个是java语言的。 c#的递归函数:   private ArrayList getJoinsAndSplitsBwteenStep(WorkflowDescriptor wd,IDictionary orgNodeMap,ResultDescriptor theResult,ArrayList traceNodeList)   {                                    ArrayList tagList = new ArrayList();    string nodeType =(System.String)orgNodeMap["node_type"];    AbstractDescriptor node = (AbstractDescriptor)orgNodeMap["node"];          ArrayList results = new ArrayList();    if (nodeType.Equals("join"))    {       JoinDescriptor join = (JoinDescriptor)node;     results.Add(join.Result);    }    if (nodeType.Equals("split"))    {     SplitDescriptor split = (SplitDescriptor)node;     results.AddRange(split.Results);    }    if (nodeType.Equals("subflow"))    {     SubflowDescriptor subflow = (SubflowDescriptor)node;     results.Add(subflow.unconditionalResult);     results.AddRange(subflow.conditionalResults);    }    if (nodeType.Equals("step"))    {     StepDescriptor step = (StepDescriptor)node;     ArrayList actions = step.actions;     for (IEnumerator iter = actions.GetEnumerator();iter.MoveNext();)     {      ActionDescriptor action =(ActionDescriptor)iter.Current;      ResultDescriptor result = action.UnconditionalResult;      results.Add(result);      results.AddRange(action.ConditionalResults);     }           }    bool bFind=false;    for (IEnumerator it = results.GetEnumerator();it.MoveNext();)    {     ResultDescriptor result = (ResultDescriptor)it.Current;     if (result.Id==theResult.Id)     {//找到跳转到的节点,退出      bFind = true;      break;     }    }    if (bFind) return tagList;    //将当前处理节点 存入traceNodeList中    traceNodeList.Add(node);             for (IEnumerator iterator = results.GetEnumerator();iterator.MoveNext();)    {            ResultDescriptor resultDesc = (ResultDescriptor)iterator.Current;     int joinid = resultDesc.join;     int splitid = resultDesc.Split;     int stepid = resultDesc.Step;     int subflowid = resultDesc.subflow;     int traceid = 0;     IDictionary orgMap = new Hashtable();//记录节点信息     if (joinid>0)     {      IDictionary m = new Hashtable();      m["join"] = joinid;//new Integer(joinid);      tagList.Add(m);      JoinDescriptor join = wd.getJoin(joinid);               orgMap["node_type"] = "join";      orgMap["node"] = join;      traceid = joinid;     }     if (splitid>0)     {      SplitDescriptor split = wd.getSplit(splitid);      orgMap["node_type"] = "split";      orgMap["node"] = split;      IDictionary m = new Hashtable();      m["split"] = splitid;// new Integer(splitid));           for (int i=0;i<split.Results.Count;i++)      {       tagList.Add(m);          }      traceid = splitid;     }     if (stepid>0)     {      StepDescriptor step = wd.getStep(stepid);      orgMap["node_type"] = "step";      orgMap["node"] = step;      traceid = stepid;     }     if (subflowid>0)     {      SubflowDescriptor subflow = wd.getSubflow(subflowid);      orgMap["node_type"] = "subflow";      orgMap["node"] = subflow;         traceid = subflowid;     }     //判断 关联到的节点 是否处理过     bool inTrace = false;     for (IEnumerator itrace=traceNodeList.GetEnumerator();itrace.MoveNext();)     {      AbstractDescriptor trace = (AbstractDescriptor)itrace.Current;      if (trace.Id==traceid)      {//已经处理过的了       inTrace = true;       break;      }         }     if (!inTrace)      tagList.AddRange(getJoinsAndSplitsBwteenStep(wd,orgMap,theResult,traceNodeList));           }    return tagList;   java的递归函数:     private List getJoinsAndSplitsBwteenStep(WorkflowDescriptor wd,Map orgNodeMap,ResultDescriptor theResult,List traceNodeList) throws WorkflowException{      List tagList =new ArrayList();      String nodeType =(String)orgNodeMap.get("node_type");      AbstractDescriptor node = (AbstractDescriptor)orgNodeMap.get("node");            List results = new ArrayList();      if (nodeType.equals("join")){       JoinDescriptor join = (JoinDescriptor)node;       results.add(join.getResult());      }      if (nodeType.equals("split")){       SplitDescriptor split = (SplitDescriptor)node;       results.addAll(split.getResults());      }      if (nodeType.equals("subflow")){       SubflowDescriptor subflow = (SubflowDescriptor)node;       results.add(subflow.getUnconditionalResult());       results.addAll(subflow.getConditionalResults());      }      if (nodeType.equals("step")){       StepDescriptor step = (StepDescriptor)node;       List actions = step.getActions();          for (Iterator iter = actions.iterator();iter.hasNext();){           ActionDescriptor action =(ActionDescriptor)iter.next();           ResultDescriptor result = action.getUnconditionalResult();           results.add(result);           results.addAll(action.getConditionalResults());          }             }      boolean bFind=false;      for (Iterator it = results.iterator();it.hasNext();){       ResultDescriptor result = (ResultDescriptor)it.next();       if (result.getId()==theResult.getId()){//找到跳转到的节点,退出        bFind = true;        break;        //return tagList;       }      }      if (bFind) return tagList;      //将当前处理节点 存入traceNodeList中      traceNodeList.add(node);         for (Iterator iterator = results.iterator();iterator.hasNext();){        ResultDescriptor resultDesc = (ResultDescriptor)iterator.next();    int joinid = resultDesc.getJoin();    int splitid = resultDesc.getSplit();    int stepid = resultDesc.getStep();    int subflowid = resultDesc.getSubflow();    int traceid = 0;    Map orgMap = new HashMap();//记录节点信息    if (joinid>0){     Map m = new HashMap();     m.put("join", new Integer(joinid));     tagList.add(m);     JoinDescriptor join = wd.getJoin(joinid);          orgMap.put("node_type", "join");     orgMap.put("node", join);         traceid = joinid;    }    if (splitid>0){     SplitDescriptor split = wd.getSplit(splitid);     orgMap.put("node_type", "split");     orgMap.put("node", split);     Map m = new HashMap();     m.put("split", new Integer(splitid));     //tagList.add(m);     for (int i=0;i<split.getResults().size();i++){      tagList.add(m);         }     traceid = splitid;    }    if (stepid>0){     StepDescriptor step = wd.getStep(stepid);     orgMap.put("node_type", "step");     orgMap.put("node", step);     traceid = stepid;    }    if (subflowid>0){     SubflowDescriptor  subflow = wd.getSubflow(subflowid);     orgMap.put("node_type", "subflow");     orgMap.put("node", subflow);        traceid = subflowid;    }    //判断 关联到的节点 是否处理过    boolean inTrace = false;    for (Iterator itrace=traceNodeList.iterator();itrace.hasNext();){     AbstractDescriptor trace = (AbstractDescriptor)itrace.next();     if (trace.getId()==traceid){//已经处理过的了      inTrace = true;      break;     }        }    if (!inTrace)     tagList.addAll(getJoinsAndSplitsBwteenStep(wd,orgMap,theResult,traceNodeList));       }      return tagList;     } 三十八 工作流软件中的定时器处理 工作流软件中的定时器处理,一般分为两种: 应用服务启动就启动的定时器 这种类型和通常的web系统中的定时器处理一样。应用服务器启动后,定时器就启动了,然后按照定制的时间段轮询处理。当应用服务器关闭的时候,这种 定时器才会关闭。这种定时器通常是对所有需要定时处理的记录来做的,在工作流软件中,就对应到所有的业务流程,所有的流程实例。 这种类型的定时器,在报表软件,报表产品中经常有用到,比如 每天晚上定时生成按日汇总表,每个月未生成月结汇总表。在工作流软件系统中,可以利用这种定时器按计划启动业务流程的流程实例。按计划扫描执行那些过期需要自动处理的任务等等。 由流程的流转来启动定时器 当流程示例流转到某个节点时,启动这个节点的定时器,按定时器设置的 时间间隔轮询执行处理。当流程流转流程实例离开这个节点了,这个定时器就关闭了。这种类型的定时器,和流程的特定节点关联,每个流程实例都会经历定时器的 启动->执行->关闭 这样整个过程。   在eworkflow工作流系统中 定时器有三种处理方式: 1. 应用服务器启动就启动的定时器。 2. 流程的节点中挂接的定时器。 3. 定制实现的定时器处理。   下面详解三种处理方式: 1. 应用服务器启动就启动的定时器。   定时器的执行程序,轮询的间隔时间,以及相关参数设置都在fcconfig.xml文件中。   当应用程序启动的时候,从fcconfig.xml中查找定时器的配置信息。根据配置信息启动定时器。     一个典型的fcconfig.xml文件中的配置信息如下:     <workflow-timer flowName="wf_series_timer" flowVersion="1" className=".fcsoft.workflow.timer.StartWorkflowJob">   <cronExpression>0 0 1 * * ?</cronExpression>      <userId>USR_0000001</userId>   <dsnName></dsnName>   <triggerName>trigger:1</triggerName>   <groupName>group:1</groupName>   <jobName>job:1</jobName>   <arg name="leave_days">3</arg>     </workflow-timer>    <workflow-timer flowName="wf_series_timer" flowVersion="1" className=".fcsoft.workflow.timer.DoActionWorkflowJob">   <cronExpression>0 0/1 * * * ?</cronExpression>      <userId>USR_0000001</userId>   <dsnName></dsnName>   <triggerName>trigger:2</triggerName>   <groupName>group:2</groupName>   <jobName>job:2</jobName>   <arg name="leave_days">3</arg>     </workflow-timer>    一个<workflow-timer>节点为一个定时器的定义。   属性 className 中设置的类为定时器启动后,按时间点执行的代码   .fcsoft.workflow.timer.StartWorkflowJob   是定时启动指定流程实例的处理代码。     <cronExpression>0 0 1 * * ?</cronExpression>  <!--表示每天晚上1点执行一次-->   这个节点定义是轮询的时间间隔,节点值的设置,从左到右的意义如下:  字段名    允许的值      允许的特殊字符   秒     0-59  ,       - * /   分     0-59  ,       - * /   小时   0-23  ,       - * /   日     1-31  ,       - * ? / L W C   月     1-12 or JAN-DEC  , - * /   周几   1-7 or SUN-SAT  , - * ? / L C #  年 (可选字段)  empty  ,  1970-2099  , - * /     一些典型的 cronExpression表达式的含义如下:  "0 0 12 * * ?"  每天中午十二点触发  "0 15 10 ? * *"  每天早上10:15触发  "0 15 10 * * ?"  每天早上10:15触发  "0 15 10 * * ? *"  每天早上10:15触发  "0 15 10 * * ? 2005"  2005年的每天早上10:15触发  "0 * 14 * * ?"  每天从下午2点开始到2点59分每分钟一次触发  "0 0/5 14 * * ?"  每天从下午2点开始到2:55分结束每5分钟一次触发  "0 0/5 14,18 * * ?"  每天的下午2点至2:55和6点至6点55分两个时间段内每5分钟一次触发  "0 0-5 14 * * ?"  每天14:00至14:05每分钟一次触发  "0 10,44 14 ? 3 WED"  三月的每周三的14:10和14:44触发  "0 15 10 ? * MON-FRI"  每个周一、周二、周三、周四、周五的10:15触发  "0 15 10 15 * ?"  每月15号的10:15触发  "0 15 10 L * ?"  每月的最后一天的10:15触发  "0 15 10 ? * 6L"  每月最后一个周五的10:15触发  "0 15 10 ? * 6L"  每月最后一个周五的10:15触发  "0 15 10 ? * 6L 2002-2005"  2002年至2005年的每月最后一个周五的10:15触发  "0 15 10 ? * 6#3"  每月的第三个周五的10:15触发         <userId>USR_0000001</userId> 为启动流程的用户userid,如果这个用户没有权限则流程实例不能启动。     <dsnName></dsnName> 为初始化流程需要访问的数据库连接的连接名   节点是fcconfig.xml中定义的数据库连接信息的dsn名称。即<ds name="daqin" dbType="sqlserver" ..... 节点中 name属性后面设置的名称。如果此节点值为空或没有设置此节点,则用fcconfig.xml中的第一个ds节点中设置的数据库连接信息。        <triggerName>trigger:1</triggerName>   <groupName>group:1</groupName>   <jobName>job:1</jobName>   这几个节点为定时器中的triggerName,groupName,jobName等。通常是每个每个定时器中的名称不一样。用于标识定时器启动后,需要执行的job。      <arg name="leave_days">3</arg> 这种<arg name="xxxx">这种节点为传递业务数据的节点,有几个需要传递到流程中的业务数据,就设置几个<arg 的节点。在流程初始化的时候,会将这种节点的数据加载到流程的上下文中。           2、流程的节点中挂接的定时器  这种类型的定时器,是利用流程节点的前置函数挂接 "计划执行触发器的定时器类" 来启动的。  当流程实例流转到这个节点时,执行前置函数,启动定时器。按设置的时间轮询,执行这个前置函数中定义的触发器。触发器类为定时执行的业务处理。  当流程实例离开这个节点时,后置函数中,挂接"取消执行触发器的定时器", 关闭这个定时器。    一个典型的应用,前置函数中的设置如下:       <pre-functions>         <function type="class">           <arg name="local">true</arg>           <arg name="groupName">test</arg>           <arg name="triggerName">testTrigger</arg>           <arg name="triggerId">10</arg>                   ------对应触发器的id号           <arg name="class.chn">计划执行触发器的定时器类</arg>           <arg name="passWord">test</arg>           <arg name="schedulerStart">true</arg>           <arg name="cronExpression">0,5,10,15 * * * * ?</arg>           <arg name="class.name">.fcsoft.workflow.util.ScheduleJob</arg>           <arg name="jobName">testJob</arg>         </function>       </pre-functions>             <post-functions>         <function type="class">           <arg name="class.name">.fcsoft.workflow.util.UnscheduleJob</arg>           <arg name="groupName">test</arg>                  <!--需要和前置函数中的groupName一样-->           <arg name="triggerName">testTrigger</arg>         <!--关闭的定时器的triggerName,需要和前置函数中triggerName一样-->           <arg name="class.chn">取消执行触发器的定时器</arg>         </function>       </post-functions>      在eworkflow工作流软件中触发器类也是前置函数类,实现了FunctionProvider接口,能获得流程上下文中的信息。在触发器类中,做实际的业务处理。        3、定制实现的定时器处理  这种类型的定时器处理,是按应用系统的业务需要,定制实现。  既不是在服务器启动的时候,启动定时器,也不是在流程的流转当中启动定时器。而是在应用系统的界面中,增加启动定时器,和关闭定时器的功能。    例如 一个流程实例启动了,在显示运行轨迹的界面上,增加启动定时器的功能,定时执行此流程实例的动作,达到自动执行此流程的效果,当流程实例运行结束后,就关闭此定时器,避免资源的浪费。也可以由操作员点击关闭此定时器,达到灵活的控制。   三十九 利用工作流引擎给业务系统建模 工作流软件系统,都会包含一个工作流引擎,流程设计器等。工作流引擎是工作流系统的微内核,是多种业务流程的抽象。
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 百科休闲 > 其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服