Activiti 工作流引擎的初步使用
最近領導讓我研究下工作流,于是查啊查就查到了Activiti,特么剛開始一直查的是Activity,查出來一堆Android的東西,我也是醉了。話不多說,下面就記錄下這2天的研究成果吧。
所用環境
Maven工程
JDK:jdk1.8.0_73
IDE:eclipse Mars.2 Release (4.5.2)
數據庫:mysql 5.1.39-ndb-7.0.9-cluster-gpl
SSM框架:(spring + spring-mvc)4.3.2.RELEASE + mybatis3.4.1
Activiti:5.21.0
spring+mvc+mybatis整合就不貼了,網上一大堆了
eclipse安裝流程設計插件
eclipse依次點擊 Help -> Install New Software -> Add:
Name:Activiti Designer
Location:http://activiti.org/designer/update/
點擊OK選中插件安裝即可
添加 Activiti 到項目中
在 pom.xml 中添加 Activiti 依賴
<activiti.version>5.21.0</activiti.version>
2.新建 applicationContext-activiti.xml,別忘了在主配置文件中將其import
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
3.啟動項目,如果未出現錯誤,Activiti會在連接的數據庫中自動新建25張表,如下:
25張表.png
至此Activiti流程引擎添加完畢,下面開始使用Activiti,實現一次請假流程
設計流程
流程設計插件安裝成功后會在eclipse新建向導中出現Activiti向導,如圖
Paste_Image.png
1.我們新建一個 Activiti Diagram 命名為 leave.bpmn,完成時如下圖:
Paste_Image.png
主要就是拖拖拉拉,從左至右控件分別為
StartEvent,UserTask,ExlusiveGateway,UserTask,EndEvent;連線都是SequenceFlow
屬性可在Properties視圖中設置,如果沒有這個視圖,可在 eclipse 中依次點擊
Window->Show View->Other 搜索Properties點擊OK即可
- 流程屬性:一般設置一個Id,Name,NameSpace就可以了,此處為分別為leaveProcess、Leave Process、http://www.mario.com; 這個Id在之后啟動流程時會用到,不同流程Id不可相同
- 開始事件(StartEvent):流程開始
- 結束事件(EndEvent):流程結束
- 用戶任務(UserTask):主要用到Id,Name,Assignee
Assignee為此任務的辦理人,在查找任務時需要用到,三個任務分別指派給 apply,pm,boss
Paste_Image.png
Paste_Image.png
- 排他網關(ExlusiveGateway):只會尋找唯一一條能走完的順序流
- 順序流(SequenceFlow):主要用到Id,Name,Condition
Condition用于指定該順序流表達式 ,day的值會在調用執行任務方法時傳入
Paste_Image.png
除了使用可視化組件,我們也可以通過xml來設計流程,以上流程的xml定義如下:
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.mario.com"><process id="leaveProcess" name="Leave Process" isExecutable="true"><startEvent id="startevent1" name="開始"></startEvent><userTask id="usertask1" name="請假申請" activiti:assignee="apply"></userTask><exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway><sequenceFlow id="flow2" name="天數判斷" sourceRef="usertask1" targetRef="exclusivegateway1"></sequenceFlow><userTask id="usertask2" name="審批(項目經理)" activiti:assignee="pm"></userTask><sequenceFlow id="flow3" name="小于等于三天" sourceRef="exclusivegateway1" targetRef="usertask2"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${day<=3}]]></conditionExpression></sequenceFlow><userTask id="usertask3" name="審批(老板)" activiti:assignee="boss"></userTask><sequenceFlow id="flow4" name="大于三天" sourceRef="exclusivegateway1" targetRef="usertask3"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${day>3}]]></conditionExpression></sequenceFlow><endEvent id="endevent1" name="End"></endEvent><sequenceFlow id="flow5" sourceRef="usertask2" targetRef="endevent1"></sequenceFlow><sequenceFlow id="flow6" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow><sequenceFlow id="flow7" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow></process><bpmndi:BPMNDiagram id="BPMNDiagram_leaveProcess"><bpmndi:BPMNPlane bpmnElement="leaveProcess" id="BPMNPlane_leaveProcess"><bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"><omgdc:Bounds height="35.0" width="35.0" x="30.0" y="211.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1"><omgdc:Bounds height="55.0" width="105.0" x="110.0" y="201.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1"><omgdc:Bounds height="40.0" width="40.0" x="285.0" y="208.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2"><omgdc:Bounds height="55.0" width="105.0" x="400.0" y="120.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3"><omgdc:Bounds height="55.0" width="105.0" x="400.0" y="290.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"><omgdc:Bounds height="35.0" width="35.0" x="560.0" y="211.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2"><omgdi:waypoint x="215.0" y="228.0"></omgdi:waypoint><omgdi:waypoint x="285.0" y="228.0"></omgdi:waypoint><bpmndi:BPMNLabel><omgdc:Bounds height="14.0" width="48.0" x="230.0" y="228.0"></omgdc:Bounds></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"><omgdi:waypoint x="305.0" y="208.0"></omgdi:waypoint><omgdi:waypoint x="452.0" y="175.0"></omgdi:waypoint><bpmndi:BPMNLabel><omgdc:Bounds height="14.0" width="100.0" x="295.0" y="180.0"></omgdc:Bounds></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"><omgdi:waypoint x="305.0" y="248.0"></omgdi:waypoint><omgdi:waypoint x="452.0" y="290.0"></omgdi:waypoint><bpmndi:BPMNLabel><omgdc:Bounds height="14.0" width="48.0" x="285.0" y="257.0"></omgdc:Bounds></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5"><omgdi:waypoint x="452.0" y="175.0"></omgdi:waypoint><omgdi:waypoint x="577.0" y="211.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6"><omgdi:waypoint x="452.0" y="290.0"></omgdi:waypoint><omgdi:waypoint x="577.0" y="246.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7"><omgdi:waypoint x="65.0" y="228.0"></omgdi:waypoint><omgdi:waypoint x="110.0" y="228.0"></omgdi:waypoint></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram> </definitions>流程部署
有了流程圖,我們就可以部署該流程了,Activiti提供多種部署方法(有自動部署,手動部署等),這里演示兩種手動部署方法
Workflow.java代碼片段
private static ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /*** 通過定義好的流程圖文件部署,一次只能部署一個流程*/ public static void deploy() {RepositoryService repositoryService = processEngine.getRepositoryService();Deployment deployment = repositoryService.createDeployment().addClasspathResource("death/note/lawliet/web/workflow/leave.bpmn").deploy(); } /*** 將多個流程文件打包部署,一次可以部署多個流程*/ public void deployByZip() {InputStream is = this.getClass().getClassLoader().getResourceAsStream("diagrams/bpm.zip");ZipInputStream zip = new ZipInputStream(is);Deployment deployment = processEngine.getRepositoryService().createDeployment().addZipInputStream(zip).deploy(); }方便起見,通過一個Deploy按鈕來部署流程
Paste_Image.png
部署成功后,會分別在 act_ge_bytearray,act_re_deployment,act_re_procdef三張表插入相應數據,多次部署同一流程的話會增加版本號,以此獲取最新的流程
啟動流程
我們通過一個表單來用于請假申請
Paste_Image.png
我們定義一個 Leave 對象用于保存請假信息,相應的數據表為 leave 。Result 對象用于封裝一些返回信息。這里的 "leaveProcess" 就是在流程屬性中定義的Id。啟動成功后會返回一個 ProcessInstance 對象,這個對象主要是一些流程的基本信息,具體可以查看文檔或源碼。這里將返回的流程實例 id 存入到我們的 leave 表,以便以后可以通過這個 id 查詢相關的流程。
@ResponseBody @RequestMapping(value = "/save", method = RequestMethod.POST) public Result save(@RequestBody Leave user) {Result result = new Result();ProcessInstance pi = Workflow.startInstanceByKey("leaveProcess");user.setInstaceId(pi.getId());leaveService.insert(user);result.info(true, 0, "保存成功");return result; }Workflow.java代碼片段
public static ProcessInstance startInstanceByKey(String instanceByKey) {RuntimeService runtimeService = processEngine.getRuntimeService();ProcessInstance instance = runtimeService.startProcessInstanceByKey(instanceByKey);return instance; }流程啟動成功后會在 act_hi_actinst,act_hi_identitylink,act_hi_procinst,act_hi_taskinst,act_ru_execution,act_ru_identitylink,act_ru_task 表中插入相應數據。我們比較關心的就是 act_ru_task 表了,它存儲了任務的相關信息。
查看任務
流程啟動完畢后,應該就是進入了請假申請任務,我們可以通過請假申請的辦理人 apply 來查詢該任務(當然還有其他方法),這里可以指定不同查詢條件和過濾條件。返回 taskList 就是當前的任務列表了,Task是Activiti為我們定義好的接口對象,主要封裝了任務的信息。
這里由于 Activiti 的Task是接口對象無法轉換為json,所以自定義了一個Task對象,來轉換成json
Workflow.java代碼片段
public static List<Task> findTaskByAssignee(String assignee) {TaskService taskService = processEngine.getTaskService();List<Task> taskList = taskService.createTaskQuery().taskAssignee(assignee).list();return taskList; }Paste_Image.png
查看流程圖
我們可以通過流程定義ID(processDefinitionId)來獲取流程圖
@RequestMapping(value = "/shwoImg/{procDefId}") public void shwoImg(@PathVariable String procDefId,HttpServletResponse response){ try { InputStream pic = Workflow.findProcessPic(procDefId); byte[] b = new byte[1024]; int len = -1; while((len = pic.read(b, 0, 1024)) != -1) { response.getOutputStream().write(b, 0, len); } } catch (Exception e) { e.printStackTrace(); } }Workflow.java代碼片段
public static InputStream findProcessPic(String procDefId) throws Exception {RepositoryService repositoryService = processEngine.getRepositoryService();ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId).singleResult();String diagramResourceName = procDef.getDiagramResourceName();InputStream imageStream = repositoryService.getResourceAsStream(procDef.getDeploymentId(), diagramResourceName);return imageStream; }然后通過processDefinitionId,executionId 來獲取當前正在執行任務的位置坐標,以便用于標識流程圖上的位置。ActivityImpl 對象封裝了任務的位置信息,包括xy坐標,長和寬。自定義 Rect 對象用于轉換json
@ResponseBody @RequestMapping(value = "/showImg/{procDefId}/{executionId}") public Rect showImg(@PathVariable String procDefId,@PathVariable String executionId ) {Rect rect = new Rect();try {ActivityImpl img = Workflow.getProcessMap(procDefId,executionId );rect.setX(img.getX());rect.setY(img.getY());rect.setWidth(img.getWidth());rect.setHeight(img.getHeight());} catch (Exception e) {e.printStackTrace();}return rect; }Workflow.java代碼片段
public static ActivityImpl getProcessMap(String procDefId, String executionId) throws Exception {ActivityImpl actImpl = null;RepositoryService repositoryService = processEngine.getRepositoryService();//獲取流程定義實體ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(procDefId);RuntimeService runtimeService = processEngine.getRuntimeService();//獲取執行實體ExecutionEntity execution = (ExecutionEntity) runtimeService.createExecutionQuery().executionId(executionId).singleResult();// 獲取當前任務執行到哪個節點String activitiId = execution.getActivityId();// 獲得當前任務的所有節點List<ActivityImpl> activitiList = def.getActivities();for (ActivityImpl activityImpl : activitiList) {String id = activityImpl.getId();if (id.equals(activitiId)) {actImpl = activityImpl;break;}}return actImpl; }最終生成圖片時分別調用2個 showImg 方法即可,紅框可以根據返回的 Rect 來繪制,起始坐標根據自己的布局自行調整
LeavePicController.js片段
var pic.rect = {}; var pic.procDefId = $stateParams.procDefId; $http.get('workflow/showImg/'+$stateParams.procDefId +'/'+$stateParams.executionId) .success(function(data) {pic.rect.x = data.x;pic.rect.y = data.y;pic.rect.width = data.width;pic.rect.height = data.height;});picture.html
<div class="container-fluid" ng-controller="LeavePicController as pic"><img src="workflow/showImg/{{pic.procDefId}}"><div style="position:absolute; border:2px solid red;left:{{pic.rect.x + 20 }}px;top:{{pic.rect.y + 88 }}px;width:{{pic.rect.width }}px;height:{{pic.rect.height }}px;"></div> </div>Paste_Image.png
流程審批
通過 taskId 就可以對當前執行的任務進行審批,這里的 day 應該從 leave 表中查詢出來,方便起見就寫死了,這個day也就是在順序流的Condition中指定的變量,小于等于3就會流向項目經理(pm)審批任務了
@ResponseBody @RequestMapping(value = "/check/{taskId}") public Result check(@PathVariable String taskId) {Result result = new Result();Map<String, Object> map = new HashMap<>();map.put("day", 3);Workflow.completeTask(taskId,map);result.info(true, 0, "審批成功");return result; }Workflow.java代碼片段
public static void completeTask(String taskid,Map<String, Object> map map) {TaskService taskService = processEngine.getTaskService();taskService.complete(taskid, map); }審批成功后,就需要通過辦理人(pm)來查詢任務了,并且請假辦理(apply)中的任務被移除了
Paste_Image.png
Paste_Image.png
同時流程圖也流向了下一節點
Paste_Image.png
項目經理再執行一次審批方法,這個流程就算走完了
最后
以上就是一次相對簡單的流程:部署,啟動,查詢任務,顯示流程圖,審批。Activiti流程引擎還有很多核心操作,包括駁回、會簽、轉辦、中止、掛起等,等有空的時候再深入研究吧
最后的最后,初來乍到,不喜勿噴或輕噴 - -!Thanks...
作者:影秋
鏈接:https://www.jianshu.com/p/bdc9c9fa719d
來源:簡書
?
?
?
總結
以上是生活随笔為你收集整理的Activiti 工作流引擎的初步使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 工作流引擎Activiti使用总结
- 下一篇: Activiti总体框架分析