Skip to content
一、TaskService
  • TaskService

    • 对用户任务(UserTask)管理和流程控制
    • 设置用户任务(UserTask)的权限信息(拥有者、候选人、办理人)
    • 针对用户任务添加用户附件、任务评论和事件记录
  • TaskService对Task管理与流程控制

    • Task对象的创建,删除
    • 查询Task、并驱动Task节点完成执行
    • Task相关参数变量(variable)设置
  • TaskService设置Task权限信息

    • 候选用户(candidateUser)和候选组(candidateGroup)
    • 指定拥有人(Owner)和办理人(Assignee)
    • 通过claim设置办理人
  • TaskService设置Task附加信息

    • 任务附件(Attachment)创建与查询
    • 任务评论(Comment)创建与查询
    • 事件记录(Event)创建与查询
  • 相关方法

    image-20200717164044240

    image-20200717164227879

    image-20200717164325284

  • 相关详解

    • Task接口

      • 定义

        • 一个Task实例表示流程中的一个任务,与其他实例一样 ,Task是一个接口,并且遵守数据映射实体的命名规范。Task的实现类为TaskEntitylmpl,对应的数据库表为ACT_RU_TASK。
      • 创建与保存Task实例

        • 调用TaskService的newTask()与 newTask(String taskld)方法,可以获取一个Task实例。
        • 调用TaskService的saveTask(Task task)方法,如果保存的Task实例有ID值,则会使用该值作为Task数据的主键,没有的话,则由Activiti为其生成主键。
      • 删除任务

        • deleteTask(String taskld):根据Task的ID删除Task数据,调用该方法不会进行级联删除。
        • deleteTask(String taskId, boolean cascade):根据Task的ID删除Task数据,由调用者决定是否进行级联删除。
        • deleteTasks(Collection<String> taskIds):提供多个Task的ID进行多条数据删除,调用该方法不会进行级联删除。
        • deleteTasks(Collection<String> taskIds, boolean cascade):提供多个Task的ID进行多条数据删除,调用该方法会进行级联删除。
    • 任务权限

      • 设置候选用户组

        • 使用 ACT_RU_IDENTITYLINK表来保存这些权限数据,因此在调用设置流程权限API时,Activiti最终会往这个表中写入数据 。

          java
          // 绑定用户组与任务的 关系
          taskService.addCandidateGroup(String taskId, String groupId);
      • 设置候选用户

        • 候选用户是一“群”将会拥有或者执行任务权限的人,但是任务只允许一个人执行或者拥有,而任务的候选人则是指一个用户群体。

          java
          // 绑定用户与任务的关系
          taskService.addCandidateUser(String taskId, String groupId);
      • 权限数据查询

        • 查询用户组和用户的候选任务,可以使用TaskService的createTaskQuery方法,得到Task对应的查询对象。TaskQuery 中提供了taskCandidateGroup和taskCandidateUser方法,可以根据用户组或者用户的ID查询候选Task的数据 。先到权限数据表中查询与用户组或者用户关联了的数据 ,查询得到 taskld后,再到Task中查询任务数据并且返回。 如果得到了任务的 ID , 想查询相应的关系数据,则可以调用TaskService的getldentityLinksForTask方法,该方法根据任务ID查询IdentityLink集合。

          java
          // 根据用户组查询任务
          List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup(group.getId()) .list();
          // 根据用户查询任务
          tasks = taskService.createTaskQuery().taskCandidateUser(user.getId()).list();
          // 查询权限数据
          List<IdentityLink> links = taskService.getidentityLinksForTask(tasks.get(O).getId()) ;
      • 设置任务持有人

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

          java
          // 设置任务持有人 
          taskService.setOwner(task.getId(), user.getId());
      • 设置任务代理人

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

          java
          // 设置任务代理人
          taskService.setAssiqnee(task.getId(), user.getId());
      • 添加任务权限数据

        • addGroupldentityLink(String taskld, String groupld, String identityLinkType):添加用户组权限数据,第一个参数为任务 ID, 第二个参数为用户组ID,第三个参数为权限数据类型标识。

        • addUserIdentityLink(String taskId, String userId, String identityLinkType)::添加用户权限数据,第一个参数为任务 ID, 第二个参数为用户ID,第三个参数为权限数据类型标识。

          java
          // 调用addGroupIdentityLink方法
          taskService.addGroupIdentityLink(task.getId(), group.getId(), IdentityLinkType.CANDIDATE);
          // 调用addUserIdentityLink方法
          taskService.addUserIdentityLink(String taskId, String userId, String identityLinkType);
          • 使用addUserldentityLink方法将权限类型标识设置为CANDIDATE,其效果等同于调用addCandidateUser方法,将权限类型标识设置为OWNER,其效果等同于调用setOwner方法,将权限类型标识设置为ASSIGNEE,其效果等同于调用setAssignee方法。
          • 需要注意的是,将用户组设置为任务所有人 或者任务代理人,这并不合适,虽然可以成功调用方法,但其在删除权限数据时,将会抛出异常。
      • 删除用户组权限

        • deleteGroupIdentityLink(String taskId, String groupId, String identityLinkType):删除任务的权限数据,第一个参数为任务ID,第二个参数为用户组ID,第三个参数为权限ID。
          • 需要注意的是,如果在调用deleteGroupldentityLink方法时传入的权限类型标识是OWNER或者ASSIGNEE,那么将会抛出异常。
          • 如果在调用addGroupldentityLink方法时使用的是自定义的权限类型标识,那么在调用deleteGroupldentityLink方法进行删除时,要传入相同的权限类型标识才能删除权限数据。
        • deleteCandidateGroup(String taskId, String groupId):删除任务的候选用户组数据,第一个参数为任务ID,第二个参数为用户组ID。
      • 删除用户权限

        • deleteCandidateUser(String taskld, String userld):删除任务权限类型标识为"CANDIDATE"的用户权限数据。
        • deleteUserldentityLink(String taskld, String userld, String identityLinkType):删除任务权限类型为identityLinkType的用户权限数据。
    • 任务参数

      当一个任务被传递到执行人手中时,他需要知道该任务的全部信息,包括任务的基本信息(创建时间、内容等),还需要得到任务的相关参数。例如一个请假申请,请假的天数、开始时间等均为该申请的参数。编写这个请假申请的任务由请假申请人发起,在执行编写请假任务时,就需要设置这一系列的请假任务参数。

      • 设置参数方法setVariable方法

      • 参数类型

        • 基本参数类型

          • Boolean:布尔类型,参数类型标识为boolean。
          • Date:日期类型,参数类型标识为date。
          • Double:双精度类型,参数类型标识为double。
          • Integer:整型,参数类型标识为integer。
          • Long: 长整型,参数类型标识为long。
          • Null: 空值,参数类型标识为null。
          • Short: 短整型,参数类型标识为short。
          • String: 字符型,参数类型标识为string。
        • 序列化参数

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

          java
          TaskService taskService = activitiRule.getTaskService();
          Task task = taskService.newTask();
          taskService.saveTask(task);
          Map<String, Object> maps = new HashMap<>();
          maps.put("name", "zhangsan");
          maps.put("age", 19);
          taskService.setVariables(task.getId(), maps);
      • 获取参数方法getVariable()方法

      • 参数作用域

        • 当任务与流程绑定后,设置的参数均会有其作用域。
        • setVariable设置全局参数,setVariable设置的参数用getVariableLocal方法是获取不到的。
        • setVariableLocal设置当前任务参数,因为该方法设置了相应的taskId,这些参数只在任务存在的阶段可用,任务删除或者完成,这些参数均被删除,可到历史数据表中查询这些参数。
      • 数据对象

        • 在BPMN文件中,可以使用dataObject元素来定义流程参数。流程启动后,这些参数将会自动被设置到流程实例中,可以使用RuntimeService、TaskService的方法来查询这些参数。
    • 任务附件管理

      在实际生活中,许多流程都会带上一些任务相关的附件,例如报销流程有可能需要附上发票的单据,奖惩流程有可能会附上相关的说明文件。使用附件表ACT_HI_ATTACHMENT,保存任务附件数据,与其对应的实体类为AttachmentEntityimpl

      • 创建任务附件
        • createAttachment(String attachmentType, String taskld, String processlnstanceld, String attachmentName, String attachmentDescription, String url):创建任务附件,attachmentType为附件类型,由调用者定义;taskld为任务ID;processlnstanceld为流程实例ID;attachmentName为附件名称;attachmentDescription为附件描述;url为该附件的 URL地址。
        • createAttachment(String attachmentType, String taskld, String processlnstanceld, String attachmentName,String attachmentD escription, InputStream content):
      • 附件查询
        • getProcesslnstanceAttachments(String processlnstanceld):根据流程实例ID查询该流程实例下全部的附件,返回Attachment集合。
        • getTaskAttachments(String taskId):根据任务ID查询该任务下全部的附件,返回Attachment集合。
        • getAttachment(String attachmentld):根据附件的ID查询附件数据,返回一个Attachment对象。
        • getAttachmentContent(String attachmentld):根据附件的ID获取该附件的内容,返回附件内容的输入流对象,如果调用的是第二个createAttachment方法(传入附件的InputStream),那么调用该方法才会返回非空的输入流,否则将返回 null。
      • 删除附件
        • 调用TaskService的deleteAttachment(String attachmentld),只会删除附件表中的数据及相应的历史数据,不会删除资源表中的数据。
    • 任务评论与事件记录

      在日常的工作流程中,随着业务的进行,可能会夹杂着一些个人的流程意见,使用Activiti, 可以将任务或者流程的评论保存到评论表( ACT_HI_COMMENT)中,接口为Comment,实现类为CornrnentEntityImpl。 评论表会保存两种类型的数据:任务评论和部分事件记录。

      • Comment对象

        • 一个Comment实例表示评论表的一条数据,CornmentEntitylmpl实际上实现了两个接口 :Event和Comment。如果该对象作为Event接口返回,则可以认为它返回的是事件记录的数据。如果该对象作为Comment返回,则其返回的是评论数据。在使用过程中,只可以得到Comment或者Event接口的实例,Comment和Event接口中只定义了一系列的getter方法用于获取相关信息,设置属性的方法,均被放到TaskService中实现。
      • 新增任务评论

        • 使用TaskService的addComment方法。
      • 数据查询

        • getComment(String commentld):根据评论数据ID查询评论数据。
        • getTaskComments(String taskld): 根据任务ID查询相应的评论数据。
        • getTaskEvents(String taskld):根据任务ID查询相应的事件记录。
        • getProcesslnstanceComments(String processlnstanceld):根据流程实例ID查询相应的评论(事件)数据。
        • getTaskEvents方法返回Event的实例集合,而 getTaskComments和getProcesslnstanceComments方法返回Comment 的实例集合。
  • 任务声明与完成

    • 任务声明实际上是指将任务分配到某个用户下,即将该用户作为该任务的代理人,可以使用TaskService的claim方法进行任务代理人指定(效果类似于setAssignee方法)。当一个任务需要完结时,可以调用TaskService的complete方法,指定该任务已经完成,让整个流程继续往下进行。

      • 任务声明

        java
         taskService.claim(task.getId(), "1");
        • 如果第二次调用claim方法时传入的用户ID与第一次调用时传入的用户ID不一致, 则抛出异常,如果一致则不会。
      • 任务完成

        • complete(String taskId);
        • complete(String taskId, Map<String, Object> variables);
        • complete(String taskId, Map<String, Object> variables, Map<String, Object> transientVariables);
        • complete(String taskId, Map<String, Object> variables, boolean localScope);
