欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Spring Boot 与 Activity7 集成,实现简单的审批流程

最编程 2024-07-17 21:43:24
...

由于客户对于系统里的一些新增数据,例如照片墙、照片等,想实现上级逐级审批通过才可见的效果,于是引入了Acitivity7工作流技术来实现,本文是对实现过程的介绍讲解,由于我是中途交接前同事的这块需求,所以具体实现方式和代码编写我暂时先按前同事的思路简单介绍,不代表我个人看法。

参考文章:

springboot+Activiti7整合实践 (一)_vue2集成activit7-****博客

org.springframework.security.core.userdetails.UsernameNotFoundException,三步解决Activiti7和Security冲突问题_cause: org.springframework.security.core.userdetai-****博客

Activiti7笔记(二)Activiti7一共涉及到25张表,哪些操作会涉及哪些表,每张表的作用是什么_activiti7数据表详细解读-****博客

Activiti7笔记(一)Activiti7是什么,入门流程操作的代码实现-腾讯云开发者社区-腾讯云 (tencent.com)

文章目录

  • 一、引入依赖
  • 二、修改配置文件
  • 三、解决Activity7和Security框架冲突
  • 四、启动项目,生成activity的数据库表
    • 审批流过程
  • 五、画流程图
  • 六、部署流程
  • 七、发起流程
    • 1.示例
    • 2.相关代码
  • 八、审批过程
    • 1.审核通过
      • 1.示例
      • 2.相关代码
    • 2.审批不通过
      • 1.相关代码
  • 九、业务表相关字段
    • 1.任务表
    • 2.业务数据表
  • 十、业务表数据权限变化

一、引入依赖

        <!--activity7工作流-->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>${activity.starter.version}</version>
            <exclusions>
                <exclusion>
                    <!-- 项目引入了mybatis-plus,则需要排除 -->
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.activiti.dependencies</groupId>
            <artifactId>activiti-dependencies</artifactId>
            <version>${activity.starter.version}</version>
            <type>pom</type>
        </dependency>

        <!-- 生成流程图 -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-image-generator</artifactId>
            <version>${activity.starter.version}</version>
        </dependency>

二、修改配置文件

  spring:
      #activity工作流配置
      activiti:
        # 自动部署验证设置:true-开启(默认)、false-关闭
        check-process-definitions: false
        # 保存历史数据
        history-level: full
        # 检测历史表是否存在
        db-history-used: true
        # 关闭自动部署
        deployment-mode: never-fail
        # 对数据库中所有表进行更新操作,如果表不存在,则自动创建
        # create_drop:启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
        # drop-create:启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
        database-schema-update: true
        # 解决频繁查询SQL问题
        async-executor-activate: false

db-history-userdhistory-level,建议按图中配置,方便查询工作流历史记录

三、解决Activity7和Security框架冲突

场景:由于Activity升级到7版本后,引入了Security来用于权限校验,但是本项目自身已经引入了Security框架,于是发生了冲突

【如果项目用的不是security框架,例如用的shiro,需要在启动项排除,参考:springboot+Activiti7整合实践 (一)_vue2集成activit7-****博客

  • 网上有很多的相关报错和解决办法,【参考:org.springframework.security.core.userdetails.UsernameNotFoundException,三步解决Activiti7和Security冲突问题_cause: org.springframework.security.core.userdetai-****博客

  • 但本项目主要报错的地方是 根据userid获取用户任务列表这个逻辑,使用activity7原生api方法会报错;

  • 解决办法(前同事处理的):不去排除冲突,直接另外新写一个方法去查activity7的表,实现上述的逻辑

本项目
在这里插入图片描述
在这里插入图片描述

网上其他做法(用的activity原生的api):

从Activiti工作流中检索所有用户任务_在activiti 7中获取流程实例的所有任务_如何在Activiti工作流中的单独实例中强制顺序执行任务 (tencent.com)
在这里插入图片描述

四、启动项目,生成activity的数据库表

引入依赖、配置好后,第一次启动会在数据库生成相关的表
在这里插入图片描述

具体介绍:Activiti7笔记(二)Activiti7一共涉及到25张表,哪些操作会涉及哪些表,每张表的作用是什么_activiti7数据表详细解读-****博客

