Activiti工作流引擎

作用:项目中审批流程的定义,原理:程序员通过流程定义语言预先定义好工作流程,通过API把流程图部署到数据库,API启动流程(比如请假)、API驱动流程一步步向下执行,最终结束.

activiti数据库环境

  • act_id_*:引擎内部的人员与组信息表,一般不用,项目会有自己的
  • act_re_*:流程部署表,固定的数据,一般不会变
  • act_ru_*:流程运行时数据表,频繁变化,但是数据量很少。因为当流程结束后,清空
  • act_hi_*:流程历史信息表,会比较大,历史流程实例、历史任务、任务审批记录,审批附件表
  • act_ge_*:二进制表,也是流程部署相关的信息

安装插件

  • 建议在eclipse下使用
  • eclipse安装:安装artifacts
    1. assignee:执行任务的单个人员,此处一般使用表达式
    2. candidate users:候选人列表,逗号分隔或使用表达式
    3. 排它网关exclusiveGateway:记得写条件
  • IDEA安装:在Plugins中搜索或使用离线安装actibpm
    • IDEA容易出现bug

核心对象

  1. ProcessEngine入口对象,相当于mybatis的SqlSession会话对象
  2. RepositoryService管理流程定义,用来部署流程,删除流程部署,查询流程部署
  3. RuntimeService流程执行服务类,启动流程,驱动流程向下执行
  4. TaskService任务服务类,获取个人待办等
  5. HistoryService历史信息服务类,查询流程历史信息,跟踪流程实例运行情况
  6. ProcessDefinition流程定义对象,可以获得资源文件
  7. ProcessInstance流程实例对象,当流程被启动后,对应有一条流程实例数据,一个流程实例下有多个任务

使用

基础配置

  1. 添加jar包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-engine</artifactId>
    <version>5.22.0</version>
    </dependency>
    <dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring</artifactId>
    <version>5.22.0</version>
    </dependency>
  2. 配置activity.cfg.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <?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
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
    <!--配置数据库-->
    <property name="dataSource" ref="dataSource" />
    <!--配置事务-->
    <property name="transactionManager" ref="transactionManager" />
    <property name="databaseSchemaUpdate" value="true" />
    </bean>

    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
    <property name="processEngineConfiguration" ref="processEngineConfiguration" />
    </bean>
    <!--配置核心对象-->
    <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
    <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
    <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
    <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
    <bean id="identityService" factory-bean="processEngine" factory-method="getIdentityService" />
    </beans>
  3. 画出bpmn图和转存一份png图

IdentityService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Autowired
private IdentityService is;

/**
* 添加普通用户信息
*/
public void add_user(){
// user唯一 标识 id
String user1 = "zhangSan";
User user = is.newUser(user1);
'可设置,可不设置'
user.setFirstName("A");
user.setLastName("Z");
is.saveUser(user);
}

/**
* 添加经理信息
*/
public void add_mgr(){
User mgr1 = is.newUser("mgr1");
User mgr2 = is.newUser("mgr2");
is.saveUser(mgr1);
is.saveUser(mgr2);
}

/**
* 添加副总组
*/
public void add_fzGroup(){
User fz1 = is.newUser("fz1");
User fz2 = is.newUser("fz2");
is.saveUser(fz1);
is.saveUser(fz2);
'创建组'
Group fz = is.newGroup("fz");
is.saveGroup(fz);
}

/**
* 设置用户与组之间的关系
*/
public void add_memerShip(){
'把fz1和fz2添加到fz组中'
is.createMembership("fz1","fz");
is.createMembership("fz2","fz");
}

RepositoryService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Autowired
private ProcessEngine pe;
@Autowired
private RepositoryService rs;

// 获取引擎入口对象
public void test_pe(){
// 自动创建一系列表
System.out.println(pe);
}

// 添加部署流程
public void test_rs(){
DeploymentBuilder deployment = rs.createDeployment();
// 设置部署名称
deployment.name("请假申请");
// 添加部署文件
deployment.addClasspathResource("bpmn/leaveProcess.bpmn");
// 添加部署图
deployment.addClasspathResource("bpmn/leaveProcess.png");
// 部署
Deployment deploy = deployment.deploy();
}

// 查看部署流程
public void test_dq(){
DeploymentQuery dq = rs.createDeploymentQuery();
// 查看所有部署
List<Deployment> list = dq.list();
System.out.println(list);
}

// 删除部署流程
public void test_de(){
// 通过deploymentId删除部署
rs.deleteDeployment("1");
}

RuntimeService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
@Autowired
private RuntimeService rs;
@Autowired
private TaskService ts;

// 删除待办任务
@Test
public void del() {
String pid = "7501";
rs.deleteProcessInstance(pid,null);
}


// 前端提交了一个请假申请,保存申请单到leaveOrders业务表中,(id,请假人,开始时间,结束时间,请假天数,请假事由,processInstanceId)
// 1.保存业务数据
// 2.启动流程
// 3.把流程实例id更新到业务数据表中作为外键
// 4.走请假申请task

// 保存业务数据
@Test
public void startProcess(){
// 从前端中获取到等于用户的信息
String loginUser = "zhangSan";
// 流程定义文件的Id,在act_re_procdef表中的KEY
String processDefKey = "myProcess";
// 实例流程对象,传入流程变量
Map<String,Object> args = new HashMap<>();
args.put("loginUser",loginUser);
ProcessInstance pi = rs.startProcessInstanceByKey(processDefKey, args);
// 获取到流程实例Id,该id必须自己保存到自己的业务表中
System.out.println(pi.getId()); // 20001
}

