JeeSite BPM 业务流程、工作流引擎、Flowable、Activiti

您所在的位置:网站首页 业务流管理流 JeeSite BPM 业务流程、工作流引擎、Flowable、Activiti

JeeSite BPM 业务流程、工作流引擎、Flowable、Activiti

2024-07-14 03:27| 来源: 网络整理| 查看: 265

# 特点 JeeSite BPM 基于 Flowable(Activiti)生来具有的稳定工作流引擎 支持在线流程设计器,流程导入导出,符合 BPMN 规范,中国式工作流 支持流程办理、退回、自由流、会签、并行、串行、服务任务等 支持退回任务,退回到指定环节,退回到上一步,退回到发起人 支持转办任务,向后加签任务,将任务交接给他人办理,办理完成后继续下一步骤 支持委托任务,向前加签任务,将任务委托给他人,他人办理完成后再回到委托人 支持加签减签,并行会签加签或减签、串行会签加签减签、串行并行签中用户查询 支持智能提交,相同处理人自动跳过,支持自由指定下一步处理人 支持作废流程,允许发起人快速终止流程,管理员维护终止流程 支持自由流程,根据环节选择,自由跳转到指定环节,特事特办 支持流程撤回,下一步未办理的任务,可进行取回撤销重做任务 支持流程跟踪图,流程状态展现,流转信息,任务历史,任务分配信息 支持一个流程模型挂接多个业务单据,如某公司8种费用审批流程,表单不一样,但流程相同 支持表单和环节多对多,即不同的环节使用相同的表单,相同的表单在不同环节中使用 流程事件脚本在线编写,包括:流程启动、完成、取消;任务分配、创建、结束等 流程脚本管理(Groovy、Beetl),在线编辑、自动完成、脚本测试、多语言脚本模板维护 我的待办任务处理,我的已办任务、我创建的任务查询、流程跟踪、审批记录查询 流程管控,在无关联表单情况下流程调试,如流程发起、挂起;流程定义、实例、任务等查询;任务办理等 支持流程组件标签定义(流程按钮、意见审批、下一步流程信息等)快速与自定义的业务表单建立关系。 版本化管理流程,新调整的流程业务不影响正在运行,未结束的流程继续流转。 方便自行扩展引擎功能:催办任务、传阅任务、流水号管理、常用语管理。 # 模块安装 # 1、前置条件

JeeSite BPM 模块要求 JeeSite 版本最低为 4.1.8,若您使用的版本低于 4.1.8,请先升级。

# 2、引入依赖

打开 web 的 pom.xml,加入如下模块依赖代码

com.jeesite jeesite-module-bpm ${project.parent.version} 123456

注意:v4.1.8+ 专业版提供,此模块依赖,请向售后人员索取。

# 3、初始化数据库

第一种情况:若是 v4.2.0+ 以上版本,或新项目初次安装

运行 jeesite-web-pro/bin/init-data.bat(sh) 初始化数据库即可。

第二种情况:若是 v4.1.8 之前版本升级上来的项目,后加入 bpm 模块

无需执行 bpm 模块的 init-data 初始化数据库脚本。 项目 pom.xml 里加入 bpm 模块依赖后,直接启动服务,系统会自动检测并初始化数据库。

第三种情况:最初就是 v4.1.8 版本的项目,使用社区版初始化的数据库

若您先初始化了核心功能模块数据库,并没有初始化 BPM 的相关表和数据。 这种情况可能会提示 js_bpm_*、act_* 的表不存在,这时您需要单独初始化这些表。 防止误操作,请先备份数据库。备份完成后,您可以直接运行 jeesite-web-bpm 项目 com.jeesite.test.InitData 类的 initStep03 单元测试,单独去初始化 BPM 模块的表及基础数据。执行前请设置 -Djeesite.initdata=true JVM 参数(该参数防止误操作需要手动添加,执行完成后删除该参数)

常见问题:

如果遇到 java.sql.SQLSyntaxErrorException: Table 'jeesite.act_id_property' doesn't exist 错误,第 1 请确认当前版本状态是否为开发版;第 2 请确认 mysql 是否区分表名大小写,请忽略表名大小写。 由于 Flowable 的表采用大写方式,所以需要注意表名大小写的问题。比如您的开发库是不区分大小写的,然后您导入到正式库上是区分大小写的,这时就造成原来大写的表名变为了小写,就造成因为大小写的问题,找不到表的情况。实际上 JeeSite 本身是支持 mysql 表名区分大小写的配置,如果从初始化库时,就在区分大小写的库上开发,是没有这方面问题的。一般我们建议直接忽略表名大小写。 # 我的第一个流程

下面让咱们以一个最简单的《请假单》为例,快速体验它的强大。

# 1、创建流程分类

首先咱们先创建一个流程分类,对咱们新建的流程进行归类。进入菜单:业务流程-> 流程管控 -> 流程分类管理,点击“新增”按钮,新建一个分类,数据如下:

分类名称:办公流程 分类编码:office # 2、流程模型设计

请假流程环节业务描述:

申请人发起请假申请,由部门经理审核。 若申请人为部门经理,则自动跳过部门经理节点。 若请假天数大于3天,则需要总经理审核,否则跳过总经理审核。 审核完成后,有人力专员进行备案,并结束流程。 审批不通过任务退回到申请人。

了解业务后,咱们开始进行流程图设计。进入菜单:业务流程-> 流程管控 -> 流程模型设计,点击“创建流程”按钮,填写数据如下:

模型名称:请假流程学习 模型Key:leave_test 描述:我的第一个流程

点击“创建新模型”按钮,接着系统会弹出流程设计界面。

# 1)画流程图

注意:已实现中国式流程,无需画“退回”线路。

# 2)为每个环节设置ID

设“主键ID”作为环节的编号,如下:

填写申请单,主键ID:edit 部门经理审核,主键ID:dept 总经理审核,主键ID:ceo 人力专员备案,主键ID:har # 3)为每个环节“分配用户” 填写申请单:流程发起人(可以不设置,启动流程后默认自动跳过第一个环节,第一个环节为填写申请单或修改申请单使用) 部门经理审核:身份存储 -> 候选组 -> 部门经理(若没有创建一个部门)

以此类推,继续配置如下环节:

总经理审核:身份存储 -> 分配给单个用户 -> 选择总经理用户 人力专员备案:身份存储 -> 分配给单个用户 -> 选择人力专员用户 # 4)配置流转条件 选择“小于等于3天”连接线,流转条件编写:${leaveDays 3}

其中:变量 leaveDays 为业务表单字段,后续会说明如何赋值。

# 5)配置流程变量

方便调试流程,此操作为可选配置(可以配置也可以不配置),主要是在“业务流程 -> 流程管控 -> 流程定义”中直接发起“非关联业务”的流程进行调试时,展现的流程变量列表。

点击流程图空白处,在下方属性列表中,点击“数据对象”,填写信息如下:

# 6)保存流程,并发布

点击左上角的“保存”按钮,接着点击“保存和关闭编辑器”按钮,保存流程图。

系统会返回到“流程模型列表”界面,找到刚新建的流程,点击“发布流程模型”按钮:

弹出如下对话框:

选择一个 office 流程分类,点击“发布流程模型”,右下角提示“发布流程成功”即可。

注意:流程表单、流程事件,不需要在流程模型中配置。发布流程后可以在流程定义管理进行关联业务,绑定表单,编写流程事件。

# 7)调试流程

验证流转是否正确,进入菜单:业务流程 -> 流程管控 -> 流程定义。

接上一步,流程发布后,在流程定义列表中会出现刚发布的“请假流程”一条数据,点击“调试流程”,可以进行“无关联业务”的流程流转测试,来验证您刚才发布的流程,是否可以正常流转。

点击“启动流程”,会发起该流程(流程名称后会带一个【调试】字样)。

在流程定义列表中点击流程名称,可以查询该流程下的所有实例。

接着点击流程实例名称,可以查询该实例下的任务。

接着点击任务名称,可以签收、办理该任务。

若当前启动人是部门经理,则部门经理审批环节自动跳过。

流程调通之后,接下来,咱们可以进行关联业务表单了。

# 3、流程与业务关联

现在我们开始准备表单,首先通过 ERMaster 创建一张请假表,如下:

导出DDL并执行,完成数据表的创建。

v4.2.2 以后的版本中新增了 BPM 的表单代码生成功能。生成时选择 “增删改查” 模板,然后在下方其它选项里找到 “是否为流程表单” 选择 “是” ,然后填写 “流程表单Key”,与 “流程定义 -> 表单或业务关联” 的 “流程表单Key” 一致。

v4.2.2 之前版本生成 “增删改查” 代码,再后续添加 BPM 代码即可。

代码生成工具生成后,创建一个 “请假管理” 菜单。

这里主要以流程为主,就不多赘述了具体生成步骤了,最终表单地址如下:

申请单查询:/oa/oaLeave/list 申请单填写:/oa/oaLeave/form 申请单编辑:/oa/oaLeave/form

这里填写和编辑的地址相同,主要用来学习入门,根据业务需要可以分开设计

# 1)业务关联配置

目前表单和流程都已经准备好,咱们正式开始建立联系。进入“业务流程 -> 流程管控 -> 流程定义”菜单。找到“请假流程”数据行,点击“表单或业务关联”操作按钮,进入“流程表单”界面。

一个流程可以关联多个业务单据,如:某公司8种费用审批流程,表单不一样,但流程相同,这里的表单 key 对应一种费用。表单的版本对应流程的版本,当新发布一个流程定义的时候,流程表单也跟随新增版本。 流程表单是一个树结构,顶级表单定义为全局表单(对所有环节生效);下级表单为环节表单(该表单只对指定环节生效)。若没有定义环节表单的环节,则获取全局表单数据。

