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

news2024/9/21 2:41:12

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

参考文章:

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

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

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

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-CSDN博客】

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

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

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

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

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

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

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

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

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

本项目主要用到的表:

  • 流程部署表 :[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<SysUser> auditorsTypeLevel = auditors.get(typeLevel + "");
        //获得和登录用户同一级的审核人的id
        List<String> auditorsTypeLevelIds = getAuditorsId(auditorsTypeLevel);
        for (int i = 0; i < auditorsTypeLevelIds.size(); i++) {
            SysTask sysTask = new SysTask();
            sysTask.setTaskName((String) map.get("taskName"));
            sysTask.setPriority(Integer.valueOf(map.get("priority") + ""));
            sysTask.setDescribe((String) map.get("desc"));
            sysTask.setPhotoWallId((String) map.get("photoWallId"));
            sysTask.setCreatorName(SecurityUtils.getLoginUser().getUsername());
            sysTask.setCreatorId(SecurityUtils.getLoginUser().getUserId());
            //审核人的名字  这里要怎么设置啊,审核人的名字和id,用的是候选用户啊
            sysTask.setAuditingPeople(auditorsTypeLevel.get(i).getUserName());
            //审核人的id
            sysTask.setAuditingPeopleid(Long.parseLong(auditorsTypeLevelIds.get(i)));
            sysTask.setState(1);
            sysTask.setAuditingType(2);
            //任务表添加
            iSysTaskService.addTask(sysTask);
            //审核人的消息
            Message message = new Message();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
            String date = sdf.format(new Date());
            message.setContent("你在" + date + "有一个照片墙的审核");
            message.setCreateTime(new Date());
            message.setState(1);
            message.setUserId(auditorsTypeLevelIds.get(i));
            message.setType(1);
            messageService.addMessage(message);
        }

        //登录用户的消息
        Message message = new Message();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
        String date = sdf.format(new Date());
        message.setContent("你在" + date + "发起了一个照片墙审核");
        message.setCreateTime(new Date());
        message.setState(1);
        message.setUserId(SecurityUtils.getLoginUser().getUserId() + "");
        message.setType(2);
        messageService.addMessage(message);
    }
}

上方代码重点

  • 设置变量,启动流程实例

    这里把每一级审核人提前都设置好(后续不变)【这里根据业务需求应该也可以设计成后续动态添加】

    业务id bussinessKey 规则在此设定,调用最后一行的activity api时候需要传递,同时将其设置到流程变量 variables 中,后续方便读取

    在这里插入图片描述

  • 业务部分

    在这里插入图片描述

    在这里插入图片描述

    1. 修改 照片墙业务表 流程相关字段
    2. 新增 审核任务业务表
    3. 发送消息

八、审批过程

现在流程走到第一个审批节点,即 二级审核2

在这里插入图片描述

此时,运行时节点人员数据信息表 ACT_RU_IDENTITYLINK,存入二级审核2 节点的 是审核人角色 的 用户,尾号20用户和尾号52用户都是该节点的审批人【该项目里,只要其中一个审批通过,就算通过,反之,一个拒绝就算拒绝】,

且被分配到了任务(同个任务),见表ACT_RU_TASK,如下图

在这里插入图片描述

在这里插入图片描述

对应业务表 任务表里,也加了2条数据

在这里插入图片描述

他们之间通过 BUSINESS_KEY_ 来关联

1.审核通过

审批通过,在activity7框架中,只需要设置SecondJudge变量,然后将task任务完成即可

在这里插入图片描述

1.示例

二级组织人员 尾号52用户 创建了一个照片墙,点击发布后,生成本级人员(尾号52和尾号20)的任务,此时52用户进入“我的待办”里(业务表 任务表),找到对应任务,点击审批通过,如下图

在这里插入图片描述

2.相关代码

