扩展Activiti工作流任务和属性

作者: 程序鱼 | 来源:发表于2016-09-24 13:01 被阅读6059次

    用过activiti的朋友都知道,activiti做工作流用起来非常方便,可以很容易的基于activiti开发出一个基础的OA流程,可以有多种任务,事件,网关提供给大家选择,想了解详细的,可以点击下方链接多了解,网上资料也特别多,这里不再赘述。

    下面给出几个快速入门的链接:
    咖啡兔的demo英文版activti使用手册中文版用户手册

    但是最近做了一个项目要求是:

    • 通过web访问的可视化可扩展的流程设计器
    • 可以定制任务节点
    • 可以扩展任务属性

    这一下懵逼了,三个问题都不简单

    首先,activiti虽然自带一个web设计器activiti modeler,但是如果要支持自定义任务节点和扩展节点属性,很难修改,而且修改之后activiti引擎并不识别。
    然后就是我们扩展的任务节点和任务属性activiti引擎不识别的问题。

    结果就是面对着activiti这个金矿却无从下手,而且如果要硬生生开发出一个activiti的新的任务节点,就得深入剖析activiti的代码,工作量和难度都非常大,最后只能决定曲线救国,看看activiti有没有什么现成的任务能支持扩展属性,如果有,这样就可以同时解决扩展属性的问题了,翻了翻用户手册,发现activiti有个任务节点叫ServiceTask,眼睛一亮这不就是我想要的么。ServiceTask参考

    ServiceTask Description 描述

    Java 服务任务用来调用外部 Java 类

    XML representation 内容

    有4钟方法来声明 java 调用逻辑:

    • 实现 JavaDelegate 或 ActivityBehavior
    • 执行解析代理对象的表达式
    • 调用一个方法表达式
    • 调用一直值表达式

    执行一个在流程执行中调用的类, 需要在'activiti:class'属性中设置全类名,这个类设置后,流程执行到serviceTask这一步时就会自动调用这个java类。

    <serviceTask id="javaService"  name="My Java Service Task" 
        activiti:class="com.yang.activiti.demo.service.ServiceTaskService" />
    

    注意,如果使用了spring mvc框架,默认配置的 activiti:class 是不能通过注解注入类的,如果想要通过注解注入java类,需要使用如下语法

    <serviceTask id="serviceTask" activiti:delegateExpression="${serviceTaskService}" />
    

    serviceTaskService是一个实现了 JavaDelegate 接口的bean, 它定义在实例的 spring 容器中

    serviceTask还可以注入多个参数

    <serviceTask id="ServiceTaskService" 
        name="ServiceTaskService" 
        activiti:class="com.yang.activiti.demo.service.ServiceTaskService">
      <extensionElements>   
        <activiti:field name="param1">
            <activiti:string> Hello World</activiti:string>
        </activiti:field>
      <activiti:field name="param2">
            <activiti:string> Hello World</activiti:string>
        </activiti:field>
      </extensionElements>        
    </serviceTask>  
    

    java后台com.yang.activiti.demo.service.ServiceTaskService

    public class ServiceTaskService implements JavaDelegate {
    
      private Expression param1;
      private Expression param2;
    
      public void execute(DelegateExecution execution) {
        String value1 = (String) param1.getValue(execution);
        execution.setVariable("var1", new StringBuffer(value1).reverse().toString());
    
        String value2 = (String) param2.getValue(execution);
        execution.setVariable("var2", new StringBuffer(value2).reverse().toString());
      }
    }
    

    还有其他的参数属性去参照ServiceTask参考不得不说这个节点真的很灵活。


    至此我发现了ServiceTask的两个重要特性:

    1. 可以绑定自定义的java class(这不就是我想要的扩展自定义任务么)
    2. 可以传递无限多个参数(这不就是我想要的扩展属性么)

    基于ServiceTask可以有无限多的可能。
    这样基本解决了以下两个大问题

    • 可以定制任务节点
    • 可以扩展任务属性

    下面就剩下一个问题

    • 通过web访问的可视化可扩展的流程设计器

    然后就继续调研,发现activiti modeler我是肯定用不了了,原因如下:

    • 因为要使用serviceTask扩展任务节点,就要涉及复制出多个serviceTask节点,结果发现复制出的serviceTask节点会将原生的serviceTask节点功能覆盖,也就是说只能同时存在一个serviceTask节点
    • 复制出来的serviceTask节点扩展属性,例如:param1,param2,activiti引擎根本不认识,会报错

    所以不能用自带的activiti modeler,那么就得去寻找一个流程设计器替代品,替代品有如下要求:

    • 代码结构尽量简单
    • 可以将设计好的流程保存为格式化数据,例如json,xml等
    • 扩展性好
    • 好集成

    找了老半天,有很多不错的设计器,但是都不太满足要求,最后还是万能的github帮了我,找到了一个大神开发的一款基于js的可拖拽流程设计器,可以将设计好的流程保存为json格式,双手附上链接LarryleWorkFlow,同时也谢谢作者wangyue20075,设计器的主页是这样子的:

    设计器的主页.PNG

    通过作者的工具类可以很容易的去扩展属性,扩展任务,然后将结果保存为json。

    下面问题又来了:
    保存的json activiti不认识
    这个就需要我们自己去写activiti的task解释器了,将我们读取到的json解释成acitivi可以认识的xml,保存在数据库中,然后读取我们的xml去调用activiti的接口发布。

    repositoryService.createDeployment() .name(workflowDesigner.getFlowId())
     .addString(processName, flowBpmnXml).deploy();
    

    下面贴一段我的代码:

    @RequestMapping("deployFlow/{flowId}")
        @ResponseBody
        public Map<String,Object> deployFlow(@PathVariable("flowId") String flowId){
            Map<String,Object> result = new HashMap<String,Object>();
            workflowDesigner workflowDesigner = workflowDesignerService.selectByPrimaryKey(flowId);
            String flowBpmnXml = workflowDesigner.getFlowBpmnXml();
            String processName = workflowDesigner.getFlowId() + ".bpmn20.xml";
            logger.debug("processName is : "+processName);
            try {
                Deployment deployment = repositoryService.createDeployment()
                                                         .name(workflowDesigner.getFlowId())
                                                         .addString(processName, flowBpmnXml).deploy();
                ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId());
                ProcessDefinition processDefinition = query.singleResult();
                result.put("code",1);
                result.put("message",processDefinition.getId());
            } catch (Exception e) {
                result.put("code",0);
                result.put("message",e.getMessage());
                logger.error(e.getMessage());
            }
            logger.debug(result.toString());
            return result;
        }
    

    这样就完成了一个自定义任务的定义,创建,发布的过程,以上仅仅是个人的思路,欢迎大家讨论或提出意见。

    相关文章

      网友评论

      • 丢在风里_af7d:最近写到这个需求 将我们读取到的json解释成acitivi可以认识的xml 这句话 能给点思路吗
      • 最后一只天蝎:设计器 没办法 连接节点与节点之间的连线
      • 牵着老虎晒月亮:博主,您好,请问能将 读取到的json解释成acitivi可以认识的xml 的代码给我一份让我参考下吗?
        绝版公子:博主您好,可以说说具体的json解释成activiti认识的xml的思路吗?或者是您能把公司业务剔除一下,给个demo开源一下吗?非常感谢您。
        程序鱼:@牵着老虎晒月亮 我可以给你说说思路,但是源码由于和公司产品集成了太多,不方便开源出来
      • 2df9ff65ed0e:博主你好,因为业务需求我要在modeler任务节点添加一些自定义节点,如果使用activiti自带的输入框之类的直接就能用,但是怎么写自己的呢?
        程序鱼:@范宇_5e15 activiti自带的设计器不支持自定义
      • 26e154491e89:博主你好,请问动态表单中的枚举类型可不可以从后台代码中获取枚举项,谢谢
        程序鱼:@Yolo丶 没有试过,还不太清楚,你试一下
      • 粗心不改:博主,您好,最近也碰到需要新加一种自定义的任务类型,我的需求是加一种类似userTask和serviceTask的结合提,就是用户绑定的表单预配置了表单数据,就自动运行,如果没有输入表单数据就停止等待用户输入表单数据,楼主有什么好的建议吗,还有serviceTask的参数怎么动态加啊,我有可能是2个、3个或者多个,如参数类型是个Map的话就满足需求了,不知道行不行,谢谢!
        程序鱼:@粗心不改 目前没发现可以这么干,应该不行
        粗心不改:对哦,json可用解决动态参数问题,非常谢谢,serviceTask像userTask那样在modeler那里配置绑定一个表单吗
        程序鱼:@粗心不改 这得画流程图时候用分支条件判断了,分支的条件是是否配置了表单

        ServiceTask 你传一个json字符串就可以了吧,在代码里面格式化
      • 爱因斯丹:我使用下面这种方法调用一个已经实现了JavaDelegate接口的类InitAssignmentDelegate
        <serviceTask id="serviceTask" activiti:delegateExpression="${serviceTaskService}" />

        public class InitAssignmentDelegate implements JavaDelegate{

        private final static Logger LOG = LoggerFactory.getLogger(InitAssignmentDelegate.class);

        @Autowired
        private AssignmentUserService assignmentUserService;

        @Override
        public void execute(DelegateExecution execution) throws Exception {
        LOG.info("aaa.");
        }
        }
        可以执行execute方法,但是assignmentUserService是null
        爱因斯丹:@程序鱼 不好意思,我写错了,因为一直在更改。流程里是这么配置的<serviceTask id="serviceTask" activiti:delegateExpression="${initAssignmentDelegate}" /> 我在InitAssignmentDelegate中autowired了AssignmentUserService,但是assignmentUserService是空的
        程序鱼:你注入的service的名字是serviceTaskService,并不是assignmentUserService啊
      • 75f7190577d4:请问下,任务的扩展属性能在LarryleWorkFlow中显示和编辑吗?比如点击任务1,然后属性表里面能够显示param1和param2进行填写和编辑吗?
        程序鱼:@小青向前冲 我不知道你指的是task本身的属性还是task节点里面的一些变量,
        如果是task本身的属性,可以通过taskId获取到这个task,查看属性;
        如果是task内部的变量,可以通过taskService获取runtimeServie setVariable流程变量的方式在后续节点中获取;
        75f7190577d4:@来只鱼 谢谢,我想再问下,不知道您有没有用过这样的场景,一个task节点需要用到它之前task的属性,这个要怎么获取呢?
        程序鱼:@小青向前冲 可以,我这边的逻辑是,编辑前需要终止该流程的所有正在运行实例,然后编辑之后更新以前的流程模板,然后重新发布该流程
      • 杨旸52:好
        程序鱼:@杨旸52凤 :grin:
      • 枝头残月野狼嚎嗷嗷呜:博主你好,我今天刚开始研究activiti,请问你说的对serviceTask的扩展,具体有什么应用呢?能举几个例子吗?谢谢
        程序鱼:@枝头残月野狼嚎嗷嗷呜
        scriptTask后台是执行一段脚本,脚本使用groovy,或者javascript。
        而serviceTask后台绑定的是一个java类可以注入其他service,可以进行数据库操作,功能和实用性都更强大
        枝头残月野狼嚎嗷嗷呜:谢谢回复!但我不是很懂这跟script task有什么区别呢?
        程序鱼:@枝头残月野狼嚎嗷嗷呜 比方说我现在需要在activiti里面增加一个任务节点,要求是这个节点在后台自动执行,不需要人工干预,而且这个节点可以在后台执行一个脚本,而且脚本的参数可以在创建的时候动态指定
      • cbab8b8ee9fb:“将我们读取到的json解释成acitivi可以认识的xml”,这个工作量巨大啊,要点开发实力!
        程序鱼:@Xluo 其实没有想象的那么复杂,因为activiti的每一种task都是有固定的xml表示,只不过是属性参数在变化,我们就是要写一个解释器把json的属性读出来,写成xml

      本文标题:扩展Activiti工作流任务和属性

      本文链接:https://www.haomeiwen.com/subject/uzjxyttx.html