activiti的使用
此文章将描述activiti的使用,activiti的集成,部署、启动、节点流转、驳回、批注等功能的基本使用。
目录
activiti的使用
背景
技术栈
参考
接口
先接入activiti吧
a、添加依赖
b、项目resources目录下新建activiti.cfg.xml
c、application.yml中srping下添加:
emm~~现在来绘制activiti流程图
1、绘制工具
2、我们会用到的图形
3、按照图形进行绘制,连线
4、设置process的id、name
5、设置每个节点的id、name
6、设置顺序流的name、condition
7、设置UserTask的候选人Canditate Users
8、生成xml、png文件
接口详解
1、创建数据库表
2、流程部署
3、流程删除
4、查询已部署的流程
5、启动流程
6、查询待处理的任务
7、查询处理中的任务
8、查询已处理的任务
9、任务处理(签收/退回/审核)
10、查询历史备注
activiti的使用
我把activiti集成在了spring-boot项目中,首先就是新建一个springboot项目,这里不再赘述springboot项目的搭建。
背景
在平常的业务开发中,遇到了一些审核流程,而且比较多,自己设计了几个数据库表,基本能满足自己的需求,但由于业务可能会越来越大,越来越复杂,就想到用activiti来解决我的问题,于是学习了activiti。
技术栈
springboot 1.X + mybatis 3.X + activiti6.0.0
参考
在学习过程中从网上找了一些博客进行参考,我也有一份电子书,文件太大就不上传了。
- 部署流程资源的三种方式 https://blog.csdn.net/zjx86320/article/details/50234707
- Activiti数据库表结构 https://blog.csdn.net/hj7jay/article/details/51302829
- IntelliJ IDEA安装Activiti插件并使用 https://blog.csdn.net/gozhuyinglong/article/details/80336765
- activiti 任务节点 处理人设置 https://blog.csdn.net/qq_30739519/article/details/51225067
- Activiti6实现自由跳转 https://segmentfault.com/a/1190000013952695 我应用此代码时加入了comment备注
- Activiti工作流的流转任务和结束任务 https://blog.csdn.net/liuming134/article/details/80453907
- Activiti添加批注(comment)信息 https://www.cnblogs.com/cxyj/p/3898535.html
- Activitivi之启动流程/完成任务的时候设置流程变量 https://www.cnblogs.com/shyroke/p/8000757.html
- Activiti任务认领 https://www.cnblogs.com/boulder/p/3658528.html
- Activiti之历史活动查询和历史任务查询和流程状态查询 https://www.cnblogs.com/shyroke/p/7994931.html
接口
我的项目完成了以下接口:
1、创建数据库表
2、流程部署
3、流程删除
4、查询已部署的流程
5、启动流程
6、查询待处理的任务
7、查询处理中的任务
8、查询已处理的任务
9、任务处理(签收/退回/审核)
10、查询历史备注
先接入activiti吧
6.0和5.21有什么区别?
在应用当中发现:
1)6.0删除了pvm包,ActivityImpl用不了了,实现节点跳转时就不能用这个了
别的区别,em~~~自己去看吧
a、添加依赖
<dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter-basic</artifactId><version>6.0.0</version>
</dependency>
b、项目resources目录下新建activiti.cfg.xml
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"><property name="jdbcUrl" value="jdbc:mysql://10.10.30.31:3306/hare-arch-demo" /><property name="jdbcDriver" value="com.mysql.jdbc.Driver" /><property name="jdbcUsername" value="root" /><property name="jdbcPassword" value="rootroot" /><property name="databaseSchemaUpdate" value="true" /></bean></beans>
c、application.yml中srping下添加:
spring:jpa:show-sql: trueproperties:hibernate:dialect: org.hibernate.dialect.MySQL5Dialectformat_sql: trueuse_sql_comments: truehibernate:ddl-auto: update
emm~~现在来绘制activiti流程图
1、绘制工具
我用的是idea的actiBPM,虽然有点缺点,但是总归还能用,没有安装的请安装此plugin。
2、我们会用到的图形
1.StartEvent 开始事件
2.EndEvent 结束事件
3.UserTask 用户任务
4.ExclusiveAateway 排他网关
5.sequenceFlow 顺序流
其它的图形我没用到,有需要的小伙伴可以自己去研究~~
先上传一个成品截图:
这是我从实际业务中拿出来的流程图,我也做了牺牲了~
3、按照图形进行绘制,连线
4、设置process的id、name
点击bpmn画布空白处,即可看到属性设置,见上图
5、设置每个节点的id、name
点击每个节点,即可看到属性设置
6、设置顺序流的name、condition
点击每个连线,即可看到属性设置
condition的表达式格式为:${pass == true}
7、设置UserTask的候选人Canditate Users
结合业务,Canditate Users的表达式格式为:${face_audit},表示面审候选人,其他UserTask候选人可按照各自功能进行命名:
${face_audit} 面审
${final_first_audit} 终审1
${final_second_audit} 终审2审
${final_third_audit} 终审3审
${res_invest} 尽调
${loan_apply} 放款申请
${loan_audit} 放款审核
为什么设置Canditate Users?
为了结合业务,实现任务的节点的签收/退回,
当Canditate Users有值而assignee为空,则为待处理;
当Canditate Users有值而assignee不为空,则为处理中。
assignee为处理人。
8、生成xml、png文件
xml文件:直接复制demo.bpmn文件,并且把文件名改为demo.xml文件即可,可以以xml格式看到这个流程。
png文件:右键demo.xml,Diagrams-->show BPMN2.0 Designer,右键空白处-->Export to file 即可。
上传一张png给大家看:
好了,demo绘制完毕,接下来看代码~
接口详解
我贴出来的是service的代码,要看完整代码,请看文章末尾gitee地址。
1、创建数据库表
/*** 创建数据库表* 根据配置文件activiti.cfg.xml创建ProcessEngine*/@Overridepublic void createProcessEngine() {ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");ProcessEngine engine = cfg.buildProcessEngine();}
执行上述接口后,对应的数据库里会自动生成activiti相关的数据库表。
2、流程部署
需要传入参数deploymentName 部署名称,force是否强制部署,error是个map。
其中参数deploymentName为xml文件中process标签的id。
/*** 流程部署** @param deploymentName 流程部署名称* @param force 是否强制部署,true强制,false非强制* @param error*/@Overridepublic void deployProcess(String deploymentName, boolean force, ErrorInfo error) {//判断流程是否已存在Deployment deploymentExist = processEngine.getRepositoryService().createDeploymentQuery().deploymentName(deploymentName).singleResult();//流程已存在if (null != deploymentExist) {if (force) {error.msg = "流程已存在,进行强制部署。";//强制部署时,先删除原有的部署流程processEngine.getRepositoryService()//true:级联删除.deleteDeployment(deploymentExist.getId(), true);log.info("【%s】流程强制部署,删除部署id【%d】", deploymentName, deploymentExist.getId());} else {error.msg = "流程已存在,不更新部署";log.info("【%s】流程已存在,不更新部署", deploymentName);return;}}//流程不存在,直接部署Deployment deployment = processEngine.getRepositoryService().createDeployment().name(deploymentName).addClasspathResource("processes/" + deploymentName + ".bpmn").addClasspathResource("processes/" + deploymentName + ".png").deploy();error.msg += "流程已部署";log.info("流程已部署,部署名称:" + deploymentName + ",部署ID:" + deployment.getId() + ",部署时间:" + deployment.getDeploymentTime());}
3、流程删除
有流程部署,就会有流程删除,看代码:
/*** 删除流程定义** @param deploymentId 流程部署ID_* @param error*/@Overridepublic void deleteDeployment(String deploymentId, ErrorInfo error) {//流程定义名称String deploymentName = "";List<Deployment> deploymentList = processEngine.getRepositoryService().createDeploymentQuery().list();if (null != deploymentList && deploymentList.size() > 0) {for (Deployment de : deploymentList) {if (de.getId().equalsIgnoreCase(deploymentId)) {deploymentName = de.getName();break;}}}//级联删除流程processEngine.getRepositoryService().deleteDeployment(deploymentId, true);error.code = 1;error.msg = "流程定义[" + deploymentName + "]已级联删除";log.info("流程定义[%s]已级联删除", deploymentName);}
4、查询已部署的流程
已成功部署的流程定义会放在act_re_deployment里。
/*** 查询全部流程定义** @return*/@Overridepublic List<DeploymentDto> getDeploymentList() {//结果集List<DeploymentDto> list = new ArrayList<>();//deployment结果集List<Deployment> deploymentList = processEngine.getRepositoryService().createDeploymentQuery().list();if (null != deploymentList && deploymentList.size() > 0) {for (Deployment de : deploymentList) {DeploymentDto dto = new DeploymentDto();dto.setId(de.getId());dto.setName(de.getName());dto.setCreateTime(de.getDeploymentTime());list.add(dto);}}return list;}
5、启动流程
启动流程时,除了传业务所需的参数外,第一个节点所涉及到的参数变量也必须传,否则流程无法启动:
variables.put("face_audit", "faceAudit");
我把节点必传的参数写在了CarProcessCanditateUsersEnum.java
/*** 启动流程** @param instanceKey 流程process的id,例:demo*/@Overridepublic ProcessInstance startProcess(String instanceKey, Map<String, Object> variables) {RuntimeService runtimeService = processEngine.getRuntimeService();//可根据id、key、message启动流程ProcessInstance myProcess_1 = runtimeService.startProcessInstanceByKey(instanceKey, variables);//流程实例IDlog.info("流程实例ID:" + myProcess_1.getId());//流程实例ProcessInstanceIdlog.info("流程实例ProcessInstanceId:" + myProcess_1.getProcessInstanceId());//流程实例ProcessDefinitionIdlog.info("流程实例ProcessDefinitionId:" + myProcess_1.getProcessDefinitionId());return myProcess_1;}
6、查询待处理的任务
我在activiti的使用中,UserTask任务用到了两个处理字段:Canditate Users 和 assignee
简单理解:当Canditate Users有值,assignee为null时,任务为待处理
/*** 查询当前任务办理人的当前任务--待处理* <p>* 两个参数都不传,则查询所有节点待办任务** @param candidateUser 当前任务候选角色变量* @return*/@Overridepublic List<PersonnelTaskDto> pendingTask(String candidateUser) {//task list结果集List<Task> tasks = new ArrayList<>();//封装成dto listList<PersonnelTaskDto> personnelTaskDtoList = new ArrayList<>();if (StringUtils.isNotBlank(candidateUser)) {//与任务相关的Servicetasks = processEngine.getTaskService().createTaskQuery()//创建一个任务查询对象.taskCandidateUser(candidateUser).orderByTaskCreateTime().desc().list();} else {//查询所有待办任务//与任务相关的Servicetasks = processEngine.getTaskService().createTaskQuery()//创建一个任务查询对象.orderByTaskCreateTime().desc().list();}if (tasks != null && tasks.size() > 0) {for (Task task : tasks) {//待处理if (StringUtils.isBlank(task.getAssignee())) {PersonnelTaskDto entity = new PersonnelTaskDto();entity.setTaskId(task.getId());entity.setAssignee(task.getAssignee());entity.setTaskName(task.getName());entity.setCreateTime(task.getCreateTime());entity.setProcessInstanceId(task.getProcessInstanceId());personnelTaskDtoList.add(entity);}}}//将结果放到pageDtoreturn personnelTaskDtoList;}
7、查询处理中的任务
我在activiti的使用中,UserTask任务用到了两个处理字段:Canditate Users 和 assignee
简单理解:当Canditate Users有值,assignee也有值时,任务为处理中
/*** 查询当前任务办理人的当前任务--处理中* <p> Constants.NODE_STATUS_HANDLING* 两个参数都不传,则查询所有节点处理中任务** @param candidateUser* @return*/@Overridepublic List<PersonnelTaskDto> handlingTask(String candidateUser) {//task list结果集List<Task> tasks = new ArrayList<>();//封装成dto listList<PersonnelTaskDto> personnelTaskDtoList = new ArrayList<>();TaskService taskService = processEngine.getTaskService();if (StringUtils.isNotBlank(candidateUser)) {//与任务相关的Servicetasks = taskService.createTaskQuery()//创建一个任务查询对象.taskCandidateUser(candidateUser).orderByTaskCreateTime().desc().list();} else {//查询所有待办任务//与任务相关的Servicetasks = taskService.createTaskQuery()//创建一个任务查询对象.orderByTaskCreateTime().desc().list();}if (tasks != null && tasks.size() > 0) {for (Task task : tasks) {//处理中if (StringUtils.isNotBlank(task.getAssignee())) {PersonnelTaskDto entity = new PersonnelTaskDto();entity.setTaskId(task.getId());entity.setAssignee(task.getAssignee());entity.setTaskName(task.getName());entity.setCreateTime(task.getCreateTime());entity.setProcessInstanceId(task.getProcessInstanceId());personnelTaskDtoList.add(entity);}}}return personnelTaskDtoList;}
8、查询已处理的任务
历史活动涉及到的表为act_hi_*
各种历史活动查询可参考资料:历史活动查询
我这里用了:
HistoryService historyService = processEngine.getHistoryService();
historyService.createHistoricActivityInstanceQuery()
可根据自己需求进行自行修改。
/*** 查询已处理的任务** @param assignee 处理人* @return*/@Overridepublic List<PersonnelTaskDto> handledTask(String assignee) {//封装成dto listList<PersonnelTaskDto> personnelTaskDtoList = new ArrayList<>();HistoryService historyService = processEngine.getHistoryService();List<HistoricActivityInstance> list = new ArrayList<>();//处理人不为空时查询处理人所属的已处理任务if (StringUtils.isNotBlank(assignee)) {list = historyService.createHistoricActivityInstanceQuery().taskAssignee(assignee).finished().desc().list();} else {//处理人为空时查询所有已处理任务list = historyService.createHistoricActivityInstanceQuery().list();}if (null != list && list.size() > 0) {for (HistoricActivityInstance instance : list) {PersonnelTaskDto entity = new PersonnelTaskDto();entity.setTaskId(instance.getId());entity.setAssignee(instance.getAssignee());entity.setTaskName(instance.getActivityName());entity.setCreateTime(instance.getStartTime());entity.setProcessInstanceId(instance.getProcessInstanceId());personnelTaskDtoList.add(entity);}}return personnelTaskDtoList;}
9、任务处理(签收/退回/审核)
流程启动之后,整个流程会停留在第一个节点,以demo为例,会停留在【面审】
然后此节点的候选人进行任务签收
当有此节点权限的人签收此任务后,可以用代码禁止再被其他人签收(我这里没做)
/*** 节点处理,0:审核,1:签收,2:退回** @param user* @param jumpToNodeDto* @param error*/@Overridepublic void handleNode(User user, JumpToNodeDto jumpToNodeDto, ErrorInfo error) {ActivitiServiceImpl impl = new ActivitiServiceImpl();//审核if (jumpToNodeDto.getIsClaim().equals(Constants.NODE_AUDIT)) {Map<String, Object> variables = prepareNodeJumpVaria(jumpToNodeDto);//流转impl.jumpToNode(jumpToNodeDto.isInOrder(), jumpToNodeDto.getTaskId(), jumpToNodeDto.getTargetFlowElementId(), jumpToNodeDto.getComment(), variables, error);} else {//签收/退回impl.claimActivity(jumpToNodeDto, user, error);}}
节点跳转,我这里做了顺序的的流转和跳转,因为在审核流程中任务有可能被驳回,也有可能最高权限的人一键审核通过。
跳转的代码,可以下载我的代码看一下,这里就不贴出来了。
/*** 节点跳转/流转** @param isInOrder 是否是顺序执行,true:下一节点;false:任意节点(包括驳回和最高权限的审核通过)* @param taskId 任务id,对应act_ru_task中的ID_* @param targetFlowElementId 目标的节点id* @param comment 审批意见* @param variables 传入下一节点时所需参数*/public void jumpToNode(boolean isInOrder, String taskId, String targetFlowElementId, String comment, Map<String, Object> variables, ErrorInfo error) {//获取taskServiceTaskService taskService = processEngine.getTaskService();Task task = taskService.createTaskQuery().taskId(taskId).singleResult();if (StringUtils.isBlank(task.getAssignee())) {error.code = -1;error.msg = "任务未签收,不允许跳转/流转";return;}//当前任务节点顺序流转if (isInOrder) {//添加审批意见taskService.addComment(taskId, null, comment);try {taskService.complete(taskId, variables);} catch (Exception e) {error.code = -1;error.msg = "节点流转失败taskId[" + taskId + "]";e.printStackTrace();log.error("节点流转失败taskId[%s]", taskId);}error.code = 1;error.msg = "节点流转成功";}//任意节点if (!isInOrder) {if (StringUtils.isBlank(targetFlowElementId)) {error.code = -1;error.msg = "节点跳转targetFlowElementId不能为空";log.error(error.msg);return;}ActivitiJump activitiJump = new ActivitiJump();try {activitiJump.jump(taskId, targetFlowElementId, comment, variables);} catch (Exception e) {e.printStackTrace();log.error("节点跳转失败taskId[%s]", taskId);error.code = -1;error.msg = "节点跳转失败taskId[" + taskId + "]";}error.code = 1;error.msg = "节点跳转成功";}}
任务的签收和退回
/*** 签收/退回任务** @param jumpToNodeDto* @param user* @param error*/public void claimActivity(JumpToNodeDto jumpToNodeDto, User user, ErrorInfo error) {TaskService taskService = processEngine.getTaskService();String isClaim = jumpToNodeDto.getIsClaim();//签收if (isClaim.equals(Constants.NODE_CLAIM)) {taskService.setAssignee(jumpToNodeDto.getTaskId(), user.getName());//添加备注taskService.addComment(jumpToNodeDto.getTaskId(), null, jumpToNodeDto.getComment());error.code = 1;error.msg = "签收成功";}//退回if (isClaim.equals(Constants.NODE_BACK)) {taskService.setAssignee(jumpToNodeDto.getTaskId(), null);taskService.addComment(jumpToNodeDto.getTaskId(), null, jumpToNodeDto.getComment());error.code = 1;error.msg = "退回成功";}}
10、查询历史备注
在节点流转/跳转、签收、退后等操作过程中,肯定会填写一些批注、原因,那么怎么查询这些批注呢?
要用HistoryService
/*** 查询历史备注** @param candidateUser* @return*/public List<Comment> getHistoryTaskComments(String candidateUser) {List<Comment> taskComments = new ArrayList<>();HistoryService historyService = processEngine.getHistoryService();TaskService taskService = processEngine.getTaskService();List<HistoricTaskInstance> list = new ArrayList<>();//处理人不为空时查询处理人所属的已处理任务if (StringUtils.isNotBlank(candidateUser)) {list = historyService.createHistoricTaskInstanceQuery().taskCandidateUser(candidateUser).orderByHistoricTaskInstanceStartTime().desc().list();} else {//处理人为空时查询所有已处理任务list = historyService.createHistoricTaskInstanceQuery().orderByHistoricTaskInstanceStartTime().desc().list();}if (null != list && list.size() > 0) {for (HistoricTaskInstance instance : list) {String hisTaskId = instance.getId();List<Comment> comments = taskService.getTaskComments(hisTaskId);if (null != comments && comments.size() > 0) {taskComments.addAll(comments);}}}return taskComments;}
至此,基本讲述完了,贴上我的gitee代码:
https://gitee.com/dayydream/activiti.git
我只是一个小菜鸟,欢迎各位大神来沟通
activiti的使用相关推荐
- Activiti——流程执行历史记录(七)
转自:http://blog.csdn.net/zjx86320/article/details/50363544 之前的几篇文章,为大家简单的介绍了部署流程定义.启动流程实例.查看和办理个人任务以及 ...
- Activiti——流程变量(六)
Activiti--流程变量 转自:http://lib.csdn.net/article/java/66665?knId=268 流程变量在整个工作流中扮演很重要的作用.例如:请假流程中有请假天数. ...
- Activiti——工作流之流程实例、任务的执行(五)
转自:http://profound-accumulation.iteye.com/blog/2244881 一.流程图 二.部署流程定义 /**部署请假流程(从zip)*/ @Test publ ...
- Activiti——数据表结构
备注: 本文转自:http://blog.csdn.net/hj7jay/article/details/51302829 转载目的在于个人学习使用,如有涉及著作权相关问题,请联系本人,本人将第一时间 ...
- Activiti——管理流程定义(四)
Activiti--管理流程定义 1.设计流程定义文档 1.1.流程图 1.2.bpmn文件 <?xml version="1.0" encoding="UTF-8 ...
- Activiti——工作流程-核心API(二)
.1 ProcessEngine 说明: 1) 在Activiti中最核心的类,其他的类都是由他而来. 2) 产生方式: 在前面看到了两种创建ProcessEngine(流程引擎)的方式(http:/ ...
- Activiti——准备开发环境(一)
准备材料下 activiti-5.17.0 1.添加Activiti5 的jar包或Maven依赖 Jar包方式 通过上述下载链接下载Activiti后,解压得到如下目录: 把libs的jar包添加到 ...
- 【应用篇】Activiti外置表单实例demo(四)
在这里我想说的外置表单.是说我们将我们自己的jsp(.form,.html)等页面上传到工作流的数据库中,当任务运行到当前结点时.给我们像前台发送绑定好的表单. 此处是给表单绑定表单的过程 不允许为: ...
- Activiti 规则任务(businessRuleTask)
Activiti 规则任务(businessRuleTask) 作者:邓家海 目前国内研究Activiti规则任务businessRuleTask)的文章在网上应该不超出3篇 小觑夜漫酒作伴,破晓黎明 ...
- activiti任务TASK
一.概要 设计TASK的表主要是:ACT_RU_TASK,ACT_HI_TASKINST(见参考-activiti表): 任务主要有:人工任务(usertask),服务任务(servicetask)等 ...
最新文章
- RDKit | 定量评估类药性(QED)
- 浏览器支持java_为什么我下载了java并提示浏览器不支持j
- session outline for different culture
- 使用Buildroot为Nxp i.mx6ul制作文件系统
- 爬虫系列---Scrapy框架学习
- 自己动手写UI库——引入ExtJs(布局)
- MSP432P401R TI Drivers 库函数学习笔记(四)GPIO
- TensorFlow 学习指南 三、学习
- next.js 安装简易教程
- ai电磁组属于什么组_飞思卡尔智能车电磁组分区算法介绍
- Android ActionBar示例教程
- Android学习(五)—— Android初级控件
- 力扣-1232 缀点成线
- 南开大学2019年数学分析考研试题
- 实验3 黑盒测试:决策表法及测试用例设计
- 软连接和硬连接的区别
- 测试进阶篇之测试用例设计-百度云盘
- golang开发的准备 - gvm(go版本管理软件)的安装
- 基于SQLSERVER--数据库表的修复
- 10分钟了解BIM+GIS融合,常见BIM数据格式及特性