二、相关测试代码
java
package com.laogoubi.coreapi;

import com.google.common.collect.Maps;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.*;
import org.activiti.engine.test.ActivitiRule;
import org.activiti.engine.test.Deployment;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.junit.Rule;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

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

/**
 * @ClassName TaskServiceTest
 * @Description TaskService
 * @Author eastern
 * @Date 2020/7/3 下午12:20
 * @Version 1.0
 **/
public class TaskServiceTest {
	private static final Logger logger = LoggerFactory.getLogger(TaskServiceTest.class);

	@Rule
	public ActivitiRule activitiRule = new ActivitiRule();

	@Test
	@Deployment(resources = {"diagrams/my-process-task.bpmn20.xml"})
	public void testTaskService(){
		Map<String, Object> variables = Maps.newHashMap();
		variables.put("message", "my test message");
		ProcessInstance processInstance = activitiRule.getRuntimeService().startProcessInstanceByKey("my-process", variables);
		TaskService taskService = activitiRule.getTaskService();
		Task task = taskService.createTaskQuery().singleResult();
		logger.info("task = {}", ToStringBuilder.reflectionToString(task, ToStringStyle.JSON_STYLE));

		logger.info("task.description = {}", task.getDescription());

		taskService.setVariable(task.getId(), "key1", "value1");
		taskService.setVariableLocal(task.getId(), "localKey1", "localValue1");

		// 获取变量的方式
		Map<String, Object> taskServiceVariables = taskService.getVariables(task.getId());
		Map<String, Object> taskServiceVariablesLocal = taskService.getVariablesLocal(task.getId());

		Map<String, Object> processVariables = activitiRule.getRuntimeService().getVariables(task.getExecutionId());

		logger.info("taskServiceVariables = {}", taskServiceVariables);
		logger.info("taskServiceVariablesLocal = {}", taskServiceVariablesLocal);
		logger.info("processVariables = {}", processVariables);

		// 流程完成
		Map<String, Object> completeVar = Maps.newConcurrentMap();
		completeVar.put("ckey1", "cValue1");
		taskService.complete(task.getId());

		Task result = taskService.createTaskQuery().taskId(task.getId()).singleResult();
		logger.info("result = {}", result);

	}

	@Test
	@Deployment(resources = {"diagrams/my-process-task.bpmn20.xml"})
	public void testTaskServiceUser(){
		Map<String, Object> variables = Maps.newHashMap();
		variables.put("message", "my test message");
		ProcessInstance processInstance = activitiRule.getRuntimeService().startProcessInstanceByKey("my-process", variables);
		TaskService taskService = activitiRule.getTaskService();
		Task task = taskService.createTaskQuery().singleResult();
		logger.info("task = {}", ToStringBuilder.reflectionToString(task, ToStringStyle.JSON_STYLE));
		logger.info("task.description = {}", task.getDescription());

		taskService.setOwner(task.getId(), "user1");
//		taskService.setAssignee(task.getId(), "user2");

		List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("user2").taskUnassigned().listPage(0, 100);
		for (Task taskTemp : tasks) {
			try {
				taskService.claim(task.getId(), "user2");
			} catch (Exception e) {
				logger.info(e.getMessage(), e);
			}
		}

		List<IdentityLink> identityLinksForTask = taskService.getIdentityLinksForTask(task.getId());
		for (IdentityLink identityLink : identityLinksForTask) {
			logger.info("identityLink = {}", identityLink);
		}

		List<Task> users = taskService.createTaskQuery().taskAssignee("user2").listPage(0, 100);
		for (Task user : users) {
			Map<String, Object> completeVar = Maps.newConcurrentMap();
			completeVar.put("ckey1", "cValue1");
			taskService.complete(user.getId(), completeVar);
		}

		users = taskService.createTaskQuery().taskAssignee("user2").listPage(0, 100);
		logger.info("是否存在 {}", CollectionUtils.isEmpty(users));

	}