@ApiOperation("照片墙审批通过")
@ApiImplicitParams({
        @ApiImplicitParam(type = "query", name = "id", value = "任务id", required = true),
        @ApiImplicitParam(type = "query", name = "opinion", value = "建议", required = false)
})
@PostMapping("/auditing/photo/approve/{id}")
public ResponseResult<String> auditingPhotoApprove(@PathVariable Integer id, @RequestParam(required = false) String opinion) {
    return ResponseResult.success(auditingPhotoService.auditingPhotoApprove(id, opinion));
}
/**
 * 审核通过
 * @param id
 * @param opinion
 * @return success
 */
public String auditingPhotoApprove(Integer id,String opinion);

下面代码解释:

通过照片墙的创建者的组织等级,走不同的审核通过实现类

@Override
@Transactional(rollbackFor = Exception.class)
public String auditingPhotoApprove(Integer id, String opinion) {
    //id是任务的id,可以获得照片墙的id
    //再去新建表中查询这个照片墙是哪级创建的,
    //这个级数直接判断吗,然后去调用哪个类的approve方法?
    SysTask sysTask = iSysTaskService.selectTaskById(id);
    //通过任务的informatinid来获得照片墙id
    String photoId = sysTask.getPhotoWallId();

    //根据照片墙的id去查询审核创建者的级别
    LambdaQueryWrapper<PhotoWall> lambdaQueryWrapper = new LambdaQueryWrapper<PhotoWall>();
    lambdaQueryWrapper.eq(PhotoWall::getPhotoWallId, photoId);
    PhotoWall photoWall = photoWallService.getOne(lambdaQueryWrapper);

    //获得当前等级
    Long organizeId = photoWall.getOrganizeId();
    Integer userOrganizeLevel = iOrganizeService.searchOrganizeById(organizeId).getOrganizeLevel();
    Integer userOrganizeLevelOne = 1;
    Integer userOrganizeLevelTwo = 2;
    Integer userOrganizeLevelThree = 3;
    Integer userOrganizeLevelFour = 4;
    Integer userOrganizeLevelFive = 5;
    Integer userOrganizeLevelSix = 6;

    //获得审核组织的级别
    Long auditingOrganizeId = photoWall.getOrganizeAuditingId();
    Integer auditingOrganizeLevel = iOrganizeService.searchOrganizeById(auditingOrganizeId).getOrganizeLevel();

    if (userOrganizeLevel.equals(userOrganizeLevelOne)) {
        auditingPhotoOneService.approve(id, opinion);
    } else if (userOrganizeLevel.equals(userOrganizeLevelTwo)) {
        auditingPhotoTwoService.approve(id, opinion, auditingOrganizeLevel);
    } else if (userOrganizeLevel.equals(userOrganizeLevelThree)) {
        auditingPhotoThreeService.approve(id, opinion, auditingOrganizeLevel);
    } else if (userOrganizeLevel.equals(userOrganizeLevelFour)) {
        auditingPhotoFourService.approve(id, opinion, auditingOrganizeLevel);
    } else if (userOrganizeLevel.equals(userOrganizeLevelFive)) {
        auditingPhotoFiveService.approve(id, opinion, auditingOrganizeLevel);
    } else if (userOrganizeLevel.equals(userOrganizeLevelSix)) {
        auditingPhotoSixService.approve(id, opinion, auditingOrganizeLevel);
    }
    return "success";
}
/**
 * 审核通过
 * @param id
 * @param opinion
 * @param auditingOrganizeLevel
 */
