SpringBoot--實戰(zhàn)開發(fā)--activiti6(六十一)

一、Activiti6簡介

??Activiti是由Alfresco軟件在2010年5月17日發(fā)布的業(yè)務(wù)流程管理(BPM)框架,它是覆蓋了業(yè)務(wù)流程管理,工作流,服務(wù)協(xié)作等領(lǐng)域的一個開源,靈活的,易擴展的可執(zhí)行流程語言框架。
官網(wǎng):https://www.activiti.org/before-you-start

二、Maven依賴

<!--activiti-->
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter-basic</artifactId>
    <version>6.0.0</version>
</dependency>
<!--jdbc-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mysql-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

三、添加Processes目錄

SpringBoot集成activiti默認會從classpath下的processes目錄下讀取流程定義文件,所以需要在src/main/resources目錄下添加processes目錄,并在目錄中創(chuàng)建流程文件,添加目錄后,目錄結(jié)構(gòu)變?yōu)椋?/p>

如果沒有processes目錄,則需要修改配置spring.activiti.process-definition-location-prefix,指定流程文件存放目錄。
Spring集成Activiti6默認支持.bpmn20.xml和.bpmn格式的流程定義文件,修改支持的文件格式,通過配置spring.activiti.process-definition-location-suffixes修改:

server.port=8888
# 自動檢查、部署流程定義文件
spring.activiti.check-process-definitions=true
# 流程定義文件存放目錄
spring.activiti.process-definition-location-prefix=classpath:/processes
# 流程文件格式后綴 **.bpmn20.xml **.bpmn 自定義(**.bpmn.zip)
#spring.activiti.process-definition-location-suffixes=**.bpmn.zip

# JDBC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
spring.datasource.username=root
spring.datasource.password=1234
# 數(shù)據(jù)庫連接池
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
# 最小空閑連接數(shù)量
spring.datasource.hikari.minimum-idle=5
# 連接池最大連接數(shù),默認是10
spring.datasource.hikari.maximum-pool-size=15
#此屬性控制從池返回的連接的默認自動提交行為,默認值:true
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=UserHikariCP
# 此屬性控制池中連接的最長生命周期,值0表示無限生命周期,默認1800000即30分鐘
spring.datasource.hikari.max-lifetime=1800000
#數(shù)據(jù)庫連接超時時間,默認30秒,即30000
spring.datasource.hikari.connection-timeout=30000
#指定校驗連接合法性執(zhí)行的sql語句
spring.datasource.hikari.connection-test-query=SELECT 1

# 配置JPA,用于初始化數(shù)據(jù)結(jié)構(gòu)
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.show-sql=true
# 每次應(yīng)用啟動不檢查Activiti數(shù)據(jù)表是否存在及版本號是否匹配,提升應(yīng)用啟動速度
spring.activiti.database-schema-update=false

spring.activiti.database-schema-update
配置項可以設(shè)置流程引擎啟動和關(guān)閉時數(shù)據(jù)庫執(zhí)行的策略,可以選擇四種模式
false:false為默認值,設(shè)置為該值后,Activiti在啟動時,會對比數(shù)據(jù)庫表中保存的版本,如果沒有表或者版本不匹配時,將在啟動時拋出異常。
true:設(shè)置為該值后,Activiti會對數(shù)據(jù)庫中所有的表進行更新,如果表不存在,則Activiti會自動創(chuàng)建。
create-drop:Activiti啟動時,會執(zhí)行數(shù)據(jù)庫表的創(chuàng)建操作,在Activiti關(guān)閉時,執(zhí)行數(shù)據(jù)庫表的刪除操作。
drop-create:Activiti啟動時,執(zhí)行數(shù)據(jù)庫表的刪除操作在Activiti關(guān)閉時,會執(zhí)行數(shù)據(jù)庫表的創(chuàng)建操作。
spring.activiti.history-level 配置項

對于歷史數(shù)據(jù),保存到何種粒度,Activiti提供了history-level屬性對其進行配置。history-level屬性有點像log4j的日志輸出級別,該屬性有以下四個值:
none:不保存任何的歷史數(shù)據(jù),因此,在流程執(zhí)行過程中,這是最高效的。
activity:級別高于none,保存流程實例與流程行為,其他數(shù)據(jù)不保存。
audit:除activity級別會保存的數(shù)據(jù)外,還會保存全部的流程任務(wù)及其屬性。audit為history的默認值。
full:保存歷史數(shù)據(jù)的最高級別,除了會保存audit級別的數(shù)據(jù)外,還會保存其他全部流程相關(guān)的細節(jié)數(shù)據(jù),包括一些流程參數(shù)等。

四、數(shù)據(jù)庫表

https://lucaslz.gitbooks.io/activiti-5-22/content/actprocdef_info_ff08_liu_cheng_ding_yi_kuo_zhan_bi.html

