Activiti6簡明教程


一、為什么選擇Activiti

工作流引擎對比

二、核心7大接口、28張表

7大接口
(一)7大接口
  1. RepositoryService:提供一系列管理流程部署和流程定義的API。
  2. RuntimeService:在流程運行時對流程實例進行管理與控制。
  3. TaskService:對流程任務進行管理,例如任務提醒、任務完成和創建任務等。
  4. IdentityService:提供對流程角色數據進行管理的API,這些角色數據包括用戶組、用戶及它們之間的關系。
  5. ManagementService:提供對流程引擎進行管理和維護的服務。
  6. HistoryService:對流程的歷史數據進行操作,包括查詢、刪除這些歷史數據。
  7. FormService:表單服務。
(二)28張表
28張表

1、act_ge_ 通用數據表,ge是general的縮寫
2、act_hi_ 歷史數據表,hi是history的縮寫,對應HistoryService接口
3、act_id_ 身份數據表,id是identity的縮寫,對應IdentityService接口
4、act_re_ 流程存儲表,re是repository的縮寫,對應RepositoryService接口,存儲流程部署和流程定義等靜態數據
5、act_ru_ 運行時數據表,ru是runtime的縮寫,對應RuntimeService接口和TaskService接口,存儲流程實例和用戶任務等動態數據

三、創建BPMN業務流程模型

1.將Activiti提供的流程設計器應用activiti-app.war部署到Tomcat的webapps目錄。
2.創建新的MySql數據庫。修改activiti-app\WEB-INF\classes\META-INF\activiti-app目錄下的activiti-app.properties配置文件,默認使用H2內存數據庫,創建的模型重啟后會丟失,改成使用MySql數據庫。
3.瀏覽器訪問http://localhost:8080/activiti-app,登錄賬戶:admin:test
4.創建一個請假審批流程圖

請假審批流程圖

  • 給每個用戶任務指派候選組(有權限執行當前任務的角色)


    指派候選組

    指派候選組
  • 排他網關設置條件分支表達式


    設置條件分支

    設置條件分支

    5.導出流程圖為.bpmn20.xml文件


    導出xml文件

四、Spring Boot與Activiti 6.0整合

1.在POM文件中添加依賴

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter-basic</artifactId>
    <version>6.0.0</version>
</dependency>

2.將導出的.bpmn20.xml文件拷貝到項目文件夾/resources/processes下
3.application.properties文件添加配置項

spring.activiti.database-schema-update=true

databaseSchemaUpdate配置項可以設置流程引擎啟動和關閉時數據庫執行的策略。 databaseSchemaUpdate有以下四個值:

  • false:false為默認值,設置為該值后,Activiti在啟動時,會對比數據庫表中保存的版本,如果沒有表或者版本不匹配時,將在啟動時拋出異常。
  • true:設置為該值后,Activiti會對數據庫中所有的表進行更新,如果表不存在,則Activiti會自動創建。
  • create-drop:Activiti啟動時,會執行數據庫表的創建操作,在Activiti關閉時,執行數據庫表的刪除操作。
  • drop-create:Activiti啟動時,執行數據庫表的刪除操作在Activiti關閉時,會執行數據庫表的創建操作。

4.啟動應用,會在數據庫里創建28張表,表創建好之后停止應用。application.properties文件修改配置項

#每次應用啟動不檢查Activiti數據表是否存在及版本號是否匹配,提升應用啟動速度
spring.activiti.database-schema-update=false

5.application.properties文件增加配置項

#保存歷史數據級別設置為full最高級別,便於歷史數據的追溯
spring.activiti.history-level=full

