Flowable 快速入门教程:Flowable 入门开发案例,结合流程设计器详细讲解

  • 前言
  • 流程设计器集成
  • 整体流程图
  • 流程节点说明
    • 第一审核人节点:实际设置审核人
      • 配置信息
      • 说明
    • 第二审核人:参数设置审核人
      • 配置信息
      • 说明
    • 第三审核人:参数分支判断与实际组配置
      • 配置信息
      • 说明
    • 会签:多人并审与参数设置用户组
      • 配置信息
      • 说明
    • 监听器会签:结合监听器实现多人并审
      • 配置信息
      • 说明
  • 示例代码
    • 项目结构
    • 流程部署
    • 启动流程
    • 业务数据列表查询
      • 页面效果
      • 功能描述
    • 审核列表数据查询
      • 效果图
      • 功能描述
      • 业务系统代码
      • 流程引擎功能封装
    • 审核功能
      • 功能描述
      • 业务系统代码
      • 流程引擎功能封装
    • 审核历史查询
      • 效果图
      • 功能描述
      • 业务系统代码
      • 流程引擎功能封装
    • 流程图查看流程进度
      • 效果图
      • 功能描述
      • 流程引擎功能封装
  • 参考代码

前言

本文以一个简答的 Demo 为案例,按节点讲解,目的是为了让刚接触流程引擎的人能更快的熟悉流程引擎开发,了解业务系统与流程引擎结合的思路。

由于是 Demo,接口存在不完善之处,需要自己补充添加。

流程设计器集成

文章:Flowable 快速入门教程:SpringBoot 集成 Flowable + Flowable Modeler 流程配置可视化(超详细)

整体流程图

起始与结束节点,就配置了名称方便查看,没做额外配置

流程节点说明

第一审核人节点:实际设置审核人

配置信息


说明

这里的分配人为固定的人,用的使用户的编号

任务完成时,直接 complete 即可

/*** 任务处理** @param taskId   任务 Id,来自 ACT_RU_TASK* @param assignee 设置审核人,替换* @param map      完成任务需要的条件参数* @return*/
@RequestMapping(value = "/task", method = RequestMethod.POST)
public void taskByAssignee(@RequestParam(value = "taskId") String taskId, @RequestParam(value = "assignee") String assignee, @RequestBody Map<String, Object> map) {// 设置审核人taskService.setAssignee(taskId, assignee);// 设置任务参数,也可不设置:key value,只是示例// 带 Local 为局部参数,只适用于本任务,不带 Local 为全局任务,可在其他任务调用参数taskService.setVariableLocal(taskId, "status", true);// 完成任务taskService.complete(taskId, map);logger.info("任务完成:" + taskId + " " + new Date());
}

第二审核人:参数设置审核人

配置信息


说明

变量:reviewer 节点审核人

说明:通过参数的形式设定分配人,因此在需要在进入任务节点之前把对应参数注入。否则会提示表达式错误。

疑问说明:如果我在审核时候才设置审核人,那我表单展示时如何知道审核人是谁?

  1. 首先我这里是第一次审核时候就放进去的,因为是 Demo 所有逻辑的严谨性上没考虑那么全
  2. 关于怎么知道表单每条数据的审核人有谁,个人建议自己建表来存关系最为方便与准确。之后审核时候审核人可以在流程开始阶段通过监听器设置进变量或者直接审核动作之前再设置都可以。

业务系统代码

// 设置审核人
feignClientService.setVariable(taskId, "reviewer", "bbb");
// 完成任务,taskId 任务节点 ID
feignClientService.taskByAssignee(taskId, user.getUsername(), map);

流程引擎系统代码,同上

/*** 任务处理** @param taskId   任务 Id,来自 ACT_RU_TASK* @param assignee 设置审核人,替换* @param map      完成任务需要的条件参数* @return*/
@RequestMapping(value = "/task", method = RequestMethod.POST)
public void taskByAssignee(@RequestParam(value = "taskId") String taskId, @RequestParam(value = "assignee") String assignee, @RequestBody Map<String, Object> map) {// 设置审核人taskService.setAssignee(taskId, assignee);// 设置任务参数,也可不设置:key value,只是示例// 带 Local 为局部参数,只适用于本任务,不带 Local 为全局任务,可在其他任务调用参数taskService.setVariableLocal(taskId, "status", true);// 完成任务taskService.complete(taskId, map);logger.info("任务完成:" + taskId + " " + new Date());
}

