怎么用文件做網(wǎng)站快速優(yōu)化網(wǎng)站排名軟件
記錄開發(fā)最核心的部分,理論結(jié)合業(yè)務(wù)實操減少廢話,從未接觸工作流快速帶入開發(fā)。假設(shè)你是后端的同學學過JAVA和流程圖,則可以繼續(xù)向后看,否則先把基礎(chǔ)課程書準備好先翻翻。
為什么要工作流
比起直接使用狀態(tài)字段,工作流有以下幾個好處:
- 直觀管理:可以直接通過維護流程圖設(shè)計來實現(xiàn)狀態(tài)流轉(zhuǎn)。流程變化可直接維護BMP流程圖發(fā)布來實現(xiàn)變更
- 復雜流程:會簽、序簽、指定時間半數(shù)以上通過等等
- 低代碼:基于表單的審批數(shù)據(jù)模式性很強,可以比較方便的實現(xiàn)低代碼,高效開發(fā)
其它理由不用多說,光這3項的場景就有充分使用的理由。如果模式非常簡單,還是狀態(tài)字段來的直接。
從流程圖說起
我們知道,一個流程圖包括開始狀態(tài)、結(jié)束狀態(tài)、任務(wù)節(jié)點、流程條件。嗯,看起來像這樣
對應(yīng)Activiti的概念:
實例:開始一個任務(wù)后,則產(chǎn)生一個流程實例,即ProcessInstance
任務(wù):當走到一個任務(wù)節(jié)點時,則產(chǎn)生任務(wù),任務(wù)有單個和多個兩種模式,多個任務(wù)有順序和并行模式?
執(zhí)行:通常一個任務(wù)節(jié)點會產(chǎn)生一個執(zhí)行,但是并行節(jié)點或分支任務(wù)會產(chǎn)生多個執(zhí)行,一個進行中的任務(wù)對應(yīng)一個執(zhí)行,每個執(zhí)行可以有多個子執(zhí)行(多個任務(wù)節(jié)點時)。這里的執(zhí)行,相當于有多少個“待辦任務(wù)”在進行。當對應(yīng)的任務(wù)完成時,執(zhí)行表的對應(yīng)記錄也會被刪除
變量:流程的數(shù)據(jù)容器,在整個流轉(zhuǎn)過程中。變量分為兩種,一種是實例級別的變量,另一種是執(zhí)行級別的變量。對應(yīng)task.setVariable和execution.setVariable,區(qū)別是execution variable只在當前的執(zhí)行節(jié)點有效,而task variable是針對任務(wù)的變量。
變量生命周期:對于task和execution,都有setVariable和setLocalVariable兩種。變量的setVariable設(shè)置的值針對整個流程實例生命周期有效,而setLocalVariable設(shè)置的值針對當前任務(wù)節(jié)點有效。可以理解為始終使用還是一次性使用(針對網(wǎng)關(guān)使用一次后作廢)。
邊界事件:定時、信號等事件,在事件網(wǎng)關(guān)時,可以根據(jù)事件來定義。實現(xiàn)時間或信號特性的約束,常見的場景例如客服1小時處理不完投訴則交給主管處理。
約束條件與網(wǎng)關(guān)
以前面的流程圖為例,超過3天和不超過3天為根據(jù)約束條件產(chǎn)生的兩種不同的操作任務(wù)。在Activiti7里用網(wǎng)關(guān)(gateway)來表示。網(wǎng)關(guān)有表達式(SPEL)、腳本和Java類3中處理。常用的為表達式模式。當前一個任務(wù)完成(complete)時,根據(jù)表達式不同走向不同的節(jié)點
網(wǎng)關(guān)有4種類型:
排他網(wǎng)關(guān)(Exclusive Gateway)
會按照給定的條件,產(chǎn)生一個任務(wù)實例
蛋疼理論:如果多個分支條件都滿足,或者一個分支都不滿足,怎么辦?
如果都滿足,流程會按照id值升序,處理第一條分支(先定義的分支id通常值較小,但不絕對)
如果都不滿足,則會拋出異常
并行網(wǎng)關(guān)(ParallelGateway)
會按照給定的條件,產(chǎn)生多個任務(wù)實例。并行網(wǎng)關(guān)可以并行作業(yè),提高業(yè)務(wù)流程速度。
包容網(wǎng)關(guān)(Inclusive Gateway)
相當于排他和并行的綜合體 ,同時執(zhí)行時可以指定條件,例如某些需要補充材料的情況
事件網(wǎng)關(guān)(Event Gateway)
用于根據(jù)事件的觸發(fā)選擇分支路徑。當指定的事件觸發(fā)時,流程會選擇對應(yīng)的分支執(zhí)行。
多實例與子執(zhí)行
有那么一種業(yè)務(wù),流程圖可能表示為多個人參與同一個活動。例如你可能想到的流程圖:
像這種活動,在BPM里,可以作為單個任務(wù)節(jié)點來實現(xiàn)。叫做多實例任務(wù)。
我們可以通過節(jié)點的MultiInstance來指定多實例,當指定多實例時,需要設(shè)置Collection的參數(shù)來決定進入任務(wù)時產(chǎn)生多少個子執(zhí)行。Collection是一組java.util.Collection接口的數(shù)據(jù)。進入任務(wù)時,將產(chǎn)生一個對應(yīng)的主執(zhí)行,和多個子執(zhí)行,這些子任務(wù)將分別指派給Collection的每一個人員。
對應(yīng)的業(yè)務(wù)場景通常有會簽和序簽。序簽用的較少,這里主要講會簽。
會簽模式
會員的模式主要有:
按數(shù)量通過:達到一定數(shù)量的通過表決后,會簽通過。
按比例通過:達到一定比例的通過表決后,會簽通過。
一票否決:只要有一個表決是否定的,會簽否決。
一票通過:只要有一個表決通過的,會簽通過。
我們可以在Task的MultiInstance配置多實例的信息??梢耘渲玫挠?#xff1a;
模式參數(shù)
順序(sequential ):執(zhí)行順序,必選項。true:多實例順序執(zhí)行。false:多實例并行。
并行(parallel):多個實例會同時并行發(fā)放給處理人
loop cardinality:循環(huán)基數(shù)(實例數(shù)量),可選項??商钫麛?shù),表示會簽的人數(shù)。
Collection:集合,可選項。會簽人數(shù)的集合list,與loop cardinality二選一。
Element variable:元素變量。選擇Collection時必選,為collection集合每次遍歷的元素。
Completion condition:完成條件,可選項。
完成條件
會簽配置關(guān)鍵就是Completion condition,可以填寫一個UEL(類似SPEL)。在流程流轉(zhuǎn)時,對于多實例節(jié)點,會內(nèi)置下面幾個參數(shù):
nrOfInstances:創(chuàng)建的實例總數(shù),在進入任務(wù)節(jié)點時則設(shè)置好,一般等于Collection的數(shù)量
nrOfActiveInstances:當前活動的實例數(shù),針對順序類型的多實例,該變量值等于1;對于并行的類型,進入任務(wù)時為Collection數(shù)量,每完成一個執(zhí)行,則活動任務(wù)數(shù)減1。
nrOfCompletedInstances:已執(zhí)行實例數(shù)。每完成一個執(zhí)行,則加1。
loopCounter:表示多實例流程循環(huán)的下標,順序執(zhí)行時記錄執(zhí)行順序。
條件實現(xiàn)
理解上面的內(nèi)容這樣實現(xiàn)的方式就很明確了:
假設(shè)我們?yōu)闀炘O(shè)置了2個投票池(說白了就是變量)approveCount和denyCount。操作時,每點擊"同意"或"拒絕"(先不考慮棄權(quán)情況)則變量+1
1)按數(shù)量通過:2人通過則通過#{approveCount > 2}
2)按比例通過:常見的過半數(shù)同意則通過?#{agreeCount/nrOfInstance > 0.5}
3)一票否決:#{denyCount== 1}
4)一票通過:#{approveCount== 1}
序簽:較少使用。需要每個人員逐一完成任務(wù)。
搶單與代辦
接下來講兩個常見業(yè)務(wù)場景,第一個是搶單。搶單的需求為我們可以將任務(wù)指定給一個處理小組,處理小組某個人點擊“開始處理”后,其它人將無法再點擊處理按鈕或點擊處理按鈕提示已被搶單。
核心業(yè)務(wù)實現(xiàn):
1)任務(wù)的CandidateUser屬性可以指定一組處理人員,Task的Candidata Users屬性可以通過SPEL指定一個列表。當指派列表后,任務(wù)流轉(zhuǎn)到節(jié)點時所有的Candidata Users都會收到一個代辦任務(wù)。
2)操作員點擊開始處理時,通過taskService.setAssignee(taskId, assignee);指派給某個人員,指派后,其它操作員將再無法看到這個任務(wù)
3)當其他操作員點擊操作時,需要檢查操作列表,如果已經(jīng)被指派給非當前操作員 ,需要返回前端提醒用戶該任務(wù)已指派。否則直接使用Activity引擎來處理指派,將會導致任務(wù)重新分配(和后面講的代辦邏輯一樣)
代辦的模式和搶單的一樣,當某任務(wù)已經(jīng)指定給某個操作員后??梢酝ㄟ^setAssignee實現(xiàn)操作的重新指派。從而實現(xiàn)代辦指派。當然這只是流程級別的操作,對于業(yè)務(wù)的具體流轉(zhuǎn)記錄,我們還時得借助日志的實現(xiàn)。
Activiti之歷史記錄
對于工作流業(yè)務(wù),僅有流程狀態(tài)支撐顯然不夠。我們還需要對歷史數(shù)據(jù)進行查詢和展示。Activiti提供了一組歷史記錄表,以ACT_HI_開頭,記錄了流程信息、流程任務(wù)信息、以及流程的變量信息。能夠滿足一些常見的業(yè)務(wù)場景需求,例如:
審批歷史
在審批的過程,我們通常需要添加批示信息。一個流程的審批歷史類似:
張三:提交請假申請
組長:同意請假?
經(jīng)理:同意請假
人事:請假申請?zhí)幚硗戤?br />
如果不借助額外的數(shù)據(jù)表,我們可以使用流程變量。定義為一個String數(shù)組或者JSON,每經(jīng)過一個處理節(jié)點添加一行記錄。流程進行或完成時,將該記錄拿出來展示,該信息記錄在表act_hi_taskinst
注意如果使用流程實例變量記錄審批過程JSON,需要留意String變量大小的限制(4000字符),以免在審批過程中超過大小。以每個節(jié)點審批信息256字符為例,我們可以推算最少大概15個節(jié)點審批信息填滿會導致審批記錄滿。
流轉(zhuǎn)節(jié)點
在任務(wù)記錄表act_hi_taskinst里,記錄了流程產(chǎn)生的任務(wù)以及經(jīng)過的節(jié)點,以及對于流程圖文件的任務(wù)節(jié)點的KEY。這樣我們可以通過act_hi_taskinst,在BPMN流程定義圖上繪制詳細的節(jié)點流轉(zhuǎn)標記進行展示
理論了很多,下一步來點干貨實操
環(huán)境搭建
以springboot+Activiti7為例,開始環(huán)境搭建。
Activiti依賴的支持有:
- spring:提供了一組starter,啟動后可以通過對應(yīng)的Bean完成流程處理
- spring-security:activiti7.X默認整合了springsecurity,使用sa-token或Shiro等其他權(quán)限控制需要剝離對spring-security的依賴,否則會報錯。對springsecurity的依賴主要包含群組權(quán)限的部分,我們不需要完全可以剝離它
- mybatis:數(shù)據(jù)存儲層使用的是mybatis來進行控制
Activiti最新的版本
如果從sonatype公共的倉庫去拿,僅能拿到4年前的版本7.1.0.M6。你可能滿腦袋的問號:Activiti開源死掉了嗎?不再支持了嗎?
如果你去Activiti官網(wǎng),會發(fā)現(xiàn)Activiti仍舊有維護,甚至發(fā)展到8.0版本。無法獲得是因為我們沒有配置Activiti官網(wǎng)倉庫。以gradle為例,我們只需要添加這個倉庫即可
maven { url 'https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases' } // activiti最新版?zhèn)}庫
然后我們可以使用最新版本了,為了避免表結(jié)構(gòu)出現(xiàn)不兼容,還是選擇7.X的最新版本:
implementation("org.activiti:activiti-spring-boot-starter:7.11.0") implementation("org.activiti:activiti-core-dependencies:7.11.0")
升級注意
JDK版本依賴:Activiti自身倉庫的版本都是使用JDK11編譯,如果是使用JDK1.8編譯會出現(xiàn)報錯。解決辦法1是升級JDK到11,2是重新使用JDK1.8編譯整個Activity源(比較麻煩)。我選擇的是升級到JDK11
剝離springsecurity依賴
剝離springsecurity依賴主要是方式是禁用Activiti里與springsecurity相關(guān)的starter
YAML配置的方式:
?
spring:autoconfigure:exclude:- org.activiti.spring.boot.ActivitiMethodSecurityAutoConfiguration- org.activiti.core.common.spring.identity.config.ActivitiSpringIdentityAutoConfiguration
代碼配置方式:
@SpringBootApplication(exclude = {ActivitiMethodSecurityAutoConfiguration.class, ActivitiSpringIdentityAutoConfiguration.class}
)
然后啟動你會發(fā)現(xiàn),還會有Bean UserGroupManager報錯,這個Bean是依賴spring security的群組功能。不使用群組的話,我們可以直接new個匿名類讓spring裝配不報錯。
我的ActivitiConfiguration如下
package org.ccframe.app;import org.activiti.api.runtime.shared.identity.UserGroupManager;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;@Configuration
public class ActivitiConfiguration {@Autowiredprivate DataSource dataSource;@Autowiredprivate PlatformTransactionManager transactionManager;//通過@Bean注解將SpringProcessEngineConfiguration實例聲明為Spring Bean,使其可供其他組件注入和使用@Beanpublic SpringProcessEngineConfiguration springProcessEngineConfiguration() {SpringProcessEngineConfiguration spec = new SpringProcessEngineConfiguration();//設(shè)置數(shù)據(jù)源,將注入的數(shù)據(jù)源設(shè)置到SpringProcessEngineConfiguration實例中spec.setDataSource(this.dataSource);//設(shè)置事務(wù)管理器將注入的事務(wù)管理器設(shè)置到SpringProcessEngineConfiguration實例中spec.setTransactionManager(this.transactionManager);//設(shè)置數(shù)據(jù)庫模式更新策略 true表示在啟動時自動創(chuàng)建或更新Activiti引擎所需的數(shù)據(jù)庫表結(jié)構(gòu)spec.setDatabaseSchemaUpdate("true");Resource[] resources = null;//配置流程部署資源//使用PathMatchingResourcePatternResolver從classpath中的bpmn目錄下加載所有以.bpmn為擴展名的文件作為流程定義資源,// 并將它們設(shè)置到SpringProcessEngineConfiguration實例中。try {resources = (new PathMatchingResourcePatternResolver()).getResources("classpath*:bpmn/*.bpmn");} catch (IOException var4) {var4.printStackTrace();}spec.setDeploymentResources(resources);return spec;}@Beanpublic UserGroupManager userGroupManager(){return new UserGroupManager(){@Overridepublic List<String> getUserGroups(String username) {return new ArrayList<>();}@Overridepublic List<String> getUserRoles(String username) {return new ArrayList<>();}@Overridepublic List<String> getGroups() {return new ArrayList<>();}@Overridepublic List<String> getUsers() {return new ArrayList<>();}};}
}
這里只是不使用group的功能,當然你可以使用自己的權(quán)限系統(tǒng)來實現(xiàn)group的功能,將group信息對應(yīng)到自己權(quán)限系統(tǒng)的用戶列表。
可控流轉(zhuǎn)的實現(xiàn)
<編寫中>