目前的项目主要有两个审批流程,在审批过程中需要发送邮件给申请人告知目前进展,此处记录下我在项目中是如何使用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. 画流程图

flowable 如何结合任务实现微服务工作流 flowable工作流入门使用_List

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

flowable 如何结合任务实现微服务工作流 flowable工作流入门使用_List_02

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

flowable 如何结合任务实现微服务工作流 flowable工作流入门使用_List_03

 

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

flowable 如何结合任务实现微服务工作流 flowable工作流入门使用_List_04

flowable 如何结合任务实现微服务工作流 flowable工作流入门使用_List_05

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());
		}
	}