From e84e330db08200d2128551931718733061b92036 Mon Sep 17 00:00:00 2001 From: lau572 <1010031226@qq.com> Date: Tue, 19 Mar 2024 23:18:54 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E4=B8=8A=E4=B8=8B=E8=A1=8C=E6=91=84?= =?UTF-8?q?=E5=83=8F=E5=A4=B4=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zc/business/controller/VideoController.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/zc-business/src/main/java/com/zc/business/controller/VideoController.java b/zc-business/src/main/java/com/zc/business/controller/VideoController.java index 32620133..d63f8cbe 100644 --- a/zc-business/src/main/java/com/zc/business/controller/VideoController.java +++ b/zc-business/src/main/java/com/zc/business/controller/VideoController.java @@ -337,26 +337,30 @@ public class VideoController extends BaseController { List> upCameraList = datalist.stream() .filter(item -> "0".equals(item.get("camOrientation"))) .map(item->{ - item.put("pileNumDistance",pileNumTransformMetre(item.get("pileNum").toString())); + item.put("distance",Math.abs(pileNumTransformMetre(item.get("pileNum").toString()) - pileNumDistance)); return item; }) - .sorted(comparing(item -> Math.abs(Integer.parseInt(item.get("pileNumDistance").toString()) - pileNumDistance))) + .sorted(comparing(item -> Integer.parseInt(item.get("distance").toString()))) .collect(Collectors.toList()); - if (upCameraList.size() > 0){ + if (upCameraList.size() > 0 && Integer.parseInt(upCameraList.get(0).get("distance").toString()) < 2000){ result.put("upCamera",upCameraList.get(0)); + } else { + result.put("upCamera",new HashMap<>()); } //下行列表 List> downCameraList = datalist.stream() .filter(item -> "1".equals(item.get("camOrientation"))) .map(item->{ - item.put("pileNumDistance",pileNumTransformMetre(item.get("pileNum").toString())); + item.put("distance",Math.abs(pileNumTransformMetre(item.get("pileNum").toString()) - pileNumDistance)); return item; }) - .sorted(comparing(item -> Math.abs(Integer.parseInt(item.get("pileNumDistance").toString()) - pileNumDistance))) + .sorted(comparing(item -> Integer.parseInt(item.get("distance").toString()))) .collect(Collectors.toList()); - if (downCameraList.size() > 0){ + if (downCameraList.size() > 0 && Integer.parseInt(downCameraList.get(0).get("distance").toString()) < 2000){ result.put("downCamera",downCameraList.get(0)); + } else { + result.put("downCamera",new HashMap<>()); } From 18a3ffde129bfbcb7afa3663e92a246f1656dd91 Mon Sep 17 00:00:00 2001 From: zhaoxianglong Date: Wed, 20 Mar 2024 13:45:19 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E9=A1=B5=E9=9D=A2=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=A4=A9=E6=B0=94=E8=AF=B7=E6=B1=82=E6=8A=A5=E9=94=99=E3=80=81?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=8A=9F=E8=83=BD=E8=AF=B7=E6=B1=82=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/DcDeviceController.java | 21 ++++++---- .../zc/business/controller/DeviceStatus.java | 5 ++- .../business/controller/StatusController.java | 38 ++++++++++++++++++- .../controller/WeatherForecastController.java | 4 +- .../java/com/zc/business/domain/DcDevice.java | 4 +- .../java/com/zc/business/domain/Status.java | 9 +++++ .../zc/business/mapper/DcDeviceMapper.java | 3 ++ .../zc/business/service/IDcDeviceService.java | 2 + .../service/impl/DcDeviceServiceImpl.java | 7 +++- .../impl/DcWeatherMonitoringServiceImpl.java | 2 +- .../mapper/business/DcDeviceMapper.xml | 6 +++ .../mapper/business/StatusMapper.xml | 6 +++ 12 files changed, 91 insertions(+), 16 deletions(-) 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 41b68888..48528ab5 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 @@ -349,17 +349,22 @@ public class DcDeviceController extends BaseController { return AjaxResult.error("设备未接入"); } - OkHttp okHttp = new OkHttp(); + try { - RequestParams requestParams = new RequestParams(props); + OkHttp okHttp = new OkHttp(); + RequestParams requestParams = new RequestParams(props); - Response response // 请求响应 - = okHttp - .url(iotAddress + "/api/iot/device/functions/" + deviceId + "/" + functionId) // 请求地址 - .data(requestParams) - .post(); // 请求方法 - return JSON.parseObject(response.body().string(), AjaxResult.class); + + Response response // 请求响应 + = okHttp + .url(iotAddress + "/api/iot/device/functions/" + deviceId + "/" + functionId) // 请求地址 + .data(requestParams) + .post(); // 请求方法 + return JSON.parseObject(response.body().string(), AjaxResult.class); + }catch (Exception e){ + return AjaxResult.error("请求失败"); + } } diff --git a/zc-business/src/main/java/com/zc/business/controller/DeviceStatus.java b/zc-business/src/main/java/com/zc/business/controller/DeviceStatus.java index 659453ba..e307a436 100644 --- a/zc-business/src/main/java/com/zc/business/controller/DeviceStatus.java +++ b/zc-business/src/main/java/com/zc/business/controller/DeviceStatus.java @@ -1,5 +1,6 @@ package com.zc.business.controller; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.github.pagehelper.util.StringUtil; import com.ruoyi.common.utils.spring.SpringUtils; import com.zc.business.domain.DcDevice; @@ -47,7 +48,9 @@ public class DeviceStatus { IDcDeviceService deviceService= SpringUtils.getBean(IDcDeviceService.class); StatusService statusService= SpringUtils.getBean(StatusService.class); ExecutorService executor = Executors.newFixedThreadPool(100); - List deviceList = deviceService.list(); + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(DcDevice::getUseState,1); + List deviceList = deviceService.list(lambdaQueryWrapper); List> futures = new ArrayList<>(); diff --git a/zc-business/src/main/java/com/zc/business/controller/StatusController.java b/zc-business/src/main/java/com/zc/business/controller/StatusController.java index 382c4cda..07e3d102 100644 --- a/zc-business/src/main/java/com/zc/business/controller/StatusController.java +++ b/zc-business/src/main/java/com/zc/business/controller/StatusController.java @@ -6,7 +6,9 @@ import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.poi.ExcelUtil; +import com.zc.business.domain.DcDevice; import com.zc.business.domain.Status; +import com.zc.business.service.impl.DcDeviceServiceImpl; import com.zc.business.service.impl.StatusService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -22,7 +24,6 @@ import java.time.temporal.ChronoUnit; import java.util.*; import java.util.stream.Collectors; -import static com.ruoyi.common.utils.PageUtils.startPage; @Api(tags="设备状态") @RestController @@ -31,6 +32,8 @@ public class StatusController extends BaseController { @Autowired private StatusService statusService; + @Autowired + private DcDeviceServiceImpl dcDeviceService; //设备列表 @ApiOperation("设备状态列表按时间和类型") @@ -94,11 +97,39 @@ public class StatusController extends BaseController { return AjaxResult.success(mapSort); } + + @ApiOperation("根据设备Id查询折线图数据") + @GetMapping("/deviceStatusList") + public AjaxResult getDeviceStatusList(Long deviceId) { + + LocalDateTime thirtyDaysAgo = LocalDateTime.now().minusDays(30); + LocalDateTime currentTime = LocalDateTime.now(); + + Status status = new Status(); + status.setStartTime(thirtyDaysAgo); + status.setTime(currentTime); + status.setDeviceId(deviceId); + + List listStatus = statusService.list(status); + + // Group by day and calculate average successRate + Map averageSuccessRateByDay = listStatus.stream() + .collect(Collectors.groupingBy(s -> s.getTime().getDayOfMonth(), + Collectors.averagingDouble(s -> Double.parseDouble(s.getSuccessRate().replace("%",""))))); + + if (averageSuccessRateByDay.isEmpty()) { + return AjaxResult.success("暂无数据"); + } + + return AjaxResult.success(averageSuccessRateByDay); + } + //按类型划分设备 @ApiOperation("设备状态列表按类型") @GetMapping ("/type") public AjaxResult getTypeList() { + List dcDeviceList = dcDeviceService.numberOfDevicesByType(); HashMap itemTypeMap = new HashMap<>(); itemTypeMap.put("1-1", "高清网络枪型固定摄像机"); itemTypeMap.put("1-2", "高清网络球形摄像机"); @@ -122,6 +153,7 @@ public class StatusController extends BaseController { Status status = new Status(); status.setStartTime(todayStart); status.setTime(currentTime); + status.setUseState(1); List listStatus = statusService.list(status); //根据时间分组 Map> map = listStatus.stream() @@ -151,8 +183,10 @@ public class StatusController extends BaseController { maps.put("sucessRate",String.format("%.2f", sucessRate)+"%"); //离线率 maps.put("failRate",failRate); + //已使用数量 + maps.put("sumUseState",String.valueOf(groupItems.size())); //总数 - maps.put("sum",String.valueOf(groupItems.size())); + maps.put("sum", String.valueOf(dcDeviceList.stream().filter(item -> Objects.equals(item.getDeviceType(), entrys.getKey())).map(DcDevice::getSumAll).collect(Collectors.toList()).get(0))); if(itemTypeMap.get(entrys.getKey())!=null) { subMap.put(itemTypeMap.get(entrys.getKey()), maps); } diff --git a/zc-business/src/main/java/com/zc/business/controller/WeatherForecastController.java b/zc-business/src/main/java/com/zc/business/controller/WeatherForecastController.java index 07becb21..9ff01fbe 100644 --- a/zc-business/src/main/java/com/zc/business/controller/WeatherForecastController.java +++ b/zc-business/src/main/java/com/zc/business/controller/WeatherForecastController.java @@ -103,7 +103,7 @@ public class WeatherForecastController extends BaseController { } } } catch (Exception e) { - return AjaxResult.error(500, "连接异常,请检查网络"); + //return AjaxResult.error(500, "连接异常,请检查网络"); } } } @@ -186,7 +186,7 @@ public class WeatherForecastController extends BaseController { } } catch (Exception e) { - return AjaxResult.error(500, "连接异常,请检查网络"); + //return AjaxResult.error(500, "连接异常,请检查网络"); } } diff --git a/zc-business/src/main/java/com/zc/business/domain/DcDevice.java b/zc-business/src/main/java/com/zc/business/domain/DcDevice.java index ee3d8a32..8600ebde 100644 --- a/zc-business/src/main/java/com/zc/business/domain/DcDevice.java +++ b/zc-business/src/main/java/com/zc/business/domain/DcDevice.java @@ -39,7 +39,7 @@ public class DcDevice { @ApiModelProperty("设备编号") private String deviceCode; @ApiModelProperty("设备类型") - private Integer deviceType; + private String deviceType; @ApiModelProperty("设备图片") private String deviceImg; @ApiModelProperty("安装日期") @@ -73,6 +73,8 @@ public class DcDevice { @TableField(exist = false) private String longitude; @TableField(exist = false) + private String sumAll; + @TableField(exist = false) private String latitude; //设备厂商 @TableField(exist = false) diff --git a/zc-business/src/main/java/com/zc/business/domain/Status.java b/zc-business/src/main/java/com/zc/business/domain/Status.java index 0b79747c..2d74b95a 100644 --- a/zc-business/src/main/java/com/zc/business/domain/Status.java +++ b/zc-business/src/main/java/com/zc/business/domain/Status.java @@ -117,6 +117,8 @@ public class Status { @Excel(name = "状态") private int deviceStatus; + @Excel(name = "使用状态") + private int useState; @@ -228,4 +230,11 @@ public class Status { private String type; + public int getUseState() { + return useState; + } + + public void setUseState(int useState) { + this.useState = useState; + } } diff --git a/zc-business/src/main/java/com/zc/business/mapper/DcDeviceMapper.java b/zc-business/src/main/java/com/zc/business/mapper/DcDeviceMapper.java index c9250885..1318201f 100644 --- a/zc-business/src/main/java/com/zc/business/mapper/DcDeviceMapper.java +++ b/zc-business/src/main/java/com/zc/business/mapper/DcDeviceMapper.java @@ -15,4 +15,7 @@ import java.util.List; public interface DcDeviceMapper extends BaseMapper { List selectDcDeviceList(DcDevice dcDevice); + List numberOfDevicesByType(); + + } diff --git a/zc-business/src/main/java/com/zc/business/service/IDcDeviceService.java b/zc-business/src/main/java/com/zc/business/service/IDcDeviceService.java index 8bd23a34..5e401f43 100644 --- a/zc-business/src/main/java/com/zc/business/service/IDcDeviceService.java +++ b/zc-business/src/main/java/com/zc/business/service/IDcDeviceService.java @@ -77,4 +77,6 @@ public interface IDcDeviceService extends IService { Long statisticalAnomalyDevice(); List devicePileNumberQueryDevice(Map parameter); + + List numberOfDevicesByType(); } 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 36111ac8..4b9cea05 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 @@ -401,7 +401,7 @@ public class DcDeviceServiceImpl extends ServiceImpl i device.setChildType(childType); } if (!Objects.equals(deviceType, "null")) { - device.setDeviceType(Integer.valueOf(deviceType)); + device.setDeviceType(deviceType); } if (!Objects.equals(deviceState, "null")) { device.setDeviceState(deviceState); @@ -446,6 +446,11 @@ public class DcDeviceServiceImpl extends ServiceImpl i return dcDevices; } + @Override + public List numberOfDevicesByType() { + return dcDeviceMapper.numberOfDevicesByType(); + } + public static List castList(Object obj, Class clazz) { List result = new ArrayList(); if (obj instanceof List) { diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcWeatherMonitoringServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcWeatherMonitoringServiceImpl.java index 2b344579..5b2363b9 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcWeatherMonitoringServiceImpl.java +++ b/zc-business/src/main/java/com/zc/business/service/impl/DcWeatherMonitoringServiceImpl.java @@ -45,7 +45,7 @@ public class DcWeatherMonitoringServiceImpl implements IDcWeatherMonitoringServi DcDevice dcDevice = new DcDevice(); - dcDevice.setDeviceType(3); + dcDevice.setDeviceType("3"); List deviceList = dcDeviceService.listDevice(dcDevice); //正常路段里程 diff --git a/zc-business/src/main/resources/mapper/business/DcDeviceMapper.xml b/zc-business/src/main/resources/mapper/business/DcDeviceMapper.xml index b9c350de..c7e78309 100644 --- a/zc-business/src/main/resources/mapper/business/DcDeviceMapper.xml +++ b/zc-business/src/main/resources/mapper/business/DcDeviceMapper.xml @@ -105,4 +105,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + diff --git a/zc-business/src/main/resources/mapper/business/StatusMapper.xml b/zc-business/src/main/resources/mapper/business/StatusMapper.xml index 4943b263..9c40d3bf 100644 --- a/zc-business/src/main/resources/mapper/business/StatusMapper.xml +++ b/zc-business/src/main/resources/mapper/business/StatusMapper.xml @@ -99,6 +99,12 @@ AND (d.device_type = #{status.type} or d.child_type=#{status.type}) + + AND s.device_id = #{status.deviceId} + + + AND d.use_state = #{status.useState} + From 7271f0f08bea924b65bf229d6bcf32a1b599d258 Mon Sep 17 00:00:00 2001 From: zhaoxianglong Date: Wed, 20 Mar 2024 14:04:36 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E5=BC=82=E5=B8=B8=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E6=B7=BB=E5=8A=A0=E7=AD=9B=E9=80=89=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/zc/business/service/impl/DcDeviceServiceImpl.java | 1 + 1 file changed, 1 insertion(+) 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 4b9cea05..0fb6b647 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 @@ -384,6 +384,7 @@ public class DcDeviceServiceImpl extends ServiceImpl i public Long statisticalAnomalyDevice() { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); lambdaQueryWrapper.eq(DcDevice::getDeviceState, 0).or().isNull(DcDevice::getDeviceState); + lambdaQueryWrapper.eq(DcDevice::getUseState,1); return (long) list(lambdaQueryWrapper).size(); } From 410ca9d444cafb09d9fdc977630012ae1a1cdef0 Mon Sep 17 00:00:00 2001 From: xiepufeng <1072271977@qq.com> Date: Wed, 20 Mar 2024 16:28:22 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E4=BA=A4=E9=80=9A=E6=96=AD=E9=9D=A2?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/zc/common/core/httpclient/OkHttp.java | 15 +- .../StatisticalRecoveryOffsetTime.java | 14 + .../controller/DcDeviceController.java | 24 ++ .../java/com/zc/business/domain/DcDevice.java | 21 + .../business/domain/DcTrafficSectionData.java | 24 +- .../zc/business/enums/DeviceDataCategory.java | 19 + .../enums/IotProductPropertiesEnum.java | 28 ++ .../com/zc/business/enums/LaneDirection.java | 34 ++ .../enums/TrafficDataPeriodTypeEnum.java | 8 +- .../device/handler/DeviceMessageHandler.java | 6 +- .../service/DcTrafficSectionDataService.java | 4 +- .../impl/DcTrafficSectionDataServiceImpl.java | 406 ++++++++++++++++-- .../cache/DailyTrafficStatisticsCache.java | 5 + .../cache/MonthlyTrafficStatisticsCache.java | 5 + .../QuarterlyTrafficStatisticsCache.java | 5 + .../cache/YearlyTrafficStatisticsCache.java | 5 + .../handler/RealtimeTrafficStatistics.java | 2 + 17 files changed, 554 insertions(+), 71 deletions(-) create mode 100644 zc-business/src/main/java/com/zc/business/constant/StatisticalRecoveryOffsetTime.java create mode 100644 zc-business/src/main/java/com/zc/business/enums/DeviceDataCategory.java create mode 100644 zc-business/src/main/java/com/zc/business/enums/IotProductPropertiesEnum.java create mode 100644 zc-business/src/main/java/com/zc/business/enums/LaneDirection.java diff --git a/ruoyi-common/src/main/java/com/zc/common/core/httpclient/OkHttp.java b/ruoyi-common/src/main/java/com/zc/common/core/httpclient/OkHttp.java index 782d845e..6cd209aa 100644 --- a/ruoyi-common/src/main/java/com/zc/common/core/httpclient/OkHttp.java +++ b/ruoyi-common/src/main/java/com/zc/common/core/httpclient/OkHttp.java @@ -11,6 +11,7 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.Type; import java.util.Map; +import java.util.Objects; import java.util.concurrent.TimeUnit; /** @@ -185,18 +186,16 @@ public class OkHttp throw new HttpException("OkHttp: url 不存在!"); } - StringBuilder urlBuilder = new StringBuilder(url).append("?"); + HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder(); if (requestParams != null) { - requestParams.params.forEach((key, value) -> - { - urlBuilder.append(key).append("=").append(toJson(value)).append("&"); - - }); + requestParams.params.forEach((key, value) -> urlBuilder.addQueryParameter(key, toJson(value))); } - Call call = okHttpClient.newCall(requestBuilder.url(urlBuilder.substring(0, urlBuilder.length() - 1)).get().build()); + String url = urlBuilder.build().toString(); + + Call call = okHttpClient.newCall(requestBuilder.url(url).get().build()); return call.execute(); } @@ -437,4 +436,4 @@ public class OkHttp return gson.toJson(object, type); } -} \ No newline at end of file +} diff --git a/zc-business/src/main/java/com/zc/business/constant/StatisticalRecoveryOffsetTime.java b/zc-business/src/main/java/com/zc/business/constant/StatisticalRecoveryOffsetTime.java new file mode 100644 index 00000000..78117d8d --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/constant/StatisticalRecoveryOffsetTime.java @@ -0,0 +1,14 @@ +package com.zc.business.constant; + +/** + * 统计恢复偏移时间类 + * 用于定义与交通数据恢复相关的偏移时间常量。 + * + * @author xiepufeng + */ +public class StatisticalRecoveryOffsetTime { + + // 定义交通数据段偏移的天数常量,表示偏移-10天 + public static final int TRAFFIC_SECTION_DATA_OFFSET_DAY = -10; +} + 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 41b68888..1e6f55f3 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 @@ -161,6 +161,30 @@ public class DcDeviceController extends BaseController { //***********************************物联设备接口************************************** + + /** + * 根据物联产品id获取设备数据 + * + * @param productId 物联产品id + * @return 获取设备数据操作结果 + */ + @ApiOperation("根据物联产品id获取设备数据") + @GetMapping("/devices/{productId}") + public AjaxResult getDeviceByProductId(@PathVariable @Parameter(description = "产品ID") String productId) throws HttpException, IOException { + + if (!StringUtils.hasText(productId)) { + return AjaxResult.error("产品ID不能为空"); + } + + OkHttp okHttp = new OkHttp(); + Response response // 请求响应 + = okHttp + .url(iotAddress + "/api/iot/device/cache/" + productId) // 请求地址 + .get(); // 请求方法 + return JSON.parseObject(response.body().string(), AjaxResult.class); + } + + /** * 根据物联设备id获取最新属性数据 * diff --git a/zc-business/src/main/java/com/zc/business/domain/DcDevice.java b/zc-business/src/main/java/com/zc/business/domain/DcDevice.java index ee3d8a32..dbcc7fe8 100644 --- a/zc-business/src/main/java/com/zc/business/domain/DcDevice.java +++ b/zc-business/src/main/java/com/zc/business/domain/DcDevice.java @@ -77,4 +77,25 @@ public class DcDevice { //设备厂商 @TableField(exist = false) private String manufacturer; + + public Integer stakeMarkToInt() { + if (stakeMark == null) { + return null; + } + + // 不区分大小写的正则表达式匹配 'k' 和 '+' + String[] parts = this.stakeMark.split("(?i)k|\\+"); + + // 提取出公里数部分和米数部分 + String kmStr = parts[1].trim(); + // 将公里数和米数转换为整数 + int km = Integer.parseInt(kmStr); + int m = 0; + if (parts.length == 3) { + String mStr = parts[2].trim(); + m = Integer.parseInt(mStr); + } + // 计算总米数 + return km * 1000 + m; + } } diff --git a/zc-business/src/main/java/com/zc/business/domain/DcTrafficSectionData.java b/zc-business/src/main/java/com/zc/business/domain/DcTrafficSectionData.java index 3edeb8e9..00c5efe9 100644 --- a/zc-business/src/main/java/com/zc/business/domain/DcTrafficSectionData.java +++ b/zc-business/src/main/java/com/zc/business/domain/DcTrafficSectionData.java @@ -1,6 +1,7 @@ package com.zc.business.domain; import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.fasterxml.jackson.annotation.JsonFormat; import com.zc.business.enums.TrafficDataPeriodTypeEnum; @@ -43,6 +44,12 @@ public class DcTrafficSectionData { */ private Date statisticalDate; + /** + * 上报时间 + */ + @TableField(exist = false) + private Date reportTime; + /** * 道路方向 */ @@ -90,7 +97,7 @@ public class DcTrafficSectionData { if (o == null || getClass() != o.getClass()) return false; // 对象为空或类型不同,返回false DcTrafficSectionData that = (DcTrafficSectionData) o; // 类型转换 return Objects.equals(deviceId, that.deviceId) && - Objects.equals(statisticalDate, that.statisticalDate) && + Objects.equals(reportTime, that.reportTime) && Objects.equals(direction, that.direction) && Objects.equals(periodType, that.periodType) && Objects.equals(stakeMark, that.stakeMark); // 比较各属性值 @@ -102,16 +109,7 @@ public class DcTrafficSectionData { */ @Override public int hashCode() { - return Objects.hash(deviceId, statisticalDate, direction, periodType, stakeMark); - } - - /** - * 设置统计日期,根据不同的统计周期类型来调整日期,使其对应周期的起始日期。 - * @param statisticalDate 统计日期,原始日期。 - */ - public void setStatisticalDate(Date statisticalDate) { - TrafficDataPeriodTypeEnum typeEnum = TrafficDataPeriodTypeEnum.valueOfCode(periodType); - setStatisticalDate(statisticalDate, typeEnum); + return Objects.hash(deviceId, reportTime, direction, periodType, stakeMark); } /** @@ -144,10 +142,10 @@ public class DcTrafficSectionData { } /** - * 根据设备ID、统计时间、方向、时段类型、桩号生成一个唯一ID + * 根据设备ID、上报时间、方向、时段类型、桩号生成一个唯一ID */ public void generateUniqueId() { - String combinedAttributes = deviceId + "_" + DateUtil.format(statisticalDate, "yyyyMMdd_HHmmss") + "_" + direction + "_" + periodType + "_" + stakeMark; + String combinedAttributes = deviceId + "_" + DateUtil.format(reportTime, "yyyyMMdd_HHmmss") + "_" + direction + "_" + periodType + "_" + stakeMark; this.id = DigestUtils.md5Hex(combinedAttributes); } diff --git a/zc-business/src/main/java/com/zc/business/enums/DeviceDataCategory.java b/zc-business/src/main/java/com/zc/business/enums/DeviceDataCategory.java new file mode 100644 index 00000000..82bd185a --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/enums/DeviceDataCategory.java @@ -0,0 +1,19 @@ +package com.zc.business.enums; + +import lombok.Getter; + +/** + * @author xiepufeng + */ +@Getter +public enum DeviceDataCategory { + REAL_TIME("实时数据"), + HISTORY("历史数据"); + + private final String description; + + DeviceDataCategory(String description) { + this.description = description; + } + +} diff --git a/zc-business/src/main/java/com/zc/business/enums/IotProductPropertiesEnum.java b/zc-business/src/main/java/com/zc/business/enums/IotProductPropertiesEnum.java new file mode 100644 index 00000000..73658394 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/enums/IotProductPropertiesEnum.java @@ -0,0 +1,28 @@ +package com.zc.business.enums; + +/** + * 产品属性枚举 + * @author xiepufeng + */ +public enum IotProductPropertiesEnum { + + /** + * 一站式产品属性01 + */ + ONE_STOP_PRODUCT_01("01"), + + /** + * 一站式产品属性11 + */ + ONE_STOP_PRODUCT_11("11"); + + private final String number; + + IotProductPropertiesEnum(String number) { + this.number = number; + } + + public String getNumber() { + return this.number; + } +} diff --git a/zc-business/src/main/java/com/zc/business/enums/LaneDirection.java b/zc-business/src/main/java/com/zc/business/enums/LaneDirection.java new file mode 100644 index 00000000..ee51bfe8 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/enums/LaneDirection.java @@ -0,0 +1,34 @@ +package com.zc.business.enums; + + +import lombok.Getter; + +/** + * 道路方向 + * @author xiepufeng + **/ +@Getter +public enum LaneDirection { + + UPWARD((byte)1, "上行"), + BIDIRECTIONAL((byte) 2, "上下行(双向)"), + DOWNWARD((byte)3, "下行"); + + private final byte value; + private final String description; + + LaneDirection(byte value, String description) { + this.value = value; + this.description = description; + } + + + public static LaneDirection fromValue(int value) { + for (LaneDirection direction : values()) { + if (direction.getValue() == value) { + return direction; + } + } + throw new IllegalArgumentException("Invalid value for LaneDirection: " + value); + } +} diff --git a/zc-business/src/main/java/com/zc/business/enums/TrafficDataPeriodTypeEnum.java b/zc-business/src/main/java/com/zc/business/enums/TrafficDataPeriodTypeEnum.java index 82b9fe09..796e6311 100644 --- a/zc-business/src/main/java/com/zc/business/enums/TrafficDataPeriodTypeEnum.java +++ b/zc-business/src/main/java/com/zc/business/enums/TrafficDataPeriodTypeEnum.java @@ -10,11 +10,11 @@ public enum TrafficDataPeriodTypeEnum { // 枚举成员:年,关联字节型代码 1 和描述 "年" YEAR((byte) 1, "年"), - // 枚举成员:月,关联字节型代码 2 和描述 "月" - MONTH((byte) 2, "月"), - // 枚举成员:季,关联字节型代码 3 和描述 "季" - QUARTER((byte) 3, "季"), + QUARTER((byte) 2, "季"), + + // 枚举成员:月,关联字节型代码 2 和描述 "月" + MONTH((byte) 3, "月"), // 枚举成员:日,关联字节型代码 4 和描述 "日" DAY((byte) 4, "日"); 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 eec32d66..48cc74a7 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 @@ -57,8 +57,6 @@ public class DeviceMessageHandler { @Autowired private IDcMeteorologicalDetectorDataService meteorologicalDetectorDataService; - @Autowired - private IDcYzsqbdcHmbldDataService dcYzsqbdcHmbldDataService; /** * 更新设备状态 @@ -112,7 +110,7 @@ public class DeviceMessageHandler { return; } - // 一站式情况调查站 + // 气象检测器 if (IotProductEnum.WEATHER_DETECTOR.value().equals(productId)) { weatherDetectorMessageHandle(data); } @@ -329,7 +327,7 @@ public class DeviceMessageHandler { * @param msg 设备消息 */ private void oneStopDeviceMessageHandle(JSONObject msg) { - dcTrafficSectionDataService.processRealtimeMessage(msg); + // dcTrafficSectionDataService.processRealtimeOneStopMessage(msg); } diff --git a/zc-business/src/main/java/com/zc/business/service/DcTrafficSectionDataService.java b/zc-business/src/main/java/com/zc/business/service/DcTrafficSectionDataService.java index 868f01ca..7e3db831 100644 --- a/zc-business/src/main/java/com/zc/business/service/DcTrafficSectionDataService.java +++ b/zc-business/src/main/java/com/zc/business/service/DcTrafficSectionDataService.java @@ -7,9 +7,9 @@ import com.zc.business.domain.DcTrafficSectionData; public interface DcTrafficSectionDataService extends IService { /** - * 处理实时接收到的设备消息,并将其转换为交通断面统计数据对象并缓存。 + * 处理实时接收到的一类交流站设备消息,并将其转换为交通断面统计数据对象并缓存。 * * @param msg 设备发送的JSON格式实时消息 */ - void processRealtimeMessage(JSONObject msg); + void processRealtimeOneStopMessage(JSONObject msg); } diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficSectionDataServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficSectionDataServiceImpl.java index 431fe06b..0548a7f6 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficSectionDataServiceImpl.java +++ b/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficSectionDataServiceImpl.java @@ -1,23 +1,31 @@ package com.zc.business.service.impl; import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSONArray; 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.redis.RedisCache; +import com.zc.business.constant.RedisKeyConstants; +import com.zc.business.constant.StatisticalRecoveryOffsetTime; +import com.zc.business.controller.DcDeviceController; +import com.zc.business.domain.DcDevice; import com.zc.business.domain.DcTrafficSectionData; -import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.enums.*; import com.zc.business.statistics.cache.*; import com.zc.business.mapper.DcTrafficSectionDataMapper; import com.zc.business.service.DcTrafficSectionDataService; import com.zc.business.statistics.handler.RealtimeTrafficStatistics; +import com.zc.common.core.httpclient.exception.HttpException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.Resource; -import java.util.Date; -import java.util.List; -import java.util.Map; +import java.io.IOException; +import java.util.*; import java.util.function.Consumer; /** @@ -30,21 +38,197 @@ public class DcTrafficSectionDataServiceImpl extends ServiceImpl implements DcTrafficSectionDataService { + // 日志记录器 + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + @Resource private DcTrafficSectionDataMapper dcTrafficSectionDataMapper; + @Resource + private RedisCache redisCache; + + @Resource + private DcDeviceController dcDeviceController; + /** * 初始化方法,用于在对象创建后恢复各种周期的交通数据缓存。 * 该方法标注了@PostConstruct注解,确保在依赖注入完成后调用。 */ @PostConstruct public void init() { - // TODO 恢复每天交通数据缓存(es获取数据) + recoveryDailyCache(); // 从es中恢复当天交通数据缓存 recoveryMonthlyCache(); // 恢复每月交通数据缓存 recoveryQuarterlyCache(); // 恢复每季度交通数据缓存 recoveryYearlyCache(); // 恢复每年交通数据缓存 } + /** + * 处理实时接收到的一类交流站设备消息,并将其转换为交通断面统计数据对象并缓存。 + * + * @param msg 设备发送的JSON格式实时消息 + */ + @Override + public void processRealtimeOneStopMessage(JSONObject msg) { + + // 1. 将设备消息转换为交通断面数据统计定义对象 + List dcTrafficSectionDataList = convertToTrafficStatistics(msg, DeviceDataCategory.REAL_TIME); + + if (dcTrafficSectionDataList != null && !dcTrafficSectionDataList.isEmpty()) { + // 2. 将转换后的数据添加到缓存中 + dcTrafficSectionDataList.forEach(DailyTrafficStatisticsCache::addCacheData); + } + } + + /** + * 定义每小时第20分钟执行的任务,用于清除过期缓存数据并将缓存中的数据整合后保存至数据库。 + */ + // @Scheduled(cron = "0 20 * * * ?") // 每小时的20分整点执行该任务 + public void performHourlyCleanupAndPersist() { + // 清除已过期的缓存数据 + DailyTrafficStatisticsCache.clearExpiredData(); + MonthlyTrafficStatisticsCache.clearExpiredData(); + QuarterlyTrafficStatisticsCache.clearExpiredData(); + YearlyTrafficStatisticsCache.clearExpiredData(); + // 整合缓存数据并保存至数据库 + // 将缓存中的数据按日统计后保存至数据库 + // 添加月交通断面数据到缓存中 + persistAggregatedData(DailyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyTrafficStatisticsCache::addCacheData); + // 将缓存中的数据按月统计后保存至数据库 + // 添加季度交通断面数据到缓存中 + persistAggregatedData(MonthlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyTrafficStatisticsCache::addCacheData); + // 将缓存中的数据按季度统计后保存至数据库 + // 添加年交通断面数据到缓存中 + persistAggregatedData(QuarterlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyTrafficStatisticsCache::addCacheData); + // 将缓存中的数据按年统计后保存至数据库 + persistAggregatedData(YearlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {}); + } + + /** + * 恢复每日缓存的函数。 + * 该方法尝试从物联平台获取所有设备信息,并对这些信息进行处理。 + * 如果获取信息失败或处理过程中发生异常,则记录错误信息。 + */ + private void recoveryDailyCache() { + + try { + // 尝试从指定产品ID获取设备信息 + Map oneStopDeviceMap = dcDeviceController.getDeviceByProductId(IotProductEnum.ONE_STOP_PRODUCT.value()); + + // 检查获取的设备信息是否为空 + if (oneStopDeviceMap == null || oneStopDeviceMap.get("data") == null) { + logger.error("获取一站式设备数据失败,产品id:{}", IotProductEnum.ONE_STOP_PRODUCT.value()); + return; + } + + // 将获取的设备信息转换为JSON数组,并遍历处理每个设备的数据 + JSONArray deviceJsonArray = JSONArray.parseArray(oneStopDeviceMap.get("data").toString()); + deviceJsonArray.forEach(this::processDeviceData); + + } catch (HttpException | IOException e) { + // 记录处理设备数据时发生的异常 + logger.error("处理设备数据时发生异常", e); + } + } + + /** + * 处理设备数据的函数。 + * 该方法首先将传入的设备对象转换为JSON对象,然后从JSON对象中提取设备ID。 + * 如果设备ID有效,则查询设备的实时流量数据,并对获取的设备属性数据进行处理。 + * + * @param deviceObject 设备对象,需要是一个JSONObject,包含设备信息。 + */ + private void processDeviceData(Object deviceObject) { + JSONObject deviceJsonObject = (JSONObject)deviceObject; + // 提取设备ID + String deviceId = deviceJsonObject.getString("id"); + + // 检查设备ID的有效性 + if (deviceId == null || deviceId.isEmpty()) { + logger.error("获取一站式设备id失败,产品id:{}", IotProductEnum.ONE_STOP_PRODUCT.value()); + return; + } + + // 查询并处理设备属性数据 + try { + HashMap props = buildPropertiesRequiredParameter(); + + // 查询设备的实时属性数据 + Map deviceProperties = dcDeviceController.queryDeviceProperties(deviceId, IotProductPropertiesEnum.ONE_STOP_PRODUCT_01.getNumber(), props); + + // 对获取的设备属性数据进行进一步处理 + processDeviceProperties(deviceProperties); + } catch (HttpException | IOException e) { + // 记录处理设备属性数据时发生的异常 + logger.error("处理设备属性数据时发生异常,设备id:{}", deviceId, e); + } + } + + + /** + * 构建必需的属性请求参数集合。 + * 该方法不接受任何参数,返回一个包含特定键值对的HashMap对象。 + * 键值对表达了查询条件,其中键是查询条件的结构化字符串表示,值是条件的具体值。 + * + * @return HashMap 包含查询条件的HashMap对象。 + */ + private HashMap buildPropertiesRequiredParameter() { + + // todo 数据库中查询出最大的时间 + + HashMap props = new HashMap<>(); + // 设置查询条件的键为“timestamp$BTW”,表示时间戳在一定范围内 + props.put("terms[0].column", "timestamp$BTW"); + ArrayList dateList = new ArrayList<>(); + // 添加当前日期的开始和结束时间到列表,用于设定时间范围 + dateList.add(DateUtil.beginOfDay(DateUtil.offsetDay(new Date(), StatisticalRecoveryOffsetTime.TRAFFIC_SECTION_DATA_OFFSET_DAY)).toString()); + dateList.add(DateUtil.endOfDay(new Date()).toString()); + // 将日期列表以逗号分隔并设置为查询条件的值 + props.put("terms[0].value", String.join(",", dateList)); + props.put("paging", false); + return props; + } + + /** + * 处理设备属性信息。 + * 该方法接收一个设备属性的映射,从中提取属性数据,并将其转换为交通断面数据统计定义对象列表,然后将这些数据添加到缓存中。 + * + * @param deviceProperties 包含设备属性数据的映射。该映射应包含一个名为"data"的键,其值是一个包含具体属性信息的JSON对象。 + */ + private void processDeviceProperties(Map deviceProperties) { + // 检查传入的设备属性映射是否为空,或者其中的"data"键对应的值是否为空 + if (deviceProperties == null || deviceProperties.get("data") == null) { + logger.error("获取一站式属性数据失败,产品id:{}", IotProductEnum.ONE_STOP_PRODUCT.value()); + return; + } + + // 从设备属性映射的"data"值中解析出JSON对象 + JSONObject propertiesObject = JSONObject.parseObject(deviceProperties.get("data").toString()); + + // 检查解析出的JSON对象是否包含"data"键 + if (propertiesObject.get("data") == null) { + logger.error("获取一站式属性数据失败,产品id:{}", IotProductEnum.ONE_STOP_PRODUCT.value()); + return; + } + + // 从解析出的JSON对象中获取设备属性数据列表 + JSONArray properties = JSONArray.parseArray(propertiesObject.get("data").toString()); + + // 遍历设备属性数据列表,对每个属性对象进行处理 + properties.forEach(propertyObject -> { + JSONObject propertyJsonObject = (JSONObject)propertyObject; + + // 将设备消息转换为交通断面数据统计定义对象 + List dcTrafficSectionDataList = convertToTrafficStatistics(propertyJsonObject, DeviceDataCategory.HISTORY); + + // 如果转换结果非空且不为空列表,则将转换后的数据添加到缓存中 + if (dcTrafficSectionDataList != null && !dcTrafficSectionDataList.isEmpty()) { + // 将转换后的数据添加到缓存中 + dcTrafficSectionDataList.forEach(DailyTrafficStatisticsCache::addCacheData); + } + + }); + } + /** * 恢复每月交通数据缓存的方法。 * 通过查询当前月份至今的每日交通数据,并将其添加到每月交通统计缓存中。 @@ -87,60 +271,202 @@ public class DcTrafficSectionDataServiceImpl dcTrafficSectionDataList.forEach(YearlyTrafficStatisticsCache::addCacheData); } + /** - * 处理实时接收到的设备消息,并将其转换为交通断面统计数据对象并缓存。 + * 将设备消息转换为交通断面数据统计定义对象 * - * @param msg 设备发送的JSON格式实时消息 + * @param msg JSON格式的设备实时消息 + * @param category 设备数据类型 + * @return 转换后的交通断面数据统计定义对象 */ - @Override - public void processRealtimeMessage(JSONObject msg) { - // 1. 将设备消息转换为交通断面数据统计定义对象 - DcTrafficSectionData dcTrafficSectionData = convertToTrafficStatistics(msg); + public List convertToTrafficStatistics(JSONObject msg, DeviceDataCategory category) { + // 获取属性 + JSONObject property; + + if (category == DeviceDataCategory.REAL_TIME) { + JSONObject properties = msg.getJSONObject("properties"); + + // 属性数据 + if (properties == null) { + logger.error("接收实时属性数据,属性数据不存在,请检查物联网一站式设备数据是否正常"); + return null; + } + + // 获取属性01的数据 + property = properties.getJSONObject(IotProductPropertiesEnum.ONE_STOP_PRODUCT_01.getNumber()); + } else { + property = msg.getJSONObject("value"); + } + + // 判断是否属性01的数据 + if (property == null) { + logger.error("非属性01的数据,无法处理,请检查物联网一站式设备数据是否正常"); + return null; + } + + // 物联设备id + String iotDeviceId = msg.getString("deviceId"); + + if (iotDeviceId == null || iotDeviceId.isEmpty()) { + logger.error("设备id为空,无法处理,请检查物联网平台一站式设备数据是否正常"); + return null; + } + + // 上报时间(时间戳) + Long timestamp = msg.getLong("timestamp"); + + if (timestamp == null || timestamp == 0L) { + logger.error("上报时间为空,无法处理,请检查物联网平台一站式设备数据是否正常"); + return null; + } + + // 从缓存中获取设备信息 + DcDevice dcDevice = redisCache.getCacheMapValue(RedisKeyConstants.DC_DEVICES, iotDeviceId); + + if (dcDevice == null) { + logger.error("设备信息不存在,请检查是否配置设备信息,物联平台设备id:{}无对应设备信息", iotDeviceId); + return null; + } - // 2. 将转换后的数据添加到缓存中 - // DailyTrafficStatisticsCache.addCacheData(dcTrafficSectionData); + // 上报时间 + Date reportTime = DateUtil.date(timestamp); + + // 车道信息 + JSONArray lanes = property.getJSONArray("lanes"); + + if (lanes == null || lanes.isEmpty()) { + logger.error("车道信息为空,无法处理,请检查物联网平台一站式设备数据是否正常"); + return null; + } + + // 分别处理上行和下行数据 + return new ArrayList<>(processLaneData(lanes, dcDevice, reportTime).values()); } /** - * 将设备实时消息转换为交通断面数据统计定义对象 + * 处理车道数据,计算上行和下行的车流量和累计平均速度。 * - * @param msg JSON格式的设备实时消息 - * @return 转换后的交通断面数据统计定义对象 + * @param lanes 车道数据的JSON数组,包含各个车道的信息。 + * @param dcDevice 设备信息,用于标识数据来源。 + * @param reportTime 报告时间,标识数据的时间戳。 + * @return 返回一个Map,包含上行和下行的交通段数据。 */ - public DcTrafficSectionData convertToTrafficStatistics(JSONObject msg) { + private Map processLaneData(JSONArray lanes, DcDevice dcDevice, Date reportTime) { + + Map resultMap = new HashMap<>(); + + // 初始化上行和下行的交通段数据 + DcTrafficSectionData upwardData = new DcTrafficSectionData(); + DcTrafficSectionData downwardData = new DcTrafficSectionData(); + + initializeTrafficSectionData(upwardData, dcDevice, reportTime, LaneDirection.UPWARD); + initializeTrafficSectionData(downwardData, dcDevice, reportTime, LaneDirection.DOWNWARD); + // 初始化上行和下行的车流量和累计平均速度 + int upwardTrafficVolume = 0, downwardTrafficVolume = 0; + double upwardCumulativeAverageSpeed = 0.0, downwardCumulativeAverageSpeed = 0.0; + // 遍历车道信息,计算上行和下行的车流量和累计平均速度 + for (Object lane : lanes) { + // 解析车道数据 + JSONObject laneData = (JSONObject) lane; + // 获取车道号,并判断是否为下行车道 + Integer laneNumber = laneData.getInteger("laneNumber"); + boolean isDownward = laneNumber >= 31 || laneNumber == 3; - DcTrafficSectionData dcTrafficSectionData = new DcTrafficSectionData(); + // 根据车道方向累加车流量和累计平均速度 + int totalTrafficFlow = laneData.getInteger("totalTrafficFlow"); + double cumulativeAverageSpeed = calculateCumulativeAverageSpeed(laneData); - // TODO - return dcTrafficSectionData; + if (isDownward) { + downwardTrafficVolume += totalTrafficFlow; + downwardCumulativeAverageSpeed += cumulativeAverageSpeed; + + } else { + upwardTrafficVolume += totalTrafficFlow; + upwardCumulativeAverageSpeed += cumulativeAverageSpeed; + } + } + // 设置上行和下行的车流量和累计平均速度 + setUpTrafficSectionData(upwardData, upwardTrafficVolume, upwardCumulativeAverageSpeed); + setUpTrafficSectionData(downwardData, downwardTrafficVolume, downwardCumulativeAverageSpeed); + + // 将上行和下行的交通段数据放入结果映射中 + resultMap.put(LaneDirection.UPWARD, upwardData); + resultMap.put(LaneDirection.DOWNWARD, downwardData); + + return resultMap; } /** - * 定义每小时第20分钟执行的任务,用于清除过期缓存数据并将缓存中的数据整合后保存至数据库。 + * 初始化交通段数据 + * @param data 交通段数据对象,用于存储交通数据 + * @param dcDevice 设备对象,包含设备的详细信息 + * @param reportTime 数据上报时间 + * @param direction 车道方向 */ -// @Scheduled(cron = "0 20 * * * ?") // 每小时的20分整点执行该任务 - public void performHourlyCleanupAndPersist() { - // 清除已过期的缓存数据 - DailyTrafficStatisticsCache.clearExpiredData(); - MonthlyTrafficStatisticsCache.clearExpiredData(); - QuarterlyTrafficStatisticsCache.clearExpiredData(); - YearlyTrafficStatisticsCache.clearExpiredData(); - // 整合缓存数据并保存至数据库 - // 将缓存中的数据按日统计后保存至数据库 - // 添加月交通断面数据到缓存中 - persistAggregatedData(DailyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyTrafficStatisticsCache::addCacheData); - // 将缓存中的数据按月统计后保存至数据库 - // 添加季度交通断面数据到缓存中 - persistAggregatedData(MonthlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyTrafficStatisticsCache::addCacheData); - // 将缓存中的数据按季度统计后保存至数据库 - // 添加年交通断面数据到缓存中 - persistAggregatedData(QuarterlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyTrafficStatisticsCache::addCacheData); - // 将缓存中的数据按年统计后保存至数据库 - persistAggregatedData(YearlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {}); + private void initializeTrafficSectionData(DcTrafficSectionData data, DcDevice dcDevice, Date reportTime, LaneDirection direction) { + + // 设置设备id + data.setDeviceId(dcDevice.getId()); + // 设置车道方向 + data.setDirection(direction.getValue()); + // 设置上报时间 + data.setReportTime(reportTime); + // 设置数据上报时间 + data.setStatisticalDate(reportTime); + // 设置统计时段类型为日 + data.setPeriodType(TrafficDataPeriodTypeEnum.DAY); + // 将设备的桩号转换为整数后设置 + data.setStakeMark(dcDevice.stakeMarkToInt()); + } + + + /** + * 设置交通路段数据。 + * 该方法用于根据给定的交通量和累积平均速度,设置交通路段的数据。 + * + * @param data 交通路段数据对象,用于存储交通路段的相关信息。 + * @param trafficVolume 该路段的交通量,单位通常为车辆数。 + * @param cumulativeAverageSpeed 该路段的累积平均速度,单位通常为千米/小时。 + */ + private void setUpTrafficSectionData(DcTrafficSectionData data, int trafficVolume, double cumulativeAverageSpeed) { + data.setTrafficVolume(trafficVolume); // 设置交通量 + data.setAverageSpeed(0); + if (trafficVolume != 0) { // 当交通量不为0时,计算并设置平均速度 + data.setAverageSpeed((int) Math.round(cumulativeAverageSpeed / trafficVolume)); // 平均速度 = 累积平均速度 / 交通量 + } + } + + /** + * 计算给定车道数据的累计平均速度。 + * 该函数根据laneData中提供的各种车辆类型的数量和平均速度,计算出累计的平均速度。 + * + * @param laneData 包含车道数据的JSONObject对象,其中包含了各种车辆类型的数量和平均速度等数据。 + * @return 返回计算出的累计平均速度。 + */ + private int calculateCumulativeAverageSpeed(JSONObject laneData) { + // 根据laneData中的数据计算累计平均速度 + // 累加平均速度(中小客车) + return laneData.getInteger("trafficNumberOfInAndSmall") * laneData.getInteger("inAndSmallAverageVehicleSpeed") + + // 累加平均速度(小型货车) + laneData.getInteger("trafficVolumeOfSmallTrucks") * laneData.getInteger("smallTrucksAverageVehicleSpeed") + + // 累加平均速度(大客车) + laneData.getInteger("busTrafficVolume") * laneData.getInteger("averageSpeedOfBus") + + // 累加平均速度(中型货车) + laneData.getInteger("mediumTruckTrafficVolume") * laneData.getInteger("averageSpeedOfMediumSizeTrucks") + + // 累加平均速度(大型货车) + laneData.getInteger("largeTruckTrafficVolume") * laneData.getInteger("averageSpeedOfLargeTrucks") + + // 累加平均速度(特大型货车) + laneData.getInteger("extraLargeTrucksTrafficVolume") * laneData.getInteger("averageSpeedOfExtraLargeTrucks") + + // 累加平均速度(集装箱车) + laneData.getInteger("containerTruckTrafficVolume") * laneData.getInteger("averageSpeedOfContainerTruck") + + // 累加平均速度(拖拉机) + laneData.getInteger("tractorTrafficVolume") * laneData.getInteger("averageSpeedOfTractor") + + // 累加平均速度(摩托车) + laneData.getInteger("motorcycleTrafficVolume") * laneData.getInteger("averageSpeedOfMotorcycle"); } /** diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/DailyTrafficStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/DailyTrafficStatisticsCache.java index f2cc4f26..043bbada 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/cache/DailyTrafficStatisticsCache.java +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/DailyTrafficStatisticsCache.java @@ -63,6 +63,11 @@ public class DailyTrafficStatisticsCache extends AbstractTrafficStatisticsCache instance.setStatisticalDateStr(formattedDate); } + // 更新上报时间 + dcTrafficSectionData.setReportTime(dcTrafficSectionData.getStatisticalDate()); + // 更新数据周期类型 + dcTrafficSectionData.setPeriodType(TrafficDataPeriodTypeEnum.DAY); + // 更新统计日期 dcTrafficSectionData.setStatisticalDate(dcTrafficSectionData.getStatisticalDate(), TrafficDataPeriodTypeEnum.DAY); // 将新数据添加到数据列表中 diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/MonthlyTrafficStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/MonthlyTrafficStatisticsCache.java index 41defc79..0d3d5956 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/cache/MonthlyTrafficStatisticsCache.java +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/MonthlyTrafficStatisticsCache.java @@ -63,6 +63,11 @@ public class MonthlyTrafficStatisticsCache extends AbstractTrafficStatisticsCach instance.setStatisticalDateStr(formattedDate); } + // 更新上报时间 + dcTrafficSectionData.setReportTime(dcTrafficSectionData.getStatisticalDate()); + // 更新数据周期类型 + dcTrafficSectionData.setPeriodType(TrafficDataPeriodTypeEnum.MONTH); + // 更新统计日期 dcTrafficSectionData.setStatisticalDate(dcTrafficSectionData.getStatisticalDate(), TrafficDataPeriodTypeEnum.MONTH); // 将新数据添加到数据列表中 diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/QuarterlyTrafficStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/QuarterlyTrafficStatisticsCache.java index 41f1d5ce..5b4d5ef0 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/cache/QuarterlyTrafficStatisticsCache.java +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/QuarterlyTrafficStatisticsCache.java @@ -66,6 +66,11 @@ public class QuarterlyTrafficStatisticsCache extends AbstractTrafficStatisticsCa instance.setStatisticalDateStr(formattedDate); } + // 更新上报时间 + dcTrafficSectionData.setReportTime(dcTrafficSectionData.getStatisticalDate()); + // 更新数据周期类型 + dcTrafficSectionData.setPeriodType(TrafficDataPeriodTypeEnum.QUARTER); + // 更新统计日期 dcTrafficSectionData.setStatisticalDate(dcTrafficSectionData.getStatisticalDate(), TrafficDataPeriodTypeEnum.QUARTER); // 将新数据添加到数据列表中 diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/YearlyTrafficStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/YearlyTrafficStatisticsCache.java index 907eecc2..8b21f675 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/cache/YearlyTrafficStatisticsCache.java +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/YearlyTrafficStatisticsCache.java @@ -66,6 +66,11 @@ public class YearlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache instance.setStatisticalDateStr(formattedDate); } + // 更新上报时间 + dcTrafficSectionData.setReportTime(dcTrafficSectionData.getStatisticalDate()); + // 更新数据周期类型 + dcTrafficSectionData.setPeriodType(TrafficDataPeriodTypeEnum.YEAR); + // 更新统计日期 dcTrafficSectionData.setStatisticalDate(dcTrafficSectionData.getStatisticalDate(), TrafficDataPeriodTypeEnum.YEAR); // 将新数据添加到数据列表中 diff --git a/zc-business/src/main/java/com/zc/business/statistics/handler/RealtimeTrafficStatistics.java b/zc-business/src/main/java/com/zc/business/statistics/handler/RealtimeTrafficStatistics.java index 32be9330..e122cc0e 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/handler/RealtimeTrafficStatistics.java +++ b/zc-business/src/main/java/com/zc/business/statistics/handler/RealtimeTrafficStatistics.java @@ -46,6 +46,8 @@ public class RealtimeTrafficStatistics { aggregatedData.setStakeMark(firstDcTrafficSectionData.getStakeMark()); // 道路方向 aggregatedData.setDirection(firstDcTrafficSectionData.getDirection()); + // 上报时间 + aggregatedData.setReportTime(firstDcTrafficSectionData.getReportTime()); // 计算平均车速并设置到汇总统计对象中 if (trafficVolume != 0) { From c1649e479b9759d5185c9e09982007377136475f Mon Sep 17 00:00:00 2001 From: xiepufeng <1072271977@qq.com> Date: Wed, 20 Mar 2024 16:30:37 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E7=9B=B8=E6=9C=BA?= =?UTF-8?q?=E7=89=A9=E8=81=94=E7=BD=91ID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business/controller/VideoController.java | 82 ++++++++++++++++++- .../zc/business/enums/CameraDirection.java | 60 ++++++++++++++ 2 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 zc-business/src/main/java/com/zc/business/enums/CameraDirection.java diff --git a/zc-business/src/main/java/com/zc/business/controller/VideoController.java b/zc-business/src/main/java/com/zc/business/controller/VideoController.java index d4544c30..756c5f78 100644 --- a/zc-business/src/main/java/com/zc/business/controller/VideoController.java +++ b/zc-business/src/main/java/com/zc/business/controller/VideoController.java @@ -7,7 +7,10 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.redis.RedisCache; +import com.zc.business.constant.DeviceTypeConstants; import com.zc.business.domain.DcDevice; +import com.zc.business.enums.CameraDirection; +import com.zc.business.enums.LaneDirection; import com.zc.business.service.IDcDeviceService; import com.zc.business.service.IMiddleDatabaseService; import com.zc.common.core.httpclient.OkHttp; @@ -22,10 +25,13 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; import static com.ruoyi.common.constant.Constants.HTTP; @@ -63,6 +69,80 @@ public class VideoController extends BaseController { @Resource private IMiddleDatabaseService middleDatabaseService; + // 组织机构id + private final static String CAM_DEPT_ID = "1301730"; + @PostConstruct + public void init() { + // synchronizeIotIds(); + } + + /** + * 同步物联网ID。 + * 该方法用于查询未同步物联网ID的摄像头设备信息,并从外部API获取这些设备的详细信息,然后更新它们的物联网ID。 + * 该过程主要涉及查询未初始化物联网ID的摄像头、构建设备IP与设备映射、从API获取设备详细信息以及批量更新设备的物联网ID。 + */ + public void synchronizeIotIds() { + // 查询未同步物联ID的摄像头设备信息 + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(DcDevice::getDeviceType, DeviceTypeConstants.CAMERA) + .isNull(DcDevice::getIotDeviceId); + + List uninitializedCameras = iDcDeviceService.list(query); + + if (uninitializedCameras.isEmpty()) { + return; // 若无待同步设备,则直接返回 + } + + // 将未初始化的摄像头列表转换为Map,以设备的桩号、方向和子类型拼接作为键,设备本身作为值 + Map cameraMap = new HashMap<>(); + uninitializedCameras.forEach(dcDevice -> { + String otherConfig = dcDevice.getOtherConfig(); + if (otherConfig == null) return; + JSONObject otherConfigJson = JSONObject.parseObject(otherConfig); + String ptzCtrl = otherConfigJson.getString("ptzCtrl"); + String key = dcDevice.getStakeMark() + "|" + dcDevice.getDirection() + "|" + ptzCtrl; + cameraMap.put(key, dcDevice); + }); + + try { + // 获取部门下的摄像头详细信息 + JSONObject camApiResponse = getCamByDept(CAM_DEPT_ID); + + if (camApiResponse == null || !camApiResponse.containsKey("data")) { + return; // 如果获取信息失败或不含有效数据,直接返回 + } + + JSONArray camDataArray = camApiResponse.getJSONArray("data"); + + // 遍历并更新具备匹配IP的摄像头设备物联ID + List devicesToUpdate = new ArrayList<>(); + camDataArray.forEach(item -> { + JSONObject cameraInfo = (JSONObject) item; + // 桩号 + String pileNum = cameraInfo.getString("pileNum"); + // 方向 + Integer camOrientation = cameraInfo.getInteger("camOrientation"); + LaneDirection laneDirection = CameraDirection.fromCode(camOrientation).toLaneDirection(); + + // 是否有云台控制 0 有(球机) 1 ⽆(枪机) + String ptzCtrl = cameraInfo.getString("ptzCtrl"); + + String key = pileNum + "|" + laneDirection.getValue() + "|" + ptzCtrl; + + if (cameraMap.containsKey(key)) { + DcDevice dcDevice = cameraMap.get(key); + dcDevice.setIotDeviceId(cameraInfo.getString("camId")); + devicesToUpdate.add(dcDevice); + } + }); + + // 批量更新摄像头设备的物联ID + iDcDeviceService.updateBatchById(devicesToUpdate); + } catch (HttpException | IOException e) { + logger.error("获取摄像头信息失败!", e); + } + } + /** * 查询附近相机 */ @@ -292,7 +372,7 @@ public class VideoController extends BaseController { public Object nearCamListPileNum(@ApiParam(value = "桩号", name = "pileNum", required = true) String pileNum) throws HttpException, IOException { // 获取济菏运管中心相机信息 - JSONObject camInfo = getCamByDept("1301730"); + JSONObject camInfo = getCamByDept(CAM_DEPT_ID); if (!camInfo.containsKey("data")) { return camInfo; diff --git a/zc-business/src/main/java/com/zc/business/enums/CameraDirection.java b/zc-business/src/main/java/com/zc/business/enums/CameraDirection.java new file mode 100644 index 00000000..ee6d1d0c --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/enums/CameraDirection.java @@ -0,0 +1,60 @@ +package com.zc.business.enums; + +/** + * 摄像头方向 + */ +public enum CameraDirection { + /** + * 摄像头上行 + */ + UPWARD(0, "上行"), + /** + * 摄像头下行 + */ + DOWN(1, "下行"), + /** + * 摄像头上下行(双向) + */ + BIDIRECTIONAL(2, "上下行(双向)"); + + private final int code; + private final String description; + + CameraDirection(int code, String description) { + this.code = code; + this.description = description; + } + + public int getCode() { + return code; + } + + public String getDescription() { + return description; + } + + // 可选:根据code获取枚举值 + public static CameraDirection fromCode(int code) { + for (CameraDirection direction : values()) { + if (direction.getCode() == code) { + return direction; + } + } + throw new IllegalArgumentException("无效的摄像机方向码: " + code); + } + + + public LaneDirection toLaneDirection() { + switch (this) { + case UPWARD: + return LaneDirection.UPWARD; + case DOWN: + return LaneDirection.DOWNWARD; + case BIDIRECTIONAL: + // 假设摄像机双向就映射为车道双向 + return LaneDirection.BIDIRECTIONAL; + default: + throw new IllegalStateException("未知的CameraDirection: " + this); + } + } +} From 78ce466c419763a536b59cbc2645acc8b185c39d Mon Sep 17 00:00:00 2001 From: zhaoxianglong Date: Wed, 20 Mar 2024 16:42:09 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=9D=9E=E6=9C=BA?= =?UTF-8?q?=E9=A2=84=E8=AD=A6=E8=AE=BE=E5=A4=87=E5=A4=87=E6=B3=A8=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=86=85=E5=AE=B9=20=E4=BC=98=E5=8C=96=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=8D=95=E4=B8=AA=E8=AE=BE=E5=A4=87=E5=9C=A8=E7=BA=BF?= =?UTF-8?q?=E7=8E=87=E8=BF=94=E5=9B=9E=E5=80=BC=E5=B0=8F=E6=95=B0=E9=95=BF?= =?UTF-8?q?=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NonAutomaticWarningController.java | 27 ++++++++++++------- .../business/controller/StatusController.java | 10 ++++--- .../java/com/zc/business/domain/Status.java | 6 ++--- .../mapper/business/StatusMapper.xml | 3 ++- 4 files changed, 28 insertions(+), 18 deletions(-) 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 4e3ad35f..0663a021 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 @@ -17,6 +17,7 @@ import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -208,30 +209,36 @@ public class NonAutomaticWarningController extends BaseController { } dcWarning.setWarningTime(happenTime); dcWarning.setCreateTime(new Date()); + String stakeMark = ""; if (crossingName.startsWith("大学城")) { - dcWarning.setStakeMark("K059+289"); + stakeMark = "K059+289"; } else if (crossingName.startsWith("长清")) { - dcWarning.setStakeMark("K072+847"); + stakeMark = "K072+847"; } else if (crossingName.startsWith("孝里")) { - dcWarning.setStakeMark("K086+499"); + stakeMark = "K086+499"; } else if (crossingName.startsWith("平阴北")) { - dcWarning.setStakeMark("K099+750"); + stakeMark = "K099+750"; } else if (crossingName.startsWith("平阴南")) { - dcWarning.setStakeMark("K126+223"); + stakeMark = "K126+223"; } else if (crossingName.startsWith("平阴")) { - dcWarning.setStakeMark("K105+904"); + stakeMark = "K105+904"; } else if (crossingName.startsWith("东平")) { - dcWarning.setStakeMark("K145+933"); + stakeMark = "K145+933"; } else if (crossingName.startsWith("梁山东")) { - dcWarning.setStakeMark("K173+950"); + stakeMark = "K173+950"; } else if (crossingName.startsWith("梁山")) { - dcWarning.setStakeMark("K179+396"); + stakeMark = "K179+396"; } else if (crossingName.startsWith("嘉祥")) { - dcWarning.setStakeMark("K190+495"); + stakeMark = "K190+495"; } + dcWarning.setStakeMark(stakeMark); + dcWarning.setWarningTitle(srcName + "收费站" + direction + "发生" + warningType + "事件"); dcWarning.setWarningSource(6); dcWarning.setWarningState(1); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); + String formattedDate = sdf.format(happenTime); + dcWarning.setRemark("非机预警设备:"+formattedDate + "在" + stakeMark + srcName + "收费站的" + direction + "发生了一起" + warningType + "事件"); dcWarningService.insertDcWarning(dcWarning); }); System.out.println("message content:" + jsonObjString); diff --git a/zc-business/src/main/java/com/zc/business/controller/StatusController.java b/zc-business/src/main/java/com/zc/business/controller/StatusController.java index 07e3d102..691175fa 100644 --- a/zc-business/src/main/java/com/zc/business/controller/StatusController.java +++ b/zc-business/src/main/java/com/zc/business/controller/StatusController.java @@ -112,11 +112,13 @@ public class StatusController extends BaseController { List listStatus = statusService.list(status); - // Group by day and calculate average successRate - Map averageSuccessRateByDay = listStatus.stream() + // Group by day and calculate average successRate with two decimal places + Map averageSuccessRateByDay = listStatus.stream() .collect(Collectors.groupingBy(s -> s.getTime().getDayOfMonth(), - Collectors.averagingDouble(s -> Double.parseDouble(s.getSuccessRate().replace("%",""))))); - + Collectors.collectingAndThen( + Collectors.averagingDouble(s -> Double.parseDouble(s.getSuccessRate().replace("%", ""))), + avg -> Math.round(Float.parseFloat(String.format("%.2f", avg))) + ))); if (averageSuccessRateByDay.isEmpty()) { return AjaxResult.success("暂无数据"); } diff --git a/zc-business/src/main/java/com/zc/business/domain/Status.java b/zc-business/src/main/java/com/zc/business/domain/Status.java index 2d74b95a..bf61b15c 100644 --- a/zc-business/src/main/java/com/zc/business/domain/Status.java +++ b/zc-business/src/main/java/com/zc/business/domain/Status.java @@ -118,7 +118,7 @@ public class Status { @Excel(name = "状态") private int deviceStatus; @Excel(name = "使用状态") - private int useState; + private Integer useState; @@ -230,11 +230,11 @@ public class Status { private String type; - public int getUseState() { + public Integer getUseState() { return useState; } - public void setUseState(int useState) { + public void setUseState(Integer useState) { this.useState = useState; } } diff --git a/zc-business/src/main/resources/mapper/business/StatusMapper.xml b/zc-business/src/main/resources/mapper/business/StatusMapper.xml index 9c40d3bf..2f303a32 100644 --- a/zc-business/src/main/resources/mapper/business/StatusMapper.xml +++ b/zc-business/src/main/resources/mapper/business/StatusMapper.xml @@ -18,6 +18,7 @@ + @@ -102,7 +103,7 @@ AND s.device_id = #{status.deviceId} - + AND d.use_state = #{status.useState} From 1a5f7cd49bc335c6310b298b813b0caded806f45 Mon Sep 17 00:00:00 2001 From: xiepufeng <1072271977@qq.com> Date: Wed, 20 Mar 2024 17:18:12 +0800 Subject: [PATCH 7/8] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=91=84=E5=83=8F?= =?UTF-8?q?=E5=A4=B4=E7=89=A9=E8=81=94=E8=AE=BE=E5=A4=87id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/zc/business/controller/VideoController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zc-business/src/main/java/com/zc/business/controller/VideoController.java b/zc-business/src/main/java/com/zc/business/controller/VideoController.java index 5109820e..51c9aa2f 100644 --- a/zc-business/src/main/java/com/zc/business/controller/VideoController.java +++ b/zc-business/src/main/java/com/zc/business/controller/VideoController.java @@ -73,7 +73,7 @@ public class VideoController extends BaseController { private final static String CAM_DEPT_ID = "1301730"; @PostConstruct public void init() { - // synchronizeIotIds(); + synchronizeIotIds(); } /** From 2bc0da0c44fe74b6a03e7ca530ae8037aa282efd Mon Sep 17 00:00:00 2001 From: "Mr.Wang" Date: Wed, 20 Mar 2024 18:05:45 +0800 Subject: [PATCH 8/8] =?UTF-8?q?dcDevice=E6=8E=A5=E5=8F=A3=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=9F=A5=E8=AF=A2=E5=BD=93=E5=A4=A9=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E5=B1=9E=E6=80=A7=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/DcDeviceController.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) 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 d98c053a..d0b0f0c3 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 @@ -1,5 +1,6 @@ package com.zc.business.controller; +import cn.hutool.core.date.DateUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -207,6 +208,47 @@ public class DcDeviceController extends BaseController { return JSON.parseObject(response.body().string(), AjaxResult.class); } + /** + * 查询当天设备指定属性列表 + * @param deviceId 设备id + * @param propertyId 属性id + * @return 属性列表 + */ + @ApiOperation("查询当天设备指定属性列表") + @GetMapping("/properties/history/day/{deviceId}/{propertyId}") + public AjaxResult queryDevicePropertiesOneDay(@PathVariable @Parameter(description = "设备ID") String deviceId, + @PathVariable @Parameter(description = "属性ID") String propertyId) throws HttpException, IOException { + + HashMap props = new HashMap<>(); + // 设置查询条件的键为“timestamp$BTW”,表示时间戳在一定范围内 + props.put("terms[0].column", "timestamp$BTW"); + ArrayList dateList = new ArrayList<>(); + // 添加当前日期的开始和结束时间到列表,用于设定时间范围 + dateList.add(DateUtil.beginOfDay(new Date()).toString()); + dateList.add(DateUtil.endOfDay(new Date()).toString()); + // 将日期列表以逗号分隔并设置为查询条件的值 + props.put("terms[0].value", String.join(",", dateList)); + props.put("paging", false); + AjaxResult ajaxResult = queryDeviceProperties(deviceId, propertyId, props); + if (!ajaxResult.get("code").equals(200)) { + return ajaxResult; + } + + Object data = JSON.parseObject(queryDeviceProperties(deviceId, propertyId, props).get("data").toString()).get("data"); + JSONArray dataArray = JSON.parseArray(data.toString()); + List list = new ArrayList<>(); + dataArray.forEach(o -> { + Map map = new HashMap<>(); + JSONObject jsonObject = JSON.parseObject(o.toString()); + JSONObject formatValue = JSON.parseObject(jsonObject.get("formatValue").toString()); + map.put("1",formatValue.get("1")); + map.put("3",formatValue.get("3")); + map.put("timestamp",jsonObject.get("timestamp")); + list.add(map); + }); + return AjaxResult.success(list); + } + /** * 查询设备指定属性列表 *