历史记录查询接口

ck
liutao 1 week ago
parent 00f5b71f27
commit 77320d3da3

@ -262,6 +262,18 @@ public class DeviceController {
return success(deviceContactModelDO);
}
@GetMapping("/historyAnalyse")
@Operation(summary = "历史记录分析查询")
// @PreAuthorize("@ss.hasPermission('iot:device:query')")
public CommonResult<Map<String, Object>> historyAnalyse(@RequestParam("deviceId") Long deviceId,
@RequestParam(name = "collectionStartTime", required = false) String collectionStartTime,
@RequestParam(name = "collectionEndTime", required = false) String collectionEndTime,
@RequestParam(name = "attributeCodes", required = false)List<String> attributeCodes
) throws JsonProcessingException {
Map<String, Object> deviceContactModelDO=deviceService.historyAnalyse(deviceId,collectionStartTime,collectionEndTime,attributeCodes);
return success(deviceContactModelDO);
}
@GetMapping("/getDeviceOperationalStatus")
@Operation(summary = "获取首页设备运行状态")
@ -295,6 +307,14 @@ public class DeviceController {
return success(deviceService.getDeviceAttributePage(pageParam, deviceModelAttributePageReqVO));
}
@GetMapping("/device-attribute/groupList")
@Operation(summary = "获得设备属性")
@Parameter(name = "deviceId", description = "设备id")
@PreAuthorize("@ss.hasPermission('iot:device:query')")
public CommonResult<List<Map<String, Object>>> getDeviceAttributeGroupList(DeviceContactModelPageReqVO deviceModelAttributePageReqVO) {
return success(deviceService.getDeviceAttributeGroupList(deviceModelAttributePageReqVO));
}
@GetMapping("/device-attribute/list")
@Operation(summary = "获得设备属性列表")

@ -78,5 +78,7 @@ public class DeviceSaveReqVO {
@Schema(description = "mqtt订阅主题")
private String topic;
@Schema(description = "记录分析的条件json", example = "{}")
private String contactInfo;
}

@ -117,4 +117,9 @@ public class DeviceDO extends BaseDO {
*/
private String topic;
/**
* json
*/
private String contactInfo;
}

@ -69,4 +69,7 @@ public interface DeviceContactModelMapper extends BaseMapperX<DeviceContactModel
Map<String, String> selectAttributeNameAndUnitById(@Param("id") Long id);
default List<DeviceContactModelDO> selectListById(DeviceContactModelPageReqVO reqVO){
return selectList(new LambdaQueryWrapperX<DeviceContactModelDO>().eqIfPresent(DeviceContactModelDO::getDeviceId, reqVO.getDeviceId()));
};
}

@ -159,4 +159,8 @@ public interface DeviceService {
PageResult<DeviceSelectRespVO> getAvailableDevicePage(DevicePageReqVO pageReqVO);
List<Map<String, Object>> singleDeviceFrom(Long deviceId);
List<Map<String, Object>> getDeviceAttributeGroupList(DeviceContactModelPageReqVO deviceModelAttributePageReqVO);
Map<String, Object> historyAnalyse(Long deviceId, String collectionStartTime, String collectionEndTime, List<String> attributeCodes);
}

