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

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

@@ -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>