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

This commit is contained in:
安浩浩 2024-11-05 23:26:34 +08:00
parent 624f5283b3
commit d7b8cf547f
8 changed files with 112 additions and 26 deletions

View File

@ -4,24 +4,24 @@ import lombok.Data;
import java.util.Map;
/**
* @ClassDescription: 查询可视化所需入参对象
* @ClassName: SelectDto
* @Author: andyz
* @Date: 2022-07-29 14:12:26
* @Version 1.0
*/
@Data
public class SelectVisualDto {
// @NotBlank(message = "invalid operation: tableName can not be empty")
/**
* 数据库名称
*/
private String dataBaseName;
// @NotBlank(message = "invalid operation: tableName can not be empty")
/**
* 表名
*/
private String tableName;
// @NotBlank(message = "invalid operation: fieldName can not be empty") //属性
/**
* 属性
*/
private String fieldName;
/**
* 查询类型0历史数据1实时数据2聚合数据
*/
@ -39,10 +39,15 @@ public class SelectVisualDto {
* 比如1s,1m,1h,1d代表1秒1分钟1小时1天
*/
private String interval;
// @NotNull(message = "invalid operation: startTime can not be null")
/**
* 开始时间
*/
private Long startTime;
// @NotNull(message = "invalid operation: endTime can not be null")
/**
* 结束时间
*/
private Long endTime;
/**

View File

@ -1,10 +1,12 @@
package cn.iocoder.yudao.module.iot.controller.admin.device;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO;
import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -16,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@ -35,4 +38,11 @@ public class IotDeviceDataController {
return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class));
}
@GetMapping("/history-data")
@Operation(summary = "获取设备属性历史数据")
public CommonResult<PageResult<IotTimeDataRespVO>> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO) {
PageResult<Map<String, Object>> list = deviceDataService.getDevicePropertiesHistoryData(deviceDataReqVO);
return success(BeanUtils.toBean(list, IotTimeDataRespVO.class));
}
}

View File

@ -1,13 +1,18 @@
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - IoT 设备数据 Request VO")
@Data
public class IotDeviceDataReqVO {
public class IotDeviceDataReqVO extends PageParam {
@Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
private Long deviceId;
@ -18,4 +23,9 @@ public class IotDeviceDataReqVO {
@Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED)
private String name;
@Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Size(min = 2, max = 2, message = "请选择时间范围")
private LocalDateTime[] times;
}

View File

@ -1,16 +1,14 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 统计的时间数据
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TimeData {
public class IotTimeDataRespVO {
/**
* 时间

View File

@ -88,6 +88,7 @@ public interface TdEngineMapper {
/**
* 创建表 - 创建超级表的子表
*
* @param tableDO 表信息
*/
@InterceptorIgnore(tenantLine = "true")
@ -114,6 +115,7 @@ public interface TdEngineMapper {
Map<String, Object> getLastData(SelectDto selectDto);
@InterceptorIgnore(tenantLine = "true")
List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto);
List<Map<String, Object>> getRealtimeData(SelectVisualDto selectVisualDto);
@ -122,5 +124,6 @@ public interface TdEngineMapper {
List<Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao);
}
@InterceptorIgnore(tenantLine = "true")
Long getHistoryCount(SelectVisualDto selectVisualDto);
}

View File

@ -1,10 +1,12 @@
package cn.iocoder.yudao.module.iot.service.device;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
import jakarta.validation.Valid;
import java.util.List;
import java.util.Map;
/**
* IoT 设备数据 Service 接口
@ -30,4 +32,12 @@ public interface IotDeviceDataService {
* @return 设备属性最新数据
*/
List<IotDeviceDataDO> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceId);
/**
* 获得设备属性历史数据
*
* @param deviceDataReqVO 设备属性历史数据 Request VO
* @return 设备属性历史数据
*/
PageResult<Map<String, Object>> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO);
}

View File

@ -1,27 +1,38 @@
package cn.iocoder.yudao.module.iot.service.device;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONObject;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper;
import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService;
import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
public class IotDeviceDataServiceImpl implements IotDeviceDataService {
@Value("${spring.datasource.dynamic.datasource.tdengine.url}")
private String url;
@Resource
private IotDeviceService deviceService;
@Resource
@ -32,6 +43,9 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
@Resource
private DeviceDataRedisDAO deviceDataRedisDAO;
@Resource
private TdEngineMapper tdEngineMapper;
@Override
public void saveDeviceData(String productKey, String deviceName, String message) {
// 1. 根据产品 key 和设备名称获得设备信息
@ -91,4 +105,33 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
});
return list;
}
@Override
public PageResult<Map<String, Object>> getDevicePropertiesHistoryData(IotDeviceDataReqVO deviceDataReqVO) {
PageResult<Map<String, Object>> pageResult = new PageResult<>();
// 1. 获取设备信息
IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());
// 2. 获取设备属性历史数据
SelectVisualDto selectVisualDto = new SelectVisualDto();
selectVisualDto.setDataBaseName(getDatabaseName());
selectVisualDto.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()));
selectVisualDto.setFieldName(deviceDataReqVO.getIdentifier());
selectVisualDto.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
selectVisualDto.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
Map<String, Object> params = new HashMap<>();
params.put("rows", deviceDataReqVO.getPageSize());
params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize());
selectVisualDto.setParams(params);
pageResult.setList(tdEngineMapper.getHistoryData(selectVisualDto));
pageResult.setTotal(tdEngineMapper.getHistoryCount(selectVisualDto));
return pageResult;
}
private String getDatabaseName() {
return url.substring(url.lastIndexOf("/") + 1);
}
private static String getDeviceTableName(String productKey, String deviceName) {
return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase());
}
}

View File

@ -292,17 +292,18 @@
<select id="getHistoryData" resultType="java.util.Map"
parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
SELECT #{fieldName}, ts
FROM #{dataBaseName}.#{tableName}
WHERE ts BETWEEN #{startTime} AND #{endTime}
LIMIT #{num}
SELECT ${fieldName} as data, time
FROM ${dataBaseName}.${tableName}
WHERE time BETWEEN #{startTime} AND #{endTime}
AND ${fieldName} IS NOT NULL
ORDER BY time DESC
LIMIT #{params.rows} offset #{params.page}
</select>
<select id="getRealtimeData" resultType="java.util.Map"
parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
SELECT #{fieldName}, ts
SELECT #{fieldName}, time
FROM #{dataBaseName}.#{tableName}
LIMIT #{num}
</select>
<select id="getAggregateData" resultType="java.util.Map"
@ -316,5 +317,11 @@
<select id="describeSuperTable" resultType="java.util.Map">
DESCRIBE ${dataBaseName}.${superTableName}
</select>
<select id="getHistoryCount" resultType="java.lang.Long">
SELECT count(time)
FROM ${dataBaseName}.${tableName}
WHERE time BETWEEN #{startTime} AND #{endTime}
AND ${fieldName} IS NOT NULL
</select>
</mapper>