public void approve(Integer id, String opinion,Integer auditingOrganizeLevel);
@Override
public void approve(Integer id, String opinion, Integer auditingOrganizeLevel) {
    //获取任务的候选人是登录用户的任务的方法
    List<Task> taskList = activitiService.getTaskIdByCandidate(SecurityUtils.getLoginUser().getUserId().toString());
    //当前登录用户的组织级别
    Integer typeLevel = iOrganizeService.getUserOrganize().getTypeLevel();
    //搜索出工作台选的任务id
    SysTask sysTask = iSysTaskService.selectTaskById(id);
    //通过任务的photoWallId来获得照片墙id
    PhotoWall photoWall = photoWallService.selectPhotoWallDetail(sysTask.getPhotoWallId() + "");
    String photoWallId = photoWall.getPhotoWallId();
    auditingInformTwoApproveService(taskList, photoWallId, typeLevel, photoWall, auditingOrganizeLevel, opinion);
    // 获取当前登录用户的组织信息
    OrganizeTreeVO userOrganize = iOrganizeService.getUserOrganize();
    //用来封装存储每一级组织的审核人,key是integer,表示不同级的组织,value是个list集合,每一级组织的审核人
    HashMap<String, List<SysUser>> auditors = new HashMap<>(10);
    //找出每一级审批流人员
    activitiService.getAuditors(userOrganize,typeLevel,auditors);
    List<String> auditorsList = new ArrayList<>();
    if(typeLevel.equals(AuditingConstant.Number.TWO)){
        auditorsList = getAuditorsId(auditors.get("2"));
    }
    if (typeLevel == 1) {
        auditorsList = getAuditorsId(auditors.get("1"));
    }

    sysTask.setState(2);
    sysTask.setOpinion(opinion);
    sysTask.setCompletionTime(new Date());
    iSysTaskService.updateTask(sysTask);
    Long userId1 = SecurityUtils.getLoginUser().getUserId();
    String photoWallId1 = sysTask.getPhotoWallId();
    //删除其他用户中关联这个照片墙id的任务
    //循环审核人列表
    for (String s : auditorsList) {
        //当审核人不等于登录用户时,即其他用户
        if (!Objects.equals(s, userId1 + "")) {
            //搜索出他的全部任务
            List<SysTask> sysTaskList = iSysTaskService.selectTaskByAuditorId(Long.parseLong(s));
            //循环任务
            for (SysTask task : sysTaskList) {
                //找出任务中关联的照片墙id等于当前登录用户审核的任务的关联的照片墙id
                if (photoWallId1.equals(task.getPhotoWallId())) {
                    //删除
                    iSysTaskService.deleteTask(task.getTaskId().intValue());
                }
            }
        }
    }

    if (typeLevel == 1 || typeLevel.equals(auditingOrganizeLevel)) {
        photoWall.setAuditState(2);
        photoWallService.updatePhotoWallForAuditing(photoWall);
        Message message = new Message();
        Date date = new Date();
        message.setContent("你的照片墙在" + typeLevel + "级审核通过");
        message.setCreateTime(date);
        message.setState(1);
        String userId = sysTask.getCreatorId() + "";
        message.setUserId(userId);
        message.setType(2);
        message.setTaskId(sysTask.getTaskId().intValue());
        messageService.addMessage(message);
        return;
    }
    if (typeLevel.equals(AuditingConstant.Number.TWO)) {
        auditorsList = getAuditorsId(auditors.get("1"));
    }
    for (String s : auditorsList) {
        SysTask sysTask1 = new SysTask();
        sysTask1.setTaskName(sysTask.getTaskName());
        sysTask1.setPriority(sysTask.getPriority());
        sysTask1.setDescribe(sysTask.getDescribe());
        sysTask1.setPhotoWallId(sysTask.getPhotoWallId());
        sysTask1.setCreatorName(SecurityUtils.getLoginUser().getUsername());
        sysTask1.setCreatorId(SecurityUtils.getLoginUser().getUserId());
        sysTask1.setAuditingPeopleid(Long.parseLong(s));
        SysUser sysUser = iSysUserService.selectUserById(Long.parseLong(s));
        sysTask1.setAuditingPeople(sysUser.getUserName());
        sysTask1.setState(1);
        sysTask1.setAuditingType(2);
        iSysTaskService.addTask(sysTask1);
        sendMessage(s, sysTask);
    }
    sendMessage(typeLevel, sysTask);
}
private void auditingInformTwoApproveService(List<Task> taskList, String photoWallId, Integer typeLevel, PhotoWall photoWall, Integer auditingOrganizeLevel, String opinion) {
    if (!ObjectUtils.isEmpty(taskList)) {
        for (Task item : taskList) {
            ProcessInstance processInstance = runtimeService
                    .createProcessInstanceQuery()
                    .processInstanceId(item.getProcessInstanceId())
                    .singleResult();
            //根据流程实例得到bussinesskey,格式是formkey:id
            String a = processInstance.getBusinessKey();
            if (StringUtils.isNotBlank(a)) {
                String[] b = a.split(":");
                String photoId = b[1];
                //判断bussinesskey,bussinesskey是任务表中的记录的id,在发起流程的时候会插入任务表,任务表的id也会变成流程变量
                if (photoId.equals(photoWallId)) {
                    if (typeLevel == 2) {
                        if (typeLevel.equals(auditingOrganizeLevel)) {
                            taskService.setVariable(item.getId(), "SecondJudge", false);
                        } else {
                            taskService.setVariable(item.getId(), "SecondJudge", true);
                        }
                    } else if (typeLevel == 1) {
                        //如果当前审核人是一级组织的,那审核就结束了
                        photoWall.setAuditState(2);
                        photoWall.setOpinion(opinion);
                        photoWallService.updatePhotoWallForAuditing(photoWall);
                    }
                    taskService.addComment(item.getId(), item.getProcessInstanceId(), "审核通过");
                    taskService.complete(item.getId());
                    break;
                }
            }
        }
    }
}

