精华内容
参与话题
问答
  • 工作流引擎

    2017-12-14 10:03:48
    本文档简单介绍了工作流引擎的概念和一些技术特点,对比了当前主流的流程引擎之间的区别。
  • 工作流引擎

    2006-11-11 15:41:00
    工作流引擎 当今社会分工越来越细,在一个单位内部也越来越强调专业化,大部分工作都需要多个部门和员工合作完成。一个制度良好的单位往往对各种工作的工作流程以文件的形式固定下来,即使是管理不太正规的单位也有...

    工作流引擎 
     当今社会分工越来越细,在一个单位内部也越来越强调专业化,大部分工作都需要多个部门和员工合作完成。一个制度良好的单位往往对各种工作的工作流程以文件的形式固定下来,即使是管理不太正规的单位也有约定俗成的工作步骤。这种工作流程保证了一件任务能按预定的顺序从起点流向终点,并且在需要的时候可以跟踪、查询和统计。工作流包括以下几个要素:
    实体(Entity) :是工作流的主体,是需要随着工作流一起流动的物件(Object)。例如,在一个采购申请批准流程中,实体就是采购申请单;在公文审批流程中,实体就是公文。
    参与者(Participant) :是各个处理步骤中的责任人,可能是人,也可能是某个职能部门,还可能是某个自动化的设备;
    流程定义(Flow Definition) :是预定义的工作步骤,它规定了实体流动的路线。它可能是完全定义的,即对每种可能的情况都能完全确定下一个参与者,也可能是不完全定义的,需要参与者根据情况决定下一个参与者;
    工作流引擎(Engine) :是驱动实体按流程定义从一个参与者流向下一个参与者的机制
        可以看出,前三个要素是静态的,而第四个要素是动态的,它将前三者结合起来,是工作流的核心组成元素。

    为什么需要电子化的工作流(eWorkFlow)?
        手工处理的工作流主要有以下几个缺点:

    • 不能及时得到处理 一个步骤完成后必须将实体物理地转移给下一个参与者,当工作量增大时,很难分清哪些是重要而需要及时处理的,甚至经常出现上一个步骤已经完成了,而下一个步骤还不知道的情况;
    • 无法跟踪 传统的手工操作要求有一个人自始至终地跟着单子(比如采购申请单)走,否则流程中的任何一个人也无法知道一项任务当前的处理位置,当出现停顿时甚至无法知道该找谁解决;
    • 效率不高 很多实际上可以并行处理的步骤(例如公文审批过程中的会签),在手工处理的时候,只能一个接一个的串行处理;
    • 缺乏分析功能 流程是人制定的,是否适合实际情况只能通过实际工作检验。但手工处理无法统计各个环节的处理效率,因此对流程的评估都是大致的,凭感觉的,无法量化,对流程的改造缺乏科学的统计数据做基础。
      通过采用先进的信息技术,以上问题可以迎刃而解。软件的力量,是把繁杂而没有条理的工作,分门别类地整理出来,给每个人一个清楚的视图,及时了解当前的工作状态,易于跟踪和查询。同时强大的统计分析功能便于从海量的数据中找出人工统计所无法发现的规律,并据此做出正确的决策。

     

    工作流(Workflow)就是工作流程的计算模型,即将工作流程中的工作如何前后组织在一起的逻辑和规则在计算机中以恰当的模型进行表示并对其实施计算。工作流要解决的主要问题是:为实现某个业务目标,在多个参与者之间,利用计算机,按某种预定规则自动传递文档、信息或者任务。 工作流管理系统(Workflow Management System, WfMS)的主要功能是通过计算机技术的支持去定义、执行和管理工作流,协调工作流执行过程中工作之间以及群体成员之间的信息交互。工作流需要依靠工作流管理系统来实现。

    工作流系统的主要组成部分
    ----1.过程定义工具

    ----过程定义工具被用来创建计算机可处理的业务过程描述。它可以是形式化的过程定义语言或对象关系模型,也可以是简单地规定用户间信息传输的一组路由命令。

    ----2.过程定义

    ----过程定义(数据)包含了所有使业务过程能被工作流执行子系统执行的必要信息。这些信息包括起始和终止条件、各个组成活动、活动调度规则、各业务的参与者需要做的工作、相关应用程序和数据的调用信息等。

    ----3.工作流执行子系统(WES)和工作流引擎

    ----工作流执行子系统也称为(业务)过程执行环境,包括一个或多个工作流引擎。工作流引擎是WFMS的核心软件组元。它的功能包括:解释过程定义;创建过程实例并控制其执行;调度各项活动;为用户工作表添加工作项;通过应用程序接口(API)调用应用程序;提供监督和管理功能等。工作流执行子系统可以包括多个工作流引擎,不同工作流引擎通过协作共同执行工作流。

    ----4.工作流控制数据

    ----指被WES和工作流引擎管理的系统数据,例如工作流实例的状态信息、每一活动的状态信息等。

    ----5.工作流相关数据

    ----指与业务过程流相关的数据。WFMS使用这些数据确定工作流实例的状态转移,例如过程调度决策数据、活动间的传输数据等。工作流相关数据既可以被工作流引擎使用,也可以被应用程序调用。

    ----6.工作表和工作表处理程序

    ----工作表列出了与业务过程的参与者相关的一系列工作项,工作表处理程序则对用户和工作表之间的交互进行管理。工作表处理程序完成的功能有:支持用户在工作表中选取一个工作项,重新分配工作项,通报工作项的完成,在工作项被处理的过程中调用相应的应用程序等。

    ----7.应用程序和应用数据

    ----应用程序可以直接被WFMS调用或通过应用程序代理被间接调用。通过应用程序调用,WFMS部分或完全自动地完成一个活动,或者对业务参与者的工作提供支持。与工作流控制数据和相关数据不同,应用数据对应用程序来讲是局部数据,对WFMS的其他部件来说是不可见的。

    展开全文
  • 工作流及工作流引擎简介
  • spring 工作流引擎 几个月前,在处理一个公司项目时,我们需要开发REST服务,该服务用于根据客户端应用程序发送的数据发送电子邮件。 在开发此服务期间,我们决定创建简单的工作流引擎,该引擎将为发送电子邮件收费...

    spring 工作流引擎

    几个月前,在处理一个公司项目时,我们需要开发REST服务,该服务用于根据客户端应用程序发送的数据发送电子邮件。 在开发此服务期间,我们决定创建简单的工作流引擎,该引擎将为发送电子邮件收费,但该引擎也可用于任何类型的简单流。

    在本文中,我将逐步说明如何实现可处理序列流的简单工作流引擎。

    为了实现此工作流引擎,我们使用了spring框架,但是无论使用哪种框架,也可以不使用任何框架,如何在任何框架上实现该想法都应相同。

    我们将从对序列工作流程模式的简短介绍开始,然后,我们将研究所需的接口,最后,我们将从使用Spring实现工作流程引擎开始。

    序列工作流程模式

    序列工作流程模式描述了其中每个步骤(动作)一步一步地完成的工作流程。 在下一张图片上,您可以看到它的外观:

    流中将要处理的每个动作都共享相同的上下文,这使流的参与者之间可以共享信息。 使用公共上下文的想法是因为每个步骤都应该彼此独立,并且应该将它们作为其他流程的一部分轻松添加。

    如果要获取有关序列工作流程模式的更多信息,请访问: 序列模式

    定义所需的界面

    下一步是创建一组接口,使我们可以轻松创建工作流程并定义工作流程操作。

    我们可以从Workflow界面开始。 该接口负责处理工作流程操作,实际上它定义了我们的工作流程引擎应该执行的操作。 这是一个非常简单的界面,只有一种方法“ processWorkflow”。

    此方法由工作流引擎调用,用于为工作流提供可在工作流内部使用的初始对象,它表示每个工作流的起点。

    package ba.codecentric.workflow;
    
    import java.util.Map;
    
    /**
    
    * Process email workflow.
    
    *
    
    * @author igor.madjeric
    
    *
    
    */
    
    public interface Workflow {
    
    /**
    
    * Method for processing workflow.
    
    *
    
    * @param parameters
    
    * maps of object which are needed for workflow processing
    
    * @return true in case that workflow is done without errors otherwise false
    
    */
    
    public boolean processWorkflow(Map<String, Object> parameters);
    
    }
    
    Next what we need is interface used for defining workflow action. This is also simple interface whit only one method too.
    
    package ba.codecentric.workflow;
    /**
    
    * Define workflow action
    
    *
    
    * @author igor.madjeric
    
    *
    
    */
    
    public interface WorkflowAction {
    
    /**
    
    * Execute action.
    
    *
    
    * @param context
    
    * @throws Exception
    
    */
    
    public void doAction(Context context) throws Exception;
    
    }
    
    So this interface define only doAction method which will be called by workflow implementation.
    
    Last interface which we need to define is Context interface. This interface define two methods, one for setting object in context and another for retrieving it.
    
    package ba.codecentric.workflow;
    
    /**
    
    * Context interface.
    
    *
    
    * Class which extend this interface should be able to provide mechanism for keeping object in context.<br />
    
    * So they can be shared between action inside workflow.
    
    *
    
    * @author igor.madjeric
    
    *
    
    */
    
    public interface Context {
    
    /**
    
    * Set value with specified name in context.
    
    * If value already exist it should overwrite value with new one.
    
    *
    
    * @param name of attribute
    
    * @param value which should be stored for specified name
    
    */
    
    public void setAttribute(String name, Object value);
    
    /**
    
    * Retrieve object with specified name from context,
    
    * if object does not exists in context it will return null.
    
    *
    
    * @param name of attribute which need to be returned
    
    * @return Object from context or null if there is no value assigned to specified name
    
    */
    
    public Object getAttribute(String name);
    
    }

    这是我们需要为简单工作流程定义的所有接口

    实施简单的工作流引擎

    定义接口之后,我们可以从实现工作流引擎开始。 引擎应具备的功能有一些要求。

    该引擎应支持顺序工作流程,这意味着一个接一个地执行动作。

    发动机也应该能够进动多于一个的流量。

    工作流操作应该能够彼此共享信息。

    如我们所见,并没有很多要求,所以我们应该从实现它开始。

    首先,我们可以创建上下文类,该上下文类将用于处理动作之间的信息。 此类实现Context接口,并且不执行其他任何操作。

    package ba.codecentric.workflow.impl;
    
    import java.util.HashMap;
    import java.util.Map;
    import ba.codecentric.workflow.Context;
    
    /**
    * Save states between different workflow action.
    *
    * @author igor.madjeric
    *
    */
    public class StandardContext implements Context {
    
    private Map<String, Object> context;
    
    /**
    
    * Create context object based.
    *
    * @param parameters
    */
    public StandardContext(Map<String, Object> parameters) {
    if (parameters == null) {
    this.context = new HashMap<String, Object>();
    } else {
    this.context = parameters;
    }
    }
    
    @Override
    public Object getAttribute(String name) {
    return context.get(name);
    }
    
    @Override
    public void setAttribute(String name, Object value) {
    context.put(name, value);
    }
    
    }

    第二步是创建实现Workflow接口的类。 我们称此类为StandardWorkflow。 除了实现Workflow接口之外,该类还实现了ApplicationContextAware接口,因为需要访问spring bean存储库。 如果您不使用spring,则不需要实现它。

    我们已经说过,工作流应该支持一个以上的流。
    因此,可以将一个工作流程的操作定义为一个列表,并且每个列表都应分配一个逻辑名称。 因此,对于动作注册,我们可以使用Map <String,List <WorkflowAction >>之类的东西。 首先,我们将看到SpringBean的StandardWorkflow和一个自定义流程的定义,然后我们将看到StandardWorkflow的实现。

    Bean的StandardWorkflow定义:

    <bean id='standardWorkflow'
    
    class='de.codecentric.oev.external.services.workflow.standard.StandardWorkflow'>
    
    <property name='workflowActions'>
    
    <map>
    
    <!-- <entry key='<CID>_action'><ref bean='<CID>_action'/></entry>-->
    
    <!-- OEVBS -->
    
    <entry key='action1_action'>
    
    <ref bean='action1_action' />
    
    </entry>
    
    <!-- PVN -->
    
    <entry key='action2_action'>
    
    <ref bean='action2_action' />
    
    </entry>
    
    <!-- WPV -->
    
    <entry key='action3_action'>
    
    <ref bean='action3_action' />
    
    </entry>
    
    </map>
    
    </property>
    
    </bean>

    从这个bean定义中,我们可以看到我们为每个客户定义了操作,并且在引用bean中定义了操作列表。

    这是其中一个客户Bean的示例:

    <bean id='action1_action' class='java.util.ArrayList'>
    
    <constructor-arg>
    
    <!-- List of Actions -->
    
    <list value-type='ba.codecentric.workflow.WorkflowAction' >
    
    <ref local='createEmailAction'/>
    
    <ref bean='sendEmailAction'/>
    
    </list>
    
    </constructor-arg>
    
    </bean>

    现在我们可以看到StandardWorkflow的样子:

    package ba.codecentric.workflow.impl;
    
    import java.util.List;
    
    import java.util.Map;
    
    import org.apache.commons.logging.Log;
    
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.beans.BeansException;
    
    import org.springframework.context.ApplicationContext;
    
    import org.springframework.context.ApplicationContextAware;
    
    import ba.codecentric.workflow.Context;
    
    import ba.codecentric.workflow.Workflow;
    
    import ba.codecentric.workflow.WorkflowAction;
    
    /**
    
    * Define standard workflow for sending email.
    
    *
    
    * @see Workflow
    
    *
    
    * @author igor.madjeric
    
    *
    
    */
    
    public class StandardWorkflow implements Workflow,
    
    ApplicationContextAware {
    
    private final Log LOG = LogFactory.getLog(StandardWorkflow.class);
    
    private static final String ACTION = 'action';
    
    private Map<String, List<WorkflowAction>> workflowActions;
    
    private ApplicationContext applicationContext;
    
    /**
    
    *@see de.codecentric.oev.external.services.workflow.Workflow#processWorkflow(java.util.Map)
    
    */
    
    @Override
    
    public boolean processWorkflow(String workflofName, Map<String, Object> parameters) {
    
    Context context = new StandardContext(parameters);
    
    List<WorkflowAction> actions = getWorkflowActions(workflofName);
    
    for (WorkflowAction action : actions) {
    
    try {
    
    action.doAction(context);
    
    } catch (Exception e) {
    
    StringBuilder message = new StringBuilder(
    'Failed to complete action:' + action.toString());
    
    message.append('\n');
    
    message.append(e.getMessage());
    
    LOG.error(message.toString());
    
    return false;
    
    }
    
    }
    
    return true;
    
    }
    private List<WorkflowAction> getWorkflowActions(String actionName) {
    
    List<WorkflowAction> actions = workflowActions.get(actionName);
    
    if (actions == null || actions.isEmpty()) {
    
    LOG.error('There is no defined action for ' + actionName);
    
    throw new IllegalArgumentException(
    'There is no defined action for ' + actionName);
    
    }
    
    return actions;
    
    }
    @Override
    
    public void setApplicationContext(ApplicationContext applicationContext)
    throws BeansException{
    this.applicationContext = applicationContext;
    
    }
    // Getter/Setter
    
    public Map<String, List<WorkflowAction>> getWorkflowActions() {
    
    return workflowActions;
    
    }
    public void setWorkflowActions(
    
    Map<String, List<WorkflowAction>> workflowActions) {
    
    this.workflowActions = workflowActions;
    
    }
    }

    再次您可以看到,这也是一个简单的类,所有工作都在processWorkflow方法中完成,我们向其提供流程名称和输入参数。 此方法使用指定的参数创建Context,然后尝试加载为指定流定义的操作,如果存在具有指定名称的流,它将开始运行流。

    如何开始流程

    这取决于您的需要。 您可以使用我们这样的休息服务,也可以使用其他任何机制(例如MBean),计划的作业,也可以直接从某些服务中进行呼叫。 您需要做的就是调用processWorkflow方法。

    参考:来自ICG Madjeric博客的JCG合作伙伴 Igor Madjeric的《 Spring的简单工作流引擎》

    翻译自: https://www.javacodegeeks.com/2012/11/simple-workflow-engine-with-spring.html

    spring 工作流引擎

    展开全文
  • GooFlow21工作流引擎

    2018-01-30 11:14:00
    GooFlow工作流引擎,GooFlow工作流引擎需要的赶紧来下载吧。
  • 工作流引擎,是指workflow作为应用系统的一部分,并为之提供对各应用系统有决定作用的根据角色、分工和条件的不同决定信息传递路由、内容等级等核心解决方案。以前一直在想如何构建一个灵活的OA工作流机制,可能开始...

    工作流引擎,是指workflow作为应用系统的一部分,并为之提供对各应用系统有决定作用的根据角色、分工和条件的不同决定信息传递路由、内容等级等核心解决方案。

    以前一直在想如何构建一个灵活的OA工作流机制,可能开始有很多人用domino来做,后来到了http://ASP.NET的时候,好像大家都醒悟了,公司老板、CTO等都开始争着要上工作流引擎实现企业信息审批流程化。

    JNPF 平台将工作流作为“工作流引擎”中间件方式无缝嵌入式集成到您现有的业务系统中,通过适配器快速实现用户组织架构无缝集成。真正做到·极致轻量级、易集成、可嵌入式集成。给你的是一体化平台,贯穿业务流程管理生命周期全过程-设计,建模,执行,监控和优化的每个阶段。根据了解到的企业办公流程,我们建设了一个切实满足企业需求的功能架构体系:

    存储层

    基于硬件完成结构化数据或非结构化数据的存储,支持多种类型数据库

    Oracle、SqlServer、MySQL,并且支持国产数据库。

    平台层

    整个平台层都是 SaaS 产品的基石,是为了后期能更好的完成 SaaS 产品搭建而创建的。对数据的拉取及表格、表单、图表、报表的展示,完成菜单的设计、角色的创建、权限的分配等,通过简单的拖拽控件及数据绑定设置即可完成表单。

    服务层

    应用系统能否快速搭建可靠得是平台层的工具支撑,但能否实现对 SaaS平台多租户同时运行靠的是服务层的支撑。因此需要具备平台统一调度、及时通讯、统一工作流配置、统一接口和多租户等服务。

    运营层

    运营层主要是帮助用户对已完成的 SaaS 产品进行版本划分、软件商城上架、租户细分以及权限划分等等。多种运营模式同时进行运行监控,通过渠道商管理分成与任务完成情况的监控工作。

    JNPF拥有功能完善的BPM 工作流程系统,纯 Web 可视化流程设计器,集流程管理、设计、测试、运行、监控、分析等功能于一体。

    BPM流程引擎全生命周期管理:建模、设计、运行、监控、优化敏捷、高效的流程实施;可视化流程建模与设计、配置基于 BPMN2 标准的流程设计规范使用成熟、稳定, 开源的流程引擎技术(activiti)中国特色流程支持:自由跳转、驳回、追回、 加签、抄送等近百种流转特色支持。构建企业流程服务中心,灵活配置实现跨系 统的业务编排整合。

    展开全文
  • Activiti工作流引擎

    2020-12-02 17:40:32
    Activiti工作流引擎使用详解 1.简单介工作流引擎与Activiti 对于工作流引擎的解释请参考百度百科:工作流引擎 1.1 我与工作流引擎 在第一家公司工作的时候主要任务就是开发OA系统,当然基本都是有工作流的支持,不过...

    Activiti工作流引擎使用详解
    1.简单介工作流引擎与Activiti
    对于工作流引擎的解释请参考百度百科:工作流引擎

    1.1 我与工作流引擎
    在第一家公司工作的时候主要任务就是开发OA系统,当然基本都是有工作流的支持,不过当时使用的工作流引擎是公司一些牛人开发的(据说是用一个开源的引擎修改的),名称叫CoreFlow;功能相对Activiti来说比较弱,但是能满足日常的使用,当然也有不少的问题所以后来我们只能修改引擎的代码打补丁。

    现在是我工作的第二家公司,因为要开发ERP、OA等系统需要使用工作流,在项目调研阶段我先搜索资料选择使用哪个开源工作流引擎,最终确定了Activiti5并基于公司的架构做了一些DEMO。

    1.2 Activiti与JBPM5?
    对于Activiti、jBPM4、jBPM5我们应该如何选择,在InfoQ上有一篇文章写的很好,从大的层面比较各个引擎之间的差异,请参考文章:纵观jBPM:从jBPM3到jBPM5以及Activiti5

    1.3 Activiti资料
    官网:http://www.activiti.org/

    下载:http://www.activiti.org/download.html

    版本:Activiti的版本是从5开始的,因为Activiti是使用jBPM4的源码;版本发布:两个月发布一次。

    Eclipse Plugin: http://activiti.org/designer/update/

    Activit中文群:5435716

    2.初次使用遇到问题收集
    因为Activiti刚刚退出不久所以资料比较空缺,中文资料更是少的可怜,所以开始的时候一头雾水(虽然之前用过工作流,但是感觉差距很多),而且官方的手册还不是很全面;所以我把我在学习使用的过程遇到的一些疑问都罗列出来分享给大家;以下几点是我遇到和想到的,如果你还有什么疑问可以在评论中和我交流再补充。

    2.1 部署流程图后中文乱码
    乱码是一直缠绕着国人的问题,之前各个技术、工具出现乱码的问题写过很多文章,这里也不例外……,Activiti的乱码问题在流程图中。

    流程图的乱码如下图所示:

    在这里插入图片描述
    解决办法有两种:

    2.1.1 修改源代码方式
    修改源码

    org.activiti.engine.impl.bpmn.diagram.ProcessDiagramCanvas
    

    在构造方法

    public ProcessDiagramCanvas(int width, int height)
    

    中有一行代码是设置字体的,默认是用 Arial 字体,这就是乱码产生的原因,把字改为本地的中文字体即可,例如:

    Font font = new Font("WenQuanYi Micro Hei", Font.BOLD, 11);
    

    当然如果你有配置文件读取工具那么可以设置在*.properties文件中,我就是这么做的:

    Font font = new Font(PropertyFileUtil.get("activiti.diagram.canvas.font"), Font.BOLD, 11);
    

    2.1.2 使用压缩包方式部署
    Activiti支持部署*.bpmn20.xml、bar、zip格式的流程定义。

    使用Activit Deisigner工具设计流程图的时候会有三个类型的文件:

    .activiti设计工具使用的文件

    .bpmn20.xml设计工具自动根据.activiti文件生成的xml文件

    .png流程图图片

    解决办法就是把xml文件和图片文件同时部署,因为在单独部署xml文件的时候Activiti会自动生成一张流程图的图片文件,但是这样在使用的时候坐标和图片对应不起来……

    所以把xml和图片同时部署的时候Activiti自动关联xml和图片,当需要获取图片的时候直接返回部署时压缩包里面的图片文件,而不是Activiti自动生成的图片文件
    2.1.2.1 使用工具打包Bar文件
    右键项目名称然后点击“Create deployment artifacts”,会在src目录中创建deployment文件夹,里面包含*.bar文件.

    2.1.2.2 使用Ant脚本打包Zip文件
    这也是我们采用的办法,你可以手动选择xml和png打包成zip格式的文件,也可以像我们一样采用ant target的方式打包这两个文件。`

    
    <?xml version="1.0" encoding="UTF-8"?>
    <project name="foo">
     
        <property name="workflow.definition" value="foo-common-core/src/main/resources/diagrams" />
        <property name="workflow.deployments" value="foo-common-core/src/main/resources/deployments" />
     
    <target name="workflow.package.oa.leave">
    		<echo>打包流程定义及流程图::OA-请假</echo>
    		<zip destfile="${workflow.deployments}/oa/leave.zip" basedir="${workflow.definition}/oa/leave" update="true"
    			includes="*.xml,*.png" />
    	</target>
    </project>
    
    

    这样当修改流程定义文件后只要运行ant命令就可以打包了:

    ant workflow.package.oa.leave
    

    现在部署bar或者zip文件查看流程图图片就不是乱码了,而是你的压缩包里面的png文件。
    2.2 使用引擎提供的Form还是自定义业务Form
    2.2.1 引擎提供的Form
    定义表单的方式在每个Task标签中定义extensionElements和activiti:formProperty即可,到达这个节点的时候可以通过API读取表单元素。

    Activiti官方的例子使用的就是在流程定义中设置每一个节点显示什么样的表单哪些字段需要显示、哪些字段只读、哪些字段必填。

    但是这种方式仅仅适用于比较简单的流程,对于稍微复杂或者页面需要业务逻辑的判断的情况就不适用了。

    对于数据的保存都是在引擎的表中,不利于和其他表的关联、对整个系统的规划也不利!

    2.2.2 自定义业务Form
    这种方式应该是大家用的最多的了,因为一般的业务系统业务逻辑都会比较复杂,而且数据库中很多表都会有依赖关系,表单中有很多状态判断。

    例如我们的系统适用jQuery UI作为UI,有很多javascript代码,页面的很多操作需要特殊处理(例如:多个选项的互斥、每个节点根据类型和操作人显示不同的按钮);基本每个公司都有一套自己的UI风格,要保持多个系统的操作习惯一致只能使用自定义表单才能满足。

    2.3 业务和流程的关联方式
    这个问题在群里面很多人都问过,这也是我刚刚开始迷惑的地方;

    后来看了以下API发现RuntimeService有两个方法:

    2.3.1 startProcessInstanceByKey
    javadoc对其说明:

    startProcessInstanceByKey(String processDefinitionKey, Map variabes) 
              Starts a new process instance in the latest version of the process definition with the given key
    

    其中businessKey就是业务ID,例如要申请请假,那么先填写登记信息,然后(保存+启动流程),因为请假是单独设计的数据表,所以保存后得到实体ID就可以把它传给processInstanceBusinessKey方法启动流程。当需要根据businessKey查询流程的时候就可以通过API查询:

    runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(processInstanceBusinessKey, processDefinitionKey);
    

    建议数据库冗余设计:在业务表设计的时候添加一列:PROCESS_INSTANCE_ID varchar2(64),在流程启动之后把流程ID更新到业务表中,这样不管从业务还是流程都可以查询到对方!

    特别说明: 此方法启动时自动选择最新版本的流程定义。

    2.3.2 startProcessInstanceById
    javadoc对其说明:

    startProcessInstanceById(String processDefinitionId, String businessKey, Map variables) 
              Starts a new process instance in the exactly specified version of the process definition with the given id
    

    processDefinitionId:这个参数的值可以通过repositoryService.createProcessDefinitionQuery()方法查询,对应数据库:ACT_RE_PROCDEF;每次部署一次流程定义就会添加一条数据,同名的版本号累加。

    特别说明: 此可以指定不同版本的流程定义,让用户多一层选择。

    2.3.3 如何选择
    建议使用startProcessInstanceByKey,特殊情况需要使用以往的版本选择使用startProcessInstanceById。

    2.4 同步用户数据
    这个问题也是比较多的人询问过,Activiti支持对任务分配到:指定人、指定组、两者组合,而这些人和组的信息都保存在ACT_ID…表中,有自己的用户和组(角色)管理让很多人不知所措了;原因是因为每个系统都会存在一个权限管理模块(维护:用户、部门、角色、授权),不知道该怎么和Activiti同步。

    2.4.1 建议处理方式
    Activiti有一个IdentityService接口,通过这个接口可以操控Activiti的ACT_ID_*表的数据,一般的做法是用业务系统的权限管理模块维护用户数据,当进行CRUD操作的时候在原有业务逻辑后面添加同步到Activiti的代码;例如添加一个用户时同步Activiti User的代码片段:

    /**
     * 保存用户信息 并且同步用户信息到activiti的identity.User,同时设置角色
     * @param user
     * @param roleIds
     */
    public void saveUser(User user, List<Long> roleIds, boolean synToActiviti) {
    	accountManager.saveEntity(user);
    	String userId = user.getId().toString();
     
    	if (synToActiviti) {
    		List<org.activiti.engine.identity.User> activitiUsers = identityService.createUserQuery().userId(userId).list();
    		if (activitiUsers.size() == 1) {
    			//更新信息
    			org.activiti.engine.identity.User activitiUser = activitiUsers.get(0);
    			activitiUser.setFirstName(user.getName());
    			activitiUser.setLastName("");
    			activitiUser.setPassword(user.getPassword());
    			activitiUser.setEmail(user.getEmail());
    			identityService.saveUser(activitiUser);
     
    			// 删除用户的membership
    			List<Group> activitiGroups = identityService.createGroupQuery().groupMember(userId).list();
    			for (Group group : activitiGroups) {
    				identityService.deleteMembership(userId, group.getId());
    			}
     
    			// 添加membership
    			for (Long roleId : roleIds) {
    				Role role = roleManager.getEntity(roleId);
    				identityService.createMembership(userId, role.getEnName());
    			}
     
    		} else {
    			org.activiti.engine.identity.User newUser = identityService.newUser(userId);
    			newUser.setFirstName(user.getName());
    			newUser.setLastName("");
    			newUser.setPassword(user.getPassword());
    			newUser.setEmail(user.getEmail());
    			identityService.saveUser(newUser);
     
    			// 添加membership
    			for (Long roleId : roleIds) {
    				Role role = roleManager.getEntity(roleId);
    				identityService.createMembership(userId, role.getEnName());
    			}
    		}
    	}
     
    }
    

    删除操作也和这个类似!

    不管从业务系统维护用户还是从Activiti维护,肯定要确定一方,然后CRUD的时候同步到对方,如果需要同步多个子系统那么可以再调用WebService实现。
    2.5 流程图设计工具用什么
    Activiti提供了两个流程设计工具,但是面向对象不同。

    Activiti Modeler,面向业务人员,使用开源的BPMN设计工具Signavio,使用BPMN描述业务流程图

    Eclipse Designer,面向开发人员,Eclipse的插件,可以让开发人员定制每个节点的属性(ID、Name、Listener、Attr等)

    2.5.1 我们的方式
    可能你会惊讶,因为我们没有使用Activiti Modeler,我们认为用Viso已经能表达流程图的意思了,而且项目经理也是技术出身,和开发人员也容易沟通。

    目前这个项目是第一个使用Activiti的,开始我们在需求调研阶段使用Viso设计流程图,利用泳道流程图设计和客户沟通,确定后由负责流程的开发人员用Eclipse Designer设计得到bpmn20.xml,最后部署。

    2.6 Eclipse Designer存在的问题
    这个插件有一个很讨厌的Bug一直未修复,安装了插件后Eclipse的复制和粘帖快捷键会被更换为(Ctrl+Insert、Shift+Insert);Bug描述请见:

    Activit Forums中报告的Bug

    Jira的登记

    所以最后我们只能单独开一个安装了Eclipse Designer的Eclipse专门用来设计流程图,这样就不影响正常使用Eclipse JAVAEE了。

    3.配置
    3.1 集成Spring
    对于和Spring的集成Activiti做的不错,简单配置一些Bean代理即可实现,但是有两个和事务相关的地方要提示:

    配置processEngineConfiguration的时候属性transactionManager要使用和业务功能的同一个事务管理Bean,否则事务不同步。

    对于实现了org.activiti.engine.delegate包中的接口的类需要被事务控制的实现类需要被Spring代理,并且添加事务的Annotation或者在xml中配置,例如:

    /**
     * 创建缴费流程的时候自动创建实体
     *
     * @author HenryYan
     */
    @Service
    @Transactional
    publicclass CreatePaymentProcessListener implementsExecutionListener {
       ....
    }
    

    4.使用单元测试
    单元测试均使用Spring的AbstractTransactionalJUnit4SpringContextTests作为SuperClass,并且在测试类添加:

    @ContextConfiguration(locations = { "/applicationContext-test.xml"})
    @RunWith(SpringJUnit4ClassRunner.class)
    

    虽然Activiti也提供了测试的一些超类,但是感觉不好用,所以自己封装了一些方法。

    代码请转移:https://gist.github.com/2182847

    4.1 验证流程图设计是否正确
    代码请转移:https://gist.github.com/2182869

    4.2 业务对象和流程关联测试
    代码请转移:https://gist.github.com/2182973
    5.各种状态的任务查询以及和业务对象关联
    我们目前分为4中状态:未签收、办理中、运行中、已完成。

    查询到任务或者流程实例后要显示在页面,这个时候需要添加业务数据,最终结果就是业务和流程的并集,请参考6.2。
    5.1 未签收(Task)
    此类任务针对于把Task分配给一个角色时,例如部门领导,因为部门领导角色可以指定多个人所以需要先签收再办理,术语:抢占式

    对应的API查询:
    /**

    • 获取未签收的任务查询对象
    • @param userId 用户ID
      */
      @Transactional(readOnly = true)
      publicTaskQuery createUnsignedTaskQuery(String userId) {
      TaskQuery taskCandidateUserQuery = taskService.createTaskQuery().processDefinitionKey(getProcessDefKey())
      .taskCandidateUser(userId);
      returntaskCandidateUserQuery;
      }
      5.2 办理中(Task)
      此类任务数据类源有两种:

    签收后的,5.1中签收后就应该为办理中状态

    节点指定的是具体到一个人,而不是角色

    对应的API查询:

    /**
     * 获取正在处理的任务查询对象
     * @param userId    用户ID
     */
    @Transactional(readOnly = true)
    publicTaskQuery createTodoTaskQuery(String userId) {
        TaskQuery taskAssigneeQuery = taskService.createTaskQuery().processDefinitionKey(getProcessDefKey()).taskAssignee(userId);
        returntaskAssigneeQuery;
    }
    

    5.3 运行中(ProcessInstance)
    说白了就是没有结束的流程,所有参与过的人都应该可以看到这个实例,但是Activiti的API没有可以通过用户查询的方法,这个只能自己用hack的方式处理了,我目前还没有处理。

    从表ACT_RU_EXECUTION中查询数据。

    对应的API查询:

    /**
     * 获取未经完成的流程实例查询对象
     * @param userId    用户ID
     */
    @Transactional(readOnly = true)
    publicProcessInstanceQuery createUnFinishedProcessInstanceQuery(String userId) {
        ProcessInstanceQuery unfinishedQuery = runtimeService.createProcessInstanceQuery().processDefinitionKey(getProcessDefKey())
                .active();
        returnunfinishedQuery;
    }
    

    5.4 已完成(HistoricProcessInstance)
    已经结束的流程实例。

    从表ACT_HI_PROCINST中查询数据。

    /**
     * 获取已经完成的流程实例查询对象
     * @param userId    用户ID
     */
    @Transactional(readOnly = true)
    publicHistoricProcessInstanceQuery createFinishedProcessInstanceQuery(String userId) {
        HistoricProcessInstanceQuery finishedQuery = historyService.createHistoricProcessInstanceQuery()
                .processDefinitionKey(getProcessDefKey()).finished();
        returnfinishedQuery;
    }
    

    5.5 查询时和业务关联
    提示:之前在业务对象添加了PROCESS_INSTANCE_ID字段

    思路:现在可以利用这个字段查询了,不管是Task还是ProcessInstance都可以得到流程实例ID,可以根据流程实例ID查询实体然后把流程对象设置到实体的一个属性中由Action或者Controller输出到前台。

    代码请参考:https://gist.github.com/2183557

    6.UI及截图
    结合实际业务描述一个业务从开始到结束的过程,对于迷惑的同学看完豁然开朗了;这里使用请假作为例子。

    6.1 单独一个列表负责申请
    这样的好处是申请和流程办理分离开处理,列表显示未启动流程的请假记录(数据库PROCESS_INSTANCE_ID为空)。

    申请界面的截图:

    在这里插入图片描述
    在这里插入图片描述
    6.3 流程跟踪
    图片方式显示当前节点:
    在这里插入图片描述
    列表形式显示流程流转过程:
    在这里插入图片描述
    6.3.1 当前节点定位JS
    Java代码请移步:https://gist.github.com/2183712

    Javascript思路:先通过Ajax获取当前节点的坐标,在指定位置添加红色边框,然后加载图片。

    代码移步:https://gist.github.com/2183804

    7.开启Logger
    添加log4j的jar
    设置log4j.logger.java.sql=DEBUG

    展开全文
  • 工作流所谓工作流引擎是指workflow作为应用系统的一部分,并为之提供对各应用系统有决定作用的根据角色、分工和条件的不同决定信息传递路由、内容等级等核心解决方案。工作流引擎包括流程的节点管理、流向管理、流程...

空空如也

1 2 3 4 5 ... 20
收藏数 7,998
精华内容 3,199
关键字:

工作流引擎