Activiti就是這麼簡單

Activiti介紹

什麼是Activiti?

Activiti5是由Alfresco軟件在2010年5月17日發佈的業務流程管理(BPM)框架,它是覆蓋了業務流程管理、工做流、服務協做等領域的一個開源的、靈活的、易擴展的可執行流程語言框架。Activiti基於Apache許可的開源BPM平臺,創始人Tom Baeyens是JBoss jBPM的項目架構師,它特點是提供了eclipse插件,開發人員能夠經過插件直接繪畫出業務 流程圖。.html

咱們即將學習的是一個業務流程管理框架, 常見開源工做流引擎框架 : OSWorkFlow、jBPM(jboss business process management),Activiti工做流(是對jBPM升級)。通常咱們稱做爲工做流框架..java

爲何要學習Activiti

那咱們爲何要學習業務流程管理框架呢???學習它幹嗎???工做流(Workflow),就是「業務過程的部分或總體在計算機應用環境下的自動化」mysql

咱們來提出一個常見的需求來更好地理解:web

咱們在學生時代,確定會遇到請假寫請假條的狀況,若是學校的請假抓得很是嚴,就須要通過多層的贊成才能肯定能不能請假..spring

這裏寫圖片描述

班主任->任課老師->教學總監->校長這麼一個流程,首先咱們先明確一點:咱們做爲一個學生,不可能直接跳過老師,去找校長申請請假的【校長是你隨便找的嗎??】sql

所以咱們請假的流程是一步一步的,並不能作跳躍數據庫

也就是說,當班主任沒有批准請假的時候,即便你去找任課老師了,任課老師會讓你回去找班主任的,做爲任課老師了,只關注班主任有沒有批准你請假,同理,做爲校長,只關注教學總監有沒有批准你請假express

進一步說:當教學總監尚未批准你請假時,你請假的請求是不會出如今校長的範圍裏的json

實際上是很是好理解的,就是一步步往下執行,當尚未執行到本身處理的點子上時,是不會有對應的處理請求的。分工有序api

對上面的請假流程進行分析**,若是咱們沒有使用框架,而把這麼一個請假流程寫到咱們的網站中,咱們會怎麼作呢**??

這裏寫圖片描述

咱們須要維護一個變量,來不斷傳遞過去給下一個處理者...若是一切正常,需求不會變,並無條件的處理。這是咱們很是但願看到的...可是,若是有條件判斷【請假三天如下、請假三天以上的處理方式不同】,需求會變【不須要校長批准了,教學總監批准完,你就可以請假了】,那麼咱們的代碼就會變得亂

基於這麼一個緣由,咱們是須要學習一個框架來幫咱們完成工做流的...

採用工做流管理系統的優勢

  • 一、提升系統的柔性,適應業務流程的變化
  • 二、實現更好的業務過程控制,提升顧客服務質量
  • 三、下降系統開發和維護成本

1、快速入門Activiti

首先咱們來梳理一下Activiti的開發步驟:

  1. 咱們要用到一個工做流,首先就要把這個工做流定義出來【也就是工做流的步驟的怎麼樣的】,Activiti支持以「圖」的方式來定義工做流
  2. 定義完工做流,就要部署到起來【咱們能夠聯想到Tomcat,咱們光下載了Tomcat是沒有用的,要把它部署起來】
  3. 隨後咱們就執行該工做流,該工做流就隨着咱們定義的步驟來一一執行!

1.1BPMN

業務流程建模與標註(Business Process Model and Notation,BPMN) ,描述流程的基本符號,包括這些圖元如何組合成一個業務流程圖(Business Process Diagram)

BPMN這個就是咱們所謂把工做流定義出來的流程圖..

1.2數據庫相關

咱們在執行工做流步驟的時候會涉及到不少數據【執行該流程的人是誰、所須要的參數是什麼、包括想查看以前流程執行的記錄等等】,所以咱們會須要用到數據庫的表來保存數據...

因爲咱們使用的是Activiti框架,這個框架會自動幫咱們把對應的數據庫表建立起來,它涉及的表有23個,可是經常使用的並非不少,所以也不用很慌...

下面就列舉一下表的狀況

Activiti的後臺是有數據庫的支持,全部的表都以ACT_開頭。 第二部分是表示表的用途的兩個字母標識。 用途也和服務的API對應。
ACT_RE_*: 'RE'表示repository。 這個前綴的表包含了流程定義和流程靜態資源 (圖片,規則,等等)。
ACT_RU_*: 'RU'表示runtime。 這些運行時的表,包含流程實例,任務,變量,異步任務,等運行中的數據。 Activiti只在流程實例執行過程當中保存這些數據, 在流程結束時就會刪除這些記錄。 這樣運行時表能夠一直很小速度很快。
ACT_ID_*: 'ID'表示identity。 這些表包含身份信息,好比用戶,組等等。
ACT_HI_*: 'HI'表示history。 這些表包含歷史數據,好比歷史流程實例, 變量,任務等等。
ACT_GE_*: 通用數據, 用於不一樣場景下,如存放資源文件。
複製代碼

