一、TaskService
TaskService
- 对用户任务(UserTask)管理和流程控制
- 设置用户任务(UserTask)的权限信息(拥有者、候选人、办理人)
- 针对用户任务添加用户附件、任务评论和事件记录
TaskService对Task管理与流程控制
- Task对象的创建,删除
- 查询Task、并驱动Task节点完成执行
- Task相关参数变量(variable)设置
TaskService设置Task权限信息
- 候选用户(candidateUser)和候选组(candidateGroup)
- 指定拥有人(Owner)和办理人(Assignee)
- 通过claim设置办理人
TaskService设置Task附加信息
- 任务附件(Attachment)创建与查询
- 任务评论(Comment)创建与查询
- 事件记录(Event)创建与查询
相关方法
相关详解
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。
- deleteGroupIdentityLink(String taskId, String groupId, String identityLinkType):删除任务的权限数据,第一个参数为任务ID,第二个参数为用户组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集合
javaTaskService 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方法,指定该任务已经完成,让整个流程继续往下进行。
任务声明
javataskService.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);
二、相关测试代码
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>