工作流flowable在项目中的应用实例

4 篇文章 1 订阅
订阅专栏

目前的项目主要有两个审批流程,在审批过程中需要发送邮件给申请人告知目前进展,此处记录下我在项目中是如何使用flowable工作流与业务相结合的。

1. 添加工作流依赖

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.0</version>
		</dependency>
		<!-- spring aop -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-rest -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-data-rest</artifactId>
		</dependency>
				
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.9</version>
        </dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- 邮件服务 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
		<dependency>
		    <groupId>com.alibaba</groupId>
		    <artifactId>fastjson</artifactId>
		    <version>1.2.61</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
		<dependency>
		    <groupId>com.fasterxml.jackson.core</groupId>
		    <artifactId>jackson-annotations</artifactId>
		</dependency>
				
		<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
		<dependency>
		    <groupId>org.aspectj</groupId>
		    <artifactId>aspectjrt</artifactId>
		</dependency>
		<!-- shiro -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.4.1</version>
		</dependency>
		
		<!-- 日志 -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
		</dependency>
		
		<dependency>
		    <groupId>commons-lang</groupId>
		    <artifactId>commons-lang</artifactId>
		    <version>2.6</version>
		</dependency>
		
		<dependency>
		    <groupId>org.apache.commons</groupId>
		    <artifactId>commons-lang3</artifactId>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>3.14</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>3.14</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
		<dependency>
		    <groupId>com.jcraft</groupId>
		    <artifactId>jsch</artifactId>
		    <version>0.1.55</version>
		</dependency>
				
		<!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
		<dependency>
		    <groupId>commons-net</groupId>
		    <artifactId>commons-net</artifactId>
		    <version>3.6</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.flowable/flowable-spring-boot-starter-process -->
		<dependency>
		    <groupId>org.flowable</groupId>
		    <artifactId>flowable-spring-boot-starter-process</artifactId>
		    <version>6.4.0</version>
		</dependency>

		
	</dependencies>

2. 在application.yml中增加配置项

flowable:
  check-process-definitions: false #设置需要手动部署工作流

3. 画流程图

设置审批候选人为固定的某个色的所有人

实际应用中,除了需要将流程完整的结束,我们通常需要跟踪业务状态,通常每个处理节点都会对应一个业务状态,此处指定方法负责更新状态和发送邮件

 

4. 由于每个业务系统都会有自己的用户角色权限管理,因此需要与flowable的用户组关系保持同步,一种方法是利用监听器,当业务系统编辑用户信息时就同步至flowable用户表中,另一种就是删除flowable的用户组关系表,利用视图将业务系统的用户组关系同步至flowable的用户组关系表中,并且视图名称与原flowable用户组关系表名称相同

5. 代码

###部署工作流###
@Transactional(readOnly = false)
public Object deploy (final String processName) {
	logger.info("---- FlowService deploy begin ----");
	final Deployment deployment = processEngine.getRepositoryService()
													.createDeployment()
													.addClasspathResource("processes/" + processName + ".bpmn")
													.addClasspathResource("processes/" + processName + ".png")
													.name(processName)
													.key(processName)
													.deploy();
	final Map<String, Object> map = new HashMap<>();
	map.put("deploymentId", deployment.getId());
	map.put("deploymentKey", deployment.getKey());
	map.put("deploymentName", deployment.getName());
	map.put("deploymentEngineVersion", deployment.getEngineVersion());
	logger.info("---- FlowService deploy end ----");
	return map;
} 


###查看已部署的工作流###
public Object deployedProcessList() {
	logger.info("---- FlowService deployedProcess begin ----");
	final List<Deployment> deploymentList = processEngine.getRepositoryService()
													.createDeploymentQuery()
													.orderByDeploymenTime()
													.asc()
													.list();
	final List<Map<String, Object>> list = new ArrayList<>();
	for (Deployment deployment : deploymentList) {
		final Map<String, Object> map = new HashMap<>();
		map.put("deploymentId", deployment.getId());
		map.put("deploymentKey", deployment.getKey());
		map.put("deploymentName", deployment.getName());
		map.put("deploymentEngineVersion", deployment.getEngineVersion());
		list.add(map);
	}
	logger.info("---- FlowService deployedProcess end ----");
	return list;
}

/**
* 查询流程定义
* @return
*/
public Object processDefinitionList () {
	logger.info("---- FlowService getProcessDefinition begin ----");
	final List<ProcessDefinition> definitionList = processEngine.getRepositoryService()
													.createProcessDefinitionQuery()
													.list();
	final List<Map<String, Object>> list = new ArrayList<>();
	for (ProcessDefinition processDefinition : definitionList) {
		final Map<String, Object> map = new HashMap<>();
		map.put("definitionId", processDefinition.getId());
		map.put("definitionKey", processDefinition.getKey());
		map.put("definitionName", processDefinition.getName());
		map.put("definitionDeploymentId", processDefinition.getDeploymentId());
		map.put("definitionResourceName", processDefinition.getResourceName());
		map.put("definitionVersion", processDefinition.getVersion());
		map.put("definitionEngineVersion", processDefinition.getEngineVersion());
		list.add(map);
	}
	logger.info("---- FlowService getProcessDefinition end ----");
	return list;
}