1.3搭建配置環境

我這裏使用的Intellij idea來使用Activiti,首先,咱們得下載插件來使用Activiti【由於定義流程圖須要用到插件】

詳情能夠看這篇博文:blog.sina.com.cn/s/blog_4b31…

Activiti插件中文亂碼問題:

www.cnblogs.com/mymelody/p/…

流程以前的連線是經過圖中的藍色小點點拖動來進行鏈接的...

導入對應的jar包

  • activation-1.1.jar
  • activiti-bpmn-converter-5.13.jar
  • activiti-bpmn-layout-5.13.jar
  • activiti-bpmn-model-5.13.jar
  • activiti-common-rest-5.13.jar
  • activiti-engine-5.13.jar
  • activiti-json-converter-5.13.jar
  • activiti-rest-5.13.jar
  • activiti-simple-workflow-5.13.jar
  • activiti-spring-5.13.jar
  • aopalliance-1.0.jar
  • commons-dbcp-1.4.jar
  • commons-email-1.2.jar
  • commons-fileupload-1.2.2.jar
  • commons-io-2.0.1.jar
  • commons-lang-2.4.jar
  • commons-pool-1.5.4.jar
  • h2-1.3.170.jar
  • hamcrest-core-1.3.jar
  • jackson-core-asl-1.9.9.jar
  • jackson-mapper-asl-1.9.9.jar
  • javaGeom-0.11.0.jar
  • jcl-over-slf4j-1.7.2.jar
  • jgraphx-1.10.4.2.jar
  • joda-time-2.1.jar
  • junit-4.11.jar
  • log4j-1.2.17.jar
  • mail-1.4.1.jar
  • mybatis-3.2.2.jar
  • mysql-connector-java.jar
  • org.restlet.ext.fileupload-2.0.15.jar
  • org.restlet.ext.jackson-2.0.15.jar
  • org.restlet.ext.servlet-2.0.15.jar
  • org.restlet-2.0.15.jar
  • slf4j-api-1.7.2.jar
  • slf4j-log4j12-1.7.2.jar
  • spring-aop-3.1.2.RELEASE.jar
  • spring-asm-3.1.2.RELEASE.jar
  • spring-beans-3.1.2.RELEASE.jar
  • spring-context-3.1.2.RELEASE.jar
  • spring-core-3.1.2.RELEASE.jar
  • spring-expression-3.1.2.RELEASE.jar
  • spring-jdbc-3.1.2.RELEASE.jar
  • spring-orm-3.1.2.RELEASE.jar
  • spring-tx-3.1.2.RELEASE.jar

1.4開發步驟

上面已經說過了,咱們要想使用Activiti就須要有數據庫的支持,雖然Activiti是自動幫咱們建立對應的數據庫表,可是咱們是須要配置數據庫的信息的。咱們配置數據庫的信息,接着拿到Activiti最重要的API------Activiti引擎

這裏寫圖片描述

1.4.1獲得工做流引擎

Activiti提供使用代碼的方式來配置數據庫的信息

@Test
    public void createActivitiEngine(){

/* *1.經過代碼形式建立 * - 取得ProcessEngineConfiguration對象 * - 設置數據庫鏈接屬性 * - 設置建立表的策略 (當沒有表時,自動建立表) * - 經過ProcessEngineConfiguration對象建立 ProcessEngine 對象*/

         //取得ProcessEngineConfiguration對象
         ProcessEngineConfiguration engineConfiguration=ProcessEngineConfiguration.
         createStandaloneProcessEngineConfiguration();
         //設置數據庫鏈接屬性
         engineConfiguration.setJdbcDriver("com.mysql.jdbc.Driver");
         engineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/activitiDB?createDatabaseIfNotExist=true"
         + "&useUnicode=true&characterEncoding=utf8");
         engineConfiguration.setJdbcUsername("root");
         engineConfiguration.setJdbcPassword("root");


         // 設置建立表的策略 (當沒有表時,自動建立表)
         // public static final java.lang.String DB_SCHEMA_UPDATE_FALSE = "false";//不會自動建立表,沒有表,則拋異常
         // public static final java.lang.String DB_SCHEMA_UPDATE_CREATE_DROP = "create-drop";//先刪除,再建立表
         // public static final java.lang.String DB_SCHEMA_UPDATE_TRUE = "true";//假如沒有表,則自動建立
         engineConfiguration.setDatabaseSchemaUpdate("true");
         //經過ProcessEngineConfiguration對象建立 ProcessEngine 對象
        ProcessEngine processEngine = engineConfiguration.buildProcessEngine();
         System.out.println("流程引擎建立成功!");
        
    }
複製代碼

Activiti也能夠經過配置文件來配置數據庫的信息,加載配置文件從而獲得工做流引擎

