看了网上一些文章,动手操作了一遍,终于学会了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工作流学习例子的更多相关文章

  1. Activiti工作流学习之概述(一)

    一.工作流介绍 我第一次听到这个词,是蒙逼的,再看百度百度,更傻眼了,完全说的不像人话啊,举几个生活中的例子,就明白多了比如:请假.报销等等,如果文字太过抽象,请看图: 二.工作流引擎 Process ...

  2. Activiti工作流学习之流程图应用详解

    Activiti工作流学习之流程图应用详解 1.目的  了解Activiti工作流是怎样应用流程图的. 2.环境准备2.1.相关软件及版本    jdk版本:Jdk1.7及以上 IDE:eclipse ...

  3. 【知识总结】Activiti工作流学习入门

    1. 我理解的工作流: 在工作中慢慢接触的业务流程,就向流程控制语言一样,一步一步都对应的不同的业务,但整体串联起来就是一个完整的业务.而且实际工作中尤其是在企业内部系统的研发中,确实需要对应许多审批 ...

  4. 工作流学习——Activiti整体认识二步曲 (zhuan)

    http://blog.csdn.net/zwk626542417/article/details/46594505 ***************************************** ...

  5. Activiti工作流学习-----基于5.19.0版本(4)

    四.使用工作流开发 org.activiti.engine.ProcessEngine提供的Service作用在工作流引擎上面,如果所示是模仿一个公司简单的审批流程,你可以下载这个Demo:Activ ...

  6. activity 工作流学习(一)

    一.了解工作流 1.工作流(Workflow),就是“业务过程的部分或整体在计算机应用环境下的自动化”,它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档.信息或任务的过程自动进行,从而实 ...

  7. Activiti工作流学习(三)Activiti工作流与spring集成

    一.前言 前面Activiti工作流的学习,说明了Activiti的基本应用,在我们开发中可以根据实际的业务参考Activiti的API去更好的理解以及巩固.我们实际的开发中我们基本上都使用sprin ...

  8. javascript闭包学习例子

    javascript中的闭包个很让人头疼的概念.总结一下 闭包是指有权访问一个函数作用域中的变量的函数.创建闭包最常见的方式,是在一个函数内部创建另一个函数,用return返回出去. 使用闭包可能造成 ...

  9. Activiti工作流学习(二)流程实例、执行对象、任务

    一.前言 前面说明了基本的流程部署.定义,启动流程实例等基本操作,下面我们继续来学习流程实例.执行对象.任务. 二.流程实例.执行对象说明 整个Activiti的生命周期经过了如下的几个步骤: 1.流 ...

随机推荐

  1. 万向节锁(Gimbal Lock)的理解

    [TOC] 结论 我直接抛出结论: Gimbal Lock 产生的原因不是欧拉角也不是旋转顺序,而是我們的思维方式和程序的执行逻辑没有对应,也就是说是我们的观念导致这个情况的发生. 他人解释 首先我们 ...

  2. jpa遇到的 org.hibernate.PersistentObjectException: detached entity passed to persist异常

    jpa遇到的 org.hibernate.PersistentObjectException: detached entity passed to persist异常 发生这个原因是因为我们已经在实体 ...

  3. xenserver磁盘扩容扩不大问题解决

    http://www.osyunwei.com/archives/7956.html xenserver将磁盘扩大后,fdisk可以看到容量大了 但是df -h确实没大?   解决: fdisk /d ...

  4. RTP RTCP在音视频传输与同步方面的使用

    转自:http://blog.csdn.net/kof98765/article/details/17733701 1 音视频实时传输 1.1 Jrtplib库介绍 本系统采用开源库Jrtplib进行 ...

  5. 关于CGContextSetBlendMode: invalid context 0x0的错误

    在ios 7的模拟器中,选择一个输入框准备输入时,会触发这个错误,以下是出错详细日志: <Error>: CGContextSetBlendMode: invalid context 0x ...

  6. wpf mvvm使用问题集锦

    问题一.usercontrol1控件使用了mvvm数据绑定,usercontrol2也使用了mvvm数据绑定,则 以下是伪代码 <usercontrol2 datacontent="{ ...

  7. C++:构造函数默认的参数声明

    C++函数的默认参数指的是在函数声明或者定义时给形式参数指定默认值,从而在调用参数时可以少写参数,少掉的参数用默认值代替.LZ的Display()函数的代码看起来似乎是可以有s2和s3两个默认参数,那 ...

  8. .net ref关键字在引用类型上的使用

    只接上干货. namespace ConsoleApplication1 { class Person { public string UserName { get; set; } } class P ...

  9. RMQ(模板 ST 区间最值,频繁的间隔时间)

    PS: 介绍:http://blog.csdn.net/liang5630/article/details/7917702 RMQ算法.是一个高速求区间最值的离线算法,预处理时间复杂度O(n*log( ...

  10. 项目实战1—LNMP的搭建、nginx反向代理和缓存等的实现

    实战一:搭建lnmp及类小米等商业网站的实现 环境:关闭防火墙,selinux 1.安装包,开启服务 yum -y install nginx mariadb-server php-fpm php-m ...