SpringBoot + Activiti6

  • 一、Activiti6简述
    • 1、简述
    • 2、接口
    • 3、库表
  • 二、创建BPMN业务流程模型
  • 二、整合
    • 1.POM依赖
    • 2. bpmn20.xml部署
    • 3.application.properties配置
    • 4.关闭SpringSecurity权限配置。
    • 5.调用服务
      • 5.1 接口描述
      • 5.2 接口实例
    • 6、节点跳转
      • 6.1、回退
      • 6.2、任意跳转
  • 三、问题
    • 1、Could not erite JSON:lazy loading outside command context;

一、Activiti6简述

1、简述

activiti介绍 Activiti是由Alfresco软件在2010年5月17日发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理,工作流,服务协作等领域的一个开源,灵活的,易扩展的可执行流程语言框架。

2、接口

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

3、库表

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

二、创建BPMN业务流程模型

bpmn编辑方式很多,最后生成的文件也是有一定差异,比如idea的actiBPM、eclipse的Designer等。
尝试几个之后,创建bpmn文件后内容标签很乱,因此采用官方Activiti提供的流程设计器应用,最终生成xml文件。提供了三个war包,分别是activiti-admin.war、activiti-app.war、activiti-rest.war。如果只是画图的话,将activiti-app.war部署到Tomcat的webapps目录,启动Tomcat即可。

二、整合

SpringBoot部分就不在描述了,不论使用idea还是eclipse都一样,先直接创建一个SpringBoot项目,配置好数据库、连接池、持久层等即可。以下支展示流程引擎相关部分。

1.POM依赖

<dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter-basic</artifactId><version>6.0.0</version>
</dependency>

2. bpmn20.xml部署

将创建出的.bpmn20.xml文件拷贝到项目文件夹/resources/processes下,可进行自动部署。若业务要求手动,也可以不添加此步骤,开发接口直接上传文件部署也可以。

3.application.properties配置

#是否每次都更新数据库
spring.activiti.database-schema-update=true
# 自动部署验证设置:true-开启(默认)、false-关闭
spring.activiti.check-process-definitions=true
spring.activiti.process-definition-location-prefix=classpath:/processes/ *.bpmn
#保存历史数据级别设置为full最高级别,便于历史数据的追溯
spring.activiti.history-level=full
spring.activiti.db-history-used=true

4.关闭SpringSecurity权限配置。

如未引入SpringSecurity权限,则需要关闭权限校验的自动配置加载:exclude = SecurityAutoConfiguration.class。如果就是使用的这个则无需操作此步骤。

@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
@MapperScan("net.cnki.mapper")
public class ActiviotiBoot6Application {public static void main(String[] args) {SpringApplication.run(ActiviotiBoot6Application.class, args);}
}

5.调用服务

一下服用调用过程中,尽量保持与业务脱离,进行解耦。

5.1 接口描述

/*** 引擎管理服务,和具体业务无关,主要是可以查询引擎配置,数据库,作业等*/@Autowiredprivate ManagementService managementService;/*** 仓储服务,用于管理流程仓库,例如:部署,删除,读取流程资源* 可以用来部署我们的流程图,还可以创建我们的流程部署查询对象,用于查询刚刚部署的流程列表,便于我们管理流程*/@Autowiredprivate RepositoryService repositoryService;/*** 运行时服务,可以处理所有正在运行状态的流程实例,任务等* 主要用来开启流程实例,一个流程实例对应多个任务,也就是多个流程节点*/@Autowiredprivate RuntimeService runtimeService;/*** 任务服务,用于管理,查询任务,例如:签收,办理,指派等* 是用来可以用来领取,完成,查询任务列表功能的*/@Autowiredprivate TaskService taskService;/*** 唯一服务,可以管理,查询用户,组之间的关系* 操作用户信息,用户分组信息等,组信息包括如部门表和职位表,可以自己建表来存储用户信息和组信息*/@Autowiredprivate IdentityService identityService;/*** 历史服务,可以查询所有历史数据,例如:流程实例,任务,活动,变量,附件等* 可以查看审批人曾经审批完成了哪些项目,审批项目总共花了多少时间,以及在哪个环节比较耗费时间等等*/@Autowiredprivate HistoryService historyService;

