1. 前言
開發(fā)過(guò)程中, 一些集合 的變動(dòng)會(huì)觸發(fā)任務(wù)去 改變 其他的集合 ,為了保障任務(wù)的正確執(zhí)行,應(yīng)避免出現(xiàn)死循環(huán)調(diào)用,即對(duì) 集合之間的影響關(guān)系 進(jìn)行一些限制。怕日后遺忘,特在此記錄。
2. 場(chǎng)景
- A 集合影響 A 集合。
- A 集合影響 B 集合,B 集合影響了 A 集合。
- A 集合影響 B 集合,B 集合影響了 C 集合,C 集合影響了 A 集合。
- A 集合影響 B 集合、C 集合,B 集合影響了 D 集合,C 集合影響了 E 集合,E 集合影響 A 集合。
3. 環(huán)境
3.1 開發(fā)環(huán)境準(zhǔn)備
- JDK 1.8
- SpringBoot 2.x
- Mysql 8
- redis
3.2 數(shù)據(jù)準(zhǔn)備
3.2.1 Mysql數(shù)據(jù)庫(kù)表及數(shù)據(jù)
dp_process表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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=房地產(chǎn)' , `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 '狀態(tài) 數(shù)據(jù)狀態(tài):0=正常,1=刪除,失效' , `CREATED_BY` varchar (32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '創(chuàng)建人' , `CREATED_TIME` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間' , `UPDATED_BY` varchar (32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人' , `UPDATED_TIME` datetime DEFAULT NULL COMMENT '更新時(shí)間' , `REVISION` int DEFAULT '0' COMMENT '樂(lè)觀鎖' , PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE =utf8mb4_general_ci COMMENT= '數(shù)據(jù)處理 ' ; |
dp_process 表中的數(shù)據(jù)
1
2
3
4
5
|
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庫(kù)數(shù)據(jù)
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. 解決方式
通過(guò)遞歸的方式循環(huán)查詢、對(duì)比。
本例主要牽扯到的知識(shí)點(diǎn)有:
-
Stack
(棧,先進(jìn)后出) - 遞歸
-
redis
簡(jiǎn)單增刪操作
本文以 修改方法 代碼為例,介紹如何實(shí)現(xiàn)防死鏈調(diào)用,非常簡(jiǎn)單。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/** * @ create 2021-07-08 更新 數(shù)據(jù)處理 * @param dpProcess 數(shù)據(jù)處理 模型 * @param updateNil 全字段更新(新增時(shí)此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether} * @ return */ @Override public int modify (DpProcess dpProcess, String updateNil){ // **省略一堆代碼** // 輸入集合統(tǒng)一處理 operInclos(dpProcess, orignDpProcess.getInCols()); // **省略一堆代碼** } |
operInclos()
方法 : 重點(diǎn) ,主要做了數(shù)據(jù)校驗(yàn)、redis中數(shù)據(jù)更新等一系列操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
/** * @create 輸入集合統(tǒng)一處理 2021/7/11 14:13 * @param dpProcess 新數(shù)據(jù)處理對(duì)象 * @param oldClos 原數(shù)據(jù)處理對(duì)象中的輸入集合 * @return */ private void operInclos(DpProcess dpProcess, String oldClos) { // 新數(shù)據(jù)處理對(duì)象中的輸入集合 String inCols = dpProcess.getInCols(); // 若新數(shù)據(jù)處理對(duì)象中的輸入集合沒(méi)有值,則直接跳過(guò),不進(jìn)行操作 if (StringUtils.isNotBlank(inCols)){ if (dpProcess.getInCols().contains(dpProcess.getOutCols())){ throw new ServiceException( "數(shù)據(jù)處理流程配置輸入流程調(diào)用了輸出集合!" ); } // 數(shù)據(jù)類型轉(zhuǎn)換 Set<String> set = new HashSet(Arrays.asList(inCols.split( "," ))); // 循環(huán)遍歷輸入集合 for (String inClo : set) { // 最終需要遍歷的list List<DpProcessVo> childFinalList = new ArrayList<>(); // 從redis中獲取當(dāng)前集合的影響關(guān)系 String dpProcessJson = (String) redisUtil.get(inClo); // 如果redis中存儲(chǔ)的集合影響關(guān)系不為空,做簡(jiǎn)單的遍歷去重處理 if (StringUtils.isNotBlank(dpProcessJson)){ // redis中存儲(chǔ)的集合影響關(guān)系列表 List<DpProcessVo> children = new ArrayList<>(); // 進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換 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中沒(méi)有此輸入集合的影響關(guān)系,則可以直接進(jìn)行添加 else { DpProcessVo dpProcess1 = new DpProcessVo(); dpProcess1.setId(dpProcess.getId()); dpProcess1.setOutCols(dpProcess.getOutCols()); childFinalList.add(dpProcess1); } // 驗(yàn)證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 Stack<DpProcessVo> nodeStack = new Stack<>(); // 設(shè)置模型 DpProcessVo dpProcessVoTop = new DpProcessVo(); dpProcessVoTop.setOutCols(inClo); dpProcessVoTop.setId(dpProcess.getId()); nodeStack.add(dpProcessVoTop); // 遍歷需要進(jìn)行死鏈校驗(yàn)的數(shù)據(jù) for (DpProcessVo dpProcessVo : childFinalList) { // 是否添加標(biāo)識(shí)(默認(rèn)為添加,如果集合為死鏈,則進(jìn)行提示) boolean addFlag = true ; // 循環(huán)遍歷棧 for (DpProcessVo processVo : nodeStack) { if (processVo.getOutCols().equals(dpProcessVo.getOutCols())){ addFlag = false ; break ; } } if (!addFlag){ throw new ServiceException( "數(shù)據(jù)處理流程配置輸入流程調(diào)用了輸出集合!" ); } // 將dpProcessVo推到這個(gè)堆棧的頂部 nodeStack.push(dpProcessVo); // 驗(yàn)證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 invaldClosInfo(nodeStack); // 移除此堆棧頂部的對(duì)象并將該對(duì)象作為此函數(shù)的值返回 nodeStack.pop(); } } // 處理需要?jiǎng)h除的集合 dealNeedDeleteCols(dpProcess, oldClos, set); // 獲取并設(shè)置最終的集合名稱 String finallyCols = StringUtils.join(set.toArray(), "," ); dpProcess.setInCols(finallyCols); // 省略一堆更新redis的操作 } } |
invaldClosInfo()
方法: 遞歸深度遍歷
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
/** * @ create 驗(yàn)證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 2021/7/20 22:10 * @param nodeStack 深度遍歷棧 * @ return void */ public void invaldClosInfo(Stack<DpProcessVo> nodeStack) { // 查看此堆棧頂部的對(duì)象而不將其從堆棧中移除 DpProcessVo dpProcessVo = nodeStack.peek(); // 從redis中查找此集合影響的流程關(guān)系 String dpProcessJson = (String) redisUtil.get(dpProcessVo.getOutCols()); // 如果集合沒(méi)有影響其他集合,則直接返回 if(StringUtils.isBlank(dpProcessJson)){ return ; } //獲得節(jié)點(diǎn)的子節(jié)點(diǎn),對(duì)于二叉樹就是獲得節(jié)點(diǎn)的左子結(jié)點(diǎn)和右子節(jié)點(diǎn) List<DpProcessVo> children = new ArrayList<>(); // redis中原來(lái)存儲(chǔ)的信息 children = JSONArray.parseArray(dpProcessJson, DpProcessVo.class); // 遍歷集合影響的集合關(guān)系 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( "數(shù)據(jù)處理流程配置輸入流程調(diào)用了輸出集合!" ); } // 將dpProcessVo推到這個(gè)堆棧的頂部 nodeStack.push(dpProcessVo1); // 驗(yàn)證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 invaldClosInfo(nodeStack); // 移除此堆棧頂部的對(duì)象并將該對(duì)象作為此函數(shù)的值返回 nodeStack.pop(); } } |
5.完整代碼
記錄代碼,方便日后復(fù)習(xí)、調(diào)用、重構(gòu)。
5.1 Model
模型主要分兩部分:數(shù)據(jù)處理模型和簡(jiǎn)化版的數(shù)據(jù)處理模型。
DpProcess:數(shù)據(jù)處理模型,數(shù)據(jù)完整的Sql操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
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> * 數(shù)據(jù)處理 * </p> * * @since 2021-07-08 */ @Data @EqualsAndHashCode(callSuper = false ) @Accessors(chain = true ) @ApiModel(value= "DpProcess對(duì)象" , description= "數(shù)據(jù)處理 " ) @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=房地產(chǎn)" ) @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 = "狀態(tài) 數(shù)據(jù)狀態(tài):0=正常,1=刪除,失效" ) @TableField(value = "STATUS" , fill = FieldFill. INSERT ) private Integer status; @ApiModelProperty(value = "創(chuàng)建人" ) @TableField(value = "CREATED_BY" , fill = FieldFill. INSERT ) private String createdBy; @ApiModelProperty(value = "創(chuàng)建時(shí)間" ) @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 = "更新時(shí)間" ) @JSONField(format = "yyyy-MM-dd HH:mm:ss" ) @TableField(value = "UPDATED_TIME" , fill = FieldFill. UPDATE ) private Date updatedTime; @ApiModelProperty(value = "樂(lè)觀鎖" ) @Version @TableField(value = "REVISION" , fill = FieldFill. INSERT ) private Integer revision; } |
DpProcessVo: 數(shù)據(jù)處理簡(jiǎn)單模型,處理redis數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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對(duì)象" , description= "數(shù)據(jù)處理簡(jiǎn)單模型 " ) public class DpProcessVo{ @ApiModelProperty(value = "ID" ) private String id; @ApiModelProperty(value = "影響集合" ) private String outCols; } |
5.2 Controller
updateNil:讓用戶選擇使用那種更新方式,也可以把接口一拆為二,主要看個(gè)人習(xí)慣。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/** * @ create 2021-07-08 更新 數(shù)據(jù)處理 * @param dpProcess 數(shù)據(jù)處理 模型 * @param updateNil 全字段更新(新增時(shí)此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether} * @ return */ @ApiOperation(value= "更新" ,notes = "更新" ) @PostMapping( "/modify" ) public Result modify ( @ApiParam( name = "dpProcess" , value = "數(shù)據(jù)處理 模型" , required = true ) @RequestBody DpProcess dpProcess, @ApiParam( name = "updateNil" , value = "全字段更新(新增時(shí)此字段可以忽略): 是: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
沒(méi)啥好說(shuō)的,就是一個(gè)接口。
1
2
3
4
5
6
7
|
/** * @ create 2021-07-08 更新 數(shù)據(jù)處理 * @param dpProcess 數(shù)據(jù)處理 模型 * @param updateNil 全字段更新(新增時(shí)此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether} * @ return */ int modify (DpProcess dpProcess, String updateNil); |
5.4 Service 實(shí)現(xiàn)類
DpRecord:數(shù)據(jù)處理記錄,不是本文重點(diǎn),此處可直接忽略,相關(guān)說(shuō)明 待 數(shù)據(jù)流程處理文章中提現(xiàn)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
/** * @ create 2021-07-08 更新 數(shù)據(jù)處理 * @param dpProcess 數(shù)據(jù)處理 模型 * @param updateNil 全字段更新(新增時(shí)此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether} * @ return */ @Override public int modify (DpProcess dpProcess, String updateNil){ if(dpProcess == null ){ throw new ServiceException( "數(shù)據(jù)處理模型不能為空!" ); } // 走更新方法 // 通過(guò)id查詢數(shù)據(jù)處理 詳情 DpProcess orignDpProcess = this.detail(dpProcess.getId()); if(dpProcess == null ){ throw new ServiceException( "數(shù)據(jù)處理模型信息不能為空!" ); } // 如果當(dāng)前任務(wù)已存在,需要先進(jìn)行取消 if( "0" .equals(dpProcess.getEnabled())){ if(defaultSchedulingConfigurer.hasTask(dpProcess.getId())){ defaultSchedulingConfigurer.cancelTriggerTask(dpProcess.getId()); } // 根據(jù)數(shù)據(jù)處理ID查看數(shù)據(jù)庫(kù)中是否有需要執(zhí)行的數(shù)據(jù)處理記錄 DpRecord dpRecord = dpRecordService.getNeedExecRecordByDppId(dpProcess.getId()); // 如果數(shù)據(jù)處理記錄信息為空,則進(jìn)行新增 if(dpRecord != null ){ // 設(shè)置結(jié)束時(shí)間為當(dāng)前時(shí)間 dpRecord.setEndTime(new Date ()); // 運(yùn)行失敗 dpRecord.setSucceed( "2" ); dpRecord.setFailedResult( "用戶取消操作" ); } // 對(duì)數(shù)據(jù)處理記錄進(jìn)行更新或者保存 dpRecordService.addOrUpdate(dpRecord, null ); } // 限制輸出集合不能為空 dpProcess.setOutCols(StringUtils.isNotBlank(dpProcess.getOutCols()) ? dpProcess.getOutCols() : orignDpProcess.getOutCols()); if(StringUtils.isBlank(dpProcess.getOutCols())){ throw new ServiceException( "數(shù)據(jù)影響集合不能為空!" ); } // 輸入集合統(tǒng)一處理 operInclos(dpProcess, orignDpProcess.getInCols()); // 全字段更新 if(SystemConst.Whether.Yes.getCode().equals(updateNil)){ if(StringUtils.isBlank(dpProcess.getRemark())){ throw new ServiceException( "數(shù)據(jù)處理備注不能為空!" ); } // 備注不能小于20字 if(dpProcess.getRemark().length() < 20){ throw new ServiceException( "數(shù)據(jù)處理備注不能小于20字!" ); } return dpProcessMapper.alwaysUpdateSomeColumnById(dpProcess); } // 數(shù)據(jù)處理代碼自動(dòng)填充 dpProcess.setCode(StringUtils.isBlank(dpProcess.getCode()) ? orignDpProcess.getCode() : dpProcess.getCode()); return dpProcessMapper.updateById(dpProcess); } |
operInclos() : 處理輸入集合的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
/** * @ create 輸入集合統(tǒng)一處理 2021/7/11 14:13 * @param dpProcess 新數(shù)據(jù)處理對(duì)象 * @param oldClos 原數(shù)據(jù)處理對(duì)象中的而輸入集合 * @ return */ private void operInclos(DpProcess dpProcess, String oldClos) { // 新數(shù)據(jù)處理對(duì)象中的輸入集合 String inCols = dpProcess.getInCols(); // 若新數(shù)據(jù)處理對(duì)象中的輸入集合沒(méi)有值,則直接跳過(guò),不進(jìn)行操作 if(StringUtils.isNotBlank(inCols)){ if(dpProcess.getInCols(). contains (dpProcess.getOutCols())){ throw new ServiceException( "數(shù)據(jù)處理流程配置輸入流程調(diào)用了輸出集合!" ); } // 數(shù)據(jù)類型轉(zhuǎn)換 Set <String> set = new HashSet(Arrays.asList(inCols.split( "," ))); // 循環(huán)遍歷輸入集合 for (String inClo : set ) { // 最終需要遍歷的list List<DpProcessVo> childFinalList = new ArrayList<>(); // 從redis中獲取當(dāng)前集合的影響關(guān)系 String dpProcessJson = (String) redisUtil.get(inClo); // 如果redis中存儲(chǔ)的集合影響關(guān)系不為空,做簡(jiǎn)單的遍歷去重處理 if(StringUtils.isNotBlank(dpProcessJson)){ // redis中存儲(chǔ)的集合影響關(guān)系列表 List<DpProcessVo> children = new ArrayList<>(); // 進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換 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中沒(méi)有此輸入集合的影響關(guān)系,則可以直接進(jìn)行添加 else { DpProcessVo dpProcess1 = new DpProcessVo(); dpProcess1.setId(dpProcess.getId()); dpProcess1.setOutCols(dpProcess.getOutCols()); childFinalList. add (dpProcess1); } // 驗(yàn)證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 Stack<DpProcessVo> nodeStack = new Stack<>(); // 設(shè)置模型 DpProcessVo dpProcessVoTop = new DpProcessVo(); dpProcessVoTop.setOutCols(inClo); dpProcessVoTop.setId(dpProcess.getId()); nodeStack. add (dpProcessVoTop); // 遍歷需要進(jìn)行死鏈校驗(yàn)的數(shù)據(jù) for (DpProcessVo dpProcessVo : childFinalList) { // 是否添加標(biāo)識(shí)(默認(rèn)為添加,如果集合為死鏈,則進(jìn)行提示) boolean addFlag = true ; // 循環(huán)遍歷棧 for (DpProcessVo processVo : nodeStack) { if(processVo.getOutCols().equals(dpProcessVo.getOutCols())){ addFlag = false ; break; } } if(!addFlag){ throw new ServiceException( "數(shù)據(jù)處理流程配置輸入流程調(diào)用了輸出集合!" ); } // 將dpProcessVo推到這個(gè)堆棧的頂部 nodeStack.push(dpProcessVo); // 驗(yàn)證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 invaldClosInfo(nodeStack); // 移除此堆棧頂部的對(duì)象并將該對(duì)象作為此函數(shù)的值返回 nodeStack.pop(); } } // 處理需要?jiǎng)h除的集合 dealNeedDeleteCols(dpProcess, oldClos, set ); // 獲取并設(shè)置最終的集合名稱 String finallyCols = StringUtils. join ( set .toArray(), "," ); dpProcess.setInCols(finallyCols); // 能走到這一步,說(shuō)明所有的集合沒(méi)有問(wèn)題,可以進(jìn)行更新操作了(再一次遍歷是為了和上面的校驗(yàn)分開,避免部分?jǐn)?shù)據(jù)被更新) for (String inClo : set ) { List<DpProcessVo> dpProcessVoList = new ArrayList<>(); // 首先獲取當(dāng)前集合影響的數(shù)據(jù)處理對(duì)象 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); // 進(jìn)行數(shù)據(jù)的存儲(chǔ) redisUtil. set (inClo, JSONArray.toJSON(dpProcessVoList).toString()); continue ; } // redis中原來(lái)存儲(chǔ)的信息 List<DpProcessVo> dpProcessVos = JSONArray.parseArray(dpProcessJson, DpProcessVo.class); // 把數(shù)據(jù)處理對(duì)象轉(zhuǎn)換為HashSet HashSet<DpProcessVo> hashSet = new HashSet(dpProcessVos); // 當(dāng)前集合影響的 其他集合列表 List<DpProcessVo> childFinalList = new ArrayList<>(); // 遍歷redis中存儲(chǔ)的集合影響關(guān)系,并進(jìn)行簡(jiǎn)單去重處理 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()); // 添加當(dāng)前數(shù)據(jù)數(shù)據(jù)對(duì)象 childFinalList. add (dpProcessVo); // 進(jìn)行數(shù)據(jù)的存儲(chǔ) redisUtil. set (inClo, JSONArray.toJSON(childFinalList).toString()); } } } |
invaldClosInfo() : 驗(yàn)證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
/** * @ create 驗(yàn)證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 2021/7/20 22:10 * @param nodeStack 深度遍歷棧 * @ return void */ public void invaldClosInfo(Stack<DpProcessVo> nodeStack) { // 查看此堆棧頂部的對(duì)象而不將其從堆棧中移除 DpProcessVo dpProcessVo = nodeStack.peek(); // 從redis中查找此集合影響的流程關(guān)系 String dpProcessJson = (String) redisUtil.get(dpProcessVo.getOutCols()); // 如果集合沒(méi)有影響其他集合,則直接返回 if(StringUtils.isBlank(dpProcessJson)){ return ; } //獲得節(jié)點(diǎn)的子節(jié)點(diǎn),對(duì)于二叉樹就是獲得節(jié)點(diǎn)的左子結(jié)點(diǎn)和右子節(jié)點(diǎn) List<DpProcessVo> children = new ArrayList<>(); // redis中原來(lái)存儲(chǔ)的信息 children = JSONArray.parseArray(dpProcessJson, DpProcessVo.class); // 遍歷集合影響的集合關(guān)系 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( "數(shù)據(jù)處理流程配置輸入流程調(diào)用了輸出集合!" ); } // 將dpProcessVo推到這個(gè)堆棧的頂部 nodeStack.push(dpProcessVo1); // 驗(yàn)證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 invaldClosInfo(nodeStack); // 移除此堆棧頂部的對(duì)象并將該對(duì)象作為此函數(shù)的值返回 nodeStack.pop(); } } |
dealNeedDeleteCols() : 主要處理--原數(shù)據(jù)為 A 集合影響 B 集合,修改為 C 集合影響了 B 集合,此時(shí)需要?jiǎng)h除 A 對(duì) B的影響關(guān)系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
/** * @ create 處理需要?jiǎng)h除的集合 2021/7/20 17:58 * @param dpProcess 數(shù)據(jù)處理模型 * @param oldClos 原來(lái)的數(shù)據(jù)處理模型中的集合信息 * @param set 最新的集合名稱信息 * @ return void */ private void dealNeedDeleteCols(DpProcess dpProcess, String oldClos, Set <String> set ) { if(StringUtils.isBlank(oldClos)){ return ; } // 獲取去重后的集合數(shù)組 List<String> newColsList = new ArrayList<>( set ); // 原來(lái)的集合數(shù)組 List<String> oldColsList = Arrays.asList(oldClos.split( "," )); // 獲取兩個(gè)集合的差集 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中原來(lái)存儲(chǔ)的信息 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(); } } // 如果當(dāng)前集合影響的流程為空,則進(jìn)行刪除 if(hashSet.isEmpty()){ // 進(jìn)行數(shù)據(jù)的存儲(chǔ) redisUtil. delete (clos); continue ; } // 進(jìn)行數(shù)據(jù)的存儲(chǔ) redisUtil. set (clos, JSONArray.toJSON(hashSet.toArray()).toString()); } } |
6.測(cè)試
可通過(guò)單元測(cè)試等多種方式,本文提供簡(jiǎn)單的測(cè)試數(shù)據(jù)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
{ "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 } |
到此這篇關(guān)于java中避免集合死鏈調(diào)用詳情的文章就介紹到這了,更多相關(guān)java中避免集合死鏈調(diào)用內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://www.cnblogs.com/wgx519/p/15324635.html?utm_source=tuicool&utm_medium=referral