第三审核人:参数分支判断与实际组配置

配置信息






说明

变量:assignee 节点审核人

说明:分支判断,如果是 admin 审核的进入 分支审核第四审核人2号 节点,其他情况走 分支审核第四审核人1号 节点,默认走 分支审核第四审核人1号

组的配置不管是实际还是参数形式其实差不多,因为都无法让流程直接拿到审核人。区别在于实际值方便那些不懂流程的人配置以及我们可以直接获取组然后查找,而参数我们可以通过变量注入。还是建议自己建表存关系,降低开发难度。

业务系统代码

// 流程完成所需的条件参数
Map<String, Object> map = new HashMap<>();
map.put("assignee", user.getUsername());
// 完成任务,taskId 任务节点 ID
feignClientService.taskByAssignee(taskId, user.getUsername(), map);

流程引擎系统代码,同上。之后流程引擎 complete 后会自动根据你的参数进行分支判断

/*** 任务处理** @param taskId   任务 Id,来自 ACT_RU_TASK* @param assignee 设置审核人,替换* @param map      完成任务需要的条件参数* @return*/
@RequestMapping(value = "/task", method = RequestMethod.POST)
public void taskByAssignee(@RequestParam(value = "taskId") String taskId, @RequestParam(value = "assignee") String assignee, @RequestBody Map<String, Object> map) {// 设置审核人taskService.setAssignee(taskId, assignee);// 设置任务参数,也可不设置:key value,只是示例// 带 Local 为局部参数,只适用于本任务,不带 Local 为全局任务,可在其他任务调用参数taskService.setVariableLocal(taskId, "status", true);// 完成任务taskService.complete(taskId, map);logger.info("任务完成:" + taskId + " " + new Date());
}

会签:多人并审与参数设置用户组

配置信息

说明

变量:

  1. assigneeList:审核人列表,注意这里不能用 ${ } 包裹,否则系统拿到的就是对应的 value 值
  2. nrOfInstances:会签环节实例总数,系统参数,可直接使用
  3. nrOfActiveInstances:还没有完成的实例数量,系统参数,可直接使用
  4. nrOfCompletedInstances:已经完成的实例的数量,系统参数,可直接使用

多实例类型:

  1. Parallel:并行模式,多人同事审核
  2. Sequential:串行模式,按顺序审核,顺序为集合顺序(暂时没试验过)

说明:
assigneeList 这个参数必须在进入会签任务之前注入,原因是任务在进入会签任务时就需要根据 assigneeList 来生成 task 任务,因此这个参数必须存在。这里可以利用监听器在任务生成之前注入,或者在之前节点就注入。

会签判断条件:完成率达到 50% 进入下个节点

业务系统代码,会签节点之前
PS:注意集合参数在请求后,流程引擎那不能用 Object 类型来接收,会导致解析时无法转化为集合,因此我这里集合单独写了个接口来设置参数

// 设置 assigneeList 参数
// 注意,这个参数是在进入会签节点之前就设置了
feignClientService.setListVariable(taskId, "assigneeList", assigneeList);

业务系统代码,会签节点

// 完成任务,taskId 任务节点 ID
feignClientService.taskByAssignee(taskId, user.getUsername(), map);

流程引擎系统代码,同上。之后流程引擎 complete 后会自动根据配置的表达式判断节点是否完成。

/*** 任务处理** @param taskId   任务 Id,来自 ACT_RU_TASK* @param assignee 设置审核人,替换* @param map      完成任务需要的条件参数* @return*/
@RequestMapping(value = "/task", method = RequestMethod.POST)
public void taskByAssignee(@RequestParam(value = "taskId") String taskId, @RequestParam(value = "assignee") String assignee, @RequestBody Map<String, Object> map) {// 设置审核人taskService.setAssignee(taskId, assignee);// 设置任务参数,也可不设置:key value,只是示例// 带 Local 为局部参数,只适用于本任务,不带 Local 为全局任务,可在其他任务调用参数taskService.setVariableLocal(taskId, "status", true);// 完成任务taskService.complete(taskId, map);logger.info("任务完成:" + taskId + " " + new Date());
}

监听器会签:结合监听器实现多人并审

配置信息


说明

变量:okNum 已审核的次数

