mysql数据库
1.部署流程
举例:部署一个如下图的流程
在src/main/resources下创建processes文件夹,然后新建bpmn20.xml文件
添加如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:flowable="http://flowable.org/bpmn"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.flowable.org/processdef">
<!-- 流程定义信息
id:在数据库里会存在key_字段中
name:流程名字
-->
<process id="process_fzoauj5k" name="审核流程">
<!--开始事件:流程的起点
id:开始节点的ID
name:开始节点的名称
outgoing:记录连线的顺序流,outgoing表示从该节点输出的线
-->
<startEvent id="startNode1" name="开始">
<outgoing>Flow_0ksehsm</outgoing>
</startEvent>
<!--结束事件:流程的终点
id:结束节点的ID
name:结束节点的名称
incoming:记录连线的顺序流,incoming表示输入该节点的线
-->
<endEvent id="Event_08rzeth" name="结束">
<incoming>Flow_18upiqx</incoming>
</endEvent>
<!--顺序流:执行时会从一个节点流向另一个节点
id:顺序流的ID
sourceRef:源头节点ID
targetRef:目标节点ID
-->
<sequenceFlow id="Flow_0ksehsm" sourceRef="startNode1" targetRef="Activity_01fk1po" />
<sequenceFlow id="Flow_1huwgzm" sourceRef="Activity_01fk1po" targetRef="Activity_0ipmowu" />
<sequenceFlow id="Flow_18upiqx" sourceRef="Activity_0ipmowu" targetRef="Event_08rzeth" />
<!--用户任务:需要指定用户来执行
flowable:assignee:任务受理人,这里先写为固定,设受理人的用户ID为1
incoming:记录连线的顺序流,incoming表示输入该节点的线
outgoing:记录连线的顺序流,outgoing表示从该节点输出的线
-->
<userTask id="Activity_01fk1po" name="申请" flowable:assignee="1">
<incoming>Flow_0ksehsm</incoming>
<outgoing>Flow_1huwgzm</outgoing>
</userTask>
<!--用户任务:需要指定用户来执行
flowable:assignee:任务受理人,这里先写为固定,设受理人的用户ID为2
-->
<userTask id="Activity_0ipmowu" name="审核" flowable:assignee="2">
<incoming>Flow_1huwgzm</incoming>
<outgoing>Flow_18upiqx</outgoing>
</userTask>
</process>
<!--节点在流程图中的位置,没有前端可不写-->
<bpmndi:BPMNDiagram id="BPMNDiagram_flow">
<bpmndi:BPMNPlane id="BPMNPlane_flow" bpmnElement="process_fzoauj5k">
<bpmndi:BPMNEdge id="Flow_0ksehsm_di" bpmnElement="Flow_0ksehsm">
<di:waypoint x="-15" y="190" />
<di:waypoint x="70" y="190" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1huwgzm_di" bpmnElement="Flow_1huwgzm">
<di:waypoint x="170" y="190" />
<di:waypoint x="260" y="190" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_18upiqx_di" bpmnElement="Flow_18upiqx">
<di:waypoint x="360" y="190" />
<di:waypoint x="452" y="190" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="BPMNShape_startNode1" bpmnElement="startNode1">
<omgdc:Bounds x="-45" y="175" width="30" height="30" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="-42" y="212" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_08rzeth_di" bpmnElement="Event_08rzeth">
<omgdc:Bounds x="452" y="172" width="36" height="36" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="459" y="215" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_19z5ig5_di" bpmnElement="Activity_01fk1po">
<omgdc:Bounds x="70" y="150" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_09wpg3z_di" bpmnElement="Activity_0ipmowu">
<omgdc:Bounds x="260" y="150" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
启动springboot,resource/processes/下的bpmn文件将会自动载入,数据会保存在act_re_deployment和act_re_procdef表中。
act_re_deployment( 部署信息表)
字段 | 字段名称 | 字段默认值 | 数据类型 | 备注 |
---|---|---|---|---|
ID_ | 主键 | varchar | ||
NAME_ | 名称 | varchar | ||
CATEGORY_ | 分类 | varchar | ||
TENANT_ID_ | 租户ID | varchar | ||
DEPLOY_TIME_ | 部署时间 | timestamp |
act_re_procdef( 流程定义数据表)
字段 | 字段名称 | 字段默认值 | 数据类型 | 备注 |
---|---|---|---|---|
ID_ | 主键 | varchar | ||
REV_ | 版本号 | int | ||
CATEGORY_ | 分类 | varchar | ||
NAME_ | 名称 | varchar | ||
KEY_ | 标识 | varchar | ||
VERSION_ | 版本 | int | ||
DEPLOYMENT_ID_ | 部署ID | varchar | ||
RESOURCE_NAME_ | 资源名称 | varchar | ||
DGRM_RESOURCE_NAME_ | 图片资源名称 | varchar | ||
DESCRIPTION_ | 描述 | varchar | ||
HAS_START_FORM_KEY_ | 表单是否有开始节点的标识 | tinyint | start节点是否存在formKey | |
0否 1是 | ||||
HAS_GRAPHICAL_NOTATION_ | 拥有图形信息 | tinyint | ||
SUSPENSION_STATE_ | 挂起状态 | int | ||
暂停状态 1激活 2暂停 | ||||
TENANT_ID_ | 租户ID | varchar |
2.引入流程引擎
FlowServiceFactory.java
package com.example.demo.factory;
import org.flowable.engine.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.flowable.form.api.FormService;
import javax.annotation.Resource;
/**
* flowable 引擎注入封装
*/
@Component
public class FlowServiceFactory {
@Resource
protected RepositoryService repositoryService;
@Resource
protected RuntimeService runtimeService;
@Resource
protected IdentityService identityService;
@Resource
protected TaskService taskService;
@Resource
protected FormService formService;
@Resource
protected HistoryService historyService;
@Resource
protected ManagementService managementService;
@Qualifier("processEngine")
@Resource
protected ProcessEngine processEngine;
public RepositoryService getRepositoryService() {
return repositoryService;
}
public RuntimeService getRuntimeService() {
return runtimeService;
}
public IdentityService getIdentityService() {
return identityService;
}
public TaskService getTaskService() {
return taskService;
}
public FormService getFormService() {
return formService;
}
public HistoryService getHistoryService() {
return historyService;
}
public ManagementService getManagementService() {
return managementService;
}
public ProcessEngine getProcessEngine() {
return processEngine;
}
}
3.启动流程
上面引入的xml文件相当于定义了一套工作流程,大家都可以使用这个流程。
拿请假流程举例:人员A和B都申请请假,那么就认为A启动了一次流程,B也启动了一次流程。这一套流程就拥有了两个流程实例。
package com.example.demo.controller;
import org.flowable.engine.IdentityService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProcessController {
private final RuntimeService runtimeService;
private final IdentityService identityService;
public ProcessController(RuntimeService runtimeService, IdentityService identityService) {
this.runtimeService = runtimeService;
this.identityService = identityService;
}
@PostMapping("/start")
@Transactional(rollbackFor = Exception.class)
public String start(@RequestParam String key) {
try {
// 设置流程启动用户
identityService.setAuthenticatedUserId("1");
//根据key启动(还可以根据ID、TENANT_ID_启动),返回流程实例(正在运行的流程)
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key);
String processInstanceId = processInstance.getProcessInstanceId();
//返回流程实例ID
return processInstanceId;
} catch (Exception e) {
e.printStackTrace();
return "启动失败!";
}
}
}
使用postman访问接口,其中key为xml中的流程id
启动完成之后,返回了流程实例ID,运行中的流程数据会保存到act_ru_actinst、act_ru_execution、act_ru_identitylink、act_ru_task表中
act_ru_actinst:存储运行中各个节点的信息:
act_ru_identitylink: 存储候选用户、处理人相关数据
act_ru_task :存储审批代办相关信息
Flowable包下的常用服务类:
RuntimeService :用于查询运行中的流程实例相关信息。
TaskService:用于查询代办、代签、签收、办理等操作。
HistoryService:用于查询流程相关历史数据。
4.查询待办列表
@Autowired
private TaskService taskService;
/**获取指定用户的待办的任务列表
* 代办
* */
@GetMapping("/getNeedDoTasks")
@Transactional(rollbackFor = Exception.class)
public String getNeedDoTasks() {
//设当前登录用户ID
String userId = "1";
//taskCandidateOrAssigned 表示不管是任务的候选人还是直接分配的人都可以查到
List<Task> tasks = taskService.createTaskQuery()
.taskCandidateOrAssigned(userId)
.list();
if (!tasks.isEmpty()) {
List<Map<String, Object>> list = new ArrayList<>();
for (Task task : tasks) {
Map<String, Object> map = new HashMap<>();
//代办id
String taskId = task.getId();
//代办节点名称
String taskName = task.getName();
//创建时间
Date createTime = task.getCreateTime();
map.put("taskId", taskId);
map.put("taskName", taskName);
map.put("createTime", createTime);
list.add(map);
}
return listToString(list,',');
}
return "当前用户在当前流程实例中并无待办!";
}
// 列表转字符串
public String listToString(List list, char separator) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
sb.append(list.get(i)).append(separator);
}
return sb.toString().substring(0,sb.toString().length()-1);
}
发起查询
可以看到用户“1”有一条待办任务,任务ID为71a2deb3-b25b-11ec-9428-d46a6adef6a5
接下来我们去审批这个任务。
5.审批通过
/**审批通过并跳转流程*/
@GetMapping("/audit")
@Transactional(rollbackFor = Exception.class)
public String audit(@RequestParam String taskId) {
try {
//跳转流程
taskService.complete(taskId);
return "已审批";
} catch (Exception e) {
e.printStackTrace();
return "跳转失败!";
}
}
访问接口:
审批成功后我们再查询一下待办任务列表看看还有没有待办
可以看到,审批通过后,在用户“1”的待办任务里已经查不到刚才的任务了。
接下来我们去看看用户“1”的已办任务列表。
6.查询已办列表
@Autowired
private HistoryService historyService;
/**获取已办任务列表
* 已办
* */
@GetMapping("/getAlreadyDoTasks")
@Transactional(rollbackFor = Exception.class)
public String getAlreadyDoTasks() {
//获取当前登录用户ID
long userId = 1L;
List<HistoricTaskInstance> taskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
.includeProcessVariables()
.finished()
.taskAssignee(userId+"")
.orderByHistoricTaskInstanceEndTime()
.desc()
.list();
if (!taskInstanceQuery.isEmpty()) {
List<Map<String, Object>> list = new ArrayList<>();
for (HistoricTaskInstance task : taskInstanceQuery) {
Map<String, Object> map = new HashMap<>();
//代办id
String taskId = task.getId();
//代办节点名称
String taskName = task.getName();
//创建时间
Date createTime = task.getCreateTime();
map.put("taskId", taskId);
map.put("taskName", taskName);
map.put("createTime", createTime);
list.add(map);
}
return listToString(list,',');
}
return "暂无已办";
}
访问接口:
可以看到刚才的申请任务已经进入到了已办列表。
7.查询发起列表
/**获取我发起的流程列表
* 发起
* 流程列表
* */
@GetMapping("/getMyStartUpProcesses")
@Transactional(rollbackFor = Exception.class)
public String getMyStartUpProcesses() {
List<HistoricProcessInstance> historicProcessInstanceList=historyService.createHistoricProcessInstanceQuery()
.startedBy("1")
.includeProcessVariables()
.orderByProcessInstanceStartTime()
.desc()
.list();
return listToString(historicProcessInstanceList,',');
}
访问接口:
可以看到用户“1”,发起了一个【审核流程】。
完事了,下一节写动态设置审批人。