/**2. 經過加載 activiti.cfg.xml 獲取 流程引擎 和自動建立數據庫及表 * ProcessEngineConfiguration engineConfiguration= ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml"); //從類加載路徑中查找資源 activiti.cfg.xm文件名能夠自定義 ProcessEngine processEngine = engineConfiguration.buildProcessEngine(); System.out.println("使用配置文件Activiti.cfg.xml獲取流程引擎"); */
複製代碼

activiti.cfg.xml

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
 <!-- 配置 ProcessEngineConfiguration -->
 <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
   <!-- 配置數據庫鏈接 -->
 <property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property>
 <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activitiDB?createDatabaseIfNotExist=true&amp;useUnicode=true&amp;characterEncoding=utf8"></property>
 <property name="jdbcUsername" value="root"></property>
 <property name="jdbcPassword" value="root"></property>
 
  <!-- 配置建立表策略 :沒有表時,自動建立 -->
  <property name="databaseSchemaUpdate" value="true"></property>
 
 </bean>

</beans>
複製代碼

這裏寫圖片描述

上面的那種加載配置文件方式,配置文件的名字是能夠自定義的,若是咱們配置文件的名字默認就是activiti.cfg.xml的話,也是放在類路徑下,咱們就可使用默認的方式來進行加載了!

@Test
    public void createActivitiEngine(){
        /** * 3. 經過ProcessEngines 來獲取默認的流程引擎 */
        // 默認會加載類路徑下的 activiti.cfg.xml
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        System.out.println("經過ProcessEngines 來獲取流程引擎");
    }

複製代碼

1.4.2定義工做流

定義工做流就須要咱們剛纔下載的插件了,咱們是使用圖形的方式來定義工做流的....

這裏寫圖片描述

在每一個流程中,咱們均可以指定對應的處理人是誰,交由誰處理

1.4.3部署工做流

咱們上面已經定義了工做流了,工做流引擎咱們也已經拿到了,接下來就是把工做流部署到工做流引擎中了

@Test
    public void deploy() {

        //獲取倉庫服務 :管理流程定義
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deploy = repositoryService.createDeployment()//建立一個部署的構建器
                .addClasspathResource("LeaveActiviti.bpmn")//從類路徑中添加資源,一次只能添加一個資源
                .name("請求單流程")//設置部署的名稱
                .category("辦公類別")//設置部署的類別
                .deploy();

        System.out.println("部署的id"+deploy.getId());
        System.out.println("部署的名稱"+deploy.getName());
    }

複製代碼

相對應的數據庫表就會插入數據、涉及到的數據庫表後面會詳細說明。如今咱們只要瞭解到,咱們工做流引擎執行操做會有數據庫表記錄

這裏寫圖片描述


1.4.4執行工做流

指定指定工做流就是咱們定義時工做流程表的id

這裏寫圖片描述

@Test
    public void startProcess(){

        //指定執行咱們剛纔部署的工做流程
        String processDefiKey="leaveBill";
        //取運行時服務
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //取得流程實例
        ProcessInstance pi = runtimeService.startProcessInstanceByKey(processDefiKey);//經過流程定義的key 來執行流程
        System.out.println("流程實例id:"+pi.getId());//流程實例id
        System.out.println("流程定義id:"+pi.getProcessDefinitionId());//輸出流程定義的id
    }
複製代碼

1.4.5根據代理人查詢當前任務的信息

剛纔咱們已經開始了工做流了,隨後工做流應該去到了申請請假的流程,申請請假的處理人是鍾福成,咱們能夠查詢出對應的信息:

//查詢任務
    @Test
    public void queryTask(){
        //任務的辦理人
        String assignee="鍾福成";
        //取得任務服務
        TaskService taskService = processEngine.getTaskService();
        //建立一個任務查詢對象
        TaskQuery taskQuery = taskService.createTaskQuery();
        //辦理人的任務列表
        List<Task> list = taskQuery.taskAssignee(assignee)//指定辦理人
                .list();
        //遍歷任務列表
        if(list!=null&&list.size()>0){
            for(Task task:list){
                System.out.println("任務的辦理人:"+task.getAssignee());
                System.out.println("任務的id:"+task.getId());
                System.out.println("任務的名稱:"+task.getName());
            }
        }
    }

複製代碼

這裏寫圖片描述

1.4.6處理任務

咱們如今處理流程去到「申請請假」中,處理人是鍾福成...接着就是鍾福成去處理任務,根據任務的id使得流程繼續往下走

任務的id剛纔咱們已經查詢出來了【上面】,咱們若是是在web端操做數據的話,那麼只要傳遞過去就好了!

//完成任務
    @Test
    public void compileTask(){
        String taskId="304";
        //taskId:任務id
        processEngine.getTaskService().complete(taskId);
        System.out.println("當前任務執行完畢");
    }
複製代碼

這裏寫圖片描述

當咱們處理完該任務的時候,就到了批准【班主任】任務了,咱們查詢一下是否是如咱們想象的效果:

這裏寫圖片描述

咱們按照定義的工做流程圖一步一步往下走,最終把流程走完

這裏寫圖片描述