说明:ExecutionListener 任务监听器,每次有人审核执行任务就把数字 +1,达到 3 个完成节点任务,
监听器会在 complete 之前执行。

流程引擎系统代码

/*** 会签监听器* @author: linjinp* @create: 2019-11-18 11:43**/
@Component
public class MyListener implements ExecutionListener {// 页面配置参数注入private FixedValue num;@Overridepublic void notify(DelegateExecution delegateExecution) {// 获取页面配置参数的值System.out.println(num.getExpressionText());// 校验 okNum 是否已经存在if (!delegateExecution.hasVariable("okNum")) {delegateExecution.setVariable("okNum", 0);}// 已审核次数,审核一次 +1int okNum = (int) delegateExecution.getVariable("okNum") + 1;delegateExecution.setVariable("okNum", okNum);}
}

示例代码

项目结构

我这里业务系统与流程引擎系统是分开的,不处于一个项目,有各自的数据库

业务系统:用户数据,表单,权限等,这里业务系统通过 feign 调用流程引擎的接口

流程引擎系统:流程引擎根据功能对接口进行封装,通过 Restful 形式的接口对外开放

Demo 一共 5 个功能:业务数据列表查询(这个不涉及流程引擎),审核列表数据查询,审核功能,审核历史查询,流程图查看流程进度

代码仅用作入门学习与逻辑参考

流程部署

ACT_DE_MODEL:保存后的流程模型信息

/*** 流程部署** @param modelId 流程ID,来自 ACT_DE_MODEL* @return*/
@RequestMapping(value = "/deploy/{modelId}", method = RequestMethod.GET)
public void deploy(@PathVariable(value = "modelId") String modelId) {// 根据模型 ID 获取模型Model modelData = modelService.getModel(modelId);byte[] bytes = modelService.getBpmnXML(modelData);if (bytes == null) {logger.error("模型数据为空,请先设计流程并成功保存,再进行发布");}BpmnModel model = modelService.getBpmnModel(modelData);if (model.getProcesses().size() == 0) {logger.error("数据模型不符要求,请至少设计一条主线流程");}byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);String processName = modelData.getName() + ".bpmn20.xml";// 部署流程repositoryService.createDeployment().name(modelData.getName()).addBytes(processName, bpmnBytes).deploy();logger.info("流程部署成功:" + modelId + " " + new Date());
}

启动流程

ACT_RE_PROCDEF:流程定义相关信息,流程多次部署后的历史信息也可以在这张表查看

/*** 启动流程** @param deployId 部署的流程 Id,来自 ACT_RE_PROCDEF* @param userId   用户 Id* @param dataKey  数据 Key,业务键,一般为表单数据的 ID,仅作为表单数据与流程实例关联的依据* @return*/
@RequestMapping(value = "/start/{deployId}/{userId}/{dataKey}", method = RequestMethod.GET)
public void start(@PathVariable(value = "deployId") String deployId, @PathVariable(value = "userId") String userId, @PathVariable(value = "dataKey") String dataKey) {// 设置发起人identityService.setAuthenticatedUserId(userId);// 根据流程 ID 启动流程runtimeService.startProcessInstanceById(deployId, dataKey);logger.info("流程启动成功:" + deployId + " " + new Date());
}

业务数据列表查询

页面效果

功能描述

由于不涉及流程引擎,因此就不上代码了。

由于表单与流程引擎本身是不存在关系的,因此这里的状态储存是直接在业务数据表里储存状态,查询起来也简单。

如果需要用到流程引擎中的参数,那就自己查询就可以了。

审核列表数据查询

效果图

功能描述

查询当前用户需要审核的数据列表

业务系统代码

整体逻辑:获取流程中所有审核人为当前用户的流程数据,返回保存在流程中的业务键以及其他需要的数据,然后对数据进行组合业务键也就是数据 ID 是在流程启动时候设置到流程中的,详细看启动接口。

