Activiti工作流開發(fā)指南(springboot 2.x iview admin vue 前后端分離 模型設(shè)計器 動態(tài)數(shù)據(jù)權(quán)限 權(quán)限按鈕顯示 spring security)


集成Activiti 5.22胧砰,考慮到文檔資料較多未選用新版本(模型設(shè)計器改動較大)或Flowable

集成的工作流編輯器在后臺靜態(tài)資源中,記得在系統(tǒng)配置中設(shè)置訪問域名超升,開發(fā)時直接填后臺請求地址前綴 http://localhost:8888 即可

幾大常用接口

  1. RepositoryService:提供一系列管理流程部署和流程定義的API
  2. RuntimeService:在流程運行時對流程實例進(jìn)行管理與控制
  3. TaskService:對流程任務(wù)進(jìn)行管理干像,例如任務(wù)提醒、任務(wù)完成和創(chuàng)建任務(wù)等
  4. IdentityService:提供對流程角色數(shù)據(jù)進(jìn)行管理的API倦畅,這些角色數(shù)據(jù)包括用戶組遮糖、用戶及它們之間的關(guān)系
  5. HistoryService:對流程的歷史數(shù)據(jù)進(jìn)行操作,包括查詢叠赐、刪除這些歷史數(shù)據(jù)

25張表

image

1欲账、act_ge_ 通用數(shù)據(jù)表,ge是general的縮寫
2芭概、act_hi_ 歷史數(shù)據(jù)表赛不,hi是history的縮寫,對應(yīng)HistoryService接口
3罢洲、act_id_ 身份數(shù)據(jù)表踢故,id是identity的縮寫,對應(yīng)IdentityService接口
4惹苗、act_re_ 流程存儲表殿较,re是repository的縮寫,對應(yīng)RepositoryService接口桩蓉,存儲流程部署和流程定義等靜態(tài)數(shù)據(jù)
5淋纲、act_ru_ 運行時數(shù)據(jù)表,ru是runtime的縮寫院究,對應(yīng)RuntimeService接口和TaskService接口洽瞬,存儲流程實例和用戶任務(wù)等動態(tài)數(shù)

XBoot擴展基本開發(fā)指南

  • 通用流程狀態(tài)表t_act_bussiness,其中table_id字段存儲關(guān)聯(lián)的表單ID

  • 后臺僅需開發(fā)相應(yīng)表單增刪改接口业汰,存儲至單張表中片任,如t_leave,唯一需注意的地方為新增接口(添加新數(shù)據(jù)時)需關(guān)聯(lián)業(yè)務(wù)act_buniess表蔬胯,向其添加流程和表ID等信息对供,參考LeaveController

  • 前臺僅需開發(fā)相應(yīng)單個表單頁面(可以通過路由傳參實現(xiàn)相應(yīng)按鈕顯示),參考leave.vue(以菜單中配置的該路由名name跳轉(zhuǎn))氛濒,記得在src/router/router.js中添加路由

  • 最后記得在系統(tǒng)中配置相應(yīng)流程信息

    • 數(shù)據(jù)字典"業(yè)務(wù)表"中添加相應(yīng)業(yè)務(wù)表名产场,如"t_leave";"業(yè)務(wù)表單路由"中添加相應(yīng)前端表單組件路由名舞竿,如"leave"
    • 流程管理中編輯填寫關(guān)聯(lián)剛開發(fā)的表單信息京景,業(yè)務(wù)表的作用主要為用戶刪除申請時關(guān)聯(lián)刪除相應(yīng)表單數(shù)據(jù),表單路由名作用為跳轉(zhuǎn)顯示剛前端開發(fā)的表單頁面
    QQ截圖20190113212518.png
  • 流程節(jié)點審批人可根據(jù)角色骗奖、部門負(fù)責(zé)人确徙、人員設(shè)置多個醒串,設(shè)置后默認(rèn)勾選,為或簽(任意一人審批鄙皇,流程進(jìn)入下一步芜赌,即先到先審)

  • 如何實現(xiàn)會簽?

    • 請增加審批節(jié)點伴逸!
    image
  • 分支網(wǎng)關(guān)設(shè)定

    • 注意:暫僅支持互斥(排他)網(wǎng)關(guān)設(shè)置缠沈。為保證工作流簡單性,建議僅使用開始错蝴、結(jié)束洲愤、任務(wù)節(jié)點和單向連線,設(shè)置分支后流程將變得不靈活顷锰,用戶容易編輯出錯柬赐。

若部署后流程圖中文字符無法顯示,是因為服務(wù)器環(huán)境jdk中無相應(yīng)中文字體官紫,百度安裝即可(配置文件已配置微軟雅黑肛宋,若未配置默認(rèn)宋體)

互斥(排他)網(wǎng)關(guān)設(shè)定

顧名思義,當(dāng)出現(xiàn)多個分支時僅選擇一個滿足條件的分支流轉(zhuǎn)