上面代码重点

  • 找到activity的任务表中,当前该用户的所有任务

    在这里插入图片描述

    @Override
    public List<Task> getTaskIdByCandidate(String userId) {
        List<ActivitiTaskId> activitiTaskId = activitiMapper.getTaskIdByCandidate(userId);
        List<String> activitiTaskIds = new ArrayList<>();
        for (com.znak.platform.entity.ActivitiTaskId taskId : activitiTaskId) {
            activitiTaskIds.add(taskId.getTaskId());
        }
        List<Task> taskList = new ArrayList<>();
        for (String taskId : activitiTaskIds) {
            Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
            taskList.add(task);
        }
        return taskList;
    }
    
    <select id="getTaskIdByCandidate" resultType="com.znak.platform.entity.ActivitiTaskId">
        select distinct TASK_ID_ from ACT_RU_IDENTITYLINK
        where USER_ID_ = #{userId}  and TASK_ID_ != ""
    </select>
    
  • 根据bussinesskey找到对应的任务,修改变量SecondJudge,并完成任务

    在这里插入图片描述

    这里注意,如果当前是走到一级审批节点了,则走下面的else if逻辑,直接将业务表修改审核通过(不用设置变量,因为没有网关了),如下图

    在这里插入图片描述

    在这里插入图片描述

  • 业务相关

    • 修改业务表的审批流相关字段,同时发送消息,同上

    • 然后删除同级别组织人员的 业务表 任务表,新建下一级的人员的业务表 任务表,见代码

2.审批不通过

过程和审批通过大致一样

在这里插入图片描述

1.相关代码

@ApiOperation("照片审核不通过")
@ApiResponses({@ApiResponse(response = ResponseResult.class, message = "1", code = 200),
        @ApiResponse(response = ServiceException.class, message = "系统错误,请稍等!", code = 4000)})
@ApiImplicitParams({
        //发起资讯申请相关字段
        @ApiImplicitParam(type = "query", name = "id", value = "任务id", required = true),
        @ApiImplicitParam(type = "query", name = "opinion", value = "建议", required = false)
})
@PostMapping("/auditing/photo/reject/{id}")
public ResponseResult<String> auditingPhotoReject(@PathVariable Integer id, @RequestParam(required = false) String opinion) {
    return ResponseResult.success(auditingPhotoService.auditingPhotoReject(id, opinion));
}
/**
 * 审核不通过
 * @param id
 * @param opinion
 * @return success
 */
