Activiti工作流管理系统(二)

前言

这篇文章将主要介绍工作流系统的发布功能,发布功能的旨在将满足bpmn协议的xml文档(流程图定义文件)发布到工作流引擎中,以便工作流引擎可以正常使用本流程图,因此作为工作流系统的首要及核心功能在第二篇文章进行说明。
说明:所有项目配置均在系列第一篇文章中进行介绍,配置系列通用。

系列二内容

发布功能的前端和后端实现

页面展示

发布

功能详细说明

实现方案

说明

根据官方文档介绍,主要实现方案有以下几种:各位在实际使用过程中可以根据自己的实际需求进行实现。

1.加载classpath方式:

DeploymentBuilder addClasspathResource(String resource);

2.InputStream方式(本系列用法):

DeploymentBuilder addInputStream(String resourceName, InputStream inputStream);

3.String方式

DeploymentBuilder addString(String resourceName, String text);

4.Zip方式

DeploymentBuilder addZipInputStream(ZipInputStream zipInputStream);

5.Bpmn模型图方式

DeploymentBuilder addBpmnModel(String resourceName, BpmnModel bpmnModel);

说明:以上五种方式实现效果相同,根据不同方式做不同处理后即可进行发布,若返回正常的deployment对象,则证明发布成功。

发布的统一语法:
Deployment deployment=repositoryService.createDeployment().addInputStream(bpmnUrl, input).deploy();

业务嵌入

由于本系列管理系统,除使用最基本的工作流外,还为了方便业务进行处理,因此加入了业务模块的代码。
当发布成功时,系统会根据发布到工作流引擎(即已经成功发布)的工作流配置中,读取配置信息,生成对应的任务关系图,并保存在业务表的task_def表中,这个表对于业务来说是十分关键的,它更加直接的表明了该流程配置中所有任务环节的基本信息和各环节的逻辑关系,对于之后业务的实现提供了很大遍历。

前端页面展示

任务环节

后台关键代码

ActivityUtils-getTaskList方法

public static List<TaskDef> getTaskList(RepositoryService repositoryService, FlowDef fd) throws Exception{
        List<TaskDef> listTaskDef = new ArrayList<TaskDef>() ;
        BpmnModel model = repositoryService.getBpmnModel(fd.getDefId());
        if(model != null){
            TaskDef tdObject = null;
            TaskDef tnObject = null;
            Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
            for(FlowElement e : flowElements){
                switch (e.getClass().toString()){
                    case Globals.USER_TASK:
                        tdObject = new TaskDef() ;
                        tdObject.setFlowId(fd.getId());
                        UserTask userTask = (UserTask)e ;
                        tdObject.setDescription(userTask.getDocumentation());
                        //方便后续添加,为开放修改任务定义接口做保留
                        tdObject.setRemark("");
                        //基本数据
                        tdObject.setTaskKey(e.getId());
                        tdObject.setName(e.getName());
                        tdObject.setType(e.getClass().toString());
                        listTaskDef.addAll(getTaskNexts(userTask,model,tdObject)) ;
                        break;
                    case Globals.PARALLEL_GATEWAY:
                        ParallelGateway pg = (ParallelGateway)e ;
                        tdObject = new TaskDef() ;
                        tdObject.setFlowId(fd.getId());
                        tdObject.setDescription(pg.getDocumentation());
                        //方便后续添加,为开放修改任务定义接口做保留
                        tdObject.setRemark("");
                        //基本数据
                        tdObject.setTaskKey(e.getId());
                        tdObject.setName(e.getName());
                        tdObject.setType(e.getClass().toString());
                        listTaskDef.addAll(getTaskNexts(pg, model, tdObject)) ;
                        break;
                    case Globals.START_EVENT:
                        tdObject = new TaskDef() ;
                        tdObject.setFlowId(fd.getId());
                        /**
                         * 思路:先找到startEvent然后一个一个向下找直到找到所有的endEvent
                         */
                        StartEvent se = (StartEvent)e ;
                        tdObject.setOrderNum(orderNum);
                        tdObject.setDescription(se.getDocumentation());
                        //方便后续添加,为开放修改任务定义接口做保留
                        tdObject.setRemark("");
                        //基本数据
                        tdObject.setTaskKey(e.getId());
                        tdObject.setName(e.getName());
                        tdObject.setType(e.getClass().toString());
                        tdObject.setFlowId(fd.getId());
                        listTaskDef.add(tdObject) ;
                        listTaskDef.addAll(getTaskNexts(se,model,tdObject)) ;
                        break;
                    case Globals.END_EVENT:
                        EndEvent ee = (EndEvent)e ;
                        tdObject = new TaskDef() ;
                        tdObject.setFlowId(fd.getId());
                        tdObject.setDescription(ee.getDocumentation());
                        //方便后续添加,为开放修改任务定义接口做保留
                        tdObject.setRemark("");
                        //基本数据
                        tdObject.setTaskKey(e.getId());
                        tdObject.setName(e.getName());
                        tdObject.setType(e.getClass().toString());
                        listTaskDef.addAll(getTaskNexts(ee,model,tdObject)) ;
                        break;
                    case Globals.EXCLUSIVE_GATEWAY:
                        ExclusiveGateway eg = (ExclusiveGateway)e ;
                        tdObject = new TaskDef() ;
                        tdObject.setFlowId(fd.getId());
                        tdObject.setDescription(eg.getDocumentation());
                        //方便后续添加,为开放修改任务定义接口做保留
                        tdObject.setRemark("");
                        //基本数据
                        tdObject.setTaskKey(e.getId());
                        tdObject.setName(e.getName());
                        tdObject.setType(e.getClass().toString());
                        listTaskDef.addAll(getTaskNexts(eg,model,tdObject)) ;
                        break;
                    default:
                        //需要考虑serviceTask的情况,即自动任务
                        break;
                }
            }
        }
        return listTaskDef;
    }

