工作流引擎Activiti系列(一)——初識
1、介紹
幾乎任何一個公司的軟體開發都會涉及到流程,以往我們可能是這麼實現的:業務表新增標誌位標識流程的節點狀態,關聯批註表實現稽核意見,根據一些業務資料分析處理邏輯,分配任務到使用者,節點的排程,審批等.....這其實是很繁瑣的,且不說開發起來比較混亂,維護起來更是難上加難:
Activiti剛好就能解決幾乎所有的這些問題,當流程開發變得簡單有趣。
Activiti專案是一項新的基於Apache許可的開源BPM平臺,從基礎開始構建,旨在提供支援新的BPMN 2.0標準,包括支援物件管理組(OMG),面對新技術的機遇,諸如互操作性和雲架構,提供技術實現。
作為開發者,使用Activiti帶給我的直接好處:
- 天然支援Spring(Spring需要在配置檔案中自己定義Activiti的Bean,Spring Boot則不需要)
- 流程定義通過畫流程圖實現(官方提供了相關工具,Eclipse也有外掛支援),簡單直觀,易維護,易修改。
- 審批條件引數化,流程分支簡單實現。
- 審批人可使用多種方式設定(支援使用者、使用者組、角色、候選組以及監聽器動態設定),靈活,簡單。
- 統一的審批介面,並不需要判斷流程當前節點和走向。
- 提供強大的JPA查詢,同時支援Name Query和Native Query。
- 流程資料與業務資料分離。
後續文章會一步步介紹Activiti的功能,主要使用基於Spring Boot的工程,也會提供單純的Spring工程Demo。
2、示例
此處演示一個小示例,暫不解釋程式碼,僅僅看看是怎樣用activiti實現的。
場景就是請假,不過這裡稍稍多了點內容,就是請假由請假人對應部門的領導審批,而不是統一的某一部分人。
2.1、使用Spring Boot工程
首先建立Spring boot工程,為了演示方便,使用記憶體資料庫,完整pom檔案如下:
2.2、流程定義
這裡使用eclipse的activiti designer外掛,外掛安裝方法另見:Eclipse安裝Activiti Designer外掛
如我所願,這裡以請假流程,使用流程設計器得到如下流程定義:
這個流程相當簡單。下面是定義的xml檔案:
裡面包含一個使用者任務、一個流程監聽器(流程結束後回寫業務資料狀態)、一個任務監聽器(以為審批是由申請員工對應部門的領導稽核的,使用監聽器可以靈活的設定任務審批候選人)。
2.3、監聽器
監聽器分任務監聽器和流程監聽器。
任務監聽器
/**
* 領導稽核監聽器
* @author anxpp.com
* 2016年12月24日 下午12:10:01
*/
public class LeaderCheckListener implements TaskListener{
private static final long serialVersionUID = 4285398130708457006L;
private final static Logger log = LoggerFactory.getLogger(LeaderCheckListener.class);
@Override
public void notify(DelegateTask task) {
log.info("領導稽核監聽器...");
//設定任務處理候選人
UserService userService = SpringUtil.getBean(UserService.class);
List<String> leaders = userService.getSimpleCheckerByDept(Long.valueOf(task.getVariable("dept").toString()));
log.info(leaders.toString());
log.info(task.getVariable("dept").toString());
task.addCandidateUsers(leaders);
}
}
流程監聽器(此處並無實際程式碼,只給寫法):
/**
* 流程監聽器
* @author anxpp.com
* 2016年12月24日 下午12:33:58
*/
public class SimpleProcessEndListener implements ExecutionListener{
private static final long serialVersionUID = 5212042435691138021L;
private final static Logger log = LoggerFactory.getLogger(SimpleProcessEndListener.class);
@Override
public void notify(DelegateExecution arg0) throws Exception {
log.info("流程結束監聽器...");
//TODO 修改業務資料狀態
}
}
單元測試
package com.anxpp.demo.activiti;
import java.util.Iterator;
import java.util.List;
import org.activiti.engine.task.Task;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.anxpp.demo.activiti.core.entity.User;
import com.anxpp.demo.activiti.core.service.UserService;
import com.anxpp.demo.activiti.simple.Config.Constant;
import com.anxpp.demo.activiti.simple.core.entity.ApplySimple;
import com.anxpp.demo.activiti.simple.core.service.ApplySimpleService;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ActivitiDemoApplicationTests {
private final static Logger log = LoggerFactory.getLogger(ActivitiDemoApplicationTests.class);
private static Long DEPT_TINY_SOFTWARE_STUDIO = 1L;
private static Long DEPT_OTHER = 2L;
@Autowired
UserService userService;
@Autowired
ApplySimpleService simpleService;
/**
* 測試程式啟動
*/
@Test
public void contextLoads() {
}
/**
* 測試資料庫操作
*/
@Test
public void testCURD(){
Long countUser = userService.countUser();
User user = new User();
user.setName("anxpp0");
user.setDept(DEPT_TINY_SOFTWARE_STUDIO);
userService.save(user);
Assert.assertEquals(new Long(countUser+1), userService.countUser());
}
/**
* 測試簡單流程
*/
@Test
public void testSimpleActiviti(){
long startAt = System.currentTimeMillis();
//新增申請使用者
User user = new User();
user.setName("anxpp");
user.setDept(DEPT_TINY_SOFTWARE_STUDIO);
user.setPosition(Constant.POSITION_GENERAL);
userService.save(user);
//新增稽核使用者
User userLeader1 = new User();
userLeader1.setName("anxpp1");
userLeader1.setDept(DEPT_TINY_SOFTWARE_STUDIO);
userLeader1.setPosition(Constant.POSITION_LEADER);
userService.save(userLeader1);
User userLeader2 = new User();
userLeader2.setName("anxpp2");
userLeader2.setDept(DEPT_OTHER);
userLeader2.setPosition(Constant.POSITION_LEADER);
userService.save(userLeader2);
Long countHis = simpleService.countProcess();
Long countLeader1Task = simpleService.countTask(userLeader1.getId());
Long countLeader2Task = simpleService.countTask(userLeader2.getId());
//建立請假申請
ApplySimple applySimple = new ApplySimple();
applySimple.setInsertBy(user.getId());
applySimple.setComtent("有事請假...");
//啟動請假流程
simpleService.startProcess(applySimple);
/**斷言歷史流程+1*/
Assert.assertEquals(simpleService.countProcess(), new Long(countHis+1));
/**斷言領導任務變化*/
Assert.assertEquals(simpleService.countTask(userLeader1.getId()), new Long(countLeader1Task+1));
Assert.assertEquals(countLeader2Task, simpleService.countTask(userLeader2.getId()));
//獲取使用者任務
List<Task> taskUserLeader1 = simpleService.getTaskByUid(userLeader1.getId());
//處理任務
Iterator<Task> iterator = taskUserLeader1.iterator();
while(iterator.hasNext()){
Task task = iterator.next();
/**斷言任務節點名稱*/
Assert.assertEquals("領導審批", task.getName());
simpleService.completeSimpleCheck(task.getId(), ApplySimpleService.STATE_PASS);
}
/**斷言領導任務變化*/
Assert.assertEquals(countLeader1Task, simpleService.countTask(userLeader1.getId()));
Assert.assertEquals(countLeader2Task, simpleService.countTask(userLeader2.getId()));
System.err.println("asdf");
log.info("測試完成,花費時間:"+(System.currentTimeMillis()-startAt));
}
}
執行單元測試,OK!通過!