diff --git a/zc-business/src/main/java/com/zc/business/constant/RedisKeyConstants.java b/zc-business/src/main/java/com/zc/business/constant/RedisKeyConstants.java new file mode 100644 index 00000000..abeba97b --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/constant/RedisKeyConstants.java @@ -0,0 +1,13 @@ +package com.zc.business.constant; + +/** + * Redis key 常量定义 + * @author xiepufeng + */ +public class RedisKeyConstants +{ + /** + * 设备 + */ + public static final String DC_DEVICES = "dc:devices"; +} diff --git a/zc-business/src/main/java/com/zc/business/controller/BroadcastController.java b/zc-business/src/main/java/com/zc/business/controller/BroadcastController.java index bd387b4e..5c594912 100644 --- a/zc-business/src/main/java/com/zc/business/controller/BroadcastController.java +++ b/zc-business/src/main/java/com/zc/business/controller/BroadcastController.java @@ -24,7 +24,7 @@ import static com.ruoyi.common.constant.Constants.HTTP; /** * License * - * @author Athena-xiepufeng + * @author Athena-zhaoxianglong */ @Api(tags = "广播接口") @RestController diff --git a/zc-business/src/main/java/com/zc/business/controller/CodeScanningAlarmController.java b/zc-business/src/main/java/com/zc/business/controller/CodeScanningAlarmController.java index 4b1edea2..92d6df7d 100644 --- a/zc-business/src/main/java/com/zc/business/controller/CodeScanningAlarmController.java +++ b/zc-business/src/main/java/com/zc/business/controller/CodeScanningAlarmController.java @@ -18,7 +18,7 @@ import java.util.Objects; /** * License * - * @author Athena-xiepufeng + * @author Athena-zhaoxianglong */ @Api(tags = "扫码报警") @RestController diff --git a/zc-business/src/main/java/com/zc/business/controller/DcDeviceController.java b/zc-business/src/main/java/com/zc/business/controller/DcDeviceController.java index af4114a7..2b408d49 100644 --- a/zc-business/src/main/java/com/zc/business/controller/DcDeviceController.java +++ b/zc-business/src/main/java/com/zc/business/controller/DcDeviceController.java @@ -93,7 +93,7 @@ public class DcDeviceController extends BaseController { @ApiOperation("无分页根据设备桩号查询设备列表") @PreAuthorize("@ss.hasPermi('iot:device:query')") @PostMapping("pileNumberQuery") - public AjaxResult devicePileNumberQueryDevice(@RequestBody Map parameter) { + public AjaxResult devicePileNumberQueryDevice(@RequestBody Map parameter) { return AjaxResult.success(dcDeviceService.devicePileNumberQueryDevice(parameter)); } diff --git a/zc-business/src/main/java/com/zc/business/controller/DcTrafficIncidentsController.java b/zc-business/src/main/java/com/zc/business/controller/DcTrafficIncidentsController.java index 4c2f2547..4fd32ec0 100644 --- a/zc-business/src/main/java/com/zc/business/controller/DcTrafficIncidentsController.java +++ b/zc-business/src/main/java/com/zc/business/controller/DcTrafficIncidentsController.java @@ -162,6 +162,8 @@ public class DcTrafficIncidentsController { * * @author liuwenge * @date 2024/1/15 17:31 + * @param searchType 查询条件(1:站点,2:原因) + * @param facilityIdList 站点id * @param controlType 类型(1:封闭,2:限行) * @param startTime 开始时间 * @param endTime 结束时间 @@ -169,10 +171,12 @@ public class DcTrafficIncidentsController { */ @ApiOperation("收费站统计分析table") @PostMapping("/selectTollStationAnalysis") - public AjaxResult selectTollStationAnalysis(@ApiParam(value="类型(1:封闭,2:限行)", name="controlType", required=true) @RequestParam ("controlType") String controlType, + public AjaxResult selectTollStationAnalysis(@ApiParam(value="查询条件(1:站点,2:原因)", name="searchType", required=true) @RequestParam ("searchType") String searchType, + @ApiParam(value="站点id", name="facilityId", required=false) @RequestParam (name="facilityId",required = false) String[] facilityIdList, + @ApiParam(value="类型(1:封闭,2:限行)", name="controlType", required=false) @RequestParam (name="controlType",required = false) String controlType, @ApiParam(value="开始时间", name="startTime", required=true) @RequestParam ("startTime") String startTime, @ApiParam(value="结束时间", name="endTime", required=true) @RequestParam ("endTime") String endTime){ - return trafficIncidentsService.selectTollStationAnalysis(controlType,startTime,endTime); + return trafficIncidentsService.selectTollStationAnalysis(searchType,facilityIdList,controlType,startTime,endTime); } /** diff --git a/zc-business/src/main/java/com/zc/business/controller/DcWarningController.java b/zc-business/src/main/java/com/zc/business/controller/DcWarningController.java index 5c3115d9..73ab8965 100644 --- a/zc-business/src/main/java/com/zc/business/controller/DcWarningController.java +++ b/zc-business/src/main/java/com/zc/business/controller/DcWarningController.java @@ -80,7 +80,9 @@ public class DcWarningController extends BaseController @PostMapping public AjaxResult add(@RequestBody DcWarning dcWarning) { - + //设置事件Id UUID无下划线格式32 + String uuid = IdUtils.fastSimpleUUID(); + dcWarning.setId(uuid); return toAjax(dcWarningService.insertDcWarning(dcWarning)); } diff --git a/zc-business/src/main/java/com/zc/business/controller/NonAutomaticWarningController.java b/zc-business/src/main/java/com/zc/business/controller/NonAutomaticWarningController.java index 3f395bab..6aa9461d 100644 --- a/zc-business/src/main/java/com/zc/business/controller/NonAutomaticWarningController.java +++ b/zc-business/src/main/java/com/zc/business/controller/NonAutomaticWarningController.java @@ -29,7 +29,7 @@ import java.util.Objects; /** * License * - * @author Athena-xiepufeng + * @author Athena-zhaoxianglong */ @Api(tags = "非机预警接口") @RestController @@ -102,7 +102,12 @@ public class NonAutomaticWarningController extends BaseController { client.setCallback(new MqttCallback() { public void connectionLost(Throwable cause) { - System.out.println("connectionLost"); + System.out.println("断开连接"); + //try { + // eventSubscription(); + //} catch (Exception e) { + // throw new RuntimeException(e); + //} } public void messageArrived(String topic, MqttMessage message) { @@ -195,9 +200,6 @@ public class NonAutomaticWarningController extends BaseController { dcWarning.setStakeMark("k190+495"); } dcWarning.setWarningTitle(crossingName+srcName); - //设置事件Id UUID无下划线格式32 - String uuid = IdUtils.fastSimpleUUID(); - dcWarning.setId(uuid); dcWarning.setWarningSource(6); dcWarning.setWarningState(1); dcWarningService.insertDcWarning(dcWarning); diff --git a/zc-business/src/main/java/com/zc/business/domain/DcEventAccident.java b/zc-business/src/main/java/com/zc/business/domain/DcEventAccident.java index 65028728..3d1785e0 100644 --- a/zc-business/src/main/java/com/zc/business/domain/DcEventAccident.java +++ b/zc-business/src/main/java/com/zc/business/domain/DcEventAccident.java @@ -158,9 +158,11 @@ public class DcEventAccident extends BaseEntity { * 3-行3 * 4-行4 */ +/* @ApiModelProperty("车道占用") private Long laneOccupancy; +*/ /** * 小型车(辆) diff --git a/zc-business/src/main/java/com/zc/business/enums/IotProductEnum.java b/zc-business/src/main/java/com/zc/business/enums/IotProductEnum.java new file mode 100644 index 00000000..e22bfef9 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/enums/IotProductEnum.java @@ -0,0 +1,35 @@ +package com.zc.business.enums; + +/** + * 物联网产品类型 + * @author xiepufeng + */ +public enum IotProductEnum { + // 摄像头检测事件 + CAMERA_DETECTION_EVENT("video-event"), + // 情报板 + VARIABLE_MESSAGE_SIGN("7877"), + + // 行车诱导 + TRAFFIC_GUIDANCE("8866"), + + // 气象检测器 + WEATHER_DETECTOR("zc-meteorological"), + + // 设备箱 + EQUIPMENT_BOX("zc-shebeixiang-1883"), + + // 一站式情况调查产品 + ONE_STOP_PRODUCT("zc-yzsqkdc-3131") + + ; + private final String value; + + IotProductEnum(String value) { + this.value = value; + } + + public String value() { + return this.value; + } +} diff --git a/zc-business/src/main/java/com/zc/business/enums/WarningSourceEnum.java b/zc-business/src/main/java/com/zc/business/enums/WarningSourceEnum.java new file mode 100644 index 00000000..1f0a6c77 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/enums/WarningSourceEnum.java @@ -0,0 +1,31 @@ +package com.zc.business.enums; + +/** + * 预警事件信息来源 + * @author xiepufeng + */ +public enum WarningSourceEnum { + VIDEO_AI(1, "视频AI"), + RADAR_RECOGNITION(2,"雷达识别"), + CONE_DETECTION(3,"锥桶感应"), + GUARDRAIL_COLLISION(4,"护栏碰撞"), + QR_CODE_ALERT(5,"扫码报警"), + NON_MOTOR_VEHICLE_WARNING(6,"非机动车预警"); + + private final Integer code; + private final String description; + + WarningSourceEnum(Integer code, String description) { + this.code = code; + this.description = description; + } + + public Integer getCode() + { + return code; + } + + public String getDescription() { + return description; + } +} diff --git a/zc-business/src/main/java/com/zc/business/enums/WarningStateEnum.java b/zc-business/src/main/java/com/zc/business/enums/WarningStateEnum.java new file mode 100644 index 00000000..62afff1f --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/enums/WarningStateEnum.java @@ -0,0 +1,30 @@ +package com.zc.business.enums; + +/** + * 警情状态 + * @author xiepufeng + */ +public enum WarningStateEnum { + REPORTED(1, "上报"), + COMPLETED(2, "已完成"), + TERMINATED(3, "已终止"), + AUTO_ENDED(4, "自动结束"); + + private final Integer code; + + private final String description; + + public Integer getCode() + { + return code; + } + + WarningStateEnum(Integer code, String description) { + this.code = code; + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/zc-business/src/main/java/com/zc/business/enums/WarningSubclassEnum.java b/zc-business/src/main/java/com/zc/business/enums/WarningSubclassEnum.java new file mode 100644 index 00000000..990fdf28 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/enums/WarningSubclassEnum.java @@ -0,0 +1,75 @@ +package com.zc.business.enums; + +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; + +/** + * 预警事件子类 + * @author xiepufeng + */ +public enum WarningSubclassEnum { + + TRAFFIC_JAM("1-1", "拥堵", "发生"), + SLOW_TRAFFIC("1-2", "缓行", "发生"), + REGULAR_PEDESTRIAN("2-1", "普通行人", "有行人:"), + STAFF_MEMBER("2-2", "工作人员", "有行人:"), + MOTORCYCLE("3-1", "摩托车", "非机动车:"), + BICYCLE("3-2", "自行车","非机动车:"), + THREE_WHEELER("3-3", "三轮车","非机动车:"), + NON_CONSTRUCTION_VEHICLE("4-1", "非工程车", "非法停车:"), + CONSTRUCTION_VEHICLE("4-2", "工程车", "非法停车:"), + MAIN_ROAD_OCCUPIED("4-3", "主路有车", "非法停车:"), + RAMP_OCCUPIED("4-4", "匝道有车", "非法停车:"), + VEHICLE_BREAKDOWN("4-5", "车辆故障", "发生"), + TRAFFIC_ACCIDENT("4-6", "交通事故", "发生"), + EMERGENCY_LANE_BLOCKED("4-7", "应急车道被占用", ""), + VEHICLE_EXIT_EMERGENCY_LANE("4-8", "车离开应急车道", ""), + OTHER_CONDITION("4-9", "其他", "非法停车:"), + REVERSING_OR_GOING_BACKWARDS("5-1", "倒车/逆行", "发现"), + FIREWORKS("6-1", "烟火", ""), + DEBRIS_ON_ROAD("7-1", "撒落物", "发现"), + RAIN("8-1", "雨", "异常天气:"), + HAIL("8-2", "冰雹", "异常天气:"), + WIND("8-3", "风", "异常天气:"), + FOG("8-4", "雾", "异常天气:"), + HIGH_TEMPERATURE("8-5", "高温", "异常天气:"), + WATER_POOLING("8-6", "积水", "异常天气:"), + SLIPPERY_ROAD_SURFACE("8-6", "路面湿滑", "异常天气:"), + ICY_ROAD("8-8", "路面结冰", "异常天气:"), + LOW_VISIBILITY("8-9", "道路能见度低", "异常天气:"), + ROAD_FOG("8-10", "道路团雾", "异常天气:"), + COLLISION_ONLY("9-1", "只碰撞不倾斜", "护栏碰撞:"), + TILTED_WITHOUT_COLLISION("9-1", "只倾斜无碰撞", "护栏碰撞:"), + COLLISION_AND_TILTED("9-3", "碰撞后倾斜", "护栏碰撞:"); + @Getter + private final String code; + @Getter + private final String info; + + private final String decorate; + + private static final Map ENUM_BY_CODE = new HashMap<>(); + + static { + for (WarningSubclassEnum value : values()) { + ENUM_BY_CODE.put(value.getCode(), value); + } + } + + WarningSubclassEnum(String code, String info, String decorate) + { + this.code = code; + this.info = info; + this.decorate = decorate; + } + + public String getDecorateInfo() { + return decorate + decorate; + } + + public static String getDecorateInfo(String code) { + return ENUM_BY_CODE.get(code).getDecorateInfo(); + } +} diff --git a/zc-business/src/main/java/com/zc/business/enums/WarningTypeEnum.java b/zc-business/src/main/java/com/zc/business/enums/WarningTypeEnum.java new file mode 100644 index 00000000..a004a6b4 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/enums/WarningTypeEnum.java @@ -0,0 +1,39 @@ +package com.zc.business.enums; + +/** + * 感知事件类型(主类) + * @author xiepufeng + */ +public enum WarningTypeEnum { + + TRAFFIC_JAM(1, "交通拥堵"), + PEDESTRIAN(2, "行人"), + NON_MOTOR_VEHICLE(3, "非机动车"), + PARK(4, "停车"), + VEHICLE_CONVERSE_RUNNING(5, "倒车/逆行"), + FIREWORKS(6, "烟火"), + OUTFALL(7, "撒落物"), + UNUSUAL_WEATHER(8, "异常天气"), + BARRIER_CRASH(9, "护栏碰撞"), + TRAFFIC_ACCIDENT(10, "交通事故"), + VEHICLE_FAULT(11, "车辆故障"), + OTHER(99, "其他"); + private final Integer code; + private final String info; + + WarningTypeEnum(Integer code, String info) + { + this.code = code; + this.info = info; + } + + public Integer getCode() + { + return code; + } + + public String getInfo() + { + return info; + } +} diff --git a/zc-business/src/main/java/com/zc/business/mapper/DcMeteorologicalDetectorDataMapper.java b/zc-business/src/main/java/com/zc/business/mapper/DcMeteorologicalDetectorDataMapper.java index 3a0868cb..b98086ce 100644 --- a/zc-business/src/main/java/com/zc/business/mapper/DcMeteorologicalDetectorDataMapper.java +++ b/zc-business/src/main/java/com/zc/business/mapper/DcMeteorologicalDetectorDataMapper.java @@ -43,6 +43,22 @@ public interface DcMeteorologicalDetectorDataMapper */ int updateDcMeteorologicalDetectorData(DcMeteorologicalDetectorData dcMeteorologicalDetectorData); + /** + * 中间库新增气象检测器数据 + * + * @param dcMeteorologicalDetectorData 气象检测器数据 + * @return 结果 + */ + Boolean insertIntermediateWarehouseData(DcMeteorologicalDetectorData dcMeteorologicalDetectorData); + + /** + * 中间库修改气象检测器数据 + * + * @param dcMeteorologicalDetectorData 气象检测器数据 + * @return 结果 + */ + Boolean updateIntermediateWarehouseData(DcMeteorologicalDetectorData dcMeteorologicalDetectorData); + /** * 删除气象检测器数据 * diff --git a/zc-business/src/main/java/com/zc/business/mapper/DcTrafficIncidentsMapper.java b/zc-business/src/main/java/com/zc/business/mapper/DcTrafficIncidentsMapper.java index 601d1176..d0cfeb9b 100644 --- a/zc-business/src/main/java/com/zc/business/mapper/DcTrafficIncidentsMapper.java +++ b/zc-business/src/main/java/com/zc/business/mapper/DcTrafficIncidentsMapper.java @@ -181,12 +181,25 @@ public interface DcTrafficIncidentsMapper { * * @author liuwenge * @date 2024/1/16 14:18 + * @param facilityIdList 站点id * @param controlType 类型(1:封闭,2:限行) * @param startTime 开始时间 * @param endTime 结束时间 * @return java.util.List> */ - List> selectTollStationAnalysis(@Param("controlType") String controlType,@Param("startTime") String startTime,@Param("endTime") String endTime); + List> selectTollStationAnalysis(@Param("facilityIdList") String[] facilityIdList,@Param("controlType") String controlType,@Param("startTime") String startTime,@Param("endTime") String endTime); + + /** + * @Description 收费站统计分析table 根据站点 + * + * @author liuwenge + * @date 2024/2/6 15:18 + * @param facilityIdList 站点id + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return java.util.List> + */ + List> selectTollStationAnalysisByFacility(@Param("facilityIdList") String[] facilityIdList,@Param("startTime") String startTime,@Param("endTime") String endTime); /** * @Description 收费站统计分析echarts diff --git a/zc-business/src/main/java/com/zc/business/message/device/handler/DeviceMessageHandler.java b/zc-business/src/main/java/com/zc/business/message/device/handler/DeviceMessageHandler.java index a03eecf9..f0b5db58 100644 --- a/zc-business/src/main/java/com/zc/business/message/device/handler/DeviceMessageHandler.java +++ b/zc-business/src/main/java/com/zc/business/message/device/handler/DeviceMessageHandler.java @@ -1,53 +1,48 @@ package com.zc.business.message.device.handler; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import com.ruoyi.common.core.redis.RedisCache; +import com.zc.business.constant.RedisKeyConstants; import com.zc.business.domain.DcDevice; +import com.zc.business.domain.DcWarning; +import com.zc.business.enums.IotProductEnum; +import com.zc.business.enums.WarningSourceEnum; +import com.zc.business.enums.WarningStateEnum; +import com.zc.business.enums.WarningSubclassEnum; import com.zc.business.service.IDcDeviceService; +import com.zc.business.service.IDcWarningService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.util.Date; import java.util.List; import java.util.stream.Collectors; /** * 设备消息处理 + * @author xiepufeng */ @Service public class DeviceMessageHandler { - enum ProductType { - // 摄像头检测事件 - CAMERA_DETECTION_EVENT("video-event"), - // 情报板 - VARIABLE_MESSAGE_SIGN("7877"), - - // 行车诱导 - TRAFFIC_GUIDANCE("8866"), - - // 气象检测器 - WEATHER_DETECTOR("zc-meteorological"), - - // 设备箱 - EQUIPMENT_BOX("zc-shebeixiang-1883"), + private final Logger logger = LoggerFactory.getLogger(this.getClass()); - // 一站式情况调查产品 - ONE_STOP_PRODUCT("zc-yzsqkdc-3131") - - ; - private final String value; - - ProductType(String value) { - this.value = value; - } + @Resource + private IDcDeviceService dcDeviceService; - public String value() { - return this.value; - } - } + @Resource + private IDcWarningService dcWarningService; @Resource - private IDcDeviceService dcDeviceService; + private RedisCache redisCache; + + @Value("${iot.address}") + private String iotAddress; /** * 更新设备状态 @@ -76,11 +71,18 @@ public class DeviceMessageHandler { public void handle(String msg) { JSONObject data = JSON.parseObject(msg, JSONObject.class); + JSONObject headers = data.getJSONObject("headers"); + + if (headers == null) { + logger.error("物联网消息解析处理,消息中缺少headers数据"); + return; + } + // 产品id - String productId = data.getJSONObject("headers").getString("productId"); + String productId = headers.getString("productId"); // 摄像头检测事件 - if (productId.equals(ProductType.CAMERA_DETECTION_EVENT.value)) { + if (IotProductEnum.CAMERA_DETECTION_EVENT.value().equals(productId)) { this.cameraDetectionEventHandle(data); } } @@ -92,7 +94,56 @@ public class DeviceMessageHandler { */ private void cameraDetectionEventHandle(JSONObject event) { - } + JSONObject data = event.getJSONObject("data"); + + DcWarning dcWarning = new DcWarning(); + + // 桩号 + dcWarning.setStakeMark(data.getString("stakeMark")); + // 方向 + dcWarning.setDirection(data.getString("direction")); + // 警情状态 + dcWarning.setWarningState(WarningStateEnum.REPORTED.getCode()); + // 捕获时间 + Long captureTime = data.getLong("captureTime"); + // 预警时间 + dcWarning.setWarningTime(new Date(captureTime)); + // 信息来源 + dcWarning.setWarningSource(WarningSourceEnum.VIDEO_AI.getCode()); + // 事件主类型 + dcWarning.setWarningType(data.getInteger("warningType")); + + String warningSubclass = data.getString("warningSubclass"); + // 子类型 + dcWarning.setWarningSubclass(warningSubclass); + + String stakeMarkDescription = data.getString("stakeMarkDescription"); + + // 标题 + dcWarning.setWarningTitle(stakeMarkDescription + WarningSubclassEnum.getDecorateInfo(warningSubclass)); + // 影响车道 + dcWarning.setLane(String.valueOf(data.getInteger("relatedLaneNo"))); + + // 物联设备id + String iotDeviceId = event.getString("deviceId"); + + DcDevice dcDevice = redisCache.getCacheMapValue(RedisKeyConstants.DC_DEVICES, iotDeviceId); + if (dcDevice != null) { + // 所属机构 + dcWarning.setDeptId(dcDevice.getGroupId()); + } + JSONArray pictures = data.getJSONArray("pictures"); + + List pictureList = pictures.stream().map(picture -> (iotAddress + "/profile" + picture)).collect(Collectors.toList()); + + JSONObject otherConfig = new JSONObject(); + otherConfig.put("pictures", pictureList); + + dcWarning.setOtherConfig(otherConfig.toString()); + + dcWarningService.insertDcWarning(dcWarning); + + } } diff --git a/zc-business/src/main/java/com/zc/business/message/device/listener/DevicePropertyReportListener.java b/zc-business/src/main/java/com/zc/business/message/device/listener/DevicePropertyReportListener.java index d7370696..110da462 100644 --- a/zc-business/src/main/java/com/zc/business/message/device/listener/DevicePropertyReportListener.java +++ b/zc-business/src/main/java/com/zc/business/message/device/listener/DevicePropertyReportListener.java @@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON; import com.google.gson.JsonObject; import com.zc.business.domain.DcDevice; import com.zc.business.domain.DcMeteorologicalDetectorData; +import com.zc.business.enums.IotProductEnum; import com.zc.business.service.IDcMeteorologicalDetectorDataService; import com.zc.common.core.redis.stream.RedisStream; import org.slf4j.Logger; @@ -23,6 +24,7 @@ import java.util.Map; /** * 设备属性上报消息监听 */ + @Component public class DevicePropertyReportListener implements StreamListener> { @@ -46,11 +48,14 @@ public class DevicePropertyReportListener implements StreamListener { Long statisticalAnomalyDevice(); - List devicePileNumberQueryDevice(Map parameter); + List devicePileNumberQueryDevice(Map parameter); } diff --git a/zc-business/src/main/java/com/zc/business/service/IDcMeteorologicalDetectorDataService.java b/zc-business/src/main/java/com/zc/business/service/IDcMeteorologicalDetectorDataService.java index f3810b7d..481967f4 100644 --- a/zc-business/src/main/java/com/zc/business/service/IDcMeteorologicalDetectorDataService.java +++ b/zc-business/src/main/java/com/zc/business/service/IDcMeteorologicalDetectorDataService.java @@ -35,6 +35,14 @@ public interface IDcMeteorologicalDetectorDataService */ int insertDcMeteorologicalDetectorData(DcMeteorologicalDetectorData dcMeteorologicalDetectorData); + /** + * 中间库新增气象检测器数据 + * + * @param dcMeteorologicalDetectorData 气象检测器数据 + * @return 结果 + */ + boolean insertIntermediateWarehouseData(DcMeteorologicalDetectorData dcMeteorologicalDetectorData); + /** * 修改气象检测器数据 * diff --git a/zc-business/src/main/java/com/zc/business/service/IDcTrafficIncidentsService.java b/zc-business/src/main/java/com/zc/business/service/IDcTrafficIncidentsService.java index 9fe3c4ff..8687a48a 100644 --- a/zc-business/src/main/java/com/zc/business/service/IDcTrafficIncidentsService.java +++ b/zc-business/src/main/java/com/zc/business/service/IDcTrafficIncidentsService.java @@ -103,12 +103,14 @@ public interface IDcTrafficIncidentsService { * @return com.ruoyi.common.core.domain.AjaxResult * @Description 路网管控-事件管控分析-收费站统计分析table * @author liuwenge + * @param searchType 查询条件 + * @param facilityIdList 站点id * @param controlType 类型(1:封闭,2:限行) * @param startTime 开始时间 * @param endTime 结束时间 * @date 2024/1/15 17:31 */ - AjaxResult selectTollStationAnalysis(String controlType,String startTime,String endTime); + AjaxResult selectTollStationAnalysis(String searchType,String[] facilityIdList,String controlType,String startTime,String endTime); /** * @Description 路网管控-事件管控分析-收费站统计分析echarts(当月) diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcDeviceServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcDeviceServiceImpl.java index 3f4a497a..6d14e74b 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcDeviceServiceImpl.java +++ b/zc-business/src/main/java/com/zc/business/service/impl/DcDeviceServiceImpl.java @@ -3,8 +3,10 @@ package com.zc.business.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.PageUtils; +import com.zc.business.constant.RedisKeyConstants; import com.zc.business.domain.DcDevice; import com.zc.business.domain.DcProduct; import com.zc.business.domain.DcStakeMark; @@ -15,6 +17,7 @@ import com.zc.business.service.IDcStakeMarkService; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.*; import java.util.regex.Matcher; @@ -35,6 +38,42 @@ public class DcDeviceServiceImpl extends ServiceImpl i @Resource private DcDeviceMapper dcDeviceMapper; + @Resource + private RedisCache redisCache; + + @PostConstruct + public void init() + { + /* + 添加数据到 redis 缓存 + */ + updateRedisCache(); + } + + /** + * 添加数据到 redis 缓存 + */ + public void updateRedisCache() + { + // 获取全部设备列表数据 + List dcDevices = list(); + if (dcDevices == null || dcDevices.isEmpty()) + { + return; + } + + // 清楚 redis 缓存数据 + redisCache.deleteObject(RedisKeyConstants.DC_DEVICES); + + // 添加 redis 缓存数据 + dcDevices.forEach(val -> { + String iotDeviceId = val.getIotDeviceId(); + if (StringUtils.hasText(iotDeviceId)) { + redisCache.setCacheMapValue(RedisKeyConstants.DC_DEVICES, val.getIotDeviceId(), val); + } + }); + } + private static String REGEX_CHINESE = "[\u4e00-\u9fa5]";// 中文正则 public LambdaQueryWrapper deviceQueryWrapper(DcDevice dcDevice) { @@ -176,7 +215,14 @@ public class DcDeviceServiceImpl extends ServiceImpl i dcDevice.setCreateTime(new Date()); dcDevice.setUseState(DcDevice.UNUSEDSTATE); - return save(dcDevice); + + if (save(dcDevice)) { + // 更新缓存 + updateRedisCache(); + return true; + } + + return false; } /** @@ -198,7 +244,14 @@ public class DcDeviceServiceImpl extends ServiceImpl i } dcDevice.setUpdateTime(new Date()); - return updateById(dcDevice); + + if (updateById(dcDevice)) { + // 更新缓存 + updateRedisCache(); + return true; + } + + return false; } /** @@ -218,7 +271,14 @@ public class DcDeviceServiceImpl extends ServiceImpl i throw new ServiceException("设备正在使用", HttpStatus.MOVED_PERM); } }); - return removeByIds(ids); + + if (removeByIds(ids)) { + // 更新缓存 + updateRedisCache(); + return true; + } + + return false; } /** @@ -258,6 +318,9 @@ public class DcDeviceServiceImpl extends ServiceImpl i queryWrapper.eq(DcDevice::getIotDeviceId, device.getIotDeviceId()); update(device, queryWrapper); } + + // 更新缓存 + updateRedisCache(); return true; } @@ -298,14 +361,18 @@ public class DcDeviceServiceImpl extends ServiceImpl i } @Override - public List devicePileNumberQueryDevice(Map parameter) { + public List devicePileNumberQueryDevice(Map parameter) { ArrayList dcDevices = new ArrayList<>(); - Integer startStakeMark = Integer.parseInt(String.valueOf(parameter.get("startStakeMark").get(0)) + String.valueOf(parameter.get("startStakeMark").get(1))); - Integer endStakeMark = Integer.parseInt(String.valueOf(parameter.get("endStakeMark").get(0)) + String.valueOf(parameter.get("endStakeMark").get(1))); + List startStakeMarks = castList(parameter.get("startStakeMark"), String.class); + List endStakeMarks = castList(parameter.get("endStakeMark"), String.class); + String deviceType = String.valueOf(parameter.get("deviceType")); + Integer startStakeMark = Integer.parseInt(startStakeMarks.get(0) + startStakeMarks.get(1)); + Integer endStakeMark = Integer.parseInt(endStakeMarks.get(0) + endStakeMarks.get(1)); LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); ArrayList integers = new ArrayList<>(); integers.add(6); - lambdaQueryWrapper.notIn(DcDevice::getDeviceType,integers); + lambdaQueryWrapper.notIn(DcDevice::getDeviceType, integers); + lambdaQueryWrapper.eq(DcDevice::getDeviceType, deviceType); List list = list(lambdaQueryWrapper); for (DcDevice dcDevice : list) { String stakeMark = dcDevice.getStakeMark(); @@ -336,5 +403,16 @@ public class DcDeviceServiceImpl extends ServiceImpl i return dcDevices; } + + public static List castList(Object obj, Class clazz) { + List result = new ArrayList(); + if (obj instanceof List) { + for (Object o : (List) obj) { + result.add(clazz.cast(o)); + } + return result; + } + return null; + } } 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 8380fcb5..2d18296f 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 @@ -109,8 +109,12 @@ private DcEventAbnormalWeatherMapper dcEventAbnormalWeatherMapper; dcEvent.setEventState(0L); //获取部门信息 dcEvent.setDeptId(SecurityUtils.getDeptId()); +//用户 +dcEvent.setUserId(SecurityUtils.getUserId()); + dcEvent.setCreateTime(DateUtils.getNowDate()); int i7 = dcEventMapper.insertDcEvent(dcEvent); + if (i7>0){ //获取事件类型 int eventType = Math.toIntExact(dcEvent.getEventType()); diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcMeteorologicalDetectorDataServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcMeteorologicalDetectorDataServiceImpl.java index 2e7d772b..c1d3d4da 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcMeteorologicalDetectorDataServiceImpl.java +++ b/zc-business/src/main/java/com/zc/business/service/impl/DcMeteorologicalDetectorDataServiceImpl.java @@ -1,6 +1,10 @@ package com.zc.business.service.impl; import java.util.List; + +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.enums.DataSourceType; +import com.zc.business.domain.DcDevice; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.zc.business.mapper.DcMeteorologicalDetectorDataMapper; @@ -55,6 +59,22 @@ public class DcMeteorologicalDetectorDataServiceImpl implements IDcMeteorologica return dcMeteorologicalDetectorDataMapper.insertDcMeteorologicalDetectorData(dcMeteorologicalDetectorData); } + /** + * 中间库新增气象检测器数据 + * + * @param dcMeteorologicalDetectorData 气象检测器数据 + * @return 结果 + */ + @DataSource(value = DataSourceType.SLAVE)//切换数据源 + @Override + public boolean insertIntermediateWarehouseData(DcMeteorologicalDetectorData dcMeteorologicalDetectorData) + { + if (!dcMeteorologicalDetectorDataMapper.updateIntermediateWarehouseData(dcMeteorologicalDetectorData)){ + return dcMeteorologicalDetectorDataMapper.insertIntermediateWarehouseData(dcMeteorologicalDetectorData); + } + return false; + } + /** * 修改气象检测器数据 * diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficIncidentsServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficIncidentsServiceImpl.java index f29ad12a..4e0dea1e 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficIncidentsServiceImpl.java +++ b/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficIncidentsServiceImpl.java @@ -2,6 +2,7 @@ package com.zc.business.service.impl; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.utils.StakeMarkUtils; +import com.ruoyi.common.utils.StringUtils; import com.zc.business.domain.DcEvent; import com.zc.business.domain.DcEventType; import com.zc.business.mapper.DcTrafficIncidentsMapper; @@ -15,6 +16,7 @@ import java.math.RoundingMode; import java.text.SimpleDateFormat; import java.time.Year; import java.util.*; +import java.util.stream.Collectors; /** * @Description 交通事件统计Service业务层处理 @@ -352,111 +354,129 @@ public class DcTrafficIncidentsServiceImpl implements IDcTrafficIncidentsService * @Description 路网管控-事件管控分析-收费站统计分析table * @return com.ruoyi.common.core.domain.AjaxResult * @author liuwenge + * @param searchType 查询条件 + * @param facilityIdList 站点id * @param controlType 类型(1:封闭,2:限行) * @param startTime 开始时间 * @param endTime 结束时间 * @date 2024/1/15 17:31 */ @Override - public AjaxResult selectTollStationAnalysis(String controlType,String startTime,String endTime){ - - //目标时段 - List> list = trafficIncidentsMapper.selectTollStationAnalysis(controlType,startTime,endTime); - BigDecimal allNum = BigDecimal.ZERO; - BigDecimal allTime = BigDecimal.ZERO; - for (Map data : list) { - allNum = allNum.add(new BigDecimal(data.get("num").toString())); - allTime = allTime.add(new BigDecimal(data.get("minuteTime").toString())); - } - for (Map data : list) { - //计算站次占比 - if (allNum.compareTo(BigDecimal.ZERO) > 0){ - BigDecimal numRatio = new BigDecimal(data.get("num").toString()).multiply(new BigDecimal("100")).divide(allNum,2, RoundingMode.HALF_UP); - data.put("numRatio",numRatio); - } else { - data.put("numRatio",0); - } + public AjaxResult selectTollStationAnalysis(String searchType,String[] facilityIdList,String controlType,String startTime,String endTime){ - //计算时长占比 - if (allTime.compareTo(BigDecimal.ZERO) > 0){ - BigDecimal timeRatio = new BigDecimal(data.get("minuteTime").toString()).multiply(new BigDecimal("100")).divide(allTime,2, RoundingMode.HALF_UP); - data.put("timeRatio",timeRatio); - } else { - data.put("timeRatio",0); - } - } + //站点 + if ("1".equals(searchType)){ + List> list = trafficIncidentsMapper.selectTollStationAnalysisByFacility(facilityIdList,startTime,endTime); + return AjaxResult.success(list); - //去年同期 - try { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - Calendar cal = Calendar.getInstance(); - - Date startDate = dateFormat.parse(startTime); - cal.setTime(startDate); - cal.add(Calendar.YEAR,-1); - String lastStartTime = dateFormat.format(cal.getTime()); - - Date endDate = dateFormat.parse(endTime); - cal.setTime(endDate); - cal.add(Calendar.YEAR,-1); - String lastEndTime = dateFormat.format(cal.getTime()); - - List> lastList = trafficIncidentsMapper.selectTollStationAnalysis(controlType,lastStartTime,lastEndTime); - BigDecimal lastAllNum = BigDecimal.ZERO; - BigDecimal lastAllTime = BigDecimal.ZERO; - - for (int i = 0; i < lastList.size(); i++) { - //去年同期站次 - lastAllNum = lastAllNum.add(new BigDecimal(lastList.get(i).get("num").toString())); - list.get(i).put("lastYearNum",lastList.get(i).get("num").toString()); - - //去年同期时长 - lastAllTime = lastAllTime.add(new BigDecimal(lastList.get(i).get("minuteTime").toString())); - list.get(i).put("lastYearTime",lastList.get(i).get("minuteTime").toString()); - } + } else { + //原因 - for (int i = 0; i < lastList.size(); i++) { - //去年同期站次占比 - if (lastAllNum.compareTo(BigDecimal.ZERO) > 0){ - BigDecimal numRatio = new BigDecimal(lastList.get(i).get("num").toString()).multiply(new BigDecimal("100")).divide(lastAllNum,2, RoundingMode.HALF_UP); - list.get(i).put("lastYearNumRatio",numRatio); + if (StringUtils.isEmpty(controlType)){ + return AjaxResult.error("请选择事件类型"); + } + //目标时段 + List> list = trafficIncidentsMapper.selectTollStationAnalysis(facilityIdList,controlType,startTime,endTime); + BigDecimal allNum = BigDecimal.ZERO; + BigDecimal allTime = BigDecimal.ZERO; + for (Map data : list) { + allNum = allNum.add(new BigDecimal(data.get("num").toString())); + allTime = allTime.add(new BigDecimal(data.get("minuteTime").toString())); + } + for (Map data : list) { + //计算站次占比 + if (allNum.compareTo(BigDecimal.ZERO) > 0){ + BigDecimal numRatio = new BigDecimal(data.get("num").toString()).multiply(new BigDecimal("100")).divide(allNum,2, RoundingMode.HALF_UP); + data.put("numRatio",numRatio); } else { - list.get(i).put("lastYearNumRatio",0); + data.put("numRatio",0); } - ////去年同期时长占比 - if (lastAllTime.compareTo(BigDecimal.ZERO) > 0) { - BigDecimal timeRatio = new BigDecimal(lastList.get(i).get("minuteTime").toString()).multiply(new BigDecimal("100")).divide(lastAllTime, 2, RoundingMode.HALF_UP); - list.get(i).put("lastYearTimeRatio", timeRatio); + + //计算时长占比 + if (allTime.compareTo(BigDecimal.ZERO) > 0){ + BigDecimal timeRatio = new BigDecimal(data.get("minuteTime").toString()).multiply(new BigDecimal("100")).divide(allTime,2, RoundingMode.HALF_UP); + data.put("timeRatio",timeRatio); } else { - list.get(i).put("lastYearTimeRatio", 0); + data.put("timeRatio",0); } } - //同比 - for (Map map : list) { - //站次同比 - if (new BigDecimal(map.get("lastYearNum").toString()).compareTo(BigDecimal.ZERO) > 0){ - BigDecimal yearNumRatio = new BigDecimal(map.get("num").toString()).multiply(new BigDecimal("100")).divide(new BigDecimal(map.get("lastYearNum").toString()),2, RoundingMode.HALF_UP); - map.put("yearNumRatio",yearNumRatio); - } else { - map.put("yearNumRatio",0); + //去年同期 + try { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Calendar cal = Calendar.getInstance(); + + Date startDate = dateFormat.parse(startTime); + cal.setTime(startDate); + cal.add(Calendar.YEAR,-1); + String lastStartTime = dateFormat.format(cal.getTime()); + + Date endDate = dateFormat.parse(endTime); + cal.setTime(endDate); + cal.add(Calendar.YEAR,-1); + String lastEndTime = dateFormat.format(cal.getTime()); + + List> lastList = trafficIncidentsMapper.selectTollStationAnalysis(facilityIdList,controlType,lastStartTime,lastEndTime); + BigDecimal lastAllNum = BigDecimal.ZERO; + BigDecimal lastAllTime = BigDecimal.ZERO; + + for (int i = 0; i < lastList.size(); i++) { + //去年同期站次 + lastAllNum = lastAllNum.add(new BigDecimal(lastList.get(i).get("num").toString())); + list.get(i).put("lastYearNum",lastList.get(i).get("num").toString()); + + //去年同期时长 + lastAllTime = lastAllTime.add(new BigDecimal(lastList.get(i).get("minuteTime").toString())); + list.get(i).put("lastYearTime",lastList.get(i).get("minuteTime").toString()); } - //时长同比 - if (new BigDecimal(map.get("lastYearTime").toString()).compareTo(BigDecimal.ZERO) > 0){ - BigDecimal yearTimeRatio = new BigDecimal(map.get("minuteTime").toString()).multiply(new BigDecimal("100")).divide(new BigDecimal(map.get("lastYearTime").toString()),2, RoundingMode.HALF_UP); - map.put("yearTimeRatio",yearTimeRatio); - } else { - map.put("yearTimeRatio",0); + for (int i = 0; i < lastList.size(); i++) { + //去年同期站次占比 + if (lastAllNum.compareTo(BigDecimal.ZERO) > 0){ + BigDecimal numRatio = new BigDecimal(lastList.get(i).get("num").toString()).multiply(new BigDecimal("100")).divide(lastAllNum,2, RoundingMode.HALF_UP); + list.get(i).put("lastYearNumRatio",numRatio); + } else { + list.get(i).put("lastYearNumRatio",0); + } + ////去年同期时长占比 + if (lastAllTime.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal timeRatio = new BigDecimal(lastList.get(i).get("minuteTime").toString()).multiply(new BigDecimal("100")).divide(lastAllTime, 2, RoundingMode.HALF_UP); + list.get(i).put("lastYearTimeRatio", timeRatio); + } else { + list.get(i).put("lastYearTimeRatio", 0); + } + } + + //同比 + for (Map map : list) { + //站次同比 + if (new BigDecimal(map.get("lastYearNum").toString()).compareTo(BigDecimal.ZERO) > 0){ + BigDecimal yearNumRatio = new BigDecimal(map.get("num").toString()).multiply(new BigDecimal("100")).divide(new BigDecimal(map.get("lastYearNum").toString()),2, RoundingMode.HALF_UP); + map.put("yearNumRatio",yearNumRatio); + } else { + map.put("yearNumRatio",0); + } + + //时长同比 + if (new BigDecimal(map.get("lastYearTime").toString()).compareTo(BigDecimal.ZERO) > 0){ + BigDecimal yearTimeRatio = new BigDecimal(map.get("minuteTime").toString()).multiply(new BigDecimal("100")).divide(new BigDecimal(map.get("lastYearTime").toString()),2, RoundingMode.HALF_UP); + map.put("yearTimeRatio",yearTimeRatio); + } else { + map.put("yearTimeRatio",0); + } } + + } catch (Exception e){ + e.printStackTrace(); } - } catch (Exception e){ - e.printStackTrace(); - } + return AjaxResult.success(list); - return AjaxResult.success(list); + } + } + private String customKey(Map map){ + return map.get("facilityId").toString(); } /** diff --git a/zc-business/src/main/resources/mapper/business/DcEventAccidentMapper.xml b/zc-business/src/main/resources/mapper/business/DcEventAccidentMapper.xml index c016d1eb..ce16e770 100644 --- a/zc-business/src/main/resources/mapper/business/DcEventAccidentMapper.xml +++ b/zc-business/src/main/resources/mapper/business/DcEventAccidentMapper.xml @@ -21,7 +21,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - @@ -34,7 +33,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select id, reporter_name, reporter_phone_number, accident_type, location_type, traffic_jam, weather_condition, impact_level, is_reverse_cargo, is_maintenance, police_contact, towing_service_contact, congestion_ahead, at_intersection, on_curve, spillage_item, vehicle_owner_phone, lane_occupancy, small_car, trucks, buses, tankers, minor_injuries, serious_injuries, fatalities, is_private, facility_id from dc_event_accident + select id, reporter_name, reporter_phone_number, accident_type, location_type, traffic_jam, weather_condition, impact_level, is_reverse_cargo, is_maintenance, police_contact, towing_service_contact, congestion_ahead, at_intersection, on_curve, spillage_item, vehicle_owner_phone, small_car, trucks, buses, tankers, minor_injuries, serious_injuries, fatalities, is_private, facility_id from dc_event_accident - + insert into dc_meteorological_detector_data @@ -146,6 +146,44 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" where id = #{id} + + insert into meteorological_detector_data + + device_id, + temperature, + humidity, + precipitation_type, + rainfall, + road_surface_status, + water_film_thickness, + update_time + + + #{iotDeviceId}, + #{temperature}, + #{humidity}, + #{precipitationType}, + #{rainfall}, + #{roadSurfaceStatus}, + #{waterFilmThickness}, + current_date + + + + + update meteorological_detector_data + + temperature = #{temperature}, + humidity = #{humidity}, + precipitation_type = #{precipitationType}, + rainfall = #{rainfall}, + road_surface_status = #{roadSurfaceStatus}, + water_film_thickness = #{waterFilmThickness}, + update_time = current_date + + where device_id = #{iotDeviceId} + + delete from dc_meteorological_detector_data where id = #{id} diff --git a/zc-business/src/main/resources/mapper/business/DcTrafficIncidentsMapper.xml b/zc-business/src/main/resources/mapper/business/DcTrafficIncidentsMapper.xml index e9337764..525b9702 100644 --- a/zc-business/src/main/resources/mapper/business/DcTrafficIncidentsMapper.xml +++ b/zc-business/src/main/resources/mapper/business/DcTrafficIncidentsMapper.xml @@ -225,15 +225,46 @@ then TIMESTAMPDIFF(MINUTE,t1.start_time,t1.end_time) else 0 end) minuteTime from dc_event t1 LEFT JOIN dc_event_traffic_control t2 on t1.id = t2.id - where - t1.event_type = '3' and t1.event_subclass = '3-2' and t2.control_type = #{controlType} + + t1.event_type = '3' and t1.event_subclass = '3-2' + + AND t2.facility_id in + + #{facilityId} + + and date_format(t1.start_time,'%Y-%m-%d %H:%i:%s') <= date_format(#{endTime},'%Y-%m-%d %H:%i:%s') and (date_format(t1.end_time,'%Y-%m-%d %H:%i:%s') > date_format(#{startTime},'%Y-%m-%d %H:%i:%s') or ISNULL(t1.end_time)) + GROUP BY t2.control_cause,t2.cause_type) t4 on t3.control_cause = t4.control_cause and t3.cause_type = t4.cause_type ORDER BY t3.control_cause, t3.cause_type + +