	@Test
	@Deployment(resources = {"diagrams/my-process-task.bpmn20.xml"})
	public void testTaskAttachment(){
		Map<String, Object> variables = Maps.newHashMap();
		variables.put("message", "my test message");
		ProcessInstance processInstance = activitiRule.getRuntimeService().startProcessInstanceByKey("my-process", variables);
		TaskService taskService = activitiRule.getTaskService();
		Task task = taskService.createTaskQuery().singleResult();
		taskService.createAttachment("url", task.getId(), task.getProcessInstanceId(), "name", "desc", "/url/test.png");
		List<Attachment> taskAttachments = taskService.getTaskAttachments(task.getId());
		for (Attachment taskAttachment : taskAttachments) {
			logger.info("taskAttachment = {}", ToStringBuilder.reflectionToString(taskAttachment, ToStringStyle.JSON_STYLE));
		}
	}

	@Test
	@Deployment(resources = {"diagrams/my-process-task.bpmn20.xml"})
	public void testTaskComment(){
		Map<String, Object> variables = Maps.newHashMap();
		variables.put("message", "my test message");
		ProcessInstance processInstance = activitiRule.getRuntimeService().startProcessInstanceByKey("my-process", variables);
		TaskService taskService = activitiRule.getTaskService();
		Task task = taskService.createTaskQuery().singleResult();
		taskService.setOwner(task.getId(), "user1");
		taskService.setAssignee(task.getId(), "user2");
		taskService.addComment(task.getId(), task.getProcessInstanceId(),"recoed note 1");
		taskService.addComment(task.getId(), task.getProcessInstanceId(),"recoed note 2");
		List<Comment> taskComments = taskService.getTaskComments(task.getId());
		for (Comment taskComment : taskComments) {
			logger.info("taskComment = {}", ToStringBuilder.reflectionToString(taskComment, ToStringStyle.JSON_STYLE));
		}

		List<Event> taskEvents = taskService.getTaskEvents(task.getId());
		for (Event taskEvent : taskEvents) {
			logger.info("taskEvent = {}", ToStringBuilder.reflectionToString(taskEvent, ToStringStyle.JSON_STYLE));
		}
	}

}
  • 资源文件:

    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: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="my-process">
    		<startEvent id="start" />
    		<sequenceFlow id="flow1" sourceRef="start" targetRef="someTask" />
    		<userTask id="someTask" name="Activiti is awesome!" activiti:candidateStarterUsers="user1,user2">
    			<documentation>some Task ${message}</documentation>
    		</userTask>
    		<sequenceFlow id="flow2" sourceRef="someTask" targetRef="end" />
    		<endEvent id="end" />
    	</process>
    </definitions>

Released under the MIT License.