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 4  
5 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 7 @Data
15 8 public class SelectVisualDto {
16 9  
17   -// @NotBlank(message = "invalid operation: tableName can not be empty")
  10 + /**
  11 + * 数据库名称
  12 + */
18 13 private String dataBaseName;
19 14  
20   -// @NotBlank(message = "invalid operation: tableName can not be empty")
  15 + /**
  16 + * 表名
  17 + */
21 18 private String tableName;
22 19  
23   -// @NotBlank(message = "invalid operation: fieldName can not be empty") //属性
  20 + /**
  21 + * 属性
  22 + */
24 23 private String fieldName;
  24 +
25 25 /**
26 26 * 查询类型,0历史数据,1实时数据,2聚合数据
27 27 */
... ... @@ -39,10 +39,15 @@ public class SelectVisualDto {
39 39 * 比如1s,1m,1h,1d代表1秒,1分钟,1小时,1天
40 40 */
41 41 private String interval;
42   - // @NotNull(message = "invalid operation: startTime can not be null")
  42 +
  43 + /**
  44 + * 开始时间
  45 + */
43 46 private Long startTime;
44 47  
45   - // @NotNull(message = "invalid operation: endTime can not be null")
  48 + /**
  49 + * 结束时间
  50 + */
46 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 1 package cn.iocoder.yudao.module.iot.controller.admin.device;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
6 7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO;
7 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 10 import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService;
9 11 import io.swagger.v3.oas.annotations.Operation;
10 12 import io.swagger.v3.oas.annotations.tags.Tag;
... ... @@ -16,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
16 18 import org.springframework.web.bind.annotation.RestController;
17 19  
18 20 import java.util.List;
  21 +import java.util.Map;
19 22  
20 23 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
21 24  
... ... @@ -35,4 +38,11 @@ public class IotDeviceDataController {
35 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 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 1 package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
3 4 import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.Size;
4 6 import lombok.Data;
  7 +import org.springframework.format.annotation.DateTimeFormat;
5 8  
6 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 13 @Schema(description = "管理后台 - IoT 设备数据 Request VO")
9 14 @Data
10   -public class IotDeviceDataReqVO {
  15 +public class IotDeviceDataReqVO extends PageParam {
11 16  
12 17 @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
13 18 private Long deviceId;
... ... @@ -18,4 +23,9 @@ public class IotDeviceDataReqVO {
18 23 @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED)
19 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 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 3 import lombok.AllArgsConstructor;
4 4 import lombok.Data;
5 5 import lombok.NoArgsConstructor;
6 6  
7   -/**
8   - * 统计的时间数据
9   - */
  7 +
10 8 @Data
11 9 @NoArgsConstructor
12 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 88  
89 89 /**
90 90 * 创建表 - 创建超级表的子表
  91 + *
91 92 * @param tableDO 表信息
92 93 */
93 94 @InterceptorIgnore(tenantLine = "true")
... ... @@ -114,6 +115,7 @@ public interface TdEngineMapper {
114 115  
115 116 Map<String, Object> getLastData(SelectDto selectDto);
116 117  
  118 + @InterceptorIgnore(tenantLine = "true")
117 119 List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto);
118 120  
119 121 List<Map<String, Object>> getRealtimeData(SelectVisualDto selectVisualDto);
... ... @@ -122,5 +124,6 @@ public interface TdEngineMapper {
122 124  
123 125 List<Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao);
124 126  
125   -
126   -}
  127 + @InterceptorIgnore(tenantLine = "true")
  128 + Long getHistoryCount(SelectVisualDto selectVisualDto);
  129 +}
127 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 1 package cn.iocoder.yudao.module.iot.service.device;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
4 5 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
5 6 import jakarta.validation.Valid;
6 7  
7 8 import java.util.List;
  9 +import java.util.Map;
8 10  
9 11 /**
10 12 * IoT 设备数据 Service 接口
... ... @@ -30,4 +32,12 @@ public interface IotDeviceDataService {
30 32 * @return 设备属性最新数据
31 33 */
32 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 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 1 package cn.iocoder.yudao.module.iot.service.device;
2 2  
  3 +import cn.hutool.core.date.DateUtil;
3 4 import cn.hutool.json.JSONObject;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
5 7 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
6 8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
7 9 import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
8 10 import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
9 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 14 import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
11 15 import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService;
12 16 import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService;
13 17 import jakarta.annotation.Resource;
14 18 import jakarta.validation.Valid;
15 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.beans.factory.annotation.Value;
16 21 import org.springframework.stereotype.Service;
17 22  
  23 +import java.time.ZoneId;
18 24 import java.util.ArrayList;
  25 +import java.util.HashMap;
19 26 import java.util.List;
  27 +import java.util.Map;
20 28  
21 29 @Slf4j
22 30 @Service
23 31 public class IotDeviceDataServiceImpl implements IotDeviceDataService {
24 32  
  33 + @Value("${spring.datasource.dynamic.datasource.tdengine.url}")
  34 + private String url;
  35 +
25 36 @Resource
26 37 private IotDeviceService deviceService;
27 38 @Resource
... ... @@ -32,6 +43,9 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
32 43 @Resource
33 44 private DeviceDataRedisDAO deviceDataRedisDAO;
34 45  
  46 + @Resource
  47 + private TdEngineMapper tdEngineMapper;
  48 +
35 49 @Override
36 50 public void saveDeviceData(String productKey, String deviceName, String message) {
37 51 // 1. 根据产品 key 和设备名称,获得设备信息
... ... @@ -91,4 +105,33 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
91 105 });
92 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 292  
293 293 <select id="getHistoryData" resultType="java.util.Map"
294 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 301 </select>
300 302  
301 303 <select id="getRealtimeData" resultType="java.util.Map"
302 304 parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
303   - SELECT #{fieldName}, ts
  305 + SELECT #{fieldName}, time
304 306 FROM #{dataBaseName}.#{tableName}
305   - LIMIT #{num}
306 307 </select>
307 308  
308 309 <select id="getAggregateData" resultType="java.util.Map"
... ... @@ -316,5 +317,11 @@
316 317 <select id="describeSuperTable" resultType="java.util.Map">
317 318 DESCRIBE ${dataBaseName}.${superTableName}
318 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 327 </mapper>
... ...