/**
 * 启动一个工作流
 * 
 * @param processName 工作流模板 (启动最新的版本)
 * @param variables 参数表
 * @param user 启动人
 * @return
 */
@Transactional(readOnly=false)
public String startProcess(String processName, String key,  Map<String, Object> variables) {
		
	IdentityService identityService = processEngine.getIdentityService();
		identityService.setAuthenticatedUserId(UserUtils.getCurrentUser().getUserName());
	logger.info("设置启动人");
	
	RuntimeService runtimeService = processEngine.getRuntimeService();
	ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processName, key, variables);
	return processInstance.getId();
}

/**
	 * 获取当前角色待办任务列表
	 * @return
	 */
	public Object getRoleTaskList() {
		logger.info("---- FlowService getRoleTaskList begin ----");
		List<Task> taskList = processEngine.getTaskService()
										 .createTaskQuery()
										 .taskCandidateGroup(UserUtils.getCurrentUser().getRoleName())
										 .orderByTaskCreateTime()
										 .asc()
										 .list();
		final List<Map<String, Object>> list = new ArrayList<>();
		for (Task task : taskList) {
			final Map<String, Object> map = new HashMap<>();
			map.put("taskId", task.getId());
			map.put("createTime", task.getCreateTime());
			map.put("taskName", task.getName());
			map.put("taskOwner", task.getOwner());
			map.put("processDefinitionId", task.getProcessDefinitionId());
			map.put("processInstanceId", task.getProcessInstanceId());
			map.put("taskDefinitionId", task.getTaskDefinitionId());
			map.put("taskDefinitiionKey", task.getTaskDefinitionKey());
			list.add(map);
		}
		logger.info("---- FlowService getRoleTaskList end ----");
		return list;
	}
	
	/**
	 * 获取当前用户待办任务列表
	 * @return
	 */
	public Object getPersonalTaskList() {
		logger.info("---- FlowService getPersonalTaskList begin ----");
		final List<Task> taskList = processEngine.getTaskService()
										.createTaskQuery()
										.taskAssignee(UserUtils.getCurrentUser().getUserName())
										.orderByTaskCreateTime()
										.asc()
										.list();
		final List<Map<String, Object>> list = new ArrayList<>();
		for (Task task : taskList) {
			final Map<String, Object> map = new HashMap<>();
			map.put("taskId", task.getId());
			map.put("createTime", task.getCreateTime());
			map.put("taskName", task.getName());
			map.put("taskOwner", task.getOwner());
			map.put("processDefinitionId", task.getProcessDefinitionId());
			map.put("processInstanceId", task.getProcessInstanceId());
			map.put("taskDefinitionId", task.getTaskDefinitionId());
			map.put("taskDefinitiionKey", task.getTaskDefinitionKey());
			list.add(map);
		}
		logger.info("---- FlowService getPersonalTaskList end ----");
		return list;
	}
	
	/**
	 * 判断所处理的任务是否是当前用户或当前用户角色的任务
	 * @param taskId
	 * @return
	 */
	public boolean isPersonalTask (final String taskId) {
		final String currentUserName = UserUtils.getCurrentUser().getUserName();
		final Task personalTask = processEngine.getTaskService()
												 .createTaskQuery()
												 .taskAssignee(currentUserName)
												 .taskId(taskId)
												 .singleResult();
		logger.info("current user name: " + currentUserName);
		logger.info("taskId: " + taskId);
		if (personalTask == null) {
			logger.info("personalTask is null");
		} else {
			logger.info("personal taskId: " + personalTask.getId() + "personal assignee: " + personalTask.getAssignee());
		}
		if (personalTask != null && taskId.equals(personalTask.getId())) {
			return true;
		}
		final String currentRoleName = UserUtils.getCurrentUser().getRoleName();
		final Task roleTask = processEngine.getTaskService()
											 .createTaskQuery()
											 .taskCandidateGroup(currentRoleName)
											 .taskId(taskId)
											 .singleResult();
		logger.info("taskId: " + taskId);
		if (roleTask == null) {
			logger.info("roleTask is null");
		} else {
			logger.info("role taskId: " + roleTask.getId() + "role assignee: " + roleTask.getAssignee());
		}
		if (roleTask != null && taskId.equals(taskId)) {
			return true;
		}
		
		return false;
	}
	
	/**
	 * 获取所有未处理的任务
	 * @return
	 */
	public Object getUnresolvedTaskList() {
		List<Task> taskList = processEngine.getTaskService()
				.createTaskQuery()
				.orderByTaskCreateTime()
				.asc()
				.list();
		final List<Map<String, Object>> list = new ArrayList<>();
		for (Task task : taskList) {
			final Map<String, Object> map = new HashMap<>();
			map.put("taskId", task.getId());
			map.put("createTime", task.getCreateTime());
			map.put("taskName", task.getName());
			map.put("taskOwner", task.getOwner());
			map.put("processDefinitionId", task.getProcessDefinitionId());
			map.put("processInstanceId", task.getProcessInstanceId());
			map.put("taskDefinitionId", task.getTaskDefinitionId());
			map.put("taskDefinitiionKey", task.getTaskDefinitionKey());
			list.add(map);
		}
		return list;
	}
	
	/**
	 * 签收任务
	 * @param taskId
	 * @param username
	 */
	@Transactional(readOnly = false)
	public void claim(String taskId, String username) {
		logger.info("签收任务:"+ taskId + " 签收人:" + username + " begin");

		processEngine.getTaskService().claim(taskId,username);
		
		logger.info("签收任务:"+ taskId + " 签收人:" + username + " end");
	}
	
	/**
	 * 执行某个流程节点
	 * @param taskId
	 * @param variables
	 */
	@Transactional(readOnly=false)
	public void completeTask(String taskId, Map<String, Object> variables) {
		if (!isPersonalTask(taskId)) {
			throw new BusinessException(ExceptionEnum.EXECUTE_BASE_CHECK_EXCPTION.setExceptionMsg("请任务处理人或处理角色操作"));
		}
		processEngine.getTaskService().complete(taskId, variables);
		logger.info("完成任务:" + taskId);
	}
	
	
	/**
	 * 执行某个流程节点, 待审批意见和附件
	 * @param taskId
	 * @param variables
	 * @param comment
	 * @param fileMap <文件路径, 文件名称>
	 */
	@Transactional(readOnly=false)
	public void completeTask(final String taskId, final Map<String, Object> variables, final String action, String opinion, final Map<String, Object> fileMap) {
		logger.info("task id: " + taskId + " ; action = " + action);
		if (!isPersonalTask(taskId)) {
			throw new BusinessException(ExceptionEnum.EXECUTE_BASE_CHECK_EXCPTION.setExceptionMsg("请任务处理人或处理角色操作"));
		}
		//添加批注时候的审核人
		Authentication.setAuthenticatedUserId(UserUtils.getCurrentUser().getUserName()); 
		final TaskService taskService = processEngine.getTaskService();
		final Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		if (StringUtils.isEmpty(opinion)) {
			opinion = action;
		} else {
			opinion = action + ": " + opinion;
		}
		taskService.addComment(taskId, task.getProcessInstanceId(), opinion);
		
		if (fileMap != null) {
			final Iterator it = fileMap.keySet().iterator();
			while(it.hasNext()) {
				final String filePath = (String) it.next();
				final String fileName = (String) fileMap.get(filePath);
				String suffix = "";
				if (fileName.contains(".") && !fileName.endsWith(".")) {
					suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
				} else {
					if (filePath.contains(".") && !filePath.endsWith(".")) {
						suffix = filePath.substring(filePath.lastIndexOf(".") + 1);
					}
				}
				taskService.createAttachment(suffix, taskId, task.getProcessInstanceId(), fileName, null, filePath);
			}
		}
		try {
			taskService.complete(taskId, variables);
		} catch (Exception e) {
			logger.error("处理任务失败", e);
			throw new BusinessException(ExceptionEnum.EXECUTE_BASE_CHECK_EXCPTION.setExceptionMsg("任务处理失败"));
		}

		logger.info("完成任务:" + taskId);
	}
	
	/**
	 * 获取审批历史记录
	 * @param processInstanceId 流程实例ID
	 * @return
	 */
	public List<ApprovalHistory> getApprovalHistory (final String processInstanceId) {
		final List<ApprovalHistory> approvalHistoryList = new ArrayList<>();
		
		//通过流程实例查询所有的(用户任务)历史活动
		List<HistoricActivityInstance> historicActivityInstanceList = processEngine.getHistoryService()
																					.createHistoricActivityInstanceQuery()
																					.processInstanceId(processInstanceId)
																					.activityType("userTask")
																					.orderByHistoricActivityInstanceStartTime()
																					.asc()
																					.list();
		for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
			
			final String historyTaskId = historicActivityInstance.getTaskId();
			//每个节点所产生的文档
			final List<Attachment> attachments = processEngine.getTaskService().getTaskAttachments(historyTaskId);
			final List<HistoryFile> historyFileList = new ArrayList<>();
			for (Attachment attachment : attachments) {
				final HistoryFile historyFile = new HistoryFile();
				historyFile.setPath(attachment.getUrl());
				historyFile.setName(attachment.getName());
				historyFileList.add(historyFile);
			}
			//每个节点的评论
			final List<Comment> comments = processEngine.getTaskService().getTaskComments(historyTaskId);
			if (comments != null && comments.size() > 0) {
				for (Comment comment : comments) {
					final ApprovalHistory approvalHistory = new ApprovalHistory();
					final User user = userDao.queryByUserName(comment.getUserId());
					approvalHistory.setRoleName(user.getRoleName());
					approvalHistory.setUserName(user.getName() + " (" + user.getUserName() + ")");
					approvalHistory.setOperateTime(comment.getTime());
					approvalHistory.setOpinion(comment.getFullMessage());
					approvalHistory.setTaskId(historyTaskId);
					approvalHistory.setProcessInstanceId(processInstanceId);
					approvalHistory.setHistoryFileList(historyFileList);
					approvalHistoryList.add(approvalHistory);
				}
			}
		}
		
		return approvalHistoryList;
	}
	
	public List<Comment> getProcessComments(String processInstanceId) {
		final List<Comment> commentList = new ArrayList<>();
		//通过流程实例查询所有的(用户任务)历史活动
		List<HistoricActivityInstance> historicActivityInstanceList = processEngine.getHistoryService()
																					.createHistoricActivityInstanceQuery()
																					.processInstanceId(processInstanceId)
																					.activityType("userTask")
																					.orderByHistoricActivityInstanceStartTime()
																					.asc()
																					.list();
		for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
			final String historyTaskId = historicActivityInstance.getTaskId();
			List<Comment> comments = processEngine.getTaskService().getTaskComments(historyTaskId);
			if (comments != null && comments.size() > 0) {
				commentList.addAll(comments);
			}
		}
		return commentList;
	}

	/**
	 * 生成流程进展图
	 * @param processKey
	 * @param request
	 * @return
	 */
	public void genProcessDiagram(final String processInstanceId, final HttpServletResponse response) {
		logger.info("---- FlowService genProcessDiagram begin ----");

		final HistoryService historyService = processEngine.getHistoryService();
		final RuntimeService runtimeService = processEngine.getRuntimeService();
		//获得当前活动的节点
		String processDefinitionId = "";
		if (isFinished(processInstanceId)) {
			//如果流程已经结束,则得到结束节点
			HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery()
														.processInstanceId(processInstanceId)
														.singleResult();
			if (hpi == null) {
				throw new BusinessException(ExceptionEnum.EXECUTE_BASE_CHECK_EXCPTION.setExceptionMsg("流程定义不存在"));
			}
			processDefinitionId = hpi.getProcessDefinitionId();
		} else {
			//如果流程没有结束,则取当前活动节点
			//根据流程实例ID获得当前处于活动状态的activityId合集
			final ProcessInstance instance = runtimeService.createProcessInstanceQuery()
															.processInstanceId(processInstanceId)
															.singleResult();
			if (instance == null) {
				throw new BusinessException(ExceptionEnum.EXECUTE_BASE_CHECK_EXCPTION.setExceptionMsg("流程实例不存在"));
			}
			processDefinitionId = instance.getProcessDefinitionId();
		}
		final List<String> highLightedActivitis = new ArrayList<String>();
		
		//获得活动的节点
		List<HistoricActivityInstance> highLightActivityList = historyService.createHistoricActivityInstanceQuery()
																			 .processInstanceId(processInstanceId)
																			 .orderByHistoricActivityInstanceStartTime()
																			 .asc()
																			 .list();
		highLightActivityList = highLightActivityList.subList(highLightActivityList.size() - 1, highLightActivityList.size());
		for (HistoricActivityInstance historicActivityInstance : highLightActivityList) {
			final String activityId = historicActivityInstance.getActivityId();
			//判断节点是否在当前节点之前
			highLightedActivitis.add(activityId);
		}
		
		//需要高亮显示的线
		final List<String> flows = new ArrayList<>();
		
		
		final BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(processDefinitionId);
		final ProcessEngineConfiguration config = processEngine.getProcessEngineConfiguration();
		final ProcessDiagramGenerator diagramGenerator = config.getProcessDiagramGenerator();
		
		final InputStream inputStream = diagramGenerator.generateDiagram(bpmnModel, "bmp", highLightedActivitis, flows, config.getActivityFontName(),
											config.getLabelFontName(), config.getAnnotationFontName(), config.getClassLoader(), 1.0, true);
		
		OutputStream outputStream = null;
		byte[] buf = new byte[1024];
        int legth = 0;
		try {
			outputStream = response.getOutputStream();
			while ((legth = inputStream.read(buf)) != -1) {
				outputStream.write(buf, 0, legth);
            }
			outputStream.flush();
		} catch (IOException e) {
			logger.error("生成流程图片失败", e);
			throw new BusinessException(ExceptionEnum.EXECUTE_RUNTIME_EXCEPTION.setExceptionMsg("生成流程图片失败"));
		} finally {
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					logger.error("输出流关闭失败", e);
				}
			}
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					logger.error("输入流关闭失败", e);
				}
			}
		}
		logger.info("---- FlowService genProcessDiagram end ----");
		
	}
	
	/**
	 * 判断节点是否结束
	 * @param processInstanceId
	 * @return
	 */
	public boolean isFinished(final String processInstanceId) {
		return processEngine.getHistoryService().createHistoricProcessInstanceQuery().finished().processInstanceId(processInstanceId).count() > 0;
	}
	
	/**
	 * 查看流程定义图
	 * @param deploymentId
	 * @param response
	 */
	public void viewDefinitionDiagram(final String deploymentId, final HttpServletResponse response) {
		final RepositoryService repositoryService = processEngine.getRepositoryService();
		final ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
		if (processDefinition == null) {
			throw new BusinessException(ExceptionEnum.EXECUTE_BASE_CHECK_EXCPTION.setExceptionMsg("流程定义不存在"));
		}
		InputStream inputStream = processEngine.getRepositoryService().getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
		OutputStream outputStream = null;
		byte[] buf = new byte[1024];
        int legth = 0;
		try {
			outputStream = response.getOutputStream();
			while ((legth = inputStream.read(buf)) != -1) {
				outputStream.write(buf, 0, legth);
            }
			outputStream.flush();
		} catch (IOException e) {
			logger.error("获取流程定义图片失败", e);
			throw new BusinessException(ExceptionEnum.EXECUTE_RUNTIME_EXCEPTION.setExceptionMsg("获取流程定义图片失败"));
		} finally {
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					logger.error("输出流关闭失败", e);
				}
			}
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					logger.error("输入流关闭失败", e);
				}
			}
		}
	}

	/**
	 * 根据 processInstanceId 获取当前的任务
	 * @param processInstanceId
	 * @return
	 */
	public Task findTaskByProcessInstanceId(String processInstanceId) {
		final Task task = processEngine.getTaskService()
										.createTaskQuery()
										.processInstanceId(processInstanceId)
										.singleResult();
		return task;
	}

	/**
	 * 撤回,返回 taskID
	 * @param processInstanceId
	 * @return
	 */
	public Object withdraw(String processInstanceId) {
//		final ProcessInstance processInstance = processEngine.getRuntimeService()
//															 .createProcessInstanceQuery()
//															 .processInstanceId(processInstanceId)
//															 .singleResult();
		final TaskService taskService = processEngine.getTaskService();
		final HistoryService historyService = processEngine.getHistoryService();
		final RepositoryService repositoryService = processEngine.getRepositoryService();
		final RuntimeService runtimeService = processEngine.getRuntimeService();
		
		final String currentUserName = UserUtils.getCurrentUser().getUserName();
		
		final Task task = taskService.createTaskQuery()
									.processInstanceId(processInstanceId)
									.singleResult();
		if (task == null) {
			throw new BusinessException(ExceptionEnum.EXECUTE_BASE_CHECK_EXCPTION.setExceptionMsg("流程未启动或已执行完成,无法撤回"));
		}
		List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
															.processInstanceId(processInstanceId)
															.orderByTaskCreateTime()
															.asc()
															.list();
		HistoricTaskInstance myTask = null;
		for (HistoricTaskInstance historicTaskInstance : htiList) {
			if(currentUserName.equals(historicTaskInstance.getAssignee())) {
				myTask = historicTaskInstance;
				break;
			}
		}
		if(myTask.getId() == null) {
			throw new BusinessException(ExceptionEnum.EXECUTE_BASE_CHECK_EXCPTION.setExceptionMsg("该任务非当前用户提交,无法撤回"));
		}
		final String processDefinitionId = myTask.getProcessDefinitionId();
		final ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) processEngine.getRepositoryService()
																				.createProcessDefinitionQuery()
																				.processDefinitionId(processDefinitionId)
																				.singleResult();
		final BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
		//变量
		Map<String, VariableInstance> variables = runtimeService.getVariableInstances(task.getExecutionId());
		String myActivityId = null;
		final List<HistoricActivityInstance> haiList = historyService.createHistoricActivityInstanceQuery()
				.executionId(myTask.getExecutionId()).finished().list();
		for(HistoricActivityInstance hai : haiList) {
			if(myTask.getId().equals(hai.getTaskId())) {
				myActivityId = hai.getActivityId();
				break;
			}
		}
		FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);
		
		
		Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
		String activityId = execution.getActivityId();
		logger.warn("------->> activityId:" + activityId);
		FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
		
		//记录原活动方向
		List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
		oriSequenceFlows.addAll(flowNode.getOutgoingFlows());
		
		//清理活动方向
		flowNode.getOutgoingFlows().clear();
		//建立新方向
		List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
		SequenceFlow newSequenceFlow = new SequenceFlow();
		newSequenceFlow.setId("newSequenceFlowId");
		newSequenceFlow.setSourceFlowElement(flowNode);
		newSequenceFlow.setTargetFlowElement(myFlowNode);
		newSequenceFlowList.add(newSequenceFlow);
		flowNode.setOutgoingFlows(newSequenceFlowList);
		
		Authentication.setAuthenticatedUserId(UserUtils.getCurrentUser().getUserName());
		taskService.addComment(task.getId(), task.getProcessInstanceId(), "撤回");
		
		Map<String,Object> currentVariables = new HashMap<String,Object>();
		currentVariables.put("applier", currentUserName);
		//完成任务
		taskService.complete(task.getId(),currentVariables);
		//恢复原方向
		flowNode.setOutgoingFlows(oriSequenceFlows);
		return task.getId();
	}