2、流程定義細講

管理流程定義主要涉及到如下的4張表:

-- 流程部署相關的表
SELECT * FROM act_ge_bytearray # 通用字節資源表

SELECT * FROM act_ge_property # 通用屬性表,能夠生成部署id

SELECT * FROM act_re_deployment  #部署表

SELECT * FROM act_re_procdef    # 流程定義表
複製代碼

2.1PNG資源

在Eclipse中Activiti插件會自動生成一個BPMN與之對應的PNG圖片,是須要經過加載PNG圖片的

@Test
    public void deploy() {

        //獲取倉庫服務 :管理流程定義
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deploy = repositoryService.createDeployment()//建立一個部署的構建器
                .addClasspathResource("LeaveActiviti.bpmn")//從類路徑中添加資源,一次只能添加一個資源
                .name("請求單流程")//設置部署的名稱
                .category("辦公類別")//設置部署的類別
                .deploy();

        System.out.println("部署的id"+deploy.getId());
        System.out.println("部署的名稱"+deploy.getName());
    }
複製代碼

而咱們的Intellij idea插件不會自動生成PNG圖片,可是咱們在加載BPMN文件的時候,好像會自動插入PNG圖片,數據庫是有對應的記錄的【咱們是沒有手動加載PNG圖片資源的】

這裏寫圖片描述

咱們查看一下

//查看bpmn 資源圖片
    @Test
    public void viewImage() throws Exception{
        String deploymentId="201";
        String imageName=null;
        //取得某個部署的資源的名稱 deploymentId
        List<String> resourceNames = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId);
        // buybill.bpmn buybill.png
        if(resourceNames!=null&&resourceNames.size()>0){
            for(String temp :resourceNames){
                if(temp.indexOf(".png")>0){
                    imageName=temp;
                }
            }
        }

        /** * 讀取資源 * deploymentId:部署的id * resourceName:資源的文件名 */
        InputStream resourceAsStream = processEngine.getRepositoryService()
                .getResourceAsStream(deploymentId, imageName);

        //把文件輸入流寫入到文件中
        File file=new File("d:/"+imageName);
        FileUtils.copyInputStreamToFile(resourceAsStream, file);
    }
複製代碼

惋惜的是,查看出來的圖片的中文數據會亂碼...

這裏寫圖片描述

2.2查看流程定義

咱們當時候查詢流程定義是經過咱們設置流程圖的id來查看的....其實咱們能夠經過其餘的屬性來查詢...而且能夠查詢出更加詳細的數據

//查看流程定義
	@Test
	public void queryProcessDefination(){
		String processDefiKey="buyBill";//流程定義key
		//獲取流程定義列表
		List<ProcessDefinition> list = processEngine.getRepositoryService().createProcessDefinitionQuery()
		//查詢 ,比如where
// .processDefinitionId(proDefiId) //流程定義id
		 // 流程定義id : buyBill:2:704 組成 : proDefikey(流程定義key)+version(版本)+自動生成id
		.processDefinitionKey(processDefiKey)//流程定義key 由bpmn 的 process 的 id屬性決定
// .processDefinitionName(name)//流程定義名稱 由bpmn 的 process 的 name屬性決定
// .processDefinitionVersion(version)//流程定義的版本
		.latestVersion()//最新版本
		
		//排序
		.orderByProcessDefinitionVersion().desc()//按版本的降序排序
		
		//結果
// .count()//統計結果
// .listPage(arg0, arg1)//分頁查詢
		.list();
		
		
		//遍歷結果
		if(list!=null&&list.size()>0){
			for(ProcessDefinition temp:list){
				System.out.print("流程定義的id: "+temp.getId());
				System.out.print("流程定義的key: "+temp.getKey());
				System.out.print("流程定義的版本: "+temp.getVersion());
				System.out.print("流程定義部署的id: "+temp.getDeploymentId());
				System.out.println("流程定義的名稱: "+temp.getName());
			}
		}
	}
複製代碼

2.3資源來自ZIP

咱們還能夠加載的是ZIP類型的資源數據

//部署流程定義,資源來自zip格式
	@Test
	public void deployProcessDefiByZip(){
		InputStream in=getClass().getClassLoader().getResourceAsStream("BuyBill.zip");
		Deployment deploy = processEngine.getRepositoryService()
				.createDeployment()
				.name("採購流程")
				.addZipInputStream(new ZipInputStream(in))
				.deploy();
		
		System.out.println("部署名稱:"+deploy.getName());
		System.out.println("部署id:"+deploy.getId());
	}
複製代碼

2.4刪除流程定義

//刪除流程定義
	@Test
	public void deleteProcessDefi(){
		//經過部署id來刪除流程定義
		String deploymentId="101";
		processEngine.getRepositoryService().deleteDeployment(deploymentId);
	}

複製代碼

再次查詢的時候,已經沒有101這個流程定義的數據了。

這裏寫圖片描述


3、流程實例與任務執行細講

流程實例與任務執行的經常使用表有如下幾個:

-- 流程實例與任務

SELECT * FROM act_ru_execution  # 流程執行對象信息
SELECT * FROM act_ru_task   # 正在運行的任務表

SELECT * FROM act_hi_procinst # 歷史流程實例表
SELECT * FROM act_hi_taskinst  # 歷史流程任務表


複製代碼

這裏就簡單簡述一下流程實例與流程對象的區別:

  • (1)若是是單例流程,執行對象ID就是流程實例ID
  • (2)若是一個流程有分支和聚合,那麼執行對象ID和流程實例ID就不相同
  • (3)一個流程中,流程實例只有1個,執行對象能夠存在多個。

3.1開始流程##

@Test
    public void startProcess(){
        String processDefiKey="leaveActiviti";//bpmn 的 process id屬性
        ProcessInstance pi = processEngine.getRuntimeService()
                .startProcessInstanceByKey(processDefiKey);

        System.out.println("流程執行對象的id:"+pi.getId());//Execution 對象
        System.out.println("流程實例的id:"+pi.getProcessInstanceId());//ProcessInstance 對象
        System.out.println("流程定義的id:"+pi.getProcessDefinitionId());//默認執行的是最新版本的流程定義
    }
複製代碼

3.2查看正在運行的任務

//查詢正在運行任務 
	@Test
	public void queryTask(){
		//取得任務服務
		TaskService taskService = processEngine.getTaskService();
		//建立一個任務查詢對象
		TaskQuery taskQuery = taskService.createTaskQuery();
		//辦理人的任務列表
		List<Task> list = taskQuery.list();
		//遍歷任務列表
		if(list!=null&&list.size()>0){
			for(Task task:list){
				System.out.println("任務的辦理人:"+task.getAssignee());
				System.out.println("任務的id:"+task.getId());
				System.out.println("任務的名稱:"+task.getName());
				
			}
		}
		
	}
複製代碼

查詢SELECT * FROM act_ru_task 表

這裏寫圖片描述

這裏寫圖片描述


3.3獲取流程實例的狀態

有的時候,咱們須要判斷它是在該流程,仍是該流程已經結束了。咱們能夠根據獲取出來的對象是否爲空來進行判斷

//獲取流程實例的狀態
    @Test
    public void getProcessInstanceState(){
        String processInstanceId="605";
        ProcessInstance pi = processEngine.getRuntimeService()
                .createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();//返回的數據要麼是單行,要麼是空 ,其餘狀況報錯
        //判斷流程實例的狀態
        if(pi!=null){
            System.out.println("該流程實例"+processInstanceId+"正在運行... "+"當前活動的任務:"+pi.getActivityId());
        }else{
            System.out.println("當前的流程實例"+processInstanceId+" 已經結束!");
        }

    }

複製代碼

這裏寫圖片描述


3.4查看歷史流程實例的信息

//查看歷史執行流程實例信息
	@Test
	public void queryHistoryProcInst(){
		List<HistoricProcessInstance> list = processEngine.getHistoryService()
		.createHistoricProcessInstanceQuery()
		.list();
		if(list!=null&&list.size()>0){
			for(HistoricProcessInstance temp:list){
				System.out.println("歷史流程實例id:"+temp.getId());
				System.out.println("歷史流程定義的id:"+temp.getProcessDefinitionId());
				System.out.println("歷史流程實例開始時間--結束時間:"+temp.getStartTime()+"-->"+temp.getEndTime());
			}
		}
	}
複製代碼

查詢表:

這裏寫圖片描述

這裏寫圖片描述

3.5查看歷史實例執行任務信息

@Test
    public void queryHistoryTask(){
        String processInstanceId="605";
        List<HistoricTaskInstance> list = processEngine.getHistoryService()
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .list();
        if(list!=null&&list.size()>0){
            for(HistoricTaskInstance temp:list){
                System.out.print("歷史流程實例任務id:"+temp.getId());
                System.out.print("歷史流程定義的id:"+temp.getProcessDefinitionId());
                System.out.print("歷史流程實例任務名稱:"+temp.getName());
                System.out.println("歷史流程實例任務處理人:"+temp.getAssignee());
            }
        }
    }
複製代碼

給予對應的實例id就能夠查詢出執行到哪一個任務了...

這裏寫圖片描述

這裏寫圖片描述

3.6執行任務

根據任務的id,就能夠把該任務執行了。

@Test
    public void compileTask(){
        String taskId="608";
        //taskId:任務id
        processEngine.getTaskService().complete(taskId);
        System.out.println("當前任務執行完畢");
    }

複製代碼

4、流程變量細講

流程變量涉及到的數據庫表:

act_ru_variable:正在執行的流程變量表
act_hi_varinst:流程變量歷史表


複製代碼

流程變量在工做流中扮演着一個很是重要的角色。例如:請假流程中有請假天數、請假緣由等一些參數都爲流程變量的範圍**。流程變量的做用域範圍是只對應一個流程實例**。也就是說各個流程實例的流程變量是不相互影響的。流程實例結束完成之後流程變量還保存在數據庫中(存放到流程變量的歷史表中)。

