工作流引擎 Activiti 保姆级教程
大家好,我是老趙!
一、工作流介紹
1.1 概念
工作流(Workflow),就是通過計算機對業務流程自動化執行管理。它主要解決的是“使在多個參與者之間按照某種預定義的規則自動進行傳遞文檔、信息或任務的過程,從而實現某個預期的業務目標,或者促使此目標的實現”。
1.2 工作流系統
一個軟件系統中具有工作流的功能,我們把它稱為工作流系統,一個系統中工作流的功能是什么?就是對系統的業務流程進行自動化管理,所以工作流是建立在業務流程的基礎上,所以一個軟件的系統核心根本上還是系統的業務流程,工作流只是協助進行業務流程管理。即使沒有工作流業務系統也可以開發運行,只不過有了工作流可以更好的管理業務流程,提高系統的可擴展性。
1.3 適用行業
消費品行業,制造業,電信服務業,銀證險等金融服務業,物流服務業,物業服務業,物業管理,大中型進出口貿易公司,政府事業機構,研究院所及教育服務業等,特別是大的跨國企業和集團公司。
1.4 具體應用
1、關鍵業務流程: 訂單、報價處理、合同審核、客戶電話處理、供應鏈管理等
2、行政管理類: 出差申請、加班申請、請假申請、用車申請、各種辦公用品申請、購買申請、日報周報等凡是原來手工流轉處理的行政表單。
3、人事管理類: 員工培訓安排、績效考評、職位變動處理、員工檔案信息管理等。
4、財務相關類: 付款請求、應收款處理、日常報銷處理、出差報銷、預算和計劃申請等。
5、客戶服務類: 客戶信息管理、客戶投訴、請求處理、售后服務管理等。
6、特殊服務類: ISO系列對應流程、質量管理對應流程、產品數據信息管理、貿易公司報關處理、物流公司貨物跟蹤處理等各種通過表單逐步手工流轉完成的任務均可應用工作流軟件自動規范地實施。
1.5 實現方式
在沒有專門的工作流引擎之前,我們之前為了實現流程控制,通常的做法就是采用狀態字段的值來跟蹤流程的變化情況。這樣不同角色的用戶,通過狀態字段的取值來決定記錄是否顯示。
針對有權限可以查看的記錄,當前用戶根據自己的角色來決定審批是否合格的操作。如果合格將狀態字段設置一個值,來代表合格;當然如果不合格也需要設置一個值來代表不合格的情況。
這是一種最為原始的方式。通過狀態字段雖然做到了流程控制,但是當我們的流程發生變更的時候,這種方式所編寫的代碼也要進行調整。
那么有沒有專業的方式來實現工作流的管理呢?并且可以做到業務流程變化之后,我們的程序可以不用改變,如果可以實現這樣的效果,那么我們的業務系統的適應能力就得到了極大提升。
二、Activiti7概述
2.1 介紹
Alfresco軟件在2010年5月17日宣布Activiti業務流程管理(BPM)開源項目的正式啟動,其首席架構師由業務流程管理BPM的專家 Tom Baeyens擔任,Tom Baeyens就是原來jbpm的架構師,而jbpm是一個非常有名的工作流引擎,當然activiti也是一個工作流引擎。
Activiti是一個工作流引擎, activiti可以將業務系統中復雜的業務流程抽取出來,使用專門的建模語言BPMN2.0進行定義,業務流程按照預先定義的流程進行執行,實現了系統的流程由activiti進行管理,減少業務系統由于流程變更進行系統升級改造的工作量,從而提高系統的健壯性,同時也減少了系統開發維護成本。
官方網站:https://www.activiti.org/
經歷的版本:
目前最新版本:Activiti7.0.0.Beta
2.1.1 BPM
BPM(Business Process Management),即業務流程管理,是一種規范化的構造端到端的業務流程,以持續的提高組織業務效率。常見商業管理教育如EMBA、MBA等均將BPM包含在內。
2.1.2 BPM軟件
BPM軟件就是根據企業中業務環境的變化,推進人與人之間、人與系統之間以及系統與系統之間的整合及調整的經營方法與解決方案的IT工具。
通過BPM軟件對企業內部及外部的業務流程的整個生命周期進行建模、自動化、管理監控和優化,使企業成本降低,利潤得以大幅提升。
BPM軟件在企業中應用領域廣泛,凡是有業務流程的地方都可以BPM軟件進行管理,比如企業人事辦公管理、采購流程管理、公文審批流程管理、財務管理等。
2.1.3 BPMN
BPMN(Business Process Model AndNotation)- 業務流程模型和符號 是由BPMI(BusinessProcess Management Initiative)開發的一套標準的業務流程建模符號,使用BPMN提供的符號可以創建業務流程。
2004年5月發布了BPMN1.0規范.BPMI于2005年9月并入OMG(The Object Management Group對象管理組織)組織。OMG于2011年1月發布BPMN2.0的最終版本。
具體發展歷史如下:
BPMN 是目前被各 BPM 廠商廣泛接受的 BPM 標準。Activiti 就是使用 BPMN 2.0 進行流程建模、流程執行管理,它包括很多的建模符號,比如:Event
用一個圓圈表示,它是流程中運行過程中發生的事情。
活動用圓角矩形表示,一個流程由一個活動或多個活動組成
Bpmn圖形其實是通過xml表示業務流程,上邊的.bpmn文件使用文本編輯器打開:
2.2 使用步驟
部署activiti
Activiti是一個工作流引擎(其實就是一堆jar包API),業務系統訪問(操作)activiti的接口,就可以方便的操作流程相關數據,這樣就可以把工作流環境與業務系統的環境集成在一起。
流程定義
使用activiti流程建模工具(activity-designer)定義業務流程(.bpmn文件) 。
.bpmn文件就是業務流程定義文件,通過xml定義業務流程。
流程定義部署
activiti部署業務流程定義(.bpmn文件)。
使用activiti提供的api把流程定義內容存儲起來,在Activiti執行過程中可以查詢定義的內容
Activiti執行把流程定義內容存儲在數據庫中
啟動一個流程實例
流程實例也叫:ProcessInstance
啟動一個流程實例表示開始一次業務流程的運行。
在員工請假流程定義部署完成后,如果張三要請假就可以啟動一個流程實例,如果李四要請假也啟動一個流程實例,兩個流程的執行互相不影響。
用戶查詢待辦任務(Task)
因為現在系統的業務流程已經交給activiti管理,通過activiti就可以查詢當前流程執行到哪了,當前用戶需要辦理什么任務了,這些activiti幫我們管理了,而不需要開發人員自己編寫在sql語句查詢。
用戶辦理任務
用戶查詢待辦任務后,就可以辦理某個任務,如果這個任務辦理完成還需要其它用戶辦理,比如采購單創建后由部門經理審核,這個過程也是由activiti幫我們完成了。
流程結束
當任務辦理完成沒有下一個任務結點了,這個流程實例就完成了。
三、Activiti環境
3.1 開發環境
Jdk1.8或以上版本
Mysql 5及以上的版本
Tomcat8.5
IDEA
注意:activiti的流程定義工具插件可以安裝在IDEA下,也可以安裝在Eclipse工具下
3.2 Activiti環境
我們使用:Activiti7.0.0.Beta1 默認支持spring5
3.2.1 下載activiti7
Activiti下載地址:http://activiti.org/download.html ,Maven的依賴如下:
<dependencyManagement><dependencies><dependency><groupId>org.activiti</groupId><artifactId>activiti-dependencies</artifactId><version>7.0.0.Beta1</version><scope>import</scope><type>pom</type></dependency></dependencies> </dependencyManagement>1) Database:
activiti運行需要有數據庫的支持,支持的數據庫有:h2, mysql, oracle, postgres, mssql, db2。
3.2.2 流程設計器IDEA下安裝
在IDEA的File菜單中找到子菜單”Settings”,后面我們再選擇左側的“plugins”菜單,如下圖所示:
此時我們就可以搜索到actiBPM插件,它就是Activiti Designer的IDEA版本,我們點擊Install安裝。
安裝好后,頁面如下:
提示需要重啟idea,點擊重啟。
重啟完成后,再次打開Settings 下的 Plugins(插件列表),點擊右側的Installed(已安裝的插件),在列表中看到actiBPM,就說明已經安裝成功了,如下圖所示:
后面的課程里,我們會使用這個流程設計器進行Activiti的流程設計。
3.3 Activiti的數據庫支持
Activiti 在運行時需要數據庫的支持,使用25張表,把流程定義節點內容讀取到數據庫表中,以供后續使用。
3.3.1 Activiti 支持的數據庫
activiti 支持的數據庫和版本如下:
3.3.2 在MySQL生成表
3.3.2.1 創建數據庫
創建 mysql 數據庫 activiti (名字任意):
CREATE?DATABASE?activiti?DEFAULT?CHARACTER?SET?utf8;3.3.2.2 使用java代碼生成表
創建 java 工程
使用idea 創建 java 的maven工程,取名:activiti01。
加入 maven 依賴的坐標(jar 包)
首先需要在 java 工程中加入 ProcessEngine 所需要的 jar 包,包括:
activiti-engine-7.0.0.beta1.jar
activiti 依賴的 jar 包:mybatis、 alf4j、 log4j 等
activiti 依賴的 spring 包
mysql數據庫驅動
第三方數據連接池 dbcp
單元測試 Junit-4.12.jar
我們使用 maven 來實現項目的構建,所以應當導入這些 jar 所對應的坐標到 pom.xml 文件中。
完整的依賴內容如下:
<properties><slf4j.version>1.6.6</slf4j.version><log4j.version>1.2.12</log4j.version><activiti.version>7.0.0.Beta1</activiti.version> </properties> <dependencies><dependency><groupId>org.activiti</groupId><artifactId>activiti-engine</artifactId><version>${activiti.version}</version></dependency><dependency><groupId>org.activiti</groupId><artifactId>activiti-spring</artifactId><version>${activiti.version}</version></dependency><!--?bpmn?模型處理?--><dependency><groupId>org.activiti</groupId><artifactId>activiti-bpmn-model</artifactId><version>${activiti.version}</version></dependency><!--?bpmn?轉換?--><dependency><groupId>org.activiti</groupId><artifactId>activiti-bpmn-converter</artifactId><version>${activiti.version}</version></dependency><!--?bpmn?json數據轉換?--><dependency><groupId>org.activiti</groupId><artifactId>activiti-json-converter</artifactId><version>${activiti.version}</version></dependency><!--?bpmn?布局?--><dependency><groupId>org.activiti</groupId><artifactId>activiti-bpmn-layout</artifactId><version>${activiti.version}</version></dependency><!--?activiti?云支持?--><dependency><groupId>org.activiti.cloud</groupId><artifactId>activiti-cloud-services-api</artifactId><version>${activiti.version}</version></dependency><!--?mysql驅動?--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.40</version></dependency><!--?mybatis?--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version></dependency><!--?鏈接池?--><dependency><groupId>commons-dbcp</groupId><artifactId>commons-dbcp</artifactId><version>1.4</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!--?log?start?--><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>${slf4j.version}</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>${slf4j.version}</version></dependency> </dependencies>添加log4j日志配置
我們使用log4j日志包,可以對日志進行配置
在resources 下創建log4j.properties
#?Set?root?category?priority?to?INFO?and?its?only?appender?to?CONSOLE. #log4j.rootCategory=INFO,?CONSOLE?debug?info?warn?error?fatal log4j.rootCategory=debug,?CONSOLE,?LOGFILE #?Set?the?enterprise?logger?category?to?FATAL?and?its?only?appender?to?CONSOLE. log4j.logger.org.apache.axis.enterprise=FATAL,?CONSOLE #?CONSOLE?is?set?to?be?a?ConsoleAppender?using?a?PatternLayout. log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601}?%-6r[%15.15t]?%-5p?%30.30c?%x?-?%m\n #?LOGFILE?is?set?to?be?a?File?appender?using?a?PatternLayout. log4j.appender.LOGFILE=org.apache.log4j.FileAppender log4j.appender.LOGFILE.File=f:\act\activiti.log log4j.appender.LOGFILE.Append=true log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601}?%-6r[%15.15t]?%-5p?%30.30c?%x?-?%m\n添加activiti配置文件
我們使用activiti提供的默認方式來創建mysql的表。
默認方式的要求是在 resources 下創建 activiti.cfg.xml 文件,注意:默認方式目錄和文件名不能修改,因為activiti的源碼中已經設置,到固定的目錄讀取固定文件名的文件。
<?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:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/contex http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> </beans>在 activiti.cfg.xml 中進行配置
默認方式要在在activiti.cfg.xml中bean的名字叫processEngineConfiguration,名字不可修改
在這里有2中配置方式:一種是單獨配置數據源,一種是不單獨配置數據源
1、直接配置processEngineConfiguration
processEngineConfiguration 用來創建 ProcessEngine,在創建 ProcessEngine 時會執行數據庫的操作。
<?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:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/contex http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"><!--?默認id對應的值?為processEngineConfiguration?--><!--?processEngine?Activiti的流程引擎?--><bean?id="processEngineConfiguration"class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"><property?name="jdbcDriver"?value="com.mysql.jdbc.Driver"/><property?name="jdbcUrl"?value="jdbc:mysql:///activiti"/><property?name="jdbcUsername"?value="root"/><property?name="jdbcPassword"?value="123456"/><!--?activiti數據庫表處理策略?--><property?name="databaseSchemaUpdate"?value="true"/></bean> </beans>2、配置數據源后,在processEngineConfiguration 引用
首先配置數據源
<?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:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/contex http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"><!--?這里可以使用?鏈接池?dbcp--><bean?id="dataSource"?class="org.apache.commons.dbcp.BasicDataSource"><property?name="driverClassName"?value="com.mysql.jdbc.Driver"?/><property?name="url"?value="jdbc:mysql:///activiti"?/><property?name="username"?value="root"?/><property?name="password"?value="123456"?/><property?name="maxActive"?value="3"?/><property?name="maxIdle"?value="1"?/></bean><bean?id="processEngineConfiguration"class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"><!--?引用數據源?上面已經設置好了--><property?name="dataSource"?ref="dataSource"?/><!--?activiti數據庫表處理策略?--><property?name="databaseSchemaUpdate"?value="true"/></bean> </beans>java類編寫程序生成表
創建一個測試類,調用activiti的工具類,生成acitivti需要的數據庫表。
直接使用activiti提供的工具類ProcessEngines,會默認讀取classpath下的activiti.cfg.xml文件,讀取其中的數據庫配置,創建 ProcessEngine,在創建ProcessEngine 時會自動創建表。
代碼如下:
package?com.itheima.activiti01.test;import?org.activiti.engine.ProcessEngine; import?org.activiti.engine.ProcessEngineConfiguration; import?org.junit.Test;public?class?TestDemo?{/***?生成?activiti的數據庫表*/@Testpublic?void?testCreateDbTable()?{//使用classpath下的activiti.cfg.xml中的配置創建processEngineProcessEngine?processEngine?=?ProcessEngines.getDefaultProcessEngine();System.out.println(processEngine);} }說明:
運行以上程序段即可完成 activiti 表創建,通過改變 activiti.cfg.xml 中databaseSchemaUpdate 參數的值執行不同的數據表處理策略。
上 邊 的 方法 getDefaultProcessEngine方法在執行時,從activiti.cfg.xml 中找固定的名稱 processEngineConfiguration 。
在測試程序執行過程中,idea的控制臺會輸出日志,說明程序正在創建數據表,類似如下,注意紅線內容:
執行完成后我們查看數據庫, 創建了 25 張表,結果如下:
到這,我們就完成activiti運行需要的數據庫和表的創建。
3.4 表結構介紹
3.4.1 表的命名規則和作用
看到剛才創建的表,我們發現Activiti 的表都以 ACT_ 開頭。
第二部分是表示表的用途的兩個字母標識。用途也和服務的 API 對應。
ACT_RE :'RE’表示 repository。這個前綴的表包含了流程定義和流程靜態資源 (圖片,規則,等等)。
ACT_RU:'RU’表示 runtime。這些運行時的表,包含流程實例,任務,變量,異步任務,等運行中的數據。Activiti 只在流程實例執行過程中保存這些數據, 在流程結束時就會刪除這些記錄。這樣運行時表可以一直很小速度很快。
ACT_HI:'HI’表示 history。這些表包含歷史數據,比如歷史流程實例, 變量,任務等等。
ACT_GE :GE 表示 general。通用數據, 用于不同場景下
3.4.2 Activiti數據表介紹
四、Activiti類關系圖
上面我們完成了Activiti數據庫表的生成,java代碼中我們調用Activiti的工具類,下面來了解Activiti的類關系
4.1 類關系圖
在新版本中,我們通過實驗可以發現IdentityService,FormService兩個Serivce都已經刪除了。
所以后面我們對于這兩個Service也不講解了,但老版本中還是有這兩個Service,同學們需要了解一下
4.2 activiti.cfg.xml
activiti的引擎配置文件,包括:ProcessEngineConfiguration的定義、數據源定義、事務管理器等,此文件其實就是一個spring配置文件。
4.3 流程引擎配置類
流程引擎的配置類(ProcessEngineConfiguration),通過ProcessEngineConfiguration可以創建工作流引擎ProceccEngine,常用的兩種方法如下:
4.3.1 StandaloneProcessEngineConfiguration
使用StandaloneProcessEngineConfigurationActiviti可以單獨運行,來創建ProcessEngine,Activiti會自己處理事務。
配置文件方式:
通常在activiti.cfg.xml配置文件中定義一個id為 processEngineConfiguration 的bean。
方法如下:
<bean?id="processEngineConfiguration"class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"><!--配置數據庫相關的信息--><!--數據庫驅動--><property?name="jdbcDriver"?value="com.mysql.jdbc.Driver"/><!--數據庫鏈接--><property?name="jdbcUrl"?value="jdbc:mysql:///activiti"/><!--數據庫用戶名--><property?name="jdbcUsername"?value="root"/><!--數據庫密碼--><property?name="jdbcPassword"?value="123456"/><!--actviti數據庫表在生成時的策略??true?-?如果數據庫中已經存在相應的表,那么直接使用,如果不存在,那么會創建--><property?name="databaseSchemaUpdate"?value="true"/></bean>還可以加入連接池:
<?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:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/contex http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"><bean?id="dataSource"?class="org.apache.commons.dbcp.BasicDataSource"><property?name="driverClassName"?value="com.mysql.jdbc.Driver"/><property?name="url"?value="jdbc:mysql:///activiti"/><property?name="username"?value="root"/><property?name="password"?value="123456"/><property?name="maxActive"?value="3"/><property?name="maxIdle"?value="1"/></bean><!--在默認方式下?bean的id??固定為?processEngineConfiguration--><bean?id="processEngineConfiguration"class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"><!--引入上面配置好的?鏈接池--><property?name="dataSource"?ref="dataSource"/><!--actviti數據庫表在生成時的策略??true?-?如果數據庫中已經存在相應的表,那么直接使用,如果不存在,那么會創建--><property?name="databaseSchemaUpdate"?value="true"/></bean> </beans>4.3.2 SpringProcessEngineConfiguration
通過org.activiti.spring.SpringProcessEngineConfiguration 與Spring整合。
創建spring與activiti的整合配置文件:
activity-spring.cfg.xml(名稱可修改)
<beans?xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"?xmlns:mvc="http://www.springframework.org/schema/mvc" 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-3.1.xsd?http://www.springframework.org/schema/mvc?http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd?http://www.springframework.org/schema/context?http://www.springframework.org/schema/context/spring-context-3.1.xsd?http://www.springframework.org/schema/aop?http://www.springframework.org/schema/aop/spring-aop-3.1.xsd?http://www.springframework.org/schema/tx?http://www.springframework.org/schema/tx/spring-tx-3.1.xsd?"><!--?工作流引擎配置bean?--><bean?id="processEngineConfiguration"?class="org.activiti.spring.SpringProcessEngineConfiguration"><!--?數據源?--><property?name="dataSource"?ref="dataSource"?/><!--?使用spring事務管理器?--><property?name="transactionManager"?ref="transactionManager"?/><!--?數據庫策略?--><property?name="databaseSchemaUpdate"?value="drop-create"?/><!--?activiti的定時任務關閉?--><property?name="jobExecutorActivate"?value="false"?/></bean><!--?流程引擎?--><bean?id="processEngine"?class="org.activiti.spring.ProcessEngineFactoryBean"><property?name="processEngineConfiguration"?ref="processEngineConfiguration"?/></bean><!--?資源服務service?--><bean?id="repositoryService"?factory-bean="processEngine"factory-method="getRepositoryService"?/><!--?流程運行service?--><bean?id="runtimeService"?factory-bean="processEngine"factory-method="getRuntimeService"?/><!--?任務管理service?--><bean?id="taskService"?factory-bean="processEngine"factory-method="getTaskService"?/><!--?歷史管理service?--><bean?id="historyService"?factory-bean="processEngine"?factory-method="getHistoryService"?/><!--?用戶管理service?--><bean?id="identityService"?factory-bean="processEngine"?factory-method="getIdentityService"?/><!--?引擎管理service?--><bean?id="managementService"?factory-bean="processEngine"?factory-method="getManagementService"?/><!--?數據源?--><bean?id="dataSource"?class="org.apache.commons.dbcp.BasicDataSource"><property?name="driverClassName"?value="com.mysql.jdbc.Driver"?/><property?name="url"?value="jdbc:mysql://localhost:3306/activiti"?/><property?name="username"?value="root"?/><property?name="password"?value="mysql"?/><property?name="maxActive"?value="3"?/><property?name="maxIdle"?value="1"?/></bean><!--?事務管理器?--><bean?id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property?name="dataSource"?ref="dataSource"?/></bean><!--?通知?--><tx:advice?id="txAdvice"?transaction-manager="transactionManager"><tx:attributes></tx:attributes><!--?傳播行為?--><tx:method?name="save*"?propagation="REQUIRED"?/><tx:method?name="insert*"?propagation="REQUIRED"?/><tx:method?name="delete*"?propagation="REQUIRED"?/><tx:method?name="update*"?propagation="REQUIRED"?/><tx:method?name="find*"?propagation="SUPPORTS"?read-only="true"?/><tx:method?name="get*"?propagation="SUPPORTS"?read-only="true"?/></tx:attributes></tx:advice><!--?切面,根據具體項目修改切點配置?--><aop:config?proxy-target-class="true"><aop:advisor?advice-ref="txAdvice"??pointcut="execution(*?com.itheima.ihrm.service.impl.*.(..))"*?/></aop:config> </beans>創建processEngineConfiguration
ProcessEngineConfiguration?configuration?=?ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml")上邊的代碼要求activiti.cfg.xml中必須有一個processEngineConfiguration的bean
也可以使用下邊的方法,更改bean 的名字:
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String?resource,?String?beanName);4.4 工作流引擎創建
工作流引擎(ProcessEngine),相當于一個門面接口,通過ProcessEngineConfiguration創建processEngine,通過ProcessEngine創建各個service接口。
4.4.1 默認創建方式
將activiti.cfg.xml文件名及路徑固定,且activiti.cfg.xml文件中有 processEngineConfiguration的配置, 可以使用如下代碼創建processEngine:
//直接使用工具類?ProcessEngines,使用classpath下的activiti.cfg.xml中的配置創建processEngine ProcessEngine?processEngine?=?ProcessEngines.getDefaultProcessEngine(); System.out.println(processEngine);4.4.2 一般創建方式
//先構建ProcessEngineConfiguration ProcessEngineConfiguration?configuration?=?ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml"); //通過ProcessEngineConfiguration創建ProcessEngine,此時會創建數據庫 ProcessEngine?processEngine?=?configuration.buildProcessEngine();4.5 Servcie服務接口
Service是工作流引擎提供用于進行工作流部署、執行、管理的服務接口,我們使用這些接口可以就是操作服務對應的數據表
4.5.1 Service創建方式
通過ProcessEngine創建Service
方式如下:
RuntimeService?runtimeService?=?processEngine.getRuntimeService(); RepositoryService?repositoryService?=?processEngine.getRepositoryService(); TaskService?taskService?=?processEngine.getTaskService();4.5.2 Service總覽
簡單介紹:
RepositoryService
是activiti的資源管理類,提供了管理和控制流程發布包和流程定義的操作。使用工作流建模工具設計的業務流程圖需要使用此service將流程定義文件的內容部署到計算機。
除了部署流程定義以外還可以:查詢引擎中的發布包和流程定義。
暫停或激活發布包,對應全部和特定流程定義。暫停意味著它們不能再執行任何操作了,激活是對應的反向操作。獲得多種資源,像是包含在發布包里的文件, 或引擎自動生成的流程圖。
獲得流程定義的pojo版本, 可以用來通過java解析流程,而不必通過xml。
RuntimeService
Activiti的流程運行管理類。可以從這個服務類中獲取很多關于流程執行相關的信息
TaskService
Activiti的任務管理類。可以從這個類中獲取任務的信息。
HistoryService
Activiti的歷史管理類,可以查詢歷史信息,執行流程時,引擎會保存很多數據(根據配置),比如流程實例啟動時間,任務的參與者, 完成任務的時間,每個流程實例的執行路徑,等等。這個服務主要通過查詢功能來獲得這些數據。
ManagementService
Activiti的引擎管理類,提供了對 Activiti 流程引擎的管理和維護功能,這些功能不在工作流驅動的應用程序中使用,主要用于 Activiti 系統的日常維護。
五、Activiti入門
在本章內容中,我們來創建一個Activiti工作流,并啟動這個流程。
創建Activiti工作流主要包含以下幾步:
定義流程,按照BPMN的規范,使用流程定義工具,用流程符號把整個流程描述出來
部署流程,把畫好的流程定義文件,加載到數據庫中,生成表的數據
啟動流程,使用java代碼來操作數據庫表中的內容
5.1 流程符號
BPMN 2.0是業務流程建模符號2.0的縮寫。
它由Business Process Management Initiative這個非營利協會創建并不斷發展。作為一種標識,BPMN 2.0是使用一些符號來明確業務流程設計流程圖的一整套符號規范,它能增進業務建模時的溝通效率。
目前BPMN2.0是最新的版本,它用于在BPM上下文中進行布局和可視化的溝通。
接下來我們先來了解在流程設計中常見的 符號。
BPMN2.0的基本符合主要包含:
事件 Event
活動 Activity
活動是工作或任務的一個通用術語。一個活動可以是一個任務,還可以是一個當前流程的子處理流程;其次,你還可以為活動指定不同的類型。常見活動如下:
網關 GateWay
網關用來處理決策,有幾種常用網關需要了解:
排他網關 (x)
——只有一條路徑會被選擇。流程執行到該網關時,按照輸出流的順序逐個計算,當條件的計算結果為true時,繼續執行當前網關的輸出流;
如果多條線路計算結果都是 true,則會執行第一個值為 true 的線路。如果所有網關計算結果沒有true,則引擎會拋出異常。
排他網關需要和條件順序流結合使用,default 屬性指定默認順序流,當所有的條件不滿足時會執行默認順序流。
并行網關 (+)
——所有路徑會被同時選擇
拆分 —— 并行執行所有輸出順序流,為每一條順序流創建一個并行執行線路。
合并 —— 所有從并行網關拆分并執行完成的線路均在此等候,直到所有的線路都執行完成才繼續向下執行。
包容網關 (+)
—— 可以同時執行多條線路,也可以在網關上設置條件
拆分 —— 計算每條線路上的表達式,當表達式計算結果為true時,創建一個并行線路并繼續執行
合并 —— 所有從并行網關拆分并執行完成的線路均在此等候,直到所有的線路都執行完成才繼續向下執行。
事件網關 (+)
—— 專門為中間捕獲事件設置的,允許設置多個輸出流指向多個不同的中間捕獲事件。當流程執行到事件網關后,流程處于等待狀態,需要等待拋出事件才能將等待狀態轉換為活動狀態。
流向 Flow
流是連接兩個流程節點的連線。常見的流向包含以下幾種:
5.2 流程設計器使用
Activiti-Designer使用
Palette(畫板)
在idea中安裝插件即可使用,畫板中包括以下結點:
Connection—連接
Event—事件
Task—任務
Gateway—網關
Container—容器
Boundary event—邊界事件
Intermediate event- -中間事件
流程圖設計完畢保存生成.bpmn文件
新建流程(IDEA工具)
首先選中存放圖形的目錄(選擇resources下的bpmn目錄),點擊菜單:New -> BpmnFile,如圖:
彈出如下圖所示框,輸入evection 表示 出差審批流程:
起完名字evection后(默認擴展名為bpmn),就可以看到流程設計頁面,如圖所示:
左側區域是繪圖區,右側區域是palette畫板區域
鼠標先點擊畫板的元素即可在左側繪圖
繪制流程
使用滑板來繪制流程,通過從右側把圖標拖拽到左側的畫板,最終效果如下:
指定流程定義Key
流程定義key即流程定義的標識,通過properties視圖查看流程的key
指定任務負責人
在properties視圖指定每個任務結點的負責人,如:填寫出差申請的負責人為 zhangsan
經理審批負責人為 jerry
總經理審批負責人為 jack
財務審批負責人為 rose
六、流程操作
6.1 流程定義
概述
流程定義是線下按照bpmn2.0標準去描述 業務流程,通常使用idea中的插件對業務流程進行建模。IDEA插件介紹:IDEA 值得推薦的十幾款優秀插件,狂,拽,屌!
使用idea下的designer設計器繪制流程,并會生成兩個文件:.bpmn和.png
.bpmn文件
使用activiti-desinger設計業務流程,會生成.bpmn文件,上面我們已經創建好了bpmn文件
BPMN 2.0根節點是definitions節點。這個元素中,可以定義多個流程定義(不過我們建議每個文件只包含一個流程定義, 可以簡化開發過程中的維護難度)。
注意,definitions元素 最少也要包含xmlns 和 targetNamespace的聲明。targetNamespace可以是任意值,它用來對流程實例進行分類。
流程定義部分:定義了流程每個結點的描述及結點之間的流程流轉。
流程布局定義:定義流程每個結點在流程圖上的位置坐標等信息。
生成.png圖片文件
IDEA工具中的操作方式
1、修改文件后綴為xml
首先將evection.bpmn文件改名為evection.xml,如下圖:
evection.xml修改前的bpmn文件,效果如下:
2、使用designer設計器打開.xml文件
在evection.xml文件上面,點右鍵并選擇Diagrams菜單,再選擇Show BPMN2.0 Designer…
3、查看打開的文件
打開后,卻出現亂碼,如圖:
4、解決中文亂碼
1、打開Settings,找到File Encodings,把encoding的選項都選擇UTF-8
2、打開IDEA安裝路徑,找到如下的安裝目錄
根據自己所安裝的版本來決定,我使用的是64位的idea,所以在idea64.exe.vmoptions文件的最后一行追加一條命令: -Dfile.encoding=UTF-8
如下所示:
一定注意,不要有空格,否則重啟IDEA時會打不開,然后 重啟IDEA。
如果以上方法已經做完,還出現亂碼,就再修改一個文件,并在文件的末尾添加:-Dfile.encoding=UTF-8,然后重啟idea,如圖:
最后重新在evection.xml文件上面,點右鍵并選擇Diagrams菜單,再選擇Show BPMN2.0 Designer…,看到生成圖片,如圖:
到此,解決亂碼問題
5、導出為圖片文件
點擊Export To File的小圖標,打開如下窗口,注意填寫文件名及擴展名,選擇好保存圖片的位置:
然后,我們把png文件拷貝到resources下的bpmn目錄,并且把evection.xml改名為evection.bpmn。
6.2 流程定義部署
概述
將上面在設計器中定義的流程部署到activiti數據庫中,就是流程定義部署。
通過調用activiti的api將流程定義的bpmn和png兩個文件一個一個添加部署到activiti中,也可以將兩個文件打成zip包進行部署。
單個文件部署方式
分別將bpmn文件和png圖片文件部署。
public?class?ActivitiDemo?{/***?部署流程定義*/@Testpublic?void?testDeployment(){ //????????1、創建ProcessEngineProcessEngine?processEngine?=?ProcessEngines.getDefaultProcessEngine(); //????????2、得到RepositoryService實例RepositoryService?repositoryService?=?processEngine.getRepositoryService(); //????????3、使用RepositoryService進行部署Deployment?deployment?=?repositoryService.createDeployment().addClasspathResource("bpmn/evection.bpmn")?//?添加bpmn資源.addClasspathResource("bpmn/evection.png")??//?添加png資源.name("出差申請流程").deploy(); //????????4、輸出部署信息System.out.println("流程部署id:"?+?deployment.getId());System.out.println("流程部署名稱:"?+?deployment.getName());} }執行此操作后activiti會將上邊代碼中指定的bpm文件和圖片文件保存在activiti數據庫。
壓縮包部署方式
將evection.bpmn和evection.png壓縮成zip包。
@Testpublic?void?deployProcessByZip()?{//?定義zip輸入流InputStream?inputStream?=?this.getClass().getClassLoader().getResourceAsStream("bpmn/evection.zip");ZipInputStream?zipInputStream?=?new?ZipInputStream(inputStream);//?獲取repositoryServiceRepositoryService?repositoryService?=?processEngine.getRepositoryService();//?流程部署Deployment?deployment?=?repositoryService.createDeployment().addZipInputStream(zipInputStream).deploy();System.out.println("流程部署id:"?+?deployment.getId());System.out.println("流程部署名稱:"?+?deployment.getName());}執行此操作后activiti會將上邊代碼中指定的bpm文件和圖片文件保存在activiti數據庫。
操作數據表
流程定義部署后操作activiti的3張表如下:
act_re_deployment 流程定義部署表,每部署一次增加一條記錄
act_re_procdef 流程定義表,部署每個新的流程定義都會在這張表中增加一條記錄
act_ge_bytearray 流程資源表
接下來我們來看看,寫入了什么數據:
SELECT?*?FROM?act_re_deployment?#流程定義部署表,記錄流程部署信息結果:
SELECT?*?FROM?act_re_procdef?#流程定義表,記錄流程定義信息結果:
注意,KEY 這個字段是用來唯一識別不同流程的關鍵字
SELECT?*?FROM?act_ge_bytearray?#資源表結果:
注意:
act_re_deployment和act_re_procdef一對多關系,一次部署在流程部署表生成一條記錄,但一次部署可以部署多個流程定義,每個流程定義在流程定義表生成一條記錄。每一個流程定義在act_ge_bytearray會存在兩個資源記錄,bpmn和png。
建議:一次部署一個流程,這樣部署表和流程定義表是一對一有關系,方便讀取流程部署及流程定義信息。
6.3 啟動流程實例
流程定義部署在activiti后就可以通過工作流管理業務流程了,也就是說上邊部署的出差申請流程可以使用了。
針對該流程,啟動一個流程表示發起一個新的出差申請單,這就相當于java類與java對象的關系,類定義好后需要new創建一個對象使用,當然可以new多個對象。對于請出差申請流程,張三發起一個出差申請單需要啟動一個流程實例,出差申請單發起一個出差單也需要啟動一個流程實例。
代碼如下:
/***?啟動流程實例*/@Testpublic?void?testStartProcess(){ //????????1、創建ProcessEngineProcessEngine?processEngine?=?ProcessEngines.getDefaultProcessEngine(); //????????2、獲取RunTimeServiceRuntimeService?runtimeService?=?processEngine.getRuntimeService(); //????????3、根據流程定義Id啟動流程ProcessInstance?processInstance?=?runtimeService.startProcessInstanceByKey("myEvection"); //????????輸出內容System.out.println("流程定義id:"?+?processInstance.getProcessDefinitionId());System.out.println("流程實例id:"?+?processInstance.getId());System.out.println("當前活動Id:"?+?processInstance.getActivityId());}輸出內容如下:
操作數據表
act_hi_actinst 流程實例執行歷史
act_hi_identitylink 流程的參與用戶歷史信息
act_hi_procinst 流程實例歷史信息
act_hi_taskinst 流程任務歷史信息
act_ru_execution 流程執行信息
act_ru_identitylink 流程的參與用戶信息
act_ru_task 任務信息
6.4 任務查詢
流程啟動后,任務的負責人就可以查詢自己當前需要處理的任務,查詢出來的任務都是該用戶的待辦任務。
/***?查詢當前個人待執行的任務*/@Testpublic?void?testFindPersonalTaskList()?{ //????????任務負責人String?assignee?=?"zhangsan";ProcessEngine?processEngine?=?ProcessEngines.getDefaultProcessEngine(); //????????創建TaskServiceTaskService?taskService?=?processEngine.getTaskService(); //????????根據流程key?和?任務負責人?查詢任務List<Task>?list?=?taskService.createTaskQuery().processDefinitionKey("myEvection")?//流程Key.taskAssignee(assignee)//只查詢該任務負責人的任務.list();for?(Task?task?:?list)?{System.out.println("流程實例id:"?+?task.getProcessInstanceId());System.out.println("任務id:"?+?task.getId());System.out.println("任務負責人:"?+?task.getAssignee());System.out.println("任務名稱:"?+?task.getName());}}輸出結果如下:
流程實例id:2501 任務id:2505 任務負責人:zhangsan 任務名稱:創建出差申請6.5 流程任務處理
任務負責人查詢待辦任務,選擇任務進行處理,完成任務。
//?完成任務@Testpublic?void?completTask(){ //????????獲取引擎ProcessEngine?processEngine?=?ProcessEngines.getDefaultProcessEngine(); //????????獲取taskServiceTaskService?taskService?=?processEngine.getTaskService();//????????根據流程key?和?任務的負責人?查詢任務 //????????返回一個任務對象Task?task?=?taskService.createTaskQuery().processDefinitionKey("myEvection")?//流程Key.taskAssignee("zhangsan")??//要查詢的負責人.singleResult();//????????完成任務,參數:任務idtaskService.complete(task.getId());}6.6 流程定義信息查詢
查詢流程相關信息,包含流程定義,流程部署,流程定義版本
/***?查詢流程定義*/@Testpublic?void?queryProcessDefinition(){//????????獲取引擎ProcessEngine?processEngine?=?ProcessEngines.getDefaultProcessEngine(); //????????repositoryServiceRepositoryService?repositoryService?=?processEngine.getRepositoryService(); //????????得到ProcessDefinitionQuery?對象ProcessDefinitionQuery?processDefinitionQuery?=?repositoryService.createProcessDefinitionQuery(); //??????????查詢出當前所有的流程定義 //??????????條件:processDefinitionKey =evection //??????????orderByProcessDefinitionVersion?按照版本排序 //????????desc倒敘 //????????list?返回集合List<ProcessDefinition>?definitionList?=?processDefinitionQuery.processDefinitionKey("myEvection").orderByProcessDefinitionVersion().desc().list(); //??????輸出流程定義信息for?(ProcessDefinition?processDefinition?:?definitionList)?{System.out.println("流程定義?id="+processDefinition.getId());System.out.println("流程定義?name="+processDefinition.getName());System.out.println("流程定義?key="+processDefinition.getKey());System.out.println("流程定義?Version="+processDefinition.getVersion());System.out.println("流程部署ID?="+processDefinition.getDeploymentId());}}輸出結果:
流程定義id:myEvection:1:4 流程定義名稱:出差申請單 流程定義key:myEvection 流程定義版本:16.7 流程刪除
public?void?deleteDeployment()?{//?流程部署idString?deploymentId?=?"1";ProcessEngine?processEngine?=?ProcessEngines.getDefaultProcessEngine();//?通過流程引擎獲取repositoryServiceRepositoryService?repositoryService?=?processEngine.getRepositoryService();//刪除流程定義,如果該流程定義已有流程實例啟動則刪除時出錯repositoryService.deleteDeployment(deploymentId);//設置true?級聯刪除流程定義,即使該流程有流程實例啟動也可以刪除,設置為false非級別刪除方式,如果流程//repositoryService.deleteDeployment(deploymentId,?true);}說明:
使用repositoryService刪除流程定義,歷史表信息不會被刪除
如果該流程定義下沒有正在運行的流程,則可以用普通刪除。
如果該流程定義下存在已經運行的流程,使用普通刪除報錯,可用級聯刪除方法將流程及相關記錄全部刪除。
先刪除沒有完成流程節點,最后就可以完全刪除流程定義信息
項目開發中級聯刪除操作一般只開放給超級管理員使用.
6.8 流程資源下載
現在我們的流程資源文件已經上傳到數據庫了,如果其他用戶想要查看這些資源文件,可以從數據庫中把資源文件下載到本地。
解決方案有:
jdbc對blob類型,clob類型數據讀取出來,保存到文件目錄
使用activiti的api來實現
使用commons-io.jar 解決IO的操作
引入commons-io依賴包
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version> </dependency>通過流程定義對象獲取流程定義資源,獲取bpmn和png
import?org.apache.commons.io.IOUtils;@Testpublic?void?deleteDeployment(){ //????????獲取引擎ProcessEngine?processEngine?=?ProcessEngines.getDefaultProcessEngine(); //????????獲取repositoryServiceRepositoryService?repositoryService?=?processEngine.getRepositoryService(); //????????根據部署id?刪除部署信息,如果想要級聯刪除,可以添加第二個參數,truerepositoryService.deleteDeployment("1");}public?void??queryBpmnFile()?throws?IOException?{ //????????1、得到引擎ProcessEngine?processEngine?=?ProcessEngines.getDefaultProcessEngine(); //????????2、獲取repositoryServiceRepositoryService?repositoryService?=?processEngine.getRepositoryService(); //??????? 3、得到查詢器:ProcessDefinitionQuery,設置查詢條件,得到想要的流程定義ProcessDefinition?processDefinition?=?repositoryService.createProcessDefinitionQuery().processDefinitionKey("myEvection").singleResult(); //????????4、通過流程定義信息,得到部署IDString?deploymentId?=?processDefinition.getDeploymentId(); //????????5、通過repositoryService的方法,實現讀取圖片信息和bpmn信息 //????????png圖片的流InputStream?pngInput?=?repositoryService.getResourceAsStream(deploymentId,?processDefinition.getDiagramResourceName()); //????????bpmn文件的流InputStream?bpmnInput?=?repositoryService.getResourceAsStream(deploymentId,?processDefinition.getResourceName()); //????????6、構造OutputStream流File?file_png?=?new?File("d:/evectionflow01.png");File?file_bpmn?=?new?File("d:/evectionflow01.bpmn");FileOutputStream?bpmnOut?=?new?FileOutputStream(file_bpmn);FileOutputStream?pngOut?=?new?FileOutputStream(file_png); //????????7、輸入流,輸出流的轉換IOUtils.copy(pngInput,pngOut);IOUtils.copy(bpmnInput,bpmnOut); //????????8、關閉流pngOut.close();bpmnOut.close();pngInput.close();bpmnInput.close();}說明:
deploymentId為流程部署ID
resource_name為act_ge_bytearray表中NAME_列的值
使用repositoryService的getDeploymentResourceNames方法可以獲取指定部署下得所有文件的名稱
使用repositoryService的getResourceAsStream方法傳入部署ID和資源圖片名稱可以獲取部署下指定名稱文件的輸入流
最后的將輸入流中的圖片資源進行輸出。
6.9 流程歷史信息的查看
即使流程定義已經刪除了,流程執行的歷史信息通過前面的分析,依然保存在activiti的act_hi_*相關的表中。所以我們還是可以查詢流程執行的歷史信息,可以通過HistoryService來查看相關的歷史記錄。
/***?查看歷史信息*/@Testpublic?void?findHistoryInfo(){ //??????獲取引擎ProcessEngine?processEngine?=?ProcessEngines.getDefaultProcessEngine(); //????????獲取HistoryServiceHistoryService?historyService?=?processEngine.getHistoryService(); //????????獲取?actinst表的查詢對象HistoricActivityInstanceQuery?instanceQuery?=?historyService.createHistoricActivityInstanceQuery(); //????????查詢 actinst表,條件:根據 InstanceId 查詢 //????????instanceQuery.processInstanceId("2501"); //????????查詢 actinst表,條件:根據 DefinitionId 查詢instanceQuery.processDefinitionId("myEvection:1:4"); //????????增加排序操作,orderByHistoricActivityInstanceStartTime?根據開始時間排序?asc?升序instanceQuery.orderByHistoricActivityInstanceStartTime().asc(); //????????查詢所有內容List<HistoricActivityInstance>?activityInstanceList?=?instanceQuery.list(); //????????輸出for?(HistoricActivityInstance?hi?:?activityInstanceList)?{System.out.println(hi.getActivityId());System.out.println(hi.getActivityName());System.out.println(hi.getProcessDefinitionId());System.out.println(hi.getProcessInstanceId());System.out.println("<==========================>");}}總結
基本功能介紹以及完成了,如果還需要更加高級的功能比如掛起、激活流程實例、流程變量等請參考
https://andyoung.blog.csdn.net/article/details/118345330
工作流引擎 Activiti 與 Spring boot 結合會是開發跟簡單,不如來看下
https://andyoung.blog.csdn.net/article/details/118372175
來源:https://blog.csdn.net/agonie201218/
article/details/118198535
精彩推薦
總結
以上是生活随笔為你收集整理的工作流引擎 Activiti 保姆级教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: catia 安装打开闪退_安装CATIA
- 下一篇: 简单的LaTeX数学表达式教程