Activiti工作流管理系统(一)


前言

作为OA自动化办公系统,自动审批流可以说是必不可少的,整好本菜鸟在最近的项目中使用了工作流有关的技术,因此准备写一系列文章来完善自己所搭建的一套工作流管理系统。

系列说明:

由于本人对于前端技术十分薄弱,并且界面长相极为普通,因此在本系列中不详细说明前端技术,仅提供部分核心代码和简要截图说明,望各位大神见谅!

虽然工作流官方的表包含了工作流整个生命周期的完整数据内容,但是为了本系统能够方便展示以及在系统内部进行数据留痕,因此本人在该系统中除了使用工作流官方表(25张)外,还将自己创建业务表进行数据保存和前台展示。随着系列的深入,将不断放出所创建的表的表结构供大家参考,旨在抛砖引玉。下面开始进入正题:


系列一内容

本系列开篇主要介绍工作流web编辑器的使用,以及工作流与业务系统的对接关系和导入工作流定义文件的功能


环境搭建

前端:iview4.x+Vue、后端:springboot

核心技术:activiti工作流5.22.0版本

web编辑器:activiti-explorer

服务器:tomcat8.5.31(用于运行web编辑器)

注:实际项目开发使用的是springboot内置的tomcat

项目管理:git,Maven



数据库表结构(一)

工作流官方表:

共25张(自动生成)


工作流官方表(25张)

自建数据业务表:

流程定义表:

flow_def(对应对象:FlowDef)

说明:流程bpmn文件发布成功后,将流程定义的信息保存到此表中在业务端进行管理和维护。


flow_def表

Domain:

    private Long id;//主键

    private String name;//流程名称

    private String flowKey;  //流程ID(KEY)

    private String defId; //部署ID

    private String flowType;//流程类别

    private String version;//版本

    private String bpmn; //bpmn文件url

    private String status;//流程状态(0:禁用 1:启用)

    private Date createDate;  //创建日期

    private Date publishDate; //发布日期

    private transient Integer instanceQuantity; //正在运行实例数量

    private transient Integer historyQuantity ; //历史实例数量

    private String description;//流程描述

    private String remark;//流程备注(同流程描述)


任务定义表:

task_def(对应对象:TaskDef)

说明:在bpmn文件部署完成后,会通过API生成该流程图内的节点关系和相关表达式,并保存在该表内部,方便业务系统进行调用。此表为业务系统中主要的节点信息获取渠道,因此必须保证各节点间关系正确无误!


task_def表

Domain:

private Long id; //主键

private String name;//任务名称

private String taskKey;//任务编码

private Long flowId;//所属流程定义

private String type;//任务类型▼[单人处理、多人处理、多人会签、自动判定、会签判定]

private String description;//任务描述

private String remark;//任务备注(同任务描述)

private Long orderNum ;排序码

private String expression ;//el表达式

private String taskListener ; //监听器(包含的任务监听器)

private String parentKey ; //为表示节点间的关系,特加入parentKey

/*-------------------- 任务环节个性设置 --------------------*/

private Boolean canAudit = true; //可填写意见

private Boolean canAtch = false; //可添加附件

private Boolean canDelete = false; //可取消流程


流程参数表:

params_record(对应对象:ParamsRecord)

说明:此表为启动工作流时,所保存的必须传入的初始化参数,至于流程流转过程中的中间参数,则在当前版本中并未保存。


params_record表

Domain:

private Long id; //主键

private String paramName;//参数名称

private String paramType;//参数类型(暂未用到)

private String paramVal;//参数值

private Long flowId;//所属流程定义ID

private Boolean isInit;//是否为初始化参数(true:是 false:否)


页面展示

Web编辑器

页面:

将activiti-explorer.war放到tomcat的webapps下,启动tomcat服务器即可。

在网址输入:http://localhost:8080/activiti-explorer/


登录页面

Tips:用户名和密码均输入kermit(内置用户)即可登录。


菜单页面
流程配置操作界面

Tips:可以选择新建或导入即可进入流程设计主界面。

使用方法介绍:


流程定义配置

此处主要对流程定义配置选项进行说明:

☆Process identitier:流程定义的Key(业务端用来判断的唯一标志,对应FlowDef:flowKey字段)

Name:流程名称(对应FlowDef:name字段)

Documentation:流程说明(对应FlowDef:description和remark字段)