本项目主要用到的表:

  • 流程部署表 :[ACT_RE_PROCDEF]

    每对一个流程图部署后,会记录在该表里(部署过程下面会讲到)

    在这里插入图片描述

  • 历史流程实例表:[ACT_HI_PROCINST]

    流程一次从头到尾执行,对应一个流程实例,流程结束时会保留下来

  • 运行时流程执行对象表 :[ACT_RU_EXECUTION]

    流程实例与执行对象的关系:‌一个流程实例在执行过程中,‌如果流程包含分支或聚合,‌那么执行对象的数量可以多个【至少有2条数据,其中第1条是对应历史流程实例表】。‌这是因为流程实例在运行过程中可能会产生多个并行的执行路径,‌每个路径上的任务或活动都可以视为一个执行对象。‌例如,‌在一个具有多个分支的审批流程中,‌不同的审批人可能会同时处理不同的分支任务,‌这些分支任务就代表了多个执行对象

    Activiti工作流学习(二)流程实例、执行对象、任务 - 百度文库 (baidu.com)

    这个表里面主要记录的是当前已经执行到哪个节点了,把对应的节点对象记录到这个里面

    流程结束后,这张表对应的数据会清除

    在这里插入图片描述

在这里插入图片描述

  • 运行时节点人员数据信息表 :[ACT_RU_IDENTITYLINK]

    运行时用户关系信息,存储任务节点与参与者的相关信息
    也就是只要当前任务有参与者,就会将参与者的信息保存到这个表,多个人参与,保存多个信息

    在这里插入图片描述

  • **运行时任务节点表:[ACT_RU_TASK] **

    一个流程有多个节点,到某一个节点的时候,都会更新这个表,将当前节点的数据更新到这个表

    在这里插入图片描述

审批流过程

  1. 部署流程,到ACT_RE_PROCDEF表看有没有新增一条对应的数据
  2. 启动流程实例,执行流程对象(可能多个),到ACT_HI_PROCINSTACT_RU_EXECUTION表看有没有新增对应数据
  3. 完成整个流程,过程主要看ACT_RU_TASKACT_RU_IDENTITYLINK表的更新情况

五、画流程图

  • IDEA安装插件:

    在这里插入图片描述

  • 在resource下创建一个目录process存放流程图文件

    在这里插入图片描述

  • 右键,新建流程图文件

    在这里插入图片描述

  • 具体画图过程省略,这里介绍画完之后的流程图的重点信息

    2个审批节点的流程图为例(增加、减少节点都需要另外画图部署)

    1. 定义流程的id和名字(后续代码可以取到)

      在这里插入图片描述

    2. 开始节点

      在这里插入图片描述

    3. 审批节点

      第1个审批节点:

      在这里插入图片描述

      第2个审批节点,同上

      在这里插入图片描述

    4. 结束节点

      在这里插入图片描述

    5. 二级节点之后的网关

      这里二级审批完之后,会出现2种不同的可能走向(审批通过,继续到一级审批节点;审批不通过,直接结束),所以需要加上一个网关

      【由于时间较赶等原因,本项目不做回退的实现,所以审核不通过都是直接走向结束节点(一级审批节点通过或不通过都是走向结束节点)】

      在这里插入图片描述

    6. 节点之间或节点与网关之间的连接线

      在这里插入图片描述

      在这里插入图片描述

      在这里插入图片描述

      重点注意:网关后的线

      定义一个条件变量,当SecondJudge=true时(代码赋值),即审核通过,会走向 一级审核 节点,否则走向结束节点,如下图所示

      【当二级组织发起审核,但是选择最终审批组织也是二级的时候,审核通过会赋值SecondJudge=false,直接走结束节点】

      在这里插入图片描述

      在这里插入图片描述

六、部署流程