###更新流程节点状态和发送邮件###
public void updateStatus (final DelegateExecution execution, final String status) {
		String businessKey = execution.getProcessInstanceBusinessKey();
		//根据业务id自行处理业务表
		System.out.println("业务表["+businessKey+"]状态更改成功,状态更改为:" + status + ", 流程实例ID:" + execution.getProcessInstanceId());
		if (businessKey.contains(":")) {
			final Integer designId = Integer.valueOf(businessKey.split(":")[1]);
			final Design design = (Design) designDao.queryById(designId);

			design.setStatus(status);
			designDao.update(design);
			if (design.getCreateUserId().equals(UserUtils.getCurrentUser().getId())) {
				return;
			}
			//根据流程关键字和状态查找邮件模板
			MailTemplate mailTemplate = mailTemplateDao.findByProcessKeyAndStatus(Constant.PROCESS_KEY_DESIGN , status);
			if (mailTemplate == null) {
				mailTemplate = new MailTemplate();
				mailTemplate.setContent("邮件通知");
				mailTemplate.setSubject("邮件通知");
			}
			String content = mailTemplate.getContent();
			if (!StringUtils.isEmpty(content)) {
				content = content.replaceAll("\\$\\{name\\}", design.getName());
			}
			final User user = (User) userDao.queryById(design.getCreateUserId());
			mailService.sendMail(user.getEmail(), mailTemplate.getSubject(), content, execution.getProcessInstanceId());
		}
	}

 

