activiti6.0+springboot 项目搭建

首先搭建流程设计器

  1. 官网下载activiti6.0,下载下来之后解压后找到activiti-app.war文件放入本地tomcat中运行,访问路径为(localhost:8080/activiti-app),账号:admin 密码:test

  2. 修改设计器数据库,设计器默认的使用的是h2数据库,需要修改tomcat中项目\activiti-app\WEB-INF\classes\META-INF\activiti-app\activiti-app.properties,选择需要的数据库设置

#datasource.driver=org.h2.Driver
#datasource.url=jdbc:h2:mem:activiti;DB_CLOSE_DELAY=-1

datasource.driver=com.mysql.jdbc.Driver
datasource.url=jdbc:mysql://**********:3306/****?characterEncoding=UTF-8

datasource.username=****
datasource.password=****

搭建activiti项目(该项目使用springboot,所以和常规搭建可能稍有不同)

  1. 引入pom

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

  1. 关闭activiti自动部署(因为我们使用流程设计器部署,不使用具体文件访问方式),在application.properties中加入以下代码
spring.activiti.check-process-definitions=false
  1. 设计流程

    遇到问题:

    在设计流程中我们发现在定义审批人时遇到了一个无法忽略的问题————无法找到指定审批人,意思是设置了一个审批人的参数,没办法根据申请人获取到相应的审批人,或许你会觉得根据申请人的部门查找不就行了吗,但是像财务、董事长之类的在部门之上的的部门和人,就没办法找对并且获取了。

    解决:

    暂时的办法是将每个部门的所有流程全部分开,每个审批人使用角色定义,这就解决了获取不到对应审批人的问题,但是紧接着又有一个问题,申请人如何才能申请到自己部门的对应流程呢,又是一个大问题,问题好像又回到了原点,但是有何开始的问题有一些不同,从同一个流程获得不同的用户角色,变成了从一个部门获取相应部门流程,那就简单了,只需要创建一个表将他们对应起来就可以了,但是这涉及到三个字段,一个是部门id,一个是流程id,还有一个类型id(就是这个流程是干什么的,借款、报销。。。),三个信息组成一条对应流程片,好像有点复杂,管理页面也有点难做。

举个例子:


activiti6.0+springboot 项目搭建_第1张图片
部门与流程联系.png

从这张图上能清楚的看出我们面临的问题,因为每个部门都有同样的流程类型,但是又不同的流程具体实现,如何才能从三个选择减少成两个甚至一个呢?

鉴于这一点,我们只能在流程id上做手脚了,将流程id根据不同的类型使用不同的名字前缀,这样就从三个字段变成了两个,例如:流通部门国内借款流程,我们将id设为 gnjk_ltjk ,这样我们就能根据id的前缀gnjk查到该流程为国内借款类型。可能有人会问为什么不直接将部门也放在流程id里面,这样就不用创建表了,也想过这个问题,而且也可以这样做,但未免太死板了,完全不给以后融合流程机会了,毕竟现在一个部门就要n条流程还是有弊端的,就是重复流程过多的问题,所以先暂时这样吧

  1. 表设计

    在正式走流程之前还有一个要做,就是业务表的设计,尽管activiti自带了大量的表,但是还是要设计关于自己项目的业务表才行。具体的业务表就不详细说了,无非是报销、借款。。。之类的,但是有一个小技巧(前人经验),因为具体业务的不同,所有会有多个表,在审批时查找起来会相当的麻烦,而且也不易于列表展示,所以提出一个共有的审核主表,该主表有流程id、业务id和具体业务的简要信息,这样才查找审批列表时直接来一个表中查找就可以了,而在审批需要查看详情时,又可以根据不同的类型去对应的表中查找审批,相当方便。

