Commit d7b8cf547f4d96b49c3371fbbf4535a1c4dfab3e

Authored by 安浩浩
1 parent 624f5283

【新增功能】 设备历史数据展示

yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java
@@ -4,24 +4,24 @@ import lombok.Data; @@ -4,24 +4,24 @@ import lombok.Data;
4 4
5 import java.util.Map; 5 import java.util.Map;
6 6
7 -/**  
8 - * @ClassDescription: 查询可视化所需入参对象  
9 - * @ClassName: SelectDto  
10 - * @Author: andyz  
11 - * @Date: 2022-07-29 14:12:26  
12 - * @Version 1.0  
13 - */  
14 @Data 7 @Data
15 public class SelectVisualDto { 8 public class SelectVisualDto {
16 9
17 -// @NotBlank(message = "invalid operation: tableName can not be empty") 10 + /**
  11 + * 数据库名称
  12 + */
18 private String dataBaseName; 13 private String dataBaseName;
19 14
20 -// @NotBlank(message = "invalid operation: tableName can not be empty") 15 + /**
  16 + * 表名
  17 + */
21 private String tableName; 18 private String tableName;
22 19
23 -// @NotBlank(message = "invalid operation: fieldName can not be empty") //属性 20 + /**
  21 + * 属性
  22 + */
24 private String fieldName; 23 private String fieldName;
  24 +
25 /** 25 /**
26 * 查询类型,0历史数据,1实时数据,2聚合数据 26 * 查询类型,0历史数据,1实时数据,2聚合数据
27 */ 27 */
@@ -39,10 +39,15 @@ public class SelectVisualDto { @@ -39,10 +39,15 @@ public class SelectVisualDto {
39 * 比如1s,1m,1h,1d代表1秒,1分钟,1小时,1天 39 * 比如1s,1m,1h,1d代表1秒,1分钟,1小时,1天
40 */ 40 */
41 private String interval; 41 private String interval;
42 - // @NotNull(message = "invalid operation: startTime can not be null") 42 +
  43 + /**
  44 + * 开始时间
  45 + */
43 private Long startTime; 46 private Long startTime;
44 47
45 - // @NotNull(message = "invalid operation: endTime can not be null") 48 + /**
  49 + * 结束时间
  50 + */
46 private Long endTime; 51 private Long endTime;
47 52
48 /** 53 /**
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java
1 package cn.iocoder.yudao.module.iot.controller.admin.device; 1 package cn.iocoder.yudao.module.iot.controller.admin.device;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; 6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; 7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; 8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
  9 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO;
8 import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; 10 import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService;
9 import io.swagger.v3.oas.annotations.Operation; 11 import io.swagger.v3.oas.annotations.Operation;
10 import io.swagger.v3.oas.annotations.tags.Tag; 12 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -16,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping; @@ -16,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
16 import org.springframework.web.bind.annotation.RestController; 18 import org.springframework.web.bind.annotation.RestController;
17 19
18 import java.util.List; 20 import java.util.List;
  21 +import java.util.Map;
19 22
20 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 23 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
21 24
@@ -35,4 +38,11 @@ public class IotDeviceDataController { @@ -35,4 +38,11 @@ public class IotDeviceDataController {
35 return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class)); 38 return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class));
36 } 39 }
37 40
  41 + @GetMapping("/history-data")
  42 + @Operation(summary = "获取设备属性历史数据")
  43 + public CommonResult<PageResult<IotTimeDataRespVO>> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO) {
  44 + PageResult<Map<String, Object>> list = deviceDataService.getDevicePropertiesHistoryData(deviceDataReqVO);
  45 + return success(BeanUtils.toBean(list, IotTimeDataRespVO.class));
  46 + }
  47 +
38 } 48 }
39 \ No newline at end of file 49 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java
1 package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData; 1 package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData;
2 2
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
3 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.Size;
4 import lombok.Data; 6 import lombok.Data;
  7 +import org.springframework.format.annotation.DateTimeFormat;
5 8
6 import java.time.LocalDateTime; 9 import java.time.LocalDateTime;
7 10
  11 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  12 +
8 @Schema(description = "管理后台 - IoT 设备数据 Request VO") 13 @Schema(description = "管理后台 - IoT 设备数据 Request VO")
9 @Data 14 @Data
10 -public class IotDeviceDataReqVO { 15 +public class IotDeviceDataReqVO extends PageParam {
11 16
12 @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") 17 @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
13 private Long deviceId; 18 private Long deviceId;
@@ -18,4 +23,9 @@ public class IotDeviceDataReqVO { @@ -18,4 +23,9 @@ public class IotDeviceDataReqVO {
18 @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED) 23 @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED)
19 private String name; 24 private String name;
20 25
  26 + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
  27 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  28 + @Size(min = 2, max = 2, message = "请选择时间范围")
  29 + private LocalDateTime[] times;
  30 +
21 } 31 }
22 \ No newline at end of file 32 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TimeData.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotTimeDataRespVO.java
1 -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; 1 +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData;
2 2
3 import lombok.AllArgsConstructor; 3 import lombok.AllArgsConstructor;
4 import lombok.Data; 4 import lombok.Data;
5 import lombok.NoArgsConstructor; 5 import lombok.NoArgsConstructor;
6 6
7 -/**  
8 - * 统计的时间数据  
9 - */ 7 +
10 @Data 8 @Data
11 @NoArgsConstructor 9 @NoArgsConstructor
12 @AllArgsConstructor 10 @AllArgsConstructor
13 -public class TimeData { 11 +public class IotTimeDataRespVO {
14 12
15 /** 13 /**
16 * 时间 14 * 时间
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java
@@ -88,6 +88,7 @@ public interface TdEngineMapper { @@ -88,6 +88,7 @@ public interface TdEngineMapper {
88 88
89 /** 89 /**
90 * 创建表 - 创建超级表的子表 90 * 创建表 - 创建超级表的子表
  91 + *
91 * @param tableDO 表信息 92 * @param tableDO 表信息
92 */ 93 */
93 @InterceptorIgnore(tenantLine = "true") 94 @InterceptorIgnore(tenantLine = "true")
@@ -114,6 +115,7 @@ public interface TdEngineMapper { @@ -114,6 +115,7 @@ public interface TdEngineMapper {
114 115
115 Map<String, Object> getLastData(SelectDto selectDto); 116 Map<String, Object> getLastData(SelectDto selectDto);
116 117
  118 + @InterceptorIgnore(tenantLine = "true")
117 List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto); 119 List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto);
118 120
119 List<Map<String, Object>> getRealtimeData(SelectVisualDto selectVisualDto); 121 List<Map<String, Object>> getRealtimeData(SelectVisualDto selectVisualDto);
@@ -122,5 +124,6 @@ public interface TdEngineMapper { @@ -122,5 +124,6 @@ public interface TdEngineMapper {
122 124
123 List<Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao); 125 List<Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao);
124 126
125 -  
126 -} 127 + @InterceptorIgnore(tenantLine = "true")
  128 + Long getHistoryCount(SelectVisualDto selectVisualDto);
  129 +}
127 \ No newline at end of file 130 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java
1 package cn.iocoder.yudao.module.iot.service.device; 1 package cn.iocoder.yudao.module.iot.service.device;
2 2
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; 4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
4 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; 5 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
5 import jakarta.validation.Valid; 6 import jakarta.validation.Valid;
6 7
7 import java.util.List; 8 import java.util.List;
  9 +import java.util.Map;
8 10
9 /** 11 /**
10 * IoT 设备数据 Service 接口 12 * IoT 设备数据 Service 接口
@@ -30,4 +32,12 @@ public interface IotDeviceDataService { @@ -30,4 +32,12 @@ public interface IotDeviceDataService {
30 * @return 设备属性最新数据 32 * @return 设备属性最新数据
31 */ 33 */
32 List<IotDeviceDataDO> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceId); 34 List<IotDeviceDataDO> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceId);
  35 +
  36 + /**
  37 + * 获得设备属性历史数据
  38 + *
  39 + * @param deviceDataReqVO 设备属性历史数据 Request VO
  40 + * @return 设备属性历史数据
  41 + */
  42 + PageResult<Map<String, Object>> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO);
33 } 43 }
34 \ No newline at end of file 44 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java
1 package cn.iocoder.yudao.module.iot.service.device; 1 package cn.iocoder.yudao.module.iot.service.device;
2 2
  3 +import cn.hutool.core.date.DateUtil;
