增加告警年统计、月统计接口,增加设备列表接口

This commit is contained in:
zhangyue 2026-07-25 18:20:41 +08:00
parent 94953eb330
commit 4cda521e81
17 changed files with 1589 additions and 10 deletions

View File

@ -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<DataCenterDataSummary> dataSummary() {
DataCenterDataSummary re = new DataCenterDataSummary();
//当前租户所有设备
List<SurvDeviceDeploy> 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<AlertSummaryVo> deviceAlertStatistic(@RequestBody AlertSummaryDTO alertSummaryDTO) {
alertSummaryDTO.setTenantId(TenantContext.getTenant());
AlertSummaryVo alertSummaryVo = survAlertRecordService.getSummary(alertSummaryDTO);
return Result.OK(alertSummaryVo);
}
/**
* 获取所有设备数据
*/
@GetMapping(value = "/deviceList")
public Result< List<SurvDeviceDeploy>> deviceList() {
List<String> 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<SurvDeviceDeploy> deviceList = survDeviceDeployService.lambdaQuery()
.in(SurvDeviceDeploy::getDeployType,deployTypes)
.orderByDesc(SurvDeviceDeploy::getDeployType)
.orderByAsc(SurvDeviceDeploy::getSortNo)
.orderByDesc(SurvDeviceDeploy::getCreateTime)
.list();
if(deviceList!=null){
List<SurvStationInfo> stationList = survStationInfoService.list();
Map<String,SurvStationInfo> 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);
}
}

View File

@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
@ -168,4 +169,15 @@ public class SurvDisplayInfoController extends JeecgController<SurvDisplayInfo,
return super.importExcel(request, response, SurvDisplayInfo.class);
}
/**
* 获取所有大屏信息
*/
@GetMapping(value = "/getQualityData")
public Result<SurvDisplayInfo> getQualityData(@RequestParam String infoKey) {
List<SurvDisplayInfo> dis5 = survDisplayInfoService.getInfoByType(infoKey);
SurvDisplayInfo re = dis5.size()>0?dis5.get(0):null;
return Result.OK(re);
}
}

View File

@ -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<SurvAlertRecord> {
IPage<SurvAlertRecord> pageByDevice(IPage<SurvAlertRecord> page, @Param("deviceList") List<String> deviceList, @Param("yearStr") String yearStr);
List<AlertSummayDetail> getSpotCheckData(@Param("deployId")String deployId,@Param("itemCode")String itemCode, @Param("dateList") List<String> dateList,@Param("startDateTime")LocalDateTime startDateTime,@Param("endDateTime")LocalDateTime endDateTime);
List<AlertSummayDetail> getDailyMaxSpotCheckData(
@Param("dateList") List<String> dateList,
@Param("startDateTime") LocalDateTime startDateTime,
@Param("endDateTime") LocalDateTime endDateTime,
@Param("deployId") String deployId,
@Param("itemCode") String itemCode
);
List<AlertSummayDetail> getMonthMaxSpotCheckData(
@Param("dateList") List<String> dateList,
@Param("startDateTime") LocalDateTime startDateTime,
@Param("endDateTime") LocalDateTime endDateTime,
@Param("deployId") String deployId,
@Param("itemCode") String itemCode
);
}

View File

@ -28,4 +28,7 @@ public interface SurvDeviceDeployMapper extends BaseMapper<SurvDeviceDeploy> {
@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);
}

View File