Process author:流程作者

Event listeners:事件监听器

Execution listerners:执行流监听

Message definitions:消息定义(可通过消息启动流程)

Signal definitions:信号定义(可通过信号启动流程)


任务定义配置

点击选中任意一个任务环节,即可进入任务定义界面,以下将介绍各个配置项的含义

Id:任务定义的业务Key(对应TaskDef的taskKey字段)

Name:任务定义的名称(对应TaskDef的name字段)

Documentation:任务描述(对应TaskDef的description和remark字段)

Execution listeners:执行流监听器

Assignments:处理人(常用el表达式进行配置)

Task listeners:任务监听器

形如:

配置为:${usertask03_assignment}

则后台以map形式进行赋值:paramsMap.put("usertask03_assignment","zhangsan")

☆以下配置项中含(Multi-instance)内容的均为多人会签任务需要配置的选项

Multi-instance type:多人任务类型

主要分为:Parallel(并行关系:即所有参与会签的人员都处理完该任务节点后触发向下执行的执行流)

                  Sequential(按次序执行:即参与会签的人员依次进行处理,等待所有人处理完成后触发向下执行的执行流)

Cardinality(Multi-instance):暂时未涉及到,还请各位大神帮忙解释一下。。

Collection(Multi-instance):可做处理人列表使用(常用el表达式)

形如:

配置为:${usertask01_assignment}

则后台以map形式进行赋值:paramsMap.put("usertask01_assignment",new ArrayList<T>()); //map的key为List对象。

Element variable(Multi-instance):为上一配置项中list集合中的每一个对象,可进行自定义为user01_assignment(普通字符串,非el表达式),在工作流官方表中可以查看到list.size()条执行流。


本篇文章使用以下工作流定义进行说明:


示例流程图

Tips:使用工作流左侧的组件树即可绘制流程图,操作起来也是十分方便。

业务系统页面

导入新流程

业务系统-流程定义列表

        本系统通过导入并解析xml的方式,将xml文件中的流程信息新增入系统中,这样简化了导入流程的操作,否则既要绘制流程图,又要将在绘制过程中已经填好的信息在重新填一遍。

 操作页面如下图:


导入新流程

Tips:此前端页面为最简单的Modal对话框和一个Upload上传组件,直接上传文件即可。

业务判断:在上传时如果有正在运行的实例,那么则不允许上传,这样可以避免不同的运行流程同时走不同版本的流程定义文件(判断相同文件的标准为:两张bpmn文件的流程定义key相同,即FlowDef中的flowKey字段相同)此判断需根据工作中的实际需求进行自行修改。


导入成功后

代码

导入行流程

前端

TableData.vue