/*** 获取审核列表* @return*/
@GetMapping(value = "/getApproveList")
public ErrorMsg getApproveList(HttpServletRequest request) {ErrorMsg errorMsg = new ErrorMsg();String token = request.getHeader("X-Token");User user = (User) redisTemplate.opsForValue().get(token);// 获取用户拥有的用户组,admin,aaa,testList<Group> hasGroup = userPermissionDao.getUserGroup(user.getUsername());// 取出组 IDList<String> groups = new ArrayList<>();hasGroup.forEach(group -> {groups.add(group.getId());});// 调用流程引擎封装的接口// 根据用户组获取需要审核的数据对应的流程信息// 主要为了满足我设置的是实际组的情况List<Map<String, Object>> idListByGroupMapList = feignClientService.getRuntimeBusinessKeyByGroup(groups);// 调用流程引擎封装的接口// 获取自己发起的正在进行的审核数据对应的流程信息List<Map<String, Object>> idListByUserMapList = feignClientService.getRuntimeBusinessKeyByUser(user.getUsername());// 整合两个 listidListByUserMapList.addAll(idListByGroupMapList);// 这里开始对数据进行组合,方便前端展示List<String> idList = new ArrayList<>();idListByUserMapList.forEach(idListByUserMap -> {// businessKey 为业务键,我用来存数据的 IDidList.add((String) idListByUserMap.get("businessKey"));});// 获取正在审核的数据List<Map<String, Object>> roles = demoService.getRoleListByIdsDemo(idList);// 数据整合,将需要在前端展示的流程信息与业务数据信息组合到一起roles.forEach(role -> {idListByUserMapList.forEach(idListByUserMap -> {if (role.get("id").toString().equals(idListByUserMap.get("businessKey").toString())) {role.put("taskId", idListByUserMap.get("taskId"));role.put("processInstanceName", idListByUserMap.get("processInstanceName"));role.put("startTime", idListByUserMap.get("startTime"));}});});// 统一返回errorMsg.setErrorCode(ErrorCode.SUCCESS);errorMsg.setErrorMsg("SUCCESS");errorMsg.setRetData(roles);return errorMsg;
}

流程引擎功能封装

根据组获取任务数据

/*** 获取组,获取需要审核的业务键 business_key 列表** @param groupIds 组 Id* @return*/
@RequestMapping(value = "/getRuntimeBusinessKeyByGroup", method = RequestMethod.POST)
public List<Map<String, Object>> getRuntimeBusinessKeyByGroup(@RequestBody List<String> groupIds) {List<Map<String, Object>> idList = new ArrayList<>();// 判断是否有组信息if (groupIds != null && groupIds.size() > 0) {// 根据发起人获取正在执行的任务列表List<Task> tasks = taskService.createTaskQuery().taskCandidateGroupIn(groupIds).list();tasks.forEach(task -> {Map<String, Object> data = new HashMap<>();// 根据任务获取流程实例ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();// 获取流程实例中的业务键data.put("businessKey", processInstance.getBusinessKey());// 获取任务 Iddata.put("taskId", task.getId());// 流程定义名称data.put("processInstanceName", processInstance.getProcessDefinitionName());// 流程开始时间data.put("startTime", processInstance.getStartTime());idList.add(data);});}return idList;
}

根据审核人获取任务数据

/*** 根据用户,获取需要审核的业务键 business_key 列表** @param userId 用户 Id* @return*/
@RequestMapping(value = "/getRuntimeBusinessKeyByUser/{userId}", method = RequestMethod.GET)
public List<Map<String, Object>> getRuntimeBusinessKeyByUser(@PathVariable(value = "userId") String userId) {List<Map<String, Object>> idList = new ArrayList<>();// 根据用户获取正在进行的任务List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).list();tasks.forEach(task -> {Map<String, Object> data = new HashMap<>();// 根据任务获取流程实例ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();// 获取流程实例中的业务键data.put("businessKey", processInstance.getBusinessKey());// 获取任务 Iddata.put("taskId", task.getId());// 流程定义名称data.put("processInstanceName", processInstance.getProcessDefinitionName());// 流程开始时间data.put("startTime", processInstance.getStartTime());idList.add(data);});return idList;
}

审核功能

功能描述

点击按钮,进行审核

业务系统代码

整体逻辑:获取任务 Id,完成任务,对需要的参数进行设置
PS:关于用户组,审核人的参数设置建议用监听器之类的来实现,这里为了方便直接在审核里设置了