CREATE TABLE `s_auditor_core` (
  `id` varchar(50) NOT NULL COMMENT '使用nextval函数获取',
  `business_id` varchar(50) DEFAULT NULL COMMENT '业务id',
  `process_id` varchar(64) DEFAULT NULL COMMENT '流程id',
  `create_user` bigint(10) DEFAULT NULL COMMENT '用户id',
  `create_date` datetime DEFAULT NULL COMMENT '创建时间',
  `sketch` varchar(255) DEFAULT NULL COMMENT '备注',
  `apply_type` varchar(255) DEFAULT NULL COMMENT '申请类型(和权限表中的权限一致)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

  1. 启动流程


    activiti6.0+springboot 项目搭建_第2张图片
    国内借款_流通部门

    因为用的是springboot,所以不需要手动的创建xml文件,然后配置一大堆东西,只需要引包,然后再需要使用的地方声明接口就好了,非常的方便。

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;

然后在获得对应的流程id后,将业务详细信息,流程,审批主表一次执行保存,一次完整的流程启动就完成了。

     * 
     * @param bGnjk  国内借款业务参数
     * @param htmlSign 模块标记,这个为借款、报销。。。类型
     * @return
     * @throws Exception
     */
    public CommonDomain gnjkProcessStart(BGnjk bGnjk,String htmlSign)throws Exception {
        CommonDomain commonDomain = new CommonDomain<>();
        //配置activiti需要参数
        Map m = new HashMap();
        m.put(ACTKeyTool.MONEY,bGnjk.getMoney());
        List process = sUserMapper.getProcessByUser(bGnjk.getCreateUser().intValue(),htmlSign);
        if(process==null||process.size()!=1){
            throw new Exception("流程启动获得多条流程");
        }
        //启动activiti
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(process.get(0).getProcess_key(),m);
        //保存业务数据
        bGnjkMapper.insertSelective(bGnjk);


        //保存审批主表
        SAuditorCore sAuditorCore = new SAuditorCore();
        sAuditorCore.setApplyType(htmlSign);
        sAuditorCore.setBusinessId(bGnjk.getId());
        sAuditorCore.setCreateDate(new Date());
        sAuditorCore.setCreateUser(bGnjk.getCreateUser());
        sAuditorCore.setProcessId(processInstance.getId());
        sAuditorCore.setSketch(bGnjk.getDescribe());
        SAuditorCoreMapper.insertSelective(sAuditorCore);

        //返回结果封装
        commonDomain.setStatuscode(PublicMsg.ResultCode.SUCCESS.value());
        commonDomain.setMsg(PublicMsg.SUCCESS_INFO);
        return commonDomain;
    }

当完成这一步的时候,业务详细表中有了业务的数据,act_ru_task中有个流程信息,并指向第一个usertask节点--流通部门经理,审批主表中有业务详细表和流程信息表的唯一id,并将它们联系起来,现在开始,流程就正式启动了

  1. 审批流程

    不推荐使用activiti的taskService原生api,因为一般每个项目都会有自己的用户表,虽然activiti支持替换用户表,但是配置起来貌似有些麻烦,所以根本没有鸟他。下面是一些taskService常用的查询方法源码

query.taskCandidateGroup 源码:
                        SELECT DISTINCT 
                          RES.* 
                        FROM
                          ACT_RU_TASK RES 
                          INNER JOIN ACT_RU_IDENTITYLINK I 
                            ON I.TASK_ID_ = RES.ID_ 
                        WHERE RES.ASSIGNEE_ IS NULL 
                          AND I.TYPE_ = 'candidate' 
                          AND (I.GROUP_ID_ IN (?))
                          
query.taskCandidateOrAssigned 源码:
    select distinct RES.* from ACT_RU_TASK RES
    left join ACT_RU_IDENTITYLINK I on I.TASK_ID_ = RES.ID_
    WHERE (
      RES.ASSIGNEE_ = ?
      or (
        RES.ASSIGNEE_ is null
        and (
          I.USER_ID_ = ?
          or
          I.GROUP_ID_ IN (
            select g.GROUP_ID_ from ACT_ID_MEMBERSHIP g where g.USER_ID_ = ?
          )
        )
      )
    )
    
    

在源码中可以看到基本上查的是ACT_RU_TASK和ACT_RU_IDENTITYLINK表,因为我们根本没有用activit的用户表,所以如果使用它的api会让sql查询变得奇怪起来,因为我们在设计流程的时候已经指定了会使用角色来充当审批人,那么这个角色只会出现在ACT_RU_IDENTITYLINK表的USER_ID_字段上,下面是我根据我们的实际业务编写的sql

    SELECT
     s.`name`apply_type,   -- 类型名称
      a.`business_id`,     -- 业务详细表id
      a.`create_date`,     -- 申请时间
      u.`name` user,       -- 申请人
      a.`id`,              -- 审批主表d
      a.`process_id`,      -- 流程id
      a.`sketch`,          -- 简介
      i.`TASK_ID_` task_id -- 任务id
    FROM
      `act_ru_task` t
      JOIN `act_ru_identitylink` i
        ON t.`ID_` = i.`TASK_ID_`
      JOIN `s_auditor_core` a ON a.process_id=t.PROC_INST_ID_
      JOIN `s_permission` s ON a.`apply_type`=s.`permission`
      JOIN `s_user` u ON u.`id`=a.`create_user`
    WHERE FIND_IN_SET(i.`USER_ID_`, #{roles})
    order by  a.`create_date` desc
    

其中s_auditor_core为审批主表,s_permission申请类型(客串,其实是权限表),s_user用户表,act_ru_task任务表,act_ru_identitylink任务关联人员表,而传进来的#{roles}也是一个登录人的所有角色集合

接下来就是审批的具体实施了,因为觉得不需要再有一个审批意见表,所以使用act_hi_comment来代替:

设置/获得 审批人和意见
设置:
审批人一般为登录人
Authentication.setAuthenticatedUserId("审批人");
taskService.addComment(task.getId(), task.getProcessInstanceId(), "批注");
获得:
List hais = historyService
                 .createHistoricActivityInstanceQuery()
                 .processInstanceId(id)
                 .activityType("userTask").list();
         for (HistoricActivityInstance hai : hais) {
                String historytaskId = hai.getTaskId();
                List comments = taskService.getTaskComments(historytaskId);
                for (Comment comment : comments) {
                    System.out.println(comment.getFullMessage()+"ren"+comment.getUserId());
                }

在同意时使用正常的审批就ok了:

taskService.complete(task.getId());

但是在拒绝时需要特殊操作:

删除正在运行的流程,主要用于拒绝操作
runtimeService.deleteProcessInstance(流程实力id,删除原因(可为空))

为什么要这样做:
因为activit并不支持突然的拒绝操作,如果想要实现这样的效果,就要在每条审批中设置一条拒绝的流程线并指向结束节点,而且还必须要用到排他网管,不然流程就不会结束,所以,鉴于这个情况的出现,我们采用直接删除流程,但流程的历史信息不回被删除,而且流程也会被置为结束,只需要在审批前填写流程审批意见就行了

遗留问题

  1. 将各部门流程融合成一个,现在重复太多不便管理
  2. 获取下一个审批人,因为换成activiti6.0后,pvm包被整体移除,所以原来的方式好像行不通了
  3. BpmnModel 操作

你可能感兴趣的:(activiti6.0+springboot 项目搭建)