@ -8,6 +8,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.opc.OpcUtils;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;
import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductUnitMapper;
import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceRateTrendPeriodEnum;
import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceStatusEnum;
import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.utils.CronExpressionUtils;
@ -44,6 +46,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceAttributeDO;
import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceAttributeMapper;
import cn.iocoder.yudao.module.iot.framework.mqtt.consumer.IMqttservice;
import cn.iocoder.yudao.module.iot.util.MapListStatsCalculator;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@ -53,6 +56,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.factory.annotation.Qualifier;
@ -130,12 +134,17 @@ public class DeviceServiceImpl implements DeviceService {
@Resource
private IMqttservice mqttService;
@Resource
private ErpProductUnitMapper productUnitMapper;
// @Resource
// private GatewayService gatewayService;
@Resource
private GatewayMapper gatewayMapper;
private static final String[] colors = new String[]{"#4888FF", "#00B42A", "#722ED1", "#FF7D00", "#F53F3F", "#86909C", "#FF7D00",
"#13A8A8","#7C3AED","#EF4444","#10B981","#FF9800","#4F46E5","#E11D48","#22C55E","#F97316","#9333EA","#F87171","#34D399","#FBBF24","#A855F7","#F87171","#FBBF24","#A855F7","#F87171","#FBBF24","#A855F7","#F871"};
@Override
@ -1053,6 +1062,118 @@ public class DeviceServiceImpl implements DeviceService {
return resultList;
}
@Override
public List<Map<String, Object>> getDeviceAttributeGroupList(DeviceContactModelPageReqVO deviceModelAttributePageReqVO) {
//Map<String, List<Map<String, Object>>> resultMap = new LinkedHashMap<>();
List<Map<String, Object>> resultList = new ArrayList<>();
// 参数校验
if (deviceModelAttributePageReqVO.getDeviceId() == null) {
throw exception(DEVICE_ID_MODEL_NOT_EXISTS);
}
//查询采集设备
DeviceDO deviceDO = deviceMapper.selectById(deviceModelAttributePageReqVO.getDeviceId());
// 判断设备模型ID是否有效
List<DeviceContactModelDO> deviceModelAttributeDOPageResult = deviceContactModelMapper.selectListById(deviceModelAttributePageReqVO);
if(CollUtil.isNotEmpty(deviceModelAttributeDOPageResult)){
List<String> typeNames = deviceModelAttributeDOPageResult.stream().map(DeviceContactModelDO::getTypeName).distinct().collect(Collectors.toList());
List<String> codes = deviceModelAttributeDOPageResult.stream().map(DeviceContactModelDO::getAttributeCode).distinct().collect(Collectors.toList());
Map<String, List<DeviceContactModelDO>> resultMap = deviceModelAttributeDOPageResult.stream().collect(Collectors.groupingBy(DeviceContactModelDO::getTypeName));
resultMap.forEach((key, value) -> {
HashMap<String, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("group", key);
objectObjectHashMap.put("typeNames", typeNames);
objectObjectHashMap.put("codes", codes);
List<Map<String,Object>> objects = new ArrayList<>();
value.stream().forEach(item -> {
Map<String, Object> objectObjectHashMap1 = new HashMap<>();
objectObjectHashMap1.put("name", item.getAttributeName());
objectObjectHashMap1.put("code", item.getAttributeCode());
objects.add(objectObjectHashMap1);
});
objectObjectHashMap.put("points", objects);
objectObjectHashMap.put("deviceDO", deviceDO);
resultList.add(objectObjectHashMap);
});
}
return resultList;
}
@Override
public Map<String, Object> historyAnalyse(Long deviceId, String collectionStartTime, String collectionEndTime, List<String> attributeCodes) {
if (deviceId == null) {
return null;
}
List<DeviceContactModelDO> deviceContactModelDOS = deviceContactModelMapper.selectList(Wrappers.lambdaQuery(DeviceContactModelDO.class).eq(DeviceContactModelDO::getDeviceId, deviceId));
if (attributeCodes == null) {
attributeCodes=deviceContactModelDOS.stream().map(DeviceContactModelDO::getAttributeCode).collect(Collectors.toList());
}
//查询所有单位
List<ErpProductUnitDO> erpProductUnitDOS = productUnitMapper.selectList();
Map<Long, List<ErpProductUnitDO>> unitMap = erpProductUnitDOS.stream().collect(Collectors.groupingBy(ErpProductUnitDO::getId));
try {
// 1. 查询TDengine分页数据
List<Map<String, Object>> deviceDataList = tdengineService.queryHistoryList(deviceId, collectionStartTime, collectionEndTime,attributeCodes);
if (deviceDataList.isEmpty()) {
return null;
}
Map<String, Object> resultMap = new HashMap();
List<Object> time = deviceDataList.stream().map(map -> map.get("time")).collect(Collectors.toList());
resultMap.put("time", time);
//查询点位
Map<String, List<DeviceContactModelDO>> collect = deviceContactModelDOS.stream().collect(Collectors.groupingBy(DeviceContactModelDO::getAttributeCode));
List<Map<String, Object>> dataMap=new ArrayList<>();
AtomicInteger a= new AtomicInteger(0);
attributeCodes.stream().forEach(item-> {
Map<String, Object> map = new HashMap();
List<DeviceContactModelDO> deviceContactModelDOS1 = collect.get(item);
if (CollUtil.isNotEmpty(deviceContactModelDOS1))
map.put("name", deviceContactModelDOS1.get(0).getAttributeName());
map.put("type", "line");
map.put("color", colors[a.incrementAndGet()]);
map.put("data", deviceDataList.stream().map(map1 -> map1.get(item)).collect(Collectors.toList()));
dataMap.add(map);
a.getAndIncrement();
});
resultMap.put("series", dataMap);
resultMap.put("data", deviceDataList);
//查询点位的最大值,及最小值及平局值及波动值
// 简洁用法
Map<String, Double> maxValues = MapListStatsCalculator.getMaxValues(deviceDataList, attributeCodes);
Map<String, Double> minValues = MapListStatsCalculator.getMinValues(deviceDataList, attributeCodes);
Map<String, Double> avgValues = MapListStatsCalculator.getAvgValues(deviceDataList, attributeCodes);
Map<String, Double> rangeValues = MapListStatsCalculator.getRangeValues(deviceDataList, attributeCodes);
List<Map<String, Object>> analyseData=new ArrayList<>();
List<String> finalAttributeCodes = attributeCodes;
deviceContactModelDOS.stream().filter(device -> finalAttributeCodes.contains(device.getAttributeCode())).forEach(item ->{
Map<String, Object> map = new HashMap();
map.put("code", item.getAttributeCode());
map.put("name", item.getAttributeName());
List<ErpProductUnitDO> erpProductUnitDOS1 = unitMap.get( NumberUtils.toLong(item.getDataUnit(), 0));
if (CollUtil.isNotEmpty(erpProductUnitDOS1)) map.put("unit", erpProductUnitDOS1.get(0).getName());
map.put("max", maxValues.get(item.getAttributeCode()));
map.put("min", minValues.get(item.getAttributeCode()));
map.put("avg", avgValues.get(item.getAttributeCode()));
map.put("range", rangeValues.get(item.getAttributeCode()));
analyseData.add(map);
});
resultMap.put("analyseData", analyseData);
return resultMap;
} catch (Exception e) {
log.error("处理设备历史数据异常", e);
return null;
}
}
@Override
public PageResult<Map<String, Object>> historyRecord(

@ -2534,5 +2534,43 @@ public class TDengineService {
return false;
}
}
@DS("tdengine")
public List<Map<String, Object>> queryHistoryList(Long deviceId, String startTime, String endTime,List<String> attributeCodes) {
if (deviceId == null || startTime == null || endTime == null || attributeCodes.isEmpty()) {
return Collections.emptyList();
}
String tableName = "besure_server.d_" + deviceId;
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("SELECT ");
attributeCodes.stream().forEach(item -> {
sqlBuilder.append("first(").append(item).append(") as ").append(item).append(",");
});
// 构建 SQL
sqlBuilder.append(" TO_CHAR(_wstart, 'hh24:mi') as time from ").append(tableName)
.append(" WHERE 1=1 ");
if (StringUtils.isNotBlank(startTime)) {
sqlBuilder.append(" AND ts >= '").append(startTime).append("' ");
}
if (StringUtils.isNotBlank(endTime)) {
sqlBuilder.append(" AND ts <= '").append(endTime).append("' ");
}
sqlBuilder.append(" INTERVAL(5m) ORDER BY _wstart ");
String sql = sqlBuilder.toString();
try {
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
return list;
} catch (EmptyResultDataAccessException e) {
return Collections.emptyList();
} catch (Exception e) {
log.error("查询设备 {} 功率数据异常", deviceId, e);
return Collections.emptyList();
}
}
}