WX20190523-221422@2x.png
  • 點擊互斥網(wǎng)關(guān)后的連線設(shè)置流轉(zhuǎn)條件的條件UEL表達(dá)式 https://www.activiti.org/userguide/#apiExpressions

    WX20190523-221523@2x.png

  • 注意勾選默認(rèn)網(wǎng)關(guān)后不得設(shè)置流轉(zhuǎn)條件万矾,否則無法部署成功


    WX20190523-221700@2x.png
  • 啟動流程時或完成一個任務(wù)節(jié)點時務(wù)必加入該變量(否則配置了流轉(zhuǎn)條件變量的流程會報錯)悼吱,示例:

// 啟動流程時添加變量,詳見ActBusinessController的apply或start方法良狈,在ActBusiness中設(shè)置params
actBusiness.getParams().put("duration", 36);

// 或者完成任務(wù)節(jié)點時加入變量
Map<String, Object> params = new HashMap<>(16);
taskService.complete(id, params);

綁定監(jiān)聽器示例

常見的業(yè)務(wù)需求需要完成一個審批流程后改變原數(shù)據(jù)狀態(tài)后添、通知審批結(jié)果消息或執(zhí)行其他操作,在結(jié)束節(jié)點上綁定監(jiān)聽器即可薪丁,一旦結(jié)束說明流程全部通過遇西,觸發(fā)自定義業(yè)務(wù)。

  • 繪制流程圖填寫定義的監(jiān)聽器類
image
image
image
  • 監(jiān)聽器示例代碼
@Slf4j
public class MyListener implements ExecutionListener {

    @Override
    public void notify(DelegateExecution delegateExecution) throws Exception {

        // 獲取關(guān)聯(lián)業(yè)務(wù)表ID變量(啟動流程代碼里已存入tableId严嗜,此處直接獲取即可)
        String tableId = (String) delegateExecution.getVariable("tableId");
        log.info(tableId);
        LeaveService leaveService = SpringContextUtil.getBean(LeaveService.class);
        Leave leave = leaveService.get(tableId);
        ... ...
    }
}

使用發(fā)起流程組件示例

  • 前端vue示例粱檀,注意各狀態(tài)下僅能顯示的各操作按鈕
<template>
    <process-start
        ref="processStart"
        @on-submit="submitedProcess"
        @on-loading="processLoading=true"
        @on-loaded="processLoading=false"
    />
    <process-cancel ref="processCancel" @on-submit="submitedProcess"/>
</template>

<script>
...
import processStart from '../../../views/my-components/xboot/process-start'
import processCancel from '../../../views/my-components/xboot/process-cancel'