Flowable工作流6.3.0 项目实战
weixin_41040119的博客
08-02 1420
flowable工作流项目的简单应用
SpringBoot集成Flowable案例
weixin_39311781的博客
04-30 715
Flowable 是一个使用 Java 编写的轻量级业务流程引擎。Flowable 流程引擎可用于部署 BPMN2.0 流程定义(用于定义流程的行业 XML 标准),创建这些流程定义的流程实例,进行查询,访问运行或历史的流程实例与相关数据,等等。
Flowable实战(一)启动第一个完整流程
jinyangjie的博客
01-07 6326
发现网上关于Flowable的资料基本都是浅尝辄止,对如何构建一个企业级的流程应用说明很少,所以写个实战系列,希望对大家和自己,都有所帮助。Flowable是一个使用Java编写的轻量级业务流程引擎。Flowable是Activiti的fork,即Flowable源自Activiti。所以可以看到,Flowable很多设计与实现,与Activiti是相同的。Flowable官网教程已经提供了一个很简单的流程运行例子,是英文版。文章下面的例子内容来源于官网教程,已经看过的同学可以直接跳过到下一节内容。
小白学流程引擎-FLowable(四) —Flowable UI应用程序详解
weixin_44143114的博客
12-03 2351
环境版本:Flowable UI 6.7.2 Flowable Modeler用于建模BPMN流程、cmmn案例模型,DMN决策模型、form表单定义,以及创建应用定义。 这里讲解BPMN流程是怎么样绘制,其他功能后续讲解。BPMN编辑器分为4个部分:画板(Palette): 用于设计流程模型的所有
工作流实战篇_01_flowable 流程Demo案例
Gblfy_Blog
12-11 3991
https://gitee.com/lwj/flowable.git 由于群里有些朋友对这个flowable还不是 很熟悉,在群里的小伙伴的建议下,我制作一个开源的项目源码,一共大家学习和交流,希望对有帮助,少走弯路 如果有不懂的问题可以入群:633168411 里面都是一些热心肠的人 项目地址:https://gitee.com/lwj/flowable.git 分支flowable-demo ...
Flowable-6.5.0 用户手册.pdf
06-09
Flowable 可以用于工作流管理,例如项目管理、workflow 管理等。 4.3. 自动化流程 Flowable 可以用于自动化流程,例如自动化办公、自动化生产等。 5. 结论 Flowable 是一个功能强大且灵活的业务流程引擎,能够...
Flowable v6.4.2版本文文档
11-21
Flowable流程引擎可用于部署BPMN 2.0流程定义(用于定义流程的行业XML标准), 创建这些流程定义的流程实例,进行查询,访问运行或历史的流程实例与相关数据,等等。这个章节将用一个可以在你自己的开发环境使用...
Flowable从入门到精通
12-31
最终结合Springboot搭建一套工作流系统,囊括一般项目所需要的知识点,理论结合实际,让真正入门到熟练。 1 简介 2 学习指南 2.1 Flowable初体验 2.1.1 Flowable是什么? 2.1.2 Flowable 和 Activiti 2.1.3 构建...
Flowable BPMN 用户手册-文版 (v 6.3.1).pdf
07-15
Flowable流程引擎可用于部署BPMN 2.0流程定义(用于定义流程的行业XML标准), 创建这些流程定义的流程实例,进行查询,访问运行或历史的流程实例与相关数据,等等。这个章节将用一个可以在你自己的开发环境使用...
Java开发案例-springboot-59-整合Flowable工作流-源代码+文档.rar
最新发布
05-31
Java开发案例-springboot-59-整合Flowable工作流-源代码+文档.rar Java开发案例-springboot-59-整合Flowable工作流-源代码+文档.rar Java开发案例-springboot-59-整合Flowable工作流-源代码+文档.rar Java开发案例-...
flowable后台管理项目
09-27
最新版flowable6.3.1后台管理项目,war包形式,放置Tomcat的webapp下,自动部署安装。
十分详细的工作流实例
07-11
一套完整的工作流源码,加上自己深刻的工作流理解,以及工作流的学习笔记。外加web端工作流开发的研究生论文。是工作流的学习帮助十分之大,是一份非常好的工作流学习资料。
基于springboot的flowable工作流实战
亲测,只为展现最完美的有效!
10-18 3366
Flowable工作流从入门到会用,工作流常用API整理,Flowable整合springboot,Flowable操作流程之排他网关,Flowable审批流程怎么做
SpringBoot+flowable-ui集成实战
ooaash的博客
10-12 7816
前言 公司最近想要使用flowable作为我们工作流引擎,主要用于各类流程审批上。在网上搜索到了很多有参考意义的文章,但有些实现细节还需要自己去摸索。本文的实战包括: 在项目引入flowable的包可以使用flowable的api; 将flowable-ui集成到自己项目里; 如何使用flowable-ui创建的流程模型(我们用的是bpmn模型,所以后面的提到的流程都是指bpmn); 集成公司的用户认证体系; 自动分配 flowable集成 flowable的集成有两部分:flowable-api与f
flowable工作流所有业务概念
m0_54850825的博客
07-27 479
官方解释如下Flowable项目提供了一套核心的开源业务流程引擎,这些引擎紧凑且高效。它们为开发人员、系统管理员和业务用户提供工作流和业务流程管理(BPM)平台。它的核心是一个闪电般快速、久经考验的动态BPMN流程引擎,伴随着DMN决策表和CMMN案例管理引擎,所有这些引擎都是用Java编写的。它们是Apache2.0许可的开放源代码,拥有一个承诺社区。所有引擎都可以嵌入在Java应用程序运行,也可以作为服务器、集群和云的服务运行。目的是管理业务审批工作流。...
Vueflowable 流程图
Boy_Martin的博客
09-06 8793
Vueflowable 流程图
Flowable各表间关系和字段详解
weixin_44192363的博客
08-01 3832
RepositoryService接口操作的表。
Flowable工作流的实现方案
Boy_Martin的博客
08-02 1311
Flowable的启动-审核-修改
工作流 flowable activity
08-16
Flowable是一个开源的、用于构建工作流和业务流程的框架。它提供了一套功能强大的工具和API,使得开发人员能够轻松地创建、管理和执行各种类型的工作流和业务流程。 在Flowable,一个工作流或业务流程通常由一系列的活动(Activities)组成,这些活动代表了流程的各个步骤或任务。Flowable提供了几种类型的活动,包括: 1. User Task(用户任务):代表需要由用户完成的任务,可以与用户界面进行交互。 2. Service Task(服务任务):代表需要执行特定服务或操作的任务,可以与外部系统进行交互。 3. Start Event(开始事件):标识流程的起始点。 4. End Event(结束事件):标识流程的结束点。 5. Exclusive Gateway(排他网关):用于根据条件进行分支和合并流程。 6. Parallel Gateway(并行网关):用于并行执行多个分支的活动。 除了这些基本的活动类型外,Flowable还支持其他类型的活动,如子流程、事件监听器等,以满足不同场景下的需求。 在Flowable,你可以使用Java或者XML来定义工作流或业务流程,并通过Flowable API来管理和执行这些流程。Flowable提供了丰富的功能,如流程实例的启动、任务的分配和完成、流程变量的管理等,使得开发人员能够轻松地构建和管理复杂的工作流和业务流程。 总结起来,Flowable是一个功能强大的工作流和业务流程框架,通过定义和管理各种类型的活动,使得开发人员能够构建和执行复杂的流程。Flowable提供了丰富的API和工具,方便开发人员进行流程的创建、管理和执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • Postgresql 当中有四种方式获取当前时间 82757
  • Spring Aop实例@Aspect、@Before、@AfterReturning@Around 注解方式配置 55197
  • springboot + mybatis设置将SQL语句打印到控制台 53122
  • mysql 插入数据时,出现"\xF0\x9F\x8F\x80"这种情况的处理! 34558
  • Java导出Excel表,POI 实现合并单元格以及列自适应宽度 33847

