<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一行代碼,實(shí)現(xiàn)請(qǐng)假審批流程(Java版)

          共 36282字,需瀏覽 73分鐘

           ·

          2024-10-25 11:44

          來源:juejin.cn/post/7401773397312782399

          • 首先畫一個(gè)流程圖
          • 測(cè)試流程圖
          • activiti 項(xiàng)目基礎(chǔ)配置
          • activiti 工作流引擎數(shù)據(jù)庫(kù)設(shè)計(jì)
          • 工作流引擎API 介紹
          • 什么是BPMN流程圖
          • 工作流引擎同類對(duì)比
          • 繼續(xù)學(xué)習(xí)方向
          • 總結(jié)

          工作流審批功能是辦公OA系統(tǒng)核心能力,如果讓你設(shè)計(jì)一個(gè)工作流審批系統(tǒng),你會(huì)嗎?千萬不要小瞧OA內(nèi)部系統(tǒng)的復(fù)雜性,大家可以頭腦風(fēng)暴思考一下實(shí)現(xiàn)方案。

          要明白工作流審批涉及多個(gè)用戶的任務(wù)流轉(zhuǎn),多個(gè)流程分支跳轉(zhuǎn),雖然是辦公內(nèi)部系統(tǒng),但是這個(gè)系統(tǒng)并不簡(jiǎn)單如果沒有強(qiáng)大的工作流引擎,難以高效擴(kuò)展舊流程,難以增加新流程,工作流審批將成為公司所有人的噩夢(mèng)

          但是在使用 activiti開源工作流引擎后,一切痛苦與噩夢(mèng)均煙消云散~

          activiti 支持新增流程非常簡(jiǎn)單,只需要兩步

          • 畫個(gè)流程圖
          • 搭配前端頁(yè)面

          首先畫一個(gè)流程圖

          將文章開頭的需求,轉(zhuǎn)化為 activiti 流程圖,使用Idea 安裝 actiBPM 插件,創(chuàng)建該流程圖,文件命名apply.mpmn,實(shí)現(xiàn)請(qǐng)假流程的二級(jí)審批能力

          • 一級(jí)主管審批
          • 超過3天,二級(jí)主管審批。
          img

          測(cè)試流程圖

          首先創(chuàng)建工作流引擎,部署流程圖

          //創(chuàng)建工作流引擎
          ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();

          //RepositoryService用于部署流程圖
          RepositoryService repositoryService = engine.getRepositoryService();

          //部署請(qǐng)假流程圖
          repositoryService.createDeployment().addClasspathResource("processes/apply.bpmn").deploy();

          部署流程圖,這部分工作一般放在工作流的后臺(tái)系統(tǒng),開發(fā)創(chuàng)建好流程圖以后,上傳部署到系統(tǒng)中。無需開發(fā)修改代碼

          repositoryService.createDeployment().addClasspathResource("processes/apply.bpmn").deploy();這行代碼負(fù)責(zé)部署流程圖到流程引擎。 工作流引擎會(huì)解析該流程圖文件,創(chuàng)建流程模版,接下來就可以在使用該流程模版,發(fā)起流程實(shí)例了。

          員工zhang3,發(fā)起新流程,設(shè)置審批人

          Map<String, Object> variableMap = new HashMap<>();
          variableMap.put("applyUser""zhang3");
          variableMap.put("supervisor""li4");
          variableMap.put("upperSupervisor""wang5");

          員工zhang3 提出請(qǐng)假申請(qǐng),在發(fā)起新流程時(shí),通過OA其他系統(tǒng),查到zhang3的一級(jí)主管是li4,二級(jí)主管是wang5,于是設(shè)置上審批人。 有人疑問,以下變量applyUser等,是系統(tǒng)默認(rèn)的,還是在哪里指定的? 在創(chuàng)建主管審批節(jié)點(diǎn)時(shí),指定審批人變量 ${applyUser}

          img

          想象一下,如果請(qǐng)假類的流程均可能需要一二級(jí)主管審批,是不是可以在發(fā)起流程時(shí),統(tǒng)一填充一二級(jí)主管 審批人變量。這部分代碼是不是就是通用的,新增流程時(shí)無需二次修改了。

          發(fā)起一個(gè)新流程

          ProcessInstance instance = runtimeService.startProcessInstanceByKey("apply_processor_1", variableMap);

          如上代碼指定了全流程審批人,發(fā)起了一個(gè)新流程。 有人會(huì)疑問apply_processor_1 是什么?在哪里指定的,這是流程模版的 Key,在使用Idea插件畫流程圖時(shí),需要指定流程圖的Key

          img

          申請(qǐng)人設(shè)置請(qǐng)假天數(shù)

          TaskService taskService = engine.getTaskService();
          Task firstTask = taskService.createTaskQuery().taskAssignee("zhang3").singleResult();

          taskService.complete(firstTask.getId(), Maps.newHashMap("day", 4));

          創(chuàng)建并開啟流程實(shí)例后,工作流引擎相當(dāng)于幫你執(zhí)行了 流程圖的 開始節(jié)點(diǎn),然后流程執(zhí)行到 請(qǐng)假申請(qǐng)節(jié)點(diǎn),此時(shí)通過 taskService 查詢 zhang3的 處理任務(wù)。TaskService是通過第一步ProcessEngine 獲取到的,主要用于任務(wù)查詢。

          有人會(huì)疑問,為什么要區(qū)分創(chuàng)建流程、處理申請(qǐng)人審批任務(wù)兩個(gè)步驟,我們?cè)谔嵴?qǐng)假申請(qǐng)時(shí),只需要提申請(qǐng)一步就完成了。 實(shí)際上 提請(qǐng)假申請(qǐng)時(shí),系統(tǒng)會(huì)幫你創(chuàng)建好流程,然后自動(dòng)替你完成審批。

          為什么工作流引擎要區(qū)分為兩步呢?所有的流程圖都需要經(jīng)過 開始節(jié)點(diǎn),也都需要結(jié)束節(jié)點(diǎn),如此設(shè)計(jì)方式,可以讓工作流引擎的抽象層次更高。它可以在開始和結(jié)束時(shí)點(diǎn)建立事件通知,維護(hù)流程狀態(tài)的完整性。

          接下來,zhang3 通過 taskService完成該任務(wù),并且設(shè)置變量 day=4,即請(qǐng)假天數(shù)是4天。

          一級(jí)主管審批任務(wù)

          Task secondTask = taskService.createTaskQuery().taskAssignee("li4").singleResult();

          taskService.setVariable(secondTask.getId(), "result1"true);

          接下來,zhang3和 領(lǐng)導(dǎo)li4 說,”我家里有事要請(qǐng)假,辛苦4哥審批一下“,領(lǐng)導(dǎo)在自己的審批后臺(tái)查詢 審批任務(wù),查到后,通過了審批任務(wù)。

          有人會(huì)疑問,怎么標(biāo)識(shí) 審批通過和不通過呢? result1 是什么東西?系統(tǒng)默認(rèn)的,還是在何處甚至的變量? 在流程圖上配置的

          img

          工作流引擎沒有審批通過不通過的概念。當(dāng)流程上存在  A 和 B 兩個(gè)分支時(shí),流程圖上可以使用排他網(wǎng)關(guān)進(jìn)行分支判定。如請(qǐng)假流程圖中,一級(jí)審批結(jié)果就是一個(gè)排他網(wǎng)關(guān)。在網(wǎng)關(guān)的下游分支上配置如果要走A分支,應(yīng)該滿足哪些條件;走B分支,要滿足哪個(gè)條件;

          而排他網(wǎng)關(guān)上并沒有配置路由條件。例如在一級(jí)主管審批后,流程上設(shè)置新的變量 result1,經(jīng)過排他網(wǎng)關(guān)時(shí),A分支 是 #{result1==true} 判定通過,于是走了A分支。工作流引擎負(fù)責(zé)檢查網(wǎng)關(guān)的下級(jí)分支的條件是否滿足,哪個(gè)條件滿足走哪個(gè)分支。

          也就是說上一個(gè)任務(wù)在處理時(shí),并不知道接下來走哪個(gè)分支,也沒有指定走哪個(gè)分支,而是將自己的處理結(jié)果放到流程變量中,在排他網(wǎng)關(guān)的下游分支條件上根據(jù)流程變量進(jìn)行判斷,接下來走哪個(gè)分支,這就是工作流引擎對(duì)于流程的抽象。

          工作流引擎負(fù)責(zé)驅(qū)動(dòng)流程到排他網(wǎng)關(guān),至于走哪個(gè)分支,由分支上的條件決定! 這個(gè)思維一定要記住哦~

          繼續(xù)剩余的流程

          一級(jí)主管審批通過后,需要判斷是否需要二級(jí)審批,這時(shí)又有一個(gè)排他網(wǎng)關(guān),使用的條件就是 #{day>3},或者 #{day<=3}

          在第2步中,zhang3 申請(qǐng)了 4天的假期,于是走到了二級(jí)主管審批頁(yè)面。二級(jí)主管wang5 在審批任務(wù)列表中,找到了zhang3的請(qǐng)假申請(qǐng),然后點(diǎn)擊了通過。

          Task thirdTask = taskService.createTaskQuery().taskAssignee("wang5").singleResult();
          if (thirdTask != null) {
             taskService.setVariable(thirdTask.getId(), "result2"true);

             log.warn("用戶任務(wù)完成,流程ID:{}, 任務(wù)名稱:{}, id:{}, assignee:{}", thirdTask.getProcessInstanceId(), thirdTask.getName(), thirdTask.getId(), thirdTask.getAssignee(), thirdTask.getDelegationState());
             taskService.complete(thirdTask.getId());
          else {
             log.warn("沒有查到二級(jí)主管審批任務(wù)");
          }

          接下來流程上沒有其他審批任務(wù),但是引擎依然會(huì)繼續(xù)驅(qū)動(dòng)流程,如下圖中二級(jí)主管審批結(jié)果通過或拒絕,分別進(jìn)入到兩個(gè)不同的終點(diǎn)。

          img

          值得一提的是,上面的代碼僅僅是各個(gè)審批人在處理審批任務(wù)時(shí),必要的代碼、通用的代碼。如審批是否通過這一流程變量,完全可以統(tǒng)一規(guī)范,無需二次開發(fā)。例如 二級(jí)主管審批通過設(shè)置了 result2,實(shí)際上可以使用 二級(jí)主管審批這個(gè)節(jié)點(diǎn)的id 后綴,如 result_10來代表執(zhí)行結(jié)果,規(guī)范以后,流程審批代碼更為統(tǒng)一。新增流程模版時(shí),在審批任務(wù)節(jié)點(diǎn),審批通過或?qū)徟芙^均不需要再次開發(fā)代碼。

          驚喜的事情是:我們沒有開發(fā)任何一行流程驅(qū)動(dòng)和分支判定相關(guān)的代碼。

          但是我們依然需要處理前端頁(yè)面。因?yàn)檎?qǐng)假申請(qǐng)頁(yè)面上,不同的假期類型需要的表單參數(shù)不同,需要新增前端頁(yè)面,新增發(fā)起流程的后端接口。

          相比整體的流程控制代碼,這部分開發(fā)工作量大大降低,難度更是大大降低。

          接下來,我提出一個(gè)問題,如果新增一個(gè)需求,要求請(qǐng)病假的時(shí)候,需要HR審批,你知道如何修改流程圖,支持新的處理流程嗎?

          很簡(jiǎn)單,在審批通過終節(jié)點(diǎn)的前面,再加一個(gè)排他網(wǎng)關(guān),判斷請(qǐng)假類型== ”帶薪病假“,如果條件通過則增加HR審批節(jié)點(diǎn),如果條件不通過,則直接走到 審批通過的 終點(diǎn)。不需要開發(fā)一行代碼就能支持需求哦~

          有了工作流引擎 activiti ,新增和修改流程 都變得非常簡(jiǎn)單,它幫我們完成了所有的流程驅(qū)動(dòng)和分支判定工作。 這就是工作流引擎的價(jià)值之一。

          如何查看完整的執(zhí)行流程圖

          通過流程引擎 ProcessEngine獲取HistoryService;通過流程實(shí)例id,拿到流程執(zhí)行的所有節(jié)點(diǎn)。執(zhí)行記錄如下圖,可完整看到流程執(zhí)行的完整過程。

          List<HistoricActivityInstance> activityInstanceList = 
          historyService.createHistoricActivityInstanceQuery().processInstanceId(instance.getId()).list();
          for (HistoricActivityInstance historicActivityInstance : activityInstanceList) {
             log.warn("activityName:{},activityType:{}, assignee:{}, taskId:{}",
                   historicActivityInstance.getActivityName(),
                   historicActivityInstance.getActivityType(),
                   historicActivityInstance.getAssignee(),
                   historicActivityInstance.getTaskId());
          img

          activiti 項(xiàng)目基礎(chǔ)配置

          首先引入activiti pom

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

          我使用的是activiti 5.x,配套的 Springboot starter parent

          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.5.4</version>
              <relativePath/> 
          </parent>

          配置數(shù)據(jù)源和工作流引擎配置類

          activiti 需要使用方提供數(shù)據(jù)庫(kù)配置,在項(xiàng)目啟動(dòng)時(shí),activiti 會(huì)自動(dòng)檢查數(shù)據(jù)庫(kù)是否包含activiti 相關(guān)的表,如果不包含,會(huì)自動(dòng)幫你建表。

          在學(xué)習(xí)activiti 的過程中,我沒有安裝MySQL,而是使用H2 內(nèi)存數(shù)據(jù)庫(kù),該數(shù)據(jù)庫(kù)在Java進(jìn)程中,隨JVM同生共死,無需擔(dān)心重復(fù)啟動(dòng),數(shù)據(jù)被污染等問題,非常適合學(xué)習(xí)activiti 的時(shí)候使用

          <?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"
                 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


              <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
                  <property name="driverClassName" value="org.h2.Driver"/>
                  <!--<property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;mode=MySQL;TRACE_LEVEL_SYSTEM_OUT=2"/>-->
                  <property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;mode=MySQL;"/>
              </bean>
              <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
                  <property name="dataSource" ref="dataSource"/>
                  <property name="databaseSchemaUpdate" value="true"/>
              </bean>
              <context:component-scan base-package="com.muppet.activiti"/>
          </beans>

          H2 需要的POM如下

          <dependency>
              <groupId>com.h2database</groupId>
              <artifactId>h2</artifactId>
              <scope>runtime</scope>
          </dependency>

          全部示例代碼

          @SpringBootTest(classes = {ActivitiStartApplication.class})
          class ActivitiStartApplicationTests {

             public static final Logger log = LoggerFactory.getLogger(ActivitiStartApplicationTests.class);
             
             @Autowired
             private ApplyTaskListener applyTaskListener;

             @Test
             void contextLoads() {
                System.out.println("啟動(dòng)成功");

                ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
                RepositoryService repositoryService = engine.getRepositoryService();
                RuntimeService runtimeService = engine.getRuntimeService();
                TaskService taskService = engine.getTaskService();
                HistoryService historyService = engine.getHistoryService();
                repositoryService.createDeployment().addClasspathResource("processes/apply.bpmn").deploy();

                Map<String, Object> variableMap = new HashMap<>();
                variableMap.put("applyUser""zhang3");
                variableMap.put("supervisor""li4");
                variableMap.put("upperSupervisor""wang5");

                ProcessInstance instance = runtimeService.startProcessInstanceByKey("apply_processor_1", variableMap);


                Task firstTask = taskService.createTaskQuery().taskAssignee("zhang3").singleResult();

                log.warn("用戶任務(wù)完成,流程ID:{}, 任務(wù)名稱 :{}, id:{}, assignee:{}", firstTask.getProcessInstanceId(), firstTask.getName(), firstTask.getId(), firstTask.getAssignee());
                taskService.complete(firstTask.getId(), Maps.newHashMap("day", 4));


                Task secondTask = taskService.createTaskQuery().taskAssignee("li4").singleResult();

                taskService.setVariable(secondTask.getId(), "result1"true);
                log.warn("用戶任務(wù)完成流程ID:{}, 任務(wù)名稱 :{}, id:{}, assignee:{}", secondTask.getProcessInstanceId(), secondTask.getName(), secondTask.getId(), secondTask.getAssignee());
                taskService.complete(secondTask.getId());

                Task thirdTask = taskService.createTaskQuery().taskAssignee("wang5").singleResult();
                if (thirdTask != null) {
                   taskService.setVariable(thirdTask.getId(), "result2"true);

                   log.warn("用戶任務(wù)完成,流程ID:{}, 任務(wù)名稱:{}, id:{}, assignee:{}", thirdTask.getProcessInstanceId(), thirdTask.getName(), thirdTask.getId(), thirdTask.getAssignee(), thirdTask.getDelegationState());
                   taskService.complete(thirdTask.getId());
                } else {
                   log.warn("沒有查到二級(jí)主管審批任務(wù)");
                }

                log.warn("流程執(zhí)行過程如下");
                List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(instance.getId()).list();
                for (HistoricActivityInstance historicActivityInstance : activityInstanceList) {
                   log.warn("activityName:{},activityType:{}, assignee:{}, taskId:{}",
                         historicActivityInstance.getActivityName(),
                         historicActivityInstance.getActivityType(),
                         historicActivityInstance.getAssignee(),
                         historicActivityInstance.getTaskId());
                }
             }
          }

          activiti 工作流引擎數(shù)據(jù)庫(kù)設(shè)計(jì)

          為了保證流程的可靠性和可恢復(fù)性,工作流引擎通常會(huì)將流程實(shí)例的狀態(tài)和數(shù)據(jù)持久化存儲(chǔ)到中。在流程執(zhí)行過程中,引擎會(huì)不斷地更新數(shù)據(jù)庫(kù)中的狀態(tài)數(shù)據(jù)。activiti 共包含了一系列用于存儲(chǔ)流程定義、運(yùn)行時(shí)數(shù)據(jù)以及歷史記錄的表。

          1. 流程定義相關(guān)表

          ACT_RE_*系列表:主要包括流程定義(Process Definitions)、流程資源(Resources)和其他靜態(tài)信息的存儲(chǔ)。

          2. 運(yùn)行時(shí)數(shù)據(jù)表

          ACT_RU_*系列表:這些表存放了流程實(shí)例執(zhí)行過程中的實(shí)時(shí)數(shù)據(jù),如任務(wù)(Tasks)、流程實(shí)例(Process Instances)、變量(Variables)、執(zhí)行對(duì)象(Executions)等。

          3. 歷史數(shù)據(jù)表

          ACT_HI_*系列表:當(dāng)流程實(shí)例結(jié)束或達(dá)到特定條件時(shí),相關(guān)的運(yùn)行時(shí)數(shù)據(jù)會(huì)被遷移到歷史表中,以供后期審計(jì)、報(bào)告分析之用。

          4. 身份和權(quán)限表

          ACT_ID_*系列表:主要用于存儲(chǔ)用戶、組以及相關(guān)的身份和權(quán)限信息。

          5. 其他輔助表

          包括事件日志表:(Event Log)、作業(yè)及定時(shí)器表(Job and Timer entities)等,它們服務(wù)于調(diào)度、異步處理等功能需求。

          工作流引擎API 介紹

          img
          • ProcessEngine: 表示Activiti工作流引擎的入口,用于獲取各種管理API操作的對(duì)象。
          • RepositoryService: 用于管理流程定義的API,包括流程的部署和刪除等操作。
          • RuntimeService: 用于管理流程實(shí)例的API,包括啟動(dòng)、暫停和刪除流程實(shí)例等操作。
          • TaskService: 用于管理任務(wù)的API,包括創(chuàng)建、完成和查詢?nèi)蝿?wù)等操作。
          • HistoryService: 用于查詢歷史記錄的API,包括查詢已完成的任務(wù)、流程實(shí)例和變量等信息。

          這5個(gè)Service我們已經(jīng)很熟悉了,現(xiàn)在跟大家介紹這部分API,大家應(yīng)該更容易理解了。

          其中 ProcessEngine我們用來獲取各類Service類,RepositoryService 用來部署流程圖,RuntimeService用來創(chuàng)建流程圖實(shí)例、TaskService用來查詢?nèi)蝿?wù)和完成任務(wù);HistoryService用來查看流程執(zhí)行過程。

          什么是BPMN流程圖

          BPMN(?Business Process Modeling Notation)?是一種流程建模的通用和標(biāo)準(zhǔn)語(yǔ)言,?用來繪制業(yè)務(wù)流程圖,?以便更好地讓各部門之間理解業(yè)務(wù)流程和相互關(guān)系。?

          BPMN 1.0規(guī)范于2004年5月對(duì)外發(fā)布,?而BPMN 2.0標(biāo)準(zhǔn)由OMG于2011年推出,?對(duì)BPMN進(jìn)行了重新定義。?

          Activiti 是由 jBPM 的創(chuàng)建者 Tom Baeyens 離開 JBoss 之后建立的項(xiàng)目,構(gòu)建在開發(fā) jBPM 版本 1 到 4 時(shí)積累的多年經(jīng)驗(yàn)的基礎(chǔ)之上,旨在創(chuàng)建下一代的 BPM 解決方案。

          同時(shí) Activiti 選擇了 Apache 許可,一方面是希望 Activiti 能有更長(zhǎng)久的生命力,因?yàn)樗皇苋魏蝹€(gè)人或是公司的控制而是屬于整個(gè)社區(qū),另一方面更是希望這個(gè)寬松的許可能夠讓 Activiti BPM 引擎和 BPMN2.0 被更廣泛的采納、使用和商業(yè)化。

          Idea actiBPM 插件如何安裝

          https://www.cnblogs.com/No2-explorer/p/11032469.html

          Idea新建的流程圖是什么

          本質(zhì)是一個(gè)XML,可以通過BPMN 可視化工具解析,如下XML代碼是 請(qǐng)假流程圖

          創(chuàng)建一個(gè)二級(jí)主管審批節(jié)點(diǎn)

          <userTask activiti:assignee="${upperSupervisor}" activiti:async="false" activiti:exclusive="true" id="_10" name="二級(jí)主管審批"/>

          創(chuàng)建一個(gè)審批結(jié)果的分支

          分支上包含連接了哪兩個(gè)節(jié)點(diǎn),以及分支的條件表達(dá)式 #{result2==true}

          <sequenceFlow id="_19" sourceRef="_17" targetRef="_8">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result2==false}]]></conditionExpression>
          </sequenceFlow>
          <sequenceFlow id="_20" sourceRef="_17" targetRef="_11">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result2==true}]]></conditionExpression>
          </sequenceFlow>

          請(qǐng)假流程圖完整XML

          <?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="m1723259512248" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
            <process id="apply_processor_1" isClosed="false" isExecutable="true" processType="None">
              <startEvent id="_4" name="開始">
                <extensionElements>
                  <activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/>
                </extensionElements>
              </startEvent>
              <userTask activiti:assignee="#{applyUser}" activiti:async="false" activiti:exclusive="true" id="_5" name="請(qǐng)假申請(qǐng)"/>
              <userTask activiti:assignee="${supervisor}" activiti:async="false" activiti:exclusive="true" id="_6" name="主管審批">
                <extensionElements>
                  <activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/>
                </extensionElements>
              </userTask>
              <exclusiveGateway gatewayDirection="Unspecified" id="_7" name="一級(jí)審批結(jié)果"/>
              <endEvent id="_8" name="審批不通過">
                <extensionElements>
                  <activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/>
                </extensionElements>
              </endEvent>
              <exclusiveGateway gatewayDirection="Unspecified" id="_9" name="天數(shù)驗(yàn)證2"/>
              <userTask activiti:assignee="${upperSupervisor}" activiti:async="false" activiti:exclusive="true" id="_10" name="二級(jí)主管審批"/>
              <endEvent id="_11" name="審批通過">
                <extensionElements>
                  <activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/>
                </extensionElements>
              </endEvent>
              <sequenceFlow id="_2" sourceRef="_4" targetRef="_5"/>
              <sequenceFlow id="_3" sourceRef="_5" targetRef="_6"/>
              <sequenceFlow id="_12" sourceRef="_6" targetRef="_7"/>
              <sequenceFlow id="_13" sourceRef="_7" targetRef="_8">
                <conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result1==false}]]></conditionExpression>
              </sequenceFlow>
              <sequenceFlow id="_14" sourceRef="_7" targetRef="_9">
                <conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result1==true}]]></conditionExpression>
              </sequenceFlow>
              <sequenceFlow id="_15" sourceRef="_9" targetRef="_10">
                <conditionExpression xsi:type="tFormalExpression"><![CDATA[#{day>3}]]></conditionExpression>
              </sequenceFlow>
              <sequenceFlow id="_16" sourceRef="_9" targetRef="_11">
                <conditionExpression xsi:type="tFormalExpression"><![CDATA[#{day<=3}]]></conditionExpression>
              </sequenceFlow>
              <exclusiveGateway gatewayDirection="Unspecified" id="_17" name="審批結(jié)果2"/>
              <sequenceFlow id="_18" sourceRef="_10" targetRef="_17"/>
              <sequenceFlow id="_19" sourceRef="_17" targetRef="_8">
                <conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result2==false}]]></conditionExpression>
              </sequenceFlow>
              <sequenceFlow id="_20" sourceRef="_17" targetRef="_11">
                <conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result2==true}]]></conditionExpression>
              </sequenceFlow>
            </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="apply_processor_1">
                <bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
                  <omgdc:Bounds height="32.0" width="32.0" x="525.0" y="170.0"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="32.0" width="32.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="495.0" y="285.0"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNShape>
                <bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6">
                  <omgdc:Bounds height="55.0" width="85.0" x="500.0" y="390.0"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNShape>
                <bpmndi:BPMNShape bpmnElement="_7" id="Shape-_7" isMarkerVisible="false">
                  <omgdc:Bounds height="32.0" width="32.0" x="520.0" y="495.0"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNShape>
                <bpmndi:BPMNShape bpmnElement="_8" id="Shape-_8">
                  <omgdc:Bounds height="32.0" width="32.0" x="295.0" y="825.0"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNShape>
                <bpmndi:BPMNShape bpmnElement="_9" id="Shape-_9" isMarkerVisible="false">
                  <omgdc:Bounds height="32.0" width="32.0" x="515.0" y="600.0"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNShape>
                <bpmndi:BPMNShape bpmnElement="_10" id="Shape-_10">
                  <omgdc:Bounds height="55.0" width="95.0" x="485.0" y="720.0"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="55.0" width="95.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNShape>
                <bpmndi:BPMNShape bpmnElement="_11" id="Shape-_11">
                  <omgdc:Bounds height="32.0" width="32.0" x="720.0" y="600.0"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNShape>
                <bpmndi:BPMNShape bpmnElement="_17" id="Shape-_17" isMarkerVisible="false">
                  <omgdc:Bounds height="32.0" width="32.0" x="510.0" y="810.0"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNShape>
                <bpmndi:BPMNEdge bpmnElement="_13" id="BPMNEdge__13" sourceElement="_7" targetElement="_8">
                  <omgdi:waypoint x="520.1953352769677" y="511.0000000000001"/>
                  <omgdi:waypoint x="310.0" y="700.0"/>
                  <omgdi:waypoint x="310.0" y="825.0808012104277"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNEdge>
                <bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_6" targetElement="_7">
                  <omgdi:waypoint x="535.9999999999999" y="444.97894395853575"/>
                  <omgdi:waypoint x="535.9999999999999" y="495.19533527696785"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNEdge>
                <bpmndi:BPMNEdge bpmnElement="_15" id="BPMNEdge__15" sourceElement="_9" targetElement="_10">
                  <omgdi:waypoint x="530.9999999999999" y="632.3606171769437"/>
                  <omgdi:waypoint x="530.9999999999999" y="719.6793002915451"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNEdge>
                <bpmndi:BPMNEdge bpmnElement="_14" id="BPMNEdge__14" sourceElement="_7" targetElement="_9">
                  <omgdi:waypoint x="533.4999999999999" y="524.4297052154194"/>
                  <omgdi:waypoint x="533.4999999999999" y="602.2445273261599"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNEdge>
                <bpmndi:BPMNEdge bpmnElement="_16" id="BPMNEdge__16" sourceElement="_9" targetElement="_11">
                  <omgdi:waypoint x="546.8934865508442" y="616.0"/>
                  <omgdi:waypoint x="719.6793107624761" y="616.0"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNEdge>
                <bpmndi:BPMNEdge bpmnElement="_19" id="BPMNEdge__19" sourceElement="_17" targetElement="_8">
                  <omgdi:waypoint x="526.0000000000001" y="842.0919987042433"/>
                  <omgdi:waypoint x="420.0" y="880.0"/>
                  <omgdi:waypoint x="311.0" y="857.0256933063517"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="0.0" width="4.0" x="0.0" y="-10.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNEdge>
                <bpmndi:BPMNEdge bpmnElement="_18" id="BPMNEdge__18" sourceElement="_10" targetElement="_17">
                  <omgdi:waypoint x="526.0000000000001" y="774.9649065975595"/>
                  <omgdi:waypoint x="526.0000000000001" y="810.6344887161214"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNEdge>
                <bpmndi:BPMNEdge bpmnElement="_2" id="BPMNEdge__2" sourceElement="_4" targetElement="_5">
                  <omgdi:waypoint x="541.0" y="202.23782176749842"/>
                  <omgdi:waypoint x="541.0" y="285.1689882302127"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNEdge>
                <bpmndi:BPMNEdge bpmnElement="_3" id="BPMNEdge__3" sourceElement="_5" targetElement="_6">
                  <omgdi:waypoint x="540.0" y="339.5907569376957"/>
                  <omgdi:waypoint x="540.0" y="389.6933376525213"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNEdge>
                <bpmndi:BPMNEdge bpmnElement="_20" id="BPMNEdge__20" sourceElement="_17" targetElement="_11">
                  <omgdi:waypoint x="541.3655112838787" y="826.0"/>
                  <omgdi:waypoint x="659.0" y="724.0"/>
                  <omgdi:waypoint x="719.6793107624761" y="616.0"/>
                  <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="1.0" width="127.0" x="0.0" y="76.0"/>
                  </bpmndi:BPMNLabel>
                </bpmndi:BPMNEdge>
              </bpmndi:BPMNPlane>
            </bpmndi:BPMNDiagram>
          </definitions>

          工作流引擎同類對(duì)比

          比較流行的 Camunda 和 Flowable 都是基于activiti 開發(fā)的。

          img

          同類框架對(duì)比

          img
          img

          3個(gè)框架的使用流程基本一致

          使用流程 5板斧

          • 定義BPMN流程圖, 使用建模工具設(shè)計(jì)定義流程圖。
          • 部署流程, 將BPMN流程圖部署到工作流引擎中。
          • 啟動(dòng)流程實(shí)例, 通過工作流引擎基于流程模版,啟動(dòng)新流程實(shí)例。
          • 執(zhí)行任務(wù), 流程執(zhí)行中,引擎會(huì)為任務(wù)節(jié)點(diǎn)創(chuàng)建任務(wù),分配給對(duì)應(yīng)執(zhí)行人。
          • 監(jiān)聽事件, 開發(fā)者可以注冊(cè)監(jiān)聽器來捕獲流程執(zhí)行過程中的各種事件,例如任務(wù)完成、流程結(jié)束等。
          • 查詢和監(jiān)控, 工作流引擎通常提供了查詢和監(jiān)控功能,允許開發(fā)者和管理人員查看流程實(shí)例的狀態(tài)、任務(wù)執(zhí)行情況以及歷史數(shù)

          繼續(xù)學(xué)習(xí)方向

          • 事件類型和事件監(jiān)聽。
          • 任務(wù)類型;接受任務(wù)、服務(wù)任務(wù)、腳本任務(wù)學(xué)習(xí)
          • 任務(wù)監(jiān)聽和執(zhí)行監(jiān)聽器
          • 表單管理
          • 順序流程和網(wǎng)關(guān)(并行網(wǎng)關(guān)等)
          • 性能和擴(kuò)展;ID生成和 分庫(kù)分表

          總結(jié)

          • activiti 工作流引擎適用場(chǎng)景,涉及多用戶參與的流程管理。
          • activiti 工作流開發(fā)分兩步 1)設(shè)計(jì)流程圖 2)部署流程圖 3) 發(fā)起和驅(qū)動(dòng)流程實(shí)例
          • 新增流程模版,無需開發(fā)代碼驅(qū)動(dòng)流程和分支判定。僅需要適配前端頁(yè)面和簡(jiǎn)單的后端交互類接口。

              

          程序汪接私活項(xiàng)目目錄,2023年總結(jié)

          Java項(xiàng)目分享  最新整理全集,找項(xiàng)目不累啦 07版

          程序汪10萬接的無線共享充電寶項(xiàng)目,開發(fā)周期3個(gè)月

          程序汪1萬接的企業(yè)官網(wǎng)項(xiàng)目,開發(fā)周期15天

          程序汪8萬接的共享口罩項(xiàng)目,開發(fā)周期1個(gè)月

          程序汪8萬塊的飲水機(jī)物聯(lián)網(wǎng)私活項(xiàng)目經(jīng)驗(yàn)分享

          程序汪接的4萬智慧餐飲項(xiàng)目

          程序汪8萬接的自助洗車小程序
          程序汪9萬接的無人自助洗寵物機(jī)項(xiàng)目,開發(fā)周期40天


          歡迎添加程序汪個(gè)人微信 itwang005  進(jìn)粉絲群或圍觀朋友圈

          瀏覽 376
          2點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          2點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产乱伦视频小说 | 麻豆久久久 | 97国产超碰 | 毛片A片中文字幕在线视频 | 北条麻妃丝袜电影 |