<template>
<div>
<div class="btn-class">
<Button type="primary" @click="createNewProcess">导入新流程</Button>
</div>
<ModalView ref="modal"></ModalView>
</div>
</template>
<script>
import ModalView from './ModalView'
export default {
name: 'TableData',
components: { ModalView },
data () {
return {
columns12: [],
data6: []
}
},
mounted(){
},
methods: {
createNewProcess(){
this.$refs.modal.modal1 = true
}
</script>


ModalView.vue

<template>
<div>
<Modal v-model="modal1" :title="title">
<Form ref="flowDef" :model="flowDef">
<input type="hidden" v-model="flowDef.id"/>
<input type="hidden" v-model="flowDef.bpmn"/>
<FormItem label="流程规范文件:" prop="bpmn">
<Upload :default-file-list="defaultList" accept=".xml" :on-remove="removeFile" :on-preview="downLoad" :on-error="handleError" :on-success="handleSuccess" :action="this.$store.state.globals.uploadUrl">
<Button icon="ios-cloud-upload-outline">上传文件</Button>
</Upload>
</FormItem>
</Form>
</Modal>
</div>
</template>
<script>
import {save} from '../api/activityManagement'
export default {
name:'ModalView',data () { return { modal1: false, title:'导入新流程', defaultList:[], flowDef:{} } },
methods:{
handleSuccess (response, file){
this.flowDef = []
this.flowDef = response.responseData.data
save(this.flowDef).then(res => {
//可自行处理逻辑,实际为保存到数据库后的回调方法
})
}
</script>


activityManagement.js

import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:8088/activity_management/';
/**保存实体**/
export const save = flowDef => {
return axios.request({
url: 'flowDef/save',
data: flowDef,
method: 'post'
})
}

globals.js

export default {
state: {
uploadUrl:'http://localhost:8088/activity_management/common/upload'
},
mutations: { },
actions: { }
}


后端:

代码说明:

①JsonResult 为本人封装的返回对象,包括:int responseCode(结果码),Enum responseMessage(结果信息)和responseData(Map对象,key:常为data,value:需返回前台的数据)属性;

②本文及系列所有代码均展示关键代码,其余业务代码因项目不同而区别较大,本文仅对共性代码进行说明,望各位谅解;

③除非有特殊情况需要特别说明外,本文后端代码只展示Controller层代码,不展示service层和mapper.xml中的代码。

FlowDefController.java(保存实体功能)

public JsonResult save(@RequestBody FlowDef flowDef){
try {
String flowKey = flowDef.getFlowKey() ; //获取流程定义的唯一标识
FlowDef fd = flowDefService.selectByFlowKey(flowKey); //根据flowKey查询对象
//若对象为空则新增,反之则根据具体业务规则更新该实体
if(fd == null){
flowDefService.insert(flowDef) ;
}else{
//除了instanceQuantity和flowKey外,其余均应更新,且将publishDate和defId置空fd.set()方法。。。
flowDefService.update(fd) ;
//更新成功后,应将taskList删除(后续更新)
taskDefService.deleteBatchByFlowId(fd.getId());
}}catch (Exception e){e.printStackTrace();}
return jr;}

CommonController:(上传功能)

public JsonResult upload(@RequestParam MultipartFile file){
//解析 MultipartFile 对象,并返回对应的流程定义实体
FlowDef flowDef = XmlParseUtils.getObjects(FileUtils.multipartFileToFile(file));
//根据flowKey获取实体
FlowDef fd = flowDefService.selectByFlowKey(  flowDef.getFlowKey() );
// ,若为空,则返回错误信息,因为原同flowKey版本的流程定义下还存在正在运行的实
//例时,则无法导入,反之返回成功信息。
if(fd != null && fd.getInstanceQuantity()>0){...}
}



XmlParseUtils(getObjects:解析xml文档,返回FlowDef对象)

// 使用SAX解析方式
public static FlowDef getObjects(File file) throws DocumentException{
FlowDef fd = new FlowDef() ;
SAXReader reader = new SAXReader();
Document document = reader.read(file);
Element processEle = document.getRootElement();
Iterator it = processEle.elementIterator();
while(it.hasNext()){
Element element = (Element) it.next();
if("process".equals(element.getName())) {
List<Attribute> attributes = element.attributes();
for(Attribute attribute : attributes){
//保存key字段
if(attribute.getName().equals("id")){
fd.setFlowKey( attribute.getValue() );
}else if(attribute.getName().equals("name")) {
//保存name字段
fd.setName(attribute.getValue());
}
Iterator itt = element.elementIterator();
while(itt.hasNext()){
Element child = (Element) itt.next();
String nodeName = child.getName();
//保存decription字段
if("documentation".equals(nodeName)) {
fd.setDescription(child.getTextTrim());
fd.setRemark(child.getTextTrim());
}}}}
String filePath = Globals.BPMN_DIR+file.getName() ;
fd.setBpmn(filePath);
fd.setCreateDate(new Date());
fd.setFlowType("CUSTOM");
fd.setStatus("-1"); // -1:待确认状态
return fd ;}



总结

        至此Activiti工作流管理系统的系列一完成了,该文章介绍了项目基本架构设计,web编辑器的介绍及基本使用方法以及导入新流程功能。如果各路大神对此有什么建议或意见,还请不吝赐教,本人将在系列第二篇中进行完善。
        由于篇幅的限制,本来想介绍发布功能的实现,但是写着写着发现内容太多了,因此将此功能放到系列第二篇中。并且本人对于简书并不是十分熟悉,因此文章可能会影响大家的观看,对此本人向各位说声抱歉,未来本人将不断熟悉这一平台,正确能为大家带来最好的观看体验,让大家能够方便的了解工作流的相关知识,谢谢大家!


下篇预告

发布功能的前台与后台实现


传递爱~

目前正处于新型冠状肺炎的隔离期间,在此希望祖国能够顺利渡过难关!武汉加油!中国加油!

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

推荐阅读更多精彩内容