/**
* 提交申请Task
*/
@Test
public void leaveApplyTask(){
String loginUser = "zhangSan";
// act_hi_taskinst中的ID
String pid = "20001";
int days = 3;
// 通过pid和loginUser查询到该任务
Task task = ts.createTaskQuery().processInstanceId(pid).taskAssignee(loginUser).singleResult();
Map<String,Object> args = new HashMap<>();
args.put("days",days);
ts.complete(task.getId(),args);
}

/**
* 获取代办事项
*/
@Test
public void getTask(){
String loginUser = "mgr1";
// 流程定义文件的Id,在act_re_procdef表中的KEY
String processDefKey = "myProcess";
// 通过processDefKey和loginUser查询到该用户的代办事项
List<Task> list = ts.createTaskQuery().processDefinitionKey(processDefKey).taskCandidateOrAssigned(loginUser).list();
for (Task task : list) {
// 查询到流程实例Id
String processInstanceId = task.getProcessInstanceId();
// 查询到待办任务Id
String id = task.getId();
// 查询到待办任务名字
String name = task.getName();
// 查询到待办任务创建时间
Date createTime = task.getCreateTime();
System.out.println(processInstanceId+"==="+id+"==="+name+"==="+createTime);
}
}

/**
* 签收待办任务
*/
@Test
public void claim(){
String loginUser = "mgr1";
// 待办任务Id
String taskId = "22503";
ts.claim(taskId,loginUser);
}

/**
* 经理审批
*/
@Test
public void mgrProve(){
// 待办任务Id
String taskId = "22503";
// InstanceId
String instanceId = "20001";
// 写审批意见
String content = "我是经理,审批通过!";
// 添加一条审批意见,评论
ts.addComment(taskId,instanceId,content);
// 传审批使用的参数
Map<String,Object> args = new HashMap<>();
args.put("approve",true);
// 提交审批,完成任务
ts.complete(taskId,args);
}

HistoryService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Autowired
private HistoryService hs;
@Autowired
private TaskService ts;

/**
* 获取历史信息,文字展示
*/
public void getHistoryTask(){
String processInstanceId = "20001";
HistoricTaskInstanceQuery hq = hs.createHistoricTaskInstanceQuery();
// 通过processInstanceId获取任务信息
hq.processInstanceId(processInstanceId);
// 通过完成时间排序
hq.orderByHistoricTaskInstanceEndTime();
// 降序
hq.desc();
List<HistoricTaskInstance> list = hq.list();
for (HistoricTaskInstance historicTaskInstance : list) {
// 获取任务Id
String taskId = historicTaskInstance.getId();
// 获取任务名称
String name = historicTaskInstance.getName();
// 获取任务执行人
String assignee = historicTaskInstance.getAssignee();
// 获取任务结束时间
Date endTime = historicTaskInstance.getEndTime();
System.out.println(taskId+"==="+name+"==="+assignee+"==="+endTime);
}
}

获取任务进度和转存图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
@Autowired
private RepositoryService rs;

@Autowired
private HistoryService hs;

@Autowired
private ProcessEngine pe;

// 查看某个流程部署的图片
@Test
public void getDeployPng() throws Exception {
// 在act_re_deploymentId中获取ID
String deploymentId = "2501";
List<String> names = rs.getDeploymentResourceNames(deploymentId);
String imageName = null;
for (String name : names) {
if (name.indexOf(".png") >= 0) {
imageName = name;
}
}

if (imageName != null) {
File f = new File("e:/" + imageName);
InputStream in = rs.getResourceAsStream(deploymentId, imageName);

FileUtils.copyInputStreamToFile(in, f);
}
}


// 获取流程跟踪图
@Test
public void getHiTask() {
// act_hi_procinst、act_ru_identitylink中的PROC_INST_ID, 前端传递到后端的流程实例id
String pid = "20001";
// 查询历史流程实例对象
HistoricProcessInstance hpi = hs.createHistoricProcessInstanceQuery().processInstanceId(pid).singleResult();

// 历史task查询
HistoricActivityInstanceQuery q = hs.createHistoricActivityInstanceQuery().processInstanceId(pid);

// 根据流程实例id,查询该流程实例下所有已执行的任务
List<HistoricActivityInstance> list = q.list();

// 已执行的任务的节点id
List<String> highLightedActivitis = new ArrayList<String>();
for (HistoricActivityInstance h : list) {
highLightedActivitis.add(h.getActivityId());
}

// 得到需要高亮的线的id集合
ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity) rs.getProcessDefinition(hpi.getProcessDefinitionId());
List<String> highLightFlows=getRedFlows(definitionEntity, list);

BpmnModel bpmnModel = rs.getBpmnModel(hpi.getProcessDefinitionId());
ProcessEngineConfiguration c = pe.getProcessEngineConfiguration();
// 流程示意图创建器
ProcessDiagramGenerator pdg = c.getProcessDiagramGenerator();
InputStream in = pdg.generateDiagram(bpmnModel, "bmp", highLightedActivitis, highLightFlows, "宋体",
"宋体", null, null, 1.0);

try {
OutputStream os = new FileOutputStream("e:/aa.bmp");
int r;
byte[] bs = new byte[2048];
while ((r = in.read(bs)) != -1) {
os.write(bs, 0, r);
}
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}

private List<String> getRedFlows(ProcessDefinitionEntity processDefinitionEntity,
List<HistoricActivityInstance> historicActivityInstances) {
// 用以保存高亮的线flowId
List<String> highFlows = new ArrayList<String>();
// 对历史流程节点进行遍历
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;
}

相关文章

数据库连接池

Junit和Spring

Tomcat

Servlet

Request,Response和ServletContext

Cookie和Session

JSP和EL和Jstl

Filter和Listener

Mybatis

SpringCache