5.2 接口实例

1、流程部署,在3张表中产生数据

  • act_ge_bytearray 流程资源文件(2条数据)
  • act_re_deployment 流程部署(1条数据)
  • act_re_procdef 流程实例(1条数据)
方式一:
Deployment deployment = repositoryService.createDeployment().name(processName)//为本次部署命名.addClasspathResource("processes/"+ bpmnName +".bpmn")//添加流程规则文件.addClasspathResource("processes/"+ bpmnName +".png")//添加流程规则图片.deploy();//部署
方式二:
Deployment deployment = repositoryService.createDeployment().name(deployName).addInputStream(fileName,ipt).deploy();//部署

2、启动流程实例
关注2张表:

  • act_ru_execution : 所有流程按照规则指定到活动节点时,都会产生一个对应的Execution
  • act_ru_task : 只针对人工任务,针对Execution做的扩展信息描述
//一般使用时不会直接记录流程过程中的各种id,而是通过绑定的业务id(businessKey)处理
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey,businessKey,variables);

3、执行流程节点
有两种情况,一是指定执行人,此时当前用户直接处理即可。二是指定角色,需要用户认领后再处理。此过程根据需要添加批注,便于记录过程中操作

//认领节点
taskService.claim(taskId,userId);
//完成节点
taskService.complete(taskId, variables);

4、添加批注
此方法有两步,先设置认证用户、再添加批注。

//设置认证用户
identityService.setAuthenticatedUserId(userId);
//添加批注
taskService.addComment(taskId,processInstanceId,message);

5、查询待处理节点
为了方便,此处使用了流程引擎的角色用户关联模块,同时包含当前流程参数的过滤条件、当前用户所属角色组以及自己所属的任务查询。
variables:启动流程实例时,对应的实例自定义参数,一般为业务相关参数。
assignee:当前用户信息,用户id或用户名均可。只要都统一就行

TaskQuery query = taskService.createTaskQuery();
variables.forEach((k,v) -> {if(!StringUtils.isEmpty(v)) query.processVariableValueEquals(k, v);});
List<Task> list = query.taskCandidateOrAssigned(assignee).includeProcessVariables().orderByTaskCreateTime().asc().list();

6、节点跳转

总有一些特别需求,不按套路出牌的。更加灵活的处理节点,还别说,这样情况不少。这是实现所需要的接口服务层。

/*** 仓储服务,用于管理流程仓库,例如:部署,删除,读取流程资源* 可以用来部署我们的流程图,还可以创建我们的流程部署查询对象,用于查询刚刚部署的流程列表,便于我们管理流程*/@Autowiredprivate RepositoryService repositoryService;/*** 运行时服务,可以处理所有正在运行状态的流程实例,任务等* 主要用来开启流程实例,一个流程实例对应多个任务,也就是多个流程节点*/@Autowiredprivate RuntimeService runtimeService;/*** 任务服务,用于管理,查询任务,例如:签收,办理,指派等* 是用来可以用来领取,完成,查询任务列表功能的*/@Autowiredprivate TaskService taskService;/*** 历史服务,可以查询所有历史数据,例如:流程实例,任务,活动,变量,附件等* 可以查看审批人曾经审批完成了哪些项目,审批项目总共花了多少时间,以及在哪个环节比较耗费时间等等*/@Autowiredprivate HistoryService historyService;/*** 唯一服务,可以管理,查询用户,组之间的关系* 操作用户信息,用户分组信息等,组信息包括如部门表和职位表,可以自己建表来存储用户信息和组信息*/@Autowiredprivate IdentityService identityService;

6.1、回退

回退到指定历史节点

