package com.gkhy.fourierSpecialGasMonitor.utils; import com.alibaba.druid.util.StringUtils; import com.gkhy.fourierSpecialGasMonitor.commons.enums.ResultCode; import com.gkhy.fourierSpecialGasMonitor.commons.exception.BusinessException; import com.gkhy.fourierSpecialGasMonitor.entity.GasConcentration; import com.gkhy.fourierSpecialGasMonitor.entity.SummaryStats; import com.gkhy.fourierSpecialGasMonitor.entity.req.SummaryStatsReqDTO; import com.gkhy.fourierSpecialGasMonitor.entity.resp.SummaryStatsPageRespDTO; import com.gkhy.fourierSpecialGasMonitor.enums.StatsTypeEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import java.lang.reflect.Field; import java.time.LocalDate; import java.time.Year; import java.time.YearMonth; import java.time.format.DateTimeParseException; import java.util.Arrays; public class SummaryUtils { private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 初始化统计数据(当当天无数据时,用新数据作为初始值) */ public static void initSummaryStats(SummaryStats stats, GasConcentration data) { // 初始化 temp 相关统计(min/max/avg 均为当前数据值) stats.setTempMin(data.getTemp()); stats.setTempMax(data.getTemp()); stats.setTempAvg(data.getTemp()); // 初始化 humidity 相关统计 stats.setHumidityMin(data.getHumidity()); stats.setHumidityMax(data.getHumidity()); stats.setHumidityAvg(data.getHumidity()); // 初始化 windSpeed 相关统计 Double windSpeed = data.getWindSpeed(); stats.setWindSpeedMin(windSpeed); stats.setWindSpeedMax(windSpeed); stats.setWindSpeedAvg(windSpeed); // 初始化 windDirection 相关统计(处理可能的 null) Integer windDirection = data.getWindDirection(); stats.setWindDirectionMin(windDirection); stats.setWindDirectionMax(windDirection); stats.setWindDirectionAvg(windDirection != null ? windDirection.doubleValue() : null); // 初始化 pressure 相关统计 Double pressure = data.getPressure(); stats.setPressureMin(pressure); stats.setPressureMax(pressure); stats.setPressureAvg(pressure); } /** * 更新统计数据(根据新数据重新计算 min/max/avg) */ public static void updateSummaryStats(SummaryStats stats, GasConcentration newData) { // 更新 temp:min 取更小值,max 取更大值,avg 重新计算(需注意:avg 不能直接用当前 avg 计算,需累计总和/数量) // 注意:此处简化处理,实际 avg 需基于总样本数计算(建议在数据库查询时直接聚合,而非内存中更新) Double newTemp = newData.getTemp(); stats.setTempMin(min(stats.getTempMin(), newTemp)); stats.setTempMax(max(stats.getTempMax(), newTemp)); // 更新 humidity Double newHumidity = newData.getHumidity(); stats.setHumidityMin(min(stats.getHumidityMin(), newHumidity)); stats.setHumidityMax(max(stats.getHumidityMax(), newHumidity)); // 更新 windSpeed Double newWindSpeed = newData.getWindSpeed(); stats.setWindSpeedMin(min(stats.getWindSpeedMin(), newWindSpeed)); stats.setWindSpeedMax(max(stats.getWindSpeedMax(), newWindSpeed)); // 更新 windDirection Integer newWindDir = newData.getWindDirection(); stats.setWindDirectionMin(min(stats.getWindDirectionMin(), newWindDir)); stats.setWindDirectionMax(max(stats.getWindDirectionMax(), newWindDir)); // 更新 pressure Double newPressure = newData.getPressure(); stats.setPressureMin(min(stats.getPressureMin(), newPressure)); stats.setPressureMax(max(stats.getPressureMax(), newPressure)); } /** * 安全计算两个 Double 的最小值(处理 null) */ private static Double min(Double a, Double b) { if (a == null) return b; if (b == null) return a; return Math.min(a, b); } /** * 安全计算两个 Integer 的最小值(处理 null) */ private static Integer min(Integer a, Integer b) { if (a == null) return b; if (b == null) return a; return Math.min(a, b); } // 同理实现 max 方法(Double 和 Integer 版本) private static Double max(Double a, Double b) { if (a == null) return b; if (b == null) return a; return Math.max(a, b); } private static Integer max(Integer a, Integer b) { if (a == null) return b; if (b == null) return a; return Math.max(a, b); } public void validateParams(SummaryStatsReqDTO reqDTO) { if (reqDTO == null) { throw new BusinessException(this.getClass(), ResultCode.PARAM_ERROR_NULL.getCode(),"请求参数不能为空"); } if (StringUtils.isEmpty(reqDTO.getType())) { throw new BusinessException(this.getClass(), ResultCode.PARAM_ERROR_NULL.getCode(),"气体类型(type)不能为空"); } if (reqDTO.getDateType() == null || !Arrays.asList(1, 2, 3).contains(reqDTO.getDateType())) { throw new BusinessException(this.getClass(), ResultCode.PARAM_ERROR_ILLEGAL.getCode(),"dateType必须为1(日)、2(月)、3(年)"); } if (StringUtils.isEmpty(reqDTO.getDate())) { throw new BusinessException(this.getClass(), ResultCode.PARAM_ERROR_NULL.getCode(),"日期(date)不能为空"); } // 校验date格式(根据dateType) try { switch (reqDTO.getDateType()) { case 1: LocalDate.parse(reqDTO.getDate()); // 校验 yyyy-MM-dd break; case 2: YearMonth.parse(reqDTO.getDate()); // 校验 yyyy-MM break; case 3: Year.parse(reqDTO.getDate()); // 校验 yyyy break; } } catch (DateTimeParseException e) { throw new BusinessException(this.getClass(), ResultCode.PARAM_ERROR_ILLEGAL.getCode(),"日期格式错误,dateType=" + reqDTO.getDateType() + "时,date应为" + (reqDTO.getDateType() == 1 ? "yyyy-MM-dd" : reqDTO.getDateType() == 2 ? "yyyy-MM" : "yyyy")); } } public SummaryStatsPageRespDTO convertToResp(SummaryStats stats, String type) { SummaryStatsPageRespDTO respDTO = new SummaryStatsPageRespDTO(); BeanUtils.copyProperties(stats, respDTO); // 复制公共字段(如 time) if (type == null) { return respDTO; // 若 type 为 null,不设置指标相关字段 } StatsTypeEnum mapper = StatsTypeEnum.getByType(type); if (mapper == null) { return respDTO; // 无效 type(理论上已在参数校验时拦截) } try { // 反射获取字段值(通过枚举映射的字段名) Field valueField = SummaryStats.class.getDeclaredField(mapper.getValueField()); Field minField = SummaryStats.class.getDeclaredField(mapper.getMinField()); Field maxField = SummaryStats.class.getDeclaredField(mapper.getMaxField()); Field avgField = SummaryStats.class.getDeclaredField(mapper.getAvgField()); valueField.setAccessible(true); minField.setAccessible(true); maxField.setAccessible(true); avgField.setAccessible(true); // 设置到响应 DTO respDTO.setValue(valueField.get(stats)); respDTO.setMin(minField.get(stats)); respDTO.setMax(maxField.get(stats)); respDTO.setAvg(avgField.get(stats)); } catch (NoSuchFieldException | IllegalAccessException e) { logger.error("转换 SummaryStats 到 DTO 失败,type={}, stats={}", type, stats, e); } return respDTO; } }