专业的JAVA编程教程与资源

网站首页 > java教程 正文

Springboot整合工作流引擎Activiti(二)

temp10 2024-10-27 14:43:04 java教程 9 ℃ 0 评论

本篇内容:实时查看当前流程进度。

说明:本篇内容是基于上一篇《Springboot整合工作流引擎Activiti(一) 》,请先阅读上一篇内容

Springboot整合工作流引擎Activiti(二)


  • HolidayService类中添加两个方法:
/**
	 *  <p>查询历史信息</p>
	 *  <p>时间:2021年1月24日-上午10:42:02</p>
	 * @author baba
	 * @param instanceId 实例ID
	 * @return HistoricProcessInstance
	 */
	public HistoricProcessInstance queryHistory(String instanceId) {
		return historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult() ;
	}
	
	/**
	 *  <p>根据实例ID获取历史活动示例查询器</p>
	 *  <p>时间:2021年1月24日-上午10:45:46</p>
	 * @author baba
	 * @param instanceId 实例ID
	 * @return HistoricActivityInstanceQuery
	 */
	public HistoricActivityInstanceQuery getHistoryActivity(String instanceId) {
		return historyService.createHistoricActivityInstanceQuery().processInstanceId(instanceId) ; 
	}
  • 配置流程图生成Bean
@Configuration
public class ActivitiConfig {
	
	@Bean
	@ConditionalOnMissingBean
	public ProcessDiagramGenerator processDiagramGenerator() {
		return new DefaultProcessDiagramGenerator() ;
	}
	
}
  • 获取各个节点信息(执行过的)
public class ActivitiUtils {
	
	/**
	 *  <p>
	 *  	获取流程走过的线
	 *  </p>
	 *  <p>时间:2021年1月24日-上午10:52:01</p>
	 * @author baba
	 * @param bpmnModel 流程对象模型
	 * @param processDefinitionEntity 流程定义对象
	 * @param historicActivityInstances 历史流程已经执行的节点,并已经按执行的先后顺序排序
	 * @return List<String> 流程走过的线
	 */
	public static List<String> getHighLightedFlows(BpmnModel bpmnModel, ProcessDefinitionEntity processDefinitionEntity, List<HistoricActivityInstance> historicActivityInstances) {
		// 用以保存高亮的线flowId
		List<String> highFlows = new ArrayList<String>();
		if(historicActivityInstances == null || historicActivityInstances.size() == 0) return highFlows;
		// 遍历历史节点
		for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
			// 取出已执行的节点
			HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i);
			// 用以保存后续开始时间相同的节点
			List<FlowNode> sameStartTimeNodes = new ArrayList<FlowNode>();
			// 获取下一个节点(用于连线)
			FlowNode sameActivityImpl = getNextFlowNode(bpmnModel, historicActivityInstances, i, activityImpl_);
			// 将后面第一个节点放在时间相同节点的集合里
			if(sameActivityImpl != null) sameStartTimeNodes.add(sameActivityImpl);
			// 循环后面节点,看是否有与此后继节点开始时间相同的节点,有则添加到后继节点集合
			for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
				HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);// 后续第一个节点
				HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);// 后续第二个节点
				if (activityImpl1.getStartTime().getTime() != activityImpl2.getStartTime().getTime()) break;
				
				// 如果第一个节点和第二个节点开始时间相同保存
				FlowNode sameActivityImpl2 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId());
				sameStartTimeNodes.add(sameActivityImpl2);
			}
			// 得到节点定义的详细信息
			FlowNode activityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId());
			// 取出节点的所有出去的线,对所有的线进行遍历
			List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows();
			for (SequenceFlow pvmTransition : pvmTransitions) {
				// 获取节点
				FlowNode pvmActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(pvmTransition.getTargetRef());
				// 不是后继节点
				if(!sameStartTimeNodes.contains(pvmActivityImpl)) continue;
				// 如果取出的线的目标节点存在时间相同的节点里,保存该线的id,进行高亮显示
				highFlows.add(pvmTransition.getId());
			}
		}
		//返回高亮的线
		return highFlows;
	}
	/**
	 *  <p>
	 *  	获取下一个节点信息
	 *  </p>
	 *  <p>时间:2021年1月24日-上午10:52:46</p>
	 * @author baba
	 * @param bpmnModel 流程模型
	 * @param historicActivityInstances 历史节点
	 * @param i 当前已经遍历到的历史节点索引(找下一个节点从此节点后)
	 * @param activityImpl_ 当前遍历到的历史节点实例
	 * @return FlowNode 下一个节点信息
	 */
	private static FlowNode getNextFlowNode(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances, int i, HistoricActivityInstance activityImpl_) {
		// 保存后一个节点
		FlowNode sameActivityImpl = null;
		// 如果当前节点不是用户任务节点,则取排序的下一个节点为后续节点
		if(!"userTask".equals(activityImpl_.getActivityType())) {
			// 是最后一个节点,没有下一个节点
			if(i == historicActivityInstances.size()) return sameActivityImpl;
			// 不是最后一个节点,取下一个节点为后继节点
			sameActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i + 1).getActivityId());// 找到紧跟在后面的一个节点
			// 返回
			return sameActivityImpl;
		}
		// 遍历后续节点,获取当前节点后续节点
		for (int k = i + 1; k <= historicActivityInstances.size() - 1; k++) {
			// 后续节点
			HistoricActivityInstance activityImp2_ = historicActivityInstances.get(k);
			// 都是userTask,且主节点与后续节点的开始时间相同,说明不是真实的后继节点
			if("userTask".equals(activityImp2_.getActivityType()) && activityImpl_.getStartTime().getTime() == activityImp2_.getStartTime().getTime()) continue;
			// 找到紧跟在后面的一个节点
			sameActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId());
			break;
		}
		return sameActivityImpl;
	}
}
  • 测试Controller