Activiti的后臺是有數(shù)據(jù)庫的支持,所有的表都以ACT_開頭。
第二部分是表示表的用途的兩個字母標識。用途也和服務(wù)的API對應(yīng)。
act_re_: 'RE’表示repository。 這個前綴的表包含了流程定義和流程靜態(tài)資源(圖片,規(guī)則,等等)。
act_ru_
: 'RU’表示runtime。 這些運行時的表,包含流程實例,任務(wù),變量,異步任務(wù),等運行中的數(shù)據(jù)。 Activiti只在流程實例執(zhí)行過程中保存這些數(shù)據(jù),在流程結(jié)束時就會刪除這些記錄。 這樣運行時表可以一直很小速度很快。
act_id_: 'ID’表示identity。 這些表包含身份信息,比如用戶,組等等。
act_hi_
: 'HI’表示history。 這些表包含歷史數(shù)據(jù),比如歷史流程實例,變量,任務(wù)等等。
act_ge_*: 通用數(shù)據(jù),用于不同場景下,如存放資源文件。
資源庫流程規(guī)則表
act_re_deployment 部署信息表
act_re_model 流程設(shè)計模型部署表
act_re_procdef 流程定義數(shù)據(jù)表
運行時數(shù)據(jù)庫表
act_ru_execution 運行時流程執(zhí)行實例表
act_ru_identitylink 運行時流程人員表,主要存儲任務(wù)節(jié)點與參與者的相關(guān)信息
act_ru_task 運行時任務(wù)節(jié)點表
act_ru_variable 運行時流程變量數(shù)據(jù)表
歷史數(shù)據(jù)庫表
act_hi_actinst 歷史節(jié)點表
act_hi_attachment 歷史附件表
act_hi_comment 歷史意見表
act_hi_identitylink 歷史流程人員表
act_hi_detail 歷史詳情表,提供歷史變量的查詢
act_hi_procinst 歷史流程實例表
act_hi_taskinst 歷史任務(wù)實例表
act_hi_varinst 歷史變量表
組織機構(gòu)表
act_id_group 用戶組信息表
act_id_info 用戶擴展信息表
act_id_membership 用戶與用戶組對應(yīng)信息表
act_id_user 用戶信息表
這四張表很常見,基本的組織機構(gòu)管理,關(guān)于用戶認證方面建議還是自己開發(fā)一套,組件自帶的功能太簡單,使用中有很多需求難以滿足
通用數(shù)據(jù)表
act_ge_bytearray 二進制數(shù)據(jù)表
act_ge_property 屬性數(shù)據(jù)表存儲整個流程引擎級別的數(shù)據(jù),初始化表結(jié)構(gòu)時,會默認插入三條記錄

生成的表結(jié)構(gòu)

五、工作流常用service

RepositoryService
Activiti 中每一個不同版本的業(yè)務(wù)流程的定義都需要使用一些定義文件,部署文件和支持數(shù)據(jù) ( 例如 BPMN2.0 XML 文件,表單定義文件,流程定義圖像文件等 ),這些文件都存儲在 Activiti 內(nèi)建的 Repository 中。Repository Service 提供了對 repository 的存取服務(wù)。

RuntimeService
Activiti 中,每當一個流程定義被啟動一次之后,都會生成一個相應(yīng)的流程對象實例。Runtime Service 提供了啟動流程、查詢流程實例、設(shè)置獲取流程實例變量等功能。此外它還提供了對流程部署,流程定義和流程實例的存取服務(wù)。

TaskService
在 Activiti 中業(yè)務(wù)流程定義中的每一個執(zhí)行節(jié)點被稱為一個 Task,對流程中的數(shù)據(jù)存取,狀態(tài)變更等操作均需要在 Task 中完成。Task Service 提供了對用戶 Task 和 Form相關(guān)的操作。它提供了運行時任務(wù)查詢、領(lǐng)取、完成、刪除以及變量設(shè)置等功能。

IdentityService
Activiti 中內(nèi)置了用戶以及組管理的功能,必須使用這些用戶和組的信息才能獲取到相應(yīng)的 Task。Identity Service 提供了對 Activiti 系統(tǒng)中的用戶和組的管理功能。

ManagementService
Management Service 提供了對 Activiti 流程引擎的管理和維護功能,這些功能不在工作流驅(qū)動的應(yīng)用程序中使用,主要用于 Activiti 系統(tǒng)的日常維護。

HistoryService
History Service 用于獲取正在運行或已經(jīng)完成的流程實例的信息,與 Runtime Service 中獲取的流程信息不同,歷史信息包含已經(jīng)持久化存儲的永久信息,并已經(jīng)被針對查詢優(yōu)化。

