從零開始學(xué)習(xí)和改造activiti流程引擎的13天,自己記錄一下
day#1(11.13)
嘗試通過spring boot 集成最新版activiti 7,但是苦于官方的文檔基本為空,無法完成spring boot的配置,最終按照activiti 6的文檔,手工初始化ProcessEngine以及完成deploy測(cè)試。
在eclipse中安裝流程模型設(shè)計(jì)器,并畫簡(jiǎn)單的流程。
day#2(11.14)
想要開啟activiti對(duì)數(shù)據(jù)庫(kù)操作的SQL日志打印,研究了好一番功夫,終于得以實(shí)現(xiàn)。實(shí)現(xiàn)方式如下:
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="60 seconds" debug="false"> <!-- 控制臺(tái)輸出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss} %-5p [%c] - %m%n</pattern> </encoder> </appender> <logger name="org.springframework" level="ERROR" /> <logger name="org.mybatis" level="ERROR" /> <logger name="com.baomidou.mybatisplus" level="ERROR" /> <logger name="org.apache" level="ERROR" /> <!-- Activiti日志 --> <logger name="org.activiti" level="ERROR" /> <logger name="org.activiti.engine.impl.persistence.entity" level="DEBUG" /> <!--myibatis log configure --> <logger name="com.ibatis" level="DEBUG" /> <logger name="com.ibatis.common.jdbc.SimpleDataSource" level="DEBUG" /> <logger name="com.ibatis.common.jdbc.ScriptRunner" level="DEBUG" /> <logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate" level="DEBUG" /> <logger name="java.sql.Connection" level="DEBUG" /> <logger name="java.sql.Statement" level="DEBUG" /> <logger name="java.sql.PreparedStatement" level="DEBUG" /> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </configuration>
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.1.8</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>1.7.22</version> </dependency>
簡(jiǎn)單的講,就是resources下放logback.xml,maven添加對(duì)logback的依賴即可,然后activiti執(zhí)行的sql就會(huì)被自動(dòng)打印,無需其他額外設(shè)置。
下載activiti 6 全部壓縮包(110MB左右),將官方的在線模型設(shè)計(jì)器運(yùn)行起來,發(fā)現(xiàn)前端使用angularJS做的。對(duì)官方demo稍作研究后決定,完全替換官方的rest API,方法就是獲取官方的后端API源碼,然后一個(gè)接口一個(gè)接口增加到自建spring mvc項(xiàng)目中。
新建spring mvc空項(xiàng)目,新建簡(jiǎn)單controller,運(yùn)行成功。
maven引入activiti engine,復(fù)制第一個(gè)API代碼以及editor前端代碼到自己的項(xiàng)目中,發(fā)現(xiàn)需要引入另一個(gè)jar包(activiti-app-logic)。maven引入之后,編譯能通過,但是無法啟動(dòng),原因是spring 啟動(dòng)時(shí)檢查引入的jar包沖突。在maven中增加exclusion修復(fù)完jar包沖突之后,總算運(yùn)行起來。
運(yùn)行起來之后,為了避開身份認(rèn)證,直接修改JS源碼,終于得以直接運(yùn)行activiti-app/editor/#/editor目錄。
day#3(11.15)
開始修復(fù)第一個(gè)API(GET rest/models),發(fā)現(xiàn)需要引用或完全替換activiti-app-logic包中的數(shù)據(jù)庫(kù)訪問層。考慮了一會(huì),考慮到modeler只涉及3張表,決定用我們之前項(xiàng)目的hibernate框架完全重寫modeler的數(shù)據(jù)庫(kù)訪問層代碼。
非常順利,很快實(shí)現(xiàn)獲取models,創(chuàng)建model,生成model縮略圖,獲取model編輯器json數(shù)據(jù),保存模型(完成編譯)等接口。唯一麻煩的事就是需要從activiti-app-logic包中搬代碼,解決各種依賴問題。
day#3(11.16)
先是修復(fù)了保存模型的接口,并且進(jìn)行了大量測(cè)試,各種復(fù)雜模型數(shù)據(jù)都可以順利保存。
接下來體驗(yàn)了modeler的大部分功能,發(fā)現(xiàn)非常的復(fù)雜,得需要完全理解activiti的基礎(chǔ)上才能開發(fā)出適用的流程引擎啊。接下來的幾天可能是學(xué)習(xí)activiti引擎了。
day#4(11.19/周一)
周末的時(shí)候花了些時(shí)間閱讀文檔,完成了大部分內(nèi)容的閱讀,特別是對(duì)BPMN2.0規(guī)范有了較深刻的認(rèn)識(shí)。
上午繼續(xù)花了約2小時(shí)完成文檔的粗略閱讀,接著制定了后面的計(jì)劃:
- (1)完成流程模型的部署(之前已完成模型創(chuàng)建和保存);
- (2)發(fā)起流程;
- (3)查看任務(wù)列表;
- (4)完成任務(wù);
- (5)完成流程;
- (6)下一階段:復(fù)雜自定義流程。
接下來首先是研究官方demo,找到并體驗(yàn)發(fā)布流程以及任務(wù)列表的功能,然后就是從官方的源碼中拷貝需要的代碼到自建工程里面。遇到了一點(diǎn)問題,就是官方demo中發(fā)布流程的業(yè)務(wù)過于復(fù)雜不適合我們項(xiàng)目,但是一開始代碼是按照官方demo寫的,所以運(yùn)行起來之后總是報(bào)json解析錯(cuò)誤,后來慢慢慢慢跟代碼,發(fā)現(xiàn)可以完全移除官方發(fā)布流程中的很多步驟,這樣才會(huì)更加適合我們的項(xiàng)目。然后就順利完成了模型的部署。
接下來為了后面的測(cè)試更加方便,自己新增了幾個(gè)rest接口將引擎下面的服務(wù)方法通過瀏覽器暴露,這樣便于后期的調(diào)試。
為了從全新的簡(jiǎn)單流程開始測(cè)試,就需要把之前創(chuàng)建的錯(cuò)誤流程刪除,并且正式開發(fā)時(shí)也需要?jiǎng)h除流程的功能,所以接著就重新開發(fā)刪除流程的接口。
day#5(11.20)
清除之前的數(shù)據(jù)之后,準(zhǔn)備創(chuàng)建一個(gè)較為真實(shí)的請(qǐng)假流程,流程圖如下:

流程圖畫好之后,發(fā)現(xiàn)流程圖及相關(guān)屬性亂碼。然后百度,一開始以為很好解決就直接從網(wǎng)上copy了一些代碼,最后發(fā)現(xiàn)都沒有解決問題,然后才開始修改editor_json接口,然后定位到造成問題的原因是因?yàn)镴sonNode對(duì)象在restcontroller返回時(shí)序列化造成的亂碼問題。嘗試將JsonNode替換為普通object,中文亂碼消失。但是普通java object有一個(gè)動(dòng)態(tài)的model對(duì)象,如果使用fasterxml的JsonNode發(fā)現(xiàn)無法正常序列化,改換為fastjson的JSONObject對(duì)象,居然可以正常序列化!至此,中文亂碼問題已解決。
接下來便是發(fā)起流程。發(fā)起流程之后,獲取流程實(shí)例,獲取taskService,獲取當(dāng)前task,嘗試設(shè)置task的owner和表單數(shù)據(jù)(通過流程變量),然后完成任務(wù)。
代碼雖然簡(jiǎn)單,但是發(fā)現(xiàn)任務(wù)的owner根本設(shè)置不上去,后來嘗試了各種方法,才發(fā)現(xiàn)問題所在:taskService.complete方法調(diào)用之后,當(dāng)前Task對(duì)象已從數(shù)據(jù)庫(kù)刪除了,如果再用此task對(duì)象的id去數(shù)據(jù)庫(kù)查詢將查不到該task,這時(shí)(complete方法調(diào)用之后),其實(shí)當(dāng)前流程實(shí)例下面包含的任務(wù)已經(jīng)是新創(chuàng)建的task了(有不同的id),之前那個(gè)complete的task已經(jīng)到history表里面去了(owner也設(shè)置成功了的)。
由于我們將要開發(fā)方便普通工作人員使用的自定義流程設(shè)計(jì)器,所以,activiti官方的流程設(shè)計(jì)器的屬性編輯器基本不能用了,因?yàn)槟莻€(gè)編輯器幾乎沒有人會(huì)用的。要替換這個(gè)編輯器,最重要的部分就是選擇assignee了,設(shè)計(jì)的原型圖如下:

