1. 程式人生 > >ssm與activiti整合之全套流程

ssm與activiti整合之全套流程

1.畫流程圖

 

2.使用junit完成流程部署

package cn.itcat.leaveBill;

import javax.annotation.Resource;

import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:conf/applicationContext.xml"})
public class LeaveBill {
	/**
	 * 與流程定義和部署物件相關的service
	 */
	@Resource(name="repositoryService")  
	private RepositoryService repositoryService;
	
	/**
	 * 部署流程定義
	 */
	@Test
	public void deploymentProcssDefiniton() {
		Deployment deploy = repositoryService.createDeployment()					// 建立一個部署物件
			.name("leaveBill請假程式")							// 新增部署名稱
			.addClasspathResource("diagrams/leaveBill.bpmn")	// 從classpath資源中載入,一次只能載入一個檔案
			.addClasspathResource("diagrams/leaveBill.png")
			.deploy();											// 完成部署
		
		System.out.println("部署id:" + deploy.getId());
		System.out.println("部署名稱:" + deploy.getName());
	}
	
	/**
	 * 刪除流程定義
	 */
	@Test
	public void deleteProcessDefinition() {
		String deploymentId = "57501";
		
		repositoryService.deleteDeployment(deploymentId, true);
		System.out.println("刪除成功!");
	}
}

 3.設計main.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	登入成功!歡迎你 ${emp.name}
	<a href="<%=request.getContextPath() %>/myTask">我的任務</a>
	
	<c:if test="${!empty list }">
		<table border="1" align="center">
			<tr>
				<th>ID</th>
				<th>請假天數</th>
				<th>請假事由</th>
				<th>請假原因</th>
				<th>請假時間</th>
				<th>請假人</th>
				<th>請假狀態</th>
				<th>操作</th>
			</tr>
			
			<c:forEach items="${list}" var="leaveBill">
				<tr>
					<td>${leaveBill.id }</td>
					<td>${leaveBill.days }</td>
					<td>${leaveBill.content }</td>
					<td>${leaveBill.remark }</td>
					<td>${leaveBill.leaveDate }</td>
					<td>${leaveBill.name }</td>
					<c:if test="${leaveBill.status == 0}">
						<td>初始錄入</td>
					</c:if>
					<c:if test="${leaveBill.status == 1}">
						<td>審批中</td>
					</c:if>
					<c:if test="${leaveBill.status == 2}">
						<td>審批完成</td>
					</c:if>
					<td>
						<c:if test="${leaveBill.status == 0}">
							<a href="<%=request.getContextPath() %>/saveStartProcess?id=${leaveBill.id }">發起請假申請</a>
						</c:if>
						<c:if test="${leaveBill.status == 2}">
							<a href="<%=request.getContextPath() %>/queryTaskHistory?id=${leaveBill.id }">檢視稽核記錄</a>
						</c:if>
					</td>
				</tr>
			</c:forEach>
		</table>
	</c:if>
</body>
</html>

4.完成saveStartProcess方法

package com.gewb.activiti.controller;

import java.util.List;
import java.util.Map;

import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.gewb.activiti.entity.LeaveBill;
import com.gewb.activiti.entity.Workflow;
import com.gewb.activiti.service.LeaveBillService;
import com.gewb.activiti.utils.SessionContext;

@Controller
public class LeaveBillController {
	@Autowired
	private LeaveBillService leaveBillService;
	
	/**
	 * 啟動流程
	 * @param id
	 * @param map
	 * @return
	 */
	@RequestMapping("/saveStartProcess")
	public String startProcess(Integer id, Map<String, Object> map) {
		leaveBillService.startProcess(id);
		
		List<Map<String, Object>> list = leaveBillService.queryAll();
		
		if(list != null && !list.isEmpty()) {
			map.put("list", list);
		}
		
		return "main";
	}
	
	/**
	 * 查詢我的任務
	 * @param map
	 * @return
	 */
	@RequestMapping("/myTask")
	public String myTask(Map<String, Object> map) {
		String name = SessionContext.get().getName();
		List<Task> list = leaveBillService.myTask(name);
		
		if(list != null && !list.isEmpty()) {
			map.put("list", list);
		}
		
		return "task";
	}
	
	/**
	 * 辦理任務
	 * @param taskId
	 * @param map
	 * @return
	 */
	@RequestMapping("/applyTask")
	public String applyTask(String taskId, Map<String, Object> map) {
		LeaveBill leaveBill = leaveBillService.queryLeaveBillByTaskId(taskId);
		List<String> outcomes = leaveBillService.findOutcomeListByTaskId(taskId);
		List<Comment> commentList = leaveBillService.findCommentsByTaskId(taskId);
		
		map.put("leaveBill", leaveBill);
		map.put("outcomes", outcomes);
		map.put("commentList", commentList);
		map.put("taskId", taskId);
		return "taskForm";
	}
	
	@RequestMapping("/saveSubmitTask")
	public String saveSubmitTask(Workflow workflow, Map<String, Object> map) {
		leaveBillService.saveSubmitTask(workflow);
		
		
		String name = SessionContext.get().getName();
		List<Task> list = leaveBillService.myTask(name);
		
		if(list != null && !list.isEmpty()) {
			map.put("list", list);
		}
		
		return "task";	
	}
	
	
	@RequestMapping("/queryTaskHistory")
	public String queryTaskHistory(Integer id, Map<String, Object> map) {
		LeaveBill leaveBill = leaveBillService.findById(id);
		List<Comment> commentList = leaveBillService.findCommentsById(id);
			
		map.put("leaveBill", leaveBill);
		map.put("commentList", commentList);
		
		return "taskFormHistory";
	}
	
}

5.完成WorkflowServiceImpl

