activiti的使用

此文章将描述activiti的使用,activiti的集成,部署、启动、节点流转、驳回、批注等功能的基本使用。

目录

activiti的使用

背景

技术栈

参考

接口

先接入activiti吧

a、添加依赖

b、项目resources目录下新建activiti.cfg.xml

 c、application.yml中srping下添加:

 emm~~现在来绘制activiti流程图

1、绘制工具

2、我们会用到的图形

3、按照图形进行绘制,连线

4、设置process的id、name

5、设置每个节点的id、name

6、设置顺序流的name、condition

7、设置UserTask的候选人Canditate Users

8、生成xml、png文件

接口详解

 1、创建数据库表

2、流程部署

3、流程删除

4、查询已部署的流程

5、启动流程

6、查询待处理的任务

7、查询处理中的任务

8、查询已处理的任务

9、任务处理(签收/退回/审核)

10、查询历史备注



activiti的使用

我把activiti集成在了spring-boot项目中,首先就是新建一个springboot项目,这里不再赘述springboot项目的搭建。

背景

在平常的业务开发中,遇到了一些审核流程,而且比较多,自己设计了几个数据库表,基本能满足自己的需求,但由于业务可能会越来越大,越来越复杂,就想到用activiti来解决我的问题,于是学习了activiti。

技术栈

springboot 1.X + mybatis 3.X + activiti6.0.0

参考

在学习过程中从网上找了一些博客进行参考,我也有一份电子书,文件太大就不上传了。

  1. 部署流程资源的三种方式 https://blog.csdn.net/zjx86320/article/details/50234707
  2. Activiti数据库表结构 https://blog.csdn.net/hj7jay/article/details/51302829
  3. IntelliJ IDEA安装Activiti插件并使用 https://blog.csdn.net/gozhuyinglong/article/details/80336765
  4. activiti 任务节点 处理人设置 https://blog.csdn.net/qq_30739519/article/details/51225067
  5. Activiti6实现自由跳转 https://segmentfault.com/a/1190000013952695 我应用此代码时加入了comment备注
  6. Activiti工作流的流转任务和结束任务 https://blog.csdn.net/liuming134/article/details/80453907
  7. Activiti添加批注(comment)信息 https://www.cnblogs.com/cxyj/p/3898535.html
  8. Activitivi之启动流程/完成任务的时候设置流程变量 https://www.cnblogs.com/shyroke/p/8000757.html
  9. Activiti任务认领 https://www.cnblogs.com/boulder/p/3658528.html
  10. Activiti之历史活动查询和历史任务查询和流程状态查询 https://www.cnblogs.com/shyroke/p/7994931.html

接口

我的项目完成了以下接口:

1、创建数据库表

2、流程部署

3、流程删除

4、查询已部署的流程

5、启动流程

6、查询待处理的任务

7、查询处理中的任务

8、查询已处理的任务

9、任务处理(签收/退回/审核)

10、查询历史备注

先接入activiti吧

6.0和5.21有什么区别?

在应用当中发现:

1)6.0删除了pvm包,ActivityImpl用不了了,实现节点跳转时就不能用这个了

别的区别,em~~~自己去看吧

a、添加依赖


   org.activiti
   activiti-spring-boot-starter-basic
   6.0.0

b、项目resources目录下新建activiti.cfg.xml


 
    
 
        
        
        
        
        
    
 

 c、application.yml中srping下添加:

spring:
    jpa:
      show-sql: true
      properties:
            hibernate:
              dialect: org.hibernate.dialect.MySQL5Dialect
              format_sql: true
              use_sql_comments: true
      hibernate:
        ddl-auto: update

 emm~~现在来绘制activiti流程图

1、绘制工具

我用的是idea的actiBPM,虽然有点缺点,但是总归还能用,没有安装的请安装此plugin。

2、我们会用到的图形

1.StartEvent                开始事件

2.EndEvent                  结束事件

3.UserTask                   用户任务

4.ExclusiveAateway    排他网关

5.sequenceFlow         顺序流

其它的图形我没用到,有需要的小伙伴可以自己去研究~~

先上传一个成品截图:

 这是我从实际业务中拿出来的流程图,我也做了牺牲了~