本项目采用的是通过接口,手动部署的方式【每新建或修改流程图都要调用一次接口来部署(后续打算优化成自动部署或者定时任务调用接口部署)】

    @ApiOperation("手动部署照片审核")
    @GetMapping("/photo/process/{typeLevel}")
    public String auditingPhotoProcessByTypeLevel(@PathVariable("typeLevel") Integer typeLevel, @RequestParam("force") Boolean force) {
        return auditingPhotoService.auditingPhotoProcessByTypeLevel(typeLevel,force);
    }
    /**
     * 手动部署
     * @param typeLevel 组织等级
     * @param force 是否强制部署(当修改了流程图的时候需要传true)
     * @return 部署结果
     */
    public String auditingPhotoProcessByTypeLevel(Integer typeLevel, Boolean force);
    @Override
    public String auditingPhotoProcessByTypeLevel(Integer typeLevel, Boolean force) {
        switch (typeLevel) {
            case 6:
                auditingPhotoSixService.process(force);
                break;
            case 5:
                auditingPhotoFiveService.process(force);
                break;
            case 4:
                auditingPhotoFourService.process(force);
                break;
            case 3:
                auditingPhotoThreeService.process(force);
                break;
            case 2:
                auditingPhotoTwoService.process(force);
                break;
            case 1:
                auditingPhotoOneService.process(force);
                break;
            default:
                throw new ServicesException(ResultStatus.ARGS_VALID_ERROR);
        }
        return typeLevel + "级照片流程部署成功";
    }
    /**
     * 部署流程
     * @param force
     * @return
     */
    public void process(Boolean force);
    public void process(Boolean force) {
        //判断流程是否已经部署,当force=0时,
        if (force == null || force) {
            //部署流程
            DeploymentBuilder builder = repositoryService.createDeployment();
            builder.addClasspathResource(AuditingConstant.BpmnXmlPhotoPathConstant.AUDITING_PHOTO_SIX_XML).disableSchemaValidation();
            String id = builder.deploy().getId();
            repositoryService.setDeploymentKey(id, AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_SIX_ID);
        } else {
            //检测流程是否已经部署过
            List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionKey(AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_SIX_ID)
                    .list();
            if (!definitions.isEmpty()) {
                // 已经部署过流程定义
                throw new ServicesException(ResultStatus.AUDITING_PROCESS_DUPLICATE);
            } else {
                //部署流程
                DeploymentBuilder builder = repositoryService.createDeployment();
                builder.addClasspathResource(AuditingConstant.BpmnXmlPhotoPathConstant.AUDITING_PHOTO_SIX_XML).disableSchemaValidation();
                String id = builder.deploy().getId();
                repositoryService.setDeploymentKey(id, AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_SIX_ID);
            }
        }
    }

这里AuditingConstant.BpmnXmlPhotoPathConstant.AUDITING_PHOTO_SIX_XML是流程图的路径,如下图所示

在这里插入图片描述

部署好后,查看数据表 ACT_RE_PROCDEF 即可判断是否成功部署

在这里插入图片描述

七、发起流程

前提: 业务表(需要审批的)数据创建 ——> 点击提交审核

如下图的照片墙审核,创建一条新的数据,此时一开始没审核,状态是“草稿”,点击发布后,才会触发审批流程

在这里插入图片描述

1.示例

二级组织用户 新增一个照片墙,点击发布(选择审核等级为 上级(一级)组织,即需要经过两个审批节点才截止

【只要是二级发起的,都走的是2个审批节点的流程图,只不过根据结束节点会有不同的走向逻辑,如果这里选择本级(二级)结束,则不论二级通过不通过,都会直接走向结束节点】

在这里插入图片描述

点击发布后,会启动一个流程实例,并执行流程对象,见表ACT_HI_PROCINSTACT_RU_EXECUTION

ACT_HI_PROCINST(历史流程实例表)

在这里插入图片描述

ACT_RU_EXECUTION(运行时流程执行对象表):

在这里插入图片描述

如下图,当二级用户点击发步后,会发起审核,先走到 “二级审核2” 节点,对应上图的第1条数据

在这里插入图片描述

2.相关代码

    @ApiOperation("发起照片审核")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "query", name = "taskName", value = "任务名称", required = true),
            @ApiImplicitParam(type = "query", name = "priority", value = "优先级", required = true),
            @ApiImplicitParam(type = "query", name = "desc", value = "描述", required = true),
            @ApiImplicitParam(type = "query", name = "photoWallId", value = "照片墙id", required = true),
            @ApiImplicitParam(type = "query", name = "auditingOrganizeId", value = "审核的组织id", required = true)})
    @ApiResponses({@ApiResponse(response = ResponseResult.class, message = "1", code = 200),
            @ApiResponse(response = ServiceException.class, message = "系统错误,请稍等!", code = 4000)})
    @PostMapping("/startPhotoAuditProcess")
    public ResponseResult<String> photoStartAuditing(@RequestBody Map<String, Object> map) {
        return ResponseResult.success(auditingPhotoService.photoStartAuditing(map));
    }
    /**
     * 启动流程
     * @param map
     * @return success
     */
    public String photoStartAuditing(Map<String,Object> map);