getTaskNexts方法(私有)

//定义全局变量:排序码
private static Long orderNum = 1l;
private static List<TaskDef> getTaskNexts(Object o, BpmnModel model, TaskDef tdObject) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //通过反射获取对象中的getOutgoingFlows方法
        Class<?> clazz = Class.forName(o.getClass().getName()) ;
        Method method = clazz.getMethod("getOutgoingFlows") ;
        List<SequenceFlow> listSF = (List<SequenceFlow>) method.invoke(o) ;
        List<TaskDef> tnObjects = new ArrayList<TaskDef>() ;
        TaskDef tnObject = new TaskDef() ;
        for(SequenceFlow sf : listSF){
            FlowElement fe =  model.getFlowElement(sf.getTargetRef()) ;
            tnObject = new TaskDef() ;
            tnObject.setFlowId(tdObject.getFlowId());
            tnObject.setType(fe.getClass().toString());
            tnObject.setDescription(fe.getDocumentation());
            tnObject.setOrderNum(++orderNum);
            tnObject.setExpression(sf.getConditionExpression());
            tnObject.setTaskKey(fe.getId());
            tnObject.setName(fe.getName());
            //这个最重要,把parentKey存入
            tnObject.setParentKey(tdObject.getTaskKey());
            tnObjects.add(tnObject) ;
        }
        return tnObjects ;
    }

以上就是实现此业务方案的全部代码(附加内容)~

发布功能实现

前端

TableData.vue

<Button type="primary" size="small" @click="deploy(row)">发布</Button>
deploy(row){
        if(row.instanceQuantity !== 0){
          this.$Message.warning('此定义下存在正在运行的实例,无法进行发布操作!');
          return
        }
        let id = row.id
        deploy({
          id:id
        }).then(res =>{
          if(res.data.responseCode === 1){
            this.$Message.success('发布成功!');
          }else {
            this.$Message.error('发布失败!');
          }
          this.getTableData() ;
        })
      }

activityManagement.js

/**发布**/
export const deploy = id => {
  return axios.request({
    url: 'flowDef/deploy',
    params: id,
    method: 'post'
  })
}

后端

FlowDefController

    @RequestMapping("/deploy")
    @Transactional
    public JsonResult deploy(Long id) throws IOException {
        JsonResult jr = new JsonResult() ;
        resultMap = new HashMap<String,Object>() ;
        InputStream input = null;
        try {
            FlowDef flowDef = flowDefService.selectByPrimaryKey(id) ;
            String bpmnUrl = flowDef.getBpmn() ;
            input = new FileInputStream(new File(bpmnUrl));
            // 根据bpmn文件部署流程
            Deployment deployment = repositoryService.createDeployment().addInputStream(bpmnUrl, input).deploy();
            //获取流程定义
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
            flowDef.setPublishDate(new Date());
            flowDef.setDefId(processDefinition.getId());
            flowDef.setVersion(processDefinition.getVersion()+"");
//            flowDef.setInstanceQuantity(flowDef.getInstanceQuantity() == null?0:flowDef.getInstanceQuantity());
            //正常启用状态
            flowDef.setStatus("1");
            flowDefService.update(flowDef) ;

            //插入任务定义表中(含继承关系)
            List<TaskDef> taskList = ActivityUtils.getTaskList(repositoryService, flowDef);
            //先清空之前所有的task
            taskDefService.deleteBatchByFlowId(id);
            //而后执行批量插入
            taskDefService.insertBatch(taskList) ;
            jr.setResponseCode(1);
            resultMap.put("data",processDefinition.getId()) ;
            jr.setResponseData(resultMap);
            jr.setResponseMessage(ResultEnum.SUCCESS);
        } catch (Exception e) {
            e.printStackTrace();
            jr.setResponseCode(0);
            jr.setResponseMessage(ResultEnum.EXCEPTION);
        }finally {
            if(input != null) {
                input.close();
            }
        }
        return jr ;
    }

总结

至此,有关于发布功能的开发全部完成,其中除工作流本身的API调用之外,还加入了大量业务端实现,以方便管理。各位若对代码有任何不同的意见或建议欢迎下方留言,大家共同进步!

下篇预告

1.工作流初始化参数的配置
2.工作流的启动
3.工作流的执行

敬请期待~~~
第二篇完结~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容