對於歷史數據,保存到何種粒度,Activiti提供了history-level屬性對其進行配置。history-level屬性有點像log4j的日志輸出級別,該屬性有以下四個值:

  • none:不保存任何的歷史數據,因此,在流程執行過程中,這是最高效的。
  • activity:級別高於none,保存流程實例與流程行為,其他數據不保存。
  • audit:除activity級別會保存的數據外,還會保存全部的流程任務及其屬性。audit為history的默認值。
  • full:保存歷史數據的最高級別,除了會保存audit級別的數據外,還會保存其他全部流程相關的細節數據,包括一些流程參數等。
    6.完成以上步驟,就可以在程序中使用自動注入的方式,使用Activiti的7大接口。
@Autowired
private RuntimeService runtimeService;

@Autowired
private TaskService taskService;

@Autowired
private IdentityService identityService;

@Autowired
private RepositoryService repositoryService;

@Autowired
private ProcessEngine processEngine;

@Autowired
private HistoryService historyService;

五、項目中的用戶、角色與Activiti中的用戶、用戶組整合

每個項目都有自己的用戶、角色表,Activiti也有自己的用戶、用戶組表。因此項目中的用戶、角色與Activiti中的用戶、用戶組要做整合。

//項目中每創建一個新用戶,對應的要創建一個Activiti用戶
//兩者的userId和userName一致
User admin=identityService.newUser("1");
admin.setLastName("admin");
identityService.saveUser(admin);

//項目中每創建一個角色,對應的要創建一個Activiti用戶組
Group adminGroup=identityService.newGroup("1");
adminGroup.setName("admin");
identityService.saveGroup(adminGroup);

//用戶與用戶組關系綁定
identityService.createMembership("1","1");

六、請假審批流程

1.請假申請和請假審批數據庫表設計
表設計原則:流程數據和業務數據相分離。Activiti相關表只負責流程的跳轉、走向等。流程中產生的業務表單數據、審批意見、附件等存儲在開發人員定義的業務表中。流程數據和業務數據之間通過processInstanceId(流程實例ID)和業務數據主鍵相互關聯。

為什么不使用Activiti相關表來存儲表單數據和附件?


activiti參數表

Activiti為了應用的靈活性和通用性采用了縱表的方式存儲表單數據。假設一條請假申請表單數據有10個字段,那就需要10條記錄存儲原本橫表只需要一條記錄存儲的數據。采用縱表的方式會有如下問題:

  • 會有大量的冗余數據並且數據量會急劇的增長
  • 查詢語句復雜,查詢效率低
  • 尤其不適合做后期的統計報表分析
activiti附件表

Activiti存儲附件使用Blob數據格式,文件存儲在數據庫里,數據庫的數據文件會變得超大,不利於數據庫備份和遷移。
請假申請表結構


請假申請表

請假審批表結構


請假審批表

2.填寫請假申請表單,啟動流程實例
填寫請假申請
//啟動流程實例,字符串"vacation"是BPMN模型文件里process元素的id
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("vacation");
//流程實例啟動后,流程會跳轉到請假申請節點
Task vacationApply = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
//設置請假申請任務的執行人
taskService.setAssignee(vacationApply.getId(), req.getUserId().toString());

//設置流程參數:請假天數和表單ID
//流程引擎會根據請假天數days>3判斷流程走向
//formId是用來將流程數據和表單數據關聯起來
Map<String, Object> args = new HashMap<>();
args.put("days", req.getDays());
args.put("formId", formId);

//完成請假申請任務
taskService.complete(vacationApply.getId(), args);

3.待審批列表


待審批列表
//查出當前登錄用戶所在的用戶組
List<Group> groups = identityService.createGroupQuery()
        .groupMember(String.valueOf(userId)).list();
List<String> groupNames = groups.stream()
        .map(group -> group.getName()).collect(Collectors.toList());

//查詢用戶組的待審批請假流程列表
List<Task> tasks = taskService.createTaskQuery()
.processDefinitionKey("vacation")
.taskCandidateGroupIn(groupNames)
.listPage(pageNum - 1, pageSize);