package com.gewb.activiti.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.gewb.activiti.dao.LeaveBillDao;
import com.gewb.activiti.entity.Workflow;
import com.gewb.activiti.service.WorkflowService;
import com.gewb.activiti.utils.SessionContext;

@Service
public class WorkflowServiceImpl implements WorkflowService {
	@Autowired
	private LeaveBillDao leaveBillDao;
	
	/**
	 * 與流程定義和部署物件相關的service
	 */
	@Resource(name="repositoryService")  
	private RepositoryService repositoryService;
	
	/**
	 * 與正在執行的流程例項和執行物件相關的service
	 */
	@Resource(name = "runtimeService")
	private RuntimeService runTimeService;
	
	/**
	 * 與正在執行的任務管理相關的service
	 */
	@Resource(name = "taskService")
	private TaskService taskService;
	
	/**
	 * 與動態表單管理相關的service
	 */
	@Resource(name = "formService")
	private FormService formService;
	
	
	/**
	 * 與歷史任務管理相關的service
	 */
	@Resource(name = "historyService")
	private HistoryService historyService;


	@Override
	@Transactional
	public void saveStartProcess(Integer id) {
		// 更新業務狀態
		leaveBillDao.updateStatus(id);
		
		// 放入審批人
		Map<String, Object> variables = new HashMap<>();
		variables.put("inputUser", SessionContext.get().getName());
		
		String businessKey = "leaveBill." + id;
		variables.put("businessKey", businessKey);
		
		runTimeService.startProcessInstanceByKey("leaveBill", businessKey, variables);
	}


	@Override
	public List<Task> myTask(String userName) {
		return taskService.createTaskQuery().taskAssignee(userName).list();
	}


	@Override
	public Integer queryLeaveBillByTaskId(String taskId) {
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		
		String processInstanceId = task.getProcessInstanceId();
		
		ProcessInstance processInstance = runTimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
		
		String businessKey = processInstance.getBusinessKey();
		
		int leaveBillId = 0;
		if(StringUtils.isNotBlank(businessKey)) {
			leaveBillId = Integer.valueOf(businessKey.split("\\.")[1]);
		}
		
		return leaveBillId;
	}


	@Override
	public List<String> findOutcomeListByTaskId(String taskId) {
		// 得到任務
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		// 得到流程定義id,用來獲取流程定義物件,從而拿到流程定義entity(bpmn檔案)
		String processDefinitionId = task.getProcessDefinitionId();
		// 強轉才能拿到當前活動
		ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processDefinitionId);
		
		String processInstanceId = task.getProcessInstanceId();
		ProcessInstance processInstance = runTimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
		
		String activityId = processInstance.getActivityId();
		// 獲取當前活動
		ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);
		// 獲取當前活動完成後的連線名稱
		List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions();
		// 存放返回連線的名稱
		List<String> list = new ArrayList<>();
		
		if(pvmTransitions != null && !pvmTransitions.isEmpty()) {
			for (PvmTransition pvmTransition : pvmTransitions) {
				String name = (String) pvmTransition.getProperty("name");
				if(StringUtils.isNotBlank(name)) {
					list.add(name);
				} else {
					list.add("預設提交");
				}
			}
		}
		
		return list;
	}
	
	@Override
	@Transactional
	public void saveSubmitTask(Workflow workflow) {
		String taskId = workflow.getTaskId();
		String outcome = workflow.getOutcome();
		String message = workflow.getComment();
		Integer leaveBillId = workflow.getLeaveBillId();
		
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		String processInstanceId = task.getProcessInstanceId();
		
		// 在新增批註時會獲取當前使用者
		Authentication.setAuthenticatedUserId(SessionContext.get().getName());
		// 新增批註
		taskService.addComment(taskId, processInstanceId, message);
		
		Map<String, Object> variables = new HashMap<>();
		if(!outcome.equals("預設提交")) {
			variables.put("outcome", outcome);	
		}
		
		taskService.complete(taskId, variables);
		
		// 判斷流程是否結束
		ProcessInstance processInstance = runTimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
		
		if(processInstance == null) {
			if(outcome.equals("批准")) {
				leaveBillDao.updatePass(leaveBillId);
			}
		}
	}
	
	@Override
	public List<Comment> findCommentsByTaskId(String taskId) {
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		String processInstanceId = task.getProcessInstanceId();
		
//		List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
//				.processInstanceId(processInstanceId).list();
//		
//		List<Comment> list = new ArrayList<>();
//		if(historicTaskInstances != null && !historicTaskInstances.isEmpty()) {
//			for (HistoricTaskInstance historicTaskInstance : historicTaskInstances) {
//				String hTaskId = historicTaskInstance.getId();
//				List<Comment> taskComments = taskService.getTaskComments(hTaskId);
//				list.addAll(taskComments);
//			}
//		}
		
		List<Comment> list2 = taskService.getProcessInstanceComments(processInstanceId);
		
		return list2;
	}


	@Override
	public List<Comment> findCommentsById(Integer id) {
		// 根據businessKey得到流程例項
		HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey("leaveBill." + id).singleResult();
		// 得到流程例項id
		String hisProcessInstanceId = historicProcessInstance.getId();
		
//		List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
//				.processInstanceId(hisProcessInstanceId).list();
//
//		List<Comment> list = new ArrayList<>();
//		if (historicTaskInstances != null && !historicTaskInstances.isEmpty()) {
//			for (HistoricTaskInstance historicTaskInstance : historicTaskInstances) {
//				String hTaskId = historicTaskInstance.getId();
//				List<Comment> taskComments = taskService.getTaskComments(hTaskId);
//				list.addAll(taskComments);
//			}
//		}
		
		List<Comment> list2 = taskService.getProcessInstanceComments(hisProcessInstanceId);
		
		return list2;
	}
	
	
	
	
	
}

6.task.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	
	<c:if test="${!empty list }">
		<table border="1" align="center">
			<tr>
				<th>任務ID</th>
				<th>任務名稱</th>
				<th>建立時間</th>
				<th>辦理人</th>
				<th>操作</th>
			</tr>
			
			<c:forEach items="${list}" var="task">
				<tr>
					<td>${task.id }</td>
					<td>${task.name }</td>
					<td>${task.createTime }</td>
					<td>${task.assignee }</td>
					<td><a href="<%=request.getContextPath() %>/applyTask?taskId=${task.id}">辦理任務</a></td>
				</tr>
			
			</c:forEach>
		
		</table>
	
	</c:if>
	<input type="button" value="返回" onclick="history.go(-1);">
</body>
</html>

7.taskForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	
	<c:if test="${!empty list }">
		<table border="1" align="center">
			<tr>
				<th>任務ID</th>
				<th>任務名稱</th>
				<th>建立時間</th>
				<th>辦理人</th>
				<th>操作</th>
			</tr>
			
			<c:forEach items="${list}" var="task">
				<tr>
					<td>${task.id }</td>
					<td>${task.name }</td>
					<td>${task.createTime }</td>
					<td>${task.assignee }</td>
					<td><a href="<%=request.getContextPath() %>/applyTask?taskId=${task.id}">辦理任務</a></td>
				</tr>
			
			</c:forEach>
		
		</table>
	
	</c:if>
	<input type="button" value="返回" onclick="history.go(-1);">
</body>
</html>

8.taskFormHistory.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
		請假天數:<input type="text" value="${leaveBill.days }" readonly="readonly"><br>
		請假原因:<input type="text" value="${leaveBill.content }" readonly="readonly"><br>
		請假備註:<input type="text" value="${leaveBill.remark }" readonly="readonly"><br>
		
		<input type="button" value="返回" onclick="history.go(-1);">
	
	<br>
	<table border="1" align="center">
		<tr>
			<th>審批時間</th>
			<th>審批人</th>
			<th>批註</th>
		</tr>
		
		<c:forEach items="${commentList}" var="comment">
			<tr>
				<td>${comment.time }</td>
				<td>${comment.userId }</td>
				<td>${comment.fullMessage }</td>
			</tr>
		</c:forEach>
	</table>
	
	
	
	
	
	
</body>
</html>

附:WorkflowEntity

package com.gewb.activiti.entity;

import java.io.Serializable;

public class Workflow implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 4343439094129672355L;

	private String taskId;
	private String outcome;
	private String comment;
	private Integer leaveBillId;
	

	public Integer getLeaveBillId() {
		return leaveBillId;
	}

	public void setLeaveBillId(Integer leaveBillId) {
		this.leaveBillId = leaveBillId;
	}

	public String getTaskId() {
		return taskId;
	}

	public void setTaskId(String taskId) {
		this.taskId = taskId;
	}

	public String getComment() {
		return comment;
	}

	public void setComment(String comment) {
		this.comment = comment;
	}

	public String getOutcome() {
		return outcome;
	}

	public void setOutcome(String outcome) {
		this.outcome = outcome;
	}

}

ManagerTaskHandler

package com.gewb.activiti.utils;

import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.gewb.activiti.entity.Employee;
import com.gewb.activiti.service.EmployeeService;

public class ManagerTaskHandler implements TaskListener {
	/**
	 * 
	 */
	private static final long serialVersionUID = -3772524270306357666L;
	
	@Override
	public void notify(DelegateTask delegateTask) {
		
		Employee employee = SessionContext.get();
		WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(ContextLoader.getCurrentWebApplicationContext().getServletContext());
		EmployeeService employeeService = (EmployeeService) applicationContext.getBean("employeeServiceImpl");
		String managerName = employeeService.queryManagerName(employee);
		
		delegateTask.setAssignee(managerName);

	}


}

SessionContext

package com.gewb.activiti.utils;

import javax.servlet.http.HttpSession;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.gewb.activiti.entity.Employee;

public class SessionContext {
	
	public static Employee get() {
		HttpSession session = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession();
		Employee emp = (Employee)session.getAttribute("emp");
		
		return emp;
	}
	
	public static void set(Employee emp) {
		if (emp != null) {
			((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession().setAttribute("emp", emp);
		} else {
			((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession().removeAttribute("emp");
		}
	}
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

	<context:component-scan base-package="com.gewb.activiti">
		<context:exclude-filter type="annotation"
			expression="org.springframework.stereotype.Controller" />
		<context:exclude-filter type="annotation"
			expression="org.springframework.web.bind.annotation.ControllerAdvice" />
	</context:component-scan>

	<!-- 配置資料來源, 整合其他框架, 事務等. -->
	<!-- 配置資料來源 -->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
		<property name="url" value="jdbc:mysql://localhost:3306/activiti?serverTimezone=GMT%2B8&amp;useSSL=false"></property>
		<property name="username" value="root"></property>
		<property name="password" value="root"></property>
	</bean>

	<!-- 載入Mybatis配置檔案,掃描所有mapping檔案檔案 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:conf/mybatis-config.xml" />
		<property name="mapperLocations" value="classpath:mappers/*Mapper.xml" />
	</bean>

	<!-- DAO介面所在包名,spring會自動查詢其下的類 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.gewb.activiti.dao"></property>
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
	</bean>

	<!-- TransactionMnager -->
	<bean id="txManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<!-- 使用Spring的註解事務 -->
	<tx:annotation-driven transaction-manager="txManager" />


	<!-- activiti -->
	<!-- 配置流程引擎檔案 -->
	<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
		<property name="dataSource" ref="dataSource" />
		<property name="transactionManager" ref="txManager" />
		<!-- 是否自動建立23張表 -->
		<property name="databaseSchemaUpdate" value="true" />
	</bean>

	<!-- 配置建立流程引擎物件 -->
	<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
		<property name="processEngineConfiguration" ref="processEngineConfiguration" />
	</bean>

	<!-- 提供靜態方法service -->
	<bean id="repositoryService" factory-bean="processEngine"
		factory-method="getRepositoryService" />
	<bean id="runtimeService" factory-bean="processEngine"
		factory-method="getRuntimeService" />
	<bean id="taskService" factory-bean="processEngine"
		factory-method="getTaskService" />
	<bean id="historyService" factory-bean="processEngine"
		factory-method="getHistoryService" />
	<bean id="formService" factory-bean="processEngine"
		factory-method="getFormService" />

</beans>

springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

	<!--  
		需要進行 Spring 整合 SpringMVC 嗎 ?
		還是否需要再加入 Spring 的 IOC 容器 ?
		是否需要再 web.xml 檔案中配置啟動 Spring IOC 容器的 ContextLoaderListener ?
		
		1. 需要: 通常情況下, 類似於資料來源, 事務, 整合其他框架都是放在 Spring 的配置檔案中(而不是放在 SpringMVC 的配置檔案中).
		實際上放入 Spring 配置檔案對應的 IOC 容器中的還有 Service 和 Dao. 
		2. 不需要: 都放在 SpringMVC 的配置檔案中. 也可以分多個 Spring 的配置檔案, 然後使用 import 節點匯入其他的配置檔案
	-->
	
	<!--  
		問題: 若 Spring 的 IOC 容器和 SpringMVC 的 IOC 容器掃描的包有重合的部分, 就會導致有的 bean 會被建立 2 次.
		解決:
		1. 使 Spring 的 IOC 容器掃描的包和 SpringMVC 的 IOC 容器掃描的包沒有重合的部分. 
		2. 使用 exclude-filter 和 include-filter 子節點來規定只能掃描的註解
	-->
	
	<!--  
		SpringMVC 的 IOC 容器中的 bean 可以來引用 Spring IOC 容器中的 bean. 
		返回來呢 ? 反之則不行. Spring IOC 容器中的 bean 卻不能來引用 SpringMVC IOC 容器中的 bean!
	-->
	
	<context:component-scan base-package="com.gewb.activiti" use-default-filters="false">
		<context:include-filter type="annotation" 
			expression="org.springframework.stereotype.Controller"/>
		<context:include-filter type="annotation" 
			expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
	</context:component-scan>

	<!-- 配置檢視解析器 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
	
	<mvc:default-servlet-handler/>
	<mvc:annotation-driven></mvc:annotation-driven>
	
	<!-- 配置MultipartResolver -->
	<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<property name="defaultEncoding" value="UTF-8"></property>
		<property name="maxUploadSize" value="1024000"></property>
	</bean>
	
	
	
</beans>

================================================================================================

另附上檢視流程圖程式碼,基於ssh

task.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="/js/commons.jspf" %>
<%@taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>任務管理</title>
</head>
<body>
	<table width="100%" border="0" align="center" cellpadding="0" cellspacing="0">
		  <tr>
		    <td height="30"><table width="100%" border="0" cellspacing="0" cellpadding="0">
		      <tr>
		        <td height="24" bgcolor="#353c44"><table width="100%" border="0" cellspacing="0" cellpadding="0">
		          <tr>
		            <td><table width="100%" border="0" cellspacing="0" cellpadding="0">
		              <tr>
		                <td width="6%" height="19" valign="bottom"><div align="center"><img src="${pageContext.request.contextPath }/images/tb.gif" width="14" height="14" /></div></td>
		                <td width="94%" valign="bottom"><span class="STYLE1">個人任務管理列表</span></td>
		              </tr>
		            </table></td>
		            <td><div align="right"><span class="STYLE1">
		              </span></div></td>
		          </tr>
		        </table></td>
		      </tr>
		    </table></td>
		  </tr>
		  <tr>
		    <td><table width="100%" border="0" cellpadding="0" cellspacing="1" bgcolor="#a8c7ce" onmouseover="changeto()"  onmouseout="changeback()">
		      <tr>
		        <td width="15%" height="20" bgcolor="d3eaef" class="STYLE6"><div align="center"><span class="STYLE10">任務ID</span></div></td>
		        <td width="25%" height="20" bgcolor="d3eaef" class="STYLE6"><div align="center"><span class="STYLE10">任務名稱</span></div></td>
		        <td width="20%" height="20" bgcolor="d3eaef" class="STYLE6"><div align="center"><span class="STYLE10">建立時間</span></div></td>
		        <td width="20%" height="20" bgcolor="d3eaef" class="STYLE6"><div align="center"><span class="STYLE10">辦理人</span></div></td>
		        <td width="20%" height="20" bgcolor="d3eaef" class="STYLE6"><div align="center"><span class="STYLE10">操作</span></div></td>
		      </tr>
		      <s:if test="#list!=null && #list.size()>0">
		      	<s:iterator value="#list">
		      		<tr>
				        <td height="20" bgcolor="#FFFFFF" class="STYLE6"><div align="center"><s:property value="id"/></div></td>
				        <td height="20" bgcolor="#FFFFFF" class="STYLE19"><div align="center"><s:property value="name"/></div></td>
				        <td height="20" bgcolor="#FFFFFF" class="STYLE19"><div align="center"><s:date name="createTime" format="yyyy-MM-dd HH:mm:ss"/></div></td>
				        <td height="20" bgcolor="#FFFFFF" class="STYLE19"><div align="center"><s:property value="assignee"/></div></td>
				        <td height="20" bgcolor="#FFFFFF"><div align="center" class="STYLE21">
				        	<a href="${pageContext.request.contextPath }/workflowAction_viewTaskForm.action?taskId=<s:property value="id"/>">辦理任務</a>
							<a target="_blank" href="workflowAction_viewCurrentImage.action?taskId=<s:property value="id"/>">檢視當前流程圖</a>
				        </div></td>
				    </tr> 
		      	</s:iterator>
		      </s:if>
		        
		      
		    </table></td>
		  </tr>
	</table>
</body>
</html>

image.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>檢視當前流程圖</title>
</head>
<body>
<!-- 1.獲取到規則流程圖 -->
<img style="position: absolute;top: 0px;left: 0px;" src="workflowAction_viewImage?deploymentId=<s:property value='#deploymentId'/>&imageName=<s:property value='#imageName'/>">

<!-- 2.根據當前活動的座標,動態繪製DIV -->
<div style="position: absolute;border:1px solid red;top:<s:property value="#acs.y"/>px;left: <s:property value="#acs.x"/>px;width: <s:property value="#acs.width"/>px;height:<s:property value="#acs.height"/>px;   "></div></body>
</html>

WorkflowAction.java

package cn.itcast.ssh.web.action;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.struts2.ServletActionContext;

import cn.itcast.ssh.domain.LeaveBill;
import cn.itcast.ssh.service.ILeaveBillService;
import cn.itcast.ssh.service.IWorkflowService;
import cn.itcast.ssh.utils.SessionContext;
import cn.itcast.ssh.utils.ValueContext;
import cn.itcast.ssh.web.form.WorkflowBean;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

@SuppressWarnings("serial")
public class WorkflowAction extends ActionSupport implements ModelDriven<WorkflowBean> {

	private WorkflowBean workflowBean = new WorkflowBean();
	
	@Override
	public WorkflowBean getModel() {
		return workflowBean;
	}
	
	private IWorkflowService workflowService;
	
	private ILeaveBillService leaveBillService;

	public void setLeaveBillService(ILeaveBillService leaveBillService) {
		this.leaveBillService = leaveBillService;
	}

	public void setWorkflowService(IWorkflowService workflowService) {
		this.workflowService = workflowService;
	}

	/**
	 * 部署管理首頁顯示
	 * @return
	 */
	public String deployHome(){
		//1:查詢部署物件資訊,對應表(act_re_deployment)
		List<Deployment> depList = workflowService.findDeploymentList();
		//2:查詢流程定義的資訊,對應表(act_re_procdef)
		List<ProcessDefinition> pdList = workflowService.findProcessDefinitionList();
		//放置到上下文物件中
		ValueContext.putValueContext("depList", depList);
		ValueContext.putValueContext("pdList", pdList);
		return "deployHome";
	}
	
	/**
	 * 釋出流程
	 * @return
	 */
	public String newdeploy(){
		//獲取頁面傳遞的值
		//1:獲取頁面上傳遞的zip格式的檔案,格式是File型別
		File file = workflowBean.getFile();
		//檔名稱
		String filename = workflowBean.getFilename();
		//完成部署
		workflowService.saveNewDeploye(file,filename);
		return "list";
	}
	
	/**
	 * 刪除部署資訊
	 */
	public String delDeployment(){
		//1:獲取部署物件ID
		String deploymentId = workflowBean.getDeploymentId();
		//2:使用部署物件ID,刪除流程定義
		workflowService.deleteProcessDefinitionByDeploymentId(deploymentId);
		return "list";
	}
	
	/**
	 * 檢視流程圖
	 * @throws Exception 
	 */
	public String viewImage() throws Exception{
		//1:獲取頁面傳遞的部署物件ID和資源圖片名稱
		//部署物件ID
		String deploymentId = workflowBean.getDeploymentId();
		//資源圖片名稱
		String imageName = workflowBean.getImageName();
		//2:獲取資原始檔表(act_ge_bytearray)中資源圖片輸入流InputStream
		InputStream in = workflowService.findImageInputStream(deploymentId,imageName);
		//3:從response物件獲取輸出流
		OutputStream out = ServletActionContext.getResponse().getOutputStream();
		//4:將輸入流中的資料讀取出來,寫到輸出流中
		for(int b=-1;(b=in.read())!=-1;){
			out.write(b);
		}
		out.close();
		in.close();
		//將圖寫到頁面上,用輸出流寫
		return null;
	}
	
	// 啟動流程
	public String startProcess(){
		//更新請假狀態,啟動流程例項,讓啟動的流程例項關聯業務
		workflowService.saveStartProcess(workflowBean);
		return "listTask";
	}
	
	
	
	/**
	 * 任務管理首頁顯示
	 * @return
	 */
	public String listTask(){
		//1:從Session中獲取當前使用者名稱
		String name = SessionContext.get().getName();
		//2:使用當前使用者名稱查詢正在執行的任務表,獲取當前任務的集合List<Task>
		List<Task> list = workflowService.findTaskListByName(name); 
		ValueContext.putValueContext("list", list);
		return "task";
	}
	
	/**
	 * 開啟任務表單
	 */
	public String viewTaskForm(){
		//任務ID
		String taskId = workflowBean.getTaskId();
		//獲取任務表單中任務節點的url連線
		String url = workflowService.findTaskFormKeyByTaskId(taskId);
		url += "?taskId="+taskId;
		ValueContext.putValueContext("url", url);
		return "viewTaskForm";
	}
	
	// 準備表單資料
	public String audit(){
		//獲取任務ID
		String taskId = workflowBean.getTaskId();
		/**一:使用任務ID,查詢請假單ID,從而獲取請假單資訊*/
		LeaveBill leaveBill = workflowService.findLeaveBillByTaskId(taskId);
		ValueContext.putValueStack(leaveBill);
		/**二:已知任務ID,查詢ProcessDefinitionEntiy物件,從而獲取當前任務完成之後的連線名稱,並放置到List<String>集合中*/
		List<String> outcomeList = workflowService.findOutComeListByTaskId(taskId);
		ValueContext.putValueContext("outcomeList", outcomeList);
		/**三:查詢所有歷史稽核人的稽核資訊,幫助當前人完成稽核,返回List<Comment>*/
		List<Comment> commentList = workflowService.findCommentByTaskId(taskId);
		ValueContext.putValueContext("commentList", commentList);
		return "taskForm";
	}
	
	/**
	 * 提交任務
	 */
	public String submitTask(){
		workflowService.saveSubmitTask(workflowBean);
		return "listTask";
	}
	
	/**
	 * 檢視當前流程圖(檢視當前活動節點,並使用紅色的框標註)
	 */
	public String viewCurrentImage(){
		//任務ID
		String taskId = workflowBean.getTaskId();
		/**一:檢視流程圖*/
		//1:獲取任務ID,獲取任務物件,使用任務物件獲取流程定義ID,查詢流程定義物件
		ProcessDefinition pd = workflowService.findProcessDefinitionByTaskId(taskId);
		//workflowAction_viewImage?deploymentId=<s:property value='#deploymentId'/>&imageName=<s:property value='#imageName'/>
		ValueContext.putValueContext("deploymentId", pd.getDeploymentId());
		ValueContext.putValueContext("imageName", pd.getDiagramResourceName());
		/**二:檢視當前活動,獲取當期活動對應的座標x,y,width,height,將4個值存放到Map<String,Object>中*/
		Map<String, Object> map = workflowService.findCoordingByTask(taskId);
		ValueContext.putValueContext("acs", map);
		return "image";
	}
	
	// 檢視歷史的批註資訊
	public String viewHisComment(){
		//獲取清單ID
		Long id = workflowBean.getId();
		//1:使用請假單ID,查詢請假單物件,將物件放置到棧頂,支援表單回顯
		LeaveBill leaveBill = leaveBillService.findLeaveBillById(id);
		ValueContext.putValueStack(leaveBill);
		//2:使用請假單ID,查詢歷史的批註資訊
		List<Comment> commentList = workflowService.findCommentByLeaveBillId(id);
		ValueContext.putValueContext("commentList", commentList);
		return "viewHisComment";
	}
}

WorkflowServiceImpl.java

package cn.itcast.ssh.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang.StringUtils;

import cn.itcast.ssh.dao.ILeaveBillDao;
import cn.itcast.ssh.domain.LeaveBill;
import cn.itcast.ssh.service.IWorkflowService;
import cn.itcast.ssh.utils.SessionContext;
import cn.itcast.ssh.web.form.WorkflowBean;

public class WorkflowServiceImpl implements IWorkflowService {
	/**請假申請Dao*/
	private ILeaveBillDao leaveBillDao;
	
	private RepositoryService repositoryService;
	
	private RuntimeService runtimeService;
	
	private TaskService taskService;
	
	private FormService formService;
	
	private HistoryService historyService;

	public void setLeaveBillDao(ILeaveBillDao leaveBillDao) {
		this.leaveBillDao = leaveBillDao;
	}

	public void setHistoryService(HistoryService historyService) {
		this.historyService = historyService;
	}
	
	public void setFormService(FormService formService) {
		this.formService = formService;
	}
	
	public void setRuntimeService(RuntimeService runtimeService) {
		this.runtimeService = runtimeService;
	}
	public void setTaskService(TaskService taskService) {
		this.taskService = taskService;
	}

	public void setRepositoryService(RepositoryService repositoryService) {
		this.repositoryService = repositoryService;
	}

	/**部署流程定義*/
	@Override
	public void saveNewDeploye(File file, String filename) {
		try {
			//2:將File型別的檔案轉化成ZipInputStream流
			ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(file));
			repositoryService.createDeployment()//建立部署物件
							.name(filename)//新增部署名稱
							.addZipInputStream(zipInputStream)//
							.deploy();//完成部署
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**查詢部署物件資訊,對應表(act_re_deployment)*/
	@Override
	public List<Deployment> findDeploymentList() {
		List<Deployment> list = repositoryService.createDeploymentQuery()//建立部署物件查詢
							.orderByDeploymenTime().asc()//
							.list();
		return list;
	}
	
	/**查詢流程定義的資訊,對應表(act_re_procdef)*/
	@Override
	public List<ProcessDefinition> findProcessDefinitionList() {
		List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()//建立流程定義查詢
							.orderByProcessDefinitionVersion().asc()//
							.list();
		return list;
	}
	
	/**使用部署物件ID和資源圖片名稱,獲取圖片的輸入流*/
	@Override
	public InputStream findImageInputStream(String deploymentId,
			String imageName) {
		return repositoryService.getResourceAsStream(deploymentId, imageName);
	}
	
	/**使用部署物件ID,刪除流程定義*/
	@Override
	public void deleteProcessDefinitionByDeploymentId(String deploymentId) {
		repositoryService.deleteDeployment(deploymentId, true);
	}
	
	/**更新請假狀態,啟動流程例項,讓啟動的流程例項關聯業務*/
	@Override
	public void saveStartProcess(WorkflowBean workflowBean) {
		//1:獲取請假單ID,使用請假單ID,查詢請假單的物件LeaveBill
		Long id = workflowBean.getId();
		LeaveBill leaveBill = leaveBillDao.findLeaveBillById(id);
		//2:更新請假單的請假狀態從0變成1(初始錄入-->稽核中)
		leaveBill.setState(1);
		//3:使用當前物件獲取到流程定義的key(物件的名稱就是流程定義的key)
		String key = leaveBill.getClass().getSimpleName();
		/**
		 * 4:從Session中獲取當前任務的辦理人,使用流程變數設定下一個任務的辦理人
			    * inputUser是流程變數的名稱,
			    * 獲取的辦理人是流程變數的值
		 */
		Map<String, Object> variables = new HashMap<String,Object>();
		variables.put("inputUser", SessionContext.get().getName());//表示惟一使用者
		/**
		 * 5:	(1)使用流程變數設定字串(格式:LeaveBill.id的形式),通過設定,讓啟動的流程(流程例項)關聯業務
   				(2)使用正在執行物件表中的一個欄位BUSINESS_KEY(Activiti提供的一個欄位),讓啟動的流程(流程例項)關聯業務
		 */
		//格式:LeaveBill.id的形式(使用流程變數)
		String objId = key+"."+id;
		variables.put("objId", objId);
		//6:使用流程定義的key,啟動流程例項,同時設定流程變數,同時向正在執行的執行物件表中的欄位BUSINESS_KEY新增業務資料,同時讓流程關聯業務
		runtimeService.startProcessInstanceByKey(key,objId,variables);
		
	}
	
	/**2:使用當前使用者名稱查詢正在執行的任務表,獲取當前任務的集合List<Task>*/
	@Override
	public List<Task> findTaskListByName(String name) {
		List<Task> list = taskService.createTaskQuery()//
					.taskAssignee(name)//指定個人任務查詢
					.orderByTaskCreateTime().asc()//
					.list();
		return list;
	}
	
	/**使用任務ID,獲取當前任務節點中對應的Form key中的連線的值*/
	@Override
	public String findTaskFormKeyByTaskId(String taskId) {
		TaskFormData formData = formService.getTaskFormData(taskId);
		//獲取Form key的值
		String url = formData.getFormKey();
		return url;
	}
	
	/**一:使用任務ID,查詢請假單ID,從而獲取請假單資訊*/
	@Override
	public LeaveBill findLeaveBillByTaskId(String taskId) {
		//1:使用任務ID,查詢任務物件Task
		Task task = taskService.createTaskQuery()//
						.taskId(taskId)//使用任務ID查詢
						.singleResult();
		//2:使用任務物件Task獲取流程例項ID
		String processInstanceId = task.getProcessInstanceId();
		//3:使用流程例項ID,查詢正在執行的執行物件表,返回流程例項物件
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
						.processInstanceId(processInstanceId)//使用流程例項ID查詢
						.singleResult();
		//4:使用流程例項物件獲取BUSINESS_KEY
		String buniness_key = pi.getBusinessKey();
		//5:獲取BUSINESS_KEY對應的主鍵ID,使用主鍵ID,查詢請假單物件(LeaveBill.1)
		String id = "";
		if(StringUtils.isNotBlank(buniness_key)){
			//擷取字串,取buniness_key小數點的第2個值
			id = buniness_key.split("\\.")[1];
		}
		//查詢請假單物件
		//使用hql語句:from LeaveBill o where o.id=1
		LeaveBill leaveBill = leaveBillDao.findLeaveBillById(Long.parseLong(id));
		return leaveBill;
	}
	
	/**二:已知任務ID,查詢ProcessDefinitionEntiy物件,從而獲取當前任務完成之後的連線名稱,並放置到List<String>集合中*/
	@Override
	public List<String> findOutComeListByTaskId(String taskId) {
		//返回存放連線的名稱集合
		List<String> list = new ArrayList<String>();
		//1:使用任務ID,查詢任務物件
		Task task = taskService.createTaskQuery()//
					.taskId(taskId)//使用任務ID查詢
					.singleResult();
		//2:獲取流程定義ID
		String processDefinitionId = task.getProcessDefinitionId();
		//3:查詢ProcessDefinitionEntiy物件
		ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
		//使用任務物件Task獲取流程例項ID
		String processInstanceId = task.getProcessInstanceId();
		//使用流程例項ID,查詢正在執行的執行物件表,返回流程例項物件
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
					.processInstanceId(processInstanceId)//使用流程例項ID查詢
					.singleResult();
		//獲取當前活動的id
		String activityId = pi.getActivityId();
		//4:獲取當前的活動
		ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);
		//5:獲取當前活動完成之後連線的名稱
		List<PvmTransition> pvmList = activityImpl.getOutgoingTransitions();
		if(pvmList!=null && pvmList.size()>0){
			for(PvmTransition pvm:pvmList){
				String name = (String) pvm.getProperty("name");
				if(StringUtils.isNotBlank(name)){
					list.add(name);
				}
				else{
					list.add("預設提交");
				}
			}
		}
		return list;
	}
	
	/**指定連線的名稱完成任務*/
	@Override
	public void saveSubmitTask(WorkflowBean workflowBean) {
		//獲取任務ID
		String taskId = workflowBean.getTaskId();
		//獲取連線的名稱
		String outcome = workflowBean.getOutcome();
		//批註資訊
		String message = workflowBean.getComment();
		//獲取請假單ID
		Long id = workflowBean.getId();
		
		/**
		 * 1:在完成之前,新增一個批註資訊,向act_hi_comment表中新增資料,用於記錄對當前申請人的一些稽核資訊
		 */
		//使用任務ID,查詢任務物件,獲取流程流程例項ID
		Task task = taskService.createTaskQuery()//
						.taskId(taskId)//使用任務ID查詢
						.singleResult();
		//獲取流程例項ID
		String processInstanceId = task.getProcessInstanceId();
		/**
		 * 注意:新增批註的時候,由於Activiti底層程式碼是使用:
		 * 		String userId = Authentication.getAuthenticatedUserId();
			    CommentEntity comment = new CommentEntity();
			    comment.setUserId(userId);
			  所有需要從Session中獲取當前登入人,作為該任務的辦理人(稽核人),對應act_hi_comment表中的User_ID的欄位,不過不新增稽核人,該欄位為null
			 所以要求,新增配置執行使用Authentication.setAuthenticatedUserId();添加當前任務的稽核人
		 * */
		Authentication.setAuthenticatedUserId(SessionContext.get().getName());
		taskService.addComment(taskId, processInstanceId, message);
		/**
		 * 2:如果連線的名稱是“預設提交”,那麼就不需要設定,如果不是,就需要設定流程變數
		 * 在完成任務之前,設定流程變數,按照連線的名稱,去完成任務
				 流程變數的名稱:outcome
				 流程變數的值:連線的名稱
		 */
		Map<String, Object> variables = new HashMap<String,Object>();
		if(outcome!=null && !outcome.equals("預設提交")){
			variables.put("outcome", outcome);
		}

		//3:使用任務ID,完成當前人的個人任務,同時流程變數
		taskService.complete(taskId, variables);
		//4:當任務完成之後,需要指定下一個任務的辦理人(使用類)-----已經開發完成
		
		/**
		 * 5:在完成任務之後,判斷流程是否結束
   			如果流程結束了,更新請假單表的狀態從1變成2(稽核中-->稽核完成)
		 */
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
						.processInstanceId(processInstanceId)//使用流程例項ID查詢
						.singleResult();
		//流程結束了
		if(pi==null){
			//更新請假單表的狀態從1變成2(稽核中-->稽核完成)
			LeaveBill bill = leaveBillDao.findLeaveBillById(id);
			bill.setState(2);
		}
	}
	
	/**獲取批註資訊,傳遞的是當前任務ID,獲取歷史任務ID對應的批註*/
	@Override
	public List<Comment> findCommentByTaskId(String taskId) {
		List<Comment> list = new ArrayList<Comment>();
		//使用當前的任務ID,查詢當前流程對應的歷史任務ID
		//使用當前任務ID,獲取當前任務物件
		Task task = taskService.createTaskQuery()//
				.taskId(taskId)//使用任務ID查詢
				.singleResult();
		//獲取流程例項ID
		String processInstanceId = task.getProcessInstanceId();
//		//使用流程例項ID,查詢歷史任務,獲取歷史任務對應的每個任務ID
//		List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()//歷史任務表查詢
//						.processInstanceId(processInstanceId)//使用流程例項ID查詢
//						.list();
//		//遍歷集合,獲取每個任務ID
//		if(htiList!=null && htiList.size()>0){
//			for(HistoricTaskInstance hti:htiList){
//				//任務ID
//				String htaskId = hti.getId();
//				//獲取批註資訊
//				List<Comment> taskList = taskService.getTaskComments(htaskId);//對用歷史完成後的任務ID
//				list.addAll(taskList);
//			}
//		}
		list = taskService.getProcessInstanceComments(processInstanceId);
		return list;
	}
	
	/**使用請假單ID,查詢歷史批註資訊*/
	@Override
	public List<Comment> findCommentByLeaveBillId(Long id) {
		//使用請假單ID,查詢請假單物件
		LeaveBill leaveBill = leaveBillDao.findLeaveBillById(id);
		//獲取物件的名稱
		String objectName = leaveBill.getClass().getSimpleName();
		//組織流程表中的欄位中的值
		String objId = objectName+"."+id;
		
		/**1:使用歷史的流程例項查詢,返回歷史的流程例項物件,獲取流程例項ID*/
//		HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery()//對應歷史的流程例項表
//						.processInstanceBusinessKey(objId)//使用BusinessKey欄位查詢
//						.singleResult();
//		//流程例項ID
//		String processInstanceId = hpi.getId();
		/**2:使用歷史的流程變數查詢,返回歷史的流程變數的物件,獲取流程例項ID*/
		HistoricVariableInstance hvi = historyService.createHistoricVariableInstanceQuery()//對應歷史的流程變量表
						.variableValueEquals("objId", objId)//使用流程變數的名稱和流程變數的值查詢
						.singleResult();
		//流程例項ID
		String processInstanceId = hvi.getProcessInstanceId();
		List<Comment> list = taskService.getProcessInstanceComments(processInstanceId);
		return list;
	}
	
	/**1:獲取任務ID,獲取任務物件,使用任務物件獲取流程定義ID,查詢流程定義物件*/
	@Override
	public ProcessDefinition findProcessDefinitionByTaskId(String taskId) {
		//使用任務ID,查詢任務物件
		Task task = taskService.createTaskQuery()//
					.taskId(taskId)//使用任務ID查詢
					.singleResult();
		//獲取流程定義ID
		String processDefinitionId = task.getProcessDefinitionId();
		//查詢流程定義的物件
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()//建立流程定義查詢物件,對應表act_re_procdef 
					.processDefinitionId(processDefinitionId)//使用流程定義ID查詢
					.singleResult();
		return pd;
	}
	
	/**
	 * 二:檢視當前活動,獲取當期活動對應的座標x,y,width,height,將4個值存放到Map<String,Object>中
		 map集合的key:表示座標x,y,width,height
		 map集合的value:表示座標對應的值
	 */
	@Override
	public Map<String, Object> findCoordingByTask(String taskId) {
		//存放座標
		Map<String, Object> map = new HashMap<String,Object>();
		//使用任務ID,查詢任務物件
		Task task = taskService.createTaskQuery()//
					.taskId(taskId)//使用任務ID查詢
					.singleResult();
		//獲取流程定義的ID
		String processDefinitionId = task.getProcessDefinitionId();
		//獲取流程定義的實體物件(對應.bpmn檔案中的資料)
		ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processDefinitionId);
		//流程例項ID
		String processInstanceId = task.getProcessInstanceId();
		//使用流程例項ID,查詢正在執行的執行物件表,獲取當前活動對應的流程例項物件
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//建立流程例項查詢
					.processInstanceId(processInstanceId)//使用流程例項ID查詢
					.singleResult();
		//獲取當前活動的ID
		String activityId = pi.getActivityId();
		//獲取當前活動物件
		ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);//活動ID
		//獲取座標
		map.put("x", activityImpl.getX());
		map.put("y", activityImpl.getY());
		map.put("width", activityImpl.getWidth());
		map.put("height", activityImpl.getHeight());
		return map;
	}
}