FormService
Activiti 中的流程和狀態(tài) Task 均可以關(guān)聯(lián)業(yè)務(wù)相關(guān)的數(shù)據(jù)。通過使用 Form Service可以存取啟動和完成任務(wù)所需的表單數(shù)據(jù)并且根據(jù)需要來渲染表單。

DynamicBpmnService
一個新增的服務(wù),用于動態(tài)修改流程中的一些參數(shù)信息等,是引擎中的一個輔助的服務(wù)

FormRepositoryService
部署表單等

五、安裝activiti

  1. 下載
    https://www.activiti.org
    文檔地址:https://www.activiti.org/userguide/

    下載

    下載

  2. 布署
    需要將以下war包,布署到tomcat中。


    布署

    解壓之后,將wars文件夾下的activiti-admin.war、activiti-app.war拷貝到tomcat的webapps下:

cp activiti-6.0.0/wars/activiti-admin.war apache-tomcat-8.0.36/webapps/
cp activiti-6.0.0/wars/activiti-app.war apache-tomcat-8.0.36/webapps/

通過tomcat的bin目錄下startup.sh啟動tomcat:
瀏覽器訪問http://192.168.11.126:8080/activiti-app訪問activiti,初始賬號密碼是admin/test

六、idea安裝Activiti插件

  1. 安裝插件
    點擊菜單【File】-->【Settings...】打開【Settings】窗口。
    點擊左側(cè)【Plugins】按鈕,在右側(cè)輸出"actiBPM",點擊下面的【Search in repositories】鏈接會打開【Browse Repositories】窗口。


    搜索插件

    安裝插件
  2. 創(chuàng)建BPMN文件


    創(chuàng)建BPMN文件

    創(chuàng)建BPMN文件
簡單流程
  1. 查看流程圖代碼


    重構(gòu)

    重構(gòu)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1569472781925" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
  <process id="myProcess_1" isClosed="false" isExecutable="true" processType="None">
    <startEvent id="_2" name="開始"/>
    <endEvent id="_3" name="結(jié)束"/>
    <userTask activiti:exclusive="true" id="_4" name="學生請假"/>
    <userTask activiti:exclusive="true" id="_5" name="老師申批"/>
    <sequenceFlow id="_6" sourceRef="_2" targetRef="_4"/>
    <sequenceFlow id="_7" sourceRef="_4" targetRef="_5"/>
    <sequenceFlow id="_8" sourceRef="_5" targetRef="_3"/>
  </process>
  <bpmndi:BPMNDiagram documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
    <bpmndi:BPMNPlane bpmnElement="myProcess_1">
      <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
        <omgdc:Bounds height="32.0" width="32.0" x="190.0" y="55.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
        <omgdc:Bounds height="32.0" width="32.0" x="195.0" y="335.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
        <omgdc:Bounds height="55.0" width="85.0" x="160.0" y="140.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
        <omgdc:Bounds height="55.0" width="85.0" x="165.0" y="225.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="_2" targetElement="_4">
        <omgdi:waypoint x="206.0" y="87.0"/>
        <omgdi:waypoint x="206.0" y="140.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_7" id="BPMNEdge__7" sourceElement="_4" targetElement="_5">
        <omgdi:waypoint x="205.0" y="195.0"/>
        <omgdi:waypoint x="205.0" y="225.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_5" targetElement="_3">
        <omgdi:waypoint x="211.0" y="280.0"/>
        <omgdi:waypoint x="211.0" y="335.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

  1. 生成圖片


    生成圖片

    生成圖片

    生成圖片

    生成圖片
  2. 部署流程
    部署之后就可以在act_re_procdef表中看到對相應(yīng)的流程信息


    獲取流程ID
    /**
     * 數(shù)據(jù)存儲服務(wù)
     */
    @Resource
    private RepositoryService repositoryService;
    /**
     * 1. 部署流程
     * 部署之后就可以在act_re_procdef表中看到對相應(yīng)的流程信息
     */
    @Test
    public void deployProcess() {
        DeploymentBuilder builder = repositoryService.createDeployment();
        // bpmn文件的名稱
        builder.addClasspathResource("processes/test.bpmn");
        // 設(shè)置key
        builder.key("myProcess_1");
        // 設(shè)定名稱,也可以在圖中定義
        builder.name("請假流程");
        // 進行布署
        Deployment deployment = builder.deploy();
        // 獲取deployment的ID
        String deploymentId = deployment.getId();
        // 根據(jù)deploymentId來獲取流程定義對象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploymentId).singleResult();
        log.info("流程定義文件 [{}] , 流程ID [{}]", processDefinition.getName(), processDefinition.getId());
    }