public String auditingPhotoReject(Integer id,String opinion);
@Override
@Transactional(rollbackFor = Exception.class)
public String auditingPhotoReject(Integer id, String opinion) {
    //id是任务的id,可以获得资讯的id
    //再去新建表中查询这个资讯是哪级创建的,
    //这个级数直接判断吗,然后去调用哪个类的approve方法?
    SysTask sysTask = iSysTaskService.selectTaskById(id);
    //通过任务的informatinid来获得资讯id
    String photoWallId = sysTask.getPhotoWallId();
    //根据资讯的id去查询审核创建者的级别
    LambdaQueryWrapper<PhotoWall> lambdaQueryWrapper = new LambdaQueryWrapper<PhotoWall>();
    lambdaQueryWrapper.eq(PhotoWall::getPhotoWallId, photoWallId);
    PhotoWall photoWall = photoWallService.getOne(lambdaQueryWrapper);

    //获得当前等级
    Long organizeId = photoWall.getOrganizeId();
    Integer userOrganizeLevel = iOrganizeService.searchOrganizeById(organizeId).getOrganizeLevel();
    Integer userOrganizeLevelOne = 1;
    Integer userOrganizeLevelTwo = 2;
    Integer userOrganizeLevelThree = 3;
    Integer userOrganizeLevelFour = 4;
    Integer userOrganizeLevelFive = 5;
    Integer userOrganizeLevelSix = 6;
    if (userOrganizeLevel.equals(userOrganizeLevelOne)) {
        auditingPhotoOneService.rej(id, opinion);
    } else if (userOrganizeLevel.equals(userOrganizeLevelTwo)) {
        auditingPhotoTwoService.rej(id, opinion);
    } else if (userOrganizeLevel.equals(userOrganizeLevelThree)) {
        auditingPhotoThreeService.rej(id, opinion);
    } else if (userOrganizeLevel.equals(userOrganizeLevelFour)) {
        auditingPhotoFourService.rej(id, opinion);
    } else if (userOrganizeLevel.equals(userOrganizeLevelFive)) {
        auditingPhotoFiveService.rej(id, opinion);
    } else if (userOrganizeLevel.equals(userOrganizeLevelSix)) {
        auditingPhotoSixService.rej(id, opinion);
    }
    return "success";
}
/**
 * 审核不通过
 * @param id
 * @param opinion
 */
