diff --git a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/controller/DataCenterController.java b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/controller/DataCenterController.java new file mode 100644 index 0000000..b29e43d --- /dev/null +++ b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/controller/DataCenterController.java @@ -0,0 +1,128 @@ +package org.jeecg.modules.appmana.controller; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.config.TenantContext; +import org.jeecg.common.constant.PollutionConstants; +import org.jeecg.common.dto.AlertSummaryDTO; +import org.jeecg.common.entity.SurvDeviceDeploy; +import org.jeecg.common.entity.SurvDisplayInfo; +import org.jeecg.common.entity.SurvPestlightAlert; +import org.jeecg.common.entity.SurvStationInfo; +import org.jeecg.common.util.DateTimeRangeUtil; +import org.jeecg.common.vo.PestLightAlertVo; +import org.jeecg.common.vo.result.DataCenterDataSummary; +import org.jeecg.common.vo.statistic.AlertSummaryVo; +import org.jeecg.modules.appmana.service.ISurvAlertRecordService; +import org.jeecg.modules.appmana.service.ISurvDeviceDeployService; +import org.jeecg.modules.appmana.service.ISurvPestlightAlertService; +import org.jeecg.modules.appmana.service.ISurvStationInfoService; +import org.jeecg.modules.appmana.service.impl.CommonServiceImpl; +import org.jeecg.modules.appmana.service.impl.IotCommonP3ServiceImpl; +import org.jeecg.modules.appmana.utils.MonitoringDataRichTextBuilder; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.time.LocalDateTime; +import java.util.*; + +@Api(tags="公共接口") +@RestController +@RequestMapping("/appmana/dataCenter") +@Slf4j +public class DataCenterController { + @Autowired + private ISurvDeviceDeployService survDeviceDeployService; + + @Autowired + private CommonServiceImpl commonService; + + @Autowired + private ISurvAlertRecordService survAlertRecordService; + + @Autowired + private ISurvStationInfoService survStationInfoService; + + /** + * 获取所有大屏信息 + */ + @GetMapping(value = "/dataSummary") + public Result dataSummary() { + DataCenterDataSummary re = new DataCenterDataSummary(); + //当前租户所有设备 + List deviceList = survDeviceDeployService.lambdaQuery() + .list(); + LocalDateTime weekStart = DateTimeRangeUtil.getWeekStart(); + LocalDateTime weekEnd = DateTimeRangeUtil.getWeekEnd(); + + LocalDateTime monthStart = DateTimeRangeUtil.getMonthStart(); + LocalDateTime monthEnd = DateTimeRangeUtil.getMonthEnd(); + + LocalDateTime yearStart = DateTimeRangeUtil.getYearStart(); + LocalDateTime yearEnd = DateTimeRangeUtil.getYearEnd(); + + + re.setWeekHtml(commonService.buildDeviceReport(deviceList,weekStart,weekEnd)); + re.setMonthHtml(commonService.buildDeviceReport(deviceList,monthStart,monthEnd)); + re.setYearHtml(commonService.buildDeviceReport(deviceList,yearStart,yearEnd)); + return Result.OK(re); + } + + /** + * 获取设备统计监测统计数据 + */ + @PostMapping(value = "/deviceAlertStatistic") + public Result deviceAlertStatistic(@RequestBody AlertSummaryDTO alertSummaryDTO) { + + alertSummaryDTO.setTenantId(TenantContext.getTenant()); + AlertSummaryVo alertSummaryVo = survAlertRecordService.getSummary(alertSummaryDTO); + return Result.OK(alertSummaryVo); + } + + /** + * 获取所有设备数据 + */ + @GetMapping(value = "/deviceList") + public Result< List> deviceList() { + List deployTypes = new ArrayList<>(); + deployTypes.add(PollutionConstants.SOIL_SURV); + deployTypes.add(PollutionConstants.AIR_SURV); + deployTypes.add(PollutionConstants.WATER_QULITY); + deployTypes.add(PollutionConstants.STINK); + deployTypes.add(PollutionConstants.WATER_LIVE); + deployTypes.add(PollutionConstants.WATER_ORIENT); + + List deviceList = survDeviceDeployService.lambdaQuery() + .in(SurvDeviceDeploy::getDeployType,deployTypes) + .orderByDesc(SurvDeviceDeploy::getDeployType) + .orderByAsc(SurvDeviceDeploy::getSortNo) + .orderByDesc(SurvDeviceDeploy::getCreateTime) + .list(); + if(deviceList!=null){ + List stationList = survStationInfoService.list(); + Map stationInfoMap = new HashMap<>(); + stationList.forEach(item->{ + stationInfoMap.put(item.getStationCode(),item); + }); + deviceList.forEach(item->{ + SurvStationInfo stationInfo = stationInfoMap.get(item.getStationCode()); + if(stationInfo!=null){ + item.setDeployDes(stationInfo.getStationName()+" - " + item.getDeployDes()); + } + }); + } + return Result.OK(deviceList); + } + +} diff --git a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/controller/SurvDisplayInfoController.java b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/controller/SurvDisplayInfoController.java index 4046cb1..bace1fe 100644 --- a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/controller/SurvDisplayInfoController.java +++ b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/controller/SurvDisplayInfoController.java @@ -20,9 +20,10 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Arrays; import java.util.Date; +import java.util.List; - /** +/** * @Description: 大屏信息管理 * @Author: jeecg-boot * @Date: 2023-07-14 @@ -168,4 +169,15 @@ public class SurvDisplayInfoController extends JeecgController getQualityData(@RequestParam String infoKey) { + List dis5 = survDisplayInfoService.getInfoByType(infoKey); + SurvDisplayInfo re = dis5.size()>0?dis5.get(0):null; + return Result.OK(re); + } + } diff --git a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/SurvAlertRecordMapper.java b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/SurvAlertRecordMapper.java index e9c14d2..3d6b1dd 100644 --- a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/SurvAlertRecordMapper.java +++ b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/SurvAlertRecordMapper.java @@ -4,7 +4,9 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import org.apache.ibatis.annotations.Param; import org.jeecg.common.entity.SurvAlertRecord; +import org.jeecg.common.vo.statistic.AlertSummayDetail; +import java.time.LocalDateTime; import java.util.List; /** @@ -20,4 +22,22 @@ public interface SurvAlertRecordMapper extends BaseMapper { IPage pageByDevice(IPage page, @Param("deviceList") List deviceList, @Param("yearStr") String yearStr); + List getSpotCheckData(@Param("deployId")String deployId,@Param("itemCode")String itemCode, @Param("dateList") List dateList,@Param("startDateTime")LocalDateTime startDateTime,@Param("endDateTime")LocalDateTime endDateTime); + + List getDailyMaxSpotCheckData( + @Param("dateList") List dateList, + @Param("startDateTime") LocalDateTime startDateTime, + @Param("endDateTime") LocalDateTime endDateTime, + @Param("deployId") String deployId, + @Param("itemCode") String itemCode + ); + + + List getMonthMaxSpotCheckData( + @Param("dateList") List dateList, + @Param("startDateTime") LocalDateTime startDateTime, + @Param("endDateTime") LocalDateTime endDateTime, + @Param("deployId") String deployId, + @Param("itemCode") String itemCode + ); } diff --git a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/SurvDeviceDeployMapper.java b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/SurvDeviceDeployMapper.java index 93fbf00..3fa7c43 100644 --- a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/SurvDeviceDeployMapper.java +++ b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/SurvDeviceDeployMapper.java @@ -28,4 +28,7 @@ public interface SurvDeviceDeployMapper extends BaseMapper { @InterceptorIgnore(illegalSql = "true", tenantLine = "true") SurvDeviceDeploy getDeviceById(@Param("deployId")String deployId, @Param("tenantId")String tenantId); + + @InterceptorIgnore(illegalSql = "true", tenantLine = "true") + SurvDeviceDeploy getDeployZhibiao(@Param("deployId")String deployId, @Param("tenantId")String tenantId); } diff --git a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/xml/SurvAlertRecordMapper.xml b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/xml/SurvAlertRecordMapper.xml index 7ad826a..d49cafd 100644 --- a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/xml/SurvAlertRecordMapper.xml +++ b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/xml/SurvAlertRecordMapper.xml @@ -30,7 +30,7 @@ - + @@ -77,4 +77,108 @@ order by ALERT_TIME desc + + + + + + + + \ No newline at end of file diff --git a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/xml/SurvDeviceDeployMapper.xml b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/xml/SurvDeviceDeployMapper.xml index 153bc22..c8309ba 100644 --- a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/xml/SurvDeviceDeployMapper.xml +++ b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/mapper/xml/SurvDeviceDeployMapper.xml @@ -147,4 +147,8 @@ + + \ No newline at end of file diff --git a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/ISurvAlertRecordService.java b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/ISurvAlertRecordService.java index bf73596..562399b 100644 --- a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/ISurvAlertRecordService.java +++ b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/ISurvAlertRecordService.java @@ -2,7 +2,9 @@ package org.jeecg.modules.appmana.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.common.dto.AlertSummaryDTO; import org.jeecg.common.entity.SurvAlertRecord; +import org.jeecg.common.vo.statistic.AlertSummaryVo; import java.util.List; @@ -17,4 +19,6 @@ public interface ISurvAlertRecordService extends IService { IPage pages(IPage page, SurvAlertRecord survAlertRecord); IPage pageByDevice(IPage page, List deviceList, String yearStr); + + AlertSummaryVo getSummary(AlertSummaryDTO alertSummaryDTO); } diff --git a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/ISurvDeviceDeployService.java b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/ISurvDeviceDeployService.java index 536e2e4..b62f53d 100644 --- a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/ISurvDeviceDeployService.java +++ b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/ISurvDeviceDeployService.java @@ -37,4 +37,6 @@ public interface ISurvDeviceDeployService extends IService { SurvDeviceDeploy getOrientDeviceByStation(String stationCode); SurvDeviceDeploy getDeviceById(String deployId,String tenantId); + + SurvDeviceDeploy getDeployZhibiao(String deployId, String tenantId); } diff --git a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/impl/CommonServiceImpl.java b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/impl/CommonServiceImpl.java index 64c1fae..b43a366 100644 --- a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/impl/CommonServiceImpl.java +++ b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/impl/CommonServiceImpl.java @@ -1,33 +1,34 @@ package org.jeecg.modules.appmana.service.impl; import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.lang3.StringUtils; import org.jeecg.common.config.TenantContext; import org.jeecg.common.constant.CommonConstants; +import org.jeecg.common.constant.DeviceReadConstants; import org.jeecg.common.constant.PollutionConstants; import org.jeecg.common.constant.enums.PollutionEnum; import org.jeecg.common.dto.CommonDTO; import org.jeecg.common.entity.*; import org.jeecg.common.vo.VOHisResult; +import org.jeecg.common.vo.statistic.AlertReport; import org.jeecg.common.vo.statistic.ScreenIndexSummaryDetailVo; -import org.jeecg.modules.appmana.service.ISurvHisdataAirService; -import org.jeecg.modules.appmana.service.ISurvHisdataLivestockwaterService; -import org.jeecg.modules.appmana.service.ISurvHisdataOrientwaterService; -import org.jeecg.modules.appmana.service.ISurvHisdataSoilService; +import org.jeecg.modules.appmana.service.*; import org.jeecg.modules.appmana.utils.HttpServletRequestUtil; +import org.jeecg.modules.appmana.utils.MonitoringDataRichTextBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; @Service @Slf4j @@ -37,6 +38,8 @@ public class CommonServiceImpl { @Autowired private ISurvHisdataSoilService hisdataSoilService; @Autowired + private ISurvHisdataVocsService vocsService; + @Autowired private ISurvHisdataOrientwaterService hisdataOrientwaterService; @Autowired private ISurvHisdataLivestockwaterService hisdataLivestockwaterService; @@ -47,6 +50,11 @@ public class CommonServiceImpl { @Autowired @Lazy private ScEquZhibiaoServiceImpl zhibiaoService; + + @Autowired + @Lazy + private ISurvAlertRecordService alertRecordService; + /** * 获取监测设备的数据 */ @@ -248,4 +256,206 @@ public class CommonServiceImpl { } return results; } + + + /** + * 构造设备的数据报告 + */ + public String buildDeviceReport(List deploys,LocalDateTime startTime,LocalDateTime endTime){ + /** + * 遍历设备类型,根据设备类型查询数据 + */ + List airList = new ArrayList<>(); + List soilList = new ArrayList<>(); + List waterList = new ArrayList<>(); + List orientIdList = new ArrayList<>(); + List liveIdList = new ArrayList<>(); + List stinkIdList = new ArrayList<>(); + + List totalList = new ArrayList<>(); + for (SurvDeviceDeploy survDeviceDeploy : deploys) { + if (PollutionConstants.SOIL_SURV.equals(survDeviceDeploy.getDeployType())) {//土壤设备 + soilList.add(survDeviceDeploy.getId()); + totalList.add(survDeviceDeploy.getId()); + } else if (PollutionConstants.AIR_SURV.equals(survDeviceDeploy.getDeployType())) {//气象设备 + airList.add(survDeviceDeploy.getId()); + totalList.add(survDeviceDeploy.getId()); + } else if (PollutionConstants.WATER_QULITY.equals(survDeviceDeploy.getDeployType())) {//水质设备 + waterList.add(survDeviceDeploy.getId()); + totalList.add(survDeviceDeploy.getId()); + }else if (PollutionConstants.WATER_ORIENT.equals(survDeviceDeploy.getDeployType())) {//面源 + orientIdList.add(survDeviceDeploy.getId()); + totalList.add(survDeviceDeploy.getId()); + } else if (PollutionConstants.WATER_LIVE.equals(survDeviceDeploy.getDeployType())) {//畜禽 + liveIdList.add(survDeviceDeploy.getId()); + totalList.add(survDeviceDeploy.getId()); + } else if (PollutionConstants.STINK.equals(survDeviceDeploy.getDeployType())) {//恶臭 + stinkIdList.add(survDeviceDeploy.getId()); + totalList.add(survDeviceDeploy.getId()); + } + } + + Long dataCounts = 0L; + //step2 根据设备类型查询数据 + if(!airList.isEmpty()){ + dataCounts = dataCounts + hisdataAirService.count(new LambdaUpdateWrapper() + .in(SurvHisdataAir::getDeployId, airList) + .between(SurvHisdataAir::getDataDateTime, startTime, endTime) + + ); + } + if(!soilList.isEmpty()){ + dataCounts = dataCounts + hisdataSoilService.count(new LambdaUpdateWrapper() + .in(SurvHisdataSoil::getDeployId, soilList) + .between(SurvHisdataSoil::getDataDateTime, startTime, endTime) + ); + } + if(!waterList.isEmpty()){ + dataCounts = dataCounts + hisdataSoilService.count(new LambdaUpdateWrapper() + .in(SurvHisdataSoil::getDeployId, waterList) + .between(SurvHisdataSoil::getDataDateTime, startTime, endTime) + ); + } + if(!orientIdList.isEmpty()){ + dataCounts = dataCounts + hisdataOrientwaterService.count(new LambdaUpdateWrapper() + .in(SurvHisdataOrientwater::getDeviceId, orientIdList) + .between(SurvHisdataOrientwater::getDataDateTime, startTime, endTime) + ); + } + if(!liveIdList.isEmpty()){ + dataCounts = dataCounts + hisdataLivestockwaterService.count(new LambdaUpdateWrapper() + .in(SurvHisdataLivestockwater::getDeviceId, liveIdList) + .between(SurvHisdataLivestockwater::getDataDateTime, startTime, endTime) + ); + } + if(!stinkIdList.isEmpty()){ + dataCounts = dataCounts + vocsService.count(new LambdaUpdateWrapper() + .in(SurvHisdataVocs::getDeployId, stinkIdList) + .between(SurvHisdataVocs::getDataDateTime, startTime, endTime) + ); + } + + List zhibiaoList = zhibiaoService.getAllChemical(totalList); + Map zhibiaoMap = new HashMap<>(); + if(!zhibiaoList.isEmpty()){ + for (ScEquZhibiao scEquZhibiao : zhibiaoList) { + zhibiaoMap.put(scEquZhibiao.getEntityField(),scEquZhibiao.getName()); + } + } + + //异常数据 + Integer abnormalCount = 0; + + List alertRecordList = alertRecordService.lambdaQuery() + .between(SurvAlertRecord::getAlertTime, startTime, endTime) + .list(); + Map alertHighMap = new HashMap<>(); + Map alertLowMap = new HashMap<>(); + List unnormalList = new ArrayList<>(); + if(!alertRecordList.isEmpty()){ + abnormalCount = alertRecordList.size(); + + for (SurvAlertRecord survAlertRecord : alertRecordList) { + unnormalList.add(survAlertRecord.getItemCode()); + + if(DeviceReadConstants.READ_TOO_LOW.equals(survAlertRecord.getReadStatus())){ + AlertReport alertReport = alertLowMap.get(survAlertRecord.getItemCode()); + + if(alertReport != null){ + BigDecimal extremeValue = new BigDecimal(survAlertRecord.getSurvValue());//当前值 + BigDecimal lastValue = new BigDecimal(alertReport.getExtremeValue());//上一个值 + //对比上一个值,如果比上一个小,就更新为当前值 + if(extremeValue.compareTo(lastValue) <= 0){ + alertReport.setExtremeValue(extremeValue.toPlainString()); + } + }else{ + alertReport = new AlertReport(); + alertReport.setItemName(survAlertRecord.getItemName()); + alertReport.setItemUnit(survAlertRecord.getSurvUnit()); + alertReport.setExtremeValue(survAlertRecord.getSurvValue()); + alertReport.setAlertCount(0); + } + alertReport.setAlertCount(alertReport.getAlertCount() + 1); + alertLowMap.put(survAlertRecord.getItemCode(),alertReport); + + }else if(DeviceReadConstants.READ_TOO_HIGH.equals(survAlertRecord.getReadStatus())){ + AlertReport alertHighReport = alertHighMap.get(survAlertRecord.getItemCode()); + + if(alertHighReport != null){ + BigDecimal extremeValue = new BigDecimal(survAlertRecord.getSurvValue());//当前值 + BigDecimal lastValue = new BigDecimal(alertHighReport.getExtremeValue());//上一个值 + //对比上一个值,如果比上一个大,就更新为当前值 + if(extremeValue.compareTo(lastValue) >= 0){ + alertHighReport.setExtremeValue(extremeValue.toPlainString()); + } + }else{ + alertHighReport = new AlertReport(); + alertHighReport.setItemName(survAlertRecord.getItemName()); + alertHighReport.setItemUnit(survAlertRecord.getSurvUnit()); + alertHighReport.setExtremeValue(survAlertRecord.getSurvValue()); + alertHighReport.setAlertCount(0); + } + alertHighReport.setAlertCount(alertHighReport.getAlertCount() + 1); + alertHighMap.put(survAlertRecord.getItemCode(),alertHighReport); + } + } + } + + MonitoringDataRichTextBuilder.MonitoringStats stats = new MonitoringDataRichTextBuilder.MonitoringStats(); + stats.setTotalSamples(dataCounts.intValue()); + stats.setIndicatorsCount(zhibiaoMap.size()); + BigDecimal total = new BigDecimal(dataCounts); + BigDecimal abnormal = new BigDecimal(abnormalCount); + + stats.setAbnormalRate(abnormal.divide(total, 3,RoundingMode.HALF_UP).doubleValue() * 100); + + + // 超上限 + List aboveList = new ArrayList<>(); + + if(!alertHighMap.isEmpty()){ + for (String s : alertHighMap.keySet()) { + AlertReport alertReport = alertHighMap.get(s); + + MonitoringDataRichTextBuilder.AbnormalItem tempItem = new MonitoringDataRichTextBuilder.AbnormalItem(); + tempItem.setIndicatorName(alertReport.getItemName()); + tempItem.setCount(alertReport.getAlertCount()); + tempItem.setExtremeValue("最高 " + alertReport.getExtremeValue()); + aboveList.add(tempItem); + } + + } + + stats.setAboveUpperLimit(aboveList); + + // 低于下限 + List belowList = new ArrayList<>(); + if(!alertLowMap.isEmpty()){ + for (String s : alertLowMap.keySet()) { + AlertReport alertReport = alertLowMap.get(s); + + MonitoringDataRichTextBuilder.AbnormalItem tempItem = new MonitoringDataRichTextBuilder.AbnormalItem(); + tempItem.setIndicatorName(alertReport.getItemName()); + tempItem.setCount(alertReport.getAlertCount()); + tempItem.setExtremeValue("最低 " + alertReport.getExtremeValue()); + belowList.add(tempItem); + } + } + stats.setBelowLowerLimit(belowList); + + + // 正常指标 + List allItems = new ArrayList<>(zhibiaoMap.keySet()); + allItems.removeAll(unnormalList); + List normalItems = new ArrayList<>(); + if(!allItems.isEmpty()){ + for (String allItem : allItems) { + String zhibiao = zhibiaoMap.get(allItem); + normalItems.add(zhibiao); + } + } + stats.setNormalIndicators(normalItems); + + return MonitoringDataRichTextBuilder.buildRichText(stats); + } } diff --git a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/impl/SurvAlertRecordServiceImpl.java b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/impl/SurvAlertRecordServiceImpl.java index 456680c..89f355c 100644 --- a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/impl/SurvAlertRecordServiceImpl.java +++ b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/impl/SurvAlertRecordServiceImpl.java @@ -1,13 +1,36 @@ package org.jeecg.modules.appmana.service.impl; +import cn.hutool.core.lang.Assert; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.constant.IotConstants; +import org.jeecg.common.constant.enums.PollutionEnum; +import org.jeecg.common.dto.AlertSummaryDTO; +import org.jeecg.common.entity.ScEquZhibiao; import org.jeecg.common.entity.SurvAlertRecord; +import org.jeecg.common.entity.SurvDeviceDeploy; +import org.jeecg.common.util.DateRangeUtil; +import org.jeecg.common.util.TimeIntervalUtils; +import org.jeecg.common.vo.iot.common.SurvItemInfo; +import org.jeecg.common.vo.statistic.AlertSummaryVo; +import org.jeecg.common.vo.statistic.AlertSummayDetail; +import org.jeecg.common.vo.statistic.CommonDateListResult; import org.jeecg.modules.appmana.mapper.SurvAlertRecordMapper; import org.jeecg.modules.appmana.service.ISurvAlertRecordService; +import org.jeecg.modules.appmana.service.ISurvDeviceDeployService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import java.util.List; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAdjusters; +import java.util.*; +import java.util.stream.Collectors; /** * @Description: surv_alert_record @@ -16,8 +39,13 @@ import java.util.List; * @Version: V1.0 */ @Service +@Slf4j public class SurvAlertRecordServiceImpl extends ServiceImpl implements ISurvAlertRecordService { + @Autowired + @Lazy + private ISurvDeviceDeployService deployService; + @Override public IPage pages(IPage page, SurvAlertRecord survAlertRecord) { return baseMapper.pages(page,survAlertRecord); @@ -27,4 +55,130 @@ public class SurvAlertRecordServiceImpl extends ServiceImpl pageByDevice(IPage page, List deviceList, String yearStr) { return baseMapper.pageByDevice(page,deviceList,yearStr); } + + @Override + public AlertSummaryVo getSummary(AlertSummaryDTO alertSummaryDTO) { + Assert.notNull(alertSummaryDTO,"传入格式错误"); + String tenantId = alertSummaryDTO.getTenantId(); + SurvDeviceDeploy deploy = deployService.getDeployZhibiao(alertSummaryDTO.getDeployId(),tenantId); + Assert.notNull(deploy,"设备无效"); + AlertSummaryVo alertSummaryVo = new AlertSummaryVo(); + List survItemInfos = new ArrayList<>(); + // step 1 所有监测项字典信息 + if(deploy.getZhibiaos()!=null && !deploy.getZhibiaos().isEmpty()){ + for (ScEquZhibiao zhibiao : deploy.getZhibiaos()) { + SurvItemInfo survItemInfo = new SurvItemInfo(); + PollutionEnum pollutionEnum = PollutionEnum.catchPollution(zhibiao.getEntityField()); + survItemInfo.setItemName(zhibiao.getName()); + survItemInfo.setColor(pollutionEnum.getColor()); + survItemInfo.setHighVal(zhibiao.getValHeight()!=null ?zhibiao.getValHeight()+"":null); + survItemInfo.setLowVal(zhibiao.getValLow()!=null?zhibiao.getValLow()+"":null); + survItemInfo.setUnit(zhibiao.getNuit()); + survItemInfo.setEntity(zhibiao.getEntityField()); + survItemInfo.setPid(zhibiao.getEleKey()); + survItemInfos.add(survItemInfo); + } + } + alertSummaryVo.setItemList(survItemInfos); + Map itemMap = survItemInfos.stream() + .collect(Collectors.toMap(SurvItemInfo::getEntity, itemss -> itemss)); + alertSummaryVo.setItemInfo(itemMap); + + //step 2:获取每5分钟 的数据 + + + String startTime = alertSummaryDTO.getStartTime(); + + + + LocalDateTime startDateTime = null; + LocalDateTime endDateTime = null; + boolean isToday = false; + LocalDateTime nowTime = LocalDateTime.now(); + LocalDate nowDate = nowTime.toLocalDate(); + if(StringUtils.isNotBlank(startTime)){ + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + if(IotConstants.day_hours.equals(alertSummaryDTO.getSummaryMode())){ + + startDateTime = LocalDateTime.of(LocalDate.parse(startTime,dtf), LocalTime.MIN); + endDateTime = LocalDateTime.of(LocalDate.parse(startTime,dtf), LocalTime.MAX).withNano(999999000); + //检查是否传入的今天 + if(nowDate.equals(startDateTime.toLocalDate())){ + isToday = true; + } + } + else if(IotConstants.month_days.equals(alertSummaryDTO.getSummaryMode())){ + startDateTime = LocalDateTime.of(LocalDate.parse(startTime + "-01",dtf), LocalTime.MIN); + endDateTime = LocalDateTime.of(startDateTime.toLocalDate().with(TemporalAdjusters.lastDayOfMonth()), LocalTime.MAX).withNano(999999000); + //检查是否传入的今天 + if(nowDate.equals(startDateTime.toLocalDate())){ + isToday = true; + } + } + else if(IotConstants.year_months.equals(alertSummaryDTO.getSummaryMode())){ + startDateTime = LocalDateTime.of(LocalDate.parse(startTime+"-01-01",dtf), LocalTime.MIN); + endDateTime = LocalDateTime.of(LocalDate.parse(startTime+"-12-31",dtf), LocalTime.MAX).withNano(999999000); + //检查是否传入的今天 + if(nowDate.equals(startDateTime.toLocalDate())){ + isToday = true; + } + } + + }else{ + isToday = true; + } + + if(isToday){//如果查询的是今天,只查询到当前时间 + if(IotConstants.day_hours.equals(alertSummaryDTO.getSummaryMode())){ + startDateTime = nowTime.minusDays(1); + endDateTime = nowTime.withNano(999999000); + } + else if(IotConstants.month_days.equals(alertSummaryDTO.getSummaryMode())){ + startDateTime = LocalDateTime.of(nowDate.getYear(),nowDate.getMonthValue(),1,0,0,0,999999000); + endDateTime = LocalDateTime.of(nowDate.getYear(),nowDate.getMonthValue(),nowDate.getDayOfMonth(),23,59,59,0); + } + else if(IotConstants.year_months.equals(alertSummaryDTO.getSummaryMode())){ + startDateTime = LocalDateTime.of(nowDate.getYear(),1,1,0,0,0,999999000); + endDateTime = LocalDateTime.of(nowDate.getYear(),12,31,23,59,59,0); + } + } + List showTime = new ArrayList<>(); + + + Map> dataMap = new HashMap<>(); + if(!survItemInfos.isEmpty()){ + for (SurvItemInfo survItemInfo : survItemInfos) { + List records = new ArrayList<>(); + if(IotConstants.day_hours.equals(alertSummaryDTO.getSummaryMode())){ + CommonDateListResult commonDateListResult = TimeIntervalUtils.generate5MinuteIntervals(startDateTime,endDateTime); + showTime = commonDateListResult.getShowList(); + records = baseMapper.getSpotCheckData(alertSummaryDTO.getDeployId(),survItemInfo.getEntity(),commonDateListResult.getQueryList(),startDateTime,endDateTime); + } + else if(IotConstants.month_days.equals(alertSummaryDTO.getSummaryMode())){ + List dateList = DateRangeUtil.getDateListBetween(startDateTime,endDateTime); + showTime = DateRangeUtil.convertDateFormatStream(dateList,"yyyy-MM-dd","MM月dd日"); + records = baseMapper.getDailyMaxSpotCheckData(dateList,startDateTime,endDateTime,alertSummaryDTO.getDeployId(),survItemInfo.getEntity()); + } + else if(IotConstants.year_months.equals(alertSummaryDTO.getSummaryMode())){ + List dateList = DateRangeUtil.getMonthListBetween(startDateTime,endDateTime); + + records = baseMapper.getMonthMaxSpotCheckData(dateList,startDateTime,endDateTime,alertSummaryDTO.getDeployId(),survItemInfo.getEntity()); + List monthList = new ArrayList<>(); + dateList.forEach(date -> { + date = date+"-01"; + monthList.add(date); + }); + showTime = DateRangeUtil.convertDateFormatStream(monthList,"yyyy-MM-dd","MM月"); + } + //组装map + if(!records.isEmpty()){ + List dataList = records.stream().map(AlertSummayDetail::getSurvValue).collect(Collectors.toList()); + dataMap.put(survItemInfo.getEntity(),dataList); + } + } + } + alertSummaryVo.setTimeList(showTime); + alertSummaryVo.setDataMap(dataMap); + return alertSummaryVo; + } } diff --git a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/impl/SurvDeviceDeployServiceImpl.java b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/impl/SurvDeviceDeployServiceImpl.java index d9451c1..ef5a076 100644 --- a/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/impl/SurvDeviceDeployServiceImpl.java +++ b/zh-module-applet/zh-applet-admin/src/main/java/org/jeecg/modules/appmana/service/impl/SurvDeviceDeployServiceImpl.java @@ -631,4 +631,9 @@ public class SurvDeviceDeployServiceImpl extends ServiceImpl aboveUpperLimit; + private List belowLowerLimit; + private List normalIndicators; + + // getter/setter + public int getTotalSamples() { return totalSamples; } + public void setTotalSamples(int totalSamples) { this.totalSamples = totalSamples; } + public int getIndicatorsCount() { return indicatorsCount; } + public void setIndicatorsCount(int indicatorsCount) { this.indicatorsCount = indicatorsCount; } + public double getAbnormalRate() { return abnormalRate; } + public void setAbnormalRate(double abnormalRate) { this.abnormalRate = abnormalRate; } + public List getAboveUpperLimit() { return aboveUpperLimit; } + public void setAboveUpperLimit(List aboveUpperLimit) { this.aboveUpperLimit = aboveUpperLimit; } + public List getBelowLowerLimit() { return belowLowerLimit; } + public void setBelowLowerLimit(List belowLowerLimit) { this.belowLowerLimit = belowLowerLimit; } + public List getNormalIndicators() { return normalIndicators; } + public void setNormalIndicators(List normalIndicators) { this.normalIndicators = normalIndicators; } + } + + /** + * 异常项 + */ + public static class AbnormalItem { + /** + * 异常指标名称 + */ + private String indicatorName; + /** + * 异常指标数量 + */ + private int count; + /** + * 异常指标异常值 + */ + private String extremeValue; + + public String getIndicatorName() { return indicatorName; } + public void setIndicatorName(String indicatorName) { this.indicatorName = indicatorName; } + public int getCount() { return count; } + public void setCount(int count) { this.count = count; } + public String getExtremeValue() { return extremeValue; } + public void setExtremeValue(String extremeValue) { this.extremeValue = extremeValue; } + } + + /** + * 生成富文本HTML内容(无Emoji,使用通用符号) + */ + public static String buildRichText(MonitoringStats stats) { + StringBuilder html = new StringBuilder(); + + // 1. 标题 - 监测数据概况 + html.append("
") + .append("【监测数据概况】") + .append("
"); + + // 2. 摘要统计信息 + html.append("
") + .append("共采集 ") + .append(stats.totalSamples) + .append("") + .append("覆盖 ") + .append(stats.indicatorsCount) + .append(" 项指标") + .append("异常率 ") + .append(String.format("%.1f", stats.abnormalRate)) + .append("%") + .append("
"); + + // 3. 超上限部分(使用 [↑] 符号) + if (stats.aboveUpperLimit != null && !stats.aboveUpperLimit.isEmpty()) { + html.append("
") + .append("[↑] 超上限:") + .append(""); + + List items = new ArrayList<>(); + for (AbnormalItem item : stats.aboveUpperLimit) { + String text = item.getIndicatorName() + ":" + + item.getCount() + " 次"; + if (item.getExtremeValue() != null && !item.getExtremeValue().isEmpty()) { + text += "(" + item.getExtremeValue() + ")"; + } + items.add(text); + } + html.append(String.join("  |  ", items)); + html.append("
"); + } + + // 4. 低于下限部分(使用 [↓] 符号) + if (stats.belowLowerLimit != null && !stats.belowLowerLimit.isEmpty()) { + html.append("
") + .append("[↓] 低于下限:") + .append(""); + + List items = new ArrayList<>(); + for (AbnormalItem item : stats.belowLowerLimit) { + String text = item.getIndicatorName() + ":" + + item.getCount() + " 次"; + if (item.getExtremeValue() != null && !item.getExtremeValue().isEmpty()) { + text += "(" + item.getExtremeValue() + ")"; + } + items.add(text); + } + html.append(String.join("  |  ", items)); + html.append("
"); + } + + // 5. 正常指标(使用 [√] 符号) + if (stats.normalIndicators != null && !stats.normalIndicators.isEmpty()) { + html.append("
") + .append("[√] 正常指标:") + .append("") + .append(String.join("、", stats.normalIndicators)) + .append("") + .append("
"); + } + + return html.toString(); + } + + /** + * 从原始数据构建(示例) + */ + public static String buildFromRawText() { + MonitoringStats stats = new MonitoringStats(); + stats.setTotalSamples(1284); + stats.setIndicatorsCount(6); + stats.setAbnormalRate(3.2); + + // 超上限 + List aboveList = new ArrayList<>(); + AbnormalItem tempItem = new AbnormalItem(); + tempItem.setIndicatorName("温度"); + tempItem.setCount(23); + tempItem.setExtremeValue("最高 42.6℃"); + aboveList.add(tempItem); + + AbnormalItem pressureItem = new AbnormalItem(); + pressureItem.setIndicatorName("气压"); + pressureItem.setCount(5); + pressureItem.setExtremeValue(null); + aboveList.add(pressureItem); + stats.setAboveUpperLimit(aboveList); + + // 低于下限 + List belowList = new ArrayList<>(); + AbnormalItem humidityItem = new AbnormalItem(); + humidityItem.setIndicatorName("湿度"); + humidityItem.setCount(8); + humidityItem.setExtremeValue("最低 18.3%"); + belowList.add(humidityItem); + stats.setBelowLowerLimit(belowList); + + // 正常指标 + List normalList = new ArrayList<>(); + normalList.add("PM2.5"); + normalList.add("CO₂"); + normalList.add("噪声"); + stats.setNormalIndicators(normalList); + + return buildRichText(stats); + } + + // ========== 主方法测试 ========== + public static void main(String[] args) { + String richText = buildFromRawText(); + System.out.println("生成的富文本HTML内容:"); + System.out.println("================================"); + System.out.println(richText); + System.out.println("================================"); + } +} diff --git a/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/dto/AlertSummaryDTO.java b/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/dto/AlertSummaryDTO.java new file mode 100644 index 0000000..e78bc1a --- /dev/null +++ b/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/dto/AlertSummaryDTO.java @@ -0,0 +1,22 @@ +package org.jeecg.common.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class AlertSummaryDTO { + @ApiModelProperty("设备ID") + private String deployId; + + @ApiModelProperty("统计模式") + private String summaryMode; + + @ApiModelProperty("开始时间") + private String startTime; + + @ApiModelProperty("结束时间") + private String endTime; + + @ApiModelProperty("租户ID") + private String tenantId; +} diff --git a/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/util/DateRangeUtil.java b/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/util/DateRangeUtil.java new file mode 100644 index 0000000..63d2ceb --- /dev/null +++ b/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/util/DateRangeUtil.java @@ -0,0 +1,549 @@ +package org.jeecg.common.util; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 日期范围工具类 - 获取两个日期之间的所有日期和月份 + */ +public class DateRangeUtil { + + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM"); + + // ==================== 日期相关方法 ==================== + + /** + * 获取两个日期之间的所有日期(包含开始和结束日期) + * @param start 开始时间 + * @param end 结束时间 + * @return 日期字符串列表,格式 yyyy-MM-dd,正序排列 + */ + public static List getDateListBetween(LocalDateTime start, LocalDateTime end) { + if (start == null || end == null) { + return new ArrayList<>(); + } + + LocalDate startDate = start.toLocalDate(); + LocalDate endDate = end.toLocalDate(); + + // 如果开始日期在结束日期之后,交换位置 + if (startDate.isAfter(endDate)) { + LocalDate temp = startDate; + startDate = endDate; + endDate = temp; + } + + return getDateListBetween(startDate, endDate); + } + + /** + * 获取两个日期之间的所有日期(包含开始和结束日期) + * @param start 开始日期 + * @param end 结束日期 + * @return 日期字符串列表,格式 yyyy-MM-dd,正序排列 + */ + public static List getDateListBetween(LocalDate start, LocalDate end) { + if (start == null || end == null) { + return new ArrayList<>(); + } + + // 如果开始日期在结束日期之后,交换位置 + if (start.isAfter(end)) { + LocalDate temp = start; + start = end; + end = temp; + } + + List dateList = new ArrayList<>(); + LocalDate current = start; + + while (!current.isAfter(end)) { + dateList.add(current.format(DATE_FORMATTER)); + current = current.plusDays(1); + } + + return dateList; + } + + /** + * 获取两个日期之间的所有日期(包含开始和结束日期) + * 使用 Stream 流式处理 + * @param start 开始时间 + * @param end 结束时间 + * @return 日期字符串列表,格式 yyyy-MM-dd,正序排列 + */ + public static List getDateListBetweenStream(LocalDateTime start, LocalDateTime end) { + if (start == null || end == null) { + return new ArrayList<>(); + } + + LocalDate startDate = start.toLocalDate(); + LocalDate endDate = end.toLocalDate(); + + // 如果开始日期在结束日期之后,交换位置 + if (startDate.isAfter(endDate)) { + LocalDate temp = startDate; + startDate = endDate; + endDate = temp; + } + + return Stream.iterate(startDate, date -> date.plusDays(1)) + .limit(startDate.until(endDate).getDays() + 1) + .map(date -> date.format(DATE_FORMATTER)) + .collect(Collectors.toList()); + } + + /** + * 获取两个日期之间的所有日期(不包含结束日期) + * @param start 开始时间 + * @param end 结束时间(不包含) + * @return 日期字符串列表,格式 yyyy-MM-dd,正序排列 + */ + public static List getDateListBetweenExclusiveEnd(LocalDateTime start, LocalDateTime end) { + if (start == null || end == null) { + return new ArrayList<>(); + } + + LocalDate startDate = start.toLocalDate(); + LocalDate endDate = end.toLocalDate(); + + // 如果开始日期在结束日期之后,交换位置 + if (startDate.isAfter(endDate)) { + LocalDate temp = startDate; + startDate = endDate; + endDate = temp; + } + + List dateList = new ArrayList<>(); + LocalDate current = startDate; + + while (current.isBefore(endDate)) { + dateList.add(current.format(DATE_FORMATTER)); + current = current.plusDays(1); + } + + return dateList; + } + + /** + * 获取两个日期之间的所有日期(不包含开始日期) + * @param start 开始时间(不包含) + * @param end 结束时间 + * @return 日期字符串列表,格式 yyyy-MM-dd,正序排列 + */ + public static List getDateListBetweenExclusiveStart(LocalDateTime start, LocalDateTime end) { + if (start == null || end == null) { + return new ArrayList<>(); + } + + LocalDate startDate = start.toLocalDate(); + LocalDate endDate = end.toLocalDate(); + + // 如果开始日期在结束日期之后,交换位置 + if (startDate.isAfter(endDate)) { + LocalDate temp = startDate; + startDate = endDate; + endDate = temp; + } + + List dateList = new ArrayList<>(); + LocalDate current = startDate.plusDays(1); + + while (!current.isAfter(endDate)) { + dateList.add(current.format(DATE_FORMATTER)); + current = current.plusDays(1); + } + + return dateList; + } + + /** + * 获取两个日期之间的所有日期(包含开始和结束日期),返回 LocalDate 对象列表 + * @param start 开始时间 + * @param end 结束时间 + * @return LocalDate 列表,正序排列 + */ + public static List getLocalDateListBetween(LocalDateTime start, LocalDateTime end) { + if (start == null || end == null) { + return new ArrayList<>(); + } + + LocalDate startDate = start.toLocalDate(); + LocalDate endDate = end.toLocalDate(); + + // 如果开始日期在结束日期之后,交换位置 + if (startDate.isAfter(endDate)) { + LocalDate temp = startDate; + startDate = endDate; + endDate = temp; + } + + List dateList = new ArrayList<>(); + LocalDate current = startDate; + + while (!current.isAfter(endDate)) { + dateList.add(current); + current = current.plusDays(1); + } + + return dateList; + } + + /** + * 获取两个日期之间的所有日期(包含开始和结束日期) + * 使用自定义日期格式 + * @param start 开始时间 + * @param end 结束时间 + * @param pattern 日期格式,如 "yyyy/MM/dd" + * @return 日期字符串列表,正序排列 + */ + public static List getDateListBetween(LocalDateTime start, LocalDateTime end, String pattern) { + if (start == null || end == null || pattern == null) { + return new ArrayList<>(); + } + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); + LocalDate startDate = start.toLocalDate(); + LocalDate endDate = end.toLocalDate(); + + // 如果开始日期在结束日期之后,交换位置 + if (startDate.isAfter(endDate)) { + LocalDate temp = startDate; + startDate = endDate; + endDate = temp; + } + + List dateList = new ArrayList<>(); + LocalDate current = startDate; + + while (!current.isAfter(endDate)) { + dateList.add(current.format(formatter)); + current = current.plusDays(1); + } + + return dateList; + } + + // ==================== 月份相关方法(新增) ==================== + + /** + * 获取两个日期之间的所有月份(包含开始和结束月份) + * @param start 开始时间 + * @param end 结束时间 + * @return 月份字符串列表,格式 yyyy-MM,正序排列 + */ + public static List getMonthListBetween(LocalDateTime start, LocalDateTime end) { + if (start == null || end == null) { + return new ArrayList<>(); + } + + YearMonth startMonth = YearMonth.from(start); + YearMonth endMonth = YearMonth.from(end); + + // 如果开始月份在结束月份之后,交换位置 + if (startMonth.isAfter(endMonth)) { + YearMonth temp = startMonth; + startMonth = endMonth; + endMonth = temp; + } + + List monthList = new ArrayList<>(); + YearMonth current = startMonth; + + while (!current.isAfter(endMonth)) { + monthList.add(current.format(MONTH_FORMATTER)); + current = current.plusMonths(1); + } + + return monthList; + } + + /** + * 获取两个日期之间的所有月份(包含开始和结束月份) + * 使用 Stream 流式处理 + * @param start 开始时间 + * @param end 结束时间 + * @return 月份字符串列表,格式 yyyy-MM,正序排列 + */ + public static List getMonthListBetweenStream(LocalDateTime start, LocalDateTime end) { + if (start == null || end == null) { + return new ArrayList<>(); + } + + YearMonth startMonth = YearMonth.from(start); + YearMonth endMonth = YearMonth.from(end); + + // 如果开始月份在结束月份之后,交换位置 + if (startMonth.isAfter(endMonth)) { + YearMonth temp = startMonth; + startMonth = endMonth; + endMonth = temp; + } + + long monthsBetween = startMonth.until(endMonth, java.time.temporal.ChronoUnit.MONTHS); + + return Stream.iterate(startMonth, month -> month.plusMonths(1)) + .limit(monthsBetween + 1) + .map(month -> month.format(MONTH_FORMATTER)) + .collect(Collectors.toList()); + } + + /** + * 获取两个日期之间的所有月份(不包含结束月份) + * @param start 开始时间 + * @param end 结束时间(不包含) + * @return 月份字符串列表,格式 yyyy-MM,正序排列 + */ + public static List getMonthListBetweenExclusiveEnd(LocalDateTime start, LocalDateTime end) { + if (start == null || end == null) { + return new ArrayList<>(); + } + + YearMonth startMonth = YearMonth.from(start); + YearMonth endMonth = YearMonth.from(end); + + // 如果开始月份在结束月份之后,交换位置 + if (startMonth.isAfter(endMonth)) { + YearMonth temp = startMonth; + startMonth = endMonth; + endMonth = temp; + } + + List monthList = new ArrayList<>(); + YearMonth current = startMonth; + + while (current.isBefore(endMonth)) { + monthList.add(current.format(MONTH_FORMATTER)); + current = current.plusMonths(1); + } + + return monthList; + } + + /** + * 获取两个日期之间的所有月份(不包含开始月份) + * @param start 开始时间(不包含) + * @param end 结束时间 + * @return 月份字符串列表,格式 yyyy-MM,正序排列 + */ + public static List getMonthListBetweenExclusiveStart(LocalDateTime start, LocalDateTime end) { + if (start == null || end == null) { + return new ArrayList<>(); + } + + YearMonth startMonth = YearMonth.from(start); + YearMonth endMonth = YearMonth.from(end); + + // 如果开始月份在结束月份之后,交换位置 + if (startMonth.isAfter(endMonth)) { + YearMonth temp = startMonth; + startMonth = endMonth; + endMonth = temp; + } + + List monthList = new ArrayList<>(); + YearMonth current = startMonth.plusMonths(1); + + while (!current.isAfter(endMonth)) { + monthList.add(current.format(MONTH_FORMATTER)); + current = current.plusMonths(1); + } + + return monthList; + } + + /** + * 获取两个日期之间的所有月份(包含开始和结束月份),返回 YearMonth 对象列表 + * @param start 开始时间 + * @param end 结束时间 + * @return YearMonth 列表,正序排列 + */ + public static List getYearMonthListBetween(LocalDateTime start, LocalDateTime end) { + if (start == null || end == null) { + return new ArrayList<>(); + } + + YearMonth startMonth = YearMonth.from(start); + YearMonth endMonth = YearMonth.from(end); + + // 如果开始月份在结束月份之后,交换位置 + if (startMonth.isAfter(endMonth)) { + YearMonth temp = startMonth; + startMonth = endMonth; + endMonth = temp; + } + + List monthList = new ArrayList<>(); + YearMonth current = startMonth; + + while (!current.isAfter(endMonth)) { + monthList.add(current); + current = current.plusMonths(1); + } + + return monthList; + } + + /** + * 获取两个日期之间的所有月份(包含开始和结束月份) + * 使用自定义月份格式 + * @param start 开始时间 + * @param end 结束时间 + * @param pattern 月份格式,如 "yyyy年MM月" + * @return 月份字符串列表,正序排列 + */ + public static List getMonthListBetween(LocalDateTime start, LocalDateTime end, String pattern) { + if (start == null || end == null || pattern == null) { + return new ArrayList<>(); + } + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); + YearMonth startMonth = YearMonth.from(start); + YearMonth endMonth = YearMonth.from(end); + + // 如果开始月份在结束月份之后,交换位置 + if (startMonth.isAfter(endMonth)) { + YearMonth temp = startMonth; + startMonth = endMonth; + endMonth = temp; + } + + List monthList = new ArrayList<>(); + YearMonth current = startMonth; + + while (!current.isAfter(endMonth)) { + monthList.add(current.format(formatter)); + current = current.plusMonths(1); + } + + return monthList; + } + + + /** + * 批量转换日期字符串格式 + * @param dateList 日期字符串列表 + * @param sourcePattern 源日期格式,如 "yyyy-MM-dd" + * @param targetPattern 目标日期格式,如 "yyyy/MM/dd" + * @return 转换后的日期字符串列表,保持原有顺序 + */ + public static List convertDateFormat(List dateList, String sourcePattern, String targetPattern) { + if (dateList == null || dateList.isEmpty() || sourcePattern == null || targetPattern == null) { + return new ArrayList<>(); + } + + DateTimeFormatter sourceFormatter = DateTimeFormatter.ofPattern(sourcePattern); + DateTimeFormatter targetFormatter = DateTimeFormatter.ofPattern(targetPattern); + + List result = new ArrayList<>(); + for (String dateStr : dateList) { + try { + // 先解析为 LocalDate,再格式化为目标格式 + LocalDate date = LocalDate.parse(dateStr, sourceFormatter); + result.add(date.format(targetFormatter)); + } catch (DateTimeParseException e) { + // 如果解析失败,跳过该日期或保留原值 + // 这里选择保留原值并打印警告 + e.printStackTrace(); + System.err.println("日期格式转换失败: " + dateStr + ", 格式: " + sourcePattern); + result.add(dateStr); + } + } + return result; + } + + /** + * 批量转换日期字符串格式(使用 Stream 流式处理) + * @param dateList 日期字符串列表 + * @param sourcePattern 源日期格式,如 "yyyy-MM-dd" + * @param targetPattern 目标日期格式,如 "yyyy/MM/dd" + * @return 转换后的日期字符串列表,保持原有顺序 + */ + public static List convertDateFormatStream(List dateList, String sourcePattern, String targetPattern) { + if (dateList == null || dateList.isEmpty() || sourcePattern == null || targetPattern == null) { + return new ArrayList<>(); + } + + DateTimeFormatter sourceFormatter = DateTimeFormatter.ofPattern(sourcePattern); + DateTimeFormatter targetFormatter = DateTimeFormatter.ofPattern(targetPattern); + + return dateList.stream() + .map(dateStr -> { + try { + LocalDate date = LocalDate.parse(dateStr, sourceFormatter); + return date.format(targetFormatter); + } catch (DateTimeParseException e) { + e.printStackTrace(); + System.err.println("日期格式转换失败: " + dateStr + ", 格式: " + sourcePattern); + return dateStr; + } + }) + .collect(Collectors.toList()); + } + + + + // ==================== 使用示例 ==================== + public static void main(String[] args) { + LocalDateTime start = LocalDateTime.of(2026, 1, 15, 10, 0, 0); + LocalDateTime end = LocalDateTime.of(2026, 7, 20, 18, 30, 0); + + System.out.println("========== 获取所有日期 =========="); + System.out.println("开始时间: " + start); + System.out.println("结束时间: " + end); + + List dateList = getDateListBetween(start, end); + System.out.println("日期列表 (包含开始和结束):"); + System.out.println("共 " + dateList.size() + " 天"); + dateList.forEach(System.out::println); + + System.out.println("\n========== 获取所有月份(新增功能) =========="); + List monthList = getMonthListBetween(start, end); + System.out.println("月份列表 (包含开始和结束月份):"); + System.out.println("共 " + monthList.size() + " 个月"); + monthList.forEach(System.out::println); + + System.out.println("\n========== 跨年月份测试 =========="); + LocalDateTime crossYearStart = LocalDateTime.of(2026, 11, 15, 0, 0, 0); + LocalDateTime crossYearEnd = LocalDateTime.of(2027, 3, 20, 0, 0, 0); + List crossYearMonths = getMonthListBetween(crossYearStart, crossYearEnd); + System.out.println("跨年月份列表:"); + crossYearMonths.forEach(System.out::println); + + System.out.println("\n========== 不包含结束月份 =========="); + List exclusiveEndMonths = getMonthListBetweenExclusiveEnd(start, end); + System.out.println("月份列表 (不包含结束月份):"); + exclusiveEndMonths.forEach(System.out::println); + + System.out.println("\n========== 不包含开始月份 =========="); + List exclusiveStartMonths = getMonthListBetweenExclusiveStart(start, end); + System.out.println("月份列表 (不包含开始月份):"); + exclusiveStartMonths.forEach(System.out::println); + + System.out.println("\n========== 使用 Stream 方式 =========="); + List streamMonths = getMonthListBetweenStream(start, end); + streamMonths.forEach(System.out::println); + + System.out.println("\n========== 返回 YearMonth 对象 =========="); + List yearMonthList = getYearMonthListBetween(start, end); + yearMonthList.forEach(System.out::println); + + System.out.println("\n========== 自定义月份格式 =========="); + List customFormatMonths = getMonthListBetween(start, end, "yyyy年MM月"); + customFormatMonths.forEach(System.out::println); + + System.out.println("\n========== 开始日期大于结束日期(自动交换) =========="); + LocalDateTime startLater = LocalDateTime.of(2026, 7, 20, 0, 0, 0); + LocalDateTime endEarlier = LocalDateTime.of(2026, 1, 15, 0, 0, 0); + List swappedMonths = getMonthListBetween(startLater, endEarlier); + System.out.println("自动交换后的月份列表:"); + swappedMonths.forEach(System.out::println); + } +} diff --git a/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/util/DateTimeRangeUtil.java b/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/util/DateTimeRangeUtil.java new file mode 100644 index 0000000..b330c93 --- /dev/null +++ b/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/util/DateTimeRangeUtil.java @@ -0,0 +1,132 @@ +package org.jeecg.common.util; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.temporal.TemporalAdjusters; + +/** + * 日期时间工具类 - 用于获取本周、本月、本年的时间范围 + */ +public class DateTimeRangeUtil { + + /** + * 获取本周的开始时间(周一 00:00:00) + */ + public static LocalDateTime getWeekStart() { + LocalDate monday = LocalDate.now().with(DayOfWeek.MONDAY); + return LocalDateTime.of(monday, LocalTime.MIN); + } + + /** + * 获取本周的结束时间(周日 23:59:59) + */ + public static LocalDateTime getWeekEnd() { + LocalDate sunday = LocalDate.now().with(DayOfWeek.SUNDAY); + return LocalDateTime.of(sunday, LocalTime.MAX); + } + + /** + * 获取本月的开始时间(1号 00:00:00) + */ + public static LocalDateTime getMonthStart() { + LocalDate firstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()); + return LocalDateTime.of(firstDay, LocalTime.MIN); + } + + /** + * 获取本月的结束时间(最后一天 23:59:59) + */ + public static LocalDateTime getMonthEnd() { + LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()); + return LocalDateTime.of(lastDay, LocalTime.MAX); + } + + /** + * 获取本年的开始时间(1月1日 00:00:00) + */ + public static LocalDateTime getYearStart() { + LocalDate firstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfYear()); + return LocalDateTime.of(firstDay, LocalTime.MIN); + } + + /** + * 获取本年的结束时间(12月31日 23:59:59) + */ + public static LocalDateTime getYearEnd() { + LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfYear()); + return LocalDateTime.of(lastDay, LocalTime.MAX); + } + + // ==================== 指定日期时间的扩展方法 ==================== + + /** + * 获取指定日期所在周的开始时间(周一 00:00:00) + */ + public static LocalDateTime getWeekStart(LocalDateTime dateTime) { + LocalDate monday = dateTime.toLocalDate().with(DayOfWeek.MONDAY); + return LocalDateTime.of(monday, LocalTime.MIN); + } + + /** + * 获取指定日期所在周的结束时间(周日 23:59:59) + */ + public static LocalDateTime getWeekEnd(LocalDateTime dateTime) { + LocalDate sunday = dateTime.toLocalDate().with(DayOfWeek.SUNDAY); + return LocalDateTime.of(sunday, LocalTime.MAX); + } + + /** + * 获取指定日期所在月的开始时间(1号 00:00:00) + */ + public static LocalDateTime getMonthStart(LocalDateTime dateTime) { + LocalDate firstDay = dateTime.toLocalDate().with(TemporalAdjusters.firstDayOfMonth()); + return LocalDateTime.of(firstDay, LocalTime.MIN); + } + + /** + * 获取指定日期所在月的结束时间(最后一天 23:59:59) + */ + public static LocalDateTime getMonthEnd(LocalDateTime dateTime) { + LocalDate lastDay = dateTime.toLocalDate().with(TemporalAdjusters.lastDayOfMonth()); + return LocalDateTime.of(lastDay, LocalTime.MAX); + } + + /** + * 获取指定日期所在年的开始时间(1月1日 00:00:00) + */ + public static LocalDateTime getYearStart(LocalDateTime dateTime) { + LocalDate firstDay = dateTime.toLocalDate().with(TemporalAdjusters.firstDayOfYear()); + return LocalDateTime.of(firstDay, LocalTime.MIN); + } + + /** + * 获取指定日期所在年的结束时间(12月31日 23:59:59) + */ + public static LocalDateTime getYearEnd(LocalDateTime dateTime) { + LocalDate lastDay = dateTime.toLocalDate().with(TemporalAdjusters.lastDayOfYear()); + return LocalDateTime.of(lastDay, LocalTime.MAX); + } + + // ==================== 使用示例 ==================== + public static void main(String[] args) { + System.out.println("========== 当前时间范围 =========="); + System.out.println("本周开始: " + getWeekStart()); + System.out.println("本周结束: " + getWeekEnd()); + System.out.println("本月开始: " + getMonthStart()); + System.out.println("本月结束: " + getMonthEnd()); + System.out.println("本年开始: " + getYearStart()); + System.out.println("本年结束: " + getYearEnd()); + + System.out.println("\n========== 指定时间范围 =========="); + LocalDateTime customTime = LocalDateTime.of(2026, 7, 15, 14, 30, 0); + System.out.println("指定时间: " + customTime); + System.out.println("所在周开始: " + getWeekStart(customTime)); + System.out.println("所在周结束: " + getWeekEnd(customTime)); + System.out.println("所在月开始: " + getMonthStart(customTime)); + System.out.println("所在月结束: " + getMonthEnd(customTime)); + System.out.println("所在年开始: " + getYearStart(customTime)); + System.out.println("所在年结束: " + getYearEnd(customTime)); + } +} \ No newline at end of file diff --git a/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/vo/result/DataCenterDataSummary.java b/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/vo/result/DataCenterDataSummary.java new file mode 100644 index 0000000..959f08c --- /dev/null +++ b/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/vo/result/DataCenterDataSummary.java @@ -0,0 +1,11 @@ +package org.jeecg.common.vo.result; + +import lombok.Data; + +@Data +public class DataCenterDataSummary { + + private String weekHtml; + private String monthHtml; + private String yearHtml; +} diff --git a/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/vo/statistic/AlertReport.java b/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/vo/statistic/AlertReport.java new file mode 100644 index 0000000..4d1a84f --- /dev/null +++ b/zh-module-applet/zh-applet-common/src/main/java/org/jeecg/common/vo/statistic/AlertReport.java @@ -0,0 +1,23 @@ +package org.jeecg.common.vo.statistic; + +import lombok.Data; + +@Data +public class AlertReport { + /** + * 异常项名称 + */ + private String itemName; + /** + * 异常项单位 + */ + private String itemUnit; + /** + * 极限值 + */ + private String extremeValue; + /** + * 异常次数 + */ + private Integer alertCount; +}