activiti的使用_第1张图片

3、按照图形进行绘制,连线

4、设置process的id、name

点击bpmn画布空白处,即可看到属性设置,见上图

5、设置每个节点的id、name

点击每个节点,即可看到属性设置

6、设置顺序流的name、condition

点击每个连线,即可看到属性设置

condition的表达式格式为:${pass == true}

7、设置UserTask的候选人Canditate Users

结合业务,Canditate Users的表达式格式为:${face_audit},表示面审候选人,其他UserTask候选人可按照各自功能进行命名:

${face_audit}               面审

${final_first_audit}        终审1

${final_second_audit}  终审2审

${final_third_audit}      终审3审

${res_invest}               尽调

${loan_apply}              放款申请

${loan_audit}               放款审核

为什么设置Canditate Users?

为了结合业务,实现任务的节点的签收/退回,

当Canditate Users有值而assignee为空,则为待处理;

当Canditate Users有值而assignee不为空,则为处理中。

assignee为处理人。

8、生成xml、png文件

xml文件:直接复制demo.bpmn文件,并且把文件名改为demo.xml文件即可,可以以xml格式看到这个流程。

png文件:右键demo.xml,Diagrams-->show BPMN2.0 Designer,右键空白处-->Export to file 即可。

上传一张png给大家看:

activiti的使用_第2张图片

好了,demo绘制完毕,接下来看代码~

接口详解

我贴出来的是service的代码,要看完整代码,请看文章末尾gitee地址。

 1、创建数据库表

    /**
     * 创建数据库表
     * 根据配置文件activiti.cfg.xml创建ProcessEngine
     */
    @Override
    public void createProcessEngine() {
        ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        ProcessEngine engine = cfg.buildProcessEngine();
    }

执行上述接口后,对应的数据库里会自动生成activiti相关的数据库表。

2、流程部署

需要传入参数deploymentName 部署名称,force是否强制部署,error是个map。

其中参数deploymentName为xml文件中process标签的id。

/**
     * 流程部署
     *
     * @param deploymentName 流程部署名称
     * @param force          是否强制部署,true强制,false非强制
     * @param error
     */
    @Override
    public void deployProcess(String deploymentName, boolean force, ErrorInfo error) {
        //判断流程是否已存在
        Deployment deploymentExist = processEngine.getRepositoryService().createDeploymentQuery().deploymentName(deploymentName).singleResult();
        //流程已存在
        if (null != deploymentExist) {
            if (force) {
                error.msg = "流程已存在,进行强制部署。";
                //强制部署时,先删除原有的部署流程
                processEngine.getRepositoryService()//true:级联删除
                        .deleteDeployment(deploymentExist.getId(), true);
                log.info("【%s】流程强制部署,删除部署id【%d】", deploymentName, deploymentExist.getId());
            } else {
                error.msg = "流程已存在,不更新部署";
                log.info("【%s】流程已存在,不更新部署", deploymentName);
                return;
            }
        }

        //流程不存在,直接部署
        Deployment deployment = processEngine.getRepositoryService().createDeployment()
                .name(deploymentName)
                .addClasspathResource("processes/" + deploymentName + ".bpmn")
                .addClasspathResource("processes/" + deploymentName + ".png")
                .deploy();
        error.msg += "流程已部署";
        log.info("流程已部署,部署名称:" + deploymentName + ",部署ID:" + deployment.getId() + ",部署时间:" + deployment.getDeploymentTime());
    }

3、流程删除

有流程部署,就会有流程删除,看代码:

/**
     * 删除流程定义
     *
     * @param deploymentId 流程部署ID_
     * @param error
     */
    @Override
    public void deleteDeployment(String deploymentId, ErrorInfo error) {
        //流程定义名称
        String deploymentName = "";
        List deploymentList = processEngine.getRepositoryService().createDeploymentQuery().list();
        if (null != deploymentList && deploymentList.size() > 0) {
            for (Deployment de : deploymentList) {
                if (de.getId().equalsIgnoreCase(deploymentId)) {
                    deploymentName = de.getName();
                    break;
                }
            }
        }

        //级联删除流程
        processEngine.getRepositoryService().deleteDeployment(deploymentId, true);
        error.code = 1;
        error.msg = "流程定义[" + deploymentName + "]已级联删除";
        log.info("流程定义[%s]已级联删除", deploymentName);
    }

