用过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;
}
这样就完成了一个自定义任务的定义,创建,发布的过程,以上仅仅是个人的思路,欢迎大家讨论或提出意见。
网友评论
ServiceTask 你传一个json字符串就可以了吧,在代码里面格式化
<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
如果是task本身的属性,可以通过taskId获取到这个task,查看属性;
如果是task内部的变量,可以通过taskService获取runtimeServie setVariable流程变量的方式在后续节点中获取;
scriptTask后台是执行一段脚本,脚本使用groovy,或者javascript。
而serviceTask后台绑定的是一个java类可以注入其他service,可以进行数据库操作,功能和实用性都更强大