@ -77,4 +77,108 @@
</where>
order by ALERT_TIME desc
</select>
<select id="getSpotCheckData" resultType="org.jeecg.common.vo.statistic.AlertSummayDetail">
SELECT
ti.interval_start as alertTime,
COALESCE(ad.ID, 0) as id,
COALESCE(ad.DEPLOY_ID, #{deployId}) as deployId,
COALESCE(ad.NORMAL_VALUE, 0) as normalValue,
COALESCE(ad.SURV_VALUE, 0) as survValue,
COALESCE(ad.ITEM_CODE, #{itemCode}) as itemCode,
CASE WHEN ad.ALERT_TIME IS NULL THEN 0 ELSE 1 END as hasData
FROM (
<foreach item="items" collection="dateList" separator="union all">
SELECT CAST(#{items} AS DATETIME) as interval_start
</foreach>
) ti
LEFT JOIN (
SELECT * FROM (
SELECT
t.*,
ROW_NUMBER() OVER (
PARTITION BY DATE(t.ALERT_TIME), HOUR(t.ALERT_TIME), FLOOR(MINUTE(t.ALERT_TIME) / 15)
ORDER BY t.ALERT_TIME ASC
) AS rn
FROM surv_alert_record t
WHERE t.ALERT_TIME &gt;= #{startDateTime}
AND t.ALERT_TIME &lt;= #{endDateTime}
AND t.DEPLOY_ID = #{deployId}
AND t.ITEM_CODE = #{itemCode}
) temp
WHERE temp.rn = 1
) ad ON ad.ALERT_TIME &gt;= ti.interval_start
AND ad.ALERT_TIME &lt; DATE_ADD(ti.interval_start, INTERVAL 5 MINUTE)
ORDER BY ti.interval_start;
</select>
<select id="getDailyMaxSpotCheckData" resultType="org.jeecg.common.vo.statistic.AlertSummayDetail">
SELECT
ti.interval_start as alertTime,
COALESCE(ad.ID, 0) as id,
COALESCE(ad.DEPLOY_ID, #{deployId}) as deployId,
COALESCE(ad.NORMAL_VALUE, 0) as normalValue,
COALESCE(ad.SURV_VALUE, 0) as survValue,
COALESCE(ad.ITEM_CODE, #{itemCode}) as itemCode,
CASE WHEN ad.ALERT_TIME IS NULL THEN 0 ELSE 1 END as hasData
FROM (
<foreach item="items" collection="dateList" separator="union all">
SELECT CAST(#{items} AS DATE) as interval_start
</foreach>
) ti
LEFT JOIN (
SELECT * FROM (
SELECT
t.*,
ROW_NUMBER() OVER (
PARTITION BY DATE(t.ALERT_TIME)
ORDER BY CAST(t.SURV_VALUE AS DECIMAL(18,2)) DESC, t.ALERT_TIME ASC
) AS rn
FROM surv_alert_record t
WHERE t.ALERT_TIME &gt;= #{startDateTime}
AND t.ALERT_TIME &lt;= #{endDateTime}
AND t.DEPLOY_ID = #{deployId}
AND t.ITEM_CODE = #{itemCode}
) temp
WHERE temp.rn = 1
) ad ON DATE(ad.ALERT_TIME) = ti.interval_start
ORDER BY ti.interval_start;
</select>
<!-- 按月查询,取每月 SURV_VALUE 最大的记录 -->
<select id="getMonthMaxSpotCheckData" resultType="org.jeecg.common.vo.statistic.AlertSummayDetail">
SELECT
ti.interval_start as alertTime,
COALESCE(ad.ID, 0) as id,
COALESCE(ad.DEPLOY_ID, #{deployId}) as deployId,
COALESCE(ad.NORMAL_VALUE, 0) as normalValue,
COALESCE(ad.SURV_VALUE, 0) as survValue,
COALESCE(ad.ITEM_CODE, #{itemCode}) as itemCode,
CASE WHEN ad.ALERT_TIME IS NULL THEN 0 ELSE 1 END as hasData
FROM (
<foreach item="items" collection="dateList" separator="union all">
-- 支持 yyyy-MM 和 yyyy-MM-dd 两种格式
SELECT CAST(CONCAT(#{items}, IF(LENGTH(#{items}) = 7, '-01', '')) AS DATE) as interval_start
</foreach>
) ti
LEFT JOIN (
SELECT * FROM (
SELECT
t.*,
ROW_NUMBER() OVER (
PARTITION BY YEAR(t.ALERT_TIME), MONTH(t.ALERT_TIME)
ORDER BY CAST(t.SURV_VALUE AS DECIMAL(18,2)) DESC, t.ALERT_TIME ASC
) AS rn
FROM surv_alert_record t
WHERE t.ALERT_TIME &gt;= #{startDateTime}
AND t.ALERT_TIME &lt;= #{endDateTime}
AND t.DEPLOY_ID = #{deployId}
AND t.ITEM_CODE = #{itemCode}
) temp
WHERE temp.rn = 1
) ad ON YEAR(ad.ALERT_TIME) = YEAR(ti.interval_start)
AND MONTH(ad.ALERT_TIME) = MONTH(ti.interval_start)
ORDER BY ti.interval_start;
</select>
</mapper>

View File

@ -147,4 +147,8 @@
<select id="getDeviceById" resultType="org.jeecg.common.entity.SurvDeviceDeploy">
select <include refid="basesql"/> from surv_device_deploy where IS_DEL = 0 AND ID = #{deployId} AND TENANT_ID = #{tenantId} limit 1
</select>
<select id="getDeployZhibiao" resultMap="zhibiaoMap">
select <include refid="basesql"/> from surv_device_deploy where IS_DEL = 0 AND ID = #{deployId} AND TENANT_ID = #{tenantId} limit 1
</select>
</mapper>

View File

@ -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<SurvAlertRecord> {
IPage<SurvAlertRecord> pages(IPage<SurvAlertRecord> page, SurvAlertRecord survAlertRecord);
IPage<SurvAlertRecord> pageByDevice(IPage<SurvAlertRecord> page, List<String> deviceList, String yearStr);
AlertSummaryVo getSummary(AlertSummaryDTO alertSummaryDTO);
}

View File

@ -37,4 +37,6 @@ public interface ISurvDeviceDeployService extends IService<SurvDeviceDeploy> {
SurvDeviceDeploy getOrientDeviceByStation(String stationCode);
SurvDeviceDeploy getDeviceById(String deployId,String tenantId);
SurvDeviceDeploy getDeployZhibiao(String deployId, String tenantId);
}

View File

@ -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<SurvDeviceDeploy> deploys,LocalDateTime startTime,LocalDateTime endTime){
/**
* 遍历设备类型根据设备类型查询数据
*/
List<String> airList = new ArrayList<>();
List<String> soilList = new ArrayList<>();
List<String> waterList = new ArrayList<>();
List<String> orientIdList = new ArrayList<>();
List<String> liveIdList = new ArrayList<>();
List<String> stinkIdList = new ArrayList<>();
List<String> 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<SurvHisdataAir>()
.in(SurvHisdataAir::getDeployId, airList)
.between(SurvHisdataAir::getDataDateTime, startTime, endTime)
);
}
if(!soilList.isEmpty()){
dataCounts = dataCounts + hisdataSoilService.count(new LambdaUpdateWrapper<SurvHisdataSoil>()
.in(SurvHisdataSoil::getDeployId, soilList)
.between(SurvHisdataSoil::getDataDateTime, startTime, endTime)
);
}
if(!waterList.isEmpty()){
dataCounts = dataCounts + hisdataSoilService.count(new LambdaUpdateWrapper<SurvHisdataSoil>()
.in(SurvHisdataSoil::getDeployId, waterList)
.between(SurvHisdataSoil::getDataDateTime, startTime, endTime)
);
}
if(!orientIdList.isEmpty()){
dataCounts = dataCounts + hisdataOrientwaterService.count(new LambdaUpdateWrapper<SurvHisdataOrientwater>()
.in(SurvHisdataOrientwater::getDeviceId, orientIdList)
.between(SurvHisdataOrientwater::getDataDateTime, startTime, endTime)
);
}
if(!liveIdList.isEmpty()){
dataCounts = dataCounts + hisdataLivestockwaterService.count(new LambdaUpdateWrapper<SurvHisdataLivestockwater>()
.in(SurvHisdataLivestockwater::getDeviceId, liveIdList)
.between(SurvHisdataLivestockwater::getDataDateTime, startTime, endTime)
);
}
if(!stinkIdList.isEmpty()){
dataCounts = dataCounts + vocsService.count(new LambdaUpdateWrapper<SurvHisdataVocs>()
.in(SurvHisdataVocs::getDeployId, stinkIdList)
.between(SurvHisdataVocs::getDataDateTime, startTime, endTime)
);
}
List<ScEquZhibiao> zhibiaoList = zhibiaoService.getAllChemical(totalList);
Map<String,String> zhibiaoMap = new HashMap<>();
if(!zhibiaoList.isEmpty()){
for (ScEquZhibiao scEquZhibiao : zhibiaoList) {
zhibiaoMap.put(scEquZhibiao.getEntityField(),scEquZhibiao.getName());
}
}
//异常数据
Integer abnormalCount = 0;
List<SurvAlertRecord> alertRecordList = alertRecordService.lambdaQuery()
.between(SurvAlertRecord::getAlertTime, startTime, endTime)
.list();
Map<String, AlertReport> alertHighMap = new HashMap<>();
Map<String, AlertReport> alertLowMap = new HashMap<>();
List<String> 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<MonitoringDataRichTextBuilder.AbnormalItem> 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<MonitoringDataRichTextBuilder.AbnormalItem> 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<String> allItems = new ArrayList<>(zhibiaoMap.keySet());
allItems.removeAll(unnormalList);
List<String> 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);
}
}

View File

@ -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<SurvAlertRecordMapper, SurvAlertRecord> implements ISurvAlertRecordService {
@Autowired
@Lazy
private ISurvDeviceDeployService deployService;
@Override
public IPage<SurvAlertRecord> pages(IPage<SurvAlertRecord> page, SurvAlertRecord survAlertRecord) {
return baseMapper.pages(page,survAlertRecord);
@ -27,4 +55,130 @@ public class SurvAlertRecordServiceImpl extends ServiceImpl<SurvAlertRecordMappe
public IPage<SurvAlertRecord> pageByDevice(IPage<SurvAlertRecord> page, List<String> 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<SurvItemInfo> 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<String, SurvItemInfo> 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<String> showTime = new ArrayList<>();
Map<String,List<String>> dataMap = new HashMap<>();
if(!survItemInfos.isEmpty()){
for (SurvItemInfo survItemInfo : survItemInfos) {
List<AlertSummayDetail> 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<String> 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<String> dateList = DateRangeUtil.getMonthListBetween(startDateTime,endDateTime);
records = baseMapper.getMonthMaxSpotCheckData(dateList,startDateTime,endDateTime,alertSummaryDTO.getDeployId(),survItemInfo.getEntity());
List<String> 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<String> dataList = records.stream().map(AlertSummayDetail::getSurvValue).collect(Collectors.toList());
dataMap.put(survItemInfo.getEntity(),dataList);
}
}
}
alertSummaryVo.setTimeList(showTime);
alertSummaryVo.setDataMap(dataMap);
return alertSummaryVo;
}
}

View File

@ -631,4 +631,9 @@ public class SurvDeviceDeployServiceImpl extends ServiceImpl<SurvDeviceDeployMap
public SurvDeviceDeploy getDeviceById(String deployId, String tenantId) {
return baseMapper.getDeviceById(deployId,tenantId);
}
@Override
public SurvDeviceDeploy getDeployZhibiao(String deployId, String tenantId) {
return baseMapper.getDeployZhibiao(deployId,tenantId);
}
}

View File

@ -0,0 +1,196 @@
package org.jeecg.modules.appmana.utils;
import java.util.ArrayList;
import java.util.List;
public class MonitoringDataRichTextBuilder {
/**
* 监测数据统计信息
*/
public static class MonitoringStats {
/**
* 总样本数
*/
private int totalSamples;
/**
* 指标总数
*/
private int indicatorsCount;
/**
* 异常指标占比
*/
private double abnormalRate;
private List<AbnormalItem> aboveUpperLimit;
private List<AbnormalItem> belowLowerLimit;
private List<String> 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<AbnormalItem> getAboveUpperLimit() { return aboveUpperLimit; }
public void setAboveUpperLimit(List<AbnormalItem> aboveUpperLimit) { this.aboveUpperLimit = aboveUpperLimit; }
public List<AbnormalItem> getBelowLowerLimit() { return belowLowerLimit; }
public void setBelowLowerLimit(List<AbnormalItem> belowLowerLimit) { this.belowLowerLimit = belowLowerLimit; }
public List<String> getNormalIndicators() { return normalIndicators; }
public void setNormalIndicators(List<String> 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("<div style='font-size:18px;font-weight:bold;color:#333;margin-bottom:12px;'>")
.append("【监测数据概况】")
.append("</div>");
// 2. 摘要统计信息
html.append("<div style='background:#f5f7fa;padding:10px 14px;border-radius:6px;margin-bottom:14px;'>")
.append("<span style='margin-right:20px;'>共采集 <strong style='color:#1890ff;'>")
.append(stats.totalSamples)
.append("</strong> 次</span>")
.append("<span style='margin-right:20px;'>覆盖 <strong style='color:#1890ff;'>")
.append(stats.indicatorsCount)
.append("</strong> 项指标</span>")
.append("<span>异常率 <strong style='color:")
.append(stats.abnormalRate > 5 ? "#f5222d" : "#faad14")
.append(";'>")
.append(String.format("%.1f", stats.abnormalRate))
.append("%</strong></span>")
.append("</div>");
// 3. 超上限部分使用 [] 符号
if (stats.aboveUpperLimit != null && !stats.aboveUpperLimit.isEmpty()) {
html.append("<div style='margin-bottom:8px;'>")
.append("<span style='font-weight:bold;color:#cf1322;'>[↑] 超上限:</span>")
.append("<span style='font-size:14px;'>");
List<String> items = new ArrayList<>();
for (AbnormalItem item : stats.aboveUpperLimit) {
String text = item.getIndicatorName() + "<span style='color:#cf1322;font-weight:bold;'>"
+ item.getCount() + " 次</span>";
if (item.getExtremeValue() != null && !item.getExtremeValue().isEmpty()) {
text += "" + item.getExtremeValue() + "";
}
items.add(text);
}
html.append(String.join(" &nbsp;|&nbsp; ", items));
html.append("</span></div>");
}
// 4. 低于下限部分使用 [] 符号
if (stats.belowLowerLimit != null && !stats.belowLowerLimit.isEmpty()) {
html.append("<div style='margin-bottom:8px;'>")
.append("<span style='font-weight:bold;color:#389e0d;'>[↓] 低于下限:</span>")
.append("<span style='font-size:14px;'>");
List<String> items = new ArrayList<>();
for (AbnormalItem item : stats.belowLowerLimit) {
String text = item.getIndicatorName() + "<span style='color:#389e0d;font-weight:bold;'>"
+ item.getCount() + " 次</span>";
if (item.getExtremeValue() != null && !item.getExtremeValue().isEmpty()) {
text += "" + item.getExtremeValue() + "";
}
items.add(text);
}
html.append(String.join(" &nbsp;|&nbsp; ", items));
html.append("</span></div>");
}
// 5. 正常指标使用 [] 符号
if (stats.normalIndicators != null && !stats.normalIndicators.isEmpty()) {
html.append("<div style='margin-top:8px;padding-top:8px;border-top:1px dashed #e8e8e8;'>")
.append("<span style='font-weight:bold;color:#52c41a;'>[√] 正常指标:</span>")
.append("<span style='font-size:14px;color:#595959;'>")
.append(String.join("", stats.normalIndicators))
.append("</span>")
.append("</div>");
}
return html.toString();
}
/**
* 从原始数据构建示例
*/
public static String buildFromRawText() {
MonitoringStats stats = new MonitoringStats();
stats.setTotalSamples(1284);
stats.setIndicatorsCount(6);
stats.setAbnormalRate(3.2);
// 超上限
List<AbnormalItem> 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<AbnormalItem> belowList = new ArrayList<>();
AbnormalItem humidityItem = new AbnormalItem();
humidityItem.setIndicatorName("湿度");
humidityItem.setCount(8);
humidityItem.setExtremeValue("最低 18.3%");
belowList.add(humidityItem);
stats.setBelowLowerLimit(belowList);
// 正常指标
List<String> 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("================================");
}
}

View File

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

View File

@ -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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<LocalDate> 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<LocalDate> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<YearMonth> 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<YearMonth> 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<String> 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<String> 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<String> convertDateFormat(List<String> 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<String> 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<String> convertDateFormatStream(List<String> 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<String> dateList = getDateListBetween(start, end);
System.out.println("日期列表 (包含开始和结束):");
System.out.println("" + dateList.size() + "");
dateList.forEach(System.out::println);
System.out.println("\n========== 获取所有月份(新增功能) ==========");
List<String> 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<String> crossYearMonths = getMonthListBetween(crossYearStart, crossYearEnd);
System.out.println("跨年月份列表:");
crossYearMonths.forEach(System.out::println);
System.out.println("\n========== 不包含结束月份 ==========");
List<String> exclusiveEndMonths = getMonthListBetweenExclusiveEnd(start, end);
System.out.println("月份列表 (不包含结束月份):");
exclusiveEndMonths.forEach(System.out::println);
System.out.println("\n========== 不包含开始月份 ==========");
List<String> exclusiveStartMonths = getMonthListBetweenExclusiveStart(start, end);
System.out.println("月份列表 (不包含开始月份):");
exclusiveStartMonths.forEach(System.out::println);
System.out.println("\n========== 使用 Stream 方式 ==========");
List<String> streamMonths = getMonthListBetweenStream(start, end);
streamMonths.forEach(System.out::println);
System.out.println("\n========== 返回 YearMonth 对象 ==========");
List<YearMonth> yearMonthList = getYearMonthListBetween(start, end);
yearMonthList.forEach(System.out::println);
System.out.println("\n========== 自定义月份格式 ==========");
List<String> 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<String> swappedMonths = getMonthListBetween(startLater, endEarlier);
System.out.println("自动交换后的月份列表:");
swappedMonths.forEach(System.out::println);
}
}

View File

@ -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));
}
}

View File

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

View File

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