看了網上一些文章,動手操作了一遍,終於學會了Activit的一些常規使用。
一、Eclipse中的Activiti插件安裝
Activiti有一個Eclipse插件,Activiti Eclipse Designer,可用於圖形化建模、測試、部署 BPMN 2.0的流程。這樣就不
用我們自己去編寫繁瑣的流程文件了。具體安裝方法見手冊。
打開 Help-> Install New Software.在如下面板中 , 點擊 Add 按鈕, 然后填入下列字段:
Name: Activiti BPMN 2.0 designer
Location: http://activiti.org/designer/update/
然后一步步的安裝就可以了。
點擊Window--->Preferences--->Activiti--->Save Actions:將Create process definition image when saving the diagram勾選,然后保存bpmn文件的時候會自動截圖。
二、實現一個請假的工作流
1、隨便模擬一個請假流程:
(1)發起請假申請;
(2)經理審批:
a.如果指定時間內沒審批,則自動通過;
b.審批同意;
c.審批不同意;
(3)總經理審批:
a.如果請假大於或等於3天,則總經理審批;
b.如果請假小於3天,則自動通過;
(4)流程結束。
2、新建一個Activiti Diagram文件,按照第1步的思路,拖拉控件,畫流程圖,並設置相關節點或連線的值




3、配置文件activiti.cfg.xml的參數,主要配置數據庫的相關參數等
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test" />
<property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="true" />
<property name="activityFontName" value="宋體"/>
<property name="labelFontName" value="宋體"/>
</bean>
</beans>
4、代碼實例,實現啟動流程、審批、畫歷史流程圖等等,詳細見代碼中注釋
package com.sc.springmvc.controller;
import java.io.File;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.zip.ZipInputStream;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.io.FileUtils;
public class ActivitiDemo {
public static void main(String[] args) {
ActivitiDemo demo = new ActivitiDemo();
//demo.deploy();
//demo.queryProcdef();
//demo.startFlow();
//demo.queryTask_manager();
//demo.startTask_manager();
//demo.queryTask_boss();
//demo.startTask_boss();
//demo.queryStatus();
//demo.historyTaskList();
//demo.processState();
//獲取流程變量
//System.out.println("生成流程圖:");
demo.generateImage("12501");
}
String currUser = "0001";
String applyUser = "0002";
String manager = "0003";
String boss = "0005";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
/**
* 發布流程
* 發布流程后,流程文件會保存到數據庫中 ,插入表act_re_deployment、act_re_procdef
*/
void deploy(){
RepositoryService repositoryService = processEngine.getRepositoryService();
//手動將myleave.bpmn和myleave.png打包成myleave.zip文件(一定要是zip別壓縮成rar)
//獲取在classpath下的流程文件
InputStream in = this.getClass().getClassLoader().getResourceAsStream("myleave.zip");
ZipInputStream zipInputStream = new ZipInputStream(in);
//使用deploy方法發布流程:如果是首次表不存在則生成23張表,往這4張表插入流程相關信息:act_ge_bytearray、 act_ge_property、act_re_deployment、act_re_procdef
repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.name("myleave")
.deploy();
}
//獲取詳細的流程定義信息
void queryProcdef(){
RepositoryService repositoryService = processEngine.getRepositoryService();
//創建查詢對象
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
//添加查詢條件
query.processDefinitionKey("leaveApply");//通過key獲取
// .processDefinitionName("請假申請")//通過name獲取
// .orderByProcessDefinitionId()//根據ID排序
//執行查詢獲取流程定義明細
List<ProcessDefinition> pds = query.list();
for (ProcessDefinition pd : pds) {
System.out.println("ID:"+pd.getId()+",NAME:"+pd.getName()+",KEY:"+pd.getKey()+",VERSION:"+pd.getVersion()+",RESOURCE_NAME:"+pd.getResourceName()+",DGRM_RESOURCE_NAME:"+pd.getDiagramResourceName());
}
}
//發布流程
public void startFlow(){
RuntimeService runtimeService = processEngine.getRuntimeService();
//IdentityService identityService = processEngine.getIdentityService();
// 用來設置啟動流程的人員ID,引擎會自動把用戶ID保存到activiti:initiator中
//identityService.setAuthenticatedUserId(currUser);
/**
* 啟動請假單流程 並獲取流程實例
* 因為該請假單流程可以會啟動多個所以每啟動一個請假單流程都會在數據庫中插入一條新版本的流程數據
* 通過key啟動的流程就是當前key下最新版本的流程
* 當流程發布后在 act_ru_task ,act_ru_execution, act_ru_identitylink 表中插入流程數據
*
*/
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("applyUser", applyUser);
variables.put("manager", manager);
variables.put("days", "5");
Date date = addTime(new Date(),0,0,1);
variables.put("dateTime", date);//到期時間
variables.put("boss", boss);
String businessKey = "10001";//保存於表act_hi_procinst中
//參數businessKey, variables為可選參數
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveApply", businessKey, variables);
System.out.println("id:"+processInstance.getId()+",activitiId:"+processInstance.getActivityId());
}
//增加時間
public static Date addTime(Date date, int day, int hour, int minute){
Calendar ca=Calendar.getInstance();
ca.setTime(date);
ca.add(Calendar.DATE, day);
ca.add(Calendar.HOUR_OF_DAY, hour);
ca.add(Calendar.MINUTE, minute);
return ca.getTime();
}
/**
* 傳入Data類型日期,返回字符串類型時間(ISO8601標准時間)
* @param date
* @return
*/
public static String getISO8601Timestamp(Date date){
TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
df.setTimeZone(tz);
String nowAsISO = df.format(date);
return nowAsISO;
}
/**
* 查看任務
*/
void queryTask_manager(){
//獲取任務服務對象
TaskService taskService = processEngine.getTaskService();
//根據接受人獲取該用戶的任務 ,表act_ru_task的字段assignee_為待辦人
List<org.activiti.engine.task.Task> tasks = taskService.createTaskQuery().processDefinitionKey("leaveApply").taskAssignee(manager).list();
for (org.activiti.engine.task.Task task : tasks) {
//ID : 表act_hi_actinst的字段act_id_
System.out.println("ID:"+task.getId()+",姓名:"+task.getName()+",接收人:"+task.getAssignee()+",開始時間:"+task.getCreateTime());
//獲取流程變量
System.out.println("Variables:");
RuntimeService runtimeService=processEngine.getRuntimeService();
String excutionId = task.getExecutionId();
String applyUser = String.valueOf(runtimeService.getVariable(excutionId, "applyUser"));
String days = String.valueOf(runtimeService.getVariable(excutionId, "days"));
String dateTime = String.valueOf(runtimeService.getVariable(excutionId, "dateTime"));
System.out.println("applyUser:" + applyUser + ",days:" + days + ",datetime:" + dateTime);
}
}
/**
* 啟動流程:經理審批
*/
void startTask_manager(){
TaskService taskService = processEngine.getTaskService();
List<org.activiti.engine.task.Task> tasks = taskService.createTaskQuery().taskAssignee(manager).list();
for (org.activiti.engine.task.Task task : tasks) {
//taskId 就是查詢任務中的 ID
String taskId = task.getId();
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("managerPass", "1");
variables.put("boss", boss);
taskService.complete(taskId, variables);
}
}
/**
* 老板查看任務
*/
void queryTask_boss(){
//獲取任務服務對象
TaskService taskService = processEngine.getTaskService();
//根據接受人獲取該用戶的任務
List<org.activiti.engine.task.Task> tasks = taskService.createTaskQuery().taskAssignee(boss).list();
for (org.activiti.engine.task.Task task : tasks) {
System.out.println("ID:"+task.getId()+",姓名:"+task.getName()+",接收人:"+task.getAssignee()+",開始時間:"+task.getCreateTime());
//獲取流程變量
System.out.println("Variables:");
RuntimeService runtimeService=processEngine.getRuntimeService();
String excutionId = task.getExecutionId();
String applyUser = String.valueOf(runtimeService.getVariable(excutionId, "applyUser"));
String manager = String.valueOf(runtimeService.getVariable(excutionId, "manager"));
String managerPass = String.valueOf(runtimeService.getVariable(excutionId, "managerPass"));
String days = String.valueOf(runtimeService.getVariable(excutionId, "days"));
System.out.println("applyUser:" + applyUser + ",manager:" + manager + ",days:" + days + ",managerPass:" + managerPass);
}
}
/**
* 老板啟動流程
*/
void startTask_boss(){
TaskService taskService = processEngine.getTaskService();
//根據接受人獲取該用戶的任務
List<org.activiti.engine.task.Task> tasks = taskService.createTaskQuery().taskAssignee(boss).list();
for (org.activiti.engine.task.Task task : tasks) {
//taskId 就是查詢任務中的 ID
String taskId = task.getId();
//完成請假申請任務
taskService.complete(taskId );
}
}
void queryStatus(){
HistoryService historyService = processEngine.getHistoryService();
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId("20001").singleResult();
System.out.println("Process instance end time: " + getDate(historicProcessInstance.getEndTime()));
}
/**
* 歷史任務查詢:歷史活動包括所有節點(流程圖圓圈)和任務(流程圖矩形),而歷史任務只包含任務
*/
public void historyTaskList(){
List<HistoricTaskInstance> list=processEngine.getHistoryService() // 歷史相關Service
.createHistoricTaskInstanceQuery() // 創建歷史任務實例查詢
.processInstanceId("37501") // 用流程實例id查詢
.finished() // 查詢已經完成的任務
.list();
for(HistoricTaskInstance hti:list){
System.out.println("任務ID:"+hti.getId());
System.out.println("流程實例ID:"+hti.getProcessInstanceId());
System.out.println("任務名稱:"+hti.getName());
System.out.println("辦理人:"+hti.getAssignee());
System.out.println("開始時間:"+hti.getStartTime());
System.out.println("結束時間:"+hti.getEndTime());
System.out.println("=================================");
}
}
/**
* 查詢流程狀態(正在執行 or 已經執行結束)
*/
void processState(){
ProcessInstance pi=processEngine.getRuntimeService() // 獲取運行時Service
.createProcessInstanceQuery() // 創建流程實例查詢
.processInstanceId("37501") // 用流程實例id查詢
.singleResult();
if(pi!=null){
System.out.println("流程正在執行!");
}else{
System.out.println("流程已經執行結束!");
}
}
String getDate(Date d){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s = sdf.format(d);
return s;
}
/**https://blog.csdn.net/u011277123/article/details/77380787
* 流程圖對歷史節點進行高亮顯示
* @param processInstanceId
* @return
*/
void generateImage(String processInstanceId)
{
HistoryService historyService = processEngine.getHistoryService();
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessEngineConfiguration processEngineConfiguration = processEngine.getProcessEngineConfiguration();
//獲取歷史流程實例
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
//獲取流程圖
BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
processEngineConfiguration = processEngine.getProcessEngineConfiguration();
Context.setProcessEngineConfiguration((ProcessEngineConfigurationImpl) processEngineConfiguration);
ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId());
List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();
//高亮環節id集合
List<String> highLightedActivitis = new ArrayList<String>();
//高亮線路id集合
List<String> highLightedFlows = getHighLightedFlows(definitionEntity,highLightedActivitList);
// 已執行完的任務節點
List<HistoricActivityInstance> finishedInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished().list();
for (HistoricActivityInstance hai : finishedInstances) {
highLightedActivitis.add(hai.getActivityId());
}
//中文顯示的是口口口,設置字體就好了
InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis,highLightedFlows,"宋體","宋體",null,1.0);
//單獨返回流程圖,不高亮顯示
// InputStream imageStream = diagramGenerator.generatePngDiagram(bpmnModel);
// 輸出資源內容到相應對象
try {
//生成本地圖片
File file = new File("D:/test3.png");
FileUtils.copyInputStreamToFile(imageStream, file);
} catch (Exception e) {
throw new RuntimeException("生成流程圖異常!", e);
} finally {
}
}
/**
* 獲取需要高亮的線
* @param processDefinitionEntity
* @param historicActivityInstances
* @return
*/
private List<String> getHighLightedFlows(ProcessDefinitionEntity processDefinitionEntity, List<HistoricActivityInstance> historicActivityInstances) {
List<String> highFlows = new ArrayList<String>();// 用以保存高亮的線flowId
for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
// 對歷史流程節點進行遍歷
ActivityImpl activityImpl = processDefinitionEntity.findActivity(historicActivityInstances.get(i).getActivityId());// 得到節點定義的詳細信息
List<ActivityImpl> sameStartTimeNodes = new ArrayList<ActivityImpl>();// 用以保存后需開始時間相同的節點
ActivityImpl sameActivityImpl1 = processDefinitionEntity.findActivity(historicActivityInstances.get(i + 1).getActivityId());
// 將后面第一個節點放在時間相同節點的集合里
sameStartTimeNodes.add(sameActivityImpl1);
for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);// 后續第一個節點
HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);// 后續第二個節點
if (activityImpl1.getStartTime().equals(activityImpl2.getStartTime())) {
// 如果第一個節點和第二個節點開始時間相同保存
ActivityImpl sameActivityImpl2 = processDefinitionEntity.findActivity(activityImpl2.getActivityId());
sameStartTimeNodes.add(sameActivityImpl2);
} else {
// 有不相同跳出循環
break;
}
}
List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions();// 取出節點的所有出去的線
for (PvmTransition pvmTransition : pvmTransitions) {
// 對所有的線進行遍歷
ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition.getDestination();
// 如果取出的線的目標節點存在時間相同的節點里,保存該線的id,進行高亮顯示
if (sameStartTimeNodes.contains(pvmActivityImpl)) {
highFlows.add(pvmTransition.getId());
}
}
}
return highFlows;
}
}
附,看過的一些不錯Activiti文章:
http://www.mossle.com/docs/activiti/index.html
https://blog.csdn.net/a67474506/article/details/38266129
http://www.cnblogs.com/shyroke/category/1126426.html
----------------------------------------------
2019.3.20補充
實際業務中,activiti工作流的開發過程:
1、畫流程圖
2、數據庫業務表增加一個流程實例字段proc_inst_id_
3、發起流程申請,后台保存邏輯
(1)設置流程下一步審批節點的參數Map<String, Object> variables
(2)插入或更新業務相關表,返回業務表的主鍵businessKey
(3)發起流程
identityService.setAuthenticatedUserId(variables.get("applyUser").toString());
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("流程名稱", businessKey, variables);
String processInstanceId = processInstance.getId();
(4)根據上一步返回的流程實例Id,更新業務表相應字段
4、sql獲取待辦任務
select
to_char(t.id_) taskId,
w.proc_inst_id_,
t.task_def_key_ taskDefinitionKey,
......
from act_ru_task t
inner join 業務表 w on w.proc_inst_id_ = t.proc_inst_id_
where t.assignee_ = 待辦人
5、待辦審批前端頁面,根據當前是哪個流程節點寫不同邏輯
<c:if test="${taskDefinitionKey eq 'xxTask'}">
經理審批......
</c:if>
<c:if test="${taskDefinitionKey eq 'yyTask'}">
老板審批......
</c:if>
6、待辦審批的后台保存邏輯
(1)判斷權限:任務是否屬於當前人;
(2)根據流程節點值taskDefinitionKey,設置流程下一步審批節點的參數Map<String, Object> variables
(3)更新相關業務表
(4)調用工作流方法
String taskId = (String) variables.get("taskId");
identityService.setAuthenticatedUserId(當前用戶主鍵);
taskService.addComment(taskId, 流程實例Id, 審批意見);
this.taskService.complete(taskId, variables);
//TODO判斷任務是否結束,是的話則根據實際情況更新業務表
