目录
- 1. 前言
- 2. 场景
- 3. 环境3.1 开发环境准备
- 3.2 数据准备3.2.1 Mysql数据库表及数据
- 3.2.2 redis库数据
- 5.1Model
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`56c, `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\”}]56c |
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*/@Overridepublic int modify(DpProcess dpProcess, String updateNil){// **省略一堆代码**// 输入集合统一处理operInclos(dpProcess, orignDpProcess.getInCols());// **省略一堆代码**}
operInclos() 方法 :本文重点,主要做了数据校验、redis中数据更新等一系列操作
/*** @create 输入集合统一处理 2021/7/11 14:13* @param dpProcess 新56c数据处理对象* @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) {// 最终需要遍历的listList<DpProcessVo> childFinalList = new ArrayList<>();// 从redis中获取当前集合的影响关系String dpProcessJson = (String) redisUtil.get(inClo);// 如果redis中存储的集合影响关系不为空,做简单的遍历去重处理if(StringUtils.isNotBlank(dpProcessJson)){// redis中存储的集合影响关系列表List<DpProcessVo> children = new ArrayList<>();ad0// 进行数据类型转换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();}}// 处理需要删除的集合dealNeedDeleteC4000ols(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*/@Overridepublic 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) {// 最终需要遍历的listList<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);// 把数据处理对象转换为HashSetHashSet<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 DpProces114csVo();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);// 遍历删除的集合中影响的流程IDHashSet<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\",56c\"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.总结
仅对今日工作进行简单记录,代码还需进一步重构,记录永不止步。