/**
* 进行审核* @param request* @param taskId 任务节点 Id* @return*/
@GetMapping(value = "/task/{taskId}/{dataId}")
public ErrorMsg taskByAssignee(HttpServletRequest request, @PathVariable(value = "taskId") String taskId, @PathVariable(value = "dataId") String dataId) {ErrorMsg errorMsg = new ErrorMsg();String token = request.getHeader("X-Token");User user = (User) redisTemplate.opsForValue().get(token);// 会签情况需要用户列表数据List<String> assigneeList = userGroupPermissionDao.getIdByGroup("ce537a73-dbc2-4d2f-ac5f-2cdc208a20e0");// 设置会签所需的用户列表数据// 会签与监听器会签节点用户组参数// 注意这里单独封装了个方法,集合不能用 Object 接收,否则流程引擎解析会失败feignClientService.setListVariable(taskId, "assigneeList", assigneeList);// 多实例基数,没用到feignClientService.setVariable(taskId, "cycleNum", assigneeList.size());// 第二审核人节点,审核人参数feignClientService.setVariable(taskId, "reviewer", "bbb");// 流程完成所需的条件参数// 主要用于第三审核人节点,根据审核人进行分支判断依据Map<String, Object> map = new HashMap<>();map.put("assignee", user.getUsername());// 根据任务节点 Id,获取流程实例 IdString processInstanceId = feignClientService.getTaskInfo(taskId);// 完成任务,taskId 任务节点 IDfeignClientService.taskByAssignee(taskId, user.getUsername(), map);// 通过流程实例 Id,判断流程是否结束boolean isFinish = feignClientService.checkProcessInstanceFinish(processInstanceId);if (isFinish) {// 更新审核状态Role role = new Role();role.setId(Integer.valueOf(dataId));role.setStatus(2);demoService.updateRoleDemo(role);}errorMsg.setErrorCode(ErrorCode.SUCCESS);errorMsg.setErrorMsg("SUCCESS");return errorMsg;
}

流程引擎功能封装

参数设置接口

/*** 设置任务参数** @param taskId 任务ID* @param key 键* @param value 值* @return*/
@RequestMapping(value = "/setVariable", method = RequestMethod.POST)
public void setVariable(@RequestParam(value = "taskId") String taskId, @RequestParam(value = "key") String key, @RequestParam(value = "value") Object value) {String processInstanceId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();runtimeService.setVariable(processInstanceId, key, value);
}/*** 设置任务参数,List 使用** @param taskId 任务ID* @param key 键* @param value 值* @return*/
@RequestMapping(value = "/setListVariable", method = RequestMethod.POST)
public void setListVariable(@RequestParam(value = "taskId") String taskId, @RequestParam(value = "key") String key, @RequestParam(value = "value") List<String> value) {String processInstanceId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();runtimeService.setVariable(processInstanceId, key, value);
}

根据任务节点获取流程实例 Id

/*** 根据任务节点获取流程实例 Id** @param taskId 任务节点 Id* @return*/
@RequestMapping(value = "/getTaskInfo/{taskId}", method = RequestMethod.GET)
public String getTaskInfo(@PathVariable(value = "taskId") String taskId) {Task task = taskService.createTaskQuery().taskId(taskId).singleResult();return task.getProcessInstanceId();
}

通过流程实例 Id,判断流程是否结束

/*** 通过流程实例 Id,判断流程是否结束** @param processInstanceId 流程实例 Id* @return true 结束,false 未结束*/
@RequestMapping(value = "/checkProcessInstanceFinish/{processInstanceId}", method = RequestMethod.GET)
public boolean checkProcessInstanceFinish(@PathVariable(value = "processInstanceId") String processInstanceId) {boolean isFinish = false;// 根据流程 ID 获取未完成的流程中是否存在此流程long count = historyService.createHistoricProcessInstanceQuery().unfinished().processInstanceId(processInstanceId).count();// 不存在说明没有结束if (count == 0) {isFinish = true;}return isFinish;
}

审核历史查询

效果图

功能描述

根据审核人字段,获取已完成的任务中,审核人为当前用户的数据

PS:组审核情况,在审核时,都直接设置了审核人,因此可以直接获取到

业务系统代码

/*** 获取审核历史记录* @return*/
@GetMapping(value = "/getApproveHistory")
public ErrorMsg getApproveHistory(HttpServletRequest request) {ErrorMsg errorMsg = new ErrorMsg();String token = request.getHeader("X-Token");User user = (User) redisTemplate.opsForValue().get(token);// 调用流程引擎接口,获取审核人为当前用户的已完成的任务List<Map<String, Object>> historys = feignClientService.getHistoryByUser(user.getUsername());errorMsg.setErrorCode(ErrorCode.SUCCESS);errorMsg.setErrorMsg("SUCCESS");errorMsg.setRetData(historys);return errorMsg;
}