設(shè)計(jì)思路:
(1)在填寫表單環(huán)節(jié),增加自定義環(huán)節(jié)屬性:是否需要用戶指定下一環(huán)節(jié)的審批人。如果下一環(huán)節(jié)只有一個(gè),并且業(yè)務(wù)需求確實(shí)是需要指定審批人,那么在用戶提交表單之前,需要選擇下一環(huán)節(jié)審批人。數(shù)據(jù)傳到服務(wù)器端之后,服務(wù)器首先保存任務(wù)的表單數(shù)據(jù),然后完成任務(wù),然后獲取下一個(gè)環(huán)節(jié)任務(wù),然后設(shè)置下一環(huán)節(jié)的審批人(來自參數(shù));
(2)如果下一環(huán)節(jié)審批人不需要流程發(fā)起人指定,那么提交表單后,服務(wù)器端處理邏輯:開始流程 -> 獲取第一個(gè)任務(wù) -> 設(shè)置owner和表單數(shù)據(jù) -> 完成任務(wù)。在下一環(huán)節(jié)的create event listener代碼中早已設(shè)置好assignee的計(jì)算邏輯。只要進(jìn)入listener,就會(huì)根據(jù)環(huán)節(jié)定義的審批人進(jìn)行計(jì)算得到當(dāng)前任務(wù)的指派者。
(3)下一環(huán)節(jié)的assignee已設(shè)置好,只要該用戶打開任務(wù)并進(jìn)行審批就可以讓流程運(yùn)轉(zhuǎn)起來了。
day#6(11.21)
開始改造模型設(shè)計(jì)器:
(1)簡(jiǎn)化工具箱,只保留空開始事件、用戶任務(wù)、并行網(wǎng)關(guān)、排他網(wǎng)關(guān)、空結(jié)束任務(wù)、文本備注;
(2)簡(jiǎn)化流程屬性編輯器;
(3)簡(jiǎn)化其他控件屬性編輯器;
(4)用戶任務(wù)控件增加自定義角色配置;
通過跟蹤分析js代碼,發(fā)現(xiàn)工具箱數(shù)據(jù)源來自stencilset_bpmn.json,然后備份好后刪除不需要的控件,運(yùn)行,成功。
繼續(xù)通過跟蹤分析js代碼,發(fā)現(xiàn)屬性編輯器的數(shù)據(jù)源同樣來自stencilset_bpmn.json,然后修改英文為中文,運(yùn)行,亂碼,同樣是fasterxml的JsonNode對(duì)象造成,替換為fastjson的JSONObject,順利解決中文亂碼問題。
首先修改User Task控件,將額外屬性移除,運(yùn)行,發(fā)現(xiàn)設(shè)計(jì)圖上面多了兩個(gè)圖標(biāo),然后一個(gè)屬性一個(gè)屬性移除,終于發(fā)現(xiàn)"multiinstance_typepackage","isforcompensationpackage"這兩個(gè)屬性不能移除,說白了這就是activiti的這兩個(gè)屬性的默認(rèn)值的bug。但是這兩個(gè)屬性不移除的話,編輯器上面就會(huì)顯示,這顯然是不符合需求的。于是開始找代碼看哪里用到這兩個(gè)屬性了。嘗試修改多個(gè)地方的代碼之后,雖然可以實(shí)現(xiàn)但終覺不妥,后來突然發(fā)現(xiàn)屬性object有個(gè)popular屬性,將其修改為false,編輯器上就直接隱藏了,甚是方便。
后來有個(gè)員工要離職,工作交接花了3小時(shí)左右。
接著完成稍微復(fù)雜一點(diǎn)的流程設(shè)計(jì),如圖:

先從簡(jiǎn)單的控件開始吧。首先需要改造的就是排他網(wǎng)關(guān)出去的兩個(gè)條件順序流。思路:
(1)為每個(gè)順序流增加flowtype字段,如果該字段有值,那么在保存模型的時(shí)候,就將該字段的值獲取出來并按照格式填充到conditionsequenceflow屬性中,并刪除模型的flowtype屬性;
(2)設(shè)置條件的表達(dá)式大致為:$(APPROVAL_RESULT=='"+flowtype+"'),這樣在上一環(huán)節(jié)完成審批時(shí),會(huì)添加一個(gè)名稱為APPROVAL_RESULT的流程變量,從而實(shí)現(xiàn)條件的跳轉(zhuǎn);
(3)后期再將flowtype的編輯器改為下拉列表,這樣用戶只需要選擇:“審批同意”、“審批拒絕”、“無條件”、“其他表單條件。。。”就可以完成條件的設(shè)置,無需輸入表達(dá)式了。
修改代碼之后運(yùn)行,直接成功。下班了,第二天可以測(cè)試環(huán)節(jié)是否可以自動(dòng)跳轉(zhuǎn)了。
day#7(11.22)
由于找了很久的文檔沒有找到UEL表達(dá)式如何判斷字符串相等,所以索性改成判斷bool相等,因?yàn)橛衐emo嘛。然后更新流程模型,更新代碼。最終修復(fù)editor_json的代碼如下:
1 public static void fix(JSONObject json) { 2 JSONArray shapes = json.getJSONArray("childShapes"); 3 JList<JSONObject> flows = JList.from(shapes).select(x -> (JSONObject) x).where(x -> { 4 JSONObject stencil = x.getJSONObject("stencil"); 5 if (!stencil.getString("id").equals("SequenceFlow")) { 6 return false; 7 } 8 JSONObject properties = x.getJSONObject("properties"); 9 String flowtype = properties.getString("flowtype"); 10 if (StringHelper.isNullOrWhitespace(flowtype)) { 11 return false; 12 } 13 if (flowtype.equals("agreed") || flowtype.equals("rejected")) { 14 JSONObject condition = new JSONObject(); 15 JSONObject expression = new JSONObject(); 16 expression.put("staticValue", "${APPROVED==" + flowtype.equals("agreed") + "}"); 17 expression.put("type", "static"); 18 condition.put("expression", expression); 19 properties.put("conditionsequenceflow", condition); 20 } 21 //properties.remove("flowtype"); 22 return true; 23 }); 24 }
開始測(cè)試:
(1)填寫請(qǐng)假表單,complete流程開始后的默認(rèn)任務(wù)(將流程推進(jìn)到經(jīng)理審核環(huán)節(jié)),返回流程實(shí)例ID;
(2)查詢?cè)摿鞒虒?shí)例下面的全部活躍任務(wù);
(3)找到經(jīng)理審核的任務(wù)(只有這一個(gè)任務(wù));
(4)根據(jù)經(jīng)理審核任務(wù)ID對(duì)請(qǐng)假進(jìn)行審核(approved=true/false),complete任務(wù)時(shí)傳入APPROVED流程變量(值來自于controller方法參數(shù));
(5)再次根據(jù)流程實(shí)例ID查詢活躍任務(wù),發(fā)現(xiàn)流程已經(jīng)根據(jù)approved參數(shù)自動(dòng)選擇分支進(jìn)行流轉(zhuǎn)了。
(6)至此,順序流條件控制測(cè)試成功。
接下來開始擴(kuò)展UserTask的自定義屬性了。
首先在stencilset_bpmn.json中增加一個(gè)complexassigneepackage,然后將此package添加到UserTask的屬性列表中,然后在properties.js中增加響應(yīng)配置,以及創(chuàng)建相應(yīng)的html模板代碼和angularJS的controller。
下圖則是這個(gè)自定義屬性(復(fù)雜指派者屬性)的編輯器原型:

對(duì)應(yīng)到服務(wù)器端的數(shù)據(jù)結(jié)構(gòu)為:
1 public class DataDto { 2 private String displayText; 3 private JList<InitiatorType> initiators; 4 private JList<Long> roleIds; 5 private JList<Long> departmentIds; 6 private JList<Long> userIds; 7 }
前期為了快速測(cè)試流程的運(yùn)轉(zhuǎn)流程,并不需要去開發(fā)復(fù)雜的前端交互界面,最簡(jiǎn)單的方式,莫過于直接修改編輯器JS源碼,然后直接從瀏覽器控制臺(tái)里面將JS對(duì)象存入對(duì)應(yīng)的屬性值里面即可。這個(gè)構(gòu)想進(jìn)行的非常順利,可以順利的顯示和保存這個(gè)DataDto的數(shù)據(jù)。
但是,這個(gè)自定義屬性在導(dǎo)出流程模型為XML的時(shí)候,生成的XML卻不包含這個(gè)自定義屬性,于是開始各種百度谷歌官方文檔,網(wǎng)上有一些例子,但大多都是復(fù)制來復(fù)制去,并且相比原文有些錯(cuò)漏,而官方文檔,不管是5還是6版的都沒有提及自定義屬性的事,最后在大概兩三小時(shí)的挫敗中終于找到了“原文鏈接”,雖然不是完全適用,但原文畢竟沒有錯(cuò)漏,提供了關(guān)鍵示例代碼,終于在XML中顯示出了自定義屬性。
然后測(cè)試發(fā)布流程模型,沒有問題。然后修改UserTask的create event listener,從該listener中獲取環(huán)節(jié)定義中的自定義屬性,然后根據(jù)自定義屬性的值修改任務(wù)的候選人/組/指派者。
改好代碼之后就開始進(jìn)入測(cè)試,發(fā)現(xiàn)在UserTask的extensionelements節(jié)點(diǎn)中的數(shù)據(jù)不完整,被截?cái)嗔耍缓笮薷腦ML增加CDATA包裹,再測(cè)試,無效,然后修改XML中屬性為URL編碼,結(jié)果可以正確解析了。
至此,自定義屬性已擴(kuò)展成功。等著第二天來測(cè)試我們這個(gè)復(fù)雜的指派系統(tǒng)了吧。
day#8(11.23)
首先完成UserTask的create event listener的指派代碼的編寫,非常順利。
然后從填寫表單開始測(cè)試,加上斷點(diǎn),順利完成指派者、候選人、候選組的測(cè)試,非常成功。
下面是對(duì)activiti指派者/候選人測(cè)試的一些總結(jié),官方文檔中不曾提及的:
(1)當(dāng)調(diào)用taskService.addCandidateUser和addCandidateGroup之后,可以通過TaskQuery查詢出任務(wù);
(2)在上一步之后,如果調(diào)用了taskService.claim認(rèn)領(lǐng)任務(wù)之后,再調(diào)用第一步的根據(jù)候選人/組進(jìn)行查詢就查不出結(jié)果了,但是從該任務(wù)的identityLinks中仍然可以看到該任務(wù)的候選人/組并沒有被清空,還是以前的值,并且claim之后,任務(wù)的assignee就是認(rèn)領(lǐng)者了;
(3)任務(wù)認(rèn)領(lǐng)之后如果再次釣claim任務(wù)就會(huì)報(bào)錯(cuò);
(4)任務(wù)認(rèn)領(lǐng)者可以是任何人,也就是說不一定非要是候選人或候選組里面的人。
接下來便是開始開發(fā)表單了,另一個(gè)重要節(jié)點(diǎn)。
首先再次仔細(xì)閱讀了官方的關(guān)于表單的知識(shí),沒有太大用處。然后又在網(wǎng)上找了一些自定義表單的文章,用處也不大。因?yàn)槲业哪繕?biāo)很明確,就是要搞清楚表單是如何存儲(chǔ)在流程模型XML中的,以及如何在任務(wù)詳情頁面呈現(xiàn)。
既然網(wǎng)上的資源質(zhì)量有限,又只得把官方的demo運(yùn)行起來,創(chuàng)建流程,創(chuàng)建表單,發(fā)起流程,一步一步測(cè)試,都可以跑通。為了知道表單定義在XML中的體現(xiàn),直接下載已定義好的流程XML,發(fā)現(xiàn)表單數(shù)據(jù)在XML中只有form-properties列表和form-key兩個(gè)屬性,那表單的定義數(shù)據(jù)就一定在其他地方存儲(chǔ)了。
day#9(11.26)
花了近一半的時(shí)間開會(huì)以及投入到其他項(xiàng)目。
由于之前已經(jīng)研究清楚,流程模型XML并不會(huì)包含表單信息,因此表單信息只能用另一張數(shù)據(jù)庫(kù)表來存儲(chǔ)。
為了讓流程設(shè)計(jì)器更加友好方便使用,我設(shè)計(jì)了如下的表單定制化界面。

