Merge remote-tracking branch 'origin/main' into main

# Conflicts:
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java
main
HuangHuiKang 1 week ago
commit 999dd4f12e

@ -27,7 +27,10 @@ public class ErpProductPageReqVO extends PageParam {
@Schema(description = "产品编号", example = "11161")
private String code;
@Schema(description = "产品编码", example = "P-001")
private String barCode;
@Schema(description = "产品规格", example = "红色")
private String standard;
}
}

@ -27,10 +27,17 @@ import java.util.List;
@Mapper
public interface ErpProductMapper extends BaseMapperX<ErpProductDO> {
// 适配barCode和code两种情况
default String resolveCode(ErpProductPageReqVO reqVO){
String barCode = StringUtils.hasText(reqVO.getBarCode()) ? reqVO.getBarCode() : reqVO.getCode();
return barCode;
}
default PageResult<ErpProductDO> selectPage(ErpProductPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ErpProductDO>()
.likeIfPresent(ErpProductDO::getName, reqVO.getName())
.likeIfPresent(ErpProductDO::getBarCode, reqVO.getCode())
.likeIfPresent(ErpProductDO::getBarCode, resolveCode(reqVO))
.eqIfPresent(ErpProductDO::getCategoryId, reqVO.getCategoryId())
.betweenIfPresent(ErpProductDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(ErpProductDO::getId));
@ -47,9 +54,10 @@ public interface ErpProductMapper extends BaseMapperX<ErpProductDO> {
}
default PageResult<ErpProductDO> selectProductCodeExist(ErpProductPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ErpProductDO>()
.eqIfPresent(ErpProductDO::getName, reqVO.getName())
.eqIfPresent(ErpProductDO::getBarCode, reqVO.getCode())
.eqIfPresent(ErpProductDO::getBarCode, resolveCode(reqVO))
.orderByAsc(ErpProductDO::getId));
}
@ -62,9 +70,7 @@ public interface ErpProductMapper extends BaseMapperX<ErpProductDO> {
Long count = selectCount(new LambdaQueryWrapper<ErpProductDO>()
.eq(ErpProductDO::getName, reqVO.getName())
.eq(ErpProductDO::getStandard, reqVO.getStandard()));
return count > 0;
}
default Long selectCountByCategoryId(Long categoryId) {

@ -239,6 +239,14 @@ public class DeviceController {
return success(deviceContactModelDO);
}
@GetMapping("/singleDeviceFrom")
@Operation(summary = "单设备查看")
// @PreAuthorize("@ss.hasPermission('iot:device:query')")
public CommonResult<List<Map<String, Object>>> singleDeviceFrom(@RequestParam("deviceId") Long deviceId) throws JsonProcessingException {
List<Map<String, Object>> deviceContactModelDO=deviceService.singleDeviceFrom(deviceId);
return success(deviceContactModelDO);
}
@GetMapping("/historyRecord")
@Operation(summary = "历史记录查询")
@ -254,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 = "获取首页设备运行状态")
@ -287,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,10 @@ public interface DeviceService {
PageResult<DeviceSelectRespVO> getAvailableDevicePage(DevicePageReqVO pageReqVO);
List<DeviceDO> deviceList(@Valid 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;
@ -45,6 +47,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;
@ -54,6 +57,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;
@ -65,9 +69,13 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -128,12 +136,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
@ -972,6 +985,197 @@ public class DeviceServiceImpl implements DeviceService {
return resultMap;
}
@Override
public List<Map<String, Object>> singleDeviceFrom(Long deviceId) {
Map<String, List<Map<String, Object>>> resultMap = new LinkedHashMap<>();
List<Map<String, Object>> resultList = new ArrayList<>();
List<DeviceContactModelDO> records = deviceContactModelMapper.selectList(Wrappers.<DeviceContactModelDO>lambdaQuery()
.eq(DeviceContactModelDO::getDeviceId,deviceId)
.orderByDesc(DeviceContactModelDO::getId));
if (records == null || records.isEmpty()) {
return resultList;
}
try {
// 获取最新一行数据
Map<String, Object> latestRow = tdengineService.newSelectLatestRow(deviceId);
if (latestRow == null || latestRow.isEmpty()) {
return resultList;
}
// 属性类型映射
Map<Long, String> idToNameMap = deviceAttributeTypeMapper.selectList()
.stream()
.collect(Collectors.toMap(
DeviceAttributeTypeDO::getId,
DeviceAttributeTypeDO::getName
));
Map<Long, String> attributeTypeCache = new HashMap<>();
// 遍历 records 填充数据
for (DeviceContactModelDO record : records) {
String attributeCode = record.getAttributeCode();
if (attributeCode == null) continue;
Object rawValue = latestRow.get(attributeCode);
Object finalValue = DataTypeParseUtil.parse(rawValue, record.getDataType());
record.setAddressValue(finalValue);
// 属性类型名称
String attributeTypeName = "其他";
Object typeObj = record.getAttributeType();
if (typeObj != null) {
String typeStr = typeObj.toString();
try {
Long typeId = Long.parseLong(typeStr);
attributeTypeName = attributeTypeCache.computeIfAbsent(typeId,
k -> idToNameMap.getOrDefault(typeId, typeStr));
} catch (NumberFormatException e) {
attributeTypeName = typeStr;
}
}
Map<String, Object> simplifiedData = new HashMap<>();
simplifiedData.put("value", record.getAddressValue());
simplifiedData.put("label", record.getAttributeName());
simplifiedData.put("prop", "parpmeter_" + record.getId());
resultMap.computeIfAbsent(attributeTypeName, k -> new ArrayList<>()).add(simplifiedData);
}
if (!resultMap.isEmpty()) {
AtomicInteger a= new AtomicInteger(0);
resultMap.forEach((key, value) -> {
HashMap<String, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("id", a.incrementAndGet());
objectObjectHashMap.put("title", key);
objectObjectHashMap.put("items", value);
resultList.add(objectObjectHashMap);
});
}
} catch (Exception e) {
log.warn("处理设备 {} 最新数据时异常: {}", deviceId, e.getMessage());
}
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().filter(d -> d.getTypeName() != null).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(
@ -1749,6 +1953,7 @@ public class DeviceServiceImpl implements DeviceService {
return deviceMapper.selectList(pageReqVO);
}
/**
*
*

@ -2560,6 +2560,44 @@ 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();
}
}
// ========================= app报表相关接口 ===================

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