Activit工作流学习例子
看了网上一些文章,动手操作了一遍,终于学会了Activit的一些常规使用。
一、Eclipse中的Activiti插件安装
Activiti有一个Eclipse插件,Activiti Eclipse Designer,可用于图形化建模、测试、部署 BPMN 2.0的流程。这样就不
用我们自己去编写繁琐的流程文件了。具体安装方法见手册。
打开 Help-> Install New Software.在如下面板中 , 点击 Add 按钮, 然后填入下列字段:
Name: Activiti BPMN 2.0 designer
Location: http://activiti.org/designer/update/
然后一步步的安装就可以了。
点击Window--->Preferences--->Activiti--->Save Actions:将Create process definition image when saving the diagram勾选,然后保存bpmn文件的时候会自动截图。
二、实现一个请假的工作流
1、随便模拟一个请假流程:
(1)发起请假申请;
(2)经理审批:
a.如果指定时间内没审批,则自动通过;
b.审批同意;
c.审批不同意;
(3)总经理审批:
a.如果请假大于或等于3天,则总经理审批;
b.如果请假小于3天,则自动通过;
(4)流程结束。
2、新建一个Activiti Diagram文件,按照第1步的思路,拖拉控件,画流程图,并设置相关节点或连线的值
3、配置文件activiti.cfg.xml的参数,主要配置数据库的相关参数等
<?xml version="1.0" encoding="UTF-8"?>
<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://localhost:3306/test" />
<property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="true" />
<property name="activityFontName" value="宋体"/>
<property name="labelFontName" value="宋体"/>
</bean> </beans>
4、代码实例,实现启动流程、审批、画历史流程图等等,详细见代码中注释
package com.sc.springmvc.controller; import java.io.File;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.zip.ZipInputStream; import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.io.FileUtils; public class ActivitiDemo { public static void main(String[] args) {
ActivitiDemo demo = new ActivitiDemo();
//demo.deploy();
//demo.queryProcdef();
//demo.startFlow();
//demo.queryTask_manager();
//demo.startTask_manager();
//demo.queryTask_boss();
//demo.startTask_boss();
//demo.queryStatus(); //demo.historyTaskList();
//demo.processState(); //获取流程变量
//System.out.println("生成流程图:");
demo.generateImage("12501");
} String currUser = "0001";
String applyUser = "0002";
String manager = "0003";
String boss = "0005"; ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /**
* 发布流程
* 发布流程后,流程文件会保存到数据库中 ,插入表act_re_deployment、act_re_procdef
*/
void deploy(){
RepositoryService repositoryService = processEngine.getRepositoryService(); //手动将myleave.bpmn和myleave.png打包成myleave.zip文件(一定要是zip别压缩成rar) //获取在classpath下的流程文件
InputStream in = this.getClass().getClassLoader().getResourceAsStream("myleave.zip");
ZipInputStream zipInputStream = new ZipInputStream(in);
//使用deploy方法发布流程:如果是首次表不存在则生成23张表,往这4张表插入流程相关信息:act_ge_bytearray、 act_ge_property、act_re_deployment、act_re_procdef
repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.name("myleave")
.deploy();
} //获取详细的流程定义信息
void queryProcdef(){
RepositoryService repositoryService = processEngine.getRepositoryService();
//创建查询对象
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
//添加查询条件
query.processDefinitionKey("leaveApply");//通过key获取
// .processDefinitionName("请假申请")//通过name获取
// .orderByProcessDefinitionId()//根据ID排序
//执行查询获取流程定义明细
List<ProcessDefinition> pds = query.list();
for (ProcessDefinition pd : pds) {
System.out.println("ID:"+pd.getId()+",NAME:"+pd.getName()+",KEY:"+pd.getKey()+",VERSION:"+pd.getVersion()+",RESOURCE_NAME:"+pd.getResourceName()+",DGRM_RESOURCE_NAME:"+pd.getDiagramResourceName());
}
} //发布流程
public void startFlow(){
RuntimeService runtimeService = processEngine.getRuntimeService(); //IdentityService identityService = processEngine.getIdentityService();
// 用来设置启动流程的人员ID,引擎会自动把用户ID保存到activiti:initiator中
//identityService.setAuthenticatedUserId(currUser);
/**
* 启动请假单流程 并获取流程实例
* 因为该请假单流程可以会启动多个所以每启动一个请假单流程都会在数据库中插入一条新版本的流程数据
* 通过key启动的流程就是当前key下最新版本的流程
* 当流程发布后在 act_ru_task ,act_ru_execution, act_ru_identitylink 表中插入流程数据
*
*/ Map<String, Object> variables = new HashMap<String, Object>();
variables.put("applyUser", applyUser);
variables.put("manager", manager);
variables.put("days", "5");
Date date = addTime(new Date(),0,0,1);
variables.put("dateTime", date);//到期时间 variables.put("boss", boss); String businessKey = "10001";//保存于表act_hi_procinst中 //参数businessKey, variables为可选参数
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveApply", businessKey, variables);
System.out.println("id:"+processInstance.getId()+",activitiId:"+processInstance.getActivityId());
} //增加时间
public static Date addTime(Date date, int day, int hour, int minute){
Calendar ca=Calendar.getInstance();
ca.setTime(date);
ca.add(Calendar.DATE, day);
ca.add(Calendar.HOUR_OF_DAY, hour);
ca.add(Calendar.MINUTE, minute);
return ca.getTime();
}
/**
* 传入Data类型日期,返回字符串类型时间(ISO8601标准时间)
* @param date
* @return
*/
public static String getISO8601Timestamp(Date date){
TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
df.setTimeZone(tz);
String nowAsISO = df.format(date);
return nowAsISO;
} /**
* 查看任务
*/
void queryTask_manager(){
//获取任务服务对象
TaskService taskService = processEngine.getTaskService();
//根据接受人获取该用户的任务 ,表act_ru_task的字段assignee_为待办人
List<org.activiti.engine.task.Task> tasks = taskService.createTaskQuery().processDefinitionKey("leaveApply").taskAssignee(manager).list();
for (org.activiti.engine.task.Task task : tasks) {
//ID : 表act_hi_actinst的字段act_id_
System.out.println("ID:"+task.getId()+",姓名:"+task.getName()+",接收人:"+task.getAssignee()+",开始时间:"+task.getCreateTime());
//获取流程变量
System.out.println("Variables:");
RuntimeService runtimeService=processEngine.getRuntimeService();
String excutionId = task.getExecutionId();
String applyUser = String.valueOf(runtimeService.getVariable(excutionId, "applyUser"));
String days = String.valueOf(runtimeService.getVariable(excutionId, "days"));
String dateTime = String.valueOf(runtimeService.getVariable(excutionId, "dateTime")); System.out.println("applyUser:" + applyUser + ",days:" + days + ",datetime:" + dateTime);
} } /**
* 启动流程:经理审批
*/
void startTask_manager(){
TaskService taskService = processEngine.getTaskService();
List<org.activiti.engine.task.Task> tasks = taskService.createTaskQuery().taskAssignee(manager).list();
for (org.activiti.engine.task.Task task : tasks) {
//taskId 就是查询任务中的 ID
String taskId = task.getId(); Map<String, Object> variables = new HashMap<String, Object>();
variables.put("managerPass", "1");
variables.put("boss", boss); taskService.complete(taskId, variables);
} } /**
* 老板查看任务
*/
void queryTask_boss(){
//获取任务服务对象
TaskService taskService = processEngine.getTaskService();
//根据接受人获取该用户的任务
List<org.activiti.engine.task.Task> tasks = taskService.createTaskQuery().taskAssignee(boss).list();
for (org.activiti.engine.task.Task task : tasks) {
System.out.println("ID:"+task.getId()+",姓名:"+task.getName()+",接收人:"+task.getAssignee()+",开始时间:"+task.getCreateTime()); //获取流程变量
System.out.println("Variables:");
RuntimeService runtimeService=processEngine.getRuntimeService();
String excutionId = task.getExecutionId();
String applyUser = String.valueOf(runtimeService.getVariable(excutionId, "applyUser"));
String manager = String.valueOf(runtimeService.getVariable(excutionId, "manager"));
String managerPass = String.valueOf(runtimeService.getVariable(excutionId, "managerPass"));
String days = String.valueOf(runtimeService.getVariable(excutionId, "days"));
System.out.println("applyUser:" + applyUser + ",manager:" + manager + ",days:" + days + ",managerPass:" + managerPass); }
} /**
* 老板启动流程
*/
void startTask_boss(){
TaskService taskService = processEngine.getTaskService();
//根据接受人获取该用户的任务
List<org.activiti.engine.task.Task> tasks = taskService.createTaskQuery().taskAssignee(boss).list();
for (org.activiti.engine.task.Task task : tasks) {
//taskId 就是查询任务中的 ID
String taskId = task.getId();
//完成请假申请任务
taskService.complete(taskId );
} } void queryStatus(){
HistoryService historyService = processEngine.getHistoryService();
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId("20001").singleResult();
System.out.println("Process instance end time: " + getDate(historicProcessInstance.getEndTime()));
} /**
* 历史任务查询:历史活动包括所有节点(流程图圆圈)和任务(流程图矩形),而历史任务只包含任务
*/
public void historyTaskList(){
List<HistoricTaskInstance> list=processEngine.getHistoryService() // 历史相关Service
.createHistoricTaskInstanceQuery() // 创建历史任务实例查询
.processInstanceId("37501") // 用流程实例id查询
.finished() // 查询已经完成的任务
.list();
for(HistoricTaskInstance hti:list){
System.out.println("任务ID:"+hti.getId());
System.out.println("流程实例ID:"+hti.getProcessInstanceId());
System.out.println("任务名称:"+hti.getName());
System.out.println("办理人:"+hti.getAssignee());
System.out.println("开始时间:"+hti.getStartTime());
System.out.println("结束时间:"+hti.getEndTime());
System.out.println("=================================");
}
}
/**
* 查询流程状态(正在执行 or 已经执行结束)
*/
void processState(){
ProcessInstance pi=processEngine.getRuntimeService() // 获取运行时Service
.createProcessInstanceQuery() // 创建流程实例查询
.processInstanceId("37501") // 用流程实例id查询
.singleResult();
if(pi!=null){
System.out.println("流程正在执行!");
}else{
System.out.println("流程已经执行结束!");
}
} String getDate(Date d){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s = sdf.format(d);
return s;
} /**https://blog.csdn.net/u011277123/article/details/77380787
* 流程图对历史节点进行高亮显示
* @param processInstanceId
* @return
*/
void generateImage(String processInstanceId)
{
HistoryService historyService = processEngine.getHistoryService();
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessEngineConfiguration processEngineConfiguration = processEngine.getProcessEngineConfiguration();
//获取历史流程实例
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
//获取流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
processEngineConfiguration = processEngine.getProcessEngineConfiguration();
Context.setProcessEngineConfiguration((ProcessEngineConfigurationImpl) processEngineConfiguration); ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId()); List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();
//高亮环节id集合
List<String> highLightedActivitis = new ArrayList<String>();
//高亮线路id集合
List<String> highLightedFlows = getHighLightedFlows(definitionEntity,highLightedActivitList); // 已执行完的任务节点
List<HistoricActivityInstance> finishedInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished().list();
for (HistoricActivityInstance hai : finishedInstances) {
highLightedActivitis.add(hai.getActivityId());
} //中文显示的是口口口,设置字体就好了
InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis,highLightedFlows,"宋体","宋体",null,1.0);
//单独返回流程图,不高亮显示
// InputStream imageStream = diagramGenerator.generatePngDiagram(bpmnModel);
// 输出资源内容到相应对象
try {
//生成本地图片
File file = new File("D:/test3.png");
FileUtils.copyInputStreamToFile(imageStream, file);
} catch (Exception e) {
throw new RuntimeException("生成流程图异常!", e);
} finally {
}
} /**
* 获取需要高亮的线
* @param processDefinitionEntity
* @param historicActivityInstances
* @return
*/
private List<String> getHighLightedFlows(ProcessDefinitionEntity processDefinitionEntity, List<HistoricActivityInstance> historicActivityInstances) {
List<String> highFlows = new ArrayList<String>();// 用以保存高亮的线flowId
for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
// 对历史流程节点进行遍历
ActivityImpl activityImpl = processDefinitionEntity.findActivity(historicActivityInstances.get(i).getActivityId());// 得到节点定义的详细信息
List<ActivityImpl> sameStartTimeNodes = new ArrayList<ActivityImpl>();// 用以保存后需开始时间相同的节点
ActivityImpl sameActivityImpl1 = processDefinitionEntity.findActivity(historicActivityInstances.get(i + 1).getActivityId());
// 将后面第一个节点放在时间相同节点的集合里
sameStartTimeNodes.add(sameActivityImpl1);
for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);// 后续第一个节点
HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);// 后续第二个节点
if (activityImpl1.getStartTime().equals(activityImpl2.getStartTime())) {
// 如果第一个节点和第二个节点开始时间相同保存
ActivityImpl sameActivityImpl2 = processDefinitionEntity.findActivity(activityImpl2.getActivityId());
sameStartTimeNodes.add(sameActivityImpl2);
} else {
// 有不相同跳出循环
break;
}
}
List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions();// 取出节点的所有出去的线
for (PvmTransition pvmTransition : pvmTransitions) {
// 对所有的线进行遍历
ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition.getDestination();
// 如果取出的线的目标节点存在时间相同的节点里,保存该线的id,进行高亮显示
if (sameStartTimeNodes.contains(pvmActivityImpl)) {
highFlows.add(pvmTransition.getId());
}
}
}
return highFlows;
} }
附,看过的一些不错Activiti文章:
http://www.mossle.com/docs/activiti/index.html
https://blog.csdn.net/a67474506/article/details/38266129
http://www.cnblogs.com/shyroke/category/1126426.html
----------------------------------------------
2019.3.20补充
实际业务中,activiti工作流的开发过程:
1、画流程图
2、数据库业务表增加一个流程实例字段proc_inst_id_
3、发起流程申请,后台保存逻辑
(1)设置流程下一步审批节点的参数Map<String, Object> variables
(2)插入或更新业务相关表,返回业务表的主键businessKey
(3)发起流程
identityService.setAuthenticatedUserId(variables.get("applyUser").toString());
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("流程名称", businessKey, variables);
String processInstanceId = processInstance.getId();
(4)根据上一步返回的流程实例Id,更新业务表相应字段
4、sql获取待办任务
select
to_char(t.id_) taskId,
w.proc_inst_id_,
t.task_def_key_ taskDefinitionKey,
......
from act_ru_task t
inner join 业务表 w on w.proc_inst_id_ = t.proc_inst_id_
where t.assignee_ = 待办人
5、待办审批前端页面,根据当前是哪个流程节点写不同逻辑
<c:if test="${taskDefinitionKey eq 'xxTask'}">
经理审批......
</c:if>
<c:if test="${taskDefinitionKey eq 'yyTask'}">
老板审批......
</c:if>
6、待办审批的后台保存逻辑
(1)判断权限:任务是否属于当前人;
(2)根据流程节点值taskDefinitionKey,设置流程下一步审批节点的参数Map<String, Object> variables
(3)更新相关业务表
(4)调用工作流方法
String taskId = (String) variables.get("taskId");
identityService.setAuthenticatedUserId(当前用户主键);
taskService.addComment(taskId, 流程实例Id, 审批意见);
this.taskService.complete(taskId, variables);
//TODO判断任务是否结束,是的话则根据实际情况更新业务表
Activit工作流学习例子的更多相关文章
- Activiti工作流学习之概述(一)
一.工作流介绍 我第一次听到这个词,是蒙逼的,再看百度百度,更傻眼了,完全说的不像人话啊,举几个生活中的例子,就明白多了比如:请假.报销等等,如果文字太过抽象,请看图: 二.工作流引擎 Process ...
- Activiti工作流学习之流程图应用详解
Activiti工作流学习之流程图应用详解 1.目的 了解Activiti工作流是怎样应用流程图的. 2.环境准备2.1.相关软件及版本 jdk版本:Jdk1.7及以上 IDE:eclipse ...
- 【知识总结】Activiti工作流学习入门
1. 我理解的工作流: 在工作中慢慢接触的业务流程,就向流程控制语言一样,一步一步都对应的不同的业务,但整体串联起来就是一个完整的业务.而且实际工作中尤其是在企业内部系统的研发中,确实需要对应许多审批 ...
- 工作流学习——Activiti整体认识二步曲 (zhuan)
http://blog.csdn.net/zwk626542417/article/details/46594505 ***************************************** ...
- Activiti工作流学习-----基于5.19.0版本(4)
四.使用工作流开发 org.activiti.engine.ProcessEngine提供的Service作用在工作流引擎上面,如果所示是模仿一个公司简单的审批流程,你可以下载这个Demo:Activ ...
- activity 工作流学习(一)
一.了解工作流 1.工作流(Workflow),就是“业务过程的部分或整体在计算机应用环境下的自动化”,它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档.信息或任务的过程自动进行,从而实 ...
- Activiti工作流学习笔记(四)——工作流引擎中责任链模式的建立与应用原理
原创/朱季谦 本文需要一定责任链模式的基础,主要分成三部分讲解: 一.简单理解责任链模式概念 二.Activiti工作流里责任链模式的建立 三.Activiti工作流里责任链模式的应用 一.简单理解责 ...
- Activiti工作流学习(三)Activiti工作流与spring集成
一.前言 前面Activiti工作流的学习,说明了Activiti的基本应用,在我们开发中可以根据实际的业务参考Activiti的API去更好的理解以及巩固.我们实际的开发中我们基本上都使用sprin ...
- javascript闭包学习例子
javascript中的闭包个很让人头疼的概念.总结一下 闭包是指有权访问一个函数作用域中的变量的函数.创建闭包最常见的方式,是在一个函数内部创建另一个函数,用return返回出去. 使用闭包可能造成 ...
随机推荐
- 使用js调用js
直接上源码: <div class="choose"> choose a mode<br> <hr> <button type=" ...
- VS2010 发布网站时文件丢失解决办法
网站项目中包含了一写rdlc的东西,发布网站选择仅限运行此应用程序所需文件,发布成功后发现这两个文件夹都没发布出来,找了下原因,解决办法是选择文件打开属性窗口找到生成操作,选项选择“内容”,重新发布, ...
- freedom is a kind of responsibility
张维迎教授在2017年7月1日北大国发院2017届毕业典礼上的发言<自由是一种责任> 张维迎:自由是一种责任 本文为张维迎教授在2017年7月1日北大国发院2017届毕业典礼上的发言 ...
- HTTP接口-跨域-callback
1.客户端和正常调用非跨域接口一样2.服务端返回的时候用callback+(返回值)的方式返回结果. callback为客户端的隐藏参数.public String converJsonResultS ...
- QPointer,QSharedPointer,QWeakPointer的区别
QPointer,QSharedPointer,QWeakPointer的区别与使用例子(QSharedPointer类似Delphi里的引用计数,是强引用,而QWeakPointer是弱引用,不影响 ...
- CRM 权限设置 ss
表结构的设计 权限表 url -url地址的正则表达式 ^$ title - 标题 角色表 name - 角色名称 permissions 多对多关联权限表 (权限和角色的关系表) 用户表 name ...
- Autel Maxisys MS908CV Description
The new Autel MaxiSys CV Heavy Duty Diagnostic is built on the powerful MaxiSys 908 platform and pro ...
- Python+OpenCV图像处理(七)—— 滤波与模糊操作
过滤是信号和图像处理中基本的任务.其目的是根据应用环境的不同,选择性的提取图像中某些认为是重要的信息.过滤可以移除图像中的噪音.提取感兴趣的可视特征.允许图像重采样等等.频域分析将图像分成从低频到高频 ...
- web前端学习路线(含20个真实web开发项目集合)
目前web前端工程师日均岗位缺口已经超过50000,随着互联网+的深入发展,html5作为前端展示技术,市场人才需求量将呈直线上涨. Web前端工程师的岗位职责是利用HTML.CSS.Java.DOM ...
- Jquery 插件 图片验证码
摘自:https://www.cnblogs.com/lusufei/p/7746465.html !(function(window, document) { var size = 5;//设置验证 ...