zoukankan      html  css  js  c++  java
  • java中避免集合死链调用

    1. 前言

     开发过程中,一些集合 的变动会触发任务去改变 其他的集合,为了保障任务的正确执行,应避免出现死循环调用,即对集合之间的影响关系进行一些限制。怕日后遗忘,特在此记录。

    2. 场景

    • A 集合影响 A 集合。

    • A 集合影响 B 集合,B 集合影响了 A 集合。

    • A 集合影响 B 集合,B 集合影响了 C 集合,C 集合影响了 A 集合。

    • A 集合影响 B 集合、C 集合,B 集合影响了 D 集合,C 集合影响了 E 集合,E 集合影响 A 集合。

    3. 环境

    3.1 开发环境准备

    • JDK 1.8
    • SpringBoot 2.x
    • Mysql 8
    • redis

    3.2 数据准备

    3.2.1 Mysql数据库表及数据

    dp_process表

    CREATE TABLE `dp_process` (
      `ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID',
      `NAME` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '名称',
      `CODE` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '代码',
      `CATEGORY` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '类型 1=楼宇,2=房地产',
      `IN_COLS` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '输入集合',
      `OUT_COLS` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '影响集合',
      `REMARK` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
      `ENABLED` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否开启',
      `STATUS` int DEFAULT NULL COMMENT '状态 数据状态:0=正常,1=删除,失效',
      `CREATED_BY` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
      `CREATED_TIME` datetime DEFAULT NULL COMMENT '创建时间',
      `UPDATED_BY` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
      `UPDATED_TIME` datetime DEFAULT NULL COMMENT '更新时间',
      `REVISION` int DEFAULT '0' COMMENT '乐观锁',
      PRIMARY KEY (`ID`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='数据处理 ';
    

    dp_process 表中的数据

    INSERT INTO `gccs`.`dp_process`(`ID`, `NAME`, `CODE`, `CATEGORY`, `IN_COLS`, `OUT_COLS`, `REMARK`, `ENABLED`, `STATUS`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `REVISION`) VALUES ('1', 'B', 'B', 'ly', 'A', 'B', 'B', '1', 0, NULL, NULL, NULL, NULL, 0);
    INSERT INTO `gccs`.`dp_process`(`ID`, `NAME`, `CODE`, `CATEGORY`, `IN_COLS`, `OUT_COLS`, `REMARK`, `ENABLED`, `STATUS`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `REVISION`) VALUES ('2', 'D', 'D', 'ly', 'B', 'D', 'D', '1', 0, NULL, NULL, NULL, NULL, 0);
    INSERT INTO `gccs`.`dp_process`(`ID`, `NAME`, `CODE`, `CATEGORY`, `IN_COLS`, `OUT_COLS`, `REMARK`, `ENABLED`, `STATUS`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `REVISION`) VALUES ('3', 'E', 'E', 'ly', 'B', 'E', 'E', '1', 0, NULL, NULL, NULL, NULL, 0);
    INSERT INTO `gccs`.`dp_process`(`ID`, `NAME`, `CODE`, `CATEGORY`, `IN_COLS`, `OUT_COLS`, `REMARK`, `ENABLED`, `STATUS`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `REVISION`) VALUES ('4', 'G', 'G', 'ly', 'D', 'G', 'G', '1', 0, NULL, NULL, NULL, NULL, 0);
    INSERT INTO `gccs`.`dp_process`(`ID`, `NAME`, `CODE`, `CATEGORY`, `IN_COLS`, `OUT_COLS`, `REMARK`, `ENABLED`, `STATUS`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `REVISION`) VALUES ('5', 'F', 'F', 'ly', 'D', 'F', 'F', '1', 0, NULL, NULL, NULL, NULL, 0);
    
    

    3.2.2 redis库数据

    key Value
    A [{ "id": "1","outCols": "B"}]
    B [{"id": "2","outCols": "D"},{"id": "3","outCols": "E"}]
    D [{"id": "4","outCols": "G"},{"id": "5","outCols": "F"}]

    4. 解决方式

    通过递归的方式循环查询、对比。

     本例主要牵扯到的知识点有:

    • Stack (栈,先进后出)
    • 递归
    • redis简单增删操作

     本文以修改方法代码为例,介绍如何实现防死链调用,非常简单。

    	/**
         * @create  2021-07-08 更新 数据处理
         * @param dpProcess 数据处理 模型
         * @param updateNil 全字段更新(新增时此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether}
         * @return
         */
        @Override
        public int modify(DpProcess dpProcess, String updateNil){
            
            // **省略一堆代码**
    
            // 输入集合统一处理
            operInclos(dpProcess, orignDpProcess.getInCols());
    
            // **省略一堆代码**
        }
    

     operInclos() 方法 :本文重点,主要做了数据校验、redis中数据更新等一系列操作

    	/**
         * @create 输入集合统一处理 2021/7/11 14:13
         * @param dpProcess 新数据处理对象
         * @param oldClos 原数据处理对象中的输入集合
         * @return
         */
        private void operInclos(DpProcess dpProcess, String oldClos) {
            // 新数据处理对象中的输入集合
            String inCols = dpProcess.getInCols();
    
            // 若新数据处理对象中的输入集合没有值,则直接跳过,不进行操作
            if(StringUtils.isNotBlank(inCols)){
                if(dpProcess.getInCols().contains(dpProcess.getOutCols())){
                    throw new ServiceException("数据处理流程配置输入流程调用了输出集合!");
                }
    
                // 数据类型转换
                Set<String> set = new HashSet(Arrays.asList(inCols.split(",")));
    
                // 循环遍历输入集合
                for (String inClo : set) {
    
                    // 最终需要遍历的list
                    List<DpProcessVo> childFinalList = new ArrayList<>();
    
                    // 从redis中获取当前集合的影响关系
                    String dpProcessJson = (String) redisUtil.get(inClo);
    
                    // 如果redis中存储的集合影响关系不为空,做简单的遍历去重处理
                    if(StringUtils.isNotBlank(dpProcessJson)){
    
                        // redis中存储的集合影响关系列表
                        List<DpProcessVo> children = new ArrayList<>();
    
                        // 进行数据类型转换
                        children = JSONArray.parseArray(dpProcessJson, DpProcessVo.class);
                        for (DpProcessVo dpProcessVo1 : children) {
                            if(dpProcess.getId().equals(dpProcessVo1.getId())){
                                continue;
                            }
                            childFinalList.add(dpProcessVo1);
                        }
                        // 添加本次影响的集合
                        DpProcessVo dpProcess1 = new DpProcessVo();
                        dpProcess1.setId(dpProcess.getId());
                        dpProcess1.setOutCols(dpProcess.getOutCols());
                        childFinalList.add(dpProcess1);
                    }
                    // 如果redis中没有此输入集合的影响关系,则可以直接进行添加
                    else{
                        DpProcessVo dpProcess1 = new DpProcessVo();
                        dpProcess1.setId(dpProcess.getId());
                        dpProcess1.setOutCols(dpProcess.getOutCols());
                        childFinalList.add(dpProcess1);
                    }
    
                    // 验证数据处理流程配置输入流程是否调用了输出集合
                    Stack<DpProcessVo> nodeStack = new Stack<>();
                    // 设置模型
                    DpProcessVo dpProcessVoTop = new DpProcessVo();
                    dpProcessVoTop.setOutCols(inClo);
                    dpProcessVoTop.setId(dpProcess.getId());
                    nodeStack.add(dpProcessVoTop);
    
                    // 遍历需要进行死链校验的数据
                    for (DpProcessVo dpProcessVo : childFinalList) {
    
                        // 是否添加标识(默认为添加,如果集合为死链,则进行提示)
                        boolean addFlag = true;
    
                        // 循环遍历栈
                        for (DpProcessVo processVo : nodeStack) {
                            if(processVo.getOutCols().equals(dpProcessVo.getOutCols())){
                                addFlag = false;
                                break;
                            }
                        }
                        if(!addFlag){
    
                            throw new ServiceException("数据处理流程配置输入流程调用了输出集合!");
                        }
                        // 将dpProcessVo推到这个堆栈的顶部
                        nodeStack.push(dpProcessVo);
    
                        // 验证数据处理流程配置输入流程是否调用了输出集合
                        invaldClosInfo(nodeStack);
    
                        // 移除此堆栈顶部的对象并将该对象作为此函数的值返回
                        nodeStack.pop();
    
                    }
                }
    
                // 处理需要删除的集合
                dealNeedDeleteCols(dpProcess, oldClos, set);
    
                // 获取并设置最终的集合名称
                String finallyCols = StringUtils.join(set.toArray(), ",");
                dpProcess.setInCols(finallyCols);
    
                // 省略一堆更新redis的操作
            }
        }
    

     invaldClosInfo()方法: 递归深度遍历

    /**
         * @create 验证数据处理流程配置输入流程是否调用了输出集合  2021/7/20 22:10
         * @param nodeStack 深度遍历栈
         * @return void
         */
        public void invaldClosInfo(Stack<DpProcessVo> nodeStack) {
    
            // 查看此堆栈顶部的对象而不将其从堆栈中移除
            DpProcessVo dpProcessVo = nodeStack.peek();
    
            // 从redis中查找此集合影响的流程关系
            String dpProcessJson = (String) redisUtil.get(dpProcessVo.getOutCols());
            // 如果集合没有影响其他集合,则直接返回
            if(StringUtils.isBlank(dpProcessJson)){
                return;
            }
    
            //获得节点的子节点,对于二叉树就是获得节点的左子结点和右子节点
            List<DpProcessVo> children = new ArrayList<>();
            // redis中原来存储的信息
            children = JSONArray.parseArray(dpProcessJson, DpProcessVo.class);
    
            // 遍历集合影响的集合关系
            for (DpProcessVo dpProcessVo1 : children) {
                boolean addFlag = true;
                for (DpProcessVo processVo : nodeStack) {
                    if(processVo.getOutCols().equals(dpProcessVo1.getOutCols())){
                        addFlag = false;
                        break;
                    }
                }
                if(!addFlag){
    
                    throw new ServiceException("数据处理流程配置输入流程调用了输出集合!");
                }
    
                // 将dpProcessVo推到这个堆栈的顶部
                nodeStack.push(dpProcessVo1);
    
                // 验证数据处理流程配置输入流程是否调用了输出集合
                invaldClosInfo(nodeStack);
    
                // 移除此堆栈顶部的对象并将该对象作为此函数的值返回
                nodeStack.pop();
            }
        }
    

    5.完整代码

     记录代码,方便日后复习、调用、重构。

    5.1Model

     模型主要分两部分:数据处理模型和简化版的数据处理模型。

     DpProcess:数据处理模型,数据完整的Sql操作

    
    import com.alibaba.fastjson.annotation.JSONField;
    import com.baomidou.mybatisplus.annotation.*;
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    import lombok.experimental.Accessors;
    
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * <p>
     * 数据处理 
     * </p>
     *
     * @since 2021-07-08
     */
    @Data
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    @ApiModel(value="DpProcess对象", description="数据处理 ")
    @TableName("dp_process")
    public class DpProcess implements Serializable {
    
        @TableField(exist = false)
        public static final String ENABLED = "ENABLED";
    
        @TableField(exist = false)
        public static final String STATUS = "STATUS";
    
        @TableField(exist = false)
        public static final String CATEGORY = "CATEGORY";
    
        private static final long serialVersionUID = 1L;
    
        @ApiModelProperty(value = "ID")
        @TableId(value = "ID", type = IdType.ASSIGN_ID)
        private String id;
    
        @ApiModelProperty(value = "名称")
        @TableField("NAME")
        private String name;
    
        @ApiModelProperty(value = "代码")
        @TableField("CODE")
        private String code;
    
        @ApiModelProperty(value = "类型 1=楼宇,2=房地产")
        @TableField("CATEGORY")
        private String category;
    
        @ApiModelProperty(value = "输入集合")
        @TableField("IN_COLS")
        private String inCols;
    
        @ApiModelProperty(value = "影响集合")
        @TableField("OUT_COLS")
        private String outCols;
    
        @ApiModelProperty(value = "备注")
        @TableField("REMARK")
        private String remark;
    
        @ApiModelProperty(value = "是否开启  0:否  1:是")
        @TableField("ENABLED")
        private String enabled;
    
        @ApiModelProperty(value = "状态 数据状态:0=正常,1=删除,失效")
        @TableField(value = "STATUS", fill = FieldFill.INSERT)
        private Integer status;
    
        @ApiModelProperty(value = "创建人")
        @TableField(value = "CREATED_BY", fill = FieldFill.INSERT)
        private String createdBy;
    
        @ApiModelProperty(value = "创建时间")
        @JSONField(format = "yyyy-MM-dd HH:mm:ss")
        @TableField(value = "CREATED_TIME", fill = FieldFill.INSERT)
        private Date createdTime;
    
        @ApiModelProperty(value = "更新人")
        @TableField(value = "UPDATED_BY", fill = FieldFill.UPDATE)
        private String updatedBy;
    
        @ApiModelProperty(value = "更新时间")
        @JSONField(format = "yyyy-MM-dd HH:mm:ss")
        @TableField(value = "UPDATED_TIME", fill = FieldFill.UPDATE)
        private Date updatedTime;
    
        @ApiModelProperty(value = "乐观锁")
        @Version
        @TableField(value = "REVISION", fill = FieldFill.INSERT)
        private Integer revision;
    
    }
    

    DpProcessVo: 数据处理简单模型,处理redis数据结构数据。

    
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    import lombok.experimental.Accessors;
    
    @Data
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    @ApiModel(value="DpProcessVo对象", description="数据处理简单模型 ")
    public class DpProcessVo{
    
        @ApiModelProperty(value = "ID")
        private String id;
    
        @ApiModelProperty(value = "影响集合")
        private String outCols;
    
    }
    

    5.2 Controller

     updateNil:让用户选择使用那种更新方式,也可以把接口一拆为二,主要看个人习惯。

    /**
         * @create  2021-07-08 更新 数据处理
         * @param dpProcess 数据处理 模型
         * @param updateNil 全字段更新(新增时此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether}
         * @return
         */
        @ApiOperation(value="更新",notes = "更新")
        @PostMapping("/modify")
        public Result modify(
                @ApiParam(name = "dpProcess", value = "数据处理 模型", required = true) @RequestBody DpProcess dpProcess,
                @ApiParam(name = "updateNil", value = "全字段更新(新增时此字段可以忽略): 是:Y 否:不传或者随意传") @RequestParam(required = false) String updateNil) {
            int addResult = dpProcessService.modify(dpProcess, updateNil);
            if (addResult > 0) {
                return new Result(CommonCode.SUCCESS, "更新成功!");
            }
            return new Result(CommonCode.FAIL, "更新失败!");
        }
    

    5.3 Service

     没啥好说的,就是一个接口。

        /**
         * @create 2021-07-08 更新 数据处理
         * @param dpProcess 数据处理 模型
         * @param updateNil 全字段更新(新增时此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether}
         * @return
         */
        int modify(DpProcess dpProcess, String updateNil);
    

    5.4 Service 实现类

     DpRecord:数据处理记录,不是本文重点,此处可直接忽略,相关说明 待 数据流程处理文章中提现。

    /**
         * @create  2021-07-08 更新 数据处理
         * @param dpProcess 数据处理 模型
         * @param updateNil 全字段更新(新增时此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether}
         * @return
         */
        @Override
        public int modify(DpProcess dpProcess, String updateNil){
            if(dpProcess == null){
                throw new ServiceException("数据处理模型不能为空!");
            }
            // 走更新方法
            // 通过id查询数据处理 详情
            DpProcess orignDpProcess = this.detail(dpProcess.getId());
            if(dpProcess == null){
                throw new ServiceException("数据处理模型信息不能为空!");
            }
    
            // 如果当前任务已存在,需要先进行取消
            if("0".equals(dpProcess.getEnabled())){
                if(defaultSchedulingConfigurer.hasTask(dpProcess.getId())){
                    defaultSchedulingConfigurer.cancelTriggerTask(dpProcess.getId());
                }
                // 根据数据处理ID查看数据库中是否有需要执行的数据处理记录
                DpRecord dpRecord = dpRecordService.getNeedExecRecordByDppId(dpProcess.getId());
                // 如果数据处理记录信息为空,则进行新增
                if(dpRecord != null){
                    // 设置结束时间为当前时间
                    dpRecord.setEndTime(new Date());
                    // 运行失败
                    dpRecord.setSucceed("2");
                    dpRecord.setFailedResult("用户取消操作");
                }
                // 对数据处理记录进行更新或者保存
                dpRecordService.addOrUpdate(dpRecord, null);
            }
    
            // 限制输出集合不能为空
            dpProcess.setOutCols(StringUtils.isNotBlank(dpProcess.getOutCols()) ? dpProcess.getOutCols() : orignDpProcess.getOutCols());
            if(StringUtils.isBlank(dpProcess.getOutCols())){
                throw new ServiceException("数据影响集合不能为空!");
            }
    
            // 输入集合统一处理
            operInclos(dpProcess, orignDpProcess.getInCols());
    
            // 全字段更新
            if(SystemConst.Whether.Yes.getCode().equals(updateNil)){
                if(StringUtils.isBlank(dpProcess.getRemark())){
                    throw new ServiceException("数据处理备注不能为空!");
                }
                // 备注不能小于20字
                if(dpProcess.getRemark().length() < 20){
                    throw new ServiceException("数据处理备注不能小于20字!");
                }
                return dpProcessMapper.alwaysUpdateSomeColumnById(dpProcess);
            }
            // 数据处理代码自动填充
            dpProcess.setCode(StringUtils.isBlank(dpProcess.getCode()) ? orignDpProcess.getCode() : dpProcess.getCode());
    
            return dpProcessMapper.updateById(dpProcess);
        }
    
    

    operInclos() : 处理输入集合的方法

    /**
     * @create 输入集合统一处理  2021/7/11 14:13
     * @param dpProcess 新数据处理对象
     * @param oldClos 原数据处理对象中的而输入集合
     * @return
     */
    private void operInclos(DpProcess dpProcess, String oldClos) {
        // 新数据处理对象中的输入集合
        String inCols = dpProcess.getInCols();
    
        // 若新数据处理对象中的输入集合没有值,则直接跳过,不进行操作
        if(StringUtils.isNotBlank(inCols)){
            if(dpProcess.getInCols().contains(dpProcess.getOutCols())){
                throw new ServiceException("数据处理流程配置输入流程调用了输出集合!");
            }
    
            // 数据类型转换
            Set<String> set = new HashSet(Arrays.asList(inCols.split(",")));
    
            // 循环遍历输入集合
            for (String inClo : set) {
    
                // 最终需要遍历的list
                List<DpProcessVo> childFinalList = new ArrayList<>();
    
                // 从redis中获取当前集合的影响关系
                String dpProcessJson = (String) redisUtil.get(inClo);
    
                // 如果redis中存储的集合影响关系不为空,做简单的遍历去重处理
                if(StringUtils.isNotBlank(dpProcessJson)){
    
                    // redis中存储的集合影响关系列表
                    List<DpProcessVo> children = new ArrayList<>();
    
                    // 进行数据类型转换
                    children = JSONArray.parseArray(dpProcessJson, DpProcessVo.class);
                    for (DpProcessVo dpProcessVo1 : children) {
                        if(dpProcess.getId().equals(dpProcessVo1.getId())){
                            continue;
                        }
                        childFinalList.add(dpProcessVo1);
                    }
                    // 添加本次影响的集合
                    DpProcessVo dpProcess1 = new DpProcessVo();
                    dpProcess1.setId(dpProcess.getId());
                    dpProcess1.setOutCols(dpProcess.getOutCols());
                    childFinalList.add(dpProcess1);
                }
                // 如果redis中没有此输入集合的影响关系,则可以直接进行添加
                else{
                    DpProcessVo dpProcess1 = new DpProcessVo();
                    dpProcess1.setId(dpProcess.getId());
                    dpProcess1.setOutCols(dpProcess.getOutCols());
                    childFinalList.add(dpProcess1);
                }
    
                // 验证数据处理流程配置输入流程是否调用了输出集合
                Stack<DpProcessVo> nodeStack = new Stack<>();
                // 设置模型
                DpProcessVo dpProcessVoTop = new DpProcessVo();
                dpProcessVoTop.setOutCols(inClo);
                dpProcessVoTop.setId(dpProcess.getId());
                nodeStack.add(dpProcessVoTop);
    
                // 遍历需要进行死链校验的数据
                for (DpProcessVo dpProcessVo : childFinalList) {
    
                    // 是否添加标识(默认为添加,如果集合为死链,则进行提示)
                    boolean addFlag = true;
    
                    // 循环遍历栈
                    for (DpProcessVo processVo : nodeStack) {
                        if(processVo.getOutCols().equals(dpProcessVo.getOutCols())){
                            addFlag = false;
                            break;
                        }
                    }
                    if(!addFlag){
    
                        throw new ServiceException("数据处理流程配置输入流程调用了输出集合!");
                    }
                    // 将dpProcessVo推到这个堆栈的顶部
                    nodeStack.push(dpProcessVo);
    
                    // 验证数据处理流程配置输入流程是否调用了输出集合
                    invaldClosInfo(nodeStack);
    
                    // 移除此堆栈顶部的对象并将该对象作为此函数的值返回
                    nodeStack.pop();
    
                }
            }
    
            // 处理需要删除的集合
            dealNeedDeleteCols(dpProcess, oldClos, set);
    
            // 获取并设置最终的集合名称
            String finallyCols = StringUtils.join(set.toArray(), ",");
            dpProcess.setInCols(finallyCols);
    
            // 能走到这一步,说明所有的集合没有问题,可以进行更新操作了(再一次遍历是为了和上面的校验分开,避免部分数据被更新)
            for (String inClo : set) {
    
                List<DpProcessVo> dpProcessVoList = new ArrayList<>();
                // 首先获取当前集合影响的数据处理对象
                String dpProcessJson = (String) redisUtil.get(inClo);
                if(StringUtils.isBlank(dpProcessJson)){
                    DpProcessVo dpProcessVo = new DpProcessVo();
                    dpProcessVo.setId(dpProcess.getId());
                    dpProcessVo.setOutCols(dpProcess.getOutCols());
                    dpProcessVoList.add(dpProcessVo);
                    // 进行数据的存储
                    redisUtil.set(inClo, JSONArray.toJSON(dpProcessVoList).toString());
                    continue;
                }
    
                // redis中原来存储的信息
                List<DpProcessVo> dpProcessVos = JSONArray.parseArray(dpProcessJson, DpProcessVo.class);
    
                // 把数据处理对象转换为HashSet
                HashSet<DpProcessVo> hashSet = new HashSet(dpProcessVos);
                // 当前集合影响的 其他集合列表
                List<DpProcessVo> childFinalList = new ArrayList<>();
    
                // 遍历redis中存储的集合影响关系,并进行简单去重处理
                for (DpProcessVo dpProcessVo : hashSet) {
                    if(dpProcessVo.getId().equals(dpProcess.getId())){
                        continue;
                    }
                    childFinalList.add(dpProcessVo);
                }
    
                // 添加上本次影响的集合
                DpProcessVo dpProcessVo = new DpProcessVo();
                dpProcessVo.setId(dpProcess.getId());
                dpProcessVo.setOutCols(dpProcess.getOutCols());
                // 添加当前数据数据对象
                childFinalList.add(dpProcessVo);
                // 进行数据的存储
                redisUtil.set(inClo, JSONArray.toJSON(childFinalList).toString());
            }
        }
    }
    

    invaldClosInfo() : 验证数据处理流程配置输入流程是否调用了输出集合

    /**
     * @create 验证数据处理流程配置输入流程是否调用了输出集合  2021/7/20 22:10
     * @param nodeStack 深度遍历栈
     * @return void
     */
    public void invaldClosInfo(Stack<DpProcessVo> nodeStack) {
    
        // 查看此堆栈顶部的对象而不将其从堆栈中移除
        DpProcessVo dpProcessVo = nodeStack.peek();
    
        // 从redis中查找此集合影响的流程关系
        String dpProcessJson = (String) redisUtil.get(dpProcessVo.getOutCols());
        // 如果集合没有影响其他集合,则直接返回
        if(StringUtils.isBlank(dpProcessJson)){
            return;
        }
    
        //获得节点的子节点,对于二叉树就是获得节点的左子结点和右子节点
        List<DpProcessVo> children = new ArrayList<>();
        // redis中原来存储的信息
        children = JSONArray.parseArray(dpProcessJson, DpProcessVo.class);
    
        // 遍历集合影响的集合关系
        for (DpProcessVo dpProcessVo1 : children) {
            boolean addFlag = true;
            for (DpProcessVo processVo : nodeStack) {
                if(processVo.getOutCols().equals(dpProcessVo1.getOutCols())){
                    addFlag = false;
                    break;
                }
            }
            if(!addFlag){
    
                throw new ServiceException("数据处理流程配置输入流程调用了输出集合!");
            }
    
            // 将dpProcessVo推到这个堆栈的顶部
            nodeStack.push(dpProcessVo1);
    
            // 验证数据处理流程配置输入流程是否调用了输出集合
            invaldClosInfo(nodeStack);
    
            // 移除此堆栈顶部的对象并将该对象作为此函数的值返回
            nodeStack.pop();
        }
    }
    

    dealNeedDeleteCols() : 主要处理--原数据为 A 集合影响 B 集合,修改为 C 集合影响了 B 集合,此时需要删除 A 对 B的影响关系

    /**
     * @create 处理需要删除的集合 2021/7/20 17:58
     * @param dpProcess 数据处理模型
     * @param oldClos 原来的数据处理模型中的集合信息
     * @param set 最新的集合名称信息
     * @return void
     */
    private void dealNeedDeleteCols(DpProcess dpProcess, String oldClos, Set<String> set) {
    
        if(StringUtils.isBlank(oldClos)){
            return;
        }
        // 获取去重后的集合数组
        List<String> newColsList = new ArrayList<>(set);
    
        // 原来的集合数组
        List<String> oldColsList = Arrays.asList(oldClos.split(","));
    
        // 获取两个集合的差集
        List<String> reduceList = oldColsList.stream().filter(item -> !newColsList.contains(item)).collect(toList());
        if(reduceList == null || reduceList.size() == 0){
            return;
        }
        for (String clos : reduceList) {
            // 获取redis中的集合
            String dpProcessJson = (String) redisUtil.get(clos);
            if(StringUtils.isBlank(dpProcessJson)){
                continue;
            }
            // redis中原来存储的信息
            List<DpProcessVo> dpProcessVos = JSONArray.parseArray(dpProcessJson, DpProcessVo.class);
            // 遍历删除的集合中影响的流程ID
            HashSet<DpProcessVo> hashSet = new HashSet(dpProcessVos);
            Iterator<DpProcessVo> it = hashSet.iterator();
            while(it.hasNext()){
                DpProcessVo dpProcessVo = it.next();
                if(dpProcessVo.getId().equals(dpProcess.getId())){
                    it.remove();
                }
            }
            // 如果当前集合影响的流程为空,则进行删除
            if(hashSet.isEmpty()){
                // 进行数据的存储
                redisUtil.delete(clos);
                continue;
            }
            // 进行数据的存储
            redisUtil.set(clos, JSONArray.toJSON(hashSet.toArray()).toString());
        }
    
    }
    

    6.测试

     可通过单元测试等多种方式,本文提供简单的测试数据。

    {
        "category": "ly",
        "code": "F",
        "createdBy": "",
        "createdTime": null,
        "enabled": "1",
        "id": "5",
        "inCols": "D",
        "name": "F",
        "outCols": "L",
        "remark": "F",
        "revision": 0,
        "status": 0,
        "updatedBy": "",
        "updatedTime": null
      }
    

    7.总结

     仅对今日工作进行简单记录,代码还需进一步重构,记录永不止步。

    作者:学海无涯519
    版权:本文版权归作者和博客园共有
    转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
  • 相关阅读:
    python学习之路-day1-python基础1
    JSON.NET基本使用
    tortoiseSVN 设置ignore
    一个简单的身份证校验
    一个HttpWebRequest工具类
    linq to NHibernate
    python-plot and networkx绘制网络关系图
    DDoS攻击及防御措施
    白帽子原则
    认识特洛伊木马
  • 原文地址:https://www.cnblogs.com/wgx519/p/15324635.html
Copyright © 2011-2022 走看看