public void rej(Integer id, String opinion);
public void rej(Integer id, String opinion) {
    //获取任务的候选人是登录用户的任务的方法
    List<Task> taskList = activitiService.getTaskIdByCandidate(SecurityUtils.getLoginUser().getUserId().toString());
    //当前登录用户的组织级别
    Integer typeLevel = iOrganizeService.getUserOrganize().getTypeLevel();

    //搜索出工作台选的任务id
    SysTask sysTask = iSysTaskService.selectTaskById(id);

    //通过任务的informatinid来获得资讯id
    PhotoWall photoWall = photoWallService.selectPhotoWallDetail(sysTask.getPhotoWallId());
    String photoWallId = photoWall.getPhotoWallId();
    auditingInformTwoRejService(taskList, photoWallId, typeLevel, photoWall,opinion);

    // 获取当前登录用户的组织信息
    OrganizeTreeVO userOrganize = iOrganizeService.getUserOrganize();
    //用来封装存储每一级组织的审核人,key是integer,表示不同级的组织,value是个list集合,每一级组织的审核人
    HashMap<String, List<SysUser>> auditors = new HashMap<>(4);
    //找出每一级审批流人员
    activitiService.getAuditors(userOrganize,typeLevel,auditors);
    //防止四级发起的审核,然后任务到达三级的时候调用此方法,会报错4级审核人找不到
    List<String> auditorsList = new ArrayList<>();

    Integer typeLevelTwo = 2;
    if (typeLevel.equals(typeLevelTwo)) {
        auditorsList = getAuditorsId(auditors.get("2"));

    }
    if (typeLevel == 1) {
        auditorsList = getAuditorsId(auditors.get("1"));
    }
    //这一个等级的组织的审核任务处理,处理上一阶段的任务
    //获得当前级别的审核人
    sysTask.setState(3);
    sysTask.setOpinion(opinion);
    sysTask.setCompletionTime(new Date());
    iSysTaskService.updateTask(sysTask);
    Long userId1 = SecurityUtils.getLoginUser().getUserId();
    String photoWallId1 = photoWall.getPhotoWallId();
    //删除其他用户中关联这个咨询id的任务
    //循环审核人列表
    for (String s : auditorsList) {
        //当审核人不等于登录用户时,即其他用户
        if (!Objects.equals(s, userId1 + "")){
            //搜索出他的全部任务
            List<SysTask> sysTaskList = iSysTaskService.selectTaskByAuditorId(Long.parseLong(s));
            //循环任务
            for(SysTask task : sysTaskList){
                //找出任务中关联的照片墙id等于当前登录用户审核的任务的关联的照片墙id
                if(photoWallId1.equals(task.getPhotoWallId())){
                    //删除
                    iSysTaskService.deleteTask(task.getTaskId().intValue());
                }
            }
        }
    }

    Message message = new Message();
    Date date = new Date();
    message.setContent("你的照片墙在" + typeLevel + "级审核没有通过");
    message.setCreateTime(date);
    message.setState(1);
    //这个审核的是谁的流程
    String userId = sysTask.getCreatorId() + "";
    message.setUserId(userId);
    message.setType(2);
    message.setTaskId(sysTask.getTaskId().intValue());
    messageService.addMessage(message);

}
private void auditingInformTwoRejService(List<Task> taskList, String photoWallId, Integer typeLevel, PhotoWall photoWall,String opinion) {
    if (!ObjectUtils.isEmpty(taskList)) {
        for (Task item : taskList) {
            ProcessInstance processInstance = runtimeService
                    .createProcessInstanceQuery()
                    .processInstanceId(item.getProcessInstanceId())
                    .singleResult();
            //根据流程实例得到bussinesskey,格式是formkey:id
            String a = processInstance.getBusinessKey();
            if (StringUtils.isNotBlank(a)) {
                String[] b = a.split(":");
                String photoId = b[1];
                //判断bussinesskey,bussinesskey是任务表中的记录的id,在发起流程的时候会插入任务表,任务表的id也会变成流程变量
                if (photoId.equals(photoWallId)) {
                    //根据当前登录用户的组织级别判断是第几级判断,设置相关的流程变量
                    if (typeLevel == 2) {
                        photoWall.setAuditState(4);
                        photoWall.setOpinion(opinion);
                        photoWallService.updatePhotoWallForAuditing(photoWall);
                        taskService.setVariable(item.getId(), "SecondJudge", false);
                    } else if (typeLevel == 1) {
                        //如果当前审核人是一级组织的,那审核就结束了
                        photoWall.setAuditState(4);
                        photoWall.setOpinion(opinion);
                        photoWallService.updatePhotoWallForAuditing(photoWall);
                    }
                    taskService.addComment(item.getId(), item.getProcessInstanceId(), "审核不通过");
                    taskService.complete(item.getId());
                    break;
                }
            }
        }
    }
}

上面代码的重点:

  • 找到activity的任务表中,当前该用户的所有任务

    同上

  • 根据bussinesskey找到对应的任务,修改变量SecondJudge,并完成任务

    同上

  • 业务相关

    • 修改业务表的审批流相关字段,同时发送消息,同上

    • 然后删除同级别组织人员的 业务表 任务表,见代码

九、业务表相关字段

1.任务表

在这里插入图片描述

2.业务数据表

以 照片墙 为例

在这里插入图片描述

十、业务表数据权限变化

  • 审核前,即草稿状态的数据,只能看自己创建的
  • 审核通过,本级 到 最终审核组织 之间的用户都可见

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1931399.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ospf的MGRE实验