流程引擎功能封装

/*** 获取用户审核历史** @param userId 发起人 Id* @return*/
@RequestMapping(value = "/getHistoryByUser/{userId}", method = RequestMethod.GET)
public List<Map<String, Object>> getHistoryByUser(@PathVariable(value = "userId") String userId) {List<Map<String, Object>> historyList = new ArrayList<>();// 根据用户,查询任务实例历史List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee(userId).finished().orderByHistoricTaskInstanceEndTime().desc().list();list.forEach(historicTaskInstance -> {// 历史流程实例HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(historicTaskInstance.getProcessInstanceId()).singleResult();// 获取需要的历史数据Map<String, Object> historyInfo = new HashMap<>();historyInfo.put("assignee", historicTaskInstance.getAssignee());// 节点名称historyInfo.put("nodeName", historicTaskInstance.getName());// 流程开始时间historyInfo.put("startTime", historicTaskInstance.getCreateTime());// 节点操作时间(本流程节点结束时间)historyInfo.put("endTime", historicTaskInstance.getEndTime());// 流程定义名称historyInfo.put("processName", hpi.getProcessDefinitionName());// 流程实例 IDhistoryInfo.put("processInstanceId", historicTaskInstance.getProcessInstanceId());// 业务键historyInfo.put("businessKey", hpi.getBusinessKey());historyList.add(historyInfo);});return historyList;
}

流程图查看流程进度

效果图

功能描述

这里直接采用流输出直接在页面显示,因此只能前端直接调用流程引擎的接口

如果想要更完善的显示流程,建议自己解析 Json,自己前端用组件生成流程图

流程引擎功能封装

根据任务 ID 获取任务进度流程图

/*** 根据任务 ID 获取任务进度流程图** @param taskId 任务节点 Id* @return*/
@RequestMapping(value = "/getTaskProcessDiagram/{taskId}", method = RequestMethod.GET)
public void getTaskProcessDiagram(@PathVariable(value = "taskId") String taskId, HttpServletResponse httpServletResponse) {// 根据任务 ID 获取流程实例 IDTask task = taskService.createTaskQuery().taskId(taskId).singleResult();String processInstanceId = task.getProcessInstanceId();// 根据流程实例获取流程图// 流程定义 IDString processDefinitionId;// 查看完成的进程中是否存在此进程long count = historyService.createHistoricProcessInstanceQuery().finished().processInstanceId(processInstanceId).count();if (count > 0) {// 如果流程已经结束,则得到结束节点HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();processDefinitionId = pi.getProcessDefinitionId();} else {// 如果流程没有结束,则取当前活动节点// 根据流程实例ID获得当前处于活动状态的ActivityId合集ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();processDefinitionId = pi.getProcessDefinitionId();}List<String> highLightedActivitis = new ArrayList<>();// 获得活动的节点List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).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 processEngineConfig = processEngine.getProcessEngineConfiguration();ProcessDiagramGenerator diagramGenerator = processEngineConfig.getProcessDiagramGenerator();InputStream in = diagramGenerator.generateDiagram(bpmnModel, "bmp", highLightedActivitis, flows, processEngineConfig.getActivityFontName(),processEngineConfig.getLabelFontName(), processEngineConfig.getAnnotationFontName(), processEngineConfig.getClassLoader(), 1.0, true);OutputStream out = null;byte[] buf = new byte[1024];int legth;try {out = httpServletResponse.getOutputStream();while ((legth = in.read(buf)) != -1) {out.write(buf, 0, legth);}} catch (IOException e) {e.printStackTrace();} finally {IOUtils.closeQuietly(out);IOUtils.closeQuietly(in);}
}

参考代码

Flowable 测试用接口部分的代码

Flowable-Demo 模块, API 包下

