一、流程审批后涉及到的表
表名 | 描述 |
---|---|
act_fo_form_instance | 存储用户填充后表单实例信息,FORM_DEFINITION_ID_字段 2bb4ecac-cfb8-11e9-9f13-1a1dea14efe7 |
act_fo_form_resource | NAME_字段 form-2bb4ecac-cfb8-11e9-9f13-1a1dea14efe7 |
act_hi_actinst | 历史的流程实例 插入多条数据 |
act_hi_identitylink | 历史的流程运行过程中用户关系 插入多条数据 |
act_hi_procinst | 历史的流程实例 REV_ 1未完成 2已完成 |
act_hi_taskinst | 历史的任务实例 REV_ 1待认领 2等审批 3已审批 |
act_hi_varinst | 历史的流程运行中的变量信息 |
act_ru_actinst | 存储运行时节点信息 与act_hi_actinst同时存储 |
act_ru_execution | 执行实例表和act_run_task表,一起控制了用户任务的产生与完成等,当在并行网关和会签多实例时,它是会产生多个执行实例,IS_ACTIVE_这个字段的值都是为1,即激活状态,当每完成一个执行实例时,它会把IS_ACTIVE设为0,非激活状态,当所有执行实例完成后,它才会转移到历史,把这个多实例自动删除 |
act_ru_identitylink | 运行时用户关系 |
act_ru_task | 运行时任务表 |
act_ru_variable | 运行的流程中的变量信息 |
当流程全部走完后,act_ru_表的数据清空了,全部移到了act_hi_表
二、流程示例
-
创建流程图
创建OrderApproval.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:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
<!--订单审批,必须定义ID-->
<process id="OrderApproval" name="订单审批" isExecutable="true">
<!--开始事件-->
<startEvent id="startEvent" name="采购订单"></startEvent>
<!--流程-->
<sequenceFlow id="sequenceFlow-3" sourceRef="startEvent" targetRef="approveTask"></sequenceFlow>
<!--定义任务 flowable:assignee 指定用户ID-->
<userTask id="approveTask" name="订单审批" flowable:assignee="${userId}">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<!--定义网关-->
<exclusiveGateway id="decision"></exclusiveGateway>
<!--定义流程条件-->
<sequenceFlow id="sequenceFlow-9" sourceRef="decision" targetRef="fail">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!approved}]]></conditionExpression>
</sequenceFlow>
<!--定义任务->同意-->
<serviceTask id="success" name="通过" flowable:class="com.xtsz.workflow.delegate.ReviewApprove"></serviceTask>
<!--定义结束事件-->
<endEvent id="approveEnd"></endEvent>
<endEvent id="rejectEnd"></endEvent>
<sequenceFlow id="sequenceFlow-e" sourceRef="success" targetRef="approveEnd"></sequenceFlow>
<sequenceFlow id="sequenceFlow-1" sourceRef="fail" targetRef="rejectEnd"></sequenceFlow>
<!--定义任务->拒绝-->
<serviceTask id="fail" name="拒绝" flowable:class="com.xtsz.workflow.delegate.ReviewNoApprove"></serviceTask>
<sequenceFlow id="sequenceFlow-5" sourceRef="approveTask" targetRef="decision"></sequenceFlow>
<sequenceFlow id="sequenceFlow-c" sourceRef="decision" targetRef="success">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved}]]></conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_OrderApproval">
<bpmndi:BPMNPlane bpmnElement="OrderApproval" id="BPMNPlane_OrderApproval">
<bpmndi:BPMNShape bpmnElement="startEvent" id="BPMNShape_startEvent">
<omgdc:Bounds height="30.0" width="30.0" x="0.0" y="95.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="approveTask" id="BPMNShape_approveTask">
<omgdc:Bounds height="60.0" width="100.0" x="75.0" y="75.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="decision" id="BPMNShape_decision">
<omgdc:Bounds height="40.0" width="40.0" x="230.0" y="90.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="success" id="BPMNShape_success">
<omgdc:Bounds height="60.0" width="100.0" x="320.0" y="0.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="approveEnd" id="BPMNShape_approveEnd">
<omgdc:Bounds height="28.0" width="28.0" x="620.0" y="16.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="rejectEnd" id="BPMNShape_rejectEnd">
<omgdc:Bounds height="28.0" width="28.0" x="570.0" y="175.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="fail" id="BPMNShape_false">
<omgdc:Bounds height="60.0" width="100.0" x="315.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-5" id="BPMNEdge_sequenceFlow-5">
<omgdi:waypoint x="174.95" y="105.0"></omgdi:waypoint>
<omgdi:waypoint x="202.5" y="105.0"></omgdi:waypoint>
<omgdi:waypoint x="202.5" y="110.0"></omgdi:waypoint>
<omgdi:waypoint x="230.0" y="110.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-c" id="BPMNEdge_sequenceFlow-c">
<omgdi:waypoint x="269.9189252336448" y="110.0"></omgdi:waypoint>
<omgdi:waypoint x="282.0" y="110.0"></omgdi:waypoint>
<omgdi:waypoint x="282.0" y="30.000000000000004"></omgdi:waypoint>
<omgdi:waypoint x="319.999999999994" y="30.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-1" id="BPMNEdge_sequenceFlow-1">
<omgdi:waypoint x="414.95000000000005" y="180.0"></omgdi:waypoint>
<omgdi:waypoint x="460.0" y="180.0"></omgdi:waypoint>
<omgdi:waypoint x="460.0" y="189.0"></omgdi:waypoint>
<omgdi:waypoint x="570.0" y="189.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-e" id="BPMNEdge_sequenceFlow-e">
<omgdi:waypoint x="419.94999999998697" y="30.0"></omgdi:waypoint>
<omgdi:waypoint x="620.0" y="30.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-3" id="BPMNEdge_sequenceFlow-3">
<omgdi:waypoint x="29.949987029268733" y="110.0"></omgdi:waypoint>
<omgdi:waypoint x="52.5" y="110.0"></omgdi:waypoint>
<omgdi:waypoint x="52.5" y="105.0"></omgdi:waypoint>
<omgdi:waypoint x="74.99999999999241" y="105.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow-9" id="BPMNEdge_sequenceFlow-9">
<omgdi:waypoint x="269.9189252336448" y="110.0"></omgdi:waypoint>
<omgdi:waypoint x="282.0" y="110.0"></omgdi:waypoint>
<omgdi:waypoint x="282.0" y="180.0"></omgdi:waypoint>
<omgdi:waypoint x="314.9999999999916" y="180.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
- 创建委托
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
/**
* 批准委托类
*/
@Slf4j
public class ReviewApprove implements JavaDelegate {
@Override
public void execute(DelegateExecution delegateExecution) {
//可以发送消息给某人
log.info("通过,userId是:{}",delegateExecution.getVariable("userId"));
}
}
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
/**
* 驳回委托类
*/
@Slf4j
public class ReviewNoApprove implements JavaDelegate {
@Override
public void execute(DelegateExecution delegateExecution) {
//可以发送消息给某人
log.info("拒绝,userId是:{}",delegateExecution.getVariable("userId"));
}
}
- 创建控制器
@RestController
@RequestMapping("/orderFlow")
@Slf4j
public class OrderFlowController {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
@Resource
private ProcessEngine processEngine;
/**
* 1.提交采购订单的审批请求
*
* @param userId 用户id
*/
@PostMapping("/start/{userId}/{purchaseOrderId}")
public Result startFlow(@PathVariable String userId, @PathVariable String purchaseOrderId) {
HashMap<String, Object> map = new HashMap<>();
map.put("userId", userId);
map.put("purchaseOrderId", purchaseOrderId);
// 流程ID->OrderApproval
ProcessInstance processInstance =
runtimeService.startProcessInstanceByKey("OrderApproval", map);
String processId = processInstance.getId();
// 名称由布署时指定
String name = processInstance.getName();
log.info(processId + ":" + name);
return Result.ok(processId + ":" + name);
}
/**
* 2.获取用户的任务
*
* @param userId 用户id
*/
@GetMapping("/getTasks/{userId}")
public Result getTasks(@PathVariable String userId) {
List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
return Result.ok(tasks.toString());
}
/**
* 3.审批通过
*/
@PostMapping("/success/{taskId}")
public Result success(@PathVariable String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (task == null) {
return Result.error("流程不存在");
}
//通过审核
HashMap<String, Object> map = new HashMap<>();
// 在流程图中获取进行处理
map.put("approved", true);
taskService.complete(taskId, map);
return Result.ok("流程审核通过!");
}
/**
* 4.审批不通过
*/
@PostMapping("/fail/{taskId}")
public Result fail(@PathVariable String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (task == null) {
return Result.error("流程不存在");
}
//通过审核
HashMap<String, Object> map = new HashMap<>();
// 在流程图中获取进行处理
map.put("approved", false);
taskService.complete(taskId, map);
return Result.ok();
}
/**
* 5. 生成流程图
*
* @param httpServletResponse
* @param processId
* @throws Exception
*/
@PostMapping(value = "processDiagram")
public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) {
/**
* 获得当前活动的节点
*/
String processDefinitionId = "";
boolean isFinish = historyService.createHistoricProcessInstanceQuery().finished()
.processInstanceId(processId).count() > 0;
if (isFinish) {// 如果流程已经结束,则得到结束节点
HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processId).singleResult();
processDefinitionId = pi.getProcessDefinitionId();
} else {// 如果流程没有结束,则取当前活动节点
// 根据流程实例ID获得当前处于活动状态的ActivityId合集
ProcessInstance pi = runtimeService.createProcessInstanceQuery()
.processInstanceId(processId).singleResult();
processDefinitionId = pi.getProcessDefinitionId();
}
List<String> highLightedActivitis = new ArrayList<String>();
/**
* 获得活动的节点
*/
List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();
for (HistoricActivityInstance tempActivity : highLightedActivitList) {
String activityId = tempActivity.getActivityId();
highLightedActivitis.add(activityId);
}
List<String> flows = new ArrayList<>();
//获取流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
// 注意:如果是PNG格式那么输出的是背景色是黑色,如果连接线上有字不容易看清楚。可以使用bmp
InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis, flows, engconf.getActivityFontName(),
engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, true);
OutputStream out = null;
byte[] buf = new byte[1024];
int legth = 0;
try {
out = httpServletResponse.getOutputStream();
while ((legth = in.read(buf)) != -1) {
out.write(buf, 0, legth);
}
} catch (IOException e) {
log.error("操作异常", e);
} finally {
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(in);
}
}
}
三、运行测试
- 提交采购订单的审批请求
http://localhost:7001/orderFlow/start/1/1
返回:
{
"msg": "3c6dc4b2-f30a-11e9-bc68-005056c00008:null",
"code": 0
}
- 获取用户的任务
http://localhost:7001/orderFlow/getTasks/1
返回:
{
"msg": "[Task[id=3c6dc4b2-f30a-11e9-bc68-005056c00008, name=订单审批]]",
"code": 0
}
{
"msg": "流程审核通过!",
"code": 0
}
- 审批驳回
http://localhost:7001/orderFlow/fail/fac2256e-f30e-11e9-a987-005056c00008
任务ID: fac2256e-f30e-11e9-a987-005056c00008
返回:
{
"msg": "success",
"code": 0
}
- 生成流程图
http://localhost:7001/orderFlow/processDiagram?processId=4fdcbade-f311-11e9-acac-005056c00008
processId: 为流程实例ID
四、事件监听器实现
事件监听器的唯一要求是实现org.flowable.engine.delegate.event.FlowableEventListener。
一个事件侦听器基类,可用于侦听特定类型的实体或所有实体的实体相关事件。它隐藏掉类型检查,并提供4种方法应覆盖:onCreate(..),onUpdate(..)并onDelete(..)创建实体时,更新或删除。
五、常见问题
- "code":401,"error":"Unauthorized."
排除配置
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
- 流程文档部署时没生成流程图片
如果流程文档部署时没生成流程图片,且流程定义中包含必要的“图形交换(diagram interchange)”信息,Flowable引擎会生成流程图。
如果由于某种原因,不需要或不希望在部署时生成流程图,可以在流程引擎配置中设置isCreateDiagramOnDeploy参数:
<property name="createDiagramOnDeploy" value="false" />
- Waiting for changelog lock....
数据库中执行:
SELECT `LOCKED` FROM workflow_flowable.ACT_DMN_DATABASECHANGELOGLOCK WHERE ID=1
UPDATE workflow_flowable.ACT_DMN_DATABASECHANGELOGLOCK
SET locked=0 WHERE ID=1
- 没有生成流程图
bpmn 任务中没有加入:
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>