用過(guò)activiti的朋友都知道,activiti做工作流用起來(lái)非常方便,可以很容易的基于activiti開(kāi)發(fā)出一個(gè)基礎(chǔ)的OA流程,可以有多種任務(wù),事件,網(wǎng)關(guān)提供給大家選擇,想了解詳細(xì)的,可以點(diǎn)擊下方鏈接多了解,網(wǎng)上資料也特別多,這里不再贅述。
下面給出幾個(gè)快速入門(mén)的鏈接:
咖啡兔的demo,英文版activti使用手冊(cè), 中文版用戶手冊(cè)
但是最近做了一個(gè)項(xiàng)目要求是:
- 通過(guò)web訪問(wèn)的可視化可擴(kuò)展的流程設(shè)計(jì)器
- 可以定制任務(wù)節(jié)點(diǎn)
- 可以擴(kuò)展任務(wù)屬性
這一下懵逼了,三個(gè)問(wèn)題都不簡(jiǎn)單
首先,activiti雖然自帶一個(gè)web設(shè)計(jì)器activiti modeler,但是如果要支持自定義任務(wù)節(jié)點(diǎn)和擴(kuò)展節(jié)點(diǎn)屬性,很難修改,而且修改之后activiti引擎并不識(shí)別。
然后就是我們擴(kuò)展的任務(wù)節(jié)點(diǎn)和任務(wù)屬性activiti引擎不識(shí)別的問(wèn)題。
結(jié)果就是面對(duì)著activiti這個(gè)金礦卻無(wú)從下手,而且如果要硬生生開(kāi)發(fā)出一個(gè)activiti的新的任務(wù)節(jié)點(diǎn),就得深入剖析activiti的代碼,工作量和難度都非常大,最后只能決定曲線救國(guó),看看activiti有沒(méi)有什么現(xiàn)成的任務(wù)能支持?jǐn)U展屬性,如果有,這樣就可以同時(shí)解決擴(kuò)展屬性的問(wèn)題了,翻了翻用戶手冊(cè),發(fā)現(xiàn)activiti有個(gè)任務(wù)節(jié)點(diǎn)叫ServiceTask,眼睛一亮這不就是我想要的么。ServiceTask參考
ServiceTask Description 描述
Java 服務(wù)任務(wù)用來(lái)調(diào)用外部 Java 類(lèi)
XML representation 內(nèi)容
有4鐘方法來(lái)聲明 java 調(diào)用邏輯:
- 實(shí)現(xiàn) JavaDelegate 或 ActivityBehavior
- 執(zhí)行解析代理對(duì)象的表達(dá)式
- 調(diào)用一個(gè)方法表達(dá)式
- 調(diào)用一直值表達(dá)式
執(zhí)行一個(gè)在流程執(zhí)行中調(diào)用的類(lèi), 需要在'activiti:class'屬性中設(shè)置全類(lèi)名,這個(gè)類(lèi)設(shè)置后,流程執(zhí)行到serviceTask這一步時(shí)就會(huì)自動(dòng)調(diào)用這個(gè)java類(lèi)。
<serviceTask id="javaService" name="My Java Service Task" activiti:class="com.yang.activiti.demo.service.ServiceTaskService" />
注意,如果使用了spring mvc框架,默認(rèn)配置的 activiti:class 是不能通過(guò)注解注入類(lèi)的,如果想要通過(guò)注解注入java類(lèi),需要使用如下語(yǔ)法
<serviceTask id="serviceTask" activiti:delegateExpression="${serviceTaskService}" />
serviceTaskService是一個(gè)實(shí)現(xiàn)了 JavaDelegate 接口的bean, 它定義在實(shí)例的 spring 容器中
serviceTask還可以注入多個(gè)參數(shù)
<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后臺(tái)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()); } }
還有其他的參數(shù)屬性去參照ServiceTask參考不得不說(shuō)這個(gè)節(jié)點(diǎn)真的很靈活。
至此我發(fā)現(xiàn)了ServiceTask的兩個(gè)重要特性:
1. 可以綁定自定義的java class(這不就是我想要的擴(kuò)展自定義任務(wù)么)
2. 可以傳遞無(wú)限多個(gè)參數(shù)(這不就是我想要的擴(kuò)展屬性么)
基于ServiceTask可以有無(wú)限多的可能。
這樣基本解決了以下兩個(gè)大問(wèn)題
- 可以定制任務(wù)節(jié)點(diǎn)
- 可以擴(kuò)展任務(wù)屬性
下面就剩下一個(gè)問(wèn)題
- 通過(guò)web訪問(wèn)的可視化可擴(kuò)展的流程設(shè)計(jì)器
然后就繼續(xù)調(diào)研,發(fā)現(xiàn)activiti modeler我是肯定用不了了,原因如下:
- 因?yàn)橐褂胹erviceTask擴(kuò)展任務(wù)節(jié)點(diǎn),就要涉及復(fù)制出多個(gè)serviceTask節(jié)點(diǎn),結(jié)果發(fā)現(xiàn)復(fù)制出的serviceTask節(jié)點(diǎn)會(huì)將原生的serviceTask節(jié)點(diǎn)功能覆蓋,也就是說(shuō)只能同時(shí)存在一個(gè)serviceTask節(jié)點(diǎn)
- 復(fù)制出來(lái)的serviceTask節(jié)點(diǎn)擴(kuò)展屬性,例如:param1,param2,activiti引擎根本不認(rèn)識(shí),會(huì)報(bào)錯(cuò)
所以不能用自帶的activiti modeler,那么就得去尋找一個(gè)流程設(shè)計(jì)器替代品,替代品有如下要求:
- 代碼結(jié)構(gòu)盡量簡(jiǎn)單
- 可以將設(shè)計(jì)好的流程保存為格式化數(shù)據(jù),例如json,xml等
- 擴(kuò)展性好
- 好集成
找了老半天,有很多不錯(cuò)的設(shè)計(jì)器,但是都不太滿足要求,最后還是萬(wàn)能的github幫了我,找到了一個(gè)大神開(kāi)發(fā)的一款基于js的可拖拽流程設(shè)計(jì)器,可以將設(shè)計(jì)好的流程保存為json格式,雙手附上鏈接LarryleWorkFlow,同時(shí)也謝謝作者wangyue20075,設(shè)計(jì)器的主頁(yè)是這樣子的:
通過(guò)作者的工具類(lèi)可以很容易的去擴(kuò)展屬性,擴(kuò)展任務(wù),然后將結(jié)果保存為json。
下面問(wèn)題又來(lái)了:
保存的json activiti不認(rèn)識(shí)
這個(gè)就需要我們自己去寫(xiě)activiti的task解釋器了,將我們讀取到的json解釋成acitivi可以認(rèn)識(shí)的xml,保存在數(shù)據(jù)庫(kù)中,然后讀取我們的xml去調(diào)用activiti的接口發(fā)布。
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;
}
這樣就完成了一個(gè)自定義任務(wù)的定義,創(chuàng)建,發(fā)布的過(guò)程,以上僅僅是個(gè)人的思路,歡迎大家討論或提出意見(jiàn)。