Flowable 快速入门教程:Flowable 入门开发案例,结合流程设计器详细讲解相关推荐

  1. 基于Flowable 6.x 的工作流管理平台源码 在线流程设计器 在线流程表单设

    基于Flowable 6.x 的工作流管理平台源码 在线流程设计器 在线流程表单设计器 单节点配置表单 多实例会签任务 任务节点配置任务/执行监听器 动态配置任务候选人 其它流程相关功能点

  2. web工作流管理系统开发之三 可视化流程设计器

    原文地址为: web工作流管理系统开发之三 可视化流程设计器 在工作流管理系统中,引擎的所有的活动,驱动,和流转,都是以流程定义为基础而展开的.流程定义文件是流程能运行的先决条件,同时流程定义文件又是 ...

  3. 工作流管理系统开发之三 可视化流程设计器

    在工作流管理系统中,引擎的所有的活动,驱动,和流转,都是以流程定义为基础而展开的.流程定义文件是流程能运行的先决条件,同时流程定义文件又是工作流引擎的设计基础,引擎必须要能生成,解释和获取到任意流程定 ...

  4. 树莓派python界面qt_树莓派QT入门教程——使用Qt开发界面程序控制GPIO

    树莓派QT入门教程--使用Qt开发界面程序控制GPIO 玩转树莓派2017-07-12 12:36 树莓派入门教程--使用Qt开发界面程序 前言 Qt是一个1991年由奇趣科技开发的跨平台C++图形用 ...

  5. Java实例开发教程:SpringBoot开发案例

    最近在做邮件发送的服务,正常来说SpringBoot整合mail还是很方便的,然而来了新的需求:A请求使用邮箱C发送,B请求使用邮箱D发送,也就是说我们需要配置两套发送服务. 单实例 首先我们来看下单 ...

  6. [ Flowable ] 与modeler流程设计器整合教程

    Flowable 与 modeler 流程设计器整合方案 本教程基于Flowable 6.2.1 ,破解 flowable-idm的权限登录,整合SpringMVC实现maven动态导入jar包,期间 ...

  7. 基金投资入门教程-----基金入门

    基金投资入门教程-----基金入门 基金投资入门教程-----基金入门 1.基金入门 什么是基金 基金的三大优势 基金的风险 基金投资入门教程-----基金入门 1.基金入门 什么是基金 基金的三大优 ...

  8. 【工作流引擎】Flowable流程设计器 基于bpmnjs开发的vue组件

    [工作流引擎]Flowable流程设计器 基于bpmnjs开发的vue组件 设计器介绍 集成设计器 设计器介绍 bpmn.js官网 bpmn.js 是一个BPMN2.0渲染工具包和web建模器, 使得 ...

  9. 快速开发工作流_02_集成在线流程设计器

    接上一篇:快速开发工作流_01_简单流程案例https://gblfy.blog.csdn.net/article/details/102881983 文章目录 七.流程设计器 modeler 7.1 ...

最新文章

  1. Leetode算法刷题宝典.pdf
  2. 摩拜活跃用户跌200万、使用次数跌6400万或告别榜首之争?
  3. CentOS7 LVM磁盘扩容
  4. 星尘小组第八周翻译-数据页和数据行
  5. 教你从头写游戏服务器框架
  6. php异步处理,执行系统命令
  7. amazon php 空间,(四)Amazon Lightsail 部署LAMP应用程序之扩展PHP前端
  8. 平台框架_从框架到平台
  9. java内存溢出分析工具:jmap使用实战
  10. node-red mysql的增删改查_通过curl或者http请求对elasticsearch中的数据进行增删改查...
  11. 数据库-Linux系统下的命令
  12. HBase实践案例:车联网监控系统
  13. SPSS偏相关分析的应用介绍
  14. 基于PS2手柄的Arduino遥控小车
  15. win7计算机启动遇到错误怎么办,W7电脑系统出现Windows错误恢复该怎么办
  16. android 手机投屏电视,手机投屏到智能电视上的几种方法
  17. 都市白领要学会的规则
  18. 好家伙,查看系统日志时我捕获了一只发生概率小于万分之一的Bug
  19. CC00417.CloudKubernetes——|KuberNetesNetworkPolicy.V09|——|NetworkPolicy.v09|隔离中间件服务.v05|
  20. 判断对象是否可回收、垃圾回收算法

热门文章

  1. Polar Si9000使用方法----阻抗匹配软件
  2. 移动端touch事件实现页面弹动--小插件
  3. 取长补短战争升级,“蔚小理”煮酒论英雄
  4. [大模拟]登机牌条码
  5. DES加解密(详细的加密流程)
  6. Deep learning with Python 学习笔记(6)
  7. [日推荐]『淘内部优惠券工具』省钱小能手
  8. 软件工程导论---概述--软件危机
  9. python2 怎么读utf8_python2读取utf8文件(中文)
  10. 【RTT】I2C总线设备:分析