activiti6.0+springboot 項(xiàng)目搭建

首先搭建流程設(shè)計(jì)器

  1. 官網(wǎng)下載activiti6.0,下載下來(lái)之后解壓后找到activiti-app.war文件放入本地tomcat中運(yùn)行,訪問(wèn)路徑為(localhost:8080/activiti-app),賬號(hào):admin 密碼:test

  2. 修改設(shè)計(jì)器數(shù)據(jù)庫(kù),設(shè)計(jì)器默認(rèn)的使用的是h2數(shù)據(jù)庫(kù),需要修改tomcat中項(xiàng)目\activiti-app\WEB-INF\classes\META-INF\activiti-app\activiti-app.properties,選擇需要的數(shù)據(jù)庫(kù)設(shè)置

#datasource.driver=org.h2.Driver
#datasource.url=jdbc:h2:mem:activiti;DB_CLOSE_DELAY=-1

datasource.driver=com.mysql.jdbc.Driver
datasource.url=jdbc:mysql://**********:3306/****?characterEncoding=UTF-8

datasource.username=****
datasource.password=****

搭建activiti項(xiàng)目(該項(xiàng)目使用springboot,所以和常規(guī)搭建可能稍有不同)

  1. 引入pom
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter-basic</artifactId>
    <version>6.0.0.RC1</version>
</dependency>
  1. 關(guān)閉activiti自動(dòng)部署(因?yàn)槲覀兪褂昧鞒淘O(shè)計(jì)器部署,不使用具體文件訪問(wèn)方式),在application.properties中加入以下代碼
spring.activiti.check-process-definitions=false
  1. 設(shè)計(jì)流程

    遇到問(wèn)題:

    在設(shè)計(jì)流程中我們發(fā)現(xiàn)在定義審批人時(shí)遇到了一個(gè)無(wú)法忽略的問(wèn)題————無(wú)法找到指定審批人,意思是設(shè)置了一個(gè)審批人的參數(shù),沒(méi)辦法根據(jù)申請(qǐng)人獲取到相應(yīng)的審批人,或許你會(huì)覺(jué)得根據(jù)申請(qǐng)人的部門查找不就行了嗎,但是像財(cái)務(wù)、董事長(zhǎng)之類的在部門之上的的部門和人,就沒(méi)辦法找對(duì)并且獲取了。

    解決:

    暫時(shí)的辦法是將每個(gè)部門的所有流程全部分開(kāi),每個(gè)審批人使用角色定義,這就解決了獲取不到對(duì)應(yīng)審批人的問(wèn)題,但是緊接著又有一個(gè)問(wèn)題,申請(qǐng)人如何才能申請(qǐng)到自己部門的對(duì)應(yīng)流程呢,又是一個(gè)大問(wèn)題,問(wèn)題好像又回到了原點(diǎn),但是有何開(kāi)始的問(wèn)題有一些不同,從同一個(gè)流程獲得不同的用戶角色,變成了從一個(gè)部門獲取相應(yīng)部門流程,那就簡(jiǎn)單了,只需要?jiǎng)?chuàng)建一個(gè)表將他們對(duì)應(yīng)起來(lái)就可以了,但是這涉及到三個(gè)字段,一個(gè)是部門id,一個(gè)是流程id,還有一個(gè)類型id(就是這個(gè)流程是干什么的,借款、報(bào)銷。。。),三個(gè)信息組成一條對(duì)應(yīng)流程片,好像有點(diǎn)復(fù)雜,管理頁(yè)面也有點(diǎn)難做。

舉個(gè)例子:


部門與流程聯(lián)系.png

從這張圖上能清楚的看出我們面臨的問(wèn)題,因?yàn)槊總€(gè)部門都有同樣的流程類型,但是又不同的流程具體實(shí)現(xiàn),如何才能從三個(gè)選擇減少成兩個(gè)甚至一個(gè)呢?

鑒于這一點(diǎn),我們只能在流程id上做手腳了,將流程id根據(jù)不同的類型使用不同的名字前綴,這樣就從三個(gè)字段變成了兩個(gè),例如:流通部門國(guó)內(nèi)借款流程,我們將id設(shè)為 gnjk_ltjk ,這樣我們就能根據(jù)id的前綴gnjk查到該流程為國(guó)內(nèi)借款類型。可能有人會(huì)問(wèn)為什么不直接將部門也放在流程id里面,這樣就不用創(chuàng)建表了,也想過(guò)這個(gè)問(wèn)題,而且也可以這樣做,但未免太死板了,完全不給以后融合流程機(jī)會(huì)了,畢竟現(xiàn)在一個(gè)部門就要n條流程還是有弊端的,就是重復(fù)流程過(guò)多的問(wèn)題,所以先暫時(shí)這樣吧

  1. 表設(shè)計(jì)

    在正式走流程之前還有一個(gè)要做,就是業(yè)務(wù)表的設(shè)計(jì),盡管activiti自帶了大量的表,但是還是要設(shè)計(jì)關(guān)于自己項(xiàng)目的業(yè)務(wù)表才行。具體的業(yè)務(wù)表就不詳細(xì)說(shuō)了,無(wú)非是報(bào)銷、借款。。。之類的,但是有一個(gè)小技巧(前人經(jīng)驗(yàn)),因?yàn)榫唧w業(yè)務(wù)的不同,所有會(huì)有多個(gè)表,在審批時(shí)查找起來(lái)會(huì)相當(dāng)?shù)穆闊?,而且也不易于列表展示,所以提出一個(gè)共有的審核主表,該主表有流程id、業(yè)務(wù)id和具體業(yè)務(wù)的簡(jiǎn)要信息,這樣才查找審批列表時(shí)直接來(lái)一個(gè)表中查找就可以了,而在審批需要查看詳情時(shí),又可以根據(jù)不同的類型去對(duì)應(yīng)的表中查找審批,相當(dāng)方便。

