工作流日常的应用软件:钉钉
什么是工作流?
- 多任务人协同完成一个复杂的业务流
- 完成工作流的框架,称为工作流管理系统。
- Activiti是一个业务框架。
Activiti的特点
- 把复杂的业务流进行图形化
- 图形化的业务部署数据库系统中(数据库化)
- 提供了流程数据库(23张表)
- 提供了业务层的API(service)
- 数据访问层使用mybatis实现
Activiti环境的搭建
- 安装BPMN插件,绘图插件
下载插件安装包:
解压安装包:
把jars目录中的jar包复制到Eclipse的安装目录的plugins文件夹中。
使用归档文件方式安装插件:
-
应用要导入activiti的类库jar
-
安装流程数据库
public class TestCreateDB {
@Test
public void testJdbc(){
//第一种方式(java代码方式)
ProcessEngineConfiguration config = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
config.setJdbcDriver("com.mysql.jdbc.Driver");
config.setJdbcUrl("jdbc:mysql://localhost:3306/activitileavedb2037?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8");
config.setJdbcUsername("agentsystem");
config.setJdbcPassword("888");
config.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
ProcessEngine engine = config.buildProcessEngine(); //创建23张表
System.out.println(engine);
}
@Test
public void testSpring(){
//第二种方式(xml配置文件方式)
ProcessEngineConfiguration config = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
ProcessEngine engine = config.buildProcessEngine(); //创建23张表
System.out.println(engine);
}
}
23张表如下:
Activiti常用的流程数据库表
- act_re_deployment 流程部署信息表
- act_re_procdef 流程定义信息表
- act_ge_bytearray 流程定义资源表 保存了二进制文件
- act_ru_task 活动任务节点信息表
- act_ru_execution 活动的任务执行表
- act_hi_procinst 流程实例历史表
- act_hi_taskinst 任务信息历史表
- act_ru_variable 流程变量信息表
- act_hi_varinst 流程变量历史表
Activiti入门程序
1.流程图的要素
- 开始节点
- 任务节点 UserTask
- 结束节点
- 任务节点的待办人
分配方式:
(1)固定名称
(2)表达式
(3)程序分配
2.流程业务的主要操作
- getRepositoryService() 流程定义有关操作
- getRuntimeService() 流程实例有关操作
- getTaskService() 活动的流程任务的操作
- getHistoryService() 查询历史记录的操作
(1) 部署流程图
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//会读取activiti.cfg.xml
@Test //1.部署流程定义图
public void testDeployDB() {
Deployment deployment = processEngine.getRepositoryService()
.createDeployment()
.name("hello入门程序")
.addClasspathResource("diagram/HelloProcess.bpmn")
.addClasspathResource("diagram/HelloProcess.png")
.deploy();
System.out.println(deployment.getId());
System.out.println(deployment.getName());
}
(2) 启动流程实例
@Test //2. 启动流程实例 ProcessInstance
public void startProcess() {
String processKey = "HelloProcess";
ProcessInstance pi = processEngine.getRuntimeService().startProcessInstanceByKey(processKey);
System.out.println("流程实例id: " + pi.getId());
System.out.println("流程定义id: " + pi.getProcessDefinitionId());
}
- 区分流程定义和流程实例区别:
流程定义相当于一种工作的“模板”,类似OOP中的“类”的概念。流程实例表示的是某一个正在运行的工作流程,相当于对象的概念。
(3) 查看个人的待办事务
@Test //3. 我的待办事务
public void findMyTask() {
String assignee = "zhang";
List<Task> list = processEngine.getTaskService()
.createTaskQuery()
.taskAssignee(assignee )
.list();
for (Task task : list) {
System.out.println("任务id:" + task.getId());
System.out.println("任务名称 : " + task.getName());
System.out.println("待办人: " + task.getAssignee());
System.out.println("当前流程实例:" + task.getProcessInstanceId());
System.out.println("当前流程定义:" + task.getProcessDefinitionId());
}
}
(4)完成个人任务
@Test // 4. 完成个人任务
public void finishMyTask() {
String taskId = "302";
processEngine.getTaskService().complete(taskId );
System.out.println("完成任务");
}
(5)查看流程定义列表
@Test //5. 查看流程定义列表
public void findProcessDefinitions() {
List<ProcessDefinition> list = processEngine.getRepositoryService()
.createProcessDefinitionQuery()
.list();
for (ProcessDefinition pd : list) {
System.out.println(pd.getId());
System.out.println(pd.getName());
System.out.println(pd.getKey());
System.out.println(pd.getResourceName()); // bpmn
System.out.println(pd.getDiagramResourceName()); //png
System.out.println(pd.getDeploymentId());
}
}
(6)删除流程定义
@Test //6. 删除流程定义模板
public void removeProcessDef() {
String deploymentId = "501";
//第个参数:true 表示强行删除,如果当前流程定义还有运行流程实例时,也要强行删除
processEngine.getRepositoryService().deleteDeployment(deploymentId , true);
System.out.println("删除成功");
}
(7)查看流程定义图
@Test //7. 查看流程定义图
public void viewPic() throws IOException {
String deploymentId = "1";
String resourceName = "diagram/HelloProcess.png";
InputStream is = processEngine.getRepositoryService().getResourceAsStream(deploymentId, resourceName);
File file = new File("d:/"+resourceName);
FileUtils.copyInputStreamToFile(is, file);
System.out.println("复制成功");
}
(8)判断流程是否结束
//8. 判断流程实例是否结束
@Test
public void testProcessInsEnd() {
String processId="701";
ProcessInstance pi = processEngine.getRuntimeService()
.createProcessInstanceQuery()
.processInstanceId(processId)
.singleResult();
if (pi!=null) {
System.out.println("流程正在运行。。。。。");
} else {
System.out.println("流程已经结束!");
}
}
流程变量
1.什么是流程变量?
保存在流程上下文中流转的业务数据。
作用:
- 保存业务数据(建议另外保存业务表)
- 分支连线判断
- 启动流程实例分配待办人
2.流程变量的设置
@Test
public void testSet() {
//processEngine.getTaskService().setVariable(taskId, key, value);
//processEngine.getRuntimeService().setVariable(arg0, arg1, arg2);
//processEngine.getTaskService().setVariables(taskId, map);
Map<String, Object> map = new HashMap<>();
map.put("money", 5000);
map.put("remark", "出差报销");
String taskId = "704";
processEngine.getTaskService().setVariables(taskId, map);
System.out.println("金额已经设置");
}
@Test
public void testGet() {
//processEngine.getTaskService().getVariables(taskId);
//processEngine.getTaskService().getVariable(taskId, key);
String taskId = "1002";
int money = (int) processEngine.getTaskService().getVariable(taskId , "money");
String remark = (String) processEngine.getTaskService().getVariable(taskId, "remark");
System.out.println(money);
System.out.println(remark);
}
分支连线
- 流程图参考如下:
- 流程图关键参数设置,设置连线的流程变量表达式:
- 单元测试
个人任务完成时传递流程变:
@Test // 3. 完成个人任务
public void finishMyTask() {
String taskId = "1403";
Map<String, Object> map = new HashMap<>();
map.put("money", 120);
processEngine.getTaskService().complete(taskId, map);
System.out.println("完成任务");
}
网关 gateway(了解)
- 排他网关
流程图以及默认路径的设置,参考下图:
测试代码,任务完成提供连线的流程变量:
@Test // 3. 完成个人任务
public void finishMyTask() {
String taskId = "1904";
Map<String, Object> map = new HashMap<>();
map.put("money", 120);
processEngine.getTaskService().complete(taskId, map);
System.out.println("完成任务");
}
- 并行网关
流程图参考如下:
并行网关的特点: 有多个任务同时并行执行,这些任务必须全部完成才汇聚一起,走下面的流程。操作时要留意流程数据库表act_ru_execution和act_ru_task的变化。
任务人分配
- 表达式分配
流程图以及待办人分配表达式,如下图:
测试代码,启动流程时指定流程变量userId
@Test //2. 启动流程实例 ProcessInstance
public void startProcess() {
String processKey = "assignProcess01";
Map<String, Object> map = new HashMap<>();
map.put("userId", "zhangsan");
ProcessInstance pi = processEngine.getRuntimeService().startProcessInstanceByKey(processKey, map);
System.out.println("流程实例id: " + pi.getId());
System.out.println("流程定义id: " + pi.getProcessDefinitionId());
}
- 程序分配
流程图以及监听类的指定:
监听类AssigneeTaskListener,参考如下
public class AssigneeTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
delegateTask.setAssignee("boss");
}
}
在web上查看流程图片
1.查看流程定义图
- 请求handler方法:
@RequestMapping("/viewImage")
public String viewImage(String deploymentId,String imageName,HttpServletResponse response) throws IOException {
InputStream in = workFlowService.findImageInputStream(deploymentId, imageName);
OutputStream out = response.getOutputStream();
//4:将输入流中的数据读取出来,写到输出流中
for(int b=-1;(b=in.read())!=-1;){
out.write(b);
}
out.close();
in.close();
return null;
}
获取流程图片的业务方法:
@Override
public InputStream findImageInputStream(String deploymentId, String imageName) {
return this.repositoryService.getResourceAsStream(deploymentId, imageName);
}
2.查看当前流程图
- 请求的handler方法:
@RequestMapping("/viewCurrentImage")
public ModelAndView viewCurrentImage(String taskId) {
/**一:查看流程图*/
//1:获取任务ID,获取任务对象,使用任务对象获取流程定义ID,查询流程定义对象
ProcessDefinition pd = workFlowService.findProcessDefinitionByTaskId(taskId);
ModelAndView mv = new ModelAndView();
mv.addObject("deploymentId", pd.getDeploymentId());
mv.addObject("imageName", pd.getDiagramResourceName());
/**
* 二:查看当前活动,获取当期活动对应的坐标x,y,width,height,将4个值存放到Map<String,Object>中*/
Map<String, Object> map = workFlowService.findCoordingByTask(taskId);
mv.addObject("acs", map);
mv.setViewName("viewimage");
return mv;
}
- 根据流程任务id获取流程定义信息:
@Override
public ProcessDefinition findProcessDefinitionByTaskId(String taskId) {
//使用任务ID,查询任务对象
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
//获取流程定义ID
String processDefinitionId = task.getProcessDefinitionId();
//查询流程定义的对象
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()//创建流程定义查询对象,对应表act_re_procdef
.processDefinitionId(processDefinitionId)//使用流程定义ID查询
.singleResult();
return pd;
}
- 查看当前活动的任务的坐标位置:
@Override
public Map<String, Object> findCoordingByTask(String taskId) {
//存放坐标
Map<String, Object> map = new HashMap<String,Object>();
//使用任务ID,查询任务对象
Task task = taskService.createTaskQuery()//
.taskId(taskId)//使用任务ID查询
.singleResult();
//获取流程定义的ID
String processDefinitionId = task.getProcessDefinitionId();
//获取流程定义的实体对象(对应.bpmn文件中的数据)
ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processDefinitionId);
//流程实例ID
String processInstanceId = task.getProcessInstanceId();
//使用流程实例ID,查询正在执行的执行对象表,获取当前活动对应的流程实例对象
ProcessInstance pi = runtimeService.createProcessInstanceQuery()//创建流程实例查询
.processInstanceId(processInstanceId)//使用流程实例ID查询
.singleResult();
//获取当前活动的ID
String activityId = pi.getActivityId();
//获取当前活动对象
ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);//活动ID
//获取坐标
map.put("x", activityImpl.getX());
map.put("y", activityImpl.getY());
map.put("width", activityImpl.getWidth());
map.put("height", activityImpl.getHeight());
return map;
}
- jsp页面显示当前流程图片:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>查看当前流程图</title>
</head>
<body>
<!-- 1.获取到规则流程图 -->
<img style="position: absolute;top: 0px;left: 0px;" src="viewImage?deploymentId=${deploymentId}&imageName=${imageName}">
<!-- 2.根据当前活动的坐标,动态绘制DIV -->
<div style="position: absolute;border:2px solid red;top:${
acs.y}px;left: ${
acs.x}px;width:${
acs.width}px;height:${
acs.height}px;"></div></body>
</html>