Activiti工作流开发指南(springboot 2.x iview admin vue 前后端分离 模型设计器 动态数据权限 权限按钮显示 spring security)


集成Activiti 5.22,考虑到文档资料较多未选用新版本(模型设计器改动较大)或Flowable

集成的工作流编辑器在后台静态资源中,记得在系统配置中设置访问域名,开发时直接填后台请求地址前缀 http://localhost:8888 即可

几大常用接口

  1. RepositoryService:提供一系列管理流程部署和流程定义的API
  2. RuntimeService:在流程运行时对流程实例进行管理与控制
  3. TaskService:对流程任务进行管理,例如任务提醒、任务完成和创建任务等
  4. IdentityService:提供对流程角色数据进行管理的API,这些角色数据包括用户组、用户及它们之间的关系
  5. HistoryService:对流程的历史数据进行操作,包括查询、删除这些历史数据

25张表

image

1、act_ge_ 通用数据表,ge是general的缩写
2、act_hi_ 历史数据表,hi是history的缩写,对应HistoryService接口
3、act_id_ 身份数据表,id是identity的缩写,对应IdentityService接口
4、act_re_ 流程存储表,re是repository的缩写,对应RepositoryService接口,存储流程部署和流程定义等静态数据
5、act_ru_ 运行时数据表,ru是runtime的缩写,对应RuntimeService接口和TaskService接口,存储流程实例和用户任务等动态数

XBoot扩展基本开发指南

  • 通用流程状态表t_act_bussiness,其中table_id字段存储关联的表单ID

  • 后台仅需开发相应表单增删改接口,存储至单张表中,如t_leave,唯一需注意的地方为新增接口(添加新数据时)需关联业务act_buniess表,向其添加流程和表ID等信息,参考LeaveController

  • 前台仅需开发相应单个表单页面(可以通过路由传参实现相应按钮显示),参考leave.vue(以菜单中配置的该路由名name跳转),记得在src/router/router.js中添加路由

  • 最后记得在系统中配置相应流程信息

    • 数据字典"业务表"中添加相应业务表名,如"t_leave";"业务表单路由"中添加相应前端表单组件路由名,如"leave"
    • 流程管理中编辑填写关联刚开发的表单信息,业务表的作用主要为用户删除申请时关联删除相应表单数据,表单路由名作用为跳转显示刚前端开发的表单页面
    QQ截图20190113212518.png
  • 流程节点审批人可根据角色、部门负责人、人员设置多个,设置后默认勾选,为或签(任意一人审批,流程进入下一步,即先到先审)

  • 如何实现会签?

    • 请增加审批节点!
    image
  • 分支网关设定

    • 注意:暂仅支持互斥(排他)网关设置。为保证工作流简单性,建议仅使用开始、结束、任务节点和单向连线,设置分支后流程将变得不灵活,用户容易编辑出错。

若部署后流程图中文字符无法显示,是因为服务器环境jdk中无相应中文字体,百度安装即可(配置文件已配置微软雅黑,若未配置默认宋体)

互斥(排他)网关设定

顾名思义,当出现多个分支时仅选择一个满足条件的分支流转

WX20190523-221422@2x.png
  • 点击互斥网关后的连线设置流转条件的条件UEL表达式 https://www.activiti.org/userguide/#apiExpressions

    WX20190523-221523@2x.png

  • 注意勾选默认网关后不得设置流转条件,否则无法部署成功


    WX20190523-221700@2x.png
  • 启动流程时或完成一个任务节点时务必加入该变量(否则配置了流转条件变量的流程会报错),示例:

// 启动流程时添加变量,详见ActBusinessController的apply或start方法,在ActBusiness中设置params
actBusiness.getParams().put("duration", 36);

// 或者完成任务节点时加入变量
Map<String, Object> params = new HashMap<>(16);
taskService.complete(id, params);

绑定监听器示例

常见的业务需求需要完成一个审批流程后改变原数据状态、通知审批结果消息或执行其他操作,在结束节点上绑定监听器即可,一旦结束说明流程全部通过,触发自定义业务。

  • 绘制流程图填写定义的监听器类
image
image
image
  • 监听器示例代码
@Slf4j
public class MyListener implements ExecutionListener {

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

        // 获取关联业务表ID变量(启动流程代码里已存入tableId,此处直接获取即可)
        String tableId = (String) delegateExecution.getVariable("tableId");
        log.info(tableId);
        LeaveService leaveService = SpringContextUtil.getBean(LeaveService.class);
        Leave leave = leaveService.get(tableId);
        ... ...
    }
}

使用发起流程组件示例

  • 前端vue示例,注意各状态下仅能显示的各操作按钮
<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: '申请状态',
          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 = '已结束'
              color = 'blue'
            }
            return h('div', [
              h(
                'Tag',
                {
                  props: {
                    color: color,
                  },
                },
                text,
              ),
            ])
          },
        },
        {
          title: '申请结果',
          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)
                      },
                    },
                  },
                  '发起申请',
                ),
                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发起流程组件
      this.$refs.processStart.show('demand', v.actBusinessId)
    },
    cancelProcess (v) {
      // 取消流程
      this.$refs.processCancel.show(v.actBusinessId, v.procInstId)
    },
    submitedProcess () {
      // 进行流程操作后刷新表单数据显示流程状态
      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,
      })
    }}
  }
}
  • 后端业务接口,注意与“工作流程-我的申请”中已有请假示例LeaveController示例中的区别,这里无需传入流程定义ID,ActBussines表与业务表两两关联记录对方ID方便查询
    @RequestMapping(value = "/add",method = RequestMethod.POST)
    @ApiOperation(value = "添加审批需求")
    public Result<Object> add(@ModelAttribute Demand demand){

        Demand d = demandService.save(demand);
        // 保存至我的申请业务
        String userId = securityUtil.getCurrUser().getId();
        ActBusiness actBusiness = new ActBusiness();
        actBusiness.setUserId(userId);
        // 记录关联业务表ID
        actBusiness.setTableId(d.getId());
        ActBusiness a = actBusinessService.save(actBusiness);
        // 记录关联流程状态表ID
        d.setActBusinessId(a.getId());
        demandService.update(d);
        return new ResultUtil<Object>().setSuccessMsg("操作成功");
    }
  • 效果预览
image
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容