export default {
  name: 'demo',
  components: {
    processStart,
    processCancel
  },
  data () {
    return {
      ...
      columns: [
        // 表頭
        {
          title: '申請狀態(tài)',
          key: 'status',
          sortable: true,
          minWidth: 110,
          fixed: 'right',
          render: (h, params) => {
            let text = '未知',
              color = ''
            if (params.row.status === 0) {
              text = '草稿'
              color = 'default'
            } else if (params.row.status === 1) {
              text = '處理中'
              color = 'orange'
            } else if (params.row.status === 2) {
              text = '已結(jié)束'
              color = 'blue'
            }
            return h('div', [
              h(
                'Tag',
                {
                  props: {
                    color: color,
                  },
                },
                text,
              ),
            ])
          },
        },
        {
          title: '申請結(jié)果',
          key: 'result',
          sortable: true,
          minWidth: 110,
          fixed: 'right',
          render: (h, params) => {
            let text = '未知',
              color = ''
            if (params.row.result == 0) {
              text = '未提交'
              color = 'default'
            } else if (params.row.result == 1) {
              text = '處理中'
              color = 'orange'
            } else if (params.row.result == 2) {
              text = '已通過'
              color = 'green'
            } else if (params.row.result == 3) {
              text = '已駁回'
              color = 'red'
            }
            return h('div', [
              h(
                'Tag',
                {
                  props: {
                    color: color,
                  },
                },
                text,
              ),
            ])
          },
        },
        {
          title: '操作',
          key: 'action',
          align: 'center',
          fixed: 'right',
          width: 260,
          render: (h, params) => {
            let result = params.row.result
            if (result == 0) {
              return h('div', [
                h(
                  'Button',
                  {
                    props: {
                      type: 'primary',
                      size: 'small',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.showProcess(params.row)
                      },
                    },
                  },
                  '發(fā)起申請',
                ),
                h(
                  'Button',
                  {
                    props: {
                      size: 'small',
                      icon: 'ios-create-outline',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.edit(params.row)
                      },
                    },
                  },
                  '編輯',
                ),
                h(
                  'Button',
                  {
                    props: {
                      type: 'error',
                      size: 'small',
                      icon: 'md-trash',
                    },
                    on: {
                      click: () => {
                        this.remove(params.row)
                      },
                    },
                  },
                  '刪除',
                ),
              ])
            }
            if (result == 1) {
              return h('div', [
                h(
                  'Button',
                  {
                    props: {
                      size: 'small',
                      type: 'warning',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.cancelProcess(params.row)
                      },
                    },
                  },
                  '撤回申請',
                ),
                h(
                  'Button',
                  {
                    props: {
                      size: 'small',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.history(params.row)
                      },
                    },
                  },
                  '審批歷史',
                ),
              ])
            }
            if (result == 2) {
                h(
                  'Button',
                  {
                    props: {
                      size: 'small',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.history(params.row)
                      },
                    },
                  },
                  '審批歷史',
                ),
              ])
            }
            if (result === 3) {
              return h('div', [
                h(
                  'Button',
                  {
                    props: {
                      type: 'primary',
                      size: 'small',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.showProcess(params.row)
                      },
                    },
                  },
                  '重新申請',
                ),
                h(
                  'Button',
                  {
                    props: {
                      size: 'small',
                      icon: 'ios-create-outline',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.edit(params.row)
                      },
                    },
                  },
                  '編輯',
                ),
              ])
            }
          },
        },
      ]
    }
  },
  methods: {
    ...
    showProcess (v) {
      // 顯示通過key發(fā)起流程組件
      this.$refs.processStart.show('demand', v.actBusinessId)
    },
    cancelProcess (v) {
      // 取消流程
      this.$refs.processCancel.show(v.actBusinessId, v.procInstId)
    },
    submitedProcess () {
      // 進(jìn)行流程操作后刷新表單數(shù)據(jù)顯示流程狀態(tài)
      this.getDataList()
    },
    history (v) {
      // 審批歷史
      if (!v.procInstId) {
        this.$Message.error('流程實例ID不存在')
        return
      }
      let query = { id: v.procInstId, backRoute: this.$route.name }
      this.$router.push({
        name: 'historic_detail',
        query: query,
      })
    }}
  }
}
  • 后端業(yè)務(wù)接口,注意與“工作流程-我的申請”中已有請假示例LeaveController示例中的區(qū)別漫玄,這里無需傳入流程定義ID茄蚯,ActBussines表與業(yè)務(wù)表兩兩關(guān)聯(lián)記錄對方ID方便查詢
    @RequestMapping(value = "/add",method = RequestMethod.POST)
    @ApiOperation(value = "添加審批需求")
    public Result<Object> add(@ModelAttribute Demand demand){

        Demand d = demandService.save(demand);
        // 保存至我的申請業(yè)務(wù)
        String userId = securityUtil.getCurrUser().getId();
        ActBusiness actBusiness = new ActBusiness();
        actBusiness.setUserId(userId);
        // 記錄關(guān)聯(lián)業(yè)務(wù)表ID
        actBusiness.setTableId(d.getId());
        ActBusiness a = actBusinessService.save(actBusiness);
        // 記錄關(guān)聯(lián)流程狀態(tài)表ID
        d.setActBusinessId(a.getId());
        demandService.update(d);
        return new ResultUtil<Object>().setSuccessMsg("操作成功");
    }
  • 效果預(yù)覽
image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市睦优,隨后出現(xiàn)的幾起案子渗常,更是在濱河造成了極大的恐慌,老刑警劉巖汗盘,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皱碘,死亡現(xiàn)場離奇詭異,居然都是意外死亡隐孽,警方通過查閱死者的電腦和手機癌椿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門健蕊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人踢俄,你說我怎么就攤上這事缩功。” “怎么了褪贵?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵掂之,是天一觀的道長抗俄。 經(jīng)常有香客問我脆丁,道長,這世上最難降的妖魔是什么动雹? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任槽卫,我火速辦了婚禮,結(jié)果婚禮上胰蝠,老公的妹妹穿的比我還像新娘歼培。我一直安慰自己,他們只是感情好茸塞,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布躲庄。 她就那樣靜靜地躺著,像睡著了一般钾虐。 火紅的嫁衣襯著肌膚如雪噪窘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天效扫,我揣著相機與錄音倔监,去河邊找鬼。 笑死菌仁,一個胖子當(dāng)著我的面吹牛浩习,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播济丘,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼谱秽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了摹迷?” 一聲冷哼從身側(cè)響起疟赊,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泪掀,沒想到半個月后听绳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡异赫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年椅挣,在試婚紗的時候發(fā)現(xiàn)自己被綠了头岔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡鼠证,死狀恐怖峡竣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情量九,我是刑警寧澤适掰,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站荠列,受9級特大地震影響类浪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肌似,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一费就、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧川队,春花似錦力细、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至斗躏,卻和暖如春逝慧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瑟捣。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工馋艺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人迈套。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓捐祠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親桑李。 傳聞我的和親對象是個殘疾皇子踱蛀,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容