第一步&#xff1a;配IP [R1-GigabitEthernet0/0/0]ip address 12.0.0.1 24 [R1-GigabitEthernet0/0/1]ip address 21.0.0.1 24 [R1-LoopBack0]ip address 192.168.1.1 24 [ISP-GigabitEthernet0/0/0]ip address 12.0.0.2 24 [ISP-GigabitEthernet0/0/1]ip address 21.0.0.2 24…

Python | Leetcode Python题解之第238题除自身以外数组的乘积

题目&#xff1a; 题解&#xff1a; class Solution:def productExceptSelf(self, nums: List[int]) -> List[int]:length len(nums)# L 和 R 分别表示左右两侧的乘积列表L, R, answer [0]*length, [0]*length, [0]*length# L[i] 为索引 i 左侧所有元素的乘积# 对于索引为…

C4D各版本软件下载+自学C4D 从入门到精通【学习视频教程全集】+【素材笔记】

下载链接&#xff1a; 迅雷网盘https://pan.xunlei.com/s/VO1tydOxEo-Az_QCM-Jz2R4RA1?pwdvxg4# 夸克网盘https://pan.quark.cn/s/fe7450b02d80 百度网盘https://pan.baidu.com/s/1Omj4WL93F1DNdA2iP4SiMQ?pwdwmb8

vue仿甘特图开发工程施工进度表

前言 本文是根据项目实际开发中一个需求开发的demo&#xff0c;仅用了elementUI&#xff0c;可当作独立组件使用&#xff0c;C V即用。 当然没考虑其他的扩展性和一些数据的校验&#xff0c;主要是提供一个处理思路&#xff0c;有需要的小伙伴可以直接复制&#xff1b;本demo的…

python-区间内的真素数(赛氪OJ)

[题目描述] 找出正整数 M 和 N 之间&#xff08;N 不小于 M&#xff09;的所有真素数。真素数的定义&#xff1a;如果一个正整数 P 为素数&#xff0c;且其反序也为素数&#xff0c;那么 P 就为真素数。 例如&#xff0c;11&#xff0c;13 均为真素数&#xff0c;因为 11 的反序…

open3d:随机采样一致性分割平面

1、背景介绍 随机采样一致性算法&#xff08;RANSAC Random Sample Consensus&#xff09;是一种迭代的参数估计算法&#xff0c;主要用于从包含大量噪声数据的样本中估计模型参数。其核心思想是通过随机采样和模型验证来找到数据中最符合模型假设的点。因此&#xff0c;只要事…

python--实验13 异常处理

目录 知识点 异常概述 异常类 异常处理 捕获简单异常 原理 示例 异常处理except 原理及代码格式 try工作原理 标记程序上下文&#xff0c;异常发生时跳转 代码示例 终止行为finally 抛出异常raise 原理 代码格式 示例 自定义异常 原理 示例 断言assert 判…

Java web从入门到精通 (第 2版)中文电子版

前言 《Java Web从入门到精通&#xff08;第2版&#xff09;》共分21章&#xff0c;包括Java Web应用开发概述、HTML与CSS网页开发基础、JavaScript脚本语言、搭建开发环境、JavaBean技术、Servlet技术、过滤器和监听器、Hibernate高级应用、Java Web的数据库操作、EL&#xf…

防火墙双机备份及带宽限制

一&#xff0c;实验拓扑&#xff1a; 二&#xff0c;实验需求&#xff1a; 1&#xff0c;对现有网络进行改造升级&#xff0c;将当个防火墙组网改成双机热备的组网形式&#xff0c;做负载分担模式&#xff0c;游客区和DMZ区走FW4&#xff0c;生产区和办公区的流量走FW2 2&…

C#(asp.net)校园宿舍管理系统-计算机毕业设计源码18150

摘 要 随着高等教育的普及&#xff0c;校园宿舍管理成为学校管理的重要组成部分。传统的宿舍管理方式存在效率低下、信息不透明等问题。同时&#xff0c;在信息技术的不断发展和应用的背景下&#xff0c;校园管理信息化已成为学校管理的重要趋势。因此&#xff0c;研究基于Asp.…

