From 50a56fed325ad20cf77bdd278419b0c6c5334a72 Mon Sep 17 00:00:00 2001 From: lau572 <1010031226@qq.com> Date: Thu, 11 Apr 2024 15:51:11 +0800 Subject: [PATCH] =?UTF-8?q?1.=E4=BA=8B=E4=BB=B6=E5=A4=84=E7=BD=AE=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=202.=E6=AF=94=E5=AF=B9=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/DcEventController.java | 44 ++ .../com/zc/business/enums/ValueConverter.java | 119 +++++ .../com/zc/business/mapper/DcEventMapper.java | 21 + .../business/mapper/DcEventProcessMapper.java | 2 + .../zc/business/service/IDcEventService.java | 31 ++ .../impl/DcEventProcessServiceImpl.java | 10 + .../service/impl/DcEventServiceImpl.java | 504 +++++++++++++++++- .../zc/business/utils/diff/AlgorithmEnum.java | 82 +++ .../java/com/zc/business/utils/diff/Diff.java | 208 ++++++++ .../algorithm/AbstractObjectAndArray.java | 49 ++ .../algorithm/AbstractPrimitiveAndOther.java | 34 ++ .../utils/diff/algorithm/AlgorithmModule.java | 66 +++ .../utils/diff/algorithm/Comparator.java | 9 + .../diff/algorithm/array/AbstractArray.java | 22 + .../diff/algorithm/array/ArrayComparator.java | 43 ++ .../array/SimilarArrayComparator.java | 252 +++++++++ .../array/SimpleArrayComparator.java | 62 +++ .../nulls/DefaultNullComparator.java | 27 + .../diff/algorithm/nulls/NullComparator.java | 25 + .../diff/algorithm/object/AbstractObject.java | 128 +++++ .../object/LeftJoinObjectComparator.java | 30 ++ .../algorithm/object/ObjectComparator.java | 40 ++ .../object/SimpleObjectComparator.java | 33 ++ .../other/DefaultOtherComparator.java | 38 ++ .../diff/algorithm/other/OtherComparator.java | 24 + .../primitive/DefaultPrimitiveComparator.java | 39 ++ .../primitive/PrimitiveComparator.java | 23 + .../business/utils/diff/model/Constants.java | 20 + .../utils/diff/model/DiffContext.java | 61 +++ .../business/utils/diff/model/PathModule.java | 122 +++++ .../zc/business/utils/diff/model/Result.java | 71 +++ .../utils/diff/model/ResultConvertUtil.java | 66 +++ .../diff/model/SingleNodeDifference.java | 69 +++ .../mapper/business/DcEventMapper.xml | 14 + .../mapper/business/DcEventProcessMapper.xml | 6 + 35 files changed, 2393 insertions(+), 1 deletion(-) create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/AlgorithmEnum.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/Diff.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/AbstractObjectAndArray.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/AbstractPrimitiveAndOther.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/AlgorithmModule.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/Comparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/AbstractArray.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/ArrayComparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/SimilarArrayComparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/SimpleArrayComparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/nulls/DefaultNullComparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/nulls/NullComparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/AbstractObject.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/LeftJoinObjectComparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/ObjectComparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/SimpleObjectComparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/other/DefaultOtherComparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/other/OtherComparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/primitive/DefaultPrimitiveComparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/algorithm/primitive/PrimitiveComparator.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/model/Constants.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/model/DiffContext.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/model/PathModule.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/model/Result.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/model/ResultConvertUtil.java create mode 100644 zc-business/src/main/java/com/zc/business/utils/diff/model/SingleNodeDifference.java diff --git a/zc-business/src/main/java/com/zc/business/controller/DcEventController.java b/zc-business/src/main/java/com/zc/business/controller/DcEventController.java index f075ffe3..d42626b4 100644 --- a/zc-business/src/main/java/com/zc/business/controller/DcEventController.java +++ b/zc-business/src/main/java/com/zc/business/controller/DcEventController.java @@ -170,4 +170,48 @@ public class DcEventController extends BaseController return toAjax(dcEventService.updateDcEventState(id,state)); } + + + + /** + * @Description 查询事件流程状态 + * + * @author liuwenge + * @date 2024/4/11 11:19 + * @param eventId 事件id + * @return com.ruoyi.common.core.domain.AjaxResult + */ + @ApiOperation("查询事件流程状态") + @GetMapping( "/getProcessNode/{eventId}") + public AjaxResult getProcessNode(@ApiParam(name = "eventId", value = "事件id", required = true) @PathVariable("eventId") String eventId){ + return dcEventService.getProcessNode(eventId); + } + + /** + * @Description 解除事件 + * + * @author liuwenge + * @date 2024/4/11 14:12 + * @param eventId + * @return com.ruoyi.common.core.domain.AjaxResult + */ + @ApiOperation("解除事件") + @PostMapping("/completeEvent") + public AjaxResult completeEvent(String eventId){ + return dcEventService.completeEvent(eventId); + } + + /** + * @Description 解除事件 + * + * @author liuwenge + * @date 2024/4/11 14:12 + * @param eventId + * @return com.ruoyi.common.core.domain.AjaxResult + */ + @ApiOperation("无需清障") + @PostMapping("/skipClear") + public AjaxResult skipClear(String eventId){ + return dcEventService.skipClear(eventId); + } } diff --git a/zc-business/src/main/java/com/zc/business/enums/ValueConverter.java b/zc-business/src/main/java/com/zc/business/enums/ValueConverter.java index 50512827..5af2c2d5 100644 --- a/zc-business/src/main/java/com/zc/business/enums/ValueConverter.java +++ b/zc-business/src/main/java/com/zc/business/enums/ValueConverter.java @@ -26,6 +26,8 @@ public class ValueConverter { private static final Map eventSourceName = new HashMap<>(); // 事件类型名称 private static final Map eventTypeName = new HashMap<>(); + // 事件字段 + private static final Map eventLabel = new HashMap<>(); static { valueMappingSource.put(1,5); @@ -112,6 +114,115 @@ public class ValueConverter { eventTypeName.put("9","设施设备隐患"); eventTypeName.put("10","异常天气"); eventTypeName.put("11","其他事件"); + + eventLabel.put("id","事件编号"); + eventLabel.put("deptId","所属部门"); + eventLabel.put("stakeMark","桩号"); + eventLabel.put("direction","方向"); + eventLabel.put("userId","处理人员"); + eventLabel.put("startTime","开始时间"); + eventLabel.put("endTime","结束时间"); + eventLabel.put("estimatedEndTime","预计解除时间"); + eventLabel.put("eventLevel","事件等级"); +// eventLabel.put("eventType","事件类型"); + eventLabel.put("eventCause","事件原因"); + eventLabel.put("description","描述"); + eventLabel.put("eventState","状态"); + eventLabel.put("eventSource","来源"); +// eventLabel.put("eventNature","事件性质"); + eventLabel.put("eventSourceTips","来源补充说明"); + eventLabel.put("occurrenceTime","发生时间"); +// eventLabel.put("isPerceived","是否感知事件"); + eventLabel.put("lang","影响车道"); + eventLabel.put("roadName","高速名称"); + eventLabel.put("eventTitle","时间标题"); + eventLabel.put("remark","备注"); + eventLabel.put("dcEventAbnormalWeather.weatherSituation","天气情况"); + eventLabel.put("dcEventAbnormalWeather.emergencyLevel","紧急级别"); + eventLabel.put("dcEventAbnormalWeather.endStakeMark","终止桩号"); + eventLabel.put("dcEventAbnormalWeather.numericalValue","异常天气数值"); + eventLabel.put("dcEventAccident.reporterName","报警人姓名"); + eventLabel.put("dcEventAccident.reporterPhoneNumber","报警人电话"); + eventLabel.put("dcEventAccident.locationType","地点"); + eventLabel.put("dcEventAccident.trafficJam","压车(公里)"); + eventLabel.put("dcEventAccident.weatherCondition","天气情况"); + eventLabel.put("dcEventAccident.impactLevel","影响"); + eventLabel.put("dcEventAccident.isReverseCargo","是否倒货"); + eventLabel.put("dcEventAccident.isMaintenance","是否养护事故"); + eventLabel.put("dcEventAccident.policeContact","交警电话"); + eventLabel.put("dcEventAccident.towingServiceContact","清障电话"); + eventLabel.put("dcEventAccident.congestionAhead","前方是否拥堵"); + eventLabel.put("dcEventAccident.atIntersection","是否分岔口"); + eventLabel.put("dcEventAccident.onCurve","是否处在弯道"); + eventLabel.put("dcEventAccident.spillageItem","洒落物名称"); + eventLabel.put("dcEventAccident.vehicleOwnerPhone","车主电话"); + eventLabel.put("dcEventAccident.smallCar","小型车(辆)"); + eventLabel.put("dcEventAccident.trucks","货车(辆)"); + eventLabel.put("dcEventAccident.buses","客车(辆)"); + eventLabel.put("dcEventAccident.tankers","罐车(辆)"); + eventLabel.put("dcEventAccident.minorInjuries","轻伤(人)"); + eventLabel.put("dcEventAccident.seriousInjuries","重伤(人)"); + eventLabel.put("dcEventAccident.fatalities","死亡(人)"); + eventLabel.put("dcEventAccident.isPrivate","私密事件"); + eventLabel.put("dcEventAccident.facilityId","设施关联"); + eventLabel.put("dcEventAccident.rampId","匝道id"); + eventLabel.put("dcEventAccident.location","地点"); + eventLabel.put("dcEventConstruction.controlMode","管制方式"); + eventLabel.put("dcEventConstruction.locationType","地点类型"); + eventLabel.put("dcEventConstruction.specialPlaceDescription","特殊地点描述"); + eventLabel.put("dcEventConstruction.specialConstruction","专项施工"); + eventLabel.put("dcEventConstruction.otherConstructionName","其他施工名称"); + eventLabel.put("dcEventConstruction.constructionMeasurement","施工措施"); + eventLabel.put("dcEventConstruction.facilityId","设施id"); + eventLabel.put("dcEventConstruction.exitsInlets","出入口"); + eventLabel.put("dcEventConstruction.laneOccupancy","占用车道"); + eventLabel.put("dcEventConstruction.trafficCondition","通行情况"); + eventLabel.put("dcEventConstruction.constructionMethod","施工方式"); + eventLabel.put("dcEventConstruction.localRoadName","地方道路名称"); + eventLabel.put("dcEventConstruction.endStakeMark","结束桩号"); + eventLabel.put("dcEventConstruction.location","地点"); + eventLabel.put("dcEventServiceArea.exitsInlets","出入口"); + eventLabel.put("dcEventServiceArea.facilityId","服务区"); + eventLabel.put("dcEventServiceArea.disableFacility","停用设施"); + eventLabel.put("dcEventTrafficCongestion.congestionMileage","拥堵里程(公里)"); + eventLabel.put("dcEventTrafficCongestion.maxCongestionMileage","最大拥堵里程(公里)"); + eventLabel.put("dcEventTrafficCongestion.congestionCause","拥堵原因"); + eventLabel.put("dcEventTrafficCongestion.facilityId","设施id"); + eventLabel.put("dcEventTrafficCongestion.rampId","匝道"); + eventLabel.put("dcEventTrafficCongestion.location","地点"); + eventLabel.put("dcEventTrafficCongestion.detailedReasons","详细原因"); + eventLabel.put("dcEventTrafficCongestion.locationType","地点类型"); + eventLabel.put("dcEventTrafficCongestion.locationDescription","地点描述"); + eventLabel.put("dcEventTrafficCongestion.endStakeMark","终止桩号"); + eventLabel.put("dcEventVehicleAccident.reporterName","报警人姓名"); + eventLabel.put("dcEventVehicleAccident.reporterPhoneNumber","报警人电话"); + eventLabel.put("dcEventVehicleAccident.locationType","地点方式"); + eventLabel.put("dcEventVehicleAccident.trafficJam","压车(公里)"); + eventLabel.put("dcEventVehicleAccident.weatherCondition","天气情况"); + eventLabel.put("dcEventVehicleAccident.congestionAhead","前方是否拥堵"); + eventLabel.put("dcEventVehicleAccident.atIntersection","是否分岔口"); + eventLabel.put("dcEventVehicleAccident.onCurve","是否处在弯道"); + eventLabel.put("dcEventVehicleAccident.smallCar","小型车(辆)"); + eventLabel.put("dcEventVehicleAccident.trucks","货车(辆)"); + eventLabel.put("dcEventVehicleAccident.buses","客车(辆)"); + eventLabel.put("dcEventVehicleAccident.tankers","罐车(辆)"); + eventLabel.put("dcEventVehicleAccident.minorInjuries","轻伤(人)"); + eventLabel.put("dcEventVehicleAccident.seriousInjuries","重伤(人)"); + eventLabel.put("dcEventVehicleAccident.fatalities","死亡(人)"); + eventLabel.put("dcEventVehicleAccident.isPrivate","私密事件"); + eventLabel.put("dcEventVehicleAccident.facilityId","设施id"); + eventLabel.put("dcEventVehicleAccident.rampId","匝道"); + eventLabel.put("dcEventVehicleAccident.location","地点"); + eventLabel.put("dcEventTrafficControl.controlType","管制分类"); + eventLabel.put("dcEventTrafficControl.controlCause","管制原因"); + eventLabel.put("dcEventTrafficControl.exitsInlets","出入口"); + eventLabel.put("dcEventTrafficControl.facilityId","设施id"); + eventLabel.put("dcEventTrafficControl.rampId","匝道"); + eventLabel.put("dcEventTrafficControl.causeType","原因类型"); + eventLabel.put("dcEventTrafficControl.measure","措施"); + eventLabel.put("dcEventTrafficControl.classify","分类"); + eventLabel.put("dcEventTrafficControl.limitedType","限制类型"); +// eventLabel.put("dcEventTrafficControl.vehicleType","车辆类型"); } } @@ -133,6 +244,10 @@ public class ValueConverter { public static Map getEventTypeName() { return ValueMappingHolder.eventTypeName; } + + public static Map getEventLabel() { + return ValueMappingHolder.eventLabel; + } /** * 根据输入值返回转换后的值 * 如果输入值不在映射中,则返回输入值本身 @@ -162,6 +277,10 @@ public class ValueConverter { Map mapping = getEventTypeName(); return mapping.getOrDefault(inputValue, inputValue); } + public static String eventLabel(String inputValue) { + Map mapping = getEventLabel(); + return mapping.getOrDefault(inputValue, inputValue); + } //测试 // @Test diff --git a/zc-business/src/main/java/com/zc/business/mapper/DcEventMapper.java b/zc-business/src/main/java/com/zc/business/mapper/DcEventMapper.java index b4be7c31..ab62a6d7 100644 --- a/zc-business/src/main/java/com/zc/business/mapper/DcEventMapper.java +++ b/zc-business/src/main/java/com/zc/business/mapper/DcEventMapper.java @@ -6,6 +6,7 @@ import com.zc.business.domain.DcEvent; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import java.util.Date; import java.util.List; import java.util.Map; @@ -85,6 +86,26 @@ public interface DcEventMapper extends BaseMapper boolean batchInsertDcEvent(List eventList); int updateDcEventState(@Param("id") String id,@Param("state") int state); + + /** + * @Description 查询事件流程状态 + * + * @author liuwenge + * @date 2024/4/11 11:19 + * @param eventId 事件id + * @return com.ruoyi.common.core.domain.AjaxResult + */ + List> getProcessNode(@Param("eventId") String eventId); + + /** + * @Description 解除事件 + * + * @author liuwenge + * @date 2024/4/11 14:15 + * @param eventId + * @return boolean + */ + boolean completeEvent(@Param("eventId") String eventId); } diff --git a/zc-business/src/main/java/com/zc/business/mapper/DcEventProcessMapper.java b/zc-business/src/main/java/com/zc/business/mapper/DcEventProcessMapper.java index 042c50cb..d512e150 100644 --- a/zc-business/src/main/java/com/zc/business/mapper/DcEventProcessMapper.java +++ b/zc-business/src/main/java/com/zc/business/mapper/DcEventProcessMapper.java @@ -73,4 +73,6 @@ public interface DcEventProcessMapper * @return */ List selectDcDispatchByEventId(String id); + + int selectFinalNode(String eventId); } diff --git a/zc-business/src/main/java/com/zc/business/service/IDcEventService.java b/zc-business/src/main/java/com/zc/business/service/IDcEventService.java index e97685fe..1740155f 100644 --- a/zc-business/src/main/java/com/zc/business/service/IDcEventService.java +++ b/zc-business/src/main/java/com/zc/business/service/IDcEventService.java @@ -1,6 +1,7 @@ package com.zc.business.service; +import com.ruoyi.common.core.domain.AjaxResult; import com.zc.business.domain.DcEvent; import java.util.List; @@ -91,4 +92,34 @@ public interface IDcEventService List eventPileNumberQueryEvent(Map parameter); int updateDcEventState(String id, int state); + + /** + * @Description 查询事件流程状态 + * + * @author liuwenge + * @date 2024/4/11 11:19 + * @param eventId 事件id + * @return com.ruoyi.common.core.domain.AjaxResult + */ + AjaxResult getProcessNode(String eventId); + + /** + * @Description 解除事件 + * + * @author liuwenge + * @date 2024/4/11 14:13 + * @param eventId + * @return com.ruoyi.common.core.domain.AjaxResult + */ + AjaxResult completeEvent(String eventId); + + /** + * @Description 无需清障 + * + * @author liuwenge + * @date 2024/4/11 14:13 + * @param eventId + * @return com.ruoyi.common.core.domain.AjaxResult + */ + AjaxResult skipClear(String eventId); } diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcEventProcessServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcEventProcessServiceImpl.java index d98b0a7f..4c91278c 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcEventProcessServiceImpl.java +++ b/zc-business/src/main/java/com/zc/business/service/impl/DcEventProcessServiceImpl.java @@ -7,6 +7,7 @@ import com.ruoyi.common.config.RuoYiConfig; import com.ruoyi.common.exception.file.InvalidExtensionException; import com.ruoyi.common.utils.file.FileUploadUtils; import com.ruoyi.common.utils.file.MimeTypeUtils; +import com.zc.business.domain.DcEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.zc.business.mapper.DcEventProcessMapper; @@ -24,6 +25,8 @@ public class DcEventProcessServiceImpl implements IDcEventProcessService { @Autowired private DcEventProcessMapper dcEventProcessMapper; + @Autowired + private DcEventServiceImpl dcEventService; /** * 查询事件处理流程 @@ -69,6 +72,13 @@ public class DcEventProcessServiceImpl implements IDcEventProcessService e.printStackTrace(); } } + //判断是否最后一个节点 + if (dcEventProcess.getProcessType() != null && dcEventProcess.getProcessType() == 1){ + int finalNode = dcEventProcessMapper.selectFinalNode(dcEventProcess.getEventId()); + if (finalNode == dcEventProcess.getProcessId()){ + dcEventService.updateDcEventState(dcEventProcess.getEventId(),2); + } + } return dcEventProcessMapper.insertDcEventProcess(dcEventProcess); } diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcEventServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcEventServiceImpl.java index 3ec6c9eb..a1f75d48 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcEventServiceImpl.java +++ b/zc-business/src/main/java/com/zc/business/service/impl/DcEventServiceImpl.java @@ -1,9 +1,13 @@ package com.zc.business.service.impl; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.uuid.IdUtils; import com.ruoyi.system.service.ISysDeptService; import com.sun.xml.bind.v2.TODO; @@ -13,10 +17,14 @@ import com.zc.business.mapper.*; import com.zc.business.service.IDcEventProcessService; import com.zc.business.service.IDcEventService; import com.zc.business.service.IMiddleDatabaseService; +import com.zc.business.utils.diff.Diff; +import com.zc.business.utils.diff.model.Result; +import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.text.SimpleDateFormat; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -546,10 +554,24 @@ public class DcEventServiceImpl extends ServiceImpl impl */ @Override public int updateDcEvent(DcEvent dcEvent) { + DcEvent oldEvent = selectEventSubclassById(dcEvent.getEventType().intValue(),dcEvent.getId()); + dcEvent.setUpdateTime(DateUtils.getNowDate()); int i7 = dcEventMapper.updateDcEvent(dcEvent); if (i7 > 0) { + String context = comparisonInfo(oldEvent,dcEvent); + + //事件处置流程记录 + DcEventProcess dcEventProcess = new DcEventProcess(); + dcEventProcess.setEventId(dcEvent.getId()); + dcEventProcess.setOperationTime(new Date()); + dcEventProcess.setOperator(SecurityUtils.getUserId().toString()); + dcEventProcess.setSource(1); + dcEventProcess.setContext(context); + dcEventProcessService.insertDcEventProcess(dcEventProcess); + + //中间库 MdEvent mdEvent = new MdEvent(dcEvent); middleDatabaseService.updateMiddleDatabaseEvent(mdEvent); @@ -823,7 +845,29 @@ public class DcEventServiceImpl extends ServiceImpl impl @Override public int updateDcEventState(String id, int state) { - return dcEventMapper.updateDcEventState(id,state); + int i = dcEventMapper.updateDcEventState(id,state); + if (i > 0){ + DcEvent dcEvent = dcEventMapper.selectDcEventById(id); + + //事件处置流程记录 + DcEventProcess dcEventProcess = new DcEventProcess(); + dcEventProcess.setEventId(dcEvent.getId()); + dcEventProcess.setOperationTime(new Date()); + dcEventProcess.setOperator(SecurityUtils.getUserId().toString()); + dcEventProcess.setSource(1); + List processConfigList = dcProcessConfigMapper.selectDcProcessConfigByEventType(dcEvent.getEventType().intValue()); + if (processConfigList != null && processConfigList.size() > 0){ + DcProcessConfig dcProcessConfig = processConfigList.get(0); + dcEventProcess.setProcessType(1); + dcEventProcess.setProcessId(Long.valueOf(dcProcessConfig.getNodeNode())); + dcEventProcess.setProcessName(dcProcessConfig.getProcessNode()); + } + String sourceName = ValueConverter.eventSourceName(dcEvent.getEventSource().toString()); + String typeName = ValueConverter.eventTypeName(dcEvent.getEventType().toString()); + dcEventProcess.setContext("由" + sourceName + "上报了一起" + typeName + "事件"); + dcEventProcessService.insertDcEventProcess(dcEventProcess); + } + return i; } public static List castList(Object obj, Class clazz) { @@ -836,4 +880,462 @@ public class DcEventServiceImpl extends ServiceImpl impl } return null; } + + /** + * @Description 事件信息比对 + * + * @author liuwenge + * @date 2024/4/10 16:24 + * @param oldEvent 旧数据 + * @param newEvent 新数据 + * @return java.lang.String + */ + public String comparisonInfo(DcEvent oldEvent,DcEvent newEvent){ + String context = ""; + if (oldEvent == null || newEvent == null){ + return context; + } + Diff diff = new Diff(); + List diffList = diff.diff(JSON.toJSON(oldEvent).toString(),JSON.toJSON(newEvent).toString()); + for (Result result : diffList) { + if ("ADD".equals(result.getDiffType()) || "MODIFY".equals(result.getDiffType())){ + if ("startTime".equals(result.getRightPath()) + || "endTime".equals(result.getRightPath()) + || "estimatedEndTime".equals(result.getRightPath()) + || "occurrenceTime".equals(result.getRightPath())){ + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + Date date = new Date(); + date.setTime(Long.parseLong(result.getRight().toString())); + result.setRight(df.format(date));; + } catch (Exception e){ + e.printStackTrace(); + } + } else if ("direction".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("上行"); + } else if ("2".equals(result.getRight())){ + result.setRight("中"); + } else if ("3".equals(result.getRight())){ + result.setRight("下行"); + } + } else if ("eventState".equals(result.getRightPath())){ + if ("0".equals(result.getRight())){ + result.setRight("待确认"); + } else if ("1".equals(result.getRight())){ + result.setRight("处理中"); + } else if ("2".equals(result.getRight())){ + result.setRight("已完成"); + } + } else if ("eventSource".equals(result.getRightPath())){ + result.setRight(ValueConverter.eventSourceName(result.getRight().toString())); + } else if ("lang".equals(result.getRightPath())){ + String langStr = ""; + String langArr[] = result.getRight().toString().split(","); + for (String lang : langArr) { + if ("1".equals(lang)){ + langStr = langStr + "行1,"; + } else if ("2".equals(lang)){ + langStr = langStr + "行2,"; + } else if ("3".equals(lang)){ + langStr = langStr + "行3,"; + } else if ("4".equals(lang)){ + langStr = langStr + "行4,"; + } else if ("0".equals(lang)){ + langStr = langStr + "应急车道,"; + } + } + if (langStr.length() > 0){ + langStr = langStr.substring(0,langStr.length() -1); + } + result.setRight(langStr); + } else if ("dcEventAbnormalWeather.weatherSituation".equals(result.getRightPath())){ + if ("1-1".equals(result.getRight())){ + result.setRight("雨雾"); + } else if ("1-2".equals(result.getRight())){ + result.setRight("雨雪"); + } else if ("1-3".equals(result.getRight())){ + result.setRight("中雨"); + } else if ("1-4".equals(result.getRight())){ + result.setRight("小雨"); + } else if ("1-5".equals(result.getRight())){ + result.setRight("大雨"); + } else if ("1-6".equals(result.getRight())){ + result.setRight("暴雨"); + } else if ("2-1".equals(result.getRight())){ + result.setRight("小雪"); + } else if ("2-2".equals(result.getRight())){ + result.setRight("中雪"); + } else if ("2-3".equals(result.getRight())){ + result.setRight("大雪"); + } else if ("2-4".equals(result.getRight())){ + result.setRight("暴雪"); + } else if ("2-5".equals(result.getRight())){ + result.setRight("大暴雪"); + } else if ("2-6".equals(result.getRight())){ + result.setRight("特大暴雪"); + } else if ("3-1".equals(result.getRight())){ + result.setRight("轻雾"); + } else if ("3-2".equals(result.getRight())){ + result.setRight("大雾"); + } else if ("3-3".equals(result.getRight())){ + result.setRight("浓雾"); + } else if ("3-4".equals(result.getRight())){ + result.setRight("强浓雾"); + } else if ("3-5".equals(result.getRight())){ + result.setRight("团雾"); + } + } else if ("dcEventAbnormalWeather.emergencyLevel".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("一般"); + } else if ("2".equals(result.getRight())){ + result.setRight("紧急"); + } + } else if ("dcEventAccident.locationType".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("高速主线"); + } else if ("2".equals(result.getRight())){ + result.setRight("服务区"); + } else if ("3".equals(result.getRight())){ + result.setRight("立交桥"); + } else if ("4".equals(result.getRight())){ + result.setRight("收费站"); + } + } else if ("dcEventAccident.weatherCondition".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("晴"); + } else if ("2".equals(result.getRight())){ + result.setRight("雨"); + } else if ("3".equals(result.getRight())){ + result.setRight("雪"); + } else if ("4".equals(result.getRight())){ + result.setRight("雾"); + } else if ("5".equals(result.getRight())){ + result.setRight("其他"); + } + } else if ("dcEventAccident.impactLevel".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("无"); + } else if ("2".equals(result.getRight())){ + result.setRight("危化品泄漏"); + } else if ("3".equals(result.getRight())){ + result.setRight("整车自燃"); + } else if ("4".equals(result.getRight())){ + result.setRight("车辆复燃"); + } else if ("5".equals(result.getRight())){ + result.setRight("散装人工倒货"); + } + } else if ("dcEventAccident.isReverseCargo".equals(result.getRightPath()) + || "dcEventAccident.isMaintenance".equals(result.getRightPath()) + || "dcEventAccident.congestionAhead".equals(result.getRightPath()) + || "dcEventAccident.atIntersection".equals(result.getRightPath()) + || "dcEventAccident.onCurve".equals(result.getRightPath()) + || "dcEventAccident.isPrivate".equals(result.getRightPath()) + || "dcEventVehicleAccident.congestionAhead".equals(result.getRightPath()) + || "dcEventVehicleAccident.atIntersection".equals(result.getRightPath()) + || "dcEventVehicleAccident.onCurve".equals(result.getRightPath()) + || "dcEventVehicleAccident.isPrivate".equals(result.getRightPath())){ + if ("0".equals(result.getRight())){ + result.setRight("否"); + } else if ("1".equals(result.getRight())){ + result.setRight("是"); + } + } else if ("dcEventConstruction.controlMode".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("封闭"); + } else if ("2".equals(result.getRight())){ + result.setRight("不封闭"); + } + } else if ("dcEventConstruction.locationType".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("单点"); + } else if ("2".equals(result.getRight())){ + result.setRight("多点"); + } + } else if ("dcEventConstruction.specialConstruction".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("专项工程施工"); + } else if ("2".equals(result.getRight())){ + result.setRight("改扩建专项工程施工"); + } else if ("3".equals(result.getRight())){ + result.setRight("桥梁专项工程施工"); + } else if ("4".equals(result.getRight())){ + result.setRight("其他专项工程施工"); + } + } else if ("dcEventConstruction.constructionMeasurement".equals(result.getRightPath())){ + if ("0".equals(result.getRight())){ + result.setRight("无"); + } else if ("1".equals(result.getRight())){ + result.setRight("并道行驶"); + } else if ("2".equals(result.getRight())){ + result.setRight("临时保通"); + } else if ("3".equals(result.getRight())){ + result.setRight("借路侧服务区通行"); + } + } else if ("dcEventConstruction.exitsInlets".equals(result.getRightPath()) + || "dcEventServiceArea.exitsInlets".equals(result.getRightPath()) + || "dcEventTrafficControl.exitsInlets".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("出口"); + } else if ("2".equals(result.getRight())){ + result.setRight("入口"); + } + } else if ("dcEventConstruction.trafficCondition".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("通行正常"); + } else if ("2".equals(result.getRight())){ + result.setRight("通行受阻"); + } + } else if ("dcEventServiceArea.disableFacility".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("卫生间"); + } else if ("2".equals(result.getRight())){ + result.setRight("餐厅"); + } else if ("3".equals(result.getRight())){ + result.setRight("停车场"); + } else if ("4".equals(result.getRight())){ + result.setRight("加油站"); + } else if ("5".equals(result.getRight())){ + result.setRight("充电桩"); + } + } else if ("dcEventTrafficCongestion.congestionCause".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("车流量大"); + } else if ("2".equals(result.getRight())){ + result.setRight("交通事故"); + } else if ("3".equals(result.getRight())){ + result.setRight("恶劣天气"); + } else if ("4".equals(result.getRight())){ + result.setRight("施工"); + } else if ("5".equals(result.getRight())){ + result.setRight("其他"); + } + } else if ("dcEventTrafficCongestion.location".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("入口"); + } else if ("2".equals(result.getRight())){ + result.setRight("出口"); + } else if ("3".equals(result.getRight())){ + result.setRight("入口内广场"); + } else if ("4".equals(result.getRight())){ + result.setRight("出口内广场"); + } else if ("5".equals(result.getRight())){ + result.setRight("外广场"); + } else if ("6".equals(result.getRight())){ + result.setRight("入口车道"); + } else if ("7".equals(result.getRight())){ + result.setRight("出口车道"); + } else if ("8".equals(result.getRight())){ + result.setRight("入口匝道"); + } else if ("9".equals(result.getRight())){ + result.setRight("出口匝道"); + } + } else if ("dcEventTrafficCongestion.locationType".equals(result.getRightPath()) + || "dcEventVehicleAccident.locationType".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("高速主线"); + } else if ("2".equals(result.getRight())){ + result.setRight("服务区"); + } else if ("3".equals(result.getRight())){ + result.setRight("立交桥"); + } else if ("4".equals(result.getRight())){ + result.setRight("收费站"); + } + } else if ("dcEventTrafficCongestion.weatherCondition".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("晴"); + } else if ("2".equals(result.getRight())){ + result.setRight("雨"); + } else if ("3".equals(result.getRight())){ + result.setRight("雪"); + } else if ("4".equals(result.getRight())){ + result.setRight("雾"); + } else if ("5".equals(result.getRight())){ + result.setRight("其他"); + } + } else if ("dcEventTrafficControl.controlType".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("封闭"); + } else if ("2".equals(result.getRight())){ + result.setRight("限行"); + } + } else if ("dcEventTrafficControl.controlCause".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("车流量大"); + } else if ("2".equals(result.getRight())){ + result.setRight("交通事故"); + } else if ("3".equals(result.getRight())){ + result.setRight("恶劣天气"); + } else if ("4".equals(result.getRight())){ + result.setRight("施工"); + } else if ("5".equals(result.getRight())){ + result.setRight("警备任务"); + } else if ("6".equals(result.getRight())){ + result.setRight("其他"); + } + } else if ("dcEventTrafficControl.causeType".equals(result.getRightPath())){ + if ("3-1".equals(result.getRight())){ + result.setRight("雨"); + } else if ("3-2".equals(result.getRight())){ + result.setRight("雪"); + } else if ("3-3".equals(result.getRight())){ + result.setRight("雾"); + } else if ("3-4".equals(result.getRight())){ + result.setRight("道路积水"); + } else if ("3-5".equals(result.getRight())){ + result.setRight("道路湿滑"); + } else if ("3-6".equals(result.getRight())){ + result.setRight("道路结冰"); + } else if ("3-7".equals(result.getRight())){ + result.setRight("沙尘暴"); + } else if ("4-1".equals(result.getRight())){ + result.setRight("专项工程施工"); + } else if ("4-2".equals(result.getRight())){ + result.setRight("改扩建工程施工"); + } else if ("4-3".equals(result.getRight())){ + result.setRight("其他施工"); + } + } else if ("dcEventTrafficControl.measure".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("临时关闭"); + } else if ("2".equals(result.getRight())){ + result.setRight("限行车辆"); + } else if ("3".equals(result.getRight())){ + result.setRight("限行车道"); + } else if ("4".equals(result.getRight())){ + result.setRight("限行车道且限行车辆"); + } else if ("5".equals(result.getRight())){ + result.setRight("间隔放行"); + } else if ("6".equals(result.getRight())){ + result.setRight("并道行驶"); + } else if ("7".equals(result.getRight())){ + result.setRight("限速"); + } + } else if ("dcEventTrafficControl.classify".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("主线关闭"); + } else if ("2".equals(result.getRight())){ + result.setRight("主线限行"); + } else if ("3".equals(result.getRight())){ + result.setRight("主线间隔放行"); + } else if ("4".equals(result.getRight())){ + result.setRight("主线并道"); + } else if ("5".equals(result.getRight())){ + result.setRight("主线限速"); + } + } else if ("dcEventTrafficControl.limitedType".equals(result.getRightPath())){ + if ("1".equals(result.getRight())){ + result.setRight("只允许"); + } else if ("2".equals(result.getRight())){ + result.setRight("禁止"); + } + } + String label = ValueConverter.eventLabel(result.getRightPath()); + context = context + label + "修改为:" + result.getRight() + ","; + } + } + + if (context.length() > 0){ + context = context.substring(0,context.length() -1); + } + + + return context; + } + + + /** + * @Description 查询事件流程状态 + * + * @author liuwenge + * @date 2024/4/11 11:19 + * @param eventId 事件id + * @return com.ruoyi.common.core.domain.AjaxResult + */ + @Override + public AjaxResult getProcessNode(String eventId){ + List> processNode = dcEventMapper.getProcessNode(eventId); + return AjaxResult.success(processNode); + } + + /** + * @Description 解除事件 + * + * @author liuwenge + * @date 2024/4/11 14:13 + * @param eventId + * @return com.ruoyi.common.core.domain.AjaxResult + */ + @Override + public AjaxResult completeEvent(String eventId){ + boolean flag = dcEventMapper.completeEvent(eventId); + if (flag){ + DcEvent dcEvent = dcEventMapper.selectDcEventById(eventId); + + String datePoor = getDatePoor(dcEvent.getEndTime(),dcEvent.getStartTime()); + + //事件处置流程记录 + DcEventProcess dcEventProcess = new DcEventProcess(); + dcEventProcess.setEventId(eventId); + dcEventProcess.setOperationTime(new Date()); + dcEventProcess.setOperator(SecurityUtils.getUserId().toString()); + dcEventProcess.setSource(1); + + dcEventProcess.setContext("事件解除,处置完毕,处置时长:" + datePoor); + dcEventProcessService.insertDcEventProcess(dcEventProcess); + return AjaxResult.success("解除事件成功"); + } + return AjaxResult.error("解除事件失败"); + } + + /** + * @Description 无需清障 + * + * @author liuwenge + * @date 2024/4/11 14:13 + * @param eventId + * @return com.ruoyi.common.core.domain.AjaxResult + */ + @Override + public AjaxResult skipClear(String eventId){ + + //事件处置流程记录 + DcEventProcess dcEventProcess = new DcEventProcess(); + dcEventProcess.setEventId(eventId); + dcEventProcess.setOperationTime(new Date()); + dcEventProcess.setOperator(SecurityUtils.getUserId().toString()); + dcEventProcess.setSource(1); + dcEventProcess.setProcessType(1); + dcEventProcess.setProcessId(6L); + dcEventProcess.setProcessName("清障结束"); + + dcEventProcess.setContext("选择不需要清障"); + dcEventProcessService.insertDcEventProcess(dcEventProcess); + return AjaxResult.success("无需清障成功"); + } + + //时间比对 + public static String getDatePoor(Date endDate, Date nowDate) { + + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + long ns = 1000; + + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + long sec = diff % nd % nh % nm / ns; + + return day + "天" + hour + "小时" + min + "分钟" + sec + "秒"; + + } + } diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/AlgorithmEnum.java b/zc-business/src/main/java/com/zc/business/utils/diff/AlgorithmEnum.java new file mode 100644 index 00000000..f9391273 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/AlgorithmEnum.java @@ -0,0 +1,82 @@ +package com.zc.business.utils.diff; + +import com.zc.business.utils.diff.algorithm.AlgorithmModule; +import com.zc.business.utils.diff.algorithm.array.SimilarArrayComparator; +import com.zc.business.utils.diff.algorithm.array.SimpleArrayComparator; +import com.zc.business.utils.diff.algorithm.nulls.DefaultNullComparator; +import com.zc.business.utils.diff.algorithm.object.LeftJoinObjectComparator; +import com.zc.business.utils.diff.algorithm.object.SimpleObjectComparator; +import com.zc.business.utils.diff.algorithm.other.DefaultOtherComparator; +import com.zc.business.utils.diff.algorithm.primitive.DefaultPrimitiveComparator; + +/** + * 算法模型枚举类:提供一些默认实现的算法模型 + * @Author JingWei + * @create 2022/3/2 + */ +public enum AlgorithmEnum { + + /** + * 默认的比较算法模型 + */ + DEFAULT(defaultAlgorithmModule()), + + /** + * 数组比较采用Simple,对象比较采用Simple + */ + SIMPLE_ARRAY_AND_SIMPLE_OBJECT(simpleAndSimpleAlgorithmModule()), + + /** + * 数组比较采用Simple,对象比较采用LeftJoin + */ + SIMPLE_ARRAY_AND_LEFTJOIN_OBJECT(simpleAndLeftJoinAlgorithmModule()), + + /** + * 数组比较采用Similar,对象比较采用LeftJoin + */ + SIMLAR_ARRAY_AND_LEFTJOIN_OBJECT(similarAndLeftJoinAlgorithmModule()), + + /** + * 数组比较采用Similar,对象比较采用Simple + */ + MOST_COMMONLY_USED(similarAndSimpleAlgorithmModule()); + + final private AlgorithmModule algorithmModule; + + AlgorithmEnum(AlgorithmModule algorithmModule) { + this.algorithmModule = algorithmModule; + } + + + public AlgorithmModule getAlgorithmModule() { + return algorithmModule; + } + + private static AlgorithmModule defaultAlgorithmModule() { + return new AlgorithmModule(new SimpleObjectComparator(), new SimilarArrayComparator(), + new DefaultPrimitiveComparator(), new DefaultNullComparator(), new DefaultOtherComparator()); + + } + + private static AlgorithmModule simpleAndSimpleAlgorithmModule() { + return new AlgorithmModule(new SimpleObjectComparator(), new SimpleArrayComparator(), + new DefaultPrimitiveComparator(), new DefaultNullComparator(), new DefaultOtherComparator()); + } + + + private static AlgorithmModule simpleAndLeftJoinAlgorithmModule() { + return new AlgorithmModule(new LeftJoinObjectComparator(), new SimpleArrayComparator(), + new DefaultPrimitiveComparator(), new DefaultNullComparator(), new DefaultOtherComparator()); + } + + private static AlgorithmModule similarAndLeftJoinAlgorithmModule() { + return new AlgorithmModule(new LeftJoinObjectComparator(), new SimilarArrayComparator(), + new DefaultPrimitiveComparator(), new DefaultNullComparator(), new DefaultOtherComparator()); + } + + private static AlgorithmModule similarAndSimpleAlgorithmModule() { + return new AlgorithmModule(new SimpleObjectComparator(), new SimilarArrayComparator(), + new DefaultPrimitiveComparator(), new DefaultNullComparator(), new DefaultOtherComparator()); + } + +} \ No newline at end of file diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/Diff.java b/zc-business/src/main/java/com/zc/business/utils/diff/Diff.java new file mode 100644 index 00000000..638b8ee9 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/Diff.java @@ -0,0 +1,208 @@ +package com.zc.business.utils.diff; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.zc.business.utils.diff.algorithm.AlgorithmModule; +import com.zc.business.utils.diff.algorithm.array.ArrayComparator; +import com.zc.business.utils.diff.algorithm.array.SimilarArrayComparator; +import com.zc.business.utils.diff.algorithm.nulls.DefaultNullComparator; +import com.zc.business.utils.diff.algorithm.nulls.NullComparator; +import com.zc.business.utils.diff.algorithm.object.ObjectComparator; +import com.zc.business.utils.diff.algorithm.object.SimpleObjectComparator; +import com.zc.business.utils.diff.algorithm.other.DefaultOtherComparator; +import com.zc.business.utils.diff.algorithm.other.OtherComparator; +import com.zc.business.utils.diff.algorithm.primitive.DefaultPrimitiveComparator; +import com.zc.business.utils.diff.algorithm.primitive.PrimitiveComparator; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; +import com.zc.business.utils.diff.model.Result; +import com.zc.business.utils.diff.model.ResultConvertUtil; + +import java.util.List; + +/** + * DIFF入口,用于比较两个Json字符串 + * @Author JingWei + * @create 2022/2/25 + */ +public class Diff { + /** + * 算法模型。根据传入的算法模型使用该算法模型进行diff比较。 + */ + private AlgorithmEnum algorithmEnum; + /** + * 特殊路径集合。当前路径符合特殊路径且特殊路径下比较结果相同,会在返回结果中做额外标识标识。 + */ + private List specialPath; + /** + * 噪音字段集合。如果当前路径符合噪音字段路径,则不会比较。 + */ + private List noisePahList; + /** + * 以下为5种可以自定义的比较器,条件是algorithmEnum为空(如果algorithmEnum有值,直接使用algorithmEnum对应算法模型,比较器不生效) + */ + private ObjectComparator objectComparator; + private ArrayComparator arrayComparator; + private PrimitiveComparator primitiveComparator; + private NullComparator nullComparator; + private OtherComparator otherComparator; + + + /** + * @param a 要比较的第一个JsonElement + * @param b 要比较的第二个JsonElement + * @return 用来展示的diff结果 + */ + public List diffElement(JsonElement a, JsonElement b) { + DiffContext diffContext; + //用噪音路径和自定义路径构造路径模型 + PathModule pathModule = new PathModule(noisePahList, specialPath); + //如果有算法模型直接使用算法模型 + if(algorithmEnum != null){ + diffContext = algorithmEnum.getAlgorithmModule().diffElement(a, b, pathModule); + }//如果也没有比较器,直接用默认算法模型 + else if(objectComparator == null && arrayComparator == null && primitiveComparator == null && nullComparator == null && otherComparator == null){ + diffContext = AlgorithmEnum.DEFAULT.getAlgorithmModule().diffElement(a, b, pathModule); + }//如果有比较器,为空的比较器用默认替换,然后构造算法模型, + else { + constrcutDefaultComparator(); + diffContext = new AlgorithmModule(objectComparator, arrayComparator, primitiveComparator, nullComparator, otherComparator).diffElement(a, b, pathModule); + } + return ResultConvertUtil.constructResult(diffContext); + } + + /** + * @param strA 要比较的第一个字符串 + * @param strB 要比较的第二个字符串 + * @return 用来展示的diff结果 + */ + public List diff(String strA, String strB) { + return diffElement(new JsonParser().parse(strA), new JsonParser().parse(strB)); + } + + public Diff() { + } + + /** + * 没有初始的比较器替换成默认的 + */ + private void constrcutDefaultComparator() { + //如果没有初始化算法,则采用默认的算法。 + if(objectComparator == null){ + objectComparator = defaultObjectComparator(); + } + if(arrayComparator == null){ + arrayComparator = defaultArrayComparator(); + } + if(primitiveComparator == null){ + primitiveComparator = defaultPrimitiveComparator(); + } + if(nullComparator == null){ + nullComparator = defaultNullComparator(); + } + if(otherComparator == null){ + otherComparator = defaultOtherComparator(); + } + } + + private ObjectComparator defaultObjectComparator() { + return new SimpleObjectComparator(); + } + + private ArrayComparator defaultArrayComparator() { + return new SimilarArrayComparator(); + } + + private PrimitiveComparator defaultPrimitiveComparator() { + return new DefaultPrimitiveComparator(); + } + + private NullComparator defaultNullComparator() { + return new DefaultNullComparator(); + } + + private OtherComparator defaultOtherComparator() { + return new DefaultOtherComparator(); + } + + /** + * 选择算法模型 + * @param algorithmEnum 算法模型枚举类 + * @return 对象本身 + */ + public Diff withAlgorithmEnum(AlgorithmEnum algorithmEnum){ + this.algorithmEnum = algorithmEnum; + return this; + } + + /** + * 设置自定义路径 + * @param specialPath 自定义路径列表 + * @return 对象本身 + */ + public Diff withSpecialPath(List specialPath){ + this.specialPath = specialPath; + return this; + } + + /** + * 设置噪音路径 + * @param noisePahList 噪音路径列表 + * @return 对象本身 + */ + public Diff withNoisePahList(List noisePahList){ + this.noisePahList = noisePahList; + return this; + } + + /** + * 自定义对象比较器 + * @param objectComparator 对象比较器 + * @return 对象本身 + */ + public Diff withObjectComparator(ObjectComparator objectComparator) { + this.objectComparator = objectComparator; + return this; + } + + /** + * 自定义数组比较器 + * @param arrayComparator 数组比较器 + * @return 对象本身 + */ + public Diff withArrayComparator(ArrayComparator arrayComparator) { + this.arrayComparator = arrayComparator; + return this; + } + + + /** + * 自定义基本类型比较器 + * @param primitiveComparator 基本类型比较器 + * @return 对象本身 + */ + public Diff withPrimitiveAlgorithm(PrimitiveComparator primitiveComparator) { + this.primitiveComparator = primitiveComparator; + return this; + } + + /** + * 自定义空类型比较器 + * @param nullComparator 空类型比较器 + * @return 对象本身 + */ + public Diff withNullComparator(NullComparator nullComparator) { + this.nullComparator = nullComparator; + return this; + } + + /** + * 自定义其他类型比较器 + * @param otherComparator 其他类型比较器 + * @return 对象本身 + */ + public Diff withOtheComparator(OtherComparator otherComparator) { + this.otherComparator = otherComparator; + return this; + } +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/AbstractObjectAndArray.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/AbstractObjectAndArray.java new file mode 100644 index 00000000..64670d1c --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/AbstractObjectAndArray.java @@ -0,0 +1,49 @@ +package com.zc.business.utils.diff.algorithm; + +import com.google.gson.JsonElement; +import com.zc.business.utils.diff.model.Constants; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; +import com.zc.business.utils.diff.model.SingleNodeDifference; + +/** + * 数组和对象比较的公有方法抽象类 + * @Author JingWei + * @create 2022/3/1 + */ +public abstract class AbstractObjectAndArray { + protected AlgorithmModule algorithmModule; + + /** + * diff算法比较核心方法,比较2个JsonElement的入口。 + * @param a 比较的第一个JsonElement + * @param b 比较的第二个JsonElement + * @param pathModule 路径模型 + * @return 不同的比较结果 + */ + public DiffContext diffElement(JsonElement a, JsonElement b, PathModule pathModule) { + return algorithmModule.diffElement(a, b, pathModule); + } + + /** + * 构造算法模型,比如使用对象和数组类型算法比较时,内部会用到其他类型的算法。 + * @param algorithmModule 算法模型 + */ + public void constructAlgorithmModule(AlgorithmModule algorithmModule) { + this.algorithmModule = algorithmModule; + } + + /** + * 如果下层diff结果不同,会把下层diff结果加入到上层diff结果中去。 + * @param parentResult 上层结果 + * @param childResult 下层结果 + */ + public void parentContextAddChildContext(DiffContext parentResult, DiffContext childResult) { + if(childResult.isSame() == Constants.DIFFERENT) { + for (SingleNodeDifference singleNodeDifference : childResult.getDiffResultModels()) { + parentResult.getDiffResultModels().add(singleNodeDifference); + } + parentResult.setSame(false); + } + } +} \ No newline at end of file diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/AbstractPrimitiveAndOther.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/AbstractPrimitiveAndOther.java new file mode 100644 index 00000000..62a564be --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/AbstractPrimitiveAndOther.java @@ -0,0 +1,34 @@ +package com.zc.business.utils.diff.algorithm; + +import com.google.gson.JsonElement; + +/** + * 基本类型和其它类型比较的公有方法抽象类 + * @Author JingWei + * @create 2022/1/11 + */ +public abstract class AbstractPrimitiveAndOther { + /** + * 将比较的元素转换成String类型方便结果展示 + * @param element 元素 + * @return 元素转换成的字符串 + */ + protected static String jsonElement2Str(JsonElement element){ + //该对象不存在的情况 + if(element == null){ + return null; + } else if (element.isJsonObject()) { + return "{省略内部字段}"; + } else if (element.isJsonArray()) { + return "[省略内部元素]"; + } else if (element.isJsonPrimitive()) { + return element.getAsJsonPrimitive().getAsString(); + } else if (element.isJsonNull()) { + return "null"; + }else{ + throw new RuntimeException("异常"); + } + } + + +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/AlgorithmModule.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/AlgorithmModule.java new file mode 100644 index 00000000..440313fe --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/AlgorithmModule.java @@ -0,0 +1,66 @@ +package com.zc.business.utils.diff.algorithm; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.zc.business.utils.diff.algorithm.array.ArrayComparator; +import com.zc.business.utils.diff.algorithm.nulls.NullComparator; +import com.zc.business.utils.diff.algorithm.object.ObjectComparator; +import com.zc.business.utils.diff.algorithm.other.OtherComparator; +import com.zc.business.utils.diff.algorithm.primitive.PrimitiveComparator; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; + +/** + * 算法模型 + * 算法模型包含5种比较器,对象、数组、基本类型、空类型、其他类型比较器。 + * 当2个JsonElement的子类同时为对象、数组、基本类型、空类型,使用前4种比较器。当2个JsonElement的子类类型不相同时,使用其它类型比较器。 + * @Author JingWei + * @create 2022/1/13 + */ +public class AlgorithmModule{ + protected ObjectComparator objectAlgorithm; + protected ArrayComparator arrayAlgorithm; + protected PrimitiveComparator primitiveComparator; + protected NullComparator nullComparator; + protected OtherComparator otherComparator; + + public AlgorithmModule(ObjectComparator objectAlgorithm, ArrayComparator arrayAlgorithm, + PrimitiveComparator primitiveComparator, NullComparator nullComparator, OtherComparator otherComparator) { + this.arrayAlgorithm = arrayAlgorithm; + this.objectAlgorithm = objectAlgorithm; + this.primitiveComparator = primitiveComparator; + this.nullComparator = nullComparator; + this.otherComparator = otherComparator; + objectAlgorithm.constructAlgorithmModule(this); + arrayAlgorithm.constructAlgorithmModule(this); + } + + /** + * 判断要比较的两个JsonElement的类型,并根据类型调用对应的算法进行比较 + * @param a 要比较的第一个元素 + * @param b 要比较的第二个元素 + * @param pathModule 路径模型 + * @return 返回比较结果 + */ + public DiffContext diffElement(JsonElement a, JsonElement b, PathModule pathModule) { + if (a instanceof JsonObject && b instanceof JsonObject) { + return objectAlgorithm.diff( (JsonObject) a, (JsonObject) b, pathModule); + } else if (a instanceof JsonArray && b instanceof JsonArray) { + return arrayAlgorithm.diffArray((JsonArray) a, (JsonArray) b, pathModule); + } else if (a instanceof JsonPrimitive && b instanceof JsonPrimitive) { + return primitiveComparator.diff((JsonPrimitive) a, (JsonPrimitive) b, pathModule); + } else if (a instanceof JsonNull && b instanceof JsonNull) { + return nullComparator.diff((JsonNull) a, (JsonNull) b, pathModule); + } else { + return otherComparator.diff(a, b, pathModule); + } + } + + public ArrayComparator getArrayAlgorithm() { + return arrayAlgorithm; + } + +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/Comparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/Comparator.java new file mode 100644 index 00000000..530ea2e4 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/Comparator.java @@ -0,0 +1,9 @@ +package com.zc.business.utils.diff.algorithm; + +/** + * 比较器最顶层的接口,逻辑意义 + * @Author JingWei + * @create 2022/3/2 + */ +public interface Comparator { +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/AbstractArray.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/AbstractArray.java new file mode 100644 index 00000000..2aa87a37 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/AbstractArray.java @@ -0,0 +1,22 @@ +package com.zc.business.utils.diff.algorithm.array; + +import com.zc.business.utils.diff.algorithm.AbstractObjectAndArray; + +/** + * 数组比较的公有方法抽象类 + * @Author JingWei + * @create 2022/3/1 + */ +public abstract class AbstractArray extends AbstractObjectAndArray implements ArrayComparator { + /** + * 数组索引号加一个中括号表示数组路径 + * @param i 数组元素的索引号 + * @return 索引号增加中括号 + */ + protected String constructArrayPath(Integer i){ + if(i == null || i < 0 ){ + throw new RuntimeException("数组索引号入参为空或者为负。 入参:" + i); + } + return "[" + i + "]"; + } +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/ArrayComparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/ArrayComparator.java new file mode 100644 index 00000000..160dccb2 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/ArrayComparator.java @@ -0,0 +1,43 @@ +package com.zc.business.utils.diff.algorithm.array; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.zc.business.utils.diff.algorithm.AlgorithmModule; +import com.zc.business.utils.diff.algorithm.Comparator; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; + + +/** + * 数组类型比较器接口,用于2个JsonElement均为数组时对2个元素进行比较。 + * @Author JingWei + * @create 2022/2/23 + */ +public interface ArrayComparator extends Comparator { + /** + * 对两个JsonArray进行比较的方法 + * @param a 要比较的第一个JsonArray + * @param b 要比较的第二个JsonArray + * @param pathModule 路径模型 + * @return 返回不相等的结果 + */ + DiffContext diffArray(JsonArray a, JsonArray b, PathModule pathModule) ; + + /** + * 对象内部包含其他非数组类型,对这些类型比较需要使用JsonElement比较方法 + * @param a 要比较的第一个JsonElement + * @param b 要比较的第一个JsonElement + * @param pathModule 路径模型 + * @return 返回不相等的结果 + */ + DiffContext diffElement(JsonElement a, JsonElement b, PathModule pathModule); + + /** + * 构造算法模型,数组中元素比较需要使用到其他非数组算法 + * @param algorithmModule 算法模型:包含对象、数组、基本类型、空类型、其他类型算法 + */ + void constructAlgorithmModule(AlgorithmModule algorithmModule); + + + +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/SimilarArrayComparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/SimilarArrayComparator.java new file mode 100644 index 00000000..1406790b --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/SimilarArrayComparator.java @@ -0,0 +1,252 @@ +package com.zc.business.utils.diff.algorithm.array; + +import com.google.gson.JsonArray; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; +import com.zc.business.utils.diff.model.SingleNodeDifference; + +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +/** + * 相似度数组算法比较器:当数组进行比较找不到相等元素时,通过算法优先找最接近的元素进行匹配 + * + * 举例: + *"[{\"a\":7,\"b\":5,\"c\":6},{\"a\":6,\"b\":2,\"c\":3}]"和 + *"[{\"a\":1,\"b\":2,\"c\":3},{\"a\":4,\"b\":5,\"c\":6},{\"a\":7,\"b\":8,\"c\":9}]"比较 + * 数组一中第一个元素依次和数组二中元素比较,分别有3组不同,1组不同,2组不同 + * 数组一中第二个元素依次和数组二中元素比较,分别有1组不同,3组不同,3组不同 + * 一、得到相似矩阵如下 + * 3 1 2 + * 1 3 3 + * 二、遍历矩阵所有元素,找到值最小的元素,并且行和列的其他元素不能再被使用。行和列即为要比较的2个元素在各自数组中的索引号 + * 第一次找到1,第二次找到另外一个1. + * 三、遍历矩阵中未使用所有列,列索引号为多余的元素在数组中的位置。 + * 只有第三列可以使用 ,第二组中第3个元素为新增元素。 + * + * @Author JingWei + * @create 2022/2/24 + */ +public class SimilarArrayComparator extends AbstractArray { + /** + * USEABLE表示当前位置可以使用 + */ + private final boolean USEABLE = false; + + /** + * USED表示当前位置已经被使用过 + */ + private final boolean USED = true; + + + /** + * @param a 要比较的第一个数组 + * @param b 要比较的第二个数组 + * @param pathModule 路径模型 + * @return 返回不相等的结果 + */ + @Override + public DiffContext diffArray(JsonArray a, JsonArray b, PathModule pathModule) { + DiffContext diffContext; + //a数组长度小于等于b,直接使用diff数组比较算法 + if(a.size() <= b.size()){ + diffContext = diff(a, b, pathModule); + } + //当a数组长度大于b时,需要交换pathModule中a和b路径, 并交换数组a和b,再使用diff数组比较算法 + else { + exchangeLeftAndRightPath(pathModule); + diffContext = diff(b, a, pathModule); + exchangeLeftAndRightPath(pathModule); + exchangeResult(diffContext); + } + return diffContext; + } + + /** + * 交换返回结果中每一个路径和结果 + * @param diffContext 返回结果 + */ + private void exchangeResult(DiffContext diffContext) { + List singleNodeDifferences = diffContext.getDiffResultModels(); + for(SingleNodeDifference singleNodeDifference : singleNodeDifferences){ + exchangePathAndResult(singleNodeDifference); + } + } + + /** + * 交换返回结果中的路径和结果 + * @param singleNodeDifference 返回结果 + */ + private void exchangePathAndResult(SingleNodeDifference singleNodeDifference) { + String tempStringA = singleNodeDifference.getLeftPath(); + Object tempLeft = singleNodeDifference.getLeft(); + singleNodeDifference.setLeftPath(singleNodeDifference.getRightPath()); + singleNodeDifference.setRightPath(tempStringA); + singleNodeDifference.setLeft(singleNodeDifference.getRight()); + singleNodeDifference.setRight(tempLeft); + } + + + /** + * 将路径模型中a和b的路径交换 + * @param pathModule 路径模型 + */ + private void exchangeLeftAndRightPath(PathModule pathModule) { + LinkedList tempA = pathModule.getLeftPath(); + pathModule.setLeftPath(pathModule.getRightPath()); + pathModule.setRightPath(tempA); + } + + + /** + * 数组比较核心算法,支持数组a长度小于b长度时使用 + */ + DiffContext diff(JsonArray a, JsonArray b, PathModule pathModule ) { + int rowlength = a.size(); + int linelength = b.size(); + //a数组中m个元素,与b数组中n个元素比较,共比较m*n次,所有比较结果会得组成一个矩阵,矩阵中的数字用来存储比较结果。 + int [][] similarMatrix = new int[rowlength][linelength]; + //创建一个行和列数组 用来判断当前行和列是否被使用过 + boolean []row = new boolean[rowlength]; + boolean []line = new boolean[linelength]; + for (int i = 0; i < rowlength; i++) { + pathModule.addLeftPath(constructArrayPath(i)); + //a数组中m个元素,与b数组中n个元素比较,比较结果生成一个m*n的矩阵 + constructSimilarMatrix(a, b, i, pathModule, similarMatrix, row, line); + pathModule.removeLastLeftPath(); + } + return obtainDiffResult(a, b, pathModule, row, line, similarMatrix); + } + + + /** + * 通过相似度矩阵,获取比较结果。 第一步在矩阵找到最小的数字,即找到最接近的几对结果。 第二步找剩余未使用的列数,即多余的元素 + */ + private DiffContext obtainDiffResult(JsonArray a, JsonArray b, PathModule pathModule, boolean[] row, boolean[] line, int[][] similarMatrix) { + DiffContext arrayDiffContext = new DiffContext(); + //找到a和b都有的结果,并且是最接近的几对结果 + obtainModifyDiffResult(a,b,pathModule,row,line,similarMatrix, arrayDiffContext); + //a没有b有的结果,即新增的结果 + obtainAddDiffResult(b,pathModule,line, arrayDiffContext); + + return arrayDiffContext; + } + + + /** + * 得到新增的结果。由于b数组长度大于a,会有几个元素多余。在选出a和b最接近的几对元素后,剩下的几个元素被认为是新增的。 + */ + private void obtainAddDiffResult(JsonArray b, PathModule pathModule, boolean[] line, DiffContext arrayDiffContext) { + for (int j = 0; j < line.length; j++) { + if (line[j] == USED) { + continue; + } + DiffContext addOrDeleteDiffContext = constructAddContext(b, j, pathModule); + parentContextAddChildContext(arrayDiffContext, addOrDeleteDiffContext); + } + } + + /** + * 从m*n的矩阵中选出最接近的几组结果,矩阵中的数字越小,说明2个元素比较时结果相差越少 + */ + private void obtainModifyDiffResult(JsonArray a, JsonArray b, PathModule pathModule, boolean[] row, boolean[] line, int[][] similarMatrix, DiffContext arrayDiffContext) { + int counts = 0; + //找到还未使用行的数量(行数小于列数) + for (boolean value : row) { + if (Objects.equals(USEABLE, value)) { + counts++; + } + } + //不同结果对数等于未使用的行数 + for (int n = 0; n < counts; n++) { + int bestLineIndex = 0; + int bestRowIndex = 0; + int minDiffPair = Integer.MAX_VALUE; + //遍历矩阵中所有元素,找最小的数字,矩阵中的数字越小,说明2个元素比较时结果相差越少 + for (int i = 0; i < row.length; i++) { + for (int j = 0; j < line.length; j++) { + //如果行或列被使用,跳过该行 + if (row[i] == USED || line[j] == USED) { + continue; + } + //如果当前元素数字更小,那么把当前行和列保存下来,数字更新为当前最优结果 + if (similarMatrix[i][j] < minDiffPair) { + bestRowIndex = i; + bestLineIndex = j; + minDiffPair = similarMatrix[i][j]; + } + } + } + //将找到的最优结果添加到返回结果中 + DiffContext modifyDiffContext = constructModifyContext(a, b, bestRowIndex, bestLineIndex, pathModule); + row[bestRowIndex] = USED; + line[bestLineIndex] = USED; + parentContextAddChildContext(arrayDiffContext, modifyDiffContext); + } + } + + /** + * 此时a数组没有该元素,b数组有元素,结果为新增 + * @param b 数组 + * @param index 索引号 + * @param pathModule 路径模型 + * @return 生成的不同结果 + */ + private DiffContext constructAddContext(JsonArray b, int index, PathModule pathModule) { + pathModule.addAllpath(constructArrayPath(index)); + DiffContext diffContext = diffElement(null, b.get(index), pathModule); + pathModule.removeAllLastPath(); + return diffContext; + } + + /** + * 返回两个数组对应索引号元素比较结果 + * @param a 数组a + * @param b 数组b + * @param i 数组a中元素的索引号 + * @param bestLineIndex 数组b中元素的索引号,即找到的和a中元素接接近的元素索引号。 + * @param pathModule 路径模型 + * @return 返回不同的结果 + */ + private DiffContext constructModifyContext(JsonArray a, JsonArray b, int i, int bestLineIndex, PathModule pathModule) { + pathModule.addLeftPath(constructArrayPath(i)); + pathModule.addRightPath(constructArrayPath(bestLineIndex)); + DiffContext diffContext = diffElement(a.get(i), b.get(bestLineIndex), pathModule); + pathModule.removeAllLastPath(); + return diffContext; + } + + /** + * 用数组a的一个元素与数组b中所有元素分别比较,会得到n次比较结果,结果为不相等的结果对数,在矩阵中更新n次结果。 + */ + private void constructSimilarMatrix(JsonArray arrayA, JsonArray arrayB, int rowIndex, PathModule pathModule, int [][]similarArray, boolean[] row, boolean[] line) { + if(rowIndex < 0 || rowIndex >= arrayB.size()){ + throw new RuntimeException("索引号入参超出数组长度。 索引号:" + rowIndex +" 数组B:" + arrayB); + } + + for (int j = 0; j < arrayB.size(); j++) { + if (line[j] == USEABLE) { + pathModule.addRightPath(constructArrayPath(j)); + DiffContext diffContext = diffElement(arrayA.get(rowIndex), arrayB.get(j), pathModule); + pathModule.removeLastRightPath(); + if (diffContext.isSame()) { + row[rowIndex] = USED; + line[j] = USED; + return; + } else if(existSpecialPath(diffContext.getSpecialPathResult())){ + similarArray[rowIndex][j] = 0 ; + } else { + similarArray[rowIndex][j] = diffContext.getDiffResultModels().size(); + } + } + } + } + + /** + * 判断比较结果是否有特殊路径 + */ + private boolean existSpecialPath(LinkedList specialPathResult) { + return specialPathResult != null && !specialPathResult.isEmpty(); + } +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/SimpleArrayComparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/SimpleArrayComparator.java new file mode 100644 index 00000000..36a3d791 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/array/SimpleArrayComparator.java @@ -0,0 +1,62 @@ +package com.zc.business.utils.diff.algorithm.array; + +import com.google.gson.JsonArray; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; + +/** + * 简单数组比较器:数组对比时按照索引号顺序依次进行比较 + * 例:[A,B,C] 与 [C,B,A]比较时,依次找索引号为0、为1、为2进行比较,相同结果对为 ,不同结果 会加入到返回结果中。 + * @Author JingWei + * @create 2022/1/14 + */ +public class SimpleArrayComparator extends AbstractArray { + /** + * 对两个JsonArray进行比较的方法。 + * @param a 要比较的第一个数组 + * @param b 要比较的第二个数组 + * @param pathModule 路径模型 + * @return 返回不相等的比较结果 + */ + @Override + public DiffContext diffArray(JsonArray a, JsonArray b, PathModule pathModule) { + DiffContext arrayDiffContext = new DiffContext(); + int maxLength = Math.max(a.size(), b.size()); + //根据数组a和b长度的大的值进行遍历 + for (int i = 0; i < maxLength; i++) { + pathModule.addAllpath(constructArrayPath(i)); + DiffContext diffContext = generateDiffResult(a,b,i,pathModule); + parentContextAddChildContext(arrayDiffContext, diffContext); + } + return arrayDiffContext; + } + + /** + * 生成比较结果,分以下3种情况考虑 + * i < a.size() && i < b.size() + * a.size() <= i + * b.size() <= i + * @param a 数组a + * @param b 数组b + * @param i 数组a和b中正在比较的元素索引号 + * @param pathModule 路径模型 + * @return 返回不相等的比较结果 + */ + private DiffContext generateDiffResult(JsonArray a, JsonArray b, int i, PathModule pathModule) { + if(i >= a.size() && i >= b.size()){ + throw new RuntimeException("数组索引号入参超过数组长度。 索引号:" + i + " 数组a:" + a + "数组b:" + b); + } + DiffContext diffContext; + if(i < a.size() && i < b.size()){ + diffContext = diffElement(a.get(i), b.get(i), pathModule); + }else if (i >= a.size()){ + diffContext = diffElement(null, b.get(i), pathModule); + }else{ + diffContext = diffElement(a.get(i), null, pathModule); + } + return diffContext; + } + + +} + diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/nulls/DefaultNullComparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/nulls/DefaultNullComparator.java new file mode 100644 index 00000000..06bab025 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/nulls/DefaultNullComparator.java @@ -0,0 +1,27 @@ +package com.zc.business.utils.diff.algorithm.nulls; + +import com.google.gson.JsonNull; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; + +/** + * 当要比较的两个JsonElement都为空类型时,默认实现的算法比较器 + * @Author JingWei + * @create 2022/1/10 + */ +public class DefaultNullComparator implements NullComparator { + + /** + * 当要比较的两个JsonElement是空类型时,默认比较方法 + * @param a 第一个空类型 + * @param b 第二个空类型 + * @param pathModule 路径模型 + * @return 返回结果 + */ + @Override + public DiffContext diff(JsonNull a, JsonNull b, PathModule pathModule){ + return new DiffContext(); + } + + +} \ No newline at end of file diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/nulls/NullComparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/nulls/NullComparator.java new file mode 100644 index 00000000..4d90e34d --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/nulls/NullComparator.java @@ -0,0 +1,25 @@ +package com.zc.business.utils.diff.algorithm.nulls; + +import com.google.gson.JsonNull; +import com.zc.business.utils.diff.algorithm.Comparator; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; + +/** + * 空类型比较器接口,用于2个JsonElement均为JsonNull时对2个元素进行比较。 + * @Author JingWei + * @create 2022/2/23 + */ +public interface NullComparator extends Comparator { + + /** + * 对两个JsonNull进行比较时,需要实现此方法。 + * @param a 要比较的第一个JsonNull + * @param b 要比较的第二个JsonNull + * @param pathModule 路径模型 + * @return 返回不同的比较结果 + */ + DiffContext diff(JsonNull a, JsonNull b, PathModule pathModule); + + +} \ No newline at end of file diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/AbstractObject.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/AbstractObject.java new file mode 100644 index 00000000..b8305829 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/AbstractObject.java @@ -0,0 +1,128 @@ +package com.zc.business.utils.diff.algorithm.object; + +import com.google.common.base.Joiner; +import com.google.gson.JsonObject; +import com.zc.business.utils.diff.algorithm.AbstractObjectAndArray; +import com.zc.business.utils.diff.model.Constants; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; + +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 对象比较的一些公有方法抽象类 + * @Author JingWei + * @create 2022/2/24 + */ +public abstract class AbstractObject extends AbstractObjectAndArray implements ObjectComparator { + + /** + * 对两个对象进行比较:遍历keySet中的所有key, 在a和b对象中找对应的value值进行比较 + * @param a 要比较的第一个对象 + * @param b 要比较的第二个对象 + * @param keySet key的集合 + * @param pathModule 路径模型 + * @return 不同的比较结果 + */ + protected DiffContext diffValueByKey(JsonObject a, JsonObject b, Set keySet, PathModule pathModule) { + DiffContext objectDiffContext = new DiffContext(); + LinkedList specialPathResult = new LinkedList<>(); + for (String key : keySet) { + //更新a和b当前路径 + pathModule.addAllpath(key); + //如果对象当前路径在噪音路径集合中,直接跳过比较 + if (!needDiff(pathModule.getNoisePahList(), pathModule.getLeftPath())) { + pathModule.removeAllLastPath(); + continue; + } + //生成比较结果 + DiffContext diffContext = diffElement(a.get(key), b.get(key), pathModule); + parentContextAddChildContext(objectDiffContext, diffContext); + //特殊路径处理 + specialPathHandle(diffContext.isSame(), specialPathResult, pathModule); + pathModule.removeAllLastPath(); + + } + objectDiffContext.setSpecialPathResult(specialPathResult); + return objectDiffContext; + } + + /** + * 如果比较的路径为特殊路径并且比较相等,特殊路径会被标识,添加到返回结果中。 + * @param isSame 比较结果是否相等 + * @param specialPathResult 返回的特殊路径结果 + * @param pathModule 路径模型 + */ + private void specialPathHandle(boolean isSame,LinkedList specialPathResult, PathModule pathModule) { + //如果比较结果不等,直接返回。 + if (!isSame){ + return; + } + //如果存在特殊路径,将特殊路径加入到集合中 + String specialPath = getSpecialPath(pathModule); + if( existPath(specialPath)){ + specialPathResult.add(specialPath); + } + } + + /** + * 校验路径是否存在,即路径是否为空 + * @param specialPath 特殊路径 + * @return 特殊路径是否为空 + */ + private boolean existPath(String specialPath) { + return specialPath != null; + } + + /** + * 判断当前路径是否在特殊路径集合中,如果在,则返回特殊路径。 + * @param pathModule 路径模型 + * @return 返回的特殊路径字符串 + */ + protected String getSpecialPath(PathModule pathModule) { + if(pathModule == null || pathModule.getSpecialPath() == null || pathModule.getSpecialPath().isEmpty()){ + return null; + } + String currentPath = listJoin(pathModule.getLeftPath()); + if(pathModule.getSpecialPath().contains(currentPath)){ + return currentPath; + } + return null; + } + + /** + * 判断当前字段是否需要diff,如果在噪音字段集合中,则不需要diff,返回false。 + * @param noisePahList 噪音路径列表 + * @param pathList 当前路径 + * @return 当前路径是否在噪音路径中 + */ + protected boolean needDiff(List noisePahList, LinkedList pathList) { + if(noisePahList == null || pathList == null || noisePahList.isEmpty() || pathList.isEmpty()){ + return true; + } + String path = listJoin(pathList); + if(noisePahList.contains(path)){ + return false; + } + return true; + } + + /** + * 将path路径列表改为字符串 + * @param path 当前路径 + * @return 当前路径字符串 + */ + protected String listJoin(LinkedList path) { + if(path == null){ + throw new RuntimeException("当前路径不能为空"); + } + List collect = path.stream().filter(e -> e.charAt(0) != '[').collect(Collectors.toList()); + return Joiner.on(Constants.MERGE_PATH).join(collect); + } + + + +} \ No newline at end of file diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/LeftJoinObjectComparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/LeftJoinObjectComparator.java new file mode 100644 index 00000000..e87b3e07 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/LeftJoinObjectComparator.java @@ -0,0 +1,30 @@ +package com.zc.business.utils.diff.algorithm.object; + +import com.google.gson.JsonObject; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; + +/** + * 左匹配对象比较器:当对两个JsonObject进行比较时,只对第一个对象中keySet中存在keyValue值进行比较。 + * + * 举例: + * {"a":1,"b":2,"c":3}和{"a":1,"b":4,"c":3,"d":4}进行比较 + * 只会比较第一个对象中有的字段,即比较第一个对象中有的"a","b","c"3个字段,第二个对象中的"d"字段没有被比较 + * + * @Author JingWei + * @create 2022/2/18 + */ +public class LeftJoinObjectComparator extends AbstractObject { + /** + * @param a 要比较的第一个JsonObject + * @param b 要比较的第二个JsonObject + * @param pathModule 路径模型 + * @return 不同的比较结果 + */ + @Override + public DiffContext diff(JsonObject a, JsonObject b, PathModule pathModule){ + //用a的keySet作为遍历集合。 + return diffValueByKey(a, b, a.keySet(), pathModule); + } + +} \ No newline at end of file diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/ObjectComparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/ObjectComparator.java new file mode 100644 index 00000000..86571d1e --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/ObjectComparator.java @@ -0,0 +1,40 @@ +package com.zc.business.utils.diff.algorithm.object; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.zc.business.utils.diff.algorithm.AlgorithmModule; +import com.zc.business.utils.diff.algorithm.Comparator; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; + +/** + * 对象类型比较器接口,用于2个JsonElement均为JsonObject时对2个元素进行比较。 + * @Author JingWei + * @create 2022/2/23 + */ +public interface ObjectComparator extends Comparator { + + /** + * 对两个JsonObject进行比较时,需要实现此方法。 + * @param a 要比较的第一个JsonObject + * @param b 要比较的第二个JsonObject + * @param pathModule 路径模型 + * @return 返回不同的比较结果 + */ + DiffContext diff(JsonObject a, JsonObject b, PathModule pathModule); + + /** + * 对象内部包含其他非JsonObject类型,对这些类型比较需要使用JsonElement比较方法 + * @param a 元素a + * @param b 元素b + * @param pathModule 路径模型 + * @return 返回不同的比较结果 + */ + DiffContext diffElement(JsonElement a, JsonElement b, PathModule pathModule); + + /** + * 构造算法模型,对象比较时,内部元素比较需要使用到其他非数组算法 + * @param algorithmModule 算法模型:包含对象、数组、基本类型、空类型、其他类型算法 + */ + void constructAlgorithmModule(AlgorithmModule algorithmModule); +} \ No newline at end of file diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/SimpleObjectComparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/SimpleObjectComparator.java new file mode 100644 index 00000000..c61551f3 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/object/SimpleObjectComparator.java @@ -0,0 +1,33 @@ +package com.zc.business.utils.diff.algorithm.object; + +import com.google.common.collect.Sets; +import com.google.gson.JsonObject; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; + +import java.util.Set; + +/** + * 简单对象比较器:当对两个JsonObject进行比较时,两个对象所有keySet中key对应的Value都会被比较。 + * 举例: + * {"b":2,"c":3}和{"a":1,"d":4}进行比较 + * 会比较两个对象中所有的字段,即比较两个对象中并集"a","b","c","d"4个字段 + * @Author JingWei + * @create 2022/2/16 + */ +public class SimpleObjectComparator extends AbstractObject { + /** + * @param a 要比较的第一个JsonObject + * @param b 要比较的第二个JsonObject + * @param pathModule 路径模型 + * @return 不同的比较结果 + */ + @Override + public DiffContext diff(JsonObject a, JsonObject b, PathModule pathModule) { + Set unionSet = Sets.union(a.keySet(), b.keySet()); + //用a和b的keySet的并集作为遍历集合。 + return diffValueByKey(a, b, unionSet, pathModule); + } + + +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/other/DefaultOtherComparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/other/DefaultOtherComparator.java new file mode 100644 index 00000000..dc43f4d3 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/other/DefaultOtherComparator.java @@ -0,0 +1,38 @@ +package com.zc.business.utils.diff.algorithm.other; + +import com.google.common.base.Joiner; +import com.google.gson.JsonElement; +import com.zc.business.utils.diff.algorithm.AbstractPrimitiveAndOther; +import com.zc.business.utils.diff.model.Constants; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; +import com.zc.business.utils.diff.model.SingleNodeDifference; + +import java.util.ArrayList; +import java.util.List; + +/** + * 当要比较的两个JsonElement的不同时为对象、组数、空、基本类型时,默认实现的比较器。 + * @Author JingWei + * @create 2022/1/10 + */ +public class DefaultOtherComparator extends AbstractPrimitiveAndOther implements OtherComparator { + + /** + * @param a 要比较的第一个JsonElement + * @param b 要比较的第二个JsonElement + * @param pathModule 路径模型 + * @return 不同的比较结果 + */ + @Override + public DiffContext diff(JsonElement a, JsonElement b, PathModule pathModule){ + //比较结果一定会不同,因为要比较的a和b类型不同才会调用该方法。 + DiffContext otherDiffContext = new DiffContext(Constants.DIFFERENT); + List singleNodeDifferences = new ArrayList<>(); + singleNodeDifferences.add(new SingleNodeDifference(Joiner.on(Constants.MERGE_PATH).join(pathModule.getLeftPath()), Joiner.on(Constants.MERGE_PATH).join(pathModule.getRightPath()), jsonElement2Str(a), jsonElement2Str(b))); + otherDiffContext.setDiffResultModels(singleNodeDifferences); + return otherDiffContext; + } + + +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/other/OtherComparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/other/OtherComparator.java new file mode 100644 index 00000000..ffe7e628 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/other/OtherComparator.java @@ -0,0 +1,24 @@ +package com.zc.business.utils.diff.algorithm.other; + +import com.google.gson.JsonElement; +import com.zc.business.utils.diff.algorithm.Comparator; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; + +/** + * 其他类型比较器接口,用于2个JsonElement不同时为对象、数组、空、基本类型时对2个元素进行比较。 + * + * 举例:第一个要比较类型为对象类型,第二个要比较类型为数组类型。 + * @Author JingWei + * @create 2022/2/23 + */ +public interface OtherComparator extends Comparator { + /** + * 对两个JsonElement进行比较并且两个JsonElement的类型不相等时,需要实现此方法。 + * @param a 要比较的第一个JsonElement + * @param b 要比较的第二个JsonElement + * @return 不同的比较结果 + */ + DiffContext diff(JsonElement a, JsonElement b, PathModule pathModule); + +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/primitive/DefaultPrimitiveComparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/primitive/DefaultPrimitiveComparator.java new file mode 100644 index 00000000..b6c2e8ee --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/primitive/DefaultPrimitiveComparator.java @@ -0,0 +1,39 @@ +package com.zc.business.utils.diff.algorithm.primitive; + +import com.google.common.base.Joiner; +import com.google.gson.JsonPrimitive; +import com.zc.business.utils.diff.algorithm.AbstractPrimitiveAndOther; +import com.zc.business.utils.diff.model.Constants; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; +import com.zc.business.utils.diff.model.SingleNodeDifference; + +import java.util.ArrayList; +import java.util.List; + +/** + * 当要比较的两个JsonElement是基本类型时,默认实现的比较器。 + * @Author JingWei + * @create 2022/1/10 + */ +public class DefaultPrimitiveComparator extends AbstractPrimitiveAndOther implements PrimitiveComparator { + /** + * @param a 要比较的第一个JsonPrimitive + * @param b 要比较的第二个JsonPrimitive + * @param pathModule 路径模型 + * @return 不同的比较结果 + */ + @Override + public DiffContext diff(JsonPrimitive a, JsonPrimitive b, PathModule pathModule){ + DiffContext primitiveDiffContext = new DiffContext(); + //如果a和b不相等,返回a和b的比较结果。 + if(Constants.DIFFERENT == a.equals(b)) { + List singleNodeDifferences = new ArrayList<>(); + singleNodeDifferences.add(new SingleNodeDifference(Joiner.on(Constants.MERGE_PATH).join(pathModule.getLeftPath()), Joiner.on(Constants.MERGE_PATH).join(pathModule.getRightPath()), jsonElement2Str(a), jsonElement2Str(b))); + primitiveDiffContext.setDiffResultModels(singleNodeDifferences); + primitiveDiffContext.setSame(Constants.DIFFERENT); + } + return primitiveDiffContext; + } + +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/primitive/PrimitiveComparator.java b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/primitive/PrimitiveComparator.java new file mode 100644 index 00000000..cfcdc0be --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/algorithm/primitive/PrimitiveComparator.java @@ -0,0 +1,23 @@ +package com.zc.business.utils.diff.algorithm.primitive; + +import com.google.gson.JsonPrimitive; +import com.zc.business.utils.diff.algorithm.Comparator; +import com.zc.business.utils.diff.model.DiffContext; +import com.zc.business.utils.diff.model.PathModule; + +/** + * 基本类型比较器接口,用于2个JsonElement均为基本类型的时对2个元素进行比较。 + * @Author JingWei + * @create 2022/2/23 + */ +public interface PrimitiveComparator extends Comparator { + /** + * 对两个基本类型进行比较时,需要实现此方法。 + * @param a 要比较的第一个JsonPrimitive + * @param b 要比较的第二个JsonPrimitive + * @param pathModule 路径模型 + * @return 不同的比较结果 + */ + DiffContext diff(JsonPrimitive a, JsonPrimitive b, PathModule pathModule); + +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/model/Constants.java b/zc-business/src/main/java/com/zc/business/utils/diff/model/Constants.java new file mode 100644 index 00000000..c5380e3b --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/model/Constants.java @@ -0,0 +1,20 @@ +package com.zc.business.utils.diff.model; + + +/** + * 常量 + * @Author JingWei + * @create 2022/3/2 + */ +public class Constants { + /** + * 描述比较结果不同 + */ + public static final boolean DIFFERENT = false; + /** + * 描述比较结果相同 + */ + public static final boolean SAME = true; + public static final String SPLIT_PATH = "\\."; + public static final String MERGE_PATH = "."; +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/model/DiffContext.java b/zc-business/src/main/java/com/zc/business/utils/diff/model/DiffContext.java new file mode 100644 index 00000000..9a0977ed --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/model/DiffContext.java @@ -0,0 +1,61 @@ +package com.zc.business.utils.diff.model; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * diff比较上下文 + * @Author JingWei + * @create 2022/3/2 + */ +public class DiffContext { + /** + * 比较结果是否相同 + */ + private boolean isSame; + + /** + * 比较结果不同时,存储所有不同的结果对 + */ + private List singleNodeDifferences; + + /** + * 比较结果中,出现了特殊路径下值相等的情况,会存储该特殊路径。 + */ + private LinkedList specialPathResult; + + public DiffContext(boolean isSame) { + this.isSame = isSame; + this.singleNodeDifferences = new ArrayList<>(); + } + + public boolean isSame() { + return isSame; + } + + public void setSame(boolean same) { + isSame = same; + } + + public List getDiffResultModels() { + return singleNodeDifferences; + } + + public void setDiffResultModels(List singleNodeDifferences) { + this.singleNodeDifferences = singleNodeDifferences; + } + + public DiffContext() { + this.isSame = true; + this.singleNodeDifferences = new ArrayList<>(); + } + + public LinkedList getSpecialPathResult() { + return specialPathResult; + } + + public void setSpecialPathResult(LinkedList specialPathResult) { + this.specialPathResult = specialPathResult; + } +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/model/PathModule.java b/zc-business/src/main/java/com/zc/business/utils/diff/model/PathModule.java new file mode 100644 index 00000000..1a95e509 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/model/PathModule.java @@ -0,0 +1,122 @@ +package com.zc.business.utils.diff.model; + +import java.util.LinkedList; +import java.util.List; + +/** + * 路径模型 + * A和B比较时,实时更新当前正在进行比较的元素路径。 + * + * @Author JingWei + * @create 2022/2/15 + */ +public class PathModule { + /** + * 对象A当前遍历到的路径 + */ + private LinkedList leftPath; + /** + * 对象B当前遍历到的路径 + */ + private LinkedList rightPath; + /** + * 特殊路径集合。当前路径符合特殊路径且特殊路径下比较结果相同,会在返回结果中做额外标识标识。 + */ + private List specialPath; + /** + * 噪音字段集合。如果当前路径符合噪音字段路径,则不会比较。 + */ + private List noisePahList; + + public PathModule() { + this.leftPath = new LinkedList<>(); + this.rightPath = new LinkedList<>(); + } + + public PathModule(List noisePahList) { + this.leftPath = new LinkedList<>(); + this.rightPath = new LinkedList<>(); + this.noisePahList = noisePahList; + } + + public PathModule(List noisePahList, List specialPath) { + this.leftPath = new LinkedList<>(); + this.rightPath = new LinkedList<>(); + this.noisePahList = noisePahList; + this.specialPath = specialPath; + } + + public List getNoisePahList() { + return noisePahList; + } + + public void setNoisePahList(List noisePahList) { + this.noisePahList = noisePahList; + } + + public List getSpecialPath() { + return specialPath; + } + + public void setSpecialPath(LinkedList specialPath) { + this.specialPath = specialPath; + } + + public LinkedList getLeftPath() { + return leftPath; + } + + public void setLeftPath(LinkedList leftPath) { + this.leftPath = leftPath; + } + + public LinkedList getRightPath() { + return rightPath; + } + + public void setRightPath(LinkedList rightPath) { + this.rightPath = rightPath; + } + + /** + * 同时在A和B路径列表最后加上一个Path路径 + */ + public void addAllpath(String lastPath) { + leftPath.add(lastPath); + rightPath.add(lastPath); + } + + public void addLeftPath(String lastPath) { + leftPath.add(lastPath); + } + + public void addRightPath(String lastPath) { + rightPath.add(lastPath); + } + + + /** + * 同时移除A和B路径列表中最后的一个路径 + */ + public void removeAllLastPath() { + leftPath.removeLast(); + rightPath.removeLast(); + } + + /** + * 移除A路径列表中最后的一个路径 + */ + public void removeLastLeftPath() { + leftPath.removeLast(); + } + + /** + * 移除B路径列表中最后的一个路径 + */ + public void removeLastRightPath() { + rightPath.removeLast(); + } + + +} + diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/model/Result.java b/zc-business/src/main/java/com/zc/business/utils/diff/model/Result.java new file mode 100644 index 00000000..0e55470a --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/model/Result.java @@ -0,0 +1,71 @@ +package com.zc.business.utils.diff.model; + +import java.io.Serializable; + +/** + * diff比较最终结果 + * @Author JingWei + * @create 2022/3/2 + */ +public class Result implements Serializable { + /** + * 第一个对象路径 + */ + private String leftPath; + /** + * 第二个对象路径 + */ + private String rightPath; + /** + * 第一个对象值 + */ + private Object left; + /** + * 第二个对象值 + */ + private Object right; + /** + * 比较结果 + */ + private String diffType; + + public String getLeftPath() { + return leftPath; + } + + public String getRightPath() { + return rightPath; + } + + public void setLeftPath(String leftPath) { + this.leftPath = leftPath; + } + + public void setRightPath(String rightPath) { + this.rightPath = rightPath; + } + + public Object getLeft() { + return left; + } + + public void setLeft(Object left) { + this.left = left; + } + + public Object getRight() { + return right; + } + + public void setRight(Object right) { + this.right = right; + } + + public String getDiffType() { + return diffType; + } + + public void setDiffType(String diffType) { + this.diffType = diffType; + } +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/model/ResultConvertUtil.java b/zc-business/src/main/java/com/zc/business/utils/diff/model/ResultConvertUtil.java new file mode 100644 index 00000000..ecc86d5b --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/model/ResultConvertUtil.java @@ -0,0 +1,66 @@ +package com.zc.business.utils.diff.model; + +import java.util.ArrayList; +import java.util.Objects; + +/** + * 结果转换工具:将diff上下文转换成diff最终结果 + * @Author JingWei + * @create 2022/2/25 + */ +public class ResultConvertUtil { + public static final String OBJECT_NULL = null; + //diff结果类型分为修改、新增、删除 + public static final String TYPE_MODIFY = "MODIFY"; + public static final String TYPE_ADD = "ADD"; + public static final String TYPE_DELETE = "DELETE"; + + /** + * 将diff上下文转换成diff最终结果 + * @param diffContext diff比较的直接结果 + * @return diff展示的结果 + */ + public static ArrayList constructResult(DiffContext diffContext) { + ArrayList list = new ArrayList<>(); + for (SingleNodeDifference resultModel : diffContext.getDiffResultModels()) { + Result printModel = convert(resultModel); + boolean leftAndRightBothNull = (Objects.equals(OBJECT_NULL,resultModel.getLeft())) + && Objects.equals(OBJECT_NULL,resultModel.getRight()) ; + //判断两个对象是否同时为空 + if (leftAndRightBothNull) { + printModel.setDiffType(TYPE_MODIFY); + } + //这种情况为对象A中keySet没这个key,或者A数组长度小于B 数组中没这个元素。 + else if (Objects.equals(OBJECT_NULL,resultModel.getLeft()) ) { + printModel.setDiffType(TYPE_ADD); + printModel.setLeftPath(null); + } + //这种情况为对象B中keySet没这个key,或者B数组长度小于A 数组中没这个元素。 + else if (Objects.equals(OBJECT_NULL,resultModel.getRight()) ) { + printModel.setDiffType(TYPE_DELETE); + printModel.setRightPath(null); + } + //其他情况 + else { + printModel.setDiffType(TYPE_MODIFY); + } + list.add(printModel); + } + return list; + } + + + /** + * 数据模型转换,增加类型字段。 + * @param resultModel 比较结果 + * @return 展示模型 + */ + private static Result convert(SingleNodeDifference resultModel) { + Result printModel = new Result(); + printModel.setLeft(resultModel.getLeft()); + printModel.setRight(resultModel.getRight()); + printModel.setLeftPath(resultModel.getLeftPath()); + printModel.setRightPath(resultModel.getRightPath()); + return printModel; + } +} diff --git a/zc-business/src/main/java/com/zc/business/utils/diff/model/SingleNodeDifference.java b/zc-business/src/main/java/com/zc/business/utils/diff/model/SingleNodeDifference.java new file mode 100644 index 00000000..da9e9b40 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/utils/diff/model/SingleNodeDifference.java @@ -0,0 +1,69 @@ +package com.zc.business.utils.diff.model; + +import java.io.Serializable; + +/** + * 单对元素比较结果 + * @Author JingWei + * @create 2022/3/2 + */ +public class SingleNodeDifference implements Serializable { + /** + * 第一个对象路径 + */ + private String leftPath; + /** + * 第二个对象路径 + */ + private String rightPath; + /** + * 第一个对象值 + */ + private Object left; + /** + * 第二个对象值 + */ + private Object right; + /** + * 比较结果 + */ + + public SingleNodeDifference(String leftPath, String rightPath, Object left, Object right) { + this.leftPath = leftPath; + this.rightPath = rightPath; + this.left = left; + this.right = right; + } + + public String getLeftPath() { + return leftPath; + } + + public void setLeftPath(String leftPath) { + this.leftPath = leftPath; + } + + public String getRightPath() { + return rightPath; + } + + public void setRightPath(String rightPath) { + this.rightPath = rightPath; + } + + public Object getLeft() { + return left; + } + + public void setLeft(Object left) { + this.left = left; + } + + public Object getRight() { + return right; + } + + public void setRight(Object right) { + this.right = right; + } +} \ No newline at end of file diff --git a/zc-business/src/main/resources/mapper/business/DcEventMapper.xml b/zc-business/src/main/resources/mapper/business/DcEventMapper.xml index 67dea91f..a2e7280c 100644 --- a/zc-business/src/main/resources/mapper/business/DcEventMapper.xml +++ b/zc-business/src/main/resources/mapper/business/DcEventMapper.xml @@ -681,6 +681,15 @@ ) t ON s.event_state = t.event_state; + + update dc_event @@ -689,4 +698,9 @@ where id = #{id} + + + update dc_event set end_time = now(),event_state = '2' + where id = #{eventId} + \ No newline at end of file diff --git a/zc-business/src/main/resources/mapper/business/DcEventProcessMapper.xml b/zc-business/src/main/resources/mapper/business/DcEventProcessMapper.xml index a4b4986a..28e045e5 100644 --- a/zc-business/src/main/resources/mapper/business/DcEventProcessMapper.xml +++ b/zc-business/src/main/resources/mapper/business/DcEventProcessMapper.xml @@ -131,5 +131,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" where event_id = #{id} ORDER BY operation_time ASC; + \ No newline at end of file