如此,用戶只需要輸入表單設(shè)計(jì)器編輯好的表單key,程序便會(huì)自動(dòng)加載表單信息,然后用戶只需要勾選當(dāng)前環(huán)節(jié)哪些字段需要顯示,哪些字段可以編輯。這可能是目前市面上最好的流程自定義表單設(shè)計(jì)器了。
思路清楚,接下來便是跟改造自定義指派的自定義屬性一樣,增加自定義表單屬性的編輯器。同樣,在前期,只通過代碼生成XML,先不做界面。
表單設(shè)計(jì)器的環(huán)節(jié)自定義屬性開發(fā)很快完成。接著便是完成表單相關(guān)表/數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì),保存數(shù)據(jù)庫(kù),開發(fā)很快完成。
day#10(11.27)
由于activiti官方將表單數(shù)據(jù)保存到跟流程模型同一張表里面,我覺得這并不是太好,還是新建一張表用來存放表單數(shù)據(jù)更好。
表單表包含簡(jiǎn)單的key/name/css/fields/buttons字段,其中fields和buttons都是json格式的數(shù)據(jù),用json而不是子表是因?yàn)榭紤]到后期擴(kuò)展表單域的靈活性。
然后就是開始開發(fā)自定義表單模塊了,當(dāng)然,為了更快讓整個(gè)流程跑起來,先就做個(gè)簡(jiǎn)單版本的表單設(shè)計(jì)器吧,如下圖:

這個(gè)表單設(shè)計(jì)器雖然簡(jiǎn)單,但是工具箱以及表單實(shí)時(shí)展示部分已經(jīng)完成了非常容易擴(kuò)展的架構(gòu)方式,這樣對(duì)于優(yōu)化上圖中的控件,以及增加復(fù)雜控件都會(huì)變得非常簡(jiǎn)單。該模塊采用vuejs渲染。
到下班前,已基本完成了工具箱及表單的呈現(xiàn)部分。
day#11(11.28)
花了點(diǎn)時(shí)間優(yōu)化表單設(shè)計(jì)器的體驗(yàn)及增加功能,然后就是保存數(shù)據(jù)庫(kù),以及從數(shù)據(jù)庫(kù)加載呈現(xiàn)表單,上午就結(jié)束了。
總的來講,表單設(shè)計(jì)器從界面設(shè)計(jì)到保存數(shù)據(jù)庫(kù)到WEB前端,花了一個(gè)下午和一個(gè)上午,已經(jīng)可以正常運(yùn)行了。
接下來便是將表單設(shè)計(jì)器整合到流程設(shè)計(jì)器里面。
本來想繼續(xù)用vuejs來渲染表單部分,這樣的話可以重用表單設(shè)計(jì)器的渲染代碼,但是嘗試了一下發(fā)現(xiàn)根本不行,因?yàn)閍ngularjs和vuejs都是實(shí)時(shí)渲染,angularjs的html只要被vuejs接管之后,我們看到的html已經(jīng)是vuejs重新渲染生成的了,反之亦然。
所以,還是老老實(shí)實(shí)用angularjs做吧。
先花了一個(gè)小時(shí)左右把a(bǔ)ngularjs的文檔大致看了下,發(fā)現(xiàn)遠(yuǎn)沒有vuejs的文檔好讀。比如,我想找一個(gè)如何渲染列表以及如何渲染動(dòng)態(tài)屬性,硬是找了很久沒有找到。最后不得不在官方文檔中通過找對(duì)應(yīng)的例子一個(gè)一個(gè)完成渲染。
至此,流程設(shè)計(jì)器里面的表單屬性編輯器已能基本上呈現(xiàn)出來了,在右側(cè)勾選屬性時(shí)就顯示,未勾選時(shí)則隱藏,從而做到實(shí)時(shí)的表單預(yù)覽。如下圖:

接下來便是將這個(gè)表單的數(shù)據(jù)存到數(shù)據(jù)庫(kù),以及從數(shù)據(jù)庫(kù)加載數(shù)據(jù)并還原表單了。
day#12(11.29)
非常順利的完成了表單屬性的保存以及從數(shù)據(jù)庫(kù)還原表單,然后將整個(gè)流程的所有用戶環(huán)節(jié)的數(shù)據(jù)進(jìn)行了更新,發(fā)現(xiàn)現(xiàn)在的整個(gè)表單設(shè)計(jì)確實(shí)非常易用,簡(jiǎn)單直觀!
接下來為了更好地進(jìn)行后邊的測(cè)試,先把之前落下的順序流條件編輯器給優(yōu)化一下。
按照官方提供的bool值模板,非常順利的完成了自定義的流程條件編輯器,最終界面如下:

接著,為了讓流程真正的跑起來,還需要對(duì)指派屬性進(jìn)行改造。
首先是定義彈出打開后加載數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),然后生成一些假的數(shù)據(jù)方便測(cè)試。然后就是利用angularjs對(duì)前端進(jìn)行綁定。最終實(shí)現(xiàn)效果如下圖:
實(shí)現(xiàn)的功能包括:
(1)部門跟用戶采用樹形數(shù)據(jù)結(jié)構(gòu)生成,支持實(shí)時(shí)名稱過濾,且只要有下級(jí)節(jié)點(diǎn),上級(jí)節(jié)點(diǎn)就一定會(huì)顯示;
(2)4個(gè)tab內(nèi)容區(qū)點(diǎn)擊復(fù)選框就會(huì)實(shí)時(shí)更新到右邊已選列表;
(3)已選列表點(diǎn)擊X就會(huì)取消選中對(duì)應(yīng)復(fù)選框;
(4)根據(jù)4個(gè)tab已選內(nèi)容生成合理的顯示文本(紅色框框區(qū));
(5)點(diǎn)擊保存將已選項(xiàng)保存到數(shù)據(jù)庫(kù)。
所有這些編碼工作在一個(gè)下午完成。接下來第二天便是從數(shù)據(jù)庫(kù)恢復(fù)表單了。
day#13(11.30)
首先完成了從數(shù)據(jù)庫(kù)還原表單已選數(shù)據(jù),接著刪除了UserTask屬性列表中的TaskListeners屬性編輯器,改為保存流程模型時(shí)對(duì)所有UserTask默認(rèn)添加我們的自定義任務(wù)指派管理器(在任務(wù)創(chuàng)建時(shí)運(yùn)行)。這2步都完成的非常順利。
接下來便可以真正測(cè)試流程運(yùn)轉(zhuǎn)了,在這個(gè)過程中還需要還原表單設(shè)計(jì)器完成的表單,以及控制顯示隱藏和可編輯性。
在任務(wù)詳情頁還原表單,以及添加簡(jiǎn)單的表單認(rèn)證,以及控制顯示隱藏表單字段和按鈕,這些開發(fā)工作都進(jìn)展的非常順利。
接下來便可以測(cè)試流程的運(yùn)轉(zhuǎn)了:
(1)發(fā)起流程,同時(shí)將任務(wù)指派給當(dāng)前用戶;
(2)用戶進(jìn)入任務(wù)詳情,能夠顯示表單,填寫表單,能夠正常提交和保存草稿;
(3)提交表單后,新創(chuàng)建的任務(wù)順利指派給了下一級(jí)節(jié)點(diǎn)。
總的來講,整個(gè)流程的運(yùn)轉(zhuǎn)跟預(yù)期的差不多,只是在審批人選擇拒絕時(shí)讓流程回到第一個(gè)節(jié)點(diǎn)時(shí),流程發(fā)起者無法再次看到該任務(wù),因?yàn)槿蝿?wù)的指派者變成空了。所以之前的方案得改了。改造后,流程開始的第一個(gè)環(huán)節(jié)即必須設(shè)置指派者或候選人,這樣也更加靈活了,然后startProcessInstanceByKey之后就不需要設(shè)置指派者了,因?yàn)榱鞒套詣?dòng)進(jìn)入我們自定義的復(fù)雜指派者監(jiān)聽器模塊,該監(jiān)聽器會(huì)自動(dòng)根據(jù)流程定義對(duì)指派者進(jìn)行設(shè)定,這樣,不管時(shí)流程剛剛開啟還是以后任何時(shí)候回退到該環(huán)節(jié),該環(huán)節(jié)都一定會(huì)有指派者了。
OK,至此整個(gè)自定義流程已經(jīng)全部完成了,接下來改下界面最后再完整的測(cè)試一遍吧。
最終的測(cè)試過程
第0步:設(shè)計(jì)流程圖和表單,以及準(zhǔn)備工作