3 import cn.hutool.json.JSONObject; 4 import cn.hutool.json.JSONObject;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; 6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
5 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
6 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; 8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; 9 import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
8 import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; 10 import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
9 import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; 11 import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
  12 +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper;
  13 +import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
10 import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; 14 import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
11 import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; 15 import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService;
12 import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; 16 import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService;
13 import jakarta.annotation.Resource; 17 import jakarta.annotation.Resource;
14 import jakarta.validation.Valid; 18 import jakarta.validation.Valid;
15 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.beans.factory.annotation.Value;
16 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
17 22
  23 +import java.time.ZoneId;
18 import java.util.ArrayList; 24 import java.util.ArrayList;
  25 +import java.util.HashMap;
19 import java.util.List; 26 import java.util.List;
  27 +import java.util.Map;
20 28
21 @Slf4j 29 @Slf4j
22 @Service 30 @Service
23 public class IotDeviceDataServiceImpl implements IotDeviceDataService { 31 public class IotDeviceDataServiceImpl implements IotDeviceDataService {
24 32
  33 + @Value("${spring.datasource.dynamic.datasource.tdengine.url}")
  34 + private String url;
  35 +
25 @Resource 36 @Resource
26 private IotDeviceService deviceService; 37 private IotDeviceService deviceService;
27 @Resource 38 @Resource
@@ -32,6 +43,9 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { @@ -32,6 +43,9 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
32 @Resource 43 @Resource
33 private DeviceDataRedisDAO deviceDataRedisDAO; 44 private DeviceDataRedisDAO deviceDataRedisDAO;
34 45
  46 + @Resource
  47 + private TdEngineMapper tdEngineMapper;
  48 +
35 @Override 49 @Override
36 public void saveDeviceData(String productKey, String deviceName, String message) { 50 public void saveDeviceData(String productKey, String deviceName, String message) {
37 // 1. 根据产品 key 和设备名称,获得设备信息 51 // 1. 根据产品 key 和设备名称,获得设备信息
@@ -91,4 +105,33 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { @@ -91,4 +105,33 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
91 }); 105 });
92 return list; 106 return list;
93 } 107 }
  108 +
  109 + @Override
  110 + public PageResult<Map<String, Object>> getDevicePropertiesHistoryData(IotDeviceDataReqVO deviceDataReqVO) {
  111 + PageResult<Map<String, Object>> pageResult = new PageResult<>();
  112 + // 1. 获取设备信息
  113 + IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());
  114 + // 2. 获取设备属性历史数据
  115 + SelectVisualDto selectVisualDto = new SelectVisualDto();
  116 + selectVisualDto.setDataBaseName(getDatabaseName());
  117 + selectVisualDto.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()));
  118 + selectVisualDto.setFieldName(deviceDataReqVO.getIdentifier());
  119 + selectVisualDto.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
  120 + selectVisualDto.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
  121 + Map<String, Object> params = new HashMap<>();
  122 + params.put("rows", deviceDataReqVO.getPageSize());
  123 + params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize());
  124 + selectVisualDto.setParams(params);
  125 + pageResult.setList(tdEngineMapper.getHistoryData(selectVisualDto));
  126 + pageResult.setTotal(tdEngineMapper.getHistoryCount(selectVisualDto));
  127 + return pageResult;
  128 + }
  129 +
  130 + private String getDatabaseName() {
  131 + return url.substring(url.lastIndexOf("/") + 1);
  132 + }
  133 +
  134 + private static String getDeviceTableName(String productKey, String deviceName) {
  135 + return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase());
  136 + }