下面代码解释:根据当前用户的组织级别,走不同的实现方法【代码比较臃肿,后续再优化,后面的代码同理】

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String photoStartAuditing(Map<String, Object> map) {
        //根据照片墙id查找照片墙,给照片墙设置审核的组织ID
        PhotoWall photoWall = photoWallService.selectPhotoWallDetail(map.get("photoWallId") + "");
        photoWall.setOrganizeAuditingId(Long.parseLong(map.get("auditingOrganizeId") + ""));
        photoWall.setOpinion("");
        OrganizeTreeVO userOrganize = iOrganizeService.getUserOrganize();
        photoWallService.updatePhotoWallForAuditing(photoWall);
        //当前组织的级别
        Integer typeLevel = userOrganize.getTypeLevel();
        Integer typeLevelSix = 6;
        Integer typeLevelFive = 5;
        Integer typeLevelFour = 4;
        Integer typeLevelThree = 3;
        Integer typeLevelTwo = 2;
        Integer typeLevelOne = 1;
        if (typeLevel.equals(typeLevelSix)) {
            auditingPhotoSixService.startAuditProcess(map);
        } else if (typeLevel.equals(typeLevelFive)) {
            auditingPhotoFiveService.startAuditProcess(map);
        } else if (typeLevel.equals(typeLevelFour)) {
            auditingPhotoFourService.startAuditProcess(map);
        } else if (typeLevel.equals(typeLevelThree)) {
            auditingPhotoThreeService.startAuditProcess(map);
        } else if (typeLevel.equals(typeLevelTwo)) {
            auditingPhotoTwoService.startAuditProcess(map);
        } else if (typeLevel.equals(typeLevelOne)) {
            auditingPhotoOneService.startAuditProcess(map);
        }
        return "success";
    }
/**
 * 启动流程
 * @param map
 */
public void startAuditProcess(Map<String,Object> map);
public void startAuditProcess(Map<String, Object> map) {
    String id = map.get("photoWallId") + "";
    //修改照片墙的状态
    PhotoWall photoWall = photoWallService.selectPhotoWallDetail(id);
    photoWall.setAuditState(1);
    photoWall.setOrganizeAuditingId(Long.parseLong(map.get("auditingOrganizeId") + ""));
    Boolean code = photoWallService.updatePhotoWallForAuditing(photoWall);

    if (code) {
        // 获取当前登录用户的组织信息
        OrganizeTreeVO userOrganize = iOrganizeService.getUserOrganize();
        //当前组织的级别
        Integer typeLevel = userOrganize.getTypeLevel();
        //用来封装存储每一级组织的审核人,key是integer,表示不同级的组织,value是个list集合,每一级组织的审核人
        HashMap<String, List<SysUser>> auditors = new HashMap<>(10);
        //找出每一级审批流人员
        activitiService.getAuditors(userOrganize,typeLevel,auditors);
        // 设置流程变量
        Map<String, Object> variables = new HashMap<>(10);
        //获得每级审核的审核人
        List<String> firstAuditor = getAuditorsId(auditors.get("1"));
        List<String> secondAuditor = getAuditorsId(auditors.get("2"));
        //审核人
        variables.put("firstAuditor", firstAuditor);
        variables.put("secondAuditor", secondAuditor);

        String formKey = AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_TWO_ID + ":";
        String bussinessKey = formKey + id;
        variables.put("bussinessKey", bussinessKey);
        // 启动流程实例
        runtimeService.startProcessInstanceByKey(AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_TWO_ID, bussinessKey, variables);

        //给接下来的每一个审核人都创建一个,在刚发起的时候,是同一级的审核人
        List
																				
															

推荐阅读