4、查询已部署的流程

已成功部署的流程定义会放在act_re_deployment里。

/**
     * 查询全部流程定义
     *
     * @return
     */
    @Override
    public List getDeploymentList() {
        //结果集
        List list = new ArrayList<>();
        //deployment结果集
        List deploymentList = processEngine.getRepositoryService().createDeploymentQuery().list();
        if (null != deploymentList && deploymentList.size() > 0) {
            for (Deployment de : deploymentList) {
                DeploymentDto dto = new DeploymentDto();
                dto.setId(de.getId());
                dto.setName(de.getName());
                dto.setCreateTime(de.getDeploymentTime());

                list.add(dto);
            }
        }
        return list;
    }

5、启动流程

启动流程时,除了传业务所需的参数外,第一个节点所涉及到的参数变量也必须传,否则流程无法启动:

variables.put("face_audit", "faceAudit");

我把节点必传的参数写在了CarProcessCanditateUsersEnum.java

    /**
     * 启动流程
     *
     * @param instanceKey 流程process的id,例:demo
     */
    @Override
    public ProcessInstance startProcess(String instanceKey, Map variables) {
        RuntimeService runtimeService = processEngine.getRuntimeService();

        //可根据id、key、message启动流程
        ProcessInstance myProcess_1 = runtimeService.startProcessInstanceByKey(instanceKey, variables);
        //流程实例ID
        log.info("流程实例ID:" + myProcess_1.getId());
        //流程实例ProcessInstanceId
        log.info("流程实例ProcessInstanceId:" + myProcess_1.getProcessInstanceId());
        //流程实例ProcessDefinitionId
        log.info("流程实例ProcessDefinitionId:" + myProcess_1.getProcessDefinitionId());
        return myProcess_1;
    }

6、查询待处理的任务

我在activiti的使用中,UserTask任务用到了两个处理字段:Canditate Users 和 assignee