下面對(duì)部分環(huán)節(jié)進(jìn)行說明:
(1)填寫請(qǐng)假單環(huán)節(jié)的指派者設(shè)置為流程發(fā)起人;
(2)部門經(jīng)理審批設(shè)置為發(fā)起者所在部門負(fù)責(zé)人(個(gè)人);
(3)CEO審批設(shè)置為指定的個(gè)人(陳麗);
(4)HR備份指派了3個(gè)人作為候選人;
(5)財(cái)務(wù)備份選擇的是財(cái)務(wù)處管理員,一種系統(tǒng)角色。
表單配置還是跟之前的一樣:

下面是整個(gè)測(cè)試過程用到的一個(gè)簡(jiǎn)單界面:

第1步:發(fā)起流程

測(cè)試?yán)佣际褂胾ser05進(jìn)行流程的發(fā)起。發(fā)起之后,可以看到指派給user05的任務(wù)列表:

第2步 填寫申請(qǐng)單
點(diǎn)擊上一步的查看任務(wù)詳情,進(jìn)入任務(wù)詳情頁:

記住流程實(shí)例ID為:145001
填寫表單后,點(diǎn)擊保存草稿。然后再刷新頁面,發(fā)現(xiàn)任務(wù)數(shù)據(jù)已保存,證明保存草稿按鈕工作正常。然后再點(diǎn)擊提交。
第3步:部門經(jīng)理審批
回到測(cè)試界面,輸入流程實(shí)例ID,加載該流程實(shí)例的活動(dòng)任務(wù),如下圖:

可以看到流程已經(jīng)運(yùn)轉(zhuǎn)到部門經(jīng)理審批環(huán)節(jié)了,指派者也已經(jīng)正確的設(shè)置為了user05-department_manager。點(diǎn)擊查看任務(wù)詳情,如下圖:

這里我們點(diǎn)擊拒絕。
第4步:重新提交申請(qǐng)表單
再次回到測(cè)試界面,加載流程實(shí)例的活動(dòng)任務(wù),如下圖:

可以看到流程已經(jīng)正確退回到第一個(gè)填寫請(qǐng)假單的環(huán)節(jié)了,指派者也正確的設(shè)置為了流程發(fā)起人user05。點(diǎn)擊查看任務(wù)詳情,再次進(jìn)入填寫表單界面,如下圖:

再次點(diǎn)擊提交。
第5步:部門經(jīng)理再次審批
再次回到測(cè)試界面,加載該流程實(shí)例的活動(dòng)任務(wù),如下圖:

再次看到流程已經(jīng)運(yùn)轉(zhuǎn)到部門經(jīng)理審批環(huán)節(jié)。點(diǎn)擊查看任務(wù)詳情,如下如:

這次點(diǎn)同意。
第6步:CEO審批
回到測(cè)試界面,再次加載流程實(shí)例的活動(dòng)任務(wù),如下圖:

可以看到流程已經(jīng)運(yùn)轉(zhuǎn)到CEO審批環(huán)節(jié)。點(diǎn)擊查看任務(wù)詳情,如下圖:

然后點(diǎn)擊同意按鈕。
第7步:HR備份
回到測(cè)試界面,再次加載流程實(shí)例的活動(dòng)任務(wù),如下圖:

可以看到指派為空,候選組為空,候選用戶有3個(gè)。這里我就不去測(cè)試領(lǐng)取任務(wù)的功能了,直接點(diǎn)擊查看任務(wù)詳情,然后點(diǎn)提交(就不截圖了)。
第8步:財(cái)務(wù)備份
回到測(cè)試界面,再次加載流程實(shí)例的活動(dòng)任務(wù),如下圖:

可以看到指派者為空,候選用戶為空,候選組為一個(gè)角色的ID。點(diǎn)擊查看任務(wù)詳情,如下圖:

在任務(wù)詳情頁面可以看到指派者仍然為空,沒關(guān)系,直接點(diǎn)擊提交。
第9步:完成
這時(shí)再次回到測(cè)試界面,輸入流程實(shí)例ID,可以看到已經(jīng)沒有任何活動(dòng)任務(wù)了,該流程實(shí)例已經(jīng)順利完成了。
總結(jié)
整個(gè)13天的學(xué)習(xí)和改造activiti的工作中,沒有什么大的技術(shù)難題,主要還是一個(gè)學(xué)習(xí)別人框架源碼的一個(gè)過程。最痛苦的莫過于官方的文檔實(shí)在太不全了,對(duì)activiti的改造幾乎全靠讀官方demo的前后端源碼。
THE END

浙公網(wǎng)安備 33010602011771號(hào)