收藏

Spring学习之整合Activiti(二)

2019-03-28  本文已影响76人  a_simple_coder

上一篇Spring学习之整合Activiti(一)已经可以进入模型的新建页面了,在本篇幅中,咱们主要学习如何创建模型。

一、页面结构介绍

进入模型新建页面如下:

image.png

左边为组件列表,上方为模型可用的一些工具菜单,右下方为流程的相关信息(可编辑),之后新建的流程的相关节点信息也是在这里显示,右上方为流程的工作区。

先说一下设计器的操作三部曲

  1. 从左侧的仓库中选择组件(可以展开多个分类)
  2. 拖拽组建到工作区并调整位置
  3. 点击组件在右侧的下方设置组件属性

下面咱们以一个例子详细介绍设计流程图(其他流程根据具体业务需求类似操作):

二、画流程

1. 指定流程名称和流程相关信息
指定流程名称和流程相关信息
2. 开始节点

从左侧的仓库中选择开始事件,拖到工作区并调整好位置,设置节点id和名称(可填)。

画开始节点
3. 发起请求的用户任务节点

如下图所示,在画完开始节点后,点击红色的小黑人(用户任务),表示下一个节点为用户任务节点:

image.png

如下图,指定id(保证在整个流程中唯一。,必填),名称(必填):

image.png

可以看到,在这个节点的下方,有很多属性,我们可以根据业务需求来设置这些属性。
在本例中,该节点为发起人节点,为用户任务节点,所以我们需要为其设置执行人,为节点设置执行人有两种方法:

  1. 在属性中配置分配用户表达式;
  2. 代码设置:task.setAssignee(userId);

这里我们采用第一种,设置属性“分配用户”:

image.png image.png

说明:
第一个Assignee:节点指定执行人,大括号里的applyuserid为变量,在之后启动流程时需要为其赋值,否则报异常。
第二个 Candidate users:节点候选人,可以为多个人,用逗号隔开。表示在该节点可能有多个人处理该任务(视具体需求而定),代码中用

task.addCandidateUsers(Collection<String> candidateUsers);

第三个 Candidate groups:节点候选组,可以为多个组,且组内的每个人都可以处理该节点任务(视具体需求而定)。

task.addCandidateGroups(Collection<String> candidateGroups);

发起人节点到这里我们配置完毕。发起人提出要求后接下来就需要审批节点,有些流程只需要一级审批点,有些则需要多级审批,则流程中我们需要根据需求画多个审批节点。

4. 审批节点

同画发起人节点,点击小黑人表名下一节点为用户任务节点:

image.png

我们用任务监听器为当前用户任务节点设置执行人,所以属性不需要配置分配用户,直接配置create任务监听器:

按照下图中所标顺序配置即可。
Event类型选的是create,因为设置该节点执行人需要在初始化该节点之前设置,即创建该节点时就指定。
因为我们用spring管理bean,将监听类交由spring管理, 所以这里选择代理表达式的方式: Delegate Expression:${cusTaskListener}

image.png

CusTaskListener.java为:

package net.northking.activiti.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Resource;

import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Component;

import net.northking.activiti.entity.CusUserTask;
import net.northking.service.activiti.CusUserTaskService;
import net.northking.util.StringUtil;

@Component
public class CusTaskListener implements TaskListener {
    private static final long serialVersionUID = 1L;

    @Resource
    protected RepositoryService repositoryService;

    @Resource
    private CusUserTaskService cusUserTaskService;

    @Resource
    private RuntimeService runtimeService;

    @Override
    public void notify(DelegateTask delegateTask) {
        setUserTasks(delegateTask);
    }

    
    /**
     * 设置用户节点处理人
     * 
     * @param delegateTask
     */
    private void setUserTasks(DelegateTask delegateTask) {

        try {
            String processInstanceId = delegateTask.getProcessInstanceId();
            ProcessInstance pi = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId).singleResult();
            final String businessKey = pi.getBusinessKey();
            List<CusUserTask> taskList = this.cusUserTaskService.findByProcDefKey(businessKey);
            String taskDefinitionKey = delegateTask.getTaskDefinitionKey();
            
            for (CusUserTask userTask : taskList) {
                String taskKey = userTask.getTaskDefKey();
                if (taskDefinitionKey.equals(taskKey)) {
                    setAssigneeToUsersTask(delegateTask, businessKey, userTask);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 普通用户节点设置特定执行人或候选人<br/>
     * 只要有一人通过即为通过
     * @param delegateTask
     * @param businessKey
     * @param projectId
     * @param userTask
     */
    private void setAssigneeToUsersTask(DelegateTask delegateTask, final String businessKey, 
            CusUserTask userTask) throws Exception{

        String taskType = userTask.getTaskType();
        String userIds = userTask.getCandidate_ids();
        String groupIds = userTask.getGroup_id();
        
        switch (taskType) {
            case CusUserTask.TYPE_ASSIGNEE: {
                System.out.println("CusTaskListener assignee userIds: " + userIds);
                delegateTask.setAssignee(userIds);
                break;
            }
            case CusUserTask.TYPE_CANDIDATEUSER: {
                System.out.println("CusTaskListener 候选用户审批 userIds: " + userIds);
                String[] assigneeIds = null;
                if (StringUtil.isNotEmpty(userIds)) {
                    assigneeIds = userIds.split(",");
                }
                List<String> assigneeList = getAssigneeList(delegateTask, assigneeIds);
                if(null == assigneeList)return;
                
                delegateTask.addCandidateUsers(assigneeList);
                break;
            }
            case CusUserTask.TYPE_CANDIDATEGROUP: {
                System.out.println("CusTaskListener 候选组审批 groupIds: " + groupIds);
                /**
                 * 设置候选人,一个通过即为通过 由于我们采用的是项目的用户管理系统,所以这里不能直接设置候选组,
                 * 需要根据groupId到项目的用户系统查询具体的用户然后作为候选人设置到工作流
                 */
                String[] candidateUserIds = getCandidateIds(businessKey, groupIds);
                List<String> assigneeList = getAssigneeList(delegateTask, candidateUserIds);
                if(null == assigneeList)return;
                delegateTask.addCandidateUsers(assigneeList);
                break;
            }
        }
    }


    private List<String> getAssigneeList(DelegateTask delegateTask, String[] candidateUserIds) throws Exception {
        List<String> assigneeList = new ArrayList<>();
        if (null != candidateUserIds) {
            assigneeList = Arrays.asList(candidateUserIds);
        }
        System.out.println("CusTaskListener getAssigneeList candidateUserIds:" + candidateUserIds
                + ";assigneeList:" + assigneeList.size());
        if (assigneeList.size() < 1) {
            autoPass(delegateTask, delegateTask.getTaskDefinitionKey());
            return null;
        }
        return assigneeList;
    }


    /**
     * 自动跳过
     * @param delegateTask
     * @param taskDefKey
     * @throws Exception
     */
    private void autoPass(DelegateTask delegateTask, String taskDefKey) throws Exception {
        ActivityImpl nextNodeInfo = ProcessDefinitionCache.get().getNextNodeInfo(repositoryService,runtimeService,delegateTask.getProcessInstanceId(),taskDefKey);
        System.out.println("CusTaskListener setAssigneeList nextNodeid:"+nextNodeInfo.getId());
        if(nextNodeInfo.getId().contains("reapply")) {
            delegateTask.setVariable(nextNodeInfo.getId(), "false");//reapply_projectManagerAudit等等
        }else {
            delegateTask.setVariable(nextNodeInfo.getId(), "true");//isPass_projectManagerAudit等等
        }
    }

    private String[] getCandidateIds(String businessKey, String groupIds) {
//      final String businessId = businessKey.contains(":") ? businessKey.split(":")[1] : "";//业务id:可能是projectId,也可能是userId等等
        String[] roleCodes = groupIds.split(",");
        return roleCodes;
    }
}

5. 网关事件:通过与驳回

既然是审批,就有通过与驳回,这叫网关事件。
如图点击,表示下一节点是一个网关:

image.png

为了在代码中获取下一节点的信息,需要为其设置一个唯一标识id,如果一个流程中有多个网关,建议网关id与当前审批节点(前一个节点)的id关联,比如当前审批节点的id为productInnovationCenterAudit,则该网关节点的id为isPass_productInnovationCenterAudit.

image.png

注意:
画网关节点时,建议先画驳回分支,再画通过分支。

同前几个节点一样,因为驳回后可以重新申请,则下一节点为用户任务节点,即点击小黑人,并调整重新申请节点位置:

image.png

点击驳回分支线:
设置驳回分支名称

image.png

点击流条件设置流向该分支条件:

image.png 驳回流条件.png

大括号中的isPass_productInnovationCenterAudit即为网关id。

6. 重新申请节点

因为需求要审批驳回后流到发起人,发起人可以重新申请,也可以取消申请。

设置重新申请节点如下图所示,由于执行人与发起人一致,为当前登录用户,所以只需设置分配用户:

重新申请.png
7. 网关事件:重新申请与取消申请

操作同上一个网关,并调整位置:
命名规则同上一个网关,由于当前审批节点为重新申请节点,id为businessManagerReApply,为了直观,前缀用了isReapply

image.png

我们先画重新申请:
重新申请后,流到需求审批节点,操作:

image.png image.png image.png image.png image.png 设置名称.png 设置流条件.png image.png
8. 需求确认节点

现在画需求审批通过分支,点击网关:

image.png

由于审批通过后,进行需求确认,该节点仍然是一个用户任务节点,点击小黑人:
由于该需求确认节点可能需要多个人进行处理该节点,即为会签节点,而且通过该节点的条件为:一人驳回则退回到需求审批节点,所有人通过(没有先后)则通过到下一节点,这里是所有人通过即归档。

需求确认.png

我们按照图中顺序说明:

  1. id:唯一标识,businessManagerConfirm
  2. 名称:不用多说;
  3. 多实例类型:通过上述,通过该节点的条件可知,该节点为多实例节点,且并行(没有先后),所以选择Parrallel;
  4. 集合:即当前多实例节点的执行人集合,可以写死(确认确实有这么个人),不确定的话建议为表达式:
    ${assigneeList_businessManagerConfirm} 命名规则类似网关,防止流程中有多个多实例节点,在代码中设置多实例节点的实际执行人集合时好区分。
  5. 元素变量:集合中的单个元素变量,assignee_businessManagerConfirm,注意,该处的变量需要和分配用户中的变量保持一致。
  6. 完成条件:完成该节点的条件,通过上述可知:
    ${agreeMembers_businessManagerConfirm == nrOfInstances || backMembers_businessManagerConfirm>0}
  1. 分配用户:将会签节点任务分配到具体的个人,${assignee_businessManagerConfirm},注意,该处的变量需要和元素变量保持一致。
9. 网关事件:通过与驳回

先画驳回:

image.png image.png image.png
10. 归档

该流程要求由发起人归档,所以分配用户为applyuserid

image.png
11. 结束节点
  1. 归档后流程结束:
    点击实心黑圆圈,添加一个结束节点。
image.png image.png
  1. 取消申请后结束流程
    点击重新申请的网关,选择结束Event,新增一个结束节点:
image.png

拖动结束节点至归档后的结束节点的下方:

image.png

删除新增的结束节点,拉箭头至归档后的结束节点的正中心:

image.png image.png

最后的流程如下:

image.png

点击左上角的保存,输入模型名称和描述信息,保存即可。

image.png

即在数据库的act_re_modelact_ge_bytearray表会插入该模型信息:

image.png
上一篇下一篇

猜你喜欢

热点阅读