简单理解:当Canditate Users有值,assignee为null时,任务为待处理

    /**
     * 查询当前任务办理人的当前任务--待处理
     * 

* 两个参数都不传,则查询所有节点待办任务 * * @param candidateUser 当前任务候选角色变量 * @return */ @Override public List pendingTask(String candidateUser) { //task list结果集 List tasks = new ArrayList<>(); //封装成dto list List personnelTaskDtoList = new ArrayList<>(); if (StringUtils.isNotBlank(candidateUser)) { //与任务相关的Service tasks = processEngine.getTaskService() .createTaskQuery()//创建一个任务查询对象 .taskCandidateUser(candidateUser) .orderByTaskCreateTime() .desc() .list(); } else { //查询所有待办任务 //与任务相关的Service tasks = processEngine.getTaskService() .createTaskQuery()//创建一个任务查询对象 .orderByTaskCreateTime() .desc() .list(); } if (tasks != null && tasks.size() > 0) { for (Task task : tasks) { //待处理 if (StringUtils.isBlank(task.getAssignee())) { PersonnelTaskDto entity = new PersonnelTaskDto(); entity.setTaskId(task.getId()); entity.setAssignee(task.getAssignee()); entity.setTaskName(task.getName()); entity.setCreateTime(task.getCreateTime()); entity.setProcessInstanceId(task.getProcessInstanceId()); personnelTaskDtoList.add(entity); } } } //将结果放到pageDto return personnelTaskDtoList; }

7、查询处理中的任务

我在activiti的使用中,UserTask任务用到了两个处理字段:Canditate Users 和 assignee

简单理解:当Canditate Users有值,assignee也有值时,任务为处理中

    /**
     * 查询当前任务办理人的当前任务--处理中
     * 

Constants.NODE_STATUS_HANDLING * 两个参数都不传,则查询所有节点处理中任务 * * @param candidateUser * @return */ @Override public List handlingTask(String candidateUser) { //task list结果集 List tasks = new ArrayList<>(); //封装成dto list List personnelTaskDtoList = new ArrayList<>(); TaskService taskService = processEngine.getTaskService(); if (StringUtils.isNotBlank(candidateUser)) { //与任务相关的Service tasks = taskService .createTaskQuery()//创建一个任务查询对象 .taskCandidateUser(candidateUser) .orderByTaskCreateTime() .desc() .list(); } else { //查询所有待办任务 //与任务相关的Service tasks = taskService .createTaskQuery()//创建一个任务查询对象 .orderByTaskCreateTime() .desc() .list(); } if (tasks != null && tasks.size() > 0) { for (Task task : tasks) { //处理中 if (StringUtils.isNotBlank(task.getAssignee())) { PersonnelTaskDto entity = new PersonnelTaskDto(); entity.setTaskId(task.getId()); entity.setAssignee(task.getAssignee()); entity.setTaskName(task.getName()); entity.setCreateTime(task.getCreateTime()); entity.setProcessInstanceId(task.getProcessInstanceId()); personnelTaskDtoList.add(entity); } } } return personnelTaskDtoList; }

8、查询已处理的任务

历史活动涉及到的表为act_hi_*

各种历史活动查询可参考资料:历史活动查询

我这里用了:

HistoryService historyService = processEngine.getHistoryService();

historyService.createHistoricActivityInstanceQuery()

可根据自己需求进行自行修改。

    /**
     * 查询已处理的任务
     *
     * @param assignee 处理人
     * @return
     */
    @Override
    public List handledTask(String assignee) {
        //封装成dto list
        List personnelTaskDtoList = new ArrayList<>();

        HistoryService historyService = processEngine.getHistoryService();
        List list = new ArrayList<>();
        //处理人不为空时查询处理人所属的已处理任务
        if (StringUtils.isNotBlank(assignee)) {
            list = historyService.createHistoricActivityInstanceQuery()
                    .taskAssignee(assignee)
                    .finished()
                    .desc()
                    .list();
        } else {
            //处理人为空时查询所有已处理任务
            list = historyService.createHistoricActivityInstanceQuery()
                    .list();
        }
        if (null != list && list.size() > 0) {
            for (HistoricActivityInstance instance : list) {
                PersonnelTaskDto entity = new PersonnelTaskDto();
                entity.setTaskId(instance.getId());
                entity.setAssignee(instance.getAssignee());
                entity.setTaskName(instance.getActivityName());
                entity.setCreateTime(instance.getStartTime());
                entity.setProcessInstanceId(instance.getProcessInstanceId());

                personnelTaskDtoList.add(entity);
            }
        }
        return personnelTaskDtoList;
    }

9、任务处理(签收/退回/审核)

流程启动之后,整个流程会停留在第一个节点,以demo为例,会停留在【面审】

然后此节点的候选人进行任务签收

当有此节点权限的人签收此任务后,可以用代码禁止再被其他人签收(我这里没做)

    /**
     * 节点处理,0:审核,1:签收,2:退回
     *
     * @param user
     * @param jumpToNodeDto
     * @param error
     */
    @Override
    public void handleNode(User user, JumpToNodeDto jumpToNodeDto, ErrorInfo error) {
        ActivitiServiceImpl impl = new ActivitiServiceImpl();
        //审核
        if (jumpToNodeDto.getIsClaim().equals(Constants.NODE_AUDIT)) {
            Map variables = prepareNodeJumpVaria(jumpToNodeDto);
            //流转
            impl.jumpToNode(jumpToNodeDto.isInOrder(), jumpToNodeDto.getTaskId(), jumpToNodeDto.getTargetFlowElementId(), jumpToNodeDto.getComment(), variables, error);
        } else {
            //签收/退回
            impl.claimActivity(jumpToNodeDto, user, error);
        }
    }

 节点跳转,我这里做了顺序的的流转和跳转,因为在审核流程中任务有可能被驳回,也有可能最高权限的人一键审核通过。

跳转的代码,可以下载我的代码看一下,这里就不贴出来了。

    /**
     * 节点跳转/流转
     *
     * @param isInOrder           是否是顺序执行,true:下一节点;false:任意节点(包括驳回和最高权限的审核通过)
     * @param taskId              任务id,对应act_ru_task中的ID_
     * @param targetFlowElementId 目标的节点id
     * @param comment             审批意见
     * @param variables           传入下一节点时所需参数
     */
    public void jumpToNode(boolean isInOrder, String taskId, String targetFlowElementId, String comment, Map variables, ErrorInfo error) {
        //获取taskService
        TaskService taskService = processEngine.getTaskService();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (StringUtils.isBlank(task.getAssignee())) {
            error.code = -1;
            error.msg = "任务未签收,不允许跳转/流转";
            return;
        }

        //当前任务节点顺序流转
        if (isInOrder) {
            //添加审批意见
            taskService.addComment(taskId, null, comment);
            try {
                taskService.complete(taskId, variables);
            } catch (Exception e) {
                error.code = -1;
                error.msg = "节点流转失败taskId[" + taskId + "]";
                e.printStackTrace();
                log.error("节点流转失败taskId[%s]", taskId);
            }
            error.code = 1;
            error.msg = "节点流转成功";
        }

        //任意节点
        if (!isInOrder) {
            if (StringUtils.isBlank(targetFlowElementId)) {
                error.code = -1;
                error.msg = "节点跳转targetFlowElementId不能为空";
                log.error(error.msg);
                return;
            }
            ActivitiJump activitiJump = new ActivitiJump();
            try {
                activitiJump.jump(taskId, targetFlowElementId, comment, variables);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("节点跳转失败taskId[%s]", taskId);
                error.code = -1;
                error.msg = "节点跳转失败taskId[" + taskId + "]";
            }
            error.code = 1;
            error.msg = "节点跳转成功";
        }
    }

任务的签收和退回

    /**
     * 签收/退回任务
     *
     * @param jumpToNodeDto
     * @param user
     * @param error
     */
    public void claimActivity(JumpToNodeDto jumpToNodeDto, User user, ErrorInfo error) {
        TaskService taskService = processEngine.getTaskService();
        String isClaim = jumpToNodeDto.getIsClaim();
        //签收
        if (isClaim.equals(Constants.NODE_CLAIM)) {
            taskService.setAssignee(jumpToNodeDto.getTaskId(), user.getName());
            //添加备注
            taskService.addComment(jumpToNodeDto.getTaskId(), null, jumpToNodeDto.getComment());
            error.code = 1;
            error.msg = "签收成功";
        }
        //退回
        if (isClaim.equals(Constants.NODE_BACK)) {
            taskService.setAssignee(jumpToNodeDto.getTaskId(), null);
            taskService.addComment(jumpToNodeDto.getTaskId(), null, jumpToNodeDto.getComment());
            error.code = 1;
            error.msg = "退回成功";
        }
    }

10、查询历史备注

在节点流转/跳转、签收、退后等操作过程中,肯定会填写一些批注、原因,那么怎么查询这些批注呢?

要用HistoryService

   /**
     * 查询历史备注
     *
     * @param candidateUser
     * @return
     */
    public List getHistoryTaskComments(String candidateUser) {
        List taskComments = new ArrayList<>();

        HistoryService historyService = processEngine.getHistoryService();
        TaskService taskService = processEngine.getTaskService();
        List list = new ArrayList<>();
        //处理人不为空时查询处理人所属的已处理任务
        if (StringUtils.isNotBlank(candidateUser)) {
            list = historyService.createHistoricTaskInstanceQuery()
                    .taskCandidateUser(candidateUser)
                    .orderByHistoricTaskInstanceStartTime()
                    .desc()
                    .list();
        } else {
            //处理人为空时查询所有已处理任务
            list = historyService.createHistoricTaskInstanceQuery()
                    .orderByHistoricTaskInstanceStartTime()
                    .desc()
                    .list();
        }
        if (null != list && list.size() > 0) {
            for (HistoricTaskInstance instance : list) {
                String hisTaskId = instance.getId();
                List comments = taskService.getTaskComments(hisTaskId);
                if (null != comments && comments.size() > 0) {
                    taskComments.addAll(comments);
                }

            }
        }
        return taskComments;
    }

至此,基本讲述完了,贴上我的gitee代码:

https://gitee.com/dayydream/activiti.git

我只是一个小菜鸟,欢迎各位大神来沟通

你可能感兴趣的:(activiti)