【贪心算法】贪心算法30题

一、贪心算法简介 证明贪心策略正确性的常用方法&#xff1a;直接证明、交换论证法、反证法、分类讨论… 二、相关编程题 2.1 柠檬水找零 题目链接 860. 柠檬水找零 - 力扣&#xff08;LeetCode&#xff09; 题目描述 算法原理 提示&#xff1a;最优解和贪心解唯一可能不同…

netxduo http server 创建回复以及json解析

我们今天要整http的response,比如我创建的http server,我对它发送了一个POST,然后服务器解析出json里的body,再回复过去。今天会用到json的解析库cjson以及postman去发送消息。这次用nx_web_http_server.h这个库,不用之前的nx_http_server.h 本教程在最后附带app_netxduo…

# ffmpeg 工具使用

文章目录 ffmpeg 工具使用1.图片转换2.图片转视频3.截取片段4. 视频格式转换5. 视频转图片方法一&#xff1a;方法二&#xff1a;生成更清晰无压缩的图片方式&#xff1a; ffmpeg 工具使用 windows安装教程&#xff1a;https://blog.csdn.net/csdn_yudong/article/details/129…

3D 渲染一个房屋需要多长时间?

3D 渲染一个房屋总共需要 6-10 个工作日&#xff0c;主要取决于项目的复杂性和最终 3D 渲染的质量&#xff0c;图像越逼真&#xff0c;效果图渲染所需时间就越长。 1.3D建模 创建 3D 模型是第一步&#xff0c;所需时间可能因项目的复杂程度而有很大差异。一个简单的住宅渲染可…

FreeCAD源码分析:Serialization

序列化(Serialization)是在程序运行时将对象转换成字节流的过程&#xff0c;进而将二进制字节流罗盘到文件系统或者通过网络发送到远端。而反序列化(Deserialization)则恰恰相反&#xff0c;是由二进制字节流重新构建对象的过程。 Ref. from Boost Serialization Here, we use …

STL—string类—模拟实现

STL—string类—模拟实现 熟悉了string的结构和各自接口的使用之后&#xff0c;现在就要尝试去模拟实现string类 这个string类为了避免和我们库里的string类冲突&#xff0c;因此我们需要定义一个自己的命名空间 namespace wzf {class string{public://成员函数private://成…

.NET 8+Vue2 部署到Window Server

.NET 8Vue2 部署到Window Server 1 配置环境 1.1 下载安装.NET 8 SDK&#xff0c;下载时需注意目标服务器的系统类型&#xff08;64位 or 32位&#xff09; https://dotnet.microsoft.com/zh-cn/download 1.2 下载安装SQL Server数据库&#xff08;服务和管理工具&#xff…

LabVIEW工业设备姿态监测系统

开发了一种基于LabVIEW的工业设备姿态监测系统&#xff0c;针对现有监测设备在适应性和反应时间上的不足&#xff0c;采用了LabVIEW软件和STM32微控制器&#xff0c;通过高精度姿态传感器实现了对设备姿态的快速准确监测&#xff0c;大大提高了工业作业的安全与效率。 项目背景…

C++解决:书本信息储存

书本信息储存 暂无标签 时间限制&#xff1a;C/C 1000MS&#xff0c;其他语言 2000MS 内存限制&#xff1a;C/C 256MB&#xff0c;其他语言 512MB 难度&#xff1a;简单 描述 创建一个储存书本信息的结构体&#xff0c;包括书号&#xff0c;名称&#xff0c;作者&#xff0…

CS110L(Rust)

1.Rust 语法总结 数值类型 有符号整数: i8, i16, i32, i64无符号整数: u8, u16, u32, u64 变量声明 声明变量: let i 0; // 类型推断let n: i32 1; // 显式类型声明 可变变量: let mut n 0; n n 1; 字符串 注意&#xff0c;let s: str "Hello world";…