//根據流程實例ID查詢請假申請表單數據
List<String> processInstanceIds = tasks.stream()
.map(task -> task.getProcessInstanceId())
.collect(Collectors.toList());
List<VacationApplyBasicPO> vacationApplyList =
vacationRepository.getVacationApplyList(processInstanceIds);

4.請假審批功能


請假審批
//查詢當前審批節點
Task vacationAudit = taskService.createTaskQuery()
        .taskId(req.getTaskId()).singleResult();

if (req.getAuditResult() == 1) {//審批通過
//設置流程參數:審批ID
Map<String, Object> args = new HashMap<>();
args.put("auditId", auditId);

<span class="hljs-comment"><span class="hljs-comment">//設置審批任務的執行人</span></span>
taskService.claim(vacationAudit.getId(), req.getUserId().toString());
<span class="hljs-comment"><span class="hljs-comment">//完成審批任務</span></span>
taskService.complete(vacationAudit.getId(), args);

} else {
//審批不通過,結束流程
runtimeService.deleteProcessInstance(vacationAudit.getProcessInstanceId(), auditId);
}

5.查看流程圖功能


查看流程圖
//controller層代碼
@RequestMapping(value = "/image", method = RequestMethod.GET)
public void image(HttpServletResponse response, @RequestParam String processInstanceId) {
    try {
        InputStream is = vacationService.getDiagram(processInstanceId);
        if (is == null)
            return;
    response.setContentType(<span class="hljs-string"><span class="hljs-string">"image/png"</span></span>);

    BufferedImage image = ImageIO.read(is);
    OutputStream out = response.getOutputStream();
    ImageIO.write(image, <span class="hljs-string"><span class="hljs-string">"png"</span></span>, out);

    is.close();
    out.close();
} <span class="hljs-keyword"><span class="hljs-keyword">catch</span></span> (Exception ex) {
    logger.error(<span class="hljs-string"><span class="hljs-string">"查看流程圖失敗"</span></span>, ex);
}

}

//service層代碼
@Override
public InputStream getDiagram(String processInstanceId) {
//獲得流程實例
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId).singleResult();
String processDefinitionId = StringUtils.EMPTY;
if (processInstance == null) {
//查詢已經結束的流程實例
HistoricProcessInstance processInstanceHistory =
historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstanceId).singleResult();
if (processInstanceHistory == null)
return null;
else
processDefinitionId = processInstanceHistory.getProcessDefinitionId();
} else {
processDefinitionId = processInstance.getProcessDefinitionId();
}

<span class="hljs-comment"><span class="hljs-comment">//使用宋體</span></span>
String fontName = <span class="hljs-string"><span class="hljs-string">"宋體"</span></span>;
<span class="hljs-comment"><span class="hljs-comment">//獲取BPMN模型對象</span></span>
BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
<span class="hljs-comment"><span class="hljs-comment">//獲取流程實例當前的節點,需要高亮顯示</span></span>
List&lt;String&gt; currentActs = Collections.EMPTY_LIST;
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (processInstance != <span class="hljs-keyword"><span class="hljs-keyword">null</span></span>)
    currentActs = runtimeService.getActiveActivityIds(processInstance.getId());

<span class="hljs-keyword"><span class="hljs-keyword">return</span></span> processEngine.getProcessEngineConfiguration()
        .getProcessDiagramGenerator()
        .generateDiagram(model, <span class="hljs-string"><span class="hljs-string">"png"</span></span>, currentActs, <span class="hljs-keyword"><span class="hljs-keyword">new</span></span> ArrayList&lt;String&gt;(),
                fontName, fontName, fontName, <span class="hljs-keyword"><span class="hljs-keyword">null</span></span>, <span class="hljs-number"><span class="hljs-number">1.0</span></span>);

}

最后,spring boot應用打成jar包部署的時候,需要注意:\color{red}{部署文件目錄一定不要出現中文}

原文地址:https://www.jianshu.com/p/701056e672a4


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM