美文网首页收藏
Spring学习之整合Activiti(二)

Spring学习之整合Activiti(二)

作者: a_simple_coder | 来源:发表于2019-03-28 18:57 被阅读76次

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

    一、页面结构介绍

    进入模型新建页面如下:

    image.png

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

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

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

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

    二、画流程

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

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

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

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

    image.png

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

    image.png

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

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

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

    • 点击分配用户:
    image.png
    • 在弹出框里输入Assignee表达式,别忘了点击保存:
    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}
    • nrOfInstances 为处理该节点的总人数,流程中会根据集合的size自动填充,
    • agreeMembers_businessManagerConfirm 该节点同意的人数
    • backMembers_businessManagerConfirm 该节点驳回人数
    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

    相关文章

      网友评论

        本文标题:Spring学习之整合Activiti(二)

        本文链接:https://www.haomeiwen.com/subject/olofbqtx.html