@ -0,0 +1,312 @@
package cn.iocoder.yudao.module.iot.util;
import java.util.*;
import java.util.stream.Collectors;
import java.util.DoubleSummaryStatistics;
import java.text.DecimalFormat;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class MapListStatsCalculator {
// 默认保留小数位数
private static final int DEFAULT_SCALE = 2;
/**
* key
*/
public static Map<String, Double> getMaxValues(List<Map<String, Object>> dataList, List<String> keys) {
return getMaxValues(dataList, keys, DEFAULT_SCALE);
}
public static Map<String, Double> getMaxValues(List<Map<String, Object>> dataList, List<String> keys, int scale) {
return keys.stream()
.collect(Collectors.toMap(
key -> key,
key -> {
OptionalDouble max = dataList.stream()
.filter(map -> map.containsKey(key) && map.get(key) != null)
.mapToDouble(map -> convertToDouble(map.get(key)))
.max();
return roundValue(max.orElse(Double.NaN), scale);
}
));
}
/**
* key
*/
public static Map<String, Double> getMinValues(List<Map<String, Object>> dataList, List<String> keys) {
return getMinValues(dataList, keys, DEFAULT_SCALE);
}
public static Map<String, Double> getMinValues(List<Map<String, Object>> dataList, List<String> keys, int scale) {
return keys.stream()
.collect(Collectors.toMap(
key -> key,
key -> {
OptionalDouble min = dataList.stream()
.filter(map -> map.containsKey(key) && map.get(key) != null)
.mapToDouble(map -> convertToDouble(map.get(key)))
.min();
return roundValue(min.orElse(Double.NaN), scale);
}
));
}
/**
* key
*/
public static Map<String, Double> getAvgValues(List<Map<String, Object>> dataList, List<String> keys) {
return getAvgValues(dataList, keys, DEFAULT_SCALE);
}
public static Map<String, Double> getAvgValues(List<Map<String, Object>> dataList, List<String> keys, int scale) {
return keys.stream()
.collect(Collectors.toMap(
key -> key,
key -> {
OptionalDouble avg = dataList.stream()
.filter(map -> map.containsKey(key) && map.get(key) != null)
.mapToDouble(map -> convertToDouble(map.get(key)))
.average();
return roundValue(avg.orElse(Double.NaN), scale);
}
));
}
/**
* key
*/
public static Map<String, Double> getRangeValues(List<Map<String, Object>> dataList, List<String> keys) {
return getRangeValues(dataList, keys, DEFAULT_SCALE);
}
public static Map<String, Double> getRangeValues(List<Map<String, Object>> dataList, List<String> keys, int scale) {
return keys.stream()
.collect(Collectors.toMap(
key -> key,
key -> {
DoubleSummaryStatistics stats = dataList.stream()
.filter(map -> map.containsKey(key) && map.get(key) != null)
.mapToDouble(map -> convertToDouble(map.get(key)))
.summaryStatistics();
double range = stats.getCount() > 0 ? stats.getMax() - stats.getMin() : 0.0;
return roundValue(range, scale);
}
));
}
/**
*
*/
public static Map<String, Map<String, Double>> getAllStats(List<Map<String, Object>> dataList, List<String> keys) {
return getAllStats(dataList, keys, DEFAULT_SCALE);
}
public static Map<String, Map<String, Double>> getAllStats(List<Map<String, Object>> dataList, List<String> keys, int scale) {
return keys.stream()
.collect(Collectors.toMap(
key -> key,
key -> {
DoubleSummaryStatistics stats = dataList.stream()
.filter(map -> map.containsKey(key) && map.get(key) != null)
.mapToDouble(map -> convertToDouble(map.get(key)))
.summaryStatistics();
Map<String, Double> result = new HashMap<>();
result.put("max", roundValue(stats.getCount() > 0 ? stats.getMax() : 0.0, scale));
result.put("min", roundValue(stats.getCount() > 0 ? stats.getMin() : 0.0, scale));
result.put("avg", roundValue(stats.getCount() > 0 ? stats.getAverage() : 0.0, scale));
result.put("range", roundValue(stats.getCount() > 0 ? stats.getMax() - stats.getMin() : 0.0, scale));
result.put("count", (double) stats.getCount()); // 数量保持整数
result.put("sum", roundValue(stats.getSum(), scale));
return result;
}
));
}
/**
* Double
*/
private static double convertToDouble(Object value) {
if (value == null) {
return 0.0;
}
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
if (value instanceof String) {
try {
return Double.parseDouble(value.toString().trim());
} catch (NumberFormatException e) {
return 0.0;
}
}
if (value instanceof Boolean) {
return (Boolean) value ? 1.0 : 0.0;
}
return 0.0;
}
/**
* - 使BigDecimal
*/
public static double roundValue(double value, int scale) {
if (Double.isNaN(value) || Double.isInfinite(value)) {
return value;
}
return BigDecimal.valueOf(value)
.setScale(scale, RoundingMode.HALF_UP)
.doubleValue();
}
/**
* - 使DecimalFormat
*/
public static String roundValueToString(double value, int scale) {
if (Double.isNaN(value)) {
return "NaN";
}
if (Double.isInfinite(value)) {
return value > 0 ? "Infinity" : "-Infinity";
}
// 构建格式字符串
StringBuilder pattern = new StringBuilder("0");
if (scale > 0) {
pattern.append(".");
for (int i = 0; i < scale; i++) {
pattern.append("0");
}
}
DecimalFormat df = new DecimalFormat(pattern.toString());
df.setRoundingMode(RoundingMode.HALF_UP);
return df.format(value);
}
/**
* 0
*/
public static String formatValue(double value, int maxScale) {
if (Double.isNaN(value) || Double.isInfinite(value)) {
return String.valueOf(value);
}
// 先四舍五入
double rounded = roundValue(value, maxScale);
// 转换为字符串去除末尾的0
String str = String.valueOf(rounded);
if (str.contains(".")) {
str = str.replaceAll("0*$", "").replaceAll("\\.$", "");
}
return str;
}
/**
*
*/
public static Map<String, Map<String, String>> getAllStatsAsString(
List<Map<String, Object>> dataList,
List<String> keys,
int scale) {
return keys.stream()
.collect(Collectors.toMap(
key -> key,
key -> {
DoubleSummaryStatistics stats = dataList.stream()
.filter(map -> map.containsKey(key) && map.get(key) != null)
.mapToDouble(map -> convertToDouble(map.get(key)))
.summaryStatistics();
Map<String, String> result = new HashMap<>();
result.put("max", roundValueToString(stats.getCount() > 0 ? stats.getMax() : 0.0, scale));
result.put("min", roundValueToString(stats.getCount() > 0 ? stats.getMin() : 0.0, scale));
result.put("avg", roundValueToString(stats.getCount() > 0 ? stats.getAverage() : 0.0, scale));
result.put("range", roundValueToString(stats.getCount() > 0 ? stats.getMax() - stats.getMin() : 0.0, scale));
result.put("count", String.valueOf(stats.getCount()));
result.put("sum", roundValueToString(stats.getSum(), scale));
return result;
}
));
}
/**
* 使
*/
public static void main(String[] args) {
// 创建测试数据
List<Map<String, Object>> dataList = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("temperature", 25.56789);
map1.put("humidity", 60.12345);
map1.put("pressure", 100.23456);
dataList.add(map1);
Map<String, Object> map2 = new HashMap<>();
map2.put("temperature", 28.33333);
map2.put("humidity", 55.55555);
map2.put("pressure", 102.77777);
dataList.add(map2);
Map<String, Object> map3 = new HashMap<>();
map3.put("temperature", 22.11111);
map3.put("humidity", 65.99999);
map3.put("pressure", 98.65432);
dataList.add(map3);
// 要统计的key
List<String> keys = Arrays.asList("temperature", "humidity", "pressure");
System.out.println("=== 默认保留2位小数 ===");
Map<String, Map<String, Double>> allStats = getAllStats(dataList, keys);
printStats(allStats);
System.out.println("\n=== 保留3位小数 ===");
Map<String, Map<String, Double>> stats3 = getAllStats(dataList, keys, 3);
printStats(stats3);
System.out.println("\n=== 字符串格式保留2位小数===");
Map<String, Map<String, String>> stringStats = getAllStatsAsString(dataList, keys, 2);
printStringStats(stringStats);
System.out.println("\n=== 单独使用四舍五入方法 ===");
double testValue = 123.456789;
System.out.println("原始值: " + testValue);
System.out.println("保留0位: " + roundValue(testValue, 0));
System.out.println("保留2位: " + roundValue(testValue, 2));
System.out.println("保留4位: " + roundValue(testValue, 4));
System.out.println("格式化输出: " + roundValueToString(testValue, 2));
System.out.println("智能格式化: " + formatValue(testValue, 2));
}
private static void printStats(Map<String, Map<String, Double>> stats) {
for (Map.Entry<String, Map<String, Double>> entry : stats.entrySet()) {
System.out.println("\n列: " + entry.getKey());
Map<String, Double> colStats = entry.getValue();
System.out.printf(" 最大值: %.4f%n", colStats.get("max"));
System.out.printf(" 最小值: %.4f%n", colStats.get("min"));
System.out.printf(" 平均值: %.4f%n", colStats.get("avg"));
System.out.printf(" 差值: %.4f%n", colStats.get("range"));
System.out.printf(" 数量: %.0f%n", colStats.get("count"));
System.out.printf(" 总和: %.4f%n", colStats.get("sum"));
}
}
private static void printStringStats(Map<String, Map<String, String>> stats) {
for (Map.Entry<String, Map<String, String>> entry : stats.entrySet()) {
System.out.println("\n列: " + entry.getKey());
Map<String, String> colStats = entry.getValue();
System.out.println(" 最大值: " + colStats.get("max"));
System.out.println(" 最小值: " + colStats.get("min"));
System.out.println(" 平均值: " + colStats.get("avg"));
System.out.println(" 差值: " + colStats.get("range"));
System.out.println(" 数量: " + colStats.get("count"));
System.out.println(" 总和: " + colStats.get("sum"));
}
}
}
Loading…
Cancel
Save