点击右上角“新增流程表单”按钮。创建一个表单,数据如下:

表单名称:请假流程 表单Key: leave(对应代码生成里指定的流程表单Key) 表单类型:URL表单 表单标题(v5.3.0 之前版本):${form.currentUser.userName} 在 ${date(),'yyyy-MM-dd'} 发起了 ${form.formName} 表单标题(v5.3.0+ 及之后版本):${user().userName} 在 ${date(),'yyyy-MM-dd'} 发起了 ${form.formName} PC表单地址:/oa/oaLeave/form?id=${task.procIns.bizKey}

其中表单标题和表单地址为 Beetl 语法模板,可以根据业务数据规则动态生成。

若每个环节的表单不同,您可以继续创建下级表单,指定环节名称,则仅对指定环节有效,如下(本例学习可以不用配置):

# 2)流程事件配置

在“流程表单”界面,找到您刚新建的“请假流程”表单数据行,点击“流程事件”操作按钮,进入“流程事件”界面。

表单下支持定义流程事件,主要用来:更新业务表单数据状态、发送通知、发送邮件、业务处理等。 目前支持的事件包括:流程启动、流程完成、流程终止、任务分配、任务创建、任务完成等;若这些事件缺少您想要的,您也可以自己在字典管理里找到 bpm_event_type 字典类型,添加 Flowable 流程事件,更多的流程事件,详见:org.flowable.common.engine.api.delegate.event.FlowableEngineEventType 枚举类,流程事件说明 (opens new window)。 流程事件脚本为 Groovy 语言,它是一个脚本语言,无需编译直接运行;可以完全使用 Java 语法,能够很好和 Java 代码进行结合;又可以使用 Groovy 自身的特性,简化 Java 语法的编写。 提供强大的脚本编辑器,自动提醒,自动完成常用语法。

点击右上角“新增”按钮。连续创建三个事件,数据如下:

事件名称:更新业务表状态(审核、退回) 是否异步:否 事件类型:任务创建 事件脚本:如下 import com.jeesite.common.entity.DataEntity; import com.jeesite.modules.bpm.utils.BpmUtils; if (BpmUtils.isCurrentCmd(execution, 'back')){ // 如果当前执行的是退回命令,则更新状态为审批退回 BpmUtils.updateStatus(execution, 'oaLeaveService', DataEntity.STATUS_AUDIT_BACK); }else{ BpmUtils.updateStatus(execution, 'oaLeaveService', DataEntity.STATUS_AUDIT); } 1234567

Cloud版本:

import com.jeesite.common.entity.DataEntity; import com.jeesite.modules.bpm.utils.BpmUtils; import com.jeesite.modules.bpm.utils.BpmCloudUtils; import com.jeesite.modules.oa.entity.OaLeave; if (BpmUtils.isCurrentCmd(execution, 'back')){ // 如果当前执行的是退回命令,则更新状态为审批退回 BpmCloudUtils.updateStatus(execution, 'oaLeaveService', new OaLeave(), DataEntity.STATUS_AUDIT_BACK); }else{ BpmCloudUtils.updateStatus(execution, 'oaLeaveService', new OaLeave(), DataEntity.STATUS_AUDIT); } 123456789事件名称:更新业务表状态(流程完成) 是否异步:否 事件类型:流程完成 事件脚本:如下 import com.jeesite.common.entity.DataEntity; import com.jeesite.modules.bpm.utils.BpmUtils; BpmUtils.updateStatus(execution, 'oaLeaveService', DataEntity.STATUS_NORMAL); 123

Cloud版本:

import com.jeesite.common.entity.DataEntity; import com.jeesite.modules.bpm.utils.BpmCloudUtils; import com.jeesite.modules.oa.entity.OaLeave; BpmCloudUtils.updateStatus(execution, 'oaLeaveService', new OaLeave(), DataEntity.STATUS_NORMAL); 1234事件名称:更新业务表状态(流程终止) 是否异步:否 事件类型:流程取消 事件脚本:如下 import com.jeesite.common.entity.DataEntity; import com.jeesite.modules.bpm.utils.BpmUtils; BpmUtils.updateStatus(execution, 'oaLeaveService', DataEntity.STATUS_DELETE); 123

Cloud版本:

import com.jeesite.common.entity.DataEntity; import com.jeesite.modules.bpm.utils.BpmCloudUtils; import com.jeesite.modules.oa.entity.OaLeave; BpmCloudUtils.updateStatus(execution, 'oaLeaveService', new OaLeave(), DataEntity.STATUS_DELETE); 1234# 3)增加流程相关后台代码

在 v4.2.2 以后的版本中新增了 流程表单 代码生成功能,可忽略该步骤,之前版本按照以下方法追加:

将实体类的父类替换为 BpmEntity 实体(树表为 BpmTreeEntity),该实体里包含提交流程的相关参数。

import com.jeesite.modules.bpm.entity.BpmEntity; public class OaLeave extends BpmEntity {} 12

修改 OaLeaveService 的 save 方法,增加流程处理代码,如下:

import com.jeesite.modules.bpm.utils.BpmUtils; public void save(OaLeave oaLeave) { // 如果未设置状态,则指定状态为审核状态,以提交审核流程 if (StringUtils.isBlank(oaLeave.getStatus())){ oaLeave.setStatus(OaLeave.STATUS_AUDIT); } // 如果状态为正常,则代表不正常操作,可能前端进行了数据参数修改 if (OaLeave.STATUS_NORMAL.equals(oaLeave.getStatus())){ throw new ServiceException(text("非法操作,前端数据被劫持!")); } // 如果状态为草稿或审核状态,才可以保存业务数据 if (OaLeave.STATUS_DRAFT.equals(oaLeave.getStatus()) || OaLeave.STATUS_AUDIT.equals(oaLeave.getStatus())){ super.save(oaLeave); } // 如果为审核状态,则进行审批流操作 if (OaLeave.STATUS_AUDIT.equals(oaLeave.getStatus())){ // 指定请假天数流程变量,作为流程条件,决定流转方向 Map variables = MapUtils.newHashMap(); variables.put("leaveDays", oaLeave.getLeaveDays()); // 如果流程实例为空,任务编号也为空,则:启动流程 if (StringUtils.isBlank(oaLeave.getBpm().getProcInsId()) && StringUtils.isBlank(oaLeave.getBpm().getTaskId())){ BpmUtils.start(oaLeave, "leave", variables, null); } // 如果有任务信息,则:提交任务 else{ BpmUtils.complete(oaLeave, variables, null); } } } 12345678910111213141516171819202122232425262728293031323334353637

关注:BpmUtils 的 start 和 complete 方法,这才是流程的重点。上面的几步判断,只是一些数据存储和验证。

# 4)增加流程相关视图代码

在 v4.2.2 以后的版本中新增了 流程表单 代码生成功能,可忽略该步骤,之前版本按照以下方法追加:

请假管理列表中增加“流程追踪”按钮:

$('#dataGrid').dataGrid({ columnModel: [ // ... 此处省略 ... {header:'${text("操作")}', name:'actions', width:120, sortable:false, title:false, formatter: function(val, obj, row, act){ var actions = []; // ... 此处省略 ... // 如果表单已启动流程,则可以显示流程追踪按钮,其中:formKey 是创建表单时的Key,bizKey 是当前业务的主键 if (row.status != Global.STATUS_DRAFT){ actions.push(';'); } return actions.join(''); }} ] }); 1234567891011121314

替换原来的提交按钮(如果需要暂存功能,可以增加暂存按钮,否则可以不加):

${text('暂 存')}; // 业务实现草稿按钮 $('#btnDraft').click(function(){ $('#status').val(Global.STATUS_DRAFT); }); // 流程按钮提交事件 BpmButton = window.BpmButton || {}; BpmButton.complete = function($this, task){ $('#status').val(Global.STATUS_AUDIT); }; 123456789101112131415161718

在表单部分增加“审批意见”和“下一步任务信息”控件(如果不需要可以不加)

${text('审批意见')}: 12345678910111213# 5)一切就绪,测试效果

到这一步说明一个请假流程就算完成了,下面贴图展示:

请假单列表:

任务办理:

要求完成时间:下一步任务的要求完成时限,提醒作用 任务优先级:根据任务的等级排序,优先级越高越靠前 下一步处理人:下一步任务的执行人设定,若不设置,则有流程引擎自动分配 流程分配的下一步处理人若与当前人想符合,流程自动跳过 **注意:**若点击办理的表单提示 “403 您的操作权限不足” 或 “404 您访问的页面不存在”,该问题一般是:流程定义 -> 表单或业务关联 -> 流程表单 -> PC表单地址,您定义的这个地址无访问权限或地址不对,您需要对这个地址给当前用户授权。

任务退回:

任务转办:

任务委托:

自由跳转(特事特办):

流程终止(作废):

流程跟踪信息:

# 附:API # BpmEntity 业务流程实体 public class BpmEntity extends DataEntity { private BpmParams bpm; // 流程相关参数 } 12345# BpmParams 流程接受参数 public class BpmParams implements Serializable { private static final long serialVersionUID = 1L; private String taskId; // 任务编号 private String procInsId; // 实例编号 private String activityId; // 活动编号 private String comment; // 审批意见 private Date dueDate; // 任务期限 private Integer priority; // 任务优先级 private String nextUserCodes; // 下一步处理人用户编码 private boolean isView; // 是否为查看表单(来自:已办、我创建、查询等) } 1234567891011121314151617# button 流程按钮组件 var p = { // 业务流程实体对象 bpmEntity: bpmEntity!, // 业务表单Key formKey: formKey!, // 按钮的标签,设置为空,则不显示按钮 claimText: claimText!text('签 收'), completeText: completeText!text('提 交'), backText: backText!text('退 回'), // 退回到之前办理过的任务环节 turnText: turnText!text('转 办'), // 将任务转交他人办理,办理完成后继续流转 delegateText: delegateText!text('委 托'), // 任务交由被委托人办理,办理完成后返回委托人 stopText: stopText!text('终 止'), // 单据作废,结束流程实例 moveText: moveText!text('自由跳转'), // 特事特办,自由流 traceText: traceText!text('流程追踪'), }; 12345678910111213141516171819

按钮事件接口:

BpmButton = window.BpmButton || {}; // 获取到任务信息后,渲染按钮前调用 BpmButton.init = function(task){ } // 点击提交按钮时调用 BpmButton.complete = function($this, task){ } // 点击签收按钮时调用 BpmButton.claim = function($this, task){ js.ajaxSubmit(ctx + '/bpm/bpmTask/claim', {id: task.id}, function(data){ js.showMessage(data.message); if(data.result == Global.TRUE){ var $button = $('#bpmButton'); $button.find('.taskClaim').addClass('hide'); $button.find('.needClaim').removeClass('hide'); } }); } // 点击退回按钮时调用 BpmButton.back = function($this, task){ js.addTabPage($this, $this.text(), ctx + '/bpm/bpmTask/back?id='+task.id); } // 点击转发按钮时调用 BpmButton.turn = function($this, task){ js.addTabPage($this, $this.text(), ctx + '/bpm/bpmTask/turn?id='+task.id); } // 点击委托按钮时调用 BpmButton.delegate = function($this, task){ js.addTabPage($this, $this.text(), ctx + '/bpm/bpmTask/turn?id='+task.id+'&delegate=true'); } // 点击自由跳转按钮时调用 BpmButton.move = function($this, task){ js.addTabPage($this, $this.text(), ctx + '/bpm/bpmTask/move?id='+task.id); } // 点击终止流程按钮时调用 BpmButton.stop = function($this, task){ js.addTabPage($this, $this.text(), ctx + '/bpm/bpmRuntime/stop?id='+task.procIns.id); } // 点击流程跟踪按钮时调用 BpmButton.trace = function($this, task){ js.addTabPage($this, $this.text(), ctx + '/bpm/bpmRuntime/trace?id='+task.procIns.id); } 12345678910111213141516171819202122232425262728293031323334353637383940

以上代码放到流程表单页面最下面

# comment 审批意见组件 var p = { // 业务流程实体对象 bpmEntity: bpmEntity!, }; 123456# nextTaskInfo 下一步任务信息 var p = { // 业务流程实体对象 bpmEntity: bpmEntity!, }; 123456# BpmUtils 工具类 /** * BPM 工具 * @author ThinkGem * @version 2019-05-20 */ public class BpmUtils { /** * 根据业务实体获取流程实例 * @param bpmEntity 业务对象 * @param formKey 表单KEY * @author ThinkGem */ public static BpmProcIns getProcIns(BpmEntityApi bpmEntity, String formKey); /** * 启动流程实例,自动完成流程的第一个节点 * @param bpmEntity 业务对象 * @param formKey 表单KEY * @param variables 流程变量 * @param transientVariables 流程瞬时变量 * @author ThinkGem */ public static BpmTask start(BpmEntityApi bpmEntity, String formKey, Map variables, Map transientVariables); /** * 提交任务 * @param bpmEntity bpm.taskId 任务编号 * @param bpmEntity bpm.procInsId 实例编号 * @param bpmEntity bpm.activityId 活动编号 * @param bpmEntity bpm.comment 提交意见 * @param bpmEntity bpm.dueDate 要求完成时间 * @param bpmEntity bpm.priority 任务优先级 * @param bpmEntity bpm.nextUserCodes 任务下一步处理人 * @param variables 流程变量 * @param transientVariables 瞬时流程变量 * @author ThinkGem */ public static BpmTask complete(BpmEntityApi bpmEntity, Map variables, Map transientVariables); /** * 获取业务主键数据 * @author ThinkGem */ public static String getBizKey(DelegateExecution execution); /** * 更新业务状态数据 * @author ThinkGem */ public static void updateStatus(DelegateExecution execution, String serviceBeanName, String status); /** * 判断当前执行的命令 * @author ThinkGem */ public static boolean isCurrentCmd(DelegateExecution execution, String cmd); } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859# 其它常用 API

根据业务查询流程实例:

String formKey = "表单Key"; String bizKey = "业务主键"; runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(formKey+":"+bizKey).active().list(); 12

根据流程实例查询任务:

String procInsId = "实例id"; taskService.createTaskQuery().processInstanceId(procInsId).active().list(); 12

查询历史实例和任务:

historyService.createHistoricProcessInstanceQuery().listPage(1, 20); historyService.createHistoricTaskInstanceQuery().listPage(1, 20); 12

执行自定义命令:

managementService.executeCommand(new CustomCmd("procDefId")); 1

拿到流程模型数据:

repositoryService.getBpmnModel(processDefinitionId); 1

获取任务节点数据:

BpmnModel bpmnModel = FlowableUtils.getBpmnModel(); UserTask userTask = FlowableUtils.getUserTask(bpmnModel, "节点id"); List outgoingFlows = userTask.getOutgoingFlows(); outgoingFlows.forEach(sequenceFlow -> { String nextUserTaskId = sequenceFlow.getTargetFlowElement().getId(); UserTask nextUserTask = FlowableUtils.getUserTask(bpmnModel, nextUserTaskId); }); 1234567# BPM参数配置 flowable: # 生成图文字体名称设置 activityFontName: "宋体" labelFontName: "宋体" annotationFontName: "宋体" # ACT 表的前缀 dataSourcePrefix: ~ # 表前缀中是否支持包含 schema tablePrefixIsSchema: false # 是否自动创建或更新数据表 databaseSchemaUpdate: true # 流程引擎的 MyBatis 映射文件配置 mybatisMappingFile: bpm/mapping/mappings.xml # IDM 模块的 MyBatis 映射文件配置 idmMybatisMappingFile: bpm/idm/mapping/mappings.xml # Modeler 模块的 MyBatis 映射文件配置 modelerMybatisMappingFile: bpm/modeler/mapping/mappings.xml # 是否自动部署流程定义 checkProcessDefinitions: true # 自动部署流程定义重复检查,是否部署资源内容 checkProcessDefinitionsAndContent: false # 可以独立关闭 BPM 的租户模块(若不启用,默认租户为 0) tenant.enabled: true # 自定义流程事件(全局事件),指定 Class 全称,多个用逗号隔开(实现 FlowableEventListener 接口) eventListeners: ~ # 分类表名称(v4.7.1+ 有 bpm_category 迁移到 biz_category) categoryTableName: biz_category 123456789101112131415161718192021222324252627282930313233343536373839# 导出导入流程

该功能是 v4.3.0 新增功能,可快速导出和导入流程 zip 压缩包(包含:流程bpmn、流程图、表单、流程事件)。

进入流程定义管理菜单,找到要导出的流程,点击操作列里的“导出”按钮即可下载 zip 文件。

点击右上角的“导入流程”,可快速导入 zip 文件里包含的流程、表单和事件等信息。

# 流程退回命令 BpmTask params = new BpmTask(); // 当前任务ID params.setId(taskId); // 退回到第一步 params.setActivityId(BpmTask.BACK_TASK_FIRST); // 退回到上一步 params.setActivityId(BpmTask.BACK_TASK_PREV); // 执行退回命令 bpmTaskService.backTask(params); 123456789# 分配人高级应用

除了通过用户或角色分配人员,我们还可以通过程序逻辑计算出结果,进行分配:

下面我们来举例:根据某角色并在某部门的用户分配方法

EmpUserDao.xml 里添加

SELECT a.user_code, a.user_name FROM js_sys_user a JOIN js_sys_user_role ur ON ur.user_code = a.user_code JOIN js_sys_employee e ON e.emp_code = a.ref_code JOIN js_sys_office o ON o.office_code = e.office_code WHERE ur.role_code = #{roleCodes} AND o.office_code= #{officeCodes} 12345678910

EmpUserService.java 里添加

public List findUserByRoleAndOffice(String roleCodes, String officeCodes) { List userList = dao.findUserByRoleAndOffice(roleCodes, officeCodes); return userList.stream().map(EmpUser:getUserCode).collect(Collectors.toList()); } 1234

数据有了,但还需要传递给流程引擎,下面我们举例 2 种调用方法:

方法一:

直接在分配人填写表达式,可以调用 Spring Bean 的方法:

流程设计器里,点击任务节点 -> 分配用户 -> 候选用户 -> 填写如下表达式:

${empUserService.findUserByRoleAndOffice("dept", "SDJN01")} 1处理人为一个人的时候,填写到 “分配” 文本框中 处理人为多人的时候,填写到 “候选用户” 文本框中 返回数据为多个角色的时候,填写到 “候选组” 文本框中

方法二:

利用流程事件脚本设置流程变量的方式

1)字典里添加活动开始事件类型(如果存在,则忽略)

2)给节点设置接受人变量

处理人一个人的时候,设置 userCode 到 “分配人” 文本框里 处理人多个人的时候,设置 userCode 到 “候选用户” 里

3)给节点添加事件,进入节点时在脚本中赋值

上图是直接赋值处理人,如何调用刚才我们写的方法呢,写如下脚本即可:

def empUserService = SpringUtils.getBean("empUserService"); String userCode = empUserService.findUserByRoleAndOffice("dept", "SDJN01"); execution.setVariable("userCode", userCode); 123

常见问题

设置 “分配人” 的时候,表达式结果如 userCode 变量应该为 String 类型; 设置 “候选用户” 或 “候选组” 的时候,表达式结果为 List 类型。 若提示 userCode 属性未定义,也可以尝试这样赋值:execution.setVariable("userCode", "user16_bw27"); # 流程事件中得到处理人

创建事件类型:任务创建

println "==================== 获取任务处理人 "; if (eventEntity != null && eventEntity instanceof org.flowable.task.service.impl .persistence.entity.TaskEntity){ // 如果设定了下一步处理人 String nextUserCodes = com.jeesite.common.web.http.ServletUtils.getParameter('nextUserCodes'); if (StringUtils.isNotBlank(nextUserCodes)){ println "下一步处理人:" + nextUserCodes; } // 如果是有处理人,则排除当前处理人 else if (StringUtils.isNotBlank(eventEntity.getAssignee())){ if (!eventEntity.getAssignee().equals(UserUtils.getUser().getUserCode())){ println "处理人:" + eventEntity.getAssignee(); } } // 如果是组任务 else{ for (org.flowable.identitylink.service.impl.persistence.entity. IdentityLinkEntity e : eventEntity.getCandidates()){ println "候选组:" + e.getGroupId(); } } } println "==================== 获取任务处理人 end "; 1234567891011121314151617181920212223

在该事件里,到的处理人或候选组后,可用于发送消息等。

发送消息参考:https://gitee.com/thinkgem/jeesite5/issues/I5EUTI (opens new window)

# 流程事件中得到流转命令 import com.jeesite.modules.bpm.utils.BpmUtils; if (BpmUtils.isCurrentCmd(execution, 'start')){ println '启动'; } else if (BpmUtils.isCurrentCmd(execution, 'claim')){ println '签收'; } else if (BpmUtils.isCurrentCmd(execution, 'complete')){ println '提交'; } else if (BpmUtils.isCurrentCmd(execution, 'back')){ println '退回'; } else if (BpmUtils.isCurrentCmd(execution, 'turn')){ println '转办'; } else if (BpmUtils.isCurrentCmd(execution, 'delegate')){ println '委托'; } else if (BpmUtils.isCurrentCmd(execution, 'stop')){ println '终止'; } else if (BpmUtils.isCurrentCmd(execution, 'move')){ println '自由跳转'; } 12345678910111213141516171819202122232425# 得到下一步处理人信息

接口获取:

POST: /a/bpm/bpmTask/getNextUser RequestBody: { prodDefKey: '流程定义Key', activityId: '当前活动ID', dataMap: {} } 1234567

Java 工具:

NextNodeParams params = new NextNodeParams(); params.setProdDefKey("流程定义Key"); params.setActivityId("当前活动ID"); params.setVariables("下一步节点用到的流程变量"); List taskList = BpmNextNodeUtils.getNextUserTasks(params); System.out.println(taskList); 123456# 每个环节使用不同的表单 进入“业务流程 -> 流程管控 -> 流程定义”菜单。找到“您要配置的流程定义”数据行,点击“业务关联”操作按钮,进入“流程表单”界面。 一个流程可以关联多个业务单据,如:某公司8种费用审批流程,表单不一样,但流程相同,这里的表单 key 对应一种费用。表单的版本对应流程的版本,当新发布一个流程定义的时候,流程表单也跟随新增版本。 流程表单是一个树结构,顶级表单定义为全局表单(对所有环节生效);下级表单为环节表单(该表单只对指定环节生效)。若没有定义环节表单的环节,则获取全局表单数据。 当指定多表单的 URL 时,需传递一个业务主键,这个业务主键贯穿整个流程,是全部环节表单的重要关联关系。如在发起表单,业务主键是ID,而在环节表单中,业务主键应该是一个外键(发起表的ID)。 # 会签参数配置 # 多实例参数 多实例类型:Parallel(并行,并发执行);Sequential(串行,按顺序执行) 多实例基数:定义多实例的基数,指定多实例会签人数,循环多少次后结束,一般设定会签人集合的大小,例如:${num} (提交任务的时候设定该变量,类型为 Integer) 多实例集合变量名:定义多实例集合的变量名,指定会签人的集合,根据集合中元素去创建任务数量,例如:pers (提交任务的时候设定该变量,类型为 List) 多实例元素变量名:集合中每个元素的变量名,可在每个任务中获取,例如:per (将该变量名设定到分配人,如:${per}) 多实例完成条件:定义多实例的完成条件,内置多实例变量包括:nrOfInstances(实例总数)、nrOfActiveInstances(当前还未完成的,对于顺序的多实例,此值总是1)、nrOfCompletedInstances(已完成的实例个数),例如:${nrOfCompletedInstances/nrOfInstances >= 0.8} (说明当有 80% 的任务完成时,完成此多实例)、${nrOfCompletedInstances/nrOfInstances == 1} (说明全部任务都完成时,完成此多实例) # 配置举例说明

1、第一种,简单情况:明确会签人员的个数,会签人员全部签收后,再继续流转

多实例类型:Parallel(并行) 多实例基数:${num} 多实例集合变量名:pers 多实例元素变量名:per 多实例完成条件:空(无需设定) 分配用户:固定值:分配: ${per}

2、第二种:复杂情况:会签人员部分签收后(如 80%),或更复杂的计算条件通过后,再继续流转

多实例类型:Parallel(并行) 多实例基数:空(无需设定) 多实例集合变量名:pers 多实例元素变量名:per 多实例完成条件:${nrOfCompletedInstances/nrOfInstances >= 0.8} 分配用户:固定值:分配: ${per}

Java 调用举例

// 1) 设置流程变量 Map variables = MapUtils.newHashMap(); List pers = ListUtils.newArrayList("user1", "user2");; // 会签人员列表 variables.put("num", pers.size()); // 第一种需要设置,第二种无需设置 variables.put("pers", pers); // 2) 启动流程示例 BpmUtils.start(oaLeave, "leave", variables, null); // or 提交流程任务 BpmUtils.complete(oaLeave, variables, null); 123456789# Flowable 学习资源

Flowable 官方文档:https://jeesite.com/front/flowable/6.5.0/bpmn/index.html (opens new window)

Flowable 数据字典表结构:https://my.oschina.net/thinkgem/blog/4765491 (opens new window)

点击我,看清晰大图。 点击我,看清晰大图。

Flowable 表结构的 Excel 版本可联系售后人员获取。

# Cloud 中使用 BPM 模块 # 1、引入依赖

v4.2.3 打开 jeesite-cloud-module-core 项目的 pom.xml 文件,加入如下模块依赖代码

com.jeesite jeesite-cloud-module-bpm-rest ${project.parent.version} 123456

v4.3.0+ 无需加入到 jeesite-cloud-module-core 直接启动 jeesite-cloud-module-bpm 项目即可。

打开 jeesite-cloud-module-test1-client 项目的 pom.xml 文件,加入如下模块依赖代码

com.jeesite jeesite-cloud-module-bpm-client ${project.parent.version} 123456# 2、新建测试代码 @Controller @RequestMapping(value = "${adminPath}/test1/testData") public class TestData1Controller2 extends BaseController { /** * 业务流程测试 */ @RequiresPermissions("test:testData:edit") @RequestMapping(value = "bpmTest") @ResponseBody public String bpmTest() { // 模拟创建一个业务实体对象 BpmEntity bpmEntity = new BpmEntity(){ private static final long serialVersionUID = 1L; private String bpmEntityId = IdGen.nextId(); @Override public String getId() { return bpmEntityId; } }; Map variables = MapUtils.newHashMap(); variables.put("leaveDays", 5); // 启动流程 BpmTask start = BpmUtils.start(bpmEntity, "leave", variables, null); System.out.println("启动流程实例:"+start.getProcIns().getId()); // 根据业务表单获取流程实例 BpmProcIns procIns = BpmUtils.getProcIns(bpmEntity, "leave"); System.out.println("获取流程实例:"+procIns.getId()); // 查询并完成流程实例下的任务,直到全部任务都已经完成 BpmTaskServiceClient bean = SpringUtils.getBean(BpmTaskServiceClient.class); while(true){ BpmTask where = new BpmTask(procIns); where.setStatus(BpmTask.STATUS_UNFINISHED); Page page = bean.findTaskPage(where); List list = page.getList(); System.out.println("====== 任务数:"+list.size()); if (list.size() == 0){ break; } for (BpmTask task : list) { bpmEntity.getBpm().setTaskId(task.getId()); if (StringUtils.isBlank(task.getAssignee())){ System.out.println("签收任务:"+task.getId()); bean.claimTask(task); } System.out.println("转办任务:"+task.getId()); task.setUserCode("admin"); bean.turnTask(task); System.out.println("完成任务:"+task.getId()); BpmUtils.complete(bpmEntity, variables, null); } } return renderResult(Global.TRUE, "业务流程测试完成,请打开 " + Global.getProperty("spring.application.name") + " 控制台,查看执行状态!"); } } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162# 3、运行测试代码

直接访问:http://127.0.0.1:8980/js/a/test1/testData/bpmTest (opens new window)

# 微服务环境下常见问题 流程事件脚本提示 No bean named 'xxx' available 没有 Bean,解决方法: 确保该类是否在当前 bpm 微服务下存在,若不存在请在 cloud-module-bpm 里引入对应 bean 的 client 包 将 BpmUtils 换为 BpmCloudUtils 专属工具,进行 updateStatus 操作 默认通过 benaName 方式获取 Service Bean,可以更改为 className 方式获取 如果出现事务锁 Lock wait timeout exceeded 请更新 BpmCloudUtils 文件


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3