注意:
默認,必須為.bpmn文件或 bpmn20.xml文件,可通過配置修改。

  1. 啟動流程
    /**
     * 運行服務(wù)
     */
    @Resource
    private RuntimeService runtimeService;
    /**
     * 2. 啟動流程
     * 啟動流程之后就會有相應(yīng)的任務(wù)產(chǎn)生,存在act_ru_task表中,可以查看任務(wù)節(jié)點
     */
    @Test
    public void startProcess() {
        // 流程的名稱,也可以使用ByID來啟動流程
        // key為流程圖的ID,
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_1");
        log.info("流程啟動成功,流程id:" + processInstance.getId());
    }
  1. 查看任務(wù)節(jié)點


    指定用戶
    /**
     * 任務(wù)服務(wù)
     */
    @Resource
    private TaskService taskService;
    /**
     * 3. 查看任務(wù)節(jié)點
     */
    @Test
    public void queryTask() {
        //  根據(jù)assignee(代理人)查詢?nèi)蝿?wù)
        String assignee = "admin";
        // 注意所在包。
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(assignee).list();

        int size = tasks.size();
        for (int i = 0; i < size; i++) {
            Task task = tasks.get(i);
        }
        // 首次運行的時候這個沒有輸出,因為第一次運行的時候掃描act_ru_task的表里面是空的,
        // 但第一次運行完成之后里面會添加一條記錄,之后每次運行里面都會添加一條記錄
        for (Task task : tasks) {
            log.info("taskId:" + task.getId() +
                    ",taskName:" + task.getName() +
                    ",assignee:" + task.getAssignee() +
                    ",createTime:" + task.getCreateTime());
        }
    }
運行結(jié)果
  1. 完成流程(審核通過)


    審批數(shù)據(jù)
    /**
     * 4. 完成流程,admin通過審核
     */
    @Test
    public void completeTasks() {
        // 審批后,任務(wù)列表數(shù)據(jù)減少
        Map<String,Object> vars = new HashMap<>();
        //  按配置的任務(wù)id填寫
        vars.put("_4","true");
        taskService.complete("2505", vars);

    }

再次進行查詢admin的任務(wù),已經(jīng)有了。act_ru_task表中已沒有該記錄。

  1. 申請駁回
  //審批不通過,結(jié)束流程
    runtimeService.deleteProcessInstance(vacationAudit.getProcessInstanceId(), auditId);

  1. 轉(zhuǎn)辦
  1. 簽收

  1. 歷史任務(wù)查詢
    /**
     * 歷史服務(wù)
     */
    @Resource
    private HistoryService historyService;
     /**
     * 歷史任務(wù)查詢
     *
     * @throws ParseException
     */
    @Test
    public void findHistoricTasks() throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery() // 創(chuàng)建歷史任務(wù)實例查詢
                .taskAssignee("admin") // 指定辦理人
//                .finished() // 查詢已經(jīng)完成的任務(wù)
                .list();
        for (HistoricTaskInstance hti : list) {
            log.info("任務(wù)ID:" + hti.getId());
            log.info("流程實例ID:" + hti.getProcessInstanceId());
            log.info("班里人:" + hti.getAssignee());
            log.info("創(chuàng)建時間:" + sdf.format(hti.getCreateTime()));
            log.info("結(jié)束時間:" + sdf.format(hti.getEndTime()));
            log.info("===========================");
        }
    }

歷史活動查詢

    /**
     * 歷史活動查詢
     * 指定流程實例id,啟動流程時,獲取的實例ID
     */
    @Test
    public void historyActInstanceList(){
        List<HistoricActivityInstance> list = historyService // 歷史任務(wù)Service
                .createHistoricActivityInstanceQuery() // 創(chuàng)建歷史活動實例查詢
                .processInstanceId("2501") // 指定流程實例id
//                .finished() // 查詢已經(jīng)完成的任務(wù)
                .list();
        for (HistoricActivityInstance hai : list) {
            log.info("任務(wù)ID:" + hai.getId());
            log.info("流程實例ID:" + hai.getProcessInstanceId());
           log.info("活動名稱:" + hai.getActivityName());
           log.info("辦理人:" + hai.getAssignee());
           log.info("開始時間:" + hai.getStartTime());
           log.info("結(jié)束時間:" + hai.getEndTime());
           log.info("===========================");
        }
    }

八、常見問題:

  1. caused by: java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
    解決:
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
  1. 不能自動創(chuàng)建表結(jié)構(gòu)
  2. 從mysql-connector-java 5.x 到 6.x,nullCatalogMeansCurrent屬性由原來的默認true改為了false。
  3. true 使用指定的數(shù)據(jù)庫進行查詢。優(yōu)先取當前傳入的數(shù)據(jù)庫名,其次取當前鏈接的數(shù)據(jù)庫名。
    將連接字符串追加:
    nullCatalogMeansCurrent=true
jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
  1. idea activiti報錯no processes deployed with key 'leave'
    出錯原因:processes目錄下
    leave.xml改為leave.bpmn問題解決
    那就是 activiti 的模版必須以 bpmn20.xml 或者 bpmn結(jié)尾
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容