需求背景和描述
对于一个工程项目,前后依赖的jar包要比较审批,以免因为某些依赖增删改导致线上 故障。
如下:(https://www.diffchecker.com/zh-Hans/text-compare/)
难点:
- 对于com:jar4这个依赖,需要表达成修改了一个,新增了一个,且能够看出有依赖冲突,引入了2个com:jar4
- 对于返回左右两的list<String>, 如果两个list size不同,前端显示会错位, 即需要返回如下才能正常显示
左
"com:jar1:1.0.0",
"com:jar2:1.0.0",
"com:jar3:1.0.0",
"com:jar4:1.0.0"
""
""
右
""
"com:jar2:1.0.0",
"com:jar3:1.0.0",
"com:jar4:1.0.1",
"com:jar4:1.0.2",
"com:jar5:1.1.0"
空字符串认为是不存在,前端就能清晰的用表格diff出来
Java代码处理返回
思考左右两边(即一个是老的依赖列表,一个是新的依赖列表)
对于左边:删除,或者修改
对于右边: 新增,或者修改(和左边一一对应的修改)
工具类:
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;public class GavDiffUtil {public static final String DESC_ADD = "[新增]";public static final String DESC_DELETE = "[删除]";public static final String DESC_MODIFY = "[修改]";public static class DiffResult {private List<String> oldList;private List<String> newList;private List<String> leftDeleteList;private List<String> rightAddList;private Map<String, String> leftRightModifyMap;public List<String> getOldList() {return oldList;}public void setOldList(List<String> oldList) {this.oldList = oldList;}public List<String> getNewList() {return newList;}public void setNewList(List<String> newList) {this.newList = newList;}public List<String> getLeftDeleteList() {return leftDeleteList;}public void setLeftDeleteList(List<String> leftDeleteList) {this.leftDeleteList = leftDeleteList;}public List<String> getRightAddList() {return rightAddList;}public void setRightAddList(List<String> rightAddList) {this.rightAddList = rightAddList;}public Map<String, String> getLeftRightModifyMap() {return leftRightModifyMap;}public void setLeftRightModifyMap(Map<String, String> leftRightModifyMap) {this.leftRightModifyMap = leftRightModifyMap;}public static void printResult(DiffResult diffResult) {List<String> newList = diffResult.getNewList();List<String> oldList = diffResult.getOldList();List<String> leftDeleteList = diffResult.getLeftDeleteList();List<String> rightAddList = diffResult.getRightAddList();Map<String, String> modifyMap = diffResult.getLeftRightModifyMap();for (int i = 0; i < newList.size(); i++) {String left = oldList.get(i);String right = newList.get(i);if (left.length() == 0) {left = "空";}if (right.length() == 0) {right = "空";}String operate = null;if (leftDeleteList.contains(left)) {operate = DESC_DELETE;}if (rightAddList.contains(right)) {operate = DESC_ADD;}if (modifyMap.containsKey(left)) {operate = DESC_MODIFY;}if (operate != null) {System.out.printf("%20s\t%20s\t%5s", left, right, operate);} else {System.out.printf("%20s\t%20s", left, right);}System.out.println();}}}public static final Function<String, String> gavFunction = (o) -> {String[] arr = o.split(":");String key = arr[0] + ":" + arr[1];return key;};public static DiffResult getDiffResult(final List<String> oldJars, final List<String> curJars, Function<String, String> getKeyFunction) {/*** 第一步,得到左右两边* 形如 <com:jar1:1.0.0, com:jar1> 的map 和* 形如 <com:jar1, [com:jar1:1.0.0, com:jar1:1.0.1]> 的map* 主要是判断修改的标准不一样,对于jar依赖:ga相同、v不同,则认为是修改操作,即本次是升级还是降级了某个依赖*/Map<String, String> curJarKeyMap = new HashMap<>(512);Map<String, List<String>> curGroupMap = curJars.stream().collect(Collectors.groupingBy(o -> {// 认为 g,a相同的,则是一个依赖String key = getKeyFunction.apply(o);curJarKeyMap.put(o, key);return key;}));Map<String, String> oldJarKeyMap = new HashMap<>(512);Map<String, List<String>> oldGroupMap = oldJars.stream().collect(Collectors.groupingBy(o -> {String key = getKeyFunction.apply(o);oldJarKeyMap.put(o, key);return key;}));/*** 第二步,得到diff情况,按照如下三种diff得到* 1. 左边删除* 2. 右边新增* 3. 左边修改, 右边对应修改*/List<String> leftDelete = new ArrayList<>();List<String> rightAdd = new ArrayList<>();Map<String, String> leftRightModifyMap = new HashMap<>(64);// 纯粹新增的jarfor (String key : curGroupMap.keySet()) {if (!oldGroupMap.containsKey(key)) {List<String> jars = curGroupMap.get(key);rightAdd.addAll(jars);}}for (String key : oldGroupMap.keySet()) {List<String> olds = oldGroupMap.get(key);if (!curGroupMap.containsKey(key)) {// 纯粹删除的jarleftDelete.addAll(olds);} else {// 有版本变化的,注意会有多版本问题,即引入一个依赖的多个版本List<String> news = curGroupMap.get(key);// 1.先获取一样的List<String> sameJars = new ArrayList<>();for (String oldJar : olds) {if (news.contains(oldJar)) {sameJars.add(oldJar);}}// 2.去除一样的if (!sameJars.isEmpty()) {olds.removeAll(sameJars);news.removeAll(sameJars);}// 3.剩下的就是修改(增加、删除了)int oldSize = olds.size();int newSize = news.size();int minLen = Math.min(oldSize, newSize);for (int i = 0; i < minLen; i++) {leftRightModifyMap.put(olds.get(i), news.get(i));}for (int i = minLen; i < oldSize; i++) {// 左边多:认定为删除leftDelete.add(olds.get(i));}for (int i = minLen; i < newSize; i++) {// 右边多:认定为新增rightAdd.add(news.get(i));}}}/*** 第三步,拼成前端需要的两边长度一样的diff, 并排序,作为最后的修改* 空字符串替换不存在的*/List<String> oldList = new ArrayList<>(oldJars);oldList.addAll(rightAdd);Collections.sort(oldList);List<String> finalOldList = new ArrayList<>();List<String> finalNewList = new ArrayList<>();for (String jar : oldList) {if (oldJars.contains(jar)) {finalOldList.add(jar);} else {finalOldList.add("");}if (curJars.contains(jar)) {finalNewList.add(jar);} else if (rightAdd.contains(jar)) {finalNewList.add(jar);} else if (leftRightModifyMap.containsKey(jar)) {finalNewList.add(leftRightModifyMap.get(jar));} else {finalNewList.add("");}}/*** 第4步: 构造返回*/DiffResult result = new DiffResult();result.setNewList(finalNewList);result.setOldList(finalOldList);result.setLeftDeleteList(leftDelete);result.setRightAddList(rightAdd);result.setLeftRightModifyMap(leftRightModifyMap);return result;}}
测试
import java.util.Arrays;
import java.util.List;public class GavDiffTest {public static void main(String[] args) {System.out.println("case1:");testDiffJars1();System.out.println();System.out.println("case2");testDiffJars2();System.out.println();System.out.println("case3");testDiffJars3();System.out.println();System.out.println("case4");testDiffJars4();System.out.println();System.out.println("case5");testDiffJars5();System.out.println();}public static void testDiffJars1() {List<String> oldJars = Arrays.asList("com:jar1:1.0.0","com:jar2:1.0.0","com:jar3:1.0.0","com:jar4:1.0.0");List<String> curJars = Arrays.asList("com:jar2:1.0.0","com:jar3:1.0.0","com:jar4:1.0.1","com:jar5:1.1.0");GavDiffUtil.DiffResult diffResult = GavDiffUtil.getDiffResult(curJars, oldJars, GavDiffUtil.gavFunction);GavDiffUtil.DiffResult.printResult(diffResult);}public static void testDiffJars2() {List<String> oldJars = Arrays.asList("com:jar1:1.0.0","com:jar2:1.0.0","com:jar3:1.0.0","com:jar4:1.0.0","com:jar4:1.0.1");List<String> curJars = Arrays.asList("com:jar2:1.0.0","com:jar3:1.0.0","com:jar4:1.0.2","com:jar5:1.1.0");GavDiffUtil.DiffResult diffResult = GavDiffUtil.getDiffResult(curJars, oldJars, GavDiffUtil.gavFunction);GavDiffUtil.DiffResult.printResult(diffResult);}public static void testDiffJars3() {List<String> oldJars = Arrays.asList("com:jar1:1.0.0","com:jar2:1.0.0","com:jar3:1.0.0","com:jar4:1.0.0");List<String> curJars = Arrays.asList("com:jar2:1.0.0","com:jar3:1.0.0","com:jar4:1.0.1","com:jar4:1.0.2","com:jar5:1.1.0");GavDiffUtil.DiffResult diffResult = GavDiffUtil.getDiffResult(curJars, oldJars, GavDiffUtil.gavFunction);GavDiffUtil.DiffResult.printResult(diffResult);}public static void testDiffJars4() {List<String> oldJars = Arrays.asList("com:jar1:1.0.0","com:jar2:1.0.0","com:jar3:1.0.0","com:jar4:1.0.0");List<String> curJars = Arrays.asList("com:jar2:1.0.0","com:jar3:1.0.0","com:jar4:1.0.0","com:jar4:1.0.2","com:jar5:1.1.0");GavDiffUtil.DiffResult diffResult = GavDiffUtil.getDiffResult(curJars, oldJars, GavDiffUtil.gavFunction);GavDiffUtil.DiffResult.printResult(diffResult);}public static void testDiffJars5() {List<String> oldJars = Arrays.asList("com:jar1:1.0.0","com:jar2:1.0.0","com:jar3:1.0.0","com:jar4:1.0.0");List<String> curJars = Arrays.asList("com:jar2:1.0.0","com:jar4:1.0.0","com:jar4:1.0.2","com:jar5:1.1.0");GavDiffUtil.DiffResult diffResult = GavDiffUtil.getDiffResult(curJars, oldJars, GavDiffUtil.gavFunction);GavDiffUtil.DiffResult.printResult(diffResult);}
}
case1: 新增、删除、修改都有
case2: 新增、删除、修改都有
case5: 新增、删除