/*** 回退到指定历史节点* @param processInstanceId 流程实例ID* @param targetTaskId 回跳任务ID,为null是默认选择最近的上一任务* @param variables 撤回者处理当前任务参数* @return*/Boolean rollBackTargetHisNode(String processInstanceId, String targetTaskId, Map<String, Object> variables);
@Overridepublic Boolean rollBackTargetHisNode(String processInstanceId, String targetTaskId, Map<String, Object> variables) {//获取流程实例ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();if (pi == null){//流程已结束return false;}//对应实例的任务历史节点List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).orderByTaskCreateTime().desc().list();// 跳转的之前某一节点HistoricTaskInstance targetTask = getTargetTask(htiList,targetTaskId);// list里第一条代表当前任务HistoricTaskInstance curTask = htiList.get(0);if (targetTask == null){return false;}targetTaskId = targetTask.getId();BpmnModel bpmnModel = repositoryService.getBpmnModel(targetTask.getProcessDefinitionId());// 得到ActivityId,只有HistoricActivityInstance对象里才有此方法String lastActivityId = getLastActivityId(targetTaskId,targetTask.getExecutionId());// 得到上个节点的信息FlowNode lastFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(lastActivityId);// 取得当前节点的信息Execution execution = runtimeService.createExecutionQuery().executionId(curTask.getExecutionId()).singleResult();FlowNode curFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(execution.getActivityId());// 记录当前节点的原活动方向List<SequenceFlow> oriSequenceFlows = new ArrayList<>();oriSequenceFlows.addAll(curFlowNode.getOutgoingFlows());// 清理活动方向curFlowNode.getOutgoingFlows().clear();// 建立新方向List<SequenceFlow> newSequenceFlow = newSequenceFlowList(curFlowNode,lastFlowNode);curFlowNode.setOutgoingFlows(newSequenceFlow);// 添加批注并完成任务Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();commentAndComplete(processInstanceId,task.getId(),task.getAssignee(),variables,"撤回");// 恢复原方向curFlowNode.setOutgoingFlows(oriSequenceFlows);Task nextTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();// 设置执行人if (nextTask != null) {taskService.setAssignee(nextTask.getId(), targetTask.getAssignee());}return true;}

6.2、任意跳转

可以跳转到本实例的任意节点-向前向后

/*** 跳转到指定节点<br>* 可以跳转到本实例的任意节点-向前向后* @param processInstanceId 实例ID* @param targetTaskId 任务ID,与运行时任务ID不同,为xml文件上的ID* @param variables 本节点处理参数* @return*/Boolean jumpSpecifiedNode(String processInstanceId, String targetTaskId, Map<String, Object> variables);
@Overridepublic Boolean jumpSpecifiedNode(String processInstanceId, String targetTaskId, Map<String, Object> variables) {//获取流程实例ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();if (pi == null){//流程已结束return false;}//当前任务Task curTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();BpmnModel bpmnModel = repositoryService.getBpmnModel(curTask.getProcessDefinitionId());//目标任务Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();Optional<FlowElement> optional = flowElements.stream().filter(target -> targetTaskId.equals(target.getId())).findFirst();if (!optional.isPresent()){//无指定任务return false;}UserTask targetUserTask = (UserTask) optional.get();// 获取目标节点的信息FlowNode targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(targetUserTask.getId());// 取得当前节点的信息Execution execution = runtimeService.createExecutionQuery().executionId(curTask.getExecutionId()).singleResult();FlowNode curFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(execution.getActivityId());// 记录当前节点的原活动方向List<SequenceFlow> oriSequenceFlows = new ArrayList<>();oriSequenceFlows.addAll(curFlowNode.getOutgoingFlows());// 清理活动方向curFlowNode.getOutgoingFlows().clear();// 建立新方向List<SequenceFlow> newSequenceFlow = newSequenceFlowList(curFlowNode,targetFlowNode);curFlowNode.setOutgoingFlows(newSequenceFlow);// 添加批注并完成任务commentAndComplete(processInstanceId,curTask.getId(),curTask.getAssignee(),variables,"跳转");// 恢复原方向curFlowNode.setOutgoingFlows(oriSequenceFlows);// 设置执行人Task nextTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();if (nextTask != null) {taskService.setAssignee(nextTask.getId(), targetUserTask.getAssignee());}return true;}

针对上边的两个跳转,提出一些不影响查看思路的公共部分。大体思路相同。

/*** 添加批注并完成任务* @param processInstanceId* @param curTaskId 当前任务ID* @param targetTaskAssignee* @param variables* @param message*/private void commentAndComplete(String processInstanceId,String curTaskId,String targetTaskAssignee,Map<String, Object> variables,String message){identityService.setAuthenticatedUserId(targetTaskAssignee);taskService.addComment(curTaskId,processInstanceId,message);taskService.complete(curTaskId,variables);}/*** 获取指定的目标历史任务* @param htiList 历史任务列表* @param targetTaskId 目标任务ID,为null是默认选择最近的上一任务* @return*/private HistoricTaskInstance getTargetTask(List<HistoricTaskInstance> htiList,String targetTaskId){HistoricTaskInstance targetTask = null;if (targetTaskId == null){targetTask = htiList.get(1);} else {Optional<HistoricTaskInstance> optional = htiList.stream().filter(hisTask -> targetTaskId.equals(hisTask.getId())).findFirst();targetTask = optional.isPresent() ? optional.get() : null;}return targetTask;}/*** 新建连线* @param curFlowNode 当前节点* @param targetFlowNode 目标节点* @return*/private List<SequenceFlow> newSequenceFlowList(FlowNode curFlowNode,FlowNode targetFlowNode) {List<SequenceFlow> newSequenceFlowList = new ArrayList<>();SequenceFlow newSequenceFlow = new SequenceFlow();newSequenceFlow.setId("newJumpFlow");newSequenceFlow.setSourceFlowElement(curFlowNode);newSequenceFlow.setTargetFlowElement(targetFlowNode);newSequenceFlowList.add(newSequenceFlow);return newSequenceFlowList;}/*** 获取目标历史节点的活动ID* @param targetTaskId 目标历史节点* @param targetExecutionId 目标历史执行流ID* @return*/private String getLastActivityId(String targetTaskId,String targetExecutionId){List<HistoricActivityInstance> haiFinishedList = historyService.createHistoricActivityInstanceQuery().executionId(targetExecutionId).finished().list();Optional<HistoricActivityInstance> optional = haiFinishedList.stream().filter(his -> targetTaskId.equals(his.getTaskId())).findFirst();String lastActivityId = optional.isPresent() ? optional.get().getActivityId() : null;return lastActivityId;}

三、问题

1、Could not erite JSON:lazy loading outside command context;

返回数据json转化异常
自己测试的时候,如果直接流程引擎相关返回的对象数据直接返回,就会有json问题,因此一般都需要自己转化一下,不要直接使用task等。

SpringBoot + Activiti6简单教程包含任意跳转节点相关推荐

  1. 最新Kafka教程(包含kafka部署与基本操作、java连接kafka、spring连接kafka以及使用springboot)

    最新Kafka教程(包含kafka部署与基本操作.java连接kafka.spring连接kafka以及使用springboot) 欢迎转载,转载请注明网址:https://blog.csdn.net ...

  2. 很详细的SpringBoot整合UEditor教程

    很详细的SpringBoot整合UEditor教程 2017年04月10日 20:27:21 小宝2333 阅读数:21529 版权声明:本文为博主原创文章,未经博主允许不得转载. https://b ...

  3. springboot Activiti6

    springboot Activiti6 流程建模 需要下载设计工具Activiti6.0 UI 运行设计工具 登录页 流程图效果 BPMN2.0构件 起点 结束 活动 流程线 网关 变量设置 画不同 ...

  4. expect简单教程

    expect简单教程 一.概述 expect是Unix系统中用来进行自动化控制和测试的软件工具,由Don Libes制作,作为Tcl脚本语言的一个扩展,应用在交互式软件中如telnet,ftp,Pas ...

  5. ASP.NET Core 登录登出 - ASP.NET Core 基础教程 - 简单教程,简单编程

    ASP.NET Core 登录登出 - ASP.NET Core 基础教程 - 简单教程,简单编程 原文:ASP.NET Core 登录登出 - ASP.NET Core 基础教程 - 简单教程,简单 ...

  6. ASP.NET Core 动作结果 - ASP.NET Core 基础教程 - 简单教程,简单编程

    ASP.NET Core 动作结果 - ASP.NET Core 基础教程 - 简单教程,简单编程 原文:ASP.NET Core 动作结果 - ASP.NET Core 基础教程 - 简单教程,简单 ...

  7. ASP.NET Core Identity 迁移数据 - ASP.NET Core 基础教程 - 简单教程,简单编程

    ASP.NET Core Identity 迁移数据 - ASP.NET Core 基础教程 - 简单教程,简单编程 原文:ASP.NET Core Identity 迁移数据 - ASP.NET C ...

  8. springboot 系列技术教程目录

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 一.教程目录地址: springboot系列技术教程目录 二.教程内容: springboot2.X ...

  9. ASP.NET Core 项目配置 ( Startup ) - ASP.NET Core 基础教程 - 简单教程,简单编程

    ASP.NET Core 项目配置 ( Startup ) - ASP.NET Core 基础教程 - 简单教程,简单编程 原文:ASP.NET Core 项目配置 ( Startup ) - ASP ...

最新文章

  1. 2673(2673)shǎ崽 OrOrOrOrz
  2. eclipse安装ADT插件重启后不显示Android SDK Manager和Android Virtual Device Manager图标的一种解决办法
  3. 内核request_mem_region 和 ioremap的理解
  4. Java基础day24
  5. 时钟源为什么会影响性能
  6. nginx利用image_filter动态生成缩略图
  7. 奇怪的 Markdown / LaTeX 笔记
  8. android 布局可大可小,UI设计教程之:ios与android ui适配(将IOS UI转换成Android经验畅谈)...
  9. 全局系统性地把握客户感知-建立VOC
  10. python数字右对齐_python用format把float、int等数字字符串化设置左对齐右对齐居中对齐,宽度,保留几位...
  11. 软件测试高频面试题真实分享/网上银行转账是怎么测的,设计一下测试用例。
  12. matlab进化树的下载,mega(进化树构建软件)下载 v7.0.14免费版
  13. c计算机软考中级考什么,计算机软考中级考试内容
  14. 如何通过DOI号来获取相关文献并下载?
  15. 手把手教你 Charles 的使用「部分逆向的解决方法」
  16. 跳妹儿学编程之ScratchJr(三):什么是儿童编程语言?初识ScratchJr和Scratch
  17. shopex php5.3,shopex.4.85支持php5.3 | 学步园
  18. html5+一屏一区域内容,iPhoneX页面安全区域与内容重叠问题
  19. Zotero+Web of Science 实现批量导入下载文献
  20. 谈谈面试题之为什么用线程池?解释下线程池参数?

热门文章

  1. 用C语言编写数字时钟
  2. 华为通信/网络方面的笔试题——电信基础知识题库
  3. 5层因特网协议栈 和 7层OSI参考模型
  4. 阿里云IoT2018年度十佳合作伙伴20强入围企业公布...
  5. 中国省份数据库+世界国家名数据库
  6. STMP3770启动流程
  7. 计算正方形长方形圆形的周长面积
  8. 网易云信短信接口java,调用网易云短信验证码接口Demo
  9. 最短路径 Dijkstra算法的Matlab代码实现
  10. 电子一寸照片怎么弄?教你修改照片尺寸小妙招