CREATE TABLE `s_auditor_core` (
  `id` varchar(50) NOT NULL COMMENT '使用nextval函數(shù)獲取',
  `business_id` varchar(50) DEFAULT NULL COMMENT '業(yè)務(wù)id',
  `process_id` varchar(64) DEFAULT NULL COMMENT '流程id',
  `create_user` bigint(10) DEFAULT NULL COMMENT '用戶id',
  `create_date` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `sketch` varchar(255) DEFAULT NULL COMMENT '備注',
  `apply_type` varchar(255) DEFAULT NULL COMMENT '申請(qǐng)類型(和權(quán)限表中的權(quán)限一致)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

  1. 啟動(dòng)流程


    國(guó)內(nèi)借款_流通部門

    因?yàn)橛玫氖莝pringboot,所以不需要手動(dòng)的創(chuàng)建xml文件,然后配置一大堆東西,只需要引包,然后再需要使用的地方聲明接口就好了,非常的方便。

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;

然后在獲得對(duì)應(yīng)的流程id后,將業(yè)務(wù)詳細(xì)信息,流程,審批主表一次執(zhí)行保存,一次完整的流程啟動(dòng)就完成了。

     * 
     * @param bGnjk  國(guó)內(nèi)借款業(yè)務(wù)參數(shù)
     * @param htmlSign 模塊標(biāo)記,這個(gè)為借款、報(bào)銷。。。類型
     * @return
     * @throws Exception
     */
    public CommonDomain<String> gnjkProcessStart(BGnjk bGnjk,String htmlSign)throws Exception {
        CommonDomain<String> commonDomain = new CommonDomain<>();
        //配置activiti需要參數(shù)
        Map m = new HashMap();
        m.put(ACTKeyTool.MONEY,bGnjk.getMoney());
        List<SUser> process = sUserMapper.getProcessByUser(bGnjk.getCreateUser().intValue(),htmlSign);
        if(process==null||process.size()!=1){
            throw new Exception("流程啟動(dòng)獲得多條流程");
        }
        //啟動(dòng)activiti
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(process.get(0).getProcess_key(),m);
        //保存業(yè)務(wù)數(shù)據(jù)
        bGnjkMapper.insertSelective(bGnjk);


        //保存審批主表
        SAuditorCore sAuditorCore = new SAuditorCore();
        sAuditorCore.setApplyType(htmlSign);
        sAuditorCore.setBusinessId(bGnjk.getId());
        sAuditorCore.setCreateDate(new Date());
        sAuditorCore.setCreateUser(bGnjk.getCreateUser());
        sAuditorCore.setProcessId(processInstance.getId());
        sAuditorCore.setSketch(bGnjk.getDescribe());
        SAuditorCoreMapper.insertSelective(sAuditorCore);

        //返回結(jié)果封裝
        commonDomain.setStatuscode(PublicMsg.ResultCode.SUCCESS.value());
        commonDomain.setMsg(PublicMsg.SUCCESS_INFO);
        return commonDomain;
    }

當(dāng)完成這一步的時(shí)候,業(yè)務(wù)詳細(xì)表中有了業(yè)務(wù)的數(shù)據(jù),act_ru_task中有個(gè)流程信息,并指向第一個(gè)usertask節(jié)點(diǎn)--流通部門經(jīng)理,審批主表中有業(yè)務(wù)詳細(xì)表和流程信息表的唯一id,并將它們聯(lián)系起來(lái),現(xiàn)在開(kāi)始,流程就正式啟動(dòng)了

  1. 審批流程

    不推薦使用activiti的taskService原生api,因?yàn)橐话忝總€(gè)項(xiàng)目都會(huì)有自己的用戶表,雖然activiti支持替換用戶表,但是配置起來(lái)貌似有些麻煩,所以根本沒(méi)有鳥(niǎo)他。下面是一些taskService常用的查詢方法源碼