這裏寫圖片描述

4.1設置流程變量

咱們有兩種服務能夠設置流程變量,TaskService【任務服務】和RuntimeService【運行時服務】

場景

  • 在流程開始的時候設置流程變量
  • 在完成某個任務的時候設置流程變量
  • 使用TaskService設置服務
  • 使用RuntimeService設置服務

做用:

  • 傳遞業務參數
  • 動態指定代理人【咱們快速入門的例子是固定在流程定義圖上寫上代理人的】
  • 指定鏈接【決定流程往哪邊走】

4.2流程變量支持類型

若是咱們使用JavaBean來做爲流程的變量,那麼咱們須要將JavaBean實現Serializable接口

Javabean類型設置獲取流程變量,除了須要這個javabean實現了Serializable接口外,還要求流程變量對象的屬性不能發生變化,不然拋出異常。解決方案,固定序列化ID

這裏寫圖片描述

4.3setVariable和setVariableLocal的區別

這裏寫圖片描述

4.4例子

//模擬流程變量設置
	@Test
	public void getAndSetProcessVariable(){
		//有兩種服務能夠設置流程變量
// TaskService taskService = processEngine.getTaskService();
// RuntimeService runtimeService = processEngine.getRuntimeService();
		
		/**1.經過 runtimeService 來設置流程變量 * executionId: 執行對象 * variableName:變量名 * values:變量值 */
// runtimeService.setVariable(executionId, variableName, values);
// runtimeService.setVariableLocal(executionId, variableName, values);
		//設置本執行對象的變量 ,該變量的做用域只在當前的execution對象
// runtimeService.setVariables(executionId, variables); 
		  //能夠設置多個變量 放在 Map<key,value> Map<String,Object>
		
		/**2. 經過TaskService來設置流程變量 * taskId:任務id */
// taskService.setVariable(taskId, variableName, values);
// taskService.setVariableLocal(taskId, variableName, values);
//// 設置本執行對象的變量 ,該變量的做用域只在當前的execution對象
// taskService.setVariables(taskId, variables); //設置的是Map<key,values>
		
		/**3. 當流程開始執行的時候,設置變量參數 * processDefiKey: 流程定義的key * variables: 設置多個變量 Map<key,values> */
// processEngine.getRuntimeService()
// .startProcessInstanceByKey(processDefiKey, variables)
		
		/**4. 當任務完成時候,能夠設置流程變量 * taskId:任務id * variables: 設置多個變量 Map<key,values> */
// processEngine.getTaskService().complete(taskId, variables);
		
		
		/** 5. 經過RuntimeService取變量值 * exxcutionId: 執行對象 * */
// runtimeService.getVariable(executionId, variableName);//取變量
// runtimeService.getVariableLocal(executionId, variableName);//取本執行對象的某個變量
// runtimeService.getVariables(variablesName);//取當前執行對象的全部變量
		/** 6. 經過TaskService取變量值 * TaskId: 執行對象 * */
// taskService.getVariable(taskId, variableName);//取變量
// taskService.getVariableLocal(taskId, variableName);//取本執行對象的某個變量
// taskService.getVariables(taskId);//取當前執行對象的全部變量
	}
複製代碼
//設置流程變量值
	@Test
	public void setVariable(){
		String taskId="1804";//任務id
		//採用TaskService來設置流程變量
		
		//1. 第一次設置流程變量
// TaskService taskService = processEngine.getTaskService();
// taskService.setVariable(taskId, "cost", 1000);//設置單一的變量,做用域在整個流程實例
// taskService.setVariable(taskId, "申請時間", new Date());
// taskService.setVariableLocal(taskId, "申請人", "何某某");//該變量只有在本任務中是有效的
		
		
		//2. 在不一樣的任務中設置變量
// TaskService taskService = processEngine.getTaskService();
// taskService.setVariable(taskId, "cost", 5000);//設置單一的變量,做用域在整個流程實例
// taskService.setVariable(taskId, "申請時間", new Date());
// taskService.setVariableLocal(taskId, "申請人", "李某某");//該變量只有在本任務中是有效的
		
		/** * 3. 變量支持的類型 * - 簡單的類型 :String 、boolean、Integer、double、date * - 自定義對象bean */
		TaskService taskService = processEngine.getTaskService();
		//傳遞的一個自定義bean對象
		AppayBillBean appayBillBean=new AppayBillBean();
		appayBillBean.setId(1);
		appayBillBean.setCost(300);
		appayBillBean.setDate(new Date());
		appayBillBean.setAppayPerson("何某某");
		taskService.setVariable(taskId, "appayBillBean", appayBillBean);
		
		
		System.out.println("設置成功!");

	}