分类专栏

  • K8S 2篇
  • RocketMQ 1篇
  • 移动云 8篇
  • VMWare 1篇
  • windows 2篇
  • 消息队列 1篇
  • Thread 3篇
  • SpringBoot发送邮件 1篇
  • Python 2篇
  • Java 101篇
  • JavaEE框架 69篇
  • ActiveMQ 7篇
  • Lua 3篇
  • Java网络爬虫 1篇
  • 数据库 29篇
  • 自媒体 1篇
  • SVN 5篇
  • Excel导出与文件下载 19篇
  • jsp与js 35篇
  • java面试题 27篇
  • 单点登录 2篇
  • Redis学习 5篇
  • Linux下部署Java Web项目 4篇
  • Acitiviti 4篇
  • Spring Boot 36篇
  • 单元测试 5篇
  • mybatis 7篇
  • git 3篇
  • SourceTree
  • Nginx配置 12篇
  • Git gerrit 2篇
  • FTP上传下载
  • Spring Cloud 20篇
  • Docker 14篇

最新评论

  • ApplicationEventPublisher的使用学习

    xl_happy_fe: 大家不要看这个文章 误人子弟

  • Spring cache + Redis实现缓存

    Alex-Zoro: createRedisCache的cacheConfig.entryTtl(duration)方法,应该是返回了一个新的对象,所以应该是需要cacheConfig = cacheConfig.entryTtl(duration)。重新赋值才生效吧

  • Java使用RSA非对称加密对大于117的字符串进行加密

    ggyoujian: javax.crypto.BadPaddingException: Decryption error

  • Spring Aop实例@Aspect、@Before、@AfterReturning@Around 注解方式配置

    m0_61332115: 经过实践 @AfterReturning 注解的执行顺序先于 @After注解,对这一点提出质疑

  • 虚拟化系列-VMware篇-Horizon介绍

    Jieminy杰: 请教一下,瘦终端wes7系统安装horizon client 可以正常连接到云桌面,但是普通电脑安装horizon client 连接云桌面就会出现错误,这种情况怎么解决?