94 } 137 }
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml
@@ -292,17 +292,18 @@ @@ -292,17 +292,18 @@
292 292
293 <select id="getHistoryData" resultType="java.util.Map" 293 <select id="getHistoryData" resultType="java.util.Map"
294 parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto"> 294 parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
295 - SELECT #{fieldName}, ts  
296 - FROM #{dataBaseName}.#{tableName}  
297 - WHERE ts BETWEEN #{startTime} AND #{endTime}  
298 - LIMIT #{num} 295 + SELECT ${fieldName} as data, time
  296 + FROM ${dataBaseName}.${tableName}
  297 + WHERE time BETWEEN #{startTime} AND #{endTime}
  298 + AND ${fieldName} IS NOT NULL
  299 + ORDER BY time DESC
  300 + LIMIT #{params.rows} offset #{params.page}
299 </select> 301 </select>
300 302
301 <select id="getRealtimeData" resultType="java.util.Map" 303 <select id="getRealtimeData" resultType="java.util.Map"
302 parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto"> 304 parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
303 - SELECT #{fieldName}, ts 305 + SELECT #{fieldName}, time
304 FROM #{dataBaseName}.#{tableName} 306 FROM #{dataBaseName}.#{tableName}
305 - LIMIT #{num}  
306 </select> 307 </select>
307 308
308 <select id="getAggregateData" resultType="java.util.Map" 309 <select id="getAggregateData" resultType="java.util.Map"
@@ -316,5 +317,11 @@ @@ -316,5 +317,11 @@
316 <select id="describeSuperTable" resultType="java.util.Map"> 317 <select id="describeSuperTable" resultType="java.util.Map">
317 DESCRIBE ${dataBaseName}.${superTableName} 318 DESCRIBE ${dataBaseName}.${superTableName}
318 </select> 319 </select>
  320 + <select id="getHistoryCount" resultType="java.lang.Long">
  321 + SELECT count(time)
  322 + FROM ${dataBaseName}.${tableName}
  323 + WHERE time BETWEEN #{startTime} AND #{endTime}
  324 + AND ${fieldName} IS NOT NULL
  325 + </select>
319 326
320 </mapper> 327 </mapper>