複製代碼
//查詢流程變量
	@Test
	public void getVariable(){
		String taskId="1804";//任務id
// TaskService taskService = processEngine.getTaskService();
// Integer cost=(Integer) taskService.getVariable(taskId, "cost");//取變量
// Date date=(Date) taskService.getVariable(taskId, "申請時間");//取本任務中的變量
//// Date date=(Date) taskService.getVariableLocal(taskId, "申請時間");//取本任務中的變量
// String appayPerson=(String) taskService.getVariableLocal(taskId, "申請人");//取本任務中的變量
//// String appayPerson=(String) taskService.getVariable(taskId, "申請人");//取本任務中的變量
// 
// System.out.println("金額:"+cost);
// System.out.println("申請時間:"+date);
// System.out.println("申請人:"+appayPerson);
		
		
		//讀取實現序列化的對象變量數據
		TaskService taskService = processEngine.getTaskService();
		AppayBillBean appayBillBean=(AppayBillBean) taskService.getVariable(taskId, "appayBillBean");
		System.out.println(appayBillBean.getCost());
		System.out.println(appayBillBean.getAppayPerson());
		
	}

複製代碼
public class AppayBillBean implements Serializable{
	private Integer id;
	private Integer cost;//金額
	private String appayPerson;//申請人
	private Date date;//申請日期
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public Integer getCost() {
		return cost;
	}
	public void setCost(Integer cost) {
		this.cost = cost;
	}
	public String getAppayPerson() {
		return appayPerson;
	}
	public void setAppayPerson(String appayPerson) {
		this.appayPerson = appayPerson;
	}
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
	

}
複製代碼

5、連線

上面咱們已將學過了流程變量了,能夠在【任務服務、運行時服務、流程開始、完成某個任務時設置流程變量】,而咱們的鏈接就是流程變量的實際應用了....

5.1定義流程圖

咱們並非全部的流程都是按一條的路徑來走的,咱們有的時候會根據條件來走不一樣的路。固然了,最終該流程是會一步步走完....

例子:

重要的信息交由老闆來處理,不重要的信息交由經理來處理

這裏寫圖片描述

表達式的結果必須是布爾型

  • #{variable=='value'}
  • ${variable==value}

5.2測試

我在任務完成時設置流程變量爲不重要,那麼跳到下一個流程時就是經理來進行處理

這裏寫圖片描述

當我設置爲重要的時候,那麼就是交由老闆來處理

這裏寫圖片描述

6、排他網關

上面咱們使用連線的時候用了兩個條件 : 要麼條件是「重要」,要麼條件是「不重要」....若是有另外一種狀況呢???就是用戶把條件輸入錯了,寫成「不知道重不重要」,那麼咱們的流程怎麼走???豈不是奔潰了???

所以,咱們要有一條默認的路來走,就是當該變量不符合任何的條件時,咱們也有一條默認的路

這裏寫圖片描述

值得注意的是:若是是在Eclipse中使用插件的BPMN流程圖,若是使用了排他網關,那麼在Idea下是解析不了的...

解決:

咱們只要從新定義BPMN流程圖的排他網關就好了,idea中的Activiti插件是不用制定默認流程的,只要咱們不設置條件,那就是默認的鏈接線

6.1測試

public class ExclusiveGetWay {
	private ProcessEngine processEngine = ProcessEngines
			.getDefaultProcessEngine();

	// 部署流程定義,資源來在bpmn格式
	@Test
	public void deployProcessDefi() {
		Deployment deploy = processEngine.getRepositoryService()
				.createDeployment().name("排他網關流程")
				.addClasspathResource("ExclusiveGateway.bpmn")
				.deploy();

		System.out.println("部署名稱:" + deploy.getName());
		System.out.println("部署id:" + deploy.getId());
	}

	// 執行流程,開始跑流程
	@Test
	public void startProcess() {
		String processDefiKey = "bankBill";// bpmn 的 process id屬性
		ProcessInstance pi = processEngine.getRuntimeService()
				.startProcessInstanceByKey(processDefiKey);

		System.out.println("流程執行對象的id:" + pi.getId());// Execution 對象
		System.out.println("流程實例的id:" + pi.getProcessInstanceId());// ProcessInstance
																	// 對象
		System.out.println("流程定義的id:" + pi.getProcessDefinitionId());// 默認執行的是最新版本的流程定義
	}

	// 查詢正在運行任務
	@Test
	public void queryTask() {
		// 取得任務服務
		TaskService taskService = processEngine.getTaskService();
		// 建立一個任務查詢對象
		TaskQuery taskQuery = taskService.createTaskQuery();
		// 辦理人的任務列表
		List<Task> list = taskQuery.list();
		// 遍歷任務列表
		if (list != null && list.size() > 0) {
			for (Task task : list) {
				System.out.println("任務的辦理人:" + task.getAssignee());
				System.out.println("任務的id:" + task.getId());
				System.out.println("任務的名稱:" + task.getName());
			}
		}

	}

	// 完成任務
	@Test
	public void compileTask() {
		String taskId = "2404";
		Map<String,Object> params=new HashMap<String, Object>();
		params.put("visitor", 6);
		// taskId:任務id
		processEngine.getTaskService().complete(taskId, params);
// processEngine.getTaskService().complete(taskId);
		System.out.println("當前任務執行完畢");
	}
	
}