大家在看

  • 【C语言】解决C语言报错:Use of Uninitialized Variable
  • 离线数仓搭建 1
  • 【Test 64】 Qt信号与槽机制! 高频的Qt 知识点! 836
  • Spring框架的原理及应用详解(三) 454
  • 查找拥有有效邮箱的用户(sql练习) 1060

最新文章

  • Redis集群脑裂导致数据丢失问题处理
  • Spring cache + Redis实现缓存
  • nginx配置location总结及rewrite规则写法
2023年4篇
2022年15篇
2021年32篇
2020年17篇
2019年29篇
2018年121篇
2017年131篇
2016年129篇

目录

目录

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

聚圣源飞驰人生在线美容美发店起名字好起个好名字男孩cctu1男宝起名缺金白金卡奖品兑换建设有限公司起名给猪起名字包青天之开封奇案剧情飞花轻寒易学起名字大全葫芦娃app河南郸城剑来小说雨宫瑞穗琉璃美人煞电视剧百度网盘庆余年电视剧在线观看全集五星体育在线宝宝起名中间字雪地飞车osxelcapitan何需清浅cp起名网站毛字怎么起名联通手机winxp下载洗冤录2粤语洗耳恭听三国战神八字缺土 起啥名淀粉肠小王子日销售额涨超10倍罗斯否认插足凯特王妃婚姻让美丽中国“从细节出发”清明节放假3天调休1天男孩疑遭霸凌 家长讨说法被踢出群国产伟哥去年销售近13亿网友建议重庆地铁不准乘客携带菜筐雅江山火三名扑火人员牺牲系谣言代拍被何赛飞拿着魔杖追着打月嫂回应掌掴婴儿是在赶虫子山西高速一大巴发生事故 已致13死高中生被打伤下体休学 邯郸通报李梦为奥运任务婉拒WNBA邀请19岁小伙救下5人后溺亡 多方发声王树国3次鞠躬告别西交大师生单亲妈妈陷入热恋 14岁儿子报警315晚会后胖东来又人满为患了倪萍分享减重40斤方法王楚钦登顶三项第一今日春分两大学生合买彩票中奖一人不认账张家界的山上“长”满了韩国人?周杰伦一审败诉网易房客欠租失踪 房东直发愁男子持台球杆殴打2名女店员被抓男子被猫抓伤后确诊“猫抓病”“重生之我在北大当嫡校长”槽头肉企业被曝光前生意红火男孩8年未见母亲被告知被遗忘恒大被罚41.75亿到底怎么缴网友洛杉矶偶遇贾玲杨倩无缘巴黎奥运张立群任西安交通大学校长黑马情侣提车了西双版纳热带植物园回应蜉蝣大爆发妈妈回应孩子在校撞护栏坠楼考生莫言也上北大硕士复试名单了韩国首次吊销离岗医生执照奥巴马现身唐宁街 黑色着装引猜测沈阳一轿车冲入人行道致3死2伤阿根廷将发行1万与2万面值的纸币外国人感慨凌晨的中国很安全男子被流浪猫绊倒 投喂者赔24万手机成瘾是影响睡眠质量重要因素春分“立蛋”成功率更高?胖东来员工每周单休无小长假“开封王婆”爆火:促成四五十对专家建议不必谈骨泥色变浙江一高校内汽车冲撞行人 多人受伤许家印被限制高消费

聚圣源 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化