工作流之第八篇:流程任务管理

任务的创建与删除
一般情况下,可以通过定义流程描述XML文件来定义一个任务,Activiti在解析该文件时,会将任务写到对应的数据表(ACT_RU_TASK)中。在此过程中,创建任务的工作已由Activiti完成了。如果需要使用任务数据,则可以调用相应查询的API查询任务数据并且进行相应的设置。下面将讲解如何使用XML文件定义任务,以及如何使用TaskService提供的API来保存和删除任务数据。
Task接口
一个Task实例表示流程中的一个任务,与其他实例一样,Task是一个接口,并且遵守数据映射实体的命名规范。Task的实现类为TaskEntityImpl,对应的数据库表为ACT_RU_TASK。TaskEntityImpl包括以下映射属性。
➢ id:主键,对应 ID_字段。
➢ revision:该数据版本号,对应REV_字段。
➢ owner:任务拥有人,对应OWNER_字段。
➢ assignee:被指定需要执行任务的人,对应ASSIGNEE_字段。
➢ delegationState:任务被委派的状态,对应DELEGATION_字段。
➢ parentTaskId:父任务的 ID(如果本身是子任务的话),对应 PARENT_TASK_ID_字段。
➢ name:任务名称,对应NAME_字段。
➢ description:任务的描述信息,对应DESCRIPTION_字段。
➢ priority:任务的优先级,默认值为50,表示正常状态,对应PRIORITY_字段。
➢ createTime:任务创建时间,对应CREATE_TIME_字段。
➢ dueDate:预订日期,对应DUE_DATE_字段。
➢ executionId:该任务对应的执行流ID,对应EXECUTION_ID_字段。
➢ processDefinitionId:任务对应的流程定义ID,对应PROC_DEF_ID_字段。
➢ claimTime:任务的提醒时间,对应CLAIM_TIME_字段。
创建与保存Task实例
与创建用户组实例(Group)、用户实例(User)一样,TaskService提供了创建Task实例的方法。调用TaskService的newTask()与newTask(String taskId)方法,可以获取一个Task实例,开发人员不需要关心Task的创建细节。调用这两个创建Task实例的方法时,TaskService会初始化Task的部分属性,这些属性包括taskId、创建时间等。
创建了Task实例后,如果需要将其保存到数据库中,则可以使用TaskService的saveTask(Task task)方法,如果保存的Task实例有ID值,则会使用该值作为Task数据的主键,没有的话,则由Activiti为其生成主键。
搭建环境

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/act" />
		<property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
		<property name="jdbcUsername" value="root" />
		<property name="jdbcPassword" value="root" />
		<property name="databaseSchemaUpdate" value="drop-create"/>
	</bean>

</beans>

log4j.properties

# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=INFO, CONSOLE
#log4j.rootCategory=INFO, CONSOLE, LOGFILE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1} - %m%n


log4j.logger.org.apache=INFO
log4j.logger.org.htmlparser=INFO
log4j.logger.org.htmlparser=INFO
log4j.logger.com.angus=INFO

测试类

package org.crazyit.activiti;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.task.Task;

public class NewTask {

	public static void main(String[] args) {
		// 创建流程引擎
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		//获取任务服务组件
		TaskService taskService = engine.getTaskService();
		//保存第一个Task,不设置ID
		Task task1 = taskService.newTask();
		taskService.saveTask(task1);
		//保存第二个Task,设置ID
		Task task2 = taskService.newTask("审核任务");
		taskService.saveTask(task2);
	}

}

删除任务
TaskService提供了多个删除任务的方法,包括删除单个任务、删除多个任务的方法。这些删除方法与7.4.5节中讲述的删除部署资源的方法类似,同样由开发人员决定是否进行级联删除。这些删除方法描述如下。
➢ deleteTask(String taskId):根据Task的ID删除Task数据,调用该方法不会进行级联删除。
➢ deleteTask(String taskId,boolean cascade):根据Task的ID删除Task数据,由调用者决定是否进行级联删除。
➢ deleteTasks(CollectiontaskIds):提供多个Task的ID进行多条数据删除,调用该方法不会进行级联删除。
➢ deleteTasks(CollectiontaskIds,boolean cascade):提供多个Task的ID进行多条数据删除,由调用者决定是否进行级联删除。
删除任务时,将会删除该任务下面全部的子任务和该任务本身,如果设置了进行级联删除,则会删除与该任务相关的全部历史数据(ACT_HI_TASKINST表)和子任务,如果不进行级联删除,则会使用历史数据将该任务设置为结束状态。除此之外,如果尝试删除一条不存的任务数据(提供不存在的taskId),此时deleteTask方法会到历史数据表中查询是否存在该任务相关的历史数据,如果存在则删除,不存在则忽略

package org.crazyit.activiti;

import java.util.ArrayList;
import java.util.List;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;

public class DeleteTask {

	public static void main(String[] args) {
		// 创建流程引擎
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		//获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 保存若干个Task
		for (int i = 1; i < 10; i++) {
			saveTask(taskService, String.valueOf(i));
		}	
		// 删除task(不包括历史数据和子任务)
		taskService.deleteTask("1");
		// 删除task(包括历史数据和子任务)
		taskService.deleteTask("2", true);
		// 删除多个task(不包括历史数据和子任务)
		List<String> ids = new ArrayList<String>();
		ids.add("3");
		ids.add("4");
		taskService.deleteTasks(ids);
		//删除多个task(包括历史数据和子任务)
		ids = new ArrayList<String>();
		ids.add("5");
		ids.add("6");
		taskService.deleteTasks(ids, true);
		// 再删除ID为3的task,此时3的历史数据也会被删除(如果有的话)
		taskService.deleteTask("3", true);
	}
	
	//保存一个task
	static void saveTask(TaskService taskService, String id) {
		Task task1 = taskService.newTask(id);
		taskService.saveTask(task1);
	}

}

任务权限
Activiti提供了设置任务权限数据的API,通过调用这些API,可以为任务设置角色的权限数据,这些角色包括用户组与用户。
设置候选用户组
流程定义与用户组(或者用户)之间的权限数据,通过一个ACT_RU_IDENTITYLINK中间表来保存,该表对应的实体为IdentityLink对象(详情请见7.3.3节)。任务的权限数据设置与之类似,也是使用ACT_RU_IDENTITYLINK表来保存这些权限数据,因此在调用设置流程权限API时,Activiti最终会往这个表中写入数据。

package org.crazyit.activiti;

import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.identity.Group;
import org.activiti.engine.task.Task;

/**
 * 使用addCandidateGroup方法
 * @author yangenxiong
 *
 */
public class AddCandidateGroup {

	public static void main(String[] args) {
		//获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取身份服务组件
		IdentityService identityService = engine.getIdentityService();
		// 新建用户组
		Group groupA = createGroup(identityService, "group1", "经理组", "manager");
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		//保存第一个Task
		Task task1 = taskService.newTask("task1");
		taskService.saveTask(task1);
		//保存第二个Task
		Task task2 = taskService.newTask("task2");
		taskService.saveTask(task2);
		//绑定用户组和任务关系
		taskService.addCandidateGroup("task1", groupA.getId());
		taskService.addCandidateGroup("task2", groupA.getId());
	}
	
	// 将用户组数据保存到数据库中
	static Group createGroup(IdentityService identityService, String id,
			String name, String type) {
		// 调用newGroup方法创建Group实例
		Group group = identityService.newGroup(id);
		group.setName(name);
		group.setType(type);
		identityService.saveGroup(group);
		return identityService.createGroupQuery().groupId(id).singleResult();
	}

}

设置候选用户
候选用户是一“群”将会拥有或者执行任务权限的人,但是任务只允许一个人执行或者拥有,而任务的候选人则是指一个用户群体。TaskService同样提供了一个设置用户权限数据的方法addCandidateUser,与addCandidateGroup方法类似,调用该方法需要提供用户ID与任务ID,执行该方法后,会向ACT_RU_IDENTITYLINK表中写入相应的权限数据

package org.crazyit.activiti;

import java.util.UUID;

import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.identity.User;
import org.activiti.engine.task.Task;

/**
 * 使用addCandidateUser方法
 * 
 * @author yangenxiong
 *
 */
public class AddCandidateUser {

	public static void main(String[] args) {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取身份服务组件
		IdentityService identityService = engine.getIdentityService();
		// 新建用户
		User user = creatUser(identityService, UUID.randomUUID().toString(),
				"张三", "lastname", "abc@163.com", "123");
		User user2 = creatUser(identityService, UUID.randomUUID().toString(),
				"李四", "lastname", "abc@163.com", "123");
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 保存一个Task
		Task task1 = taskService.newTask("task1");
		taskService.saveTask(task1);
		// 绑定用户与任务关系
		taskService.addCandidateUser("task1", user.getId());
		taskService.addCandidateUser("task1", user2.getId());
		System.out.println("=========================");
	}

	// 创建用户方法
	static User creatUser(IdentityService identityService, String id,
			String first, String last, String email, String passwd) {
		// 使用newUser方法创建User实例
		User user = identityService.newUser(id);
		// 设置用户的各个属性
		user.setFirstName(first);
		user.setLastName(last);
		user.setEmail(email);
		user.setPassword(passwd);
		// 使用saveUser方法保存用户
		identityService.saveUser(user);
		return identityService.createUserQuery().userId(id).singleResult();
	}

}

权限数据查询
如果需要查询用户组和用户的候选任务,可以使用TaskService的createTaskQuery方法,得到Task对应的查询对象。TaskQuery中提供了taskCandidateGroup和taskCandidateUser方法,这两个方法可以根据用户组或者用户的ID查询候选Task的数据。与流程定义一样,这些查询方法会先到权限数据表中查询与用户组或者用户关联了的数据,查询得到taskId后,再到Task中查询任务数据并且返回。如果得到了任务的ID,想查询相应的关系数据,则可以调用TaskService的getIdentityLinksForTask方法,该方法根据任务ID查询IdentityLink集合。

package org.crazyit.activiti;

import java.util.ArrayList;
import java.util.List;

import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.User;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;

/**
 * 权限数据查询
 * @author yangenxiong
 *
 */
public class Query {

	public static void main(String[] args) {
		//获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取身份服务组件
		IdentityService identityService = engine.getIdentityService();
		// 新建用户
		User user = creatUser(identityService, "user1", "张三", "last", "abc@163.com", "123");
		// 新建用户组
		Group groupA = createGroup(identityService, "group1", "经理组", "manager");
		Group groupB = createGroup(identityService, "group2", "员工组", "employee");
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		//保存第一个Task
		Task task1 = taskService.newTask("task1");
		task1.setName("申请假期");
		taskService.saveTask(task1);
		//保存第二个Task
		Task task2 = taskService.newTask("task2");
		task2.setName("审批假期");
		taskService.saveTask(task2);
		//绑定权限
		taskService.addCandidateGroup("task1", groupA.getId());
		taskService.addCandidateGroup("task2", groupB.getId());
		taskService.addCandidateUser("task2", user.getId());		
		//根据用户组查询任务
		List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup(groupA.getId()).list();
		System.out.println("经理组的候选任务有:");
		for (Task task : tasks) {
			System.out.println("   " + task.getName());
		}		
		//根据用户查询任务
		tasks = taskService.createTaskQuery().taskCandidateUser(user.getId()).list();
		System.out.println("张三的候选任务有");
		for (Task task : tasks) {
			System.out.println("   " + task.getName());
		}
		//调用taskCandidateGroupIn
		List<String> groupIds = new ArrayList<String>();
		groupIds.add(groupA.getId());
		groupIds.add(groupB.getId());
		tasks = taskService.createTaskQuery().taskCandidateGroupIn(groupIds).list();
		System.out.println("经理组与员工组的任务有:");
		for (Task task : tasks) {
			System.out.println("   " + task.getName());
		}
		//查询权限数据
		List<IdentityLink> links = taskService.getIdentityLinksForTask(tasks.get(0).getId());
		System.out.println("关系数据量: " + links.size());
	}

	// 将用户组数据保存到数据库中
	static Group createGroup(IdentityService identityService, String id,
			String name, String type) {
		// 调用newGroup方法创建Group实例
		Group group = identityService.newGroup(id);
		group.setName(name);
		group.setType(type);
		identityService.saveGroup(group);
		return identityService.createGroupQuery().groupId(id).singleResult();
	}
	
	//创建用户方法
	static User creatUser(IdentityService identityService, String id, String first, 
			String last, String email, String passwd) {
		// 使用newUser方法创建User实例
		User user = identityService.newUser(id);
		// 设置用户的各个属性
		user.setFirstName(first);
		user.setLastName(last);
		user.setEmail(email);
		user.setPassword(passwd);
		// 使用saveUser方法保存用户
		identityService.saveUser(user);
		return identityService.createUserQuery().userId(id).singleResult();
	}
}

设置任务持有人
TaskService中提供了一个setOwner方法来设置任务的持有人,调用该方法后,会设置流程表的OWNER_字段为相应用户的ID。如果想根据用户查询其所拥有的任务,可以调用TaskQuery的taskOwner方法

package org.crazyit.activiti;

import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.identity.User;
import org.activiti.engine.task.Task;

/**
 * 设置任务持有人
 * 
 * @author yangenxiong
 * 
 */
public class SetOwner {

	public static void main(String[] args) {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取身份服务组件
		IdentityService identityService = engine.getIdentityService();
		// 新建用户
		User user = creatUser(identityService, "user1", "张三", "last",
				"abc@163.com", "123");
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 保存一个Task
		Task task1 = taskService.newTask("task1 ");
		task1.setName("申请任务");
		taskService.saveTask(task1);
		// 设置任务持有人
		taskService.setOwner(task1.getId(), user.getId());
		System.out
				.println("用户张三持有任务数量:"
						+ taskService.createTaskQuery().taskOwner(user.getId())
								.count());
	}

	// 创建用户方法
	static User creatUser(IdentityService identityService, String id,
			String first, String last, String email, String passwd) {
		// 使用newUser方法创建User实例
		User user = identityService.newUser(id);
		// 设置用户的各个属性
		user.setFirstName(first);
		user.setLastName(last);
		user.setEmail(email);
		user.setPassword(passwd);
		// 使用saveUser方法保存用户
		identityService.saveUser(user);
		return identityService.createUserQuery().userId(id).singleResult();
	}
}

设置任务代理人
除设置任务持有人外,TaskService还提供了一个setAssignee方法用于设置任务的代理人,与setOwner方法一样,setAssignee方法会改变ACT_RU_TASK表的ASSIGNEE_字段值。当需要根据任务代理人查询任务时,可以调用TaskQuery的taskAssignee方法设定该查询条件。

package org.crazyit.activiti;

import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.identity.User;
import org.activiti.engine.task.Task;

/**
 * 设置任务受理人
 * 
 * @author yangenxiong
 * 
 */
public class SetAssignee {

	public static void main(String[] args) {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取身份服务组件
		IdentityService identityService = engine.getIdentityService();
		// 新建用户
		User user = creatUser(identityService, "user1", "张三", "last",
				"abc@163.com", "123");
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 保存一个Task
		Task task1 = taskService.newTask("task1");
		task1.setName("申请任务");
		taskService.saveTask(task1);
		// 设置任务持有人
		taskService.setAssignee(task1.getId(), user.getId());
		System.out.println("用户张三受理的任务数量:"
				+ taskService.createTaskQuery().taskAssignee(user.getId())
						.count());
	}

	// 创建用户方法
	static User creatUser(IdentityService identityService, String id,
			String first, String last, String email, String passwd) {
		// 使用newUser方法创建User实例
		User user = identityService.newUser(id);
		// 设置用户的各个属性
		user.setFirstName(first);
		user.setLastName(last);
		user.setEmail(email);
		user.setPassword(passwd);
		// 使用saveUser方法保存用户
		identityService.saveUser(user);
		return identityService.createUserQuery().userId(id).singleResult();
	}
}

添加任务权限数据
在前面的几节中,讲述了如何设置任务的权限数据,除了这些方法外,TaskService还提供了两个添加任务权限数据的方法,这两个方法的描述如下。
➢ addGroupIdentityLink(String taskId,String groupId,String identityLinkType):添加用户组权限数据,第一个参数为任务ID,第二个参数为用户组ID,第三个参数为权限数据类型标识。
➢ addUserIdentityLink(String taskId,String userId,String identityLinkType):添加用户权限数据,第一个参数为任务ID,第二个参数为用户ID,第三个参数为权限数据类型标识。

package org.crazyit.activiti;

import java.util.List;

import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.User;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.IdentityLinkType;
import org.activiti.engine.task.Task;

/**
 * 添加任务权限数据
 * @author yangenxiong
 *
 */
public class AddIdentityLink {

	public static void main(String[] args) {
		//获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取身份服务组件
		IdentityService identityService = engine.getIdentityService();
		// 新建用户组
		Group groupA = createGroup(identityService, "group1", "经理组", "manager");
		// 新建用户
		User user = creatUser(identityService, "user1", "张三", "last", "abc@163.com", "123");
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		//保存第一个Task
		Task task1 = taskService.newTask("task1");
		task1.setName("申请任务");
		taskService.saveTask(task1);
		//调用addGroupIdentityLink方法
		taskService.addGroupIdentityLink(task1.getId(), groupA.getId(), IdentityLinkType.CANDIDATE);
		taskService.addGroupIdentityLink(task1.getId(), groupA.getId(), IdentityLinkType.OWNER);
		taskService.addGroupIdentityLink(task1.getId(), groupA.getId(), IdentityLinkType.ASSIGNEE);
		//调用addUserIdentityLink方法
		Task task2 = taskService.newTask("task2");
		task2.setName("申请任务2");
		taskService.saveTask(task2);
		taskService.addUserIdentityLink(task2.getId(), user.getId(), IdentityLinkType.CANDIDATE);
		taskService.addUserIdentityLink(task2.getId(), user.getId(), IdentityLinkType.OWNER);
		taskService.addUserIdentityLink(task2.getId(), user.getId(), IdentityLinkType.ASSIGNEE);
	}

	//创建用户方法
	static User creatUser(IdentityService identityService, String id, String first, 
			String last, String email, String passwd) {
		// 使用newUser方法创建User实例
		User user = identityService.newUser(id);
		// 设置用户的各个属性
		user.setFirstName(first);
		user.setLastName(last);
		user.setEmail(email);
		user.setPassword(passwd);
		// 使用saveUser方法保存用户
		identityService.saveUser(user);
		return identityService.createUserQuery().userId(id).singleResult();
	}
	
	// 将用户组数据保存到数据库中
	static Group createGroup(IdentityService identityService, String id,
			String name, String type) {
		// 调用newGroup方法创建Group实例
		Group group = identityService.newGroup(id);
		group.setName(name);
		group.setType(type);
		identityService.saveGroup(group);
		return identityService.createGroupQuery().groupId(id).singleResult();
	}
}

调用addGroupIdentityLink和addUserIdentityLink方法三次,分别将第三个参数(权限类型标识)设置为CANDIDATE、OWNER和ASSIGNEE。
使用addUserIdentityLink方法将权限类型标识设置为CANDIDATE,其效果等同于调用addCandidateUser方法;将权限类型标识设置为OWNER,其效果等同于调用setOwner方法;将权限类型标识设置为ASSIGNEE,其效果等同于调用setAssignee方法。调用addGroupIdentityLink方法与调用addUserIdentityLink方法的效果类似,但是需要注意的是,将用户组设置为任务所有人或者任务代理人,这并不合适,虽然可以成功调用addGroupIdentityLink方法,但其在删除权限数据时,将会抛出异常。

删除用户组权限
TaskService中提供了两个方法用于删除用户组的任务权限,这两个方法的描述如下。
➢ deleteGroupIdentityLink(String taskId,String groupId,String identityLinkType):删除任务的权限数据,第一个参数为任务ID,第二个参数为用户组ID,第三个参数为任务权限类型标识。
➢ deleteCandidateGroup(String taskId,String groupId):删除任务的候选用户组数据,第一个参数为任务ID,第二个参数为用户组ID。

package org.crazyit.activiti;

import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.identity.Group;
import org.activiti.engine.task.IdentityLinkType;
import org.activiti.engine.task.Task;

/**
 * 删除用户组权限数据
 * 
 * @author yangenxiong
 * 
 */
public class DeleteGroupIdentity {

	public static void main(String[] args) {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取身份服务组件
		IdentityService identityService = engine.getIdentityService();
		// 新建用户组
		Group groupA = createGroup(identityService, "group1", "经理组", "manager");
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 保存第一个Task
		Task task1 = taskService.newTask("task1");
		task1.setName("审批任务");
		taskService.saveTask(task1);
		// 调用addGroupIdentityLink方法
		taskService.addGroupIdentityLink(task1.getId(), groupA.getId(),
				IdentityLinkType.CANDIDATE);
		taskService.addGroupIdentityLink(task1.getId(), groupA.getId(),
				IdentityLinkType.OWNER);
		taskService.addGroupIdentityLink(task1.getId(), groupA.getId(),
				IdentityLinkType.ASSIGNEE);
		// 调用delete方法
		taskService.deleteCandidateGroup(task1.getId(), groupA.getId());
		// 以下两个s方法将抛出异常
		taskService.deleteGroupIdentityLink(task1.getId(), groupA.getId(),
				IdentityLinkType.OWNER);
		taskService.deleteGroupIdentityLink(task1.getId(), groupA.getId(),
				IdentityLinkType.ASSIGNEE);
	}

	// 将用户组数据保存到数据库中
	static Group createGroup(IdentityService identityService, String id,
			String name, String type) {
		// 调用newGroup方法创建Group实例
		Group group = identityService.newGroup(id);
		group.setName(name);
		group.setType(type);
		identityService.saveGroup(group);
		return identityService.createGroupQuery().groupId(id).singleResult();
	}
}

调用deleteGroupIdentityLink和deleteCandidateGroup方法删除任务组的用户权限。在此需要注意的是,如果在调用deleteGroupIdentityLink方法时传入的权限类型标识是OWNER或者ASSIGNEE,那么将会抛出异常。异常信息为:Incompatible usage:cannot use type’owner’together with a groupId。
如果在调用addGroupIdentityLink方法时使用的是自定义的权限类型标识,那么在调用deleteGroupIdentityLink方法进行删除时,要传入相同的权限类型标识才能删除权限数据。

删除用户权限
TaskService中提供了两个类似的方法用于删除用户的任务权限数据,这两个方法的描述如下。
➢ deleteCandidateUser(String taskId,String userId):删除任务权限类型标识为“CANDIDATE”的用户权限数据。
➢ deleteUserIdentityLink(String taskId,String userId,String identityLinkType):删除任务权限类型为identityLinkType的用户权限数据。
在使用addCandidateUser方法时,会插入一条权限数据到权限中间表中,如果需要删除该权限数据,则可以调用相应的deleteCandidateUser方法。除了addCandidateUser方法外,还有setOwner、setAssignee和addUserIdentityLink方法可以用于添加不同类型的权限数据,其中setOwner和setAssignee方法会改变任务表的OWNER_和ASSIGNEE_字段值。在调用deleteUserIdentityLink方法时,如果传入的类型标识为OWNER或者ASSIGNEE,那么会将任务表的OWNER_和ASSIGNEE_字段的值设置为null。

package org.crazyit.activiti;

import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.identity.User;
import org.activiti.engine.task.IdentityLinkType;
import org.activiti.engine.task.Task;

/**
 * 删除用户组权限数据
 * @author yangenxiong
 *
 */
public class DeleteUserIdentity {

	public static void main(String[] args) {
		//获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取身份服务组件
		IdentityService identityService = engine.getIdentityService();
		// 新建用户
		User user = creatUser(identityService, "user1", "first", "last", "abc@163.com", "123");
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		//保存第一个Task
		Task task1 = taskService.newTask("task1");
		taskService.saveTask(task1);
		//添加用户权限
		taskService.addCandidateUser(task1.getId(), user.getId());
		long count = taskService.createTaskQuery().taskCandidateUser(user.getId()).count();
		System.out.println("调用addCandidateUser方法后,用户的候选任务数量:" + count);
		//删除用户权限
		taskService.deleteCandidateUser(task1.getId(), user.getId());
		count = taskService.createTaskQuery().taskCandidateUser(user.getId()).count();
		System.out.println("调用deleteCandidateUser方法后,用户的候选任务数量:" + count);
		//添加用户权限
		taskService.addUserIdentityLink(task1.getId(), user.getId(), IdentityLinkType.OWNER);
		count = taskService.createTaskQuery().taskOwner(user.getId()).count();
		System.out.println("调用addUserIdentityLink方法后,用户的候选任务数量:" + count);
		//删除用户权限
		taskService.deleteUserIdentityLink(task1.getId(), user.getId(), IdentityLinkType.OWNER);
		count = taskService.createTaskQuery().taskOwner(user.getId()).count();
		System.out.println("调用deleteUserIdentityLink方法后,用户的候选任务数量:" + count);
	}
	
	//创建用户方法
	static User creatUser(IdentityService identityService, String id, String first, 
			String last, String email, String passwd) {
		// 使用newUser方法创建User实例
		User user = identityService.newUser(id);
		// 设置用户的各个属性
		user.setFirstName(first);
		user.setLastName(last);
		user.setEmail(email);
		user.setPassword(passwd);
		// 使用saveUser方法保存用户
		identityService.saveUser(user);
		return identityService.createUserQuery().userId(id).singleResult();
	}
}

任务参数
当一个任务被传递到执行人手中时,他需要知道该任务的全部信息,包括任务的基本信息(创建时间、内容等),还需要得到任务的相关参数。例如一个请假申请,请假的天数、开始时间等均为该申请的参数。编写这个请假申请的任务由请假申请人发起,在执行编写请假任务时,就需要设置这一系列的请假任务参数。
基本类型参数设置
在Activiti数据库设计相关章节中我们讲过,Activiti中的各种参数均保存在ACT_RU_VARIABLE表中,因此当调用了相应的API设置参数后,这些参数都会体现在参数数据表中。Activiti支持多种参数类型设置,开发者可以根据实际情况设置不同的参数。例如在一个请假流程中,若需要设置天数,可以使用Integer类型;需要设置日期,可以使用Date类型。设置参数可以调用TaskService的setVariable(String taskId,String variableName,Object value)方法。调用该方法需要传入taskId、参数名称和参数值,其中参数值类型为Object,根据传入的参数类型,参数表的TYPE_字段会记录参数的类型标识,当前Activiti支持以下基本参数类型。
➢ Boolean:布尔类型,参数类型标识为boolean。
➢ Date:日期类型,参数类型标识为date。
➢ Double:双精度类型,参数类型标识为double。
➢ Integer:整型,参数类型标识为integer。
➢ Long:长整型,参数类型标识为long。
➢ Null:空值,参数类型标识为null。
➢ Short:短整型,参数类型标识为short。
➢ String:字符型,参数类型标识为string

package org.crazyit.activiti;

import java.util.Date;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;

/**
 * 参数类型
 * @author yangenxiong
 *
 */
public class BasicVariableType {

	public static void main(String[] args) {
		//获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		//保存第一个Task
		Task task1 = taskService.newTask("task1");
		taskService.saveTask(task1);	
		Date d = new Date();
		short s = 3;
		//设置各种基本类型参数
		taskService.setVariable(task1.getId(), "arg0", false);
		taskService.setVariable(task1.getId(), "arg1", d);
		taskService.setVariable(task1.getId(), "arg2", 1.5D);
		taskService.setVariable(task1.getId(), "arg3", 2);
		taskService.setVariable(task1.getId(), "arg4", 10L);
		taskService.setVariable(task1.getId(), "arg5", null);
		taskService.setVariable(task1.getId(), "arg6", s);
		taskService.setVariable(task1.getId(), "arg7", "test");
	}

}

序列化参数
设置基本数据类型的参数,setVariable方法的第三个参数的类型为Object,因此在调用该方法时,可以传入自定义对象。如果传入的参数为自定义对象,那么调用setVariable方法时,会将该对象进行序列化(被序列化的对象必须实现Serializable接口),然后将其保存到资源表(ACT_GE_BYTEARRAY)中,而参数表为该参数数据做外键关

package org.crazyit.activiti;

import java.util.UUID;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;

/**
 * 参数类型
 * @author yangenxiong
 *
 */
public class SerilizableVariableType {

	public static void main(String[] args) throws Exception {
		//获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		//保存第一个Task
		Task task1 = taskService.newTask(UUID.randomUUID().toString());
		task1.setName("出差申请");
		taskService.saveTask(task1);
		//设置序列化参数
		taskService.setVariable(task1.getId(), "arg0", new TestVO("crazyit"));
		TestVO var = (TestVO)taskService.getVariable(task1.getId(), "arg0");
		System.out.println(var.getName());
	}

}

创建TestVo

package org.crazyit.activiti;

import java.io.Serializable;

/**
 * 序列化对象
 * @author yangenxiong
 *
 */
public class TestVO implements Serializable {

	private String name;
		
	public TestVO(String name) {
		this.name = name;
	}
	
	public String getName() {
		return this.name;
	}
}

序列化参数的设置与基本类型参数的设置一样,都是直接调用setVariable方法,但是TestVO对象必须为序列化对象。
将任务的参数都设计为一个对象,这更符合面向对象的思想,但是如果将一些大对象进行序列化,必然会损耗一定的性能,因此在使用序列化对象作为任务参数时,尽量避免传入大对象

package org.crazyit.activiti;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;

/**
 * 参数类型
 * @author yangenxiong
 *
 */
public class GetVariable {

	public static void main(String[] args) {
		//获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		//保存第一个Task
		Task task1 = taskService.newTask("task1");
		task1.setName("请差申请");
		taskService.saveTask(task1);
		//设置各种基本类型参数
		taskService.setVariable(task1.getId(), "days", 5);
		taskService.setVariable(task1.getId(), "target", new TestVO("北京"));
		//获取天数
		Integer days = (Integer)taskService.getVariable(task1.getId(), "days");
		System.out.println("出差天数:" + days);
		//获取目的地
		TestVO target = (TestVO)taskService.getVariable(task1.getId(), "target");
		System.out.println("出差目的地:" + target.getName());
	}

}

参数作用域
当任务与流程绑定后,设置的参数均会有其作用域。例如设置一个任务参数,希望在整个流程中均可以使用,那么可以调用setVariable方法,如果只希望该参数仅仅在当前这个任务中使用,那么可以调用TaskService的setVariableLocal方法。调用了setVariable方法后,如果调用getVariableLocal方法来获取参数,将查找不到任何值,因为getVariableLocal方法会查询当前任务的参数,而不会查询整个流程中的全局参数

package org.crazyit.activiti;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

/**
 * 参数类型
 * 
 * @author yangenxiong
 * 
 */
public class LocalVariable {

	public static void main(String[] args) {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 获取运行服务组件
		RuntimeService runtimeService = engine.getRuntimeService();
		// 流程存储服务组件
		RepositoryService repositoryService = engine.getRepositoryService();
		// 部署流程描述文件
		Deployment dep = repositoryService.createDeployment()
				.addClasspathResource("bpmn/vacation.bpmn").deploy();
		// 查找流程定义
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.deploymentId(dep.getId()).singleResult();
		// 启动流程
		ProcessInstance pi = runtimeService
				.startProcessInstanceById(pd.getId());
		// 分别调用setVariable和setVariableLocal方法
		Task task = taskService.createTaskQuery().processInstanceId(pi.getId())
				.singleResult();
		taskService.setVariable(task.getId(), "days", 10);
		taskService.setVariableLocal(task.getId(), "target", "欧洲");
		// 获取参数
		Object data1 = taskService.getVariable(task.getId(), "days");
		System.out.println("获取休假天数:" + data1);
		Object data2 = taskService.getVariable(task.getId(), "target");
		System.out.println("获取目的地: " + data2);
		// 获取参数
		Object data3 = taskService.getVariableLocal(task.getId(), "days");
		System.out.println("使用getVariableLocal方法获取天数:" + data3);
	}

}

先调用setVariable和setVariableLocal方法,为task设置了arg0和arg1两个参数,然后调用getVariable和getVariableLocal方法查找参数,其中使用getVariable方法查找两个参数,使用getVariableLocal方法查找days参数,由于days参数是使用setVariable方法设置的,即days是流程的全局参数,因此使用getVariableLocal方法查找days时,将会返回null。由此可见,getVariable方法会查询任务参数(setVariableLocal方法设置的参数)和流程的全局参数(setVariable方法设置的参数),并且会优先查询任务参数,而getVariableLocal方法只会查询任务参数(setVariableLocal方法设置的参数)。
设置多个参数
如果一个任务需要设置多个参数,则可以定义一个序列化对象,将这些参数放到这个对象中,也可以使用TaskService的setVariables和setVariablesLocal方法,传入参数的Map集合,参数Map的key为参数的名称,value为参数值。

package org.crazyit.activiti;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;

/**
 * 设置多个任务参数
 * @author yangenxiong
 *
 */
public class Variables {

	public static void main(String[] args) {
		//获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		//保存第一个Task
		Task task1 = taskService.newTask(UUID.randomUUID().toString());
		task1.setName("请假流程");
		taskService.saveTask(task1);
		//初始化参数
		Map<String,Object> vars = new HashMap<String, Object>();
		vars.put("days", 10);
		vars.put("target", "欧洲");
		taskService.setVariables(task1.getId(), vars);
	}

}

数据对象
在BPMN文件中,可以使用dataObject元素来定义流程参数。流程启动后,这些参数将会自动被设置到流程实例中,可以使用RuntimeService、TaskService的方法来查询这些参数
创建dataObject.bpmn.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
	xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
	typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
	targetNamespace="http://www.activiti.org/test">


	<process id="vacationProcess" name="vacation">
		<!-- 定义了名称,默认值为 Crazyit -->
		<dataObject id="personName" name="personName" itemSubjectRef="xsd:string">
			<extensionElements>
				<activiti:value>Crazyit</activiti:value>
			</extensionElements>
		</dataObject>
		<!-- 定义了年龄,默认值为20 -->
		<dataObject id="personAge" name="personAge" itemSubjectRef="xsd:int">
			<extensionElements>
				<activiti:value>20</activiti:value>
			</extensionElements>
		</dataObject>
		<startEvent id="startevent1" name="Start"></startEvent>
		<userTask id="usertask1" name="Write Vacation"></userTask>
		<endEvent id="endevent1" name="End"></endEvent>
		<sequenceFlow id="flow1" name="" sourceRef="startevent1"
			targetRef="usertask1"></sequenceFlow>
		<sequenceFlow id="flow2" name="" sourceRef="usertask1"
			targetRef="endevent1"></sequenceFlow>
	</process>
</definitions>

在这里插入图片描述
测试

package org.crazyit.activiti;
import java.util.Map;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.DataObject;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

/**
 * 参数类型
 * 
 * @author yangenxiong
 * 
 */
public class TestDataObject {

	public static void main(String[] args) {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 获取运行服务组件
		RuntimeService runtimeService = engine.getRuntimeService();
		// 流程存储服务组件
		RepositoryService repositoryService = engine.getRepositoryService();
		// 部署流程描述文件
		Deployment dep = repositoryService.createDeployment()
				.addClasspathResource("bpmn/dataObject.bpmn").deploy();
		// 查找流程定义
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.deploymentId(dep.getId()).singleResult();
		// 启动流程
		ProcessInstance pi = runtimeService
				.startProcessInstanceById(pd.getId());
		// 查询流程任务
		Task task = taskService.createTaskQuery().processInstanceId(pi.getId())
				.singleResult();
		// 获取全部参数
		Map<String, DataObject> objs = taskService.getDataObjects(task.getId());
		// 输出参数
		for(String key : objs.keySet()) {
			System.out.println(key + "---" + objs.get(key).getValue());
		}
	}

}

任务附件管理
在实际生活中,许多流程都会带上一些任务相关的附件,例如报销流程有可能需要附上发票的单据,奖惩流程有可能会附上相关的说明文件。为此,TaskService中提供了用于附件管理的API,通过调用这些API,可以对任务附件进行创建、删除和查询的操作。在Activiti中使用附件表(ACT_HI_ATTACHMENT)保存任务附件数据,与其对应的实体类为AttachmentEntityImpl。
Attachment对象
一个Attachment实例表示一条任务附件的数据,Attachment接口提供了获取任务附件属性的各个方法,如何设置该对象的属性,完全由TaskService完成,使用者不需要关心设置的过程。该接口的实现类为AttachmentEntityImpl,包括以下属性。
➢ id:附件表的主键,对应ID_字段。
➢ revision:附件数据的版本,对应REV_字段。
➢ name:附件名称,由调用者提供,保存在NAME_字段。
➢ desciption:附件描述,由调用者提供,保存在DESCRIPTION_字段。
➢ type:附件类型,由调用者定义,保存在TYPE_字段。
➢ taskId:该附件对应的任务ID,对应TASK_ID_字段。
➢ processInstanceId:流程实例ID,对应PROC_INST_ID_字段。
➢ url:附件的URL,由调用者提供,对应URL_字段。
➢ contentId:附件内容的ID,如果调用者提供了输入流作为附件的内容,那么这些内容将会被保存到资源表(ACT_GE_BYTEARRAY)中,该字段将为资源表的外键 ID,对应CONTENT_ID_字段。
创建任务附件
TaskService中提供了两个创建任务附件的方法,这两个方法的描述如下。
➢ createAttachment(String attachmentType,String taskId,String processInstanceId,String attachmentName,String attachmentDescription,String url):创建任务附件,attachmentType为附件类型,由调用者定义;taskId 为任务 ID;processInstanceId 为流程实例 ID;attachmentName为附件名称;attachmentDescription为附件描述;url为该附件的URL地址。
➢ createAttachment(String attachmentType,String taskId,String processInstanceId,String attachmentName,String attachmentDescription,InputStream content):该方法与前一个createAttachment方法一样,只是最后一个参数的类型为InputStream。当调用该方法时,会将最后的输出流参数转换为byte数组,并保存到资源表中,最后设置CONTENT_ID_的字段值为资源表中的数据ID。
在此需要注意的是,当调用第一个createAttachment方法时,对于提供的URL,Activiti并不会解析它并生成byte数组,只是单纯地将该URL保存到URL_字段中。
创建附件

package org.crazyit.activiti;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

/**
 * 创建任务附件
 * @author yangenxiong
 *
 */
public class CreateAttachment {

	public static void main(String[] args) throws Exception {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 获取运行服务组件
		RuntimeService runtimeService = engine.getRuntimeService();
		// 流程存储服务组件
		RepositoryService repositoryService = engine.getRepositoryService();
		// 部署流程描述文件
		Deployment dep = repositoryService.createDeployment()
				.addClasspathResource("bpmn/vacation.bpmn").deploy();
		// 查找流程定义
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.deploymentId(dep.getId()).singleResult();
		// 启动流程
		ProcessInstance pi = runtimeService
				.startProcessInstanceById(pd.getId());
		// 查找任务
		Task task = taskService.createTaskQuery().processInstanceId(pi.getId())
				.singleResult();
		// 设置任务附件
		taskService.createAttachment("web url", task.getId(), pi.getId(), "163.com", 
				"163 web page", "http://www.163.com");
		// 创建图片输入流
		InputStream is = new FileInputStream(new File("resource/artifact/result.png"));
		// 设置输入流为任务附件
		taskService.createAttachment("web url", task.getId(), pi.getId(), "163.com", 
				"163 web page", is);
	}
}

附件查询
TaskService中提供了四个查询附件的方法,这些方法包括根据任务ID查询附件集合、根据流程实例ID查询附件集合、根据附件ID查询附件数据和根据附件ID查询附件内容。以下为这四个查询附件方法的描述。
➢ getProcessInstanceAttachments(String processInstanceId):根据流程实例ID查询该流程实例下全部的附件,返回Attachment集合。
➢ getTaskAttachments(String taskId):根据任务 ID 查询该任务下全部的附件,返回Attachment集合。
➢ getAttachment(String attachmentId):根据附件的ID查询附件数据,返回一个Attachment对象。
➢ getAttachmentContent(String attachmentId):根据附件的ID获取该附件的内容,返回附件内容的输入流对象,如果调用的是第二个 createAttachment 方法(传入附件的InputStream),那么调用该方法才会返回非空的输入流,否则将返回null。

package org.crazyit.activiti;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.List;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Attachment;
import org.activiti.engine.task.Task;

/**
 * 附件查询
 * @author yangenxiong
 *
 */
public class GetAttachment {

	public static void main(String[] args) throws Exception {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 获取运行服务组件
		RuntimeService runtimeService = engine.getRuntimeService();
		// 流程存储服务组件
		RepositoryService repositoryService = engine.getRepositoryService();
		// 部署流程描述文件
		Deployment dep = repositoryService.createDeployment()
				.addClasspathResource("bpmn/vacation.bpmn").deploy();
		// 查找流程定义
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.deploymentId(dep.getId()).singleResult();
		// 启动流程
		ProcessInstance pi = runtimeService
				.startProcessInstanceById(pd.getId());
		// 查找任务
		Task task = taskService.createTaskQuery().processInstanceId(pi.getId())
				.singleResult();
		// 设置任务附件
		Attachment att1 = taskService.createAttachment("web url", task.getId(), pi.getId(), "Attachement1", 
				"163 web page", "http://www.163.com");
		// 创建图片输入流
		InputStream is = new FileInputStream(new File("resource/artifact/result.png"));
		// 设置输入流为任务附件
		Attachment att2 = taskService.createAttachment("web url", task.getId(), pi.getId(), "Attachement2", 
				"Image InputStream", is);
		// 根据流程实例ID查询附件
		List<Attachment> attas1 = taskService.getProcessInstanceAttachments(pi.getId());
		System.out.println("流程附件数量:" + attas1.size());
		// 根据任务ID查询附件
		List<Attachment> attas2 = taskService.getTaskAttachments(task.getId());
		System.out.println("任务附件数量:" + attas2.size());
		// 根据附件ID查询附件
		Attachment attResult = taskService.getAttachment(att1.getId());
		System.out.println("附件1名称:" + attResult.getName());
		// 根据附件ID查询附件内容		
		InputStream stream1 = taskService.getAttachmentContent(att1.getId());
		System.out.println("附件1的输入流:" + stream1);
		InputStream stream2 = taskService.getAttachmentContent(att2.getId());
		System.out.println("附件2的输入流:" + stream2);
	}

}

在这里插入图片描述删除附件

package org.crazyit.activiti;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.List;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Attachment;
import org.activiti.engine.task.Task;

/**
 * 创建任务附件
 * @author yangenxiong
 *
 */
public class DeleteAttachment {
	public static void main(String[] args) throws Exception {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 获取运行服务组件
		RuntimeService runtimeService = engine.getRuntimeService();
		// 流程存储服务组件
		RepositoryService repositoryService = engine.getRepositoryService();
		// 部署流程描述文件
		Deployment dep = repositoryService.createDeployment()
				.addClasspathResource("bpmn/vacation.bpmn").deploy();
		// 查找流程定义
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.deploymentId(dep.getId()).singleResult();
		// 启动流程
		ProcessInstance pi = runtimeService
				.startProcessInstanceById(pd.getId());
		// 查找任务
		Task task = taskService.createTaskQuery().processInstanceId(pi.getId())
				.singleResult();
		// 设置任务附件
		Attachment att1 = taskService.createAttachment("web url", task.getId(), pi.getId(), "Attachement1", 
				"163 web page", "http://www.163.com");
		// 创建图片输入流
		InputStream is = new FileInputStream(new File("resource/artifact/result.png"));
		// 设置输入流为任务附件
		Attachment att2 = taskService.createAttachment("web url", task.getId(), pi.getId(), "Attachement2", 
				"Image InputStream", is);
		System.out.println("删除前数量:" + taskService.getTaskAttachments(task.getId()).size());
		taskService.deleteAttachment(att2.getId());
		System.out.println("删除后数量:" + taskService.getTaskAttachments(task.getId()).size());
	}

}

任务评论与事件记录
在日常的工作流程中,随着业务的进行,可能会夹杂着一些个人的流程意见,使用Activiti,可以将任务或者流程的评论保存到评论表(ACT_HI_COMMENT)中,接口为Comment,实现类为CommentEntityImpl。评论表会保存两种类型的数据:任务评论和部分事件记录。下面将介绍如何调用TaskService的方法管理这些任务评论数据与操作事件记录。
Comment对象
一个Comment实例表示评论表的一条数据,CommentEntityImpl实际上实现了两个接口:Event和Comment。如果该对象作为Event接口返回,则可以认为它返回的是事件记录的数据,如果该对象作为Comment返回,则其返回的是评论数据。在使用过程中,只可以得到Comment或者Event接口的实例,Comment和Event接口中只定义了一系列的getter方法用于获取相关信息,设置属性的方法,均被放到TaskService中实现。CommentEntityImpl主要包含以下属性。
➢ id:评论表的主键,对应ID_字段
➢ type:该数据的类型,对应TYPE_字段,该属性有两个值,分别为“event”和“comment”。当值为“event”时,表示该数据为事件的记录;当值为“comment”时,表示为任务或者流程的评论数据。
➢ userId:产生此数据用户的ID,对应USER_ID_字段。
➢ time:该数据的产生时间,对应TIME_字段。
➢ taskId:该评论(或者事件记录)数据对应的任务ID,对应TASK_字段。
➢ processInstanceId:该评论(或者事件记录)数据对应的流程实例 ID,对应PROC_INST_ID_字段。
➢ action:该数据的操作标识,对应ACTION_字段。
➢ message:该评论(或者事件记录)数据的信息,对应MESSAGE_字段。
➢ fullMessage:该评论(或者事件记录)数据的信息,对应FULL_MSG_字段。

package org.crazyit.activiti;

import java.util.List;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Attachment;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Event;
import org.activiti.engine.task.Task;

/**
 * 事件记录
 * @author yangenxiong
 *
 */
public class EventRecord {

	public static void main(String[] args) {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 获取运行服务组件
		RuntimeService runtimeService = engine.getRuntimeService();
		// 流程存储服务组件
		RepositoryService repositoryService = engine.getRepositoryService();
		// 部署流程描述文件
		Deployment dep = repositoryService.createDeployment()
				.addClasspathResource("bpmn/vacation.bpmn").deploy();
		// 查找流程定义
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.deploymentId(dep.getId()).singleResult();
		// 启动流程
		ProcessInstance pi = runtimeService
				.startProcessInstanceById(pd.getId());
		// 查找任务
		Task task = taskService.createTaskQuery().processInstanceId(pi.getId())
				.singleResult();
		// 调用各个记录事件的方法 
		taskService.addComment(task.getId(), pi.getId(), "this is comment message");		
		taskService.addUserIdentityLink(task.getId(), "1", "user");
		taskService.deleteUserIdentityLink(task.getId(), "1", "user");
		taskService.addGroupIdentityLink(task.getId(), "1", "group");
		taskService.deleteGroupIdentityLink(task.getId(), "1", "group");
		Attachment atta = taskService.createAttachment("test", task.getId(), pi.getId(), "test", "test", "");
		taskService.deleteAttachment(atta.getId());
		// 查询Comment和Event
		List<Comment> comments = taskService.getTaskComments(task.getId());
		System.out.println("总共的评论数量:" + comments.size());
		List<Event> events = taskService.getTaskEvents(task.getId());
		System.out.println("总共的事件数量:" + events.size());
	}
}

其中ACTION_字段会出现以下值。
➢ AddUserLink:调用TaskService的addUserIdentityLink方法时设置该值。
➢ DeleteUserLink:调用TaskService的deleteUserIdentityLink方法时设置该值。
➢addGroupIDentityLink:调用tasKService的addGroupIDentityLink方法时设置该值
➢ DeleteGroupLink:调用TaskService的deleteGroupIdentityLink方法时设置该值。
➢ AddComment:调用TaskService的addComment方法时设置该值。
➢ AddAttachment:调用TaskService的createAttachment方法时设置该值。➢ DeleteAttachment:调用TaskService的deleteAttachment方法时设置该值。
根据产生的数据结果,可以得出结论,除了addComment方法外,其他的方法产生的数据中,TYPE_字段值均为“event”,表示这些数据为事件记录数据;而addComment方法产生的数据中,TYPE_字段值为“comment”,表示这是一条任务(流程)的评论数据。
在这里插入图片描述数据查询
TaskService中提供了以下几个查询评论表数据的方法。
➢ getComment(String commentId):根据评论数据ID查询评论数据。
➢ getTaskComments(String taskId):根据任务ID查询相应的评论数据。
➢ getTaskEvents(String taskId):根据任务ID查询相应的事件记录。
➢ getProcessInstanceComments(String processInstanceId):根据流程实例ID查询相应的评论(事件)数据。
其中,getTaskEvents方法返回Event的实例集合,而getTaskComments和getProcessInstance-Comments方法返回Comment的实例集合

package org.crazyit.activiti;

import java.util.List;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Attachment;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Event;
import org.activiti.engine.task.Task;

/**
 * ACT_HI_COMMENT表数据查询 
 * @author yangenxiong
 *
 */
public class CommentQuery {

	public static void main(String[] args) {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 获取运行服务组件
		RuntimeService runtimeService = engine.getRuntimeService();
		// 流程存储服务组件
		RepositoryService repositoryService = engine.getRepositoryService();
		// 部署流程描述文件
		Deployment dep = repositoryService.createDeployment()
				.addClasspathResource("bpmn/vacation.bpmn").deploy();
		// 查找流程定义
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.deploymentId(dep.getId()).singleResult();
		// 启动流程
		ProcessInstance pi = runtimeService
				.startProcessInstanceById(pd.getId());
		// 查找任务
		Task task = taskService.createTaskQuery().processInstanceId(pi.getId())
				.singleResult();
		// 调用各个记录事件的方法 
		taskService.addComment(task.getId(), pi.getId(), "this is comment message");		
		taskService.addUserIdentityLink(task.getId(), "1", "user");
		taskService.deleteUserIdentityLink(task.getId(), "1", "user");
		taskService.addGroupIdentityLink(task.getId(), "1", "group");
		taskService.deleteGroupIdentityLink(task.getId(), "1", "group");
		Attachment atta = taskService.createAttachment("test", task.getId(), pi.getId(), "test", "test", "");
		taskService.deleteAttachment(atta.getId());
		// 查询事件与评论
		List<Comment> commonts1 = taskService.getProcessInstanceComments(pi.getId());
		System.out.println("流程评论(事件)数量:" + commonts1.size());
		commonts1 = taskService.getTaskComments(task.getId());
		System.out.println("任务评论数量:" + commonts1.size());
		List<Event> events = taskService.getTaskEvents(task.getId());
		System.out.println("事件数量:" + events.size());
	}

}

在这里插入图片描述任务声明与完成
任务声明实际上是指将任务分配到某个用户下,即将该用户作为该任务的代理人,可以使用TaskService的claim方法进行任务代理人指定(效果类似于setAssignee方法)。当一个任务需要完结时,可以调用TaskService的complete方法,指定该任务已经完成,让整个流程继续往下进行。
任务声明
调用Taskservice的claim方法可以将任务分配到用户下,即设置任务表的ASSIGNEE_字段值为用户的ID,该效果与使用setAssignee方法的效果类似,但是不同的是,一旦调用了claim方法声明任务的代理人,如果再次调用该方法将同一个任务分配到另外的用户下,则会抛出异常。
创建vacation.bpmn配置文件
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="vacationProcess" name="vacation">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="usertask1" name="Write Vacation"></userTask>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <sequenceFlow id="flow2" name="" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
  </process>
</definitions>

测试类

package org.crazyit.activiti;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

/**
 * 任务提醒 
 * @author yangenxiong
 *
 */
public class Claim {

	public static void main(String[] args) {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 获取运行服务组件
		RuntimeService runtimeService = engine.getRuntimeService();
		// 流程存储服务组件
		RepositoryService repositoryService = engine.getRepositoryService();
		// 部署流程描述文件
		Deployment dep = repositoryService.createDeployment()
				.addClasspathResource("bpmn/vacation.bpmn").deploy();
		// 查找流程定义
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.deploymentId(dep.getId()).singleResult();
		// 启动流程
		ProcessInstance pi = runtimeService
				.startProcessInstanceById(pd.getId());
		// 查找任务
		Task task = taskService.createTaskQuery().processInstanceId(pi.getId())
				.singleResult();
		// 调用claim方法
		taskService.claim(task.getId(), "1");
		// 此处将会抛出异常
		taskService.claim(task.getId(), "2");
	}

}

任务完成
TaskService提供了多个完成任务的方法,对于第一个complete方法,只需要提供任务的ID即可,另外几个complete方法需要提供任务参数。当完成一个任务需要若干参数时,可以使用带参数的complete方法。例如现在有一个填写请假单的任务,完成这个任务时,需要提供请假天数等参数,那么可以使用带参数的complete方法,可以传入Map参数。假设现在有一个假期申请流程,需要由员工填写,经理审批,
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="vacationProcess" name="vacation">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="usertask1" name="Write Vacation"></userTask>
    <userTask id="usertask2" name="Audit"></userTask>
    <sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <sequenceFlow id="flow2" name="" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow3" name="" sourceRef="usertask2" targetRef="endevent1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_vacationProcess">
    <bpmndi:BPMNPlane bpmnElement="vacationProcess" id="BPMNPlane_vacationProcess">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35" width="35" x="130" y="180"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
        <omgdc:Bounds height="55" width="105" x="200" y="170"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
        <omgdc:Bounds height="55" width="105" x="360" y="170"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35" width="35" x="520" y="180"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="165" y="197"></omgdi:waypoint>
        <omgdi:waypoint x="200" y="197"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="305" y="197"></omgdi:waypoint>
        <omgdi:waypoint x="360" y="197"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="465" y="197"></omgdi:waypoint>
        <omgdi:waypoint x="520" y="197"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

测试类

package org.crazyit.activiti;

import java.util.HashMap;
import java.util.Map;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

/**
 * 任务提醒 
 * @author yangenxiong
 *
 */
public class Complete {

	public static void main(String[] args) {
		// 获取流程引擎实例
		ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
		// 获取任务服务组件
		TaskService taskService = engine.getTaskService();
		// 获取运行服务组件
		RuntimeService runtimeService = engine.getRuntimeService();
		// 流程存储服务组件
		RepositoryService repositoryService = engine.getRepositoryService();
		// 部署流程描述文件
		Deployment dep = repositoryService.createDeployment()
				.addClasspathResource("bpmn/vacation2.bpmn").deploy();
		// 查找流程定义
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.deploymentId(dep.getId()).singleResult();
		// 启动流程
		ProcessInstance pi = runtimeService
				.startProcessInstanceById(pd.getId());
		// 查找任务
		Task task = taskService.createTaskQuery().processInstanceId(pi.getId())
				.singleResult();
		// 调用complete方法完成任务,传入参数
		Map<String, Object> vars = new HashMap<String, Object>();
		vars.put("days", 2);
		// 设置临时的参数
		Map<String, Object> vars2 = new HashMap<String, Object>();
		vars2.put("temp", "temp var");
		taskService.complete(task.getId(), vars, vars2);
		// 再次查找任务
		task = taskService.createTaskQuery().processInstanceId(pi.getId())
				.singleResult();
		// 无法查询临时参数
		String tempVar = (String)taskService.getVariable(task.getId(), "temp");
		System.out.println("查询临时参数:" + tempVar);
		
		//得到参数
		Integer days = (Integer)taskService.getVariable(task.getId(), "days");
		if (days > 5) {
			System.out.println("大于5天,不批");
		} else {
			System.out.println("小于5天,完成任务,流程结束");
			taskService.complete(task.getId());
		}
	}

}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值