@RestController
@RequestMapping("/view")
public class ProcessViewController {
	
	private static Logger logger = LoggerFactory.getLogger(ProcessViewController.class) ;
 	
	@Resource
	private HolidayService holidayService ;
	@Resource
	private RepositoryService repositoryService ;
	@Resource
	private ProcessDiagramGenerator processDiagramGenerator ;
	
	@ResponseBody
	@GetMapping("/image")
	public void showImg(String instanceId, HttpServletResponse response) throws Exception {
		response.setContentType("text/html;charset=utf-8") ;
		if (StringUtils.isEmpty(instanceId)) {
			PrintWriter out = response.getWriter() ;
			out.write("error") ;
			out.close() ;
			return ;
		}
		// 获取流程实例
		HistoricProcessInstance processInstance = holidayService.queryHistory(instanceId) ;
		if(processInstance == null) {
			logger.error("流程实例ID:{}没查询到流程实例!", instanceId);
			PrintWriter out = response.getWriter() ;
			out.write("error instance not exists") ;
			out.close() ;
			return;
		}
		// 根据流程对象获取流程对象模型
		BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
		// 构造历史流程查询
		HistoricActivityInstanceQuery historyInstanceQuery = holidayService.getHistoryActivity(instanceId) ;
		// 查看已执行的节点集合,获取流程历史中已执行节点,并按照节点在流程中执行先后顺序排序
		List<HistoricActivityInstance> historicActivityInstanceList = historyInstanceQuery.orderByHistoricActivityInstanceStartTime().asc().list();
		if(historicActivityInstanceList == null || historicActivityInstanceList.isEmpty()) {
			logger.error("流程实例ID: {}, 没有历史节点信息!", instanceId) ;
			outputImg(response, bpmnModel, null, null) ;
			return ;
		}
		// 已执行的节点ID集合(将historicActivityInstanceList中元素的activityId字段取出封装到executedActivityIdList)
		List<String> executedActivityIdList = historicActivityInstanceList.stream().map(item -> item.getActivityId()).collect(Collectors.toList());
		// 获取流程定义
		ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(processInstance.getProcessDefinitionId());
		List<String> flowIds = ActivitiUtils.getHighLightedFlows(bpmnModel, processDefinition, historicActivityInstanceList);
		// 输出图像,并设置高亮
		outputImg(response, bpmnModel, flowIds, executedActivityIdList);
	}

	/**
	 *  <p>
	 *  	输出图像
	 *  </p>
	 *  <p>时间:2021年1月24日-上午10:49:42</p>
	 * @author baba
	 * @param response
	 * @param bpmnModel 图像对象
	 * @param flowIds 已执行的线集合
	 * @param executedActivityIdList 已执行的节点ID集合
	 * @return void
	 */
	private void outputImg(HttpServletResponse response, BpmnModel bpmnModel, List<String> flowIds, List<String> executedActivityIdList) {
		InputStream imageStream = null;
		try {
			imageStream = processDiagramGenerator.generateDiagram(bpmnModel, executedActivityIdList, flowIds, "宋体", "微软雅黑", "黑体", true, "png");
			// 输出资源内容到相应对象
			byte[] b = new byte[10 * 1024] ;
			int len = -1 ;
			while ((len = imageStream.read(b)) != -1) {
				response.getOutputStream().write(b, 0, len);
			}
			response.getOutputStream().flush();
		}catch(Exception e) {
			logger.error("流程图输出异常!", e);
		} finally {
			if (imageStream != null) {
				try {
					imageStream.close() ;
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
}

测试:

  1. 生成一个新的请假流程
    调用接口:
    /holidays/start?processDefinitionId=holiday:1:0a012ce6-5df2-11eb-afe9-00d861e5b732&userId=10000
  2. 查看当前用户的任务列表(找出刚启动流程的实例id)
    调用接口:
    /holidays/tasks?userId=10000
  1. 查看当前流程图
    调用接口:
    /view/image?instanceId=341ee217-5df2-11eb-afe9-00d861e5b732


  1. 继续执行下一步流程
    调用接口:
    /holidays/apply?days=3&mgr=10001&explain=生病&instanceId=341ee217-5df2-11eb-afe9-00d861e5b732
  1. 查看当前流程图
    调用接口:
    /view/image?instanceId=341ee217-5df2-11eb-afe9-00d861e5b732

到此完毕!!!

各位好心人给个关注+转发好不好谢谢了


Springboot整合工作流引擎Activiti(一)

Tomcat配置参数connectionTimeout意义

Java并发编程之CompletionService

Alibaba Sentinel动态规则(Nacos数据源)

volatile对指令重排的影响

springboot mybatis jpa 实现读写分离

alibaba sentinel 流控使用

Spring Cloud Sentinel 热点参数限流

Springboot Security 基础应用 (1)

mybatis sharding-jdbc Java8日期

SpringMVC内嵌Tomcat零配置

JavaScript继承与原型链

SpringBoot中使用Cache及JSR107的使用

Spring Cloud Gateway应用详解1之谓词

SpringBoot RabbitMQ消息可靠发送与接收

Springboot接口幂等性基于token实现方案

SpringBoot2 整合OAuth2实现统一认证

springboot 中使用JWT保护资源安全

SpringBoot+Atomikos多数据源分布式事务

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表