複製代碼

咱們指定的值並非VIP也不是後臺,那麼就會自動去普通窗口中處理

這裏寫圖片描述


7、拓展閱讀

並行網關:

這裏寫圖片描述

等待活動:

這裏寫圖片描述

用戶任務:

  • 使用流程變量指定處理人:

咱們在快速入門的例子中,是在定義流程圖中硬性指定處理人,其實這麼幹是不夠靈活的,咱們學了流程變量以後,咱們是能夠靈活地指定處理人的....

這裏寫圖片描述

@Test
	public void deployProcessDefi() {
		Deployment deploy = processEngine.getRepositoryService()
				.createDeployment().name("用戶任務指定流程")
				.addClasspathResource("AppayBill.bpmn")
				.deploy();

		System.out.println("部署名稱:" + deploy.getName());
		System.out.println("部署id:" + deploy.getId());
	}

	// 執行流程,開始跑流程
	@Test
	public void startProcess() {
		String processDefiKey = "appayBill";// bpmn 的 process id屬性
		Map<String,Object> params=new HashMap<String, Object>();
		params.put("userID", "王某某");
		ProcessInstance pi = processEngine.getRuntimeService()
				.startProcessInstanceByKey(processDefiKey, params);

		System.out.println("流程執行對象的id:" + pi.getId());// Execution 對象
		System.out.println("流程實例的id:" + pi.getProcessInstanceId());// ProcessInstance
		// 對象
		System.out.println("流程定義的id:" + pi.getProcessDefinitionId());// 默認執行的是最新版本的流程定義
	}

	// 查詢正在運行任務
	@Test
	public void queryTask() {
		String assignee="王某某";//指定任務處理人
		// 取得任務服務
		TaskService taskService = processEngine.getTaskService();
		// 建立一個任務查詢對象
		TaskQuery taskQuery = taskService.createTaskQuery();
		// 辦理人的任務列表
		List<Task> list = taskQuery
				.taskAssignee(assignee)
				.list();
		// 遍歷任務列表
		if (list != null && list.size() > 0) {
			for (Task task : list) {
				System.out.println("任務的辦理人:" + task.getAssignee());
				System.out.println("任務的id:" + task.getId());
				System.out.println("任務的名稱:" + task.getName());
			}
		}

	}
複製代碼

這裏寫圖片描述

使用類指定:

這裏寫圖片描述

組任務:

  • 直接指定辦理人

這裏寫圖片描述

  • 使用流程變量

這裏寫圖片描述

  • 使用類

這裏寫圖片描述

總結

  • 若是一個業務須要多方面角色進行處理的話,那麼咱們最好就是用工做流框架。由於若是其中一個環節的需求發生了變化的話,咱們要是沒有用到工做流。那就須要修改不少的代碼。十分麻煩。
  • Activiti工做流框架快速入門:
    • 定義工做流,使用插件來把咱們的流程圖畫出來。這個流程圖就是咱們定義的工做流。
    • 工做流引擎是工做流的核心,可以讓咱們定義出來的工做流部署起來。
    • 因爲咱們使用工做流的時候是有不少數據產生的,所以Activiti是將數據保存到數據庫表中的。這些數據庫表由Actitviti建立,由Activiti維護。
    • 部署完的工做流是須要手動去執行該工做流的。
    • 根據由誰處理當前任務,咱們就能夠查詢出具體的任務信息。
    • 根據任務的id,咱們就能夠執行任務了。
  • 流程定義涉及到了四張數據庫表
    • 咱們能夠經過API把咱們的流程定義圖讀取出來
    • 能夠根據查詢最新版本的流程定義
    • 刪除流程定義
    • 部署流程定義的時候也能夠是ZIP文件
  • 流程在運行中,涉及到兩個對象,四張數據庫表:
    • 流程實例
    • 流程任務
    • 流程實例能夠有多個,流程對象只能有一個。
      • 若是流程沒有分支的話,那麼流程實例就等於流程對象
    • 基於這麼兩個對象,咱們就能夠作不少事情了
      • 獲取流程實例和任務的歷史信息
      • 判斷流程實例是否爲空來判斷流程是否結束了
      • 查看正在運行服務的詳細信息
      • 經過流程實例來開啓流程
  • 流程變量:它涉及到了兩張表。流程變量實際上就是咱們的條件。
    • 流程變量的做用域只在流程實例中。
    • 咱們能夠在流程開始的時候設置流程變量,在任務完成的時候設置流程變量。
    • 運行時服務和流程任務均可以設置流程變量。
  • 經過連線咱們能夠在其中設置條件,根據不一樣的條件流程走不一樣的分支
  • 若是沒有設置默認的條件,當條件不吻合的時候,那麼流程就走不下去了,所以須要排他網關來設置一條默認的路徑。

若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y

相關文章
相關標籤/搜索