query.taskCandidateGroup 源碼:
                        SELECT DISTINCT 
                          RES.* 
                        FROM
                          ACT_RU_TASK RES 
                          INNER JOIN ACT_RU_IDENTITYLINK I 
                            ON I.TASK_ID_ = RES.ID_ 
                        WHERE RES.ASSIGNEE_ IS NULL 
                          AND I.TYPE_ = 'candidate' 
                          AND (I.GROUP_ID_ IN (?))
                          
query.taskCandidateOrAssigned 源碼:
    select distinct RES.* from ACT_RU_TASK RES
    left join ACT_RU_IDENTITYLINK I on I.TASK_ID_ = RES.ID_
    WHERE (
      RES.ASSIGNEE_ = ?
      or (
        RES.ASSIGNEE_ is null
        and (
          I.USER_ID_ = ?
          or
          I.GROUP_ID_ IN (
            select g.GROUP_ID_ from ACT_ID_MEMBERSHIP g where g.USER_ID_ = ?
          )
        )
      )
    )
    
    

在源碼中可以看到基本上查的是ACT_RU_TASK和ACT_RU_IDENTITYLINK表,因?yàn)槲覀兏緵](méi)有用activit的用戶表,所以如果使用它的api會(huì)讓sql查詢變得奇怪起來(lái),因?yàn)槲覀冊(cè)谠O(shè)計(jì)流程的時(shí)候已經(jīng)指定了會(huì)使用角色來(lái)充當(dāng)審批人,那么這個(gè)角色只會(huì)出現(xiàn)在ACT_RU_IDENTITYLINK表的USER_ID_字段上,下面是我根據(jù)我們的實(shí)際業(yè)務(wù)編寫(xiě)的sql

    SELECT
     s.`name`apply_type,   -- 類型名稱
      a.`business_id`,     -- 業(yè)務(wù)詳細(xì)表id
      a.`create_date`,     -- 申請(qǐng)時(shí)間
      u.`name` user,       -- 申請(qǐng)人
      a.`id`,              -- 審批主表d
      a.`process_id`,      -- 流程id
      a.`sketch`,          -- 簡(jiǎn)介
      i.`TASK_ID_` task_id -- 任務(wù)id
    FROM
      `act_ru_task` t
      JOIN `act_ru_identitylink` i
        ON t.`ID_` = i.`TASK_ID_`
      JOIN `s_auditor_core` a ON a.process_id=t.PROC_INST_ID_
      JOIN `s_permission` s ON a.`apply_type`=s.`permission`
      JOIN `s_user` u ON u.`id`=a.`create_user`
    WHERE FIND_IN_SET(i.`USER_ID_`, #{roles})
    order by  a.`create_date` desc
    

其中s_auditor_core為審批主表,s_permission申請(qǐng)類型(客串,其實(shí)是權(quán)限表),s_user用戶表,act_ru_task任務(wù)表,act_ru_identitylink任務(wù)關(guān)聯(lián)人員表,而傳進(jìn)來(lái)的#{roles}也是一個(gè)登錄人的所有角色集合

接下來(lái)就是審批的具體實(shí)施了,因?yàn)橛X(jué)得不需要再有一個(gè)審批意見(jiàn)表,所以使用act_hi_comment來(lái)代替:

設(shè)置/獲得 審批人和意見(jiàn)
設(shè)置:
審批人一般為登錄人
Authentication.setAuthenticatedUserId("審批人");
taskService.addComment(task.getId(), task.getProcessInstanceId(), "批注");
獲得:
List<HistoricActivityInstance> hais = historyService
                 .createHistoricActivityInstanceQuery()
                 .processInstanceId(id)
                 .activityType("userTask").list();
         for (HistoricActivityInstance hai : hais) {
                String historytaskId = hai.getTaskId();
                List<Comment> comments = taskService.getTaskComments(historytaskId);
                for (Comment comment : comments) {
                    System.out.println(comment.getFullMessage()+"ren"+comment.getUserId());
                }

在同意時(shí)使用正常的審批就ok了:

taskService.complete(task.getId());

但是在拒絕時(shí)需要特殊操作:

刪除正在運(yùn)行的流程,主要用于拒絕操作
runtimeService.deleteProcessInstance(流程實(shí)力id,刪除原因(可為空))

為什么要這樣做:
因?yàn)閍ctivit并不支持突然的拒絕操作,如果想要實(shí)現(xiàn)這樣的效果,就要在每條審批中設(shè)置一條拒絕的流程線并指向結(jié)束節(jié)點(diǎn),而且還必須要用到排他網(wǎng)管,不然流程就不會(huì)結(jié)束,所以,鑒于這個(gè)情況的出現(xiàn),我們采用直接刪除流程,但流程的歷史信息不回被刪除,而且流程也會(huì)被置為結(jié)束,只需要在審批前填寫(xiě)流程審批意見(jiàn)就行了

遺留問(wèn)題

  1. 將各部門流程融合成一個(gè),現(xiàn)在重復(fù)太多不便管理
  2. 獲取下一個(gè)審批人,因?yàn)閾Q成activiti6.0后,pvm包被整體移除,所以原來(lái)的方式好像行不通了
  3. BpmnModel 操作
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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