activiti5入门(四)流程图之六大构成元素

来源:互联网 发布:mac ps做图 编辑:程序博客网 时间:2024/05/29 12:02

1、Events事件

2、Tasks任务(任务无法删除,只能结束。执行任务只要taskId即可,不需要当前用户,因为不排除用户离职的情况)

3、Gateways网关

4、Container容器

5、Connection连接

6、Artifacts说明

-----------------------------------------------------------

仅涉及Tasks任务的流程图实例:

根据用户查询当前任务:

String userId = (String) request.getSession().getAttribute("userId");这个userid和流程图xml格式中的 activiti:assignee一致
List<Task> taskLists = taskService.createTaskQuery().taskAssignee(userId).list();

如果我们在流程图里没有指定用户,而是用${userId}指定,那么最好每个usertask都要用不同的${xxx},因为如果相同,就说明这个userTask是同一个人

那我们怎么用起来呢?

在流程启动的时候就要把它用进去:

Map<String, Object> map = new HashMap<>();
map.put("userId", "admin1");map.put()...

runtimeService.startProcessInstanceByKey(pdKey,bussinessId,map);

这样就在代码中指定了用户,这样每个流程实例都可能会有不同的人

注意,这里使用了bussinessId,这个值的作用,可以理解为它是另外一张业务表(非activiti表)的某个主键字段,这个字段会被很多其他业务表引用,当我们查询act_hi_procinst表时就会用到BUSINESS_KEY_这个字段。不用查,用processInstence.getBusinessKey();即可


用户执行任务:

String taskId = request.getParameter("taskId");
taskService.complete(taskId);

可见只要知道任务id即可,不关心真正的用户是谁

每执行一次任务,act_ru_task表中不会增加一条数据,而是更新一条数据,且把ID都更新了,那原来那条数据保存在哪里?保存在act_hi_taskinst中。

----------------------------------

ACT_RU_EXECUTION表中的executionID一般与流程实例ID相同,所以流程实例有多少条,他就有多少条,当一个流程走到某个节点,此时这个节点既有usertask又有消息边界事件,那么ACT_RU_EXECUTION中会多出一个一条数据,假设ID为0001,这条数据的父ID是该流程,在act_ru_task表和act_ru_event_subscr都会出现一条数据,表中的执行ID不再是流程ID,而是0001。正常情况下,执行任务ID和流程ID都是一致的。当存在不知道要走哪个时,就会多出一条数据,即流程ID的延伸流程。出现这种情况如何获取执行ID?

Execution execution = runtimeService.createExecutionQuery().messageEventSubscriptionName("全局消息名").singleResult();

runtimeService.messageEventReceived("全局消息名", execution.getId());//相当于执行了消息边界事件,没有执行usertask。

--------------------------

根据流程实例id查询当前任务(流程实例理解为譬如一张请假单)

List<Task> taskLists = taskService.createTaskQuery().processInstanceId(piId).list();

-------------------

查询所有历史流程实例,涉及act_hi_procinst表

HistoryService historyService = pe.getHistoryService();
List<HistoricProcessInstance> hpiLists = historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceId().asc().list();

根据某历史流程实例id查询对应的历史任务(包括没有执行完的历史流程)涉及act_hi_taskinst表

HistoryService historyService = pe.getHistoryService();
List<HistoricTaskInstance> htiLists = historyService.createHistoricTaskInstanceQuery().processInstanceId(hpiId).list();

----------------------------------------------------

当usertask的用户为候选人时

例如xml中为 activiti:candidateUsers="xxx1,xxx2,xxx13" : 指定候选人.(拉模式),

那么在用流程实例查询时是找不到指定人的,一定要有个指定人去认领任务才有用

TaskQuery tq = taskService.createTaskQuery();

List<Task> candidateUserTaskLists = tq.taskCandidateUser(userId).list();
// 迭代任务
for (Task task : candidateUserTaskLists){
// 领取任务
taskService.claim(task.getId(), userId);//领取任务时,也就是所谓的任务分配时,因为这个时候有assignee了
}

// 根据任务的处理人查询任务
List<Task> taskLists = taskService.createTaskQuery().taskAssignee(userId).list();//注意这里又要去重新获取一把任务查询,因为不重新获取,查的还是老数据,有种默认可重复读的感觉

-----------------------------------------------

当usertask的用户为候选组,且用了变量${group}时,那么在启动流程实例时,

map.put("group", "g1");//这里g1用字符串仅表示组名,不代表组员。因为业务中最后可能是根据用户名查找到组名,然后通过组名查找到这个组的所有任务

runtimeService.startProcessInstanceById(pdId, map);

查询组任务:

String userId = (String) request.getSession().getAttribute("userId");
Map<String, Object> map = new HashMap<>();
String[] arr = { "a", "b", "c" };
List<String> asList = Arrays.asList(arr);
map.put("g1", asList);//这里只是模拟表示g1这个组有abc三个用户
String group = null;
for (Entry<String, Object> entry : map.entrySet()) {
List<String> list = (List<String>) entry.getValue();
if (list.contains(userId)) {//根据用户找到用户所在组
group = entry.getKey();
}
}
List<Task> taskLists1 = new ArrayList<>();
if (group != null) {
taskLists1 = taskService.createTaskQuery().taskCandidateGroup(group).list();//根据用户所在组名找到所有任务
}


for (Task task : taskLists1) {
taskService.claim(task.getId(), userId);//第一个登录的用户将认领任务,同时这个组就不在有这个任务,即taskCandidateGroup(group).list()将没有任务
}
List<Task> taskLists2 = taskService.createTaskQuery().taskAssignee(userId).list();// 当用户认领后,那么act_ru_task中这个任务的Assignee就是他了,它就能查到。没有领取任务的用户就查不到任务,除非是其它非组任务

request.setAttribute("taskLists", taskLists2);

request.getRequestDispatcher("/userTasks.jsp").forward(request, response);

-----------------------------------------------------------

userTask的监听器

总共有4种:

create表示当创建任务时触发。

assignment表示当任务被分配后触发。

complete表示当任务完成后触发。

activiti中的表达式是${}形式,也有#{},后者在监听器中的表达式中有使用,也叫流程变量,在一个流程中有效。在启动流程时或在该usertask之前要做好初始化。放map中。

要使用监听器,我们必须在流程图中找到usertask里面的listener,然后新建,指定我们自定义的监听器。我们写的监听器要实现TaskListener。

以创建时定时器为例子:

public class CreateTaskListener implements TaskListener {
private Expression name;//这个变量是在流程图中指定的,可以用来传入到java代码中
private Expression age;
@Override
public void notify(DelegateTask delegateTask) {//DelegateTask 称为委派任务
System.out.println("事件:" + delegateTask.getEventName());
System.out.println(name.getValue(delegateTask));//使用getValue的方法获取流程图中监听器的变量
System.out.println(age.getValue(delegateTask));
// 分配任务
String userId = (String) name.getValue(delegateTask);
delegateTask.setAssignee(userId);//分配任务到某个人
// 分配完成后,分配监听器开始启动
}

delegateTask的api有很多,比如获取所有流程变量等。

除了java类型的定时器,还有#{}表达式,比如#{user.xxx()}就是一个。我们创建一个User类实现Serializable,因为要存数据库,所以要实现序列化。#{user.xxx()}里面是能传参数的,比如#{user.xxx(‘james’)}。他也分create、create和complete。它是怎么找到user的。一样在启动流程实例时,使用map,map.put("user", new User());

除了,java,expression,还有与spring连用的delegate expression委托表达式,以后会说。

usertask不仅仅有task listener,也有execution listener,与task listener的区别是,后者只有两个阶段,start和end,start会比creat还早。另外实现的监听接口不一样,后者是ExecutionListener。

监听器中获取引擎的方式:delegatetask.getExecution().getEngineServices()就是引擎(ProcessEngine)

taskUser中的表单我们很少用,它要配合监听器使用,为的是在监听器中能获取表单服务。但表单一般我们都有自己的表。


taskuser里面还有一个多实例,即表示一个任务必须多个用户都做好后才能走到下一步。

有几个选项:

Sequential为true表示有顺序,必须一个一个做;为false表示无顺序,用户不必等前一个用户。

collection是一个变量名,如userList。在流程启动时赋值,如

List<String> userList = new ArrayList<>();
userList.add("admin1");
userList.add("admin2");
userList.add("admin3");
userList.add("admin4");
userList.add("admin5");
map.put("userList", userList);

elementVariable是一个变量名(必须的),在assignee用${admin}这个表示后,在multiInstance中就能用admin代表${admin}了

completionCondition条件:比如有5个人完成,我现在只有三个人完成,我也算他任务完成,那就要用${nrOfCompletedInstances/nrOfInstances>=0.6}表示。其中nrOfCompletedInstances和nrOfInstances是activiti在act_ru_variable中生成的字段名,表示已完成实例和总实例。在流程图中我们不需要指定Loop,因为collection里面就能知道循环次数

-----------------------------------------------------------------------

其它任务类型:

ScriptTask脚本任务(自动触发),写一段javascript脚本

ServiceTask服务任务(自动触发),写一个类实现ServiceTaskDelegate,和自定义监听器实现监听器接口类似

ManualTask手工任务(自动触发)

ReceiveTask接收任务(信号触发).这4种我们都很少用

邮件任务(挺有用):例子

依赖

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
<version>1.4</version>
</dependency>

在部署前调用ProcessEngineConfiguration的

.setMailServerDefaultFrom("xxx@dzkf.com").setMailServerHost("dzkf.com")
.setMailServerUsername("xxx").setMailServerPassword("xxx");

在流程图中选择mail task,在配置中用流程变量指定发送者,接受者等,如${to},${from},${subject}(主题)。它的正文有html格式或者普通文本格式

最后在启动流程实例时

map.put("to", "zzz@dzkf.com");
map.put("from", "xxx@dzkf.com");
map.put("subject", "邮件任务");

即可。

-------------------------------------------

Events事件

用得最多的是Start Event,可以在流程图中定义发起人,但我们可以不用这样做,我们一般将第一个任务看成是流程发起人

TimerStartEvent定时开始事件,它的定时时间由activiti特殊规定,但在使用前,要调用ProcessEngineConfiguration.setJobExecutorActivate(true)激活定时任务

MessageStartEvent消息开始事件,要先定义一个全局消息,在空白处点击,然后就可以在properties界面找到message,定义消息了。然后在开始事件中引用这个消息,启动流程实例要用message,如runtimeService.startProcessInstanceByMessage("xxx");xxx就是前面全局消息的name值。

SignalStartEvent信号开始事件:要先定义一个全局信号,在空白处点击,然后就可以在properties界面找到signal,然后在开始事件中引用这个消息,动流程实例要用signal,

runtimeService.signalEventReviced("xxx");xxx就是前面全局信号的name值。

结束事件 ErrorEndEvent错误结束事件用于子流程。与ErrorBoundaryEvent错误边界事件(接收错误码)子流程连用

边界事件,只能放在任务上,比如任务不处理了,那么就可以由边界事件来完成

TimerBoundaryEvent定时边界事件,比如<timeDuration>PT10S</timeDuration>就是延迟10秒后做

 MessageBoundaryEvent消息边界事件上面有提到过

SignalBoundaryEvent信号边界事件与上面是雷同的

定义全局信号,然后在边界事件引用它,

 /** 根据信号名查询执行对象 */

Execution execution = runtimeService.createExecutionQuery().signalEventSubscriptionName("mySignal").singleResult();
/** 信号事件接收完成当前信号边界事件 */
runtimeService.signalEventReceived("全局信号名", execution.getId());

 ErrorBoundaryEvent错误边界事件在子流程的时候说

中间捕获事件:

TimerCatchingEvent定时捕获事件

MessageCatchingEvent消息捕获事件

SignalCatchingEvent信号捕获事件

NoneThrowingEvent空引发事件

这个事件意义不大

-----------------------------



原创粉丝点击