增加站点设备多项统计接口
This commit is contained in:
parent
0e92650804
commit
fe786d39ce
|
|
@ -9,6 +9,7 @@ import org.jeecg.common.api.vo.Result;
|
||||||
import org.jeecg.common.config.TenantContext;
|
import org.jeecg.common.config.TenantContext;
|
||||||
import org.jeecg.common.constant.PollutionConstants;
|
import org.jeecg.common.constant.PollutionConstants;
|
||||||
import org.jeecg.common.dto.AlertSummaryDTO;
|
import org.jeecg.common.dto.AlertSummaryDTO;
|
||||||
|
import org.jeecg.common.dto.SurvSummaryDTO;
|
||||||
import org.jeecg.common.entity.SurvDeviceDeploy;
|
import org.jeecg.common.entity.SurvDeviceDeploy;
|
||||||
import org.jeecg.common.entity.SurvDisplayInfo;
|
import org.jeecg.common.entity.SurvDisplayInfo;
|
||||||
import org.jeecg.common.entity.SurvPestlightAlert;
|
import org.jeecg.common.entity.SurvPestlightAlert;
|
||||||
|
|
@ -17,10 +18,8 @@ import org.jeecg.common.util.DateTimeRangeUtil;
|
||||||
import org.jeecg.common.vo.PestLightAlertVo;
|
import org.jeecg.common.vo.PestLightAlertVo;
|
||||||
import org.jeecg.common.vo.result.DataCenterDataSummary;
|
import org.jeecg.common.vo.result.DataCenterDataSummary;
|
||||||
import org.jeecg.common.vo.statistic.AlertSummaryVo;
|
import org.jeecg.common.vo.statistic.AlertSummaryVo;
|
||||||
import org.jeecg.modules.appmana.service.ISurvAlertRecordService;
|
import org.jeecg.common.vo.statistic.SurvSummaryVo;
|
||||||
import org.jeecg.modules.appmana.service.ISurvDeviceDeployService;
|
import org.jeecg.modules.appmana.service.*;
|
||||||
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.CommonServiceImpl;
|
||||||
import org.jeecg.modules.appmana.service.impl.IotCommonP3ServiceImpl;
|
import org.jeecg.modules.appmana.service.impl.IotCommonP3ServiceImpl;
|
||||||
import org.jeecg.modules.appmana.utils.MonitoringDataRichTextBuilder;
|
import org.jeecg.modules.appmana.utils.MonitoringDataRichTextBuilder;
|
||||||
|
|
@ -48,12 +47,13 @@ public class DataCenterController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private CommonServiceImpl commonService;
|
private CommonServiceImpl commonService;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ISurvAlertRecordService survAlertRecordService;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISurvStationInfoService survStationInfoService;
|
private ISurvStationInfoService survStationInfoService;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有大屏信息
|
* 获取所有大屏信息
|
||||||
*/
|
*/
|
||||||
|
|
@ -83,11 +83,12 @@ public class DataCenterController {
|
||||||
* 获取设备统计监测统计数据
|
* 获取设备统计监测统计数据
|
||||||
*/
|
*/
|
||||||
@PostMapping(value = "/deviceAlertStatistic")
|
@PostMapping(value = "/deviceAlertStatistic")
|
||||||
public Result<AlertSummaryVo> deviceAlertStatistic(@RequestBody AlertSummaryDTO alertSummaryDTO) {
|
public Result<SurvSummaryVo> deviceAlertStatistic(@RequestBody SurvSummaryDTO survSummaryDTO) {
|
||||||
|
|
||||||
alertSummaryDTO.setTenantId(TenantContext.getTenant());
|
survSummaryDTO.setTenantId(TenantContext.getTenant());
|
||||||
AlertSummaryVo alertSummaryVo = survAlertRecordService.getSummary(alertSummaryDTO);
|
SurvSummaryVo survSummaryVo = commonService.getSurvSummary(survSummaryDTO);
|
||||||
return Result.OK(alertSummaryVo);
|
|
||||||
|
return Result.OK(survSummaryVo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.jeecg.common.api.vo.Result;
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
import org.jeecg.common.dto.SurvSummaryDTO;
|
||||||
import org.jeecg.common.entity.PollutionSetVo;
|
import org.jeecg.common.entity.PollutionSetVo;
|
||||||
import org.jeecg.common.entity.SurvDeviceDeploy;
|
import org.jeecg.common.entity.SurvDeviceDeploy;
|
||||||
import org.jeecg.common.iot.common.VOIntegrateStatistic;
|
import org.jeecg.common.iot.common.VOIntegrateStatistic;
|
||||||
|
|
@ -16,6 +17,7 @@ import org.jeecg.common.vo.CommonDataTrans;
|
||||||
import org.jeecg.common.vo.VOBigScreenSurvQ;
|
import org.jeecg.common.vo.VOBigScreenSurvQ;
|
||||||
import org.jeecg.common.vo.VOHisResult;
|
import org.jeecg.common.vo.VOHisResult;
|
||||||
import org.jeecg.common.vo.inner.*;
|
import org.jeecg.common.vo.inner.*;
|
||||||
|
import org.jeecg.common.vo.statistic.SurvSummaryVo;
|
||||||
import org.jeecg.modules.appmana.service.ISurvDeviceDeployService;
|
import org.jeecg.modules.appmana.service.ISurvDeviceDeployService;
|
||||||
import org.jeecg.modules.appmana.service.impl.CommonServiceImpl;
|
import org.jeecg.modules.appmana.service.impl.CommonServiceImpl;
|
||||||
import org.jeecg.modules.appmana.service.impl.IotCommonP3ServiceImpl;
|
import org.jeecg.modules.appmana.service.impl.IotCommonP3ServiceImpl;
|
||||||
|
|
@ -180,4 +182,16 @@ public class InnerController {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "13. 站点设备多项统计图", notes = "")
|
||||||
|
@ApiOperationSupport(order = 13)
|
||||||
|
@PostMapping(value = "/stationDeviceSummary")
|
||||||
|
public SurvSummaryVo stationDeviceSummary(@RequestBody SurvSummaryDTO survSummaryDTO) {
|
||||||
|
if(survSummaryDTO!=null){
|
||||||
|
return commonService.getSurvSummary(survSummaryDTO);
|
||||||
|
}else {
|
||||||
|
return new SurvSummaryVo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package org.jeecg.modules.appmana.mapper;
|
||||||
|
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.jeecg.common.dto.statistic.FlexibleTimeQueryDTO;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 灵活监测数据Mapper
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface FlexibleMonitorMapper extends BaseMapper<Object> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 灵活时间维度统计(核心方法)
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> getFlexibleTimeStats(@Param("query") FlexibleTimeQueryDTO query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查表是否存在
|
||||||
|
*/
|
||||||
|
Integer checkTableExists(@Param("tableName") String tableName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取表的数值列信息
|
||||||
|
*/
|
||||||
|
List<Map<String, String>> getTableColumns(@Param("tableName") String tableName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取表统计信息
|
||||||
|
*/
|
||||||
|
Map<String, Object> getStatistics(@Param("tableName") String tableName);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,237 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="org.jeecg.modules.appmana.mapper.FlexibleMonitorMapper">
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 1. 核心查询:灵活时间维度统计(MySQL 8.4 版本) -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<select id="getFlexibleTimeStats" resultType="java.util.LinkedHashMap">
|
||||||
|
<bind name="timeCol" value="query.timeColumn != null ? query.timeColumn : 'collect_time'"/>
|
||||||
|
<bind name="dimension" value="query.dimension.name()"/>
|
||||||
|
<bind name="fillEmpty" value="query.fillEmpty != null ? query.fillEmpty : true"/>
|
||||||
|
<bind name="emptyVal" value="query.emptyValue != null ? query.emptyValue : 0"/>
|
||||||
|
|
||||||
|
<!-- 判断是否使用自定义时间点 -->
|
||||||
|
<choose>
|
||||||
|
<when test="query.customTimePoints != null and query.customTimePoints.size() > 0">
|
||||||
|
<bind name="useCustomPoints" value="true"/>
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
<bind name="useCustomPoints" value="false"/>
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
|
||||||
|
WITH RECURSIVE
|
||||||
|
<!-- ======== CTE 1: 生成时间序列 ======== -->
|
||||||
|
TimeGen AS (
|
||||||
|
<choose>
|
||||||
|
<when test="useCustomPoints">
|
||||||
|
<!-- 使用自定义时间点 -->
|
||||||
|
SELECT * FROM (VALUES
|
||||||
|
<foreach collection="query.customTimePoints" item="point" separator=",">
|
||||||
|
(#{point})
|
||||||
|
</foreach>
|
||||||
|
) AS Points(TimePoint)
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
<!-- 自动生成时间序列 -->
|
||||||
|
<choose>
|
||||||
|
<when test="dimension == 'MINUTE'">
|
||||||
|
SELECT DATE_FORMAT(#{query.startTime}, '%Y-%m-%d %H:%i:00') AS TimePoint
|
||||||
|
UNION ALL
|
||||||
|
SELECT DATE_ADD(TimePoint, INTERVAL 1 MINUTE)
|
||||||
|
FROM TimeGen
|
||||||
|
WHERE TimePoint < DATE_FORMAT(#{query.endTime}, '%Y-%m-%d %H:%i:00')
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'HOUR'">
|
||||||
|
SELECT DATE_FORMAT(#{query.startTime}, '%Y-%m-%d %H:00:00') AS TimePoint
|
||||||
|
UNION ALL
|
||||||
|
SELECT DATE_ADD(TimePoint, INTERVAL 1 HOUR)
|
||||||
|
FROM TimeGen
|
||||||
|
WHERE TimePoint < DATE_FORMAT(#{query.endTime}, '%Y-%m-%d %H:00:00')
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'DAY'">
|
||||||
|
SELECT DATE_FORMAT(#{query.startTime}, '%Y-%m-%d 00:00:00') AS TimePoint
|
||||||
|
UNION ALL
|
||||||
|
SELECT DATE_ADD(TimePoint, INTERVAL 1 DAY)
|
||||||
|
FROM TimeGen
|
||||||
|
WHERE TimePoint < DATE_FORMAT(#{query.endTime}, '%Y-%m-%d 00:00:00')
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'WEEK'">
|
||||||
|
SELECT DATE_FORMAT(#{query.startTime}, '%Y-%m-%d 00:00:00') AS TimePoint
|
||||||
|
UNION ALL
|
||||||
|
SELECT DATE_ADD(TimePoint, INTERVAL 1 WEEK)
|
||||||
|
FROM TimeGen
|
||||||
|
WHERE TimePoint < DATE_FORMAT(#{query.endTime}, '%Y-%m-%d 00:00:00')
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'MONTH'">
|
||||||
|
SELECT DATE_FORMAT(#{query.startTime}, '%Y-%m-01 00:00:00') AS TimePoint
|
||||||
|
UNION ALL
|
||||||
|
SELECT DATE_ADD(TimePoint, INTERVAL 1 MONTH)
|
||||||
|
FROM TimeGen
|
||||||
|
WHERE TimePoint < DATE_FORMAT(#{query.endTime}, '%Y-%m-01 00:00:00')
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'YEAR'">
|
||||||
|
SELECT DATE_FORMAT(#{query.startTime}, '%Y-01-01 00:00:00') AS TimePoint
|
||||||
|
UNION ALL
|
||||||
|
SELECT DATE_ADD(TimePoint, INTERVAL 1 YEAR)
|
||||||
|
FROM TimeGen
|
||||||
|
WHERE TimePoint < DATE_FORMAT(#{query.endTime}, '%Y-01-01 00:00:00')
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
-- 默认按小时
|
||||||
|
SELECT DATE_FORMAT(#{query.startTime}, '%Y-%m-%d %H:00:00') AS TimePoint
|
||||||
|
UNION ALL
|
||||||
|
SELECT DATE_ADD(TimePoint, INTERVAL 1 HOUR)
|
||||||
|
FROM TimeGen
|
||||||
|
WHERE TimePoint < DATE_FORMAT(#{query.endTime}, '%Y-%m-%d %H:00:00')
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
),
|
||||||
|
|
||||||
|
<!-- ======== CTE 2: 聚合原始数据 ======== -->
|
||||||
|
Aggregated AS (
|
||||||
|
SELECT
|
||||||
|
<choose>
|
||||||
|
<when test="dimension == 'MINUTE'">
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-%m-%d %H:%i:00') AS TimePoint,
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'HOUR'">
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-%m-%d %H:00:00') AS TimePoint,
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'DAY'">
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-%m-%d 00:00:00') AS TimePoint,
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'WEEK'">
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-%m-%d 00:00:00') AS TimePoint,
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'MONTH'">
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-%m-01 00:00:00') AS TimePoint,
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'YEAR'">
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-01-01 00:00:00') AS TimePoint,
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-%m-%d %H:00:00') AS TimePoint,
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
<foreach collection="query.valueColumns" item="col" separator=",">
|
||||||
|
MAX(${col}) AS ${col}_max,
|
||||||
|
MIN(${col}) AS ${col}_min,
|
||||||
|
AVG(${col}) AS ${col}_avg,
|
||||||
|
COUNT(${col}) AS ${col}_count
|
||||||
|
</foreach>
|
||||||
|
FROM ${query.tableName}
|
||||||
|
WHERE 1=1
|
||||||
|
<if test="query.startTime != null and query.endTime != null">
|
||||||
|
AND ${timeCol} >= #{query.startTime}
|
||||||
|
AND ${timeCol} < #{query.endTime}
|
||||||
|
</if>
|
||||||
|
<if test="query.deviceColumn != null and query.deviceColumn != ''
|
||||||
|
and query.deviceId != null and query.deviceId != ''">
|
||||||
|
AND ${query.deviceColumn} = #{query.deviceId}
|
||||||
|
</if>
|
||||||
|
<!-- 如果使用自定义时间点,也过滤数据 -->
|
||||||
|
<if test="useCustomPoints and query.customTimePoints != null">
|
||||||
|
AND ${timeCol} >= (SELECT MIN(TimePoint) FROM TimeGen)
|
||||||
|
AND ${timeCol} < (SELECT MAX(TimePoint) FROM TimeGen)
|
||||||
|
</if>
|
||||||
|
GROUP BY
|
||||||
|
<choose>
|
||||||
|
<when test="dimension == 'MINUTE'">
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-%m-%d %H:%i:00')
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'HOUR'">
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-%m-%d %H:00:00')
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'DAY'">
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-%m-%d 00:00:00')
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'WEEK'">
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-%m-%d 00:00:00')
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'MONTH'">
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-%m-01 00:00:00')
|
||||||
|
</when>
|
||||||
|
<when test="dimension == 'YEAR'">
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-01-01 00:00:00')
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
DATE_FORMAT(${timeCol}, '%Y-%m-%d %H:00:00')
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
)
|
||||||
|
|
||||||
|
<!-- ======== 最终查询:左连接填充空值 ======== -->
|
||||||
|
SELECT
|
||||||
|
TimeGen.TimePoint AS time_point,
|
||||||
|
<choose>
|
||||||
|
<when test="fillEmpty">
|
||||||
|
<!-- 填充空值 -->
|
||||||
|
<foreach collection="query.valueColumns" item="col" separator=",">
|
||||||
|
IFNULL(Aggregated.${col}_max, ${emptyVal}) AS ${col}_max,
|
||||||
|
IFNULL(Aggregated.${col}_min, ${emptyVal}) AS ${col}_min,
|
||||||
|
IFNULL(Aggregated.${col}_avg, ${emptyVal}) AS ${col}_avg,
|
||||||
|
IFNULL(Aggregated.${col}_count, 0) AS ${col}_count
|
||||||
|
</foreach>
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
<!-- 不填充,保留NULL -->
|
||||||
|
<foreach collection="query.valueColumns" item="col" separator=",">
|
||||||
|
Aggregated.${col}_max AS ${col}_max,
|
||||||
|
Aggregated.${col}_min AS ${col}_min,
|
||||||
|
Aggregated.${col}_avg AS ${col}_avg,
|
||||||
|
Aggregated.${col}_count AS ${col}_count
|
||||||
|
</foreach>
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
FROM TimeGen
|
||||||
|
LEFT JOIN Aggregated ON TimeGen.TimePoint = Aggregated.TimePoint
|
||||||
|
ORDER BY TimeGen.TimePoint ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 2. 检查表是否存在 -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<select id="checkTableExists" resultType="java.lang.Integer">
|
||||||
|
SELECT COUNT(1)
|
||||||
|
FROM INFORMATION_SCHEMA.TABLES
|
||||||
|
WHERE TABLE_NAME = #{tableName}
|
||||||
|
AND TABLE_SCHEMA = DATABASE()
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 3. 获取表的数值列信息 -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<select id="getTableColumns" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
COLUMN_NAME AS columnName,
|
||||||
|
DATA_TYPE AS dataType,
|
||||||
|
IS_NULLABLE AS isNullable,
|
||||||
|
NUMERIC_PRECISION AS numericPrecision,
|
||||||
|
NUMERIC_SCALE AS numericScale
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE TABLE_NAME = #{tableName}
|
||||||
|
AND TABLE_SCHEMA = DATABASE()
|
||||||
|
AND DATA_TYPE IN ('int', 'bigint', 'smallint', 'tinyint',
|
||||||
|
'decimal', 'numeric', 'float', 'double',
|
||||||
|
'real', 'money', 'smallmoney')
|
||||||
|
ORDER BY ORDINAL_POSITION
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 4. 获取表统计信息 -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<select id="getStatistics" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
COUNT(1) AS totalRecords,
|
||||||
|
MIN(collect_time) AS earliestTime,
|
||||||
|
MAX(collect_time) AS latestTime,
|
||||||
|
COUNT(DISTINCT device_code) AS deviceCount
|
||||||
|
FROM ${tableName}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
package org.jeecg.modules.appmana.service;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import org.jeecg.common.dto.statistic.FlexibleTimeQueryDTO;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 灵活时间维度监测数据统计服务
|
||||||
|
*/
|
||||||
|
public interface FlexibleMonitorService {
|
||||||
|
// ==================== 核心方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最底层通用方法(所有其他方法最终都调这个方法)
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> getFlexibleTimeStats(FlexibleTimeQueryDTO query);
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 按小时统计 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询最近N小时数据(最常用)
|
||||||
|
*
|
||||||
|
* @param tableName 表名
|
||||||
|
* @param timeColumn 时间字段名(如:collect_time, create_time, record_time)
|
||||||
|
* @param deviceColumn 设备字段名(如:device_code, device_id)
|
||||||
|
* @param deviceId 设备ID
|
||||||
|
* @param valueColumns 监测列列表
|
||||||
|
* @param hours 查询多少小时(默认24)
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> queryHourlyStats(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
Integer hours
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询指定时间范围的按小时统计数据
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> queryHourlyStatsByRange(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 按天统计 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询最近N天数据
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> queryDailyStats(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
Integer days
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询指定时间范围的按天统计数据
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> queryDailyStatsByRange(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 按月统计 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询最近N个月数据
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> queryMonthlyStats(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
Integer months
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询指定时间范围的按月统计数据
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> queryMonthlyStatsByRange(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 按年统计 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询最近N年数据
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> queryYearlyStats(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
Integer years
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询指定时间范围的按年统计数据
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> queryYearlyStatsByRange(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 按分钟统计 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询最近N分钟数据(实时监控用)
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> queryMinuteStats(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
Integer minutes
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 自定义时间点 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用自定义时间点数组查询(只查指定时间点的数据)
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> queryStatsWithCustomPoints(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
List<LocalDateTime> timePoints
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 多设备对比 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量查询多个设备(对比用)
|
||||||
|
*/
|
||||||
|
Map<String, List<Map<String, Object>>> queryMultiDeviceStats(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
List<String> deviceIds,
|
||||||
|
List<String> valueColumns,
|
||||||
|
FlexibleTimeQueryDTO.TimeDimension dimension,
|
||||||
|
Integer timeRange,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 辅助方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动识别表中的监测列(数值类型)
|
||||||
|
*/
|
||||||
|
List<String> autoDetectColumns(String tableName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取表统计信息
|
||||||
|
*/
|
||||||
|
Map<String, Object> getTableStatistics(String tableName);
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,24 @@
|
||||||
package org.jeecg.modules.appmana.service.impl;
|
package org.jeecg.modules.appmana.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.beanutils.BeanUtils;
|
import org.apache.commons.beanutils.BeanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jeecg.common.config.TenantContext;
|
import org.jeecg.common.config.TenantContext;
|
||||||
import org.jeecg.common.constant.CommonConstants;
|
import org.jeecg.common.constant.*;
|
||||||
import org.jeecg.common.constant.DeviceReadConstants;
|
|
||||||
import org.jeecg.common.constant.PollutionConstants;
|
|
||||||
import org.jeecg.common.constant.enums.PollutionEnum;
|
import org.jeecg.common.constant.enums.PollutionEnum;
|
||||||
import org.jeecg.common.dto.CommonDTO;
|
import org.jeecg.common.dto.CommonDTO;
|
||||||
|
import org.jeecg.common.dto.SurvSummaryDTO;
|
||||||
import org.jeecg.common.entity.*;
|
import org.jeecg.common.entity.*;
|
||||||
|
import org.jeecg.common.util.*;
|
||||||
import org.jeecg.common.vo.VOHisResult;
|
import org.jeecg.common.vo.VOHisResult;
|
||||||
|
import org.jeecg.common.vo.iot.common.SurvItemInfo;
|
||||||
import org.jeecg.common.vo.statistic.AlertReport;
|
import org.jeecg.common.vo.statistic.AlertReport;
|
||||||
|
import org.jeecg.common.vo.statistic.AlertSummaryVo;
|
||||||
import org.jeecg.common.vo.statistic.ScreenIndexSummaryDetailVo;
|
import org.jeecg.common.vo.statistic.ScreenIndexSummaryDetailVo;
|
||||||
|
import org.jeecg.common.vo.statistic.SurvSummaryVo;
|
||||||
import org.jeecg.modules.appmana.service.*;
|
import org.jeecg.modules.appmana.service.*;
|
||||||
import org.jeecg.modules.appmana.utils.HttpServletRequestUtil;
|
import org.jeecg.modules.appmana.utils.HttpServletRequestUtil;
|
||||||
import org.jeecg.modules.appmana.utils.MonitoringDataRichTextBuilder;
|
import org.jeecg.modules.appmana.utils.MonitoringDataRichTextBuilder;
|
||||||
|
|
@ -29,6 +33,7 @@ import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
|
@ -47,6 +52,10 @@ public class CommonServiceImpl {
|
||||||
@Lazy
|
@Lazy
|
||||||
private SurvConfigServiceImpl configService;
|
private SurvConfigServiceImpl configService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Lazy
|
||||||
|
private SurvDeviceDeployServiceImpl deviceDeployService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Lazy
|
@Lazy
|
||||||
private ScEquZhibiaoServiceImpl zhibiaoService;
|
private ScEquZhibiaoServiceImpl zhibiaoService;
|
||||||
|
|
@ -55,6 +64,9 @@ public class CommonServiceImpl {
|
||||||
@Lazy
|
@Lazy
|
||||||
private ISurvAlertRecordService alertRecordService;
|
private ISurvAlertRecordService alertRecordService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FlexibleMonitorService flexibleMonitorService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取监测设备的数据
|
* 获取监测设备的数据
|
||||||
*/
|
*/
|
||||||
|
|
@ -458,4 +470,302 @@ public class CommonServiceImpl {
|
||||||
|
|
||||||
return MonitoringDataRichTextBuilder.buildRichText(stats);
|
return MonitoringDataRichTextBuilder.buildRichText(stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SurvSummaryVo getSurvSummary(SurvSummaryDTO survSummaryDTO) {
|
||||||
|
|
||||||
|
SurvSummaryVo survSummaryVo = new SurvSummaryVo();
|
||||||
|
Map<String, SurvItemInfo> itemInfo = new HashMap<>();
|
||||||
|
List<SurvItemInfo> itemList = new ArrayList<>();
|
||||||
|
List<String> timeList = new ArrayList<>();
|
||||||
|
Map<String,List<String>> dataMap = new HashMap<>();
|
||||||
|
|
||||||
|
if(StringUtils.isNotBlank(survSummaryDTO.getStationCode())){
|
||||||
|
//获取站点下的所有设备
|
||||||
|
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 = deviceDeployService.getDeviceListByStation(survSummaryDTO.getStationCode(),deployTypes);
|
||||||
|
if(deviceList!=null && !deviceList.isEmpty()){
|
||||||
|
for (SurvDeviceDeploy deploy : deviceList) {
|
||||||
|
survSummaryDTO.setDeployId(deploy.getId());
|
||||||
|
SurvSummaryVo temp = getDeviceSummary(deploy,survSummaryDTO.getSummaryMode(),survSummaryDTO.getStartTime(),survSummaryDTO.getEndTime());
|
||||||
|
itemInfo.putAll(temp.getItemInfo());
|
||||||
|
itemList.addAll(temp.getItemList());
|
||||||
|
dataMap.putAll(temp.getDataMap());
|
||||||
|
|
||||||
|
timeList = temp.getTimeList();
|
||||||
|
}
|
||||||
|
survSummaryVo.setItemInfo(itemInfo);
|
||||||
|
survSummaryVo.setItemList(itemList);
|
||||||
|
survSummaryVo.setDataMap(dataMap);
|
||||||
|
survSummaryVo.setTimeList(timeList);
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
SurvDeviceDeploy deploy = deviceDeployService.getById(survSummaryDTO.getDeployId());
|
||||||
|
Assert.notNull(deploy,"设备不存在");
|
||||||
|
survSummaryVo = getDeviceSummary(deploy,survSummaryDTO.getSummaryMode(),survSummaryDTO.getStartTime(),survSummaryDTO.getEndTime());
|
||||||
|
}
|
||||||
|
return survSummaryVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public SurvSummaryVo getDeviceSummary(SurvDeviceDeploy deploy,String summaryMode,String startTime,String endTime) {
|
||||||
|
SurvSummaryVo survSummaryVo = new SurvSummaryVo();
|
||||||
|
String tableName = "";
|
||||||
|
String timeColumn = "";
|
||||||
|
String deviceIdColumn = "";
|
||||||
|
String timeFormat = "";
|
||||||
|
LocalDateTime startDate = null;
|
||||||
|
LocalDateTime endDate = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(StringUtils.isBlank(summaryMode)){
|
||||||
|
summaryMode = IotConstants.month_days;//默认月查询
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
List<ScEquZhibiao> zhibiaoList = zhibiaoService.getListByEquid(deploy.getId());
|
||||||
|
Map<String,ScEquZhibiao> zhibiaoMap = new HashMap<>();
|
||||||
|
List<String> zhibiaoKeys = new ArrayList<>();
|
||||||
|
List<SurvItemInfo> survItemInfos = new ArrayList<>();
|
||||||
|
|
||||||
|
if (zhibiaoList!=null && !zhibiaoList.isEmpty()){
|
||||||
|
zhibiaoList.forEach(zhibiao ->{
|
||||||
|
zhibiaoMap.put(zhibiao.getEntityField(),zhibiao);
|
||||||
|
zhibiaoKeys.add(zhibiao.getEntityField());
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, SurvItemInfo> itemMap = survItemInfos.stream()
|
||||||
|
.collect(Collectors.toMap(SurvItemInfo::getEntity, itemss -> itemss));
|
||||||
|
survSummaryVo.setItemList(survItemInfos);
|
||||||
|
survSummaryVo.setItemInfo(itemMap);
|
||||||
|
|
||||||
|
switch (deploy.getDeployType()){
|
||||||
|
case PollutionConstants.SOIL_SURV:
|
||||||
|
case PollutionConstants.WATER_QULITY:
|
||||||
|
tableName = "surv_hisdata_soil";
|
||||||
|
timeColumn = "DATA_DATE_TIME";
|
||||||
|
deviceIdColumn = "DEPLOY_ID";
|
||||||
|
break;
|
||||||
|
case PollutionConstants.AIR_SURV:
|
||||||
|
tableName = "surv_hisdata_air";
|
||||||
|
timeColumn = "DATA_DATE_TIME";
|
||||||
|
deviceIdColumn = "DEPLOY_ID";
|
||||||
|
break;
|
||||||
|
case PollutionConstants.STINK:
|
||||||
|
tableName = "surv_hisdata_vocs";
|
||||||
|
timeColumn = "DATA_DATE_TIME";
|
||||||
|
deviceIdColumn = "DEPLOY_ID";
|
||||||
|
break;
|
||||||
|
case PollutionConstants.WATER_LIVE:
|
||||||
|
tableName = "surv_hisdata_livestockwater";
|
||||||
|
timeColumn = "DATA_DATE_TIME";
|
||||||
|
deviceIdColumn = "DEVICE_ID";
|
||||||
|
break;
|
||||||
|
case PollutionConstants.WATER_ORIENT:
|
||||||
|
tableName = "surv_hisdata_orientwater";
|
||||||
|
timeColumn = "DATA_DATE_TIME";
|
||||||
|
deviceIdColumn = "DEVICE_ID";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> dbFields = NamingConverter.camelsToDbFields(zhibiaoKeys);
|
||||||
|
|
||||||
|
List<Map<String, Object>> result = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
if(IotConstants.day_hours.equals(summaryMode)){
|
||||||
|
boolean isNow = true;
|
||||||
|
timeFormat = "d日H时";
|
||||||
|
if(StringUtils.isNotBlank(startTime)){
|
||||||
|
isNow = false;
|
||||||
|
startDate = DateParseUtils.parseToLocalDateTime(startTime+" 00:00:00","yyyy-MM-dd HH:mm:ss");
|
||||||
|
}
|
||||||
|
if(StringUtils.isNotBlank(endTime)){
|
||||||
|
endDate = DateParseUtils.parseToLocalDateTime(endTime+" 23:59:59","yyyy-MM-dd HH:mm:ss").withNano(999999000);
|
||||||
|
}
|
||||||
|
if(isNow){
|
||||||
|
result = flexibleMonitorService.queryHourlyStats(
|
||||||
|
tableName, // 表名
|
||||||
|
timeColumn, // 时间字段
|
||||||
|
deviceIdColumn, // 设备字段
|
||||||
|
deploy.getId(), // 设备ID
|
||||||
|
dbFields, // 监测列
|
||||||
|
24
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
result = flexibleMonitorService.queryHourlyStatsByRange(
|
||||||
|
tableName, // 表名
|
||||||
|
timeColumn, // 时间字段
|
||||||
|
deviceIdColumn, // 设备字段
|
||||||
|
deploy.getId(), // 设备ID
|
||||||
|
dbFields, // 监测列
|
||||||
|
startDate,
|
||||||
|
endDate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(IotConstants.custom_days.equals(summaryMode)){
|
||||||
|
boolean isNow = true;
|
||||||
|
timeFormat = "M月d日";
|
||||||
|
if(StringUtils.isNotBlank(startTime)){
|
||||||
|
isNow = false;
|
||||||
|
startDate = DateParseUtils.parseToLocalDateTime(startTime+" 00:00:00","yyyy-MM-dd HH:mm:ss");
|
||||||
|
}
|
||||||
|
if(StringUtils.isNotBlank(endTime)){
|
||||||
|
endDate = DateParseUtils.parseToLocalDateTime(endTime+" 23:59:59","yyyy-MM-dd HH:mm:ss");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isNow){
|
||||||
|
result = flexibleMonitorService.queryDailyStats(
|
||||||
|
tableName, // 表名
|
||||||
|
timeColumn, // 时间字段
|
||||||
|
deviceIdColumn, // 设备字段
|
||||||
|
deploy.getId(), // 设备ID
|
||||||
|
dbFields, // 监测列
|
||||||
|
30
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
result = flexibleMonitorService.queryDailyStatsByRange(
|
||||||
|
tableName, // 表名
|
||||||
|
timeColumn, // 时间字段
|
||||||
|
deviceIdColumn, // 设备字段
|
||||||
|
deploy.getId(), // 设备ID
|
||||||
|
dbFields, // 监测列
|
||||||
|
startDate,
|
||||||
|
endDate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(IotConstants.month_days.equals(summaryMode)){
|
||||||
|
boolean isNow = true;
|
||||||
|
timeFormat = "M月d日";
|
||||||
|
if(StringUtils.isNotBlank(startTime)){
|
||||||
|
isNow = false;
|
||||||
|
startDate = DateParseUtils.parseToLocalDateTime(startTime+"-01 00:00:00","yyyy-MM-dd HH:mm:ss");
|
||||||
|
}
|
||||||
|
if(StringUtils.isNotBlank(endTime)){
|
||||||
|
endDate = DateParseUtils.parseToLocalDateTime(endTime+"-01 23:59:59","yyyy-MM-dd HH:mm:ss");
|
||||||
|
endDate = DateTimeRangeUtil.getMonthEnd(endDate).withNano(999999000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isNow){
|
||||||
|
result = flexibleMonitorService.queryDailyStats(
|
||||||
|
tableName, // 表名
|
||||||
|
timeColumn, // 时间字段
|
||||||
|
deviceIdColumn, // 设备字段
|
||||||
|
deploy.getId(), // 设备ID
|
||||||
|
dbFields, // 监测列
|
||||||
|
30
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
result = flexibleMonitorService.queryDailyStatsByRange(
|
||||||
|
tableName, // 表名
|
||||||
|
timeColumn, // 时间字段
|
||||||
|
deviceIdColumn, // 设备字段
|
||||||
|
deploy.getId(), // 设备ID
|
||||||
|
dbFields, // 监测列
|
||||||
|
startDate,
|
||||||
|
endDate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(IotConstants.year_months.equals(summaryMode)){
|
||||||
|
boolean isNow = true;
|
||||||
|
timeFormat = "yy年M月";
|
||||||
|
if(StringUtils.isNotBlank(startTime)){
|
||||||
|
isNow = false;
|
||||||
|
startDate = DateParseUtils.parseToLocalDateTime(startTime+"-01-01 00:00:00","yyyy-MM-dd HH:mm:ss");
|
||||||
|
}
|
||||||
|
if(StringUtils.isNotBlank(endTime)){
|
||||||
|
endDate = DateParseUtils.parseToLocalDateTime(endTime+"-12-31 23:59:59","yyyy-MM-dd HH:mm:ss");
|
||||||
|
endDate = DateTimeRangeUtil.getYearEnd(endDate).withNano(999999000);
|
||||||
|
}
|
||||||
|
if(isNow){
|
||||||
|
result = flexibleMonitorService.queryMonthlyStats(
|
||||||
|
tableName, // 表名
|
||||||
|
timeColumn, // 时间字段
|
||||||
|
deviceIdColumn, // 设备字段
|
||||||
|
deploy.getId(), // 设备ID
|
||||||
|
dbFields, // 监测列
|
||||||
|
3
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
result = flexibleMonitorService.queryMonthlyStatsByRange(
|
||||||
|
tableName, // 表名
|
||||||
|
timeColumn, // 时间字段
|
||||||
|
deviceIdColumn, // 设备字段
|
||||||
|
deploy.getId(), // 设备ID
|
||||||
|
dbFields, // 监测列
|
||||||
|
startDate,
|
||||||
|
endDate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
List<String> timeList = new ArrayList<>();
|
||||||
|
Map<String,List<String>> dataMap = new HashMap<>();
|
||||||
|
if(result!=null && !result.isEmpty()){
|
||||||
|
|
||||||
|
for (Map<String, Object> item : result) {
|
||||||
|
String timePoint = item.get(StatisticConstants.RESULT_TIME_COLUMN).toString();
|
||||||
|
timeList.add(timePoint);
|
||||||
|
for (String db : dbFields) {
|
||||||
|
String dbKey = db+StatisticConstants.RESULT_VALUE_MAX;
|
||||||
|
String value = item.get(dbKey).toString();
|
||||||
|
String zhibiaoKey = NamingConverter.dbFieldToCamel(dbKey.replace(StatisticConstants.RESULT_VALUE_MAX,""));
|
||||||
|
List<String> zhibiaoData = dataMap.get(zhibiaoKey);
|
||||||
|
if(zhibiaoData==null){
|
||||||
|
zhibiaoData = new ArrayList<>();
|
||||||
|
}
|
||||||
|
zhibiaoData.add(value);
|
||||||
|
dataMap.put(zhibiaoKey,zhibiaoData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timeList = DateRangeUtil.convertDateFormatStream(timeList,"yyyy-MM-dd HH:mm:ss",timeFormat);
|
||||||
|
survSummaryVo.setTimeList(timeList);
|
||||||
|
survSummaryVo.setDataMap(dataMap);
|
||||||
|
|
||||||
|
return survSummaryVo;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,394 @@
|
||||||
|
package org.jeecg.modules.appmana.service.impl;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jeecg.common.dto.statistic.FlexibleTimeQueryDTO;
|
||||||
|
import org.jeecg.modules.appmana.mapper.FlexibleMonitorMapper;
|
||||||
|
import org.jeecg.modules.appmana.service.FlexibleMonitorService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class FlexibleMonitorServiceImpl implements FlexibleMonitorService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FlexibleMonitorMapper flexibleMonitorMapper;
|
||||||
|
|
||||||
|
// ==================== 核心方法 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> getFlexibleTimeStats(FlexibleTimeQueryDTO query) {
|
||||||
|
// 参数自动补全
|
||||||
|
autoCompleteParams(query);
|
||||||
|
logQueryInfo(query);
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
List<Map<String, Object>> result = flexibleMonitorMapper.getFlexibleTimeStats(query);
|
||||||
|
log.info("查询完成,耗时: {}ms, 记录数: {}", System.currentTimeMillis() - start, result.size());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 按小时统计 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> queryHourlyStats(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
Integer hours) {
|
||||||
|
|
||||||
|
if (hours == null) hours = 24;
|
||||||
|
|
||||||
|
LocalDateTime endTime = LocalDateTime.now();
|
||||||
|
LocalDateTime startTime = endTime.minusHours(hours);
|
||||||
|
|
||||||
|
return queryHourlyStatsByRange(tableName, timeColumn, deviceColumn, deviceId, valueColumns, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> queryHourlyStatsByRange(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime) {
|
||||||
|
|
||||||
|
FlexibleTimeQueryDTO query = buildQuery(
|
||||||
|
tableName, timeColumn, deviceColumn, deviceId, valueColumns,
|
||||||
|
FlexibleTimeQueryDTO.TimeDimension.HOUR, startTime, endTime
|
||||||
|
);
|
||||||
|
return getFlexibleTimeStats(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 按天统计 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> queryDailyStats(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
Integer days) {
|
||||||
|
|
||||||
|
if (days == null) days = 30;
|
||||||
|
|
||||||
|
LocalDateTime endTime = LocalDateTime.now();
|
||||||
|
LocalDateTime startTime = endTime.minusDays(days);
|
||||||
|
|
||||||
|
return queryDailyStatsByRange(tableName, timeColumn, deviceColumn, deviceId, valueColumns, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> queryDailyStatsByRange(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime) {
|
||||||
|
|
||||||
|
FlexibleTimeQueryDTO query = buildQuery(
|
||||||
|
tableName, timeColumn, deviceColumn, deviceId, valueColumns,
|
||||||
|
FlexibleTimeQueryDTO.TimeDimension.DAY, startTime, endTime
|
||||||
|
);
|
||||||
|
return getFlexibleTimeStats(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 按月统计 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> queryMonthlyStats(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
Integer months) {
|
||||||
|
|
||||||
|
if (months == null) months = 12;
|
||||||
|
|
||||||
|
LocalDateTime endTime = LocalDateTime.now();
|
||||||
|
LocalDateTime startTime = endTime.minusMonths(months);
|
||||||
|
|
||||||
|
return queryMonthlyStatsByRange(tableName, timeColumn, deviceColumn, deviceId, valueColumns, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> queryMonthlyStatsByRange(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime) {
|
||||||
|
|
||||||
|
FlexibleTimeQueryDTO query = buildQuery(
|
||||||
|
tableName, timeColumn, deviceColumn, deviceId, valueColumns,
|
||||||
|
FlexibleTimeQueryDTO.TimeDimension.MONTH, startTime, endTime
|
||||||
|
);
|
||||||
|
return getFlexibleTimeStats(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 按年统计 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> queryYearlyStats(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
Integer years) {
|
||||||
|
|
||||||
|
if (years == null) years = 5;
|
||||||
|
|
||||||
|
LocalDateTime endTime = LocalDateTime.now();
|
||||||
|
LocalDateTime startTime = endTime.minusYears(years);
|
||||||
|
|
||||||
|
return queryYearlyStatsByRange(tableName, timeColumn, deviceColumn, deviceId, valueColumns, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> queryYearlyStatsByRange(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime) {
|
||||||
|
|
||||||
|
FlexibleTimeQueryDTO query = buildQuery(
|
||||||
|
tableName, timeColumn, deviceColumn, deviceId, valueColumns,
|
||||||
|
FlexibleTimeQueryDTO.TimeDimension.YEAR, startTime, endTime
|
||||||
|
);
|
||||||
|
return getFlexibleTimeStats(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 按分钟统计 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> queryMinuteStats(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
Integer minutes) {
|
||||||
|
|
||||||
|
if (minutes == null) minutes = 60;
|
||||||
|
|
||||||
|
LocalDateTime endTime = LocalDateTime.now();
|
||||||
|
LocalDateTime startTime = endTime.minusMinutes(minutes);
|
||||||
|
|
||||||
|
FlexibleTimeQueryDTO query = buildQuery(
|
||||||
|
tableName, timeColumn, deviceColumn, deviceId, valueColumns,
|
||||||
|
FlexibleTimeQueryDTO.TimeDimension.MINUTE, startTime, endTime
|
||||||
|
);
|
||||||
|
return getFlexibleTimeStats(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 自定义时间点 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> queryStatsWithCustomPoints(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
List<LocalDateTime> timePoints) {
|
||||||
|
|
||||||
|
if (timePoints == null || timePoints.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("时间点数组不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
FlexibleTimeQueryDTO query = new FlexibleTimeQueryDTO();
|
||||||
|
query.setTableName(tableName);
|
||||||
|
query.setTimeColumn(timeColumn);
|
||||||
|
query.setDeviceColumn(deviceColumn);
|
||||||
|
query.setDeviceId(deviceId);
|
||||||
|
query.setValueColumns(valueColumns);
|
||||||
|
query.setDimension(FlexibleTimeQueryDTO.TimeDimension.HOUR);
|
||||||
|
query.setCustomTimePoints(timePoints);
|
||||||
|
query.setFillEmpty(true);
|
||||||
|
query.setEmptyValue(0);
|
||||||
|
|
||||||
|
return getFlexibleTimeStats(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 多设备对比 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<Map<String, Object>>> queryMultiDeviceStats(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
List<String> deviceIds,
|
||||||
|
List<String> valueColumns,
|
||||||
|
FlexibleTimeQueryDTO.TimeDimension dimension,
|
||||||
|
Integer timeRange,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime) {
|
||||||
|
|
||||||
|
if (deviceIds == null || deviceIds.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("设备ID列表不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有指定时间范围,根据维度自动计算
|
||||||
|
if (startTime == null || endTime == null) {
|
||||||
|
endTime = LocalDateTime.now();
|
||||||
|
if (timeRange == null) timeRange = getDefaultTimeRange(dimension);
|
||||||
|
switch (dimension) {
|
||||||
|
case MINUTE:
|
||||||
|
startTime = endTime.minusMinutes(timeRange);
|
||||||
|
break;
|
||||||
|
case HOUR:
|
||||||
|
startTime = endTime.minusHours(timeRange);
|
||||||
|
break;
|
||||||
|
case DAY:
|
||||||
|
startTime = endTime.minusDays(timeRange);
|
||||||
|
break;
|
||||||
|
case MONTH:
|
||||||
|
startTime = endTime.minusMonths(timeRange);
|
||||||
|
break;
|
||||||
|
case YEAR:
|
||||||
|
startTime = endTime.minusYears(timeRange);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
startTime = endTime.minusHours(24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<Map<String, Object>>> resultMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
for (String deviceId : deviceIds) {
|
||||||
|
FlexibleTimeQueryDTO query = buildQuery(
|
||||||
|
tableName, timeColumn, deviceColumn, deviceId, valueColumns,
|
||||||
|
dimension, startTime, endTime
|
||||||
|
);
|
||||||
|
List<Map<String, Object>> data = getFlexibleTimeStats(query);
|
||||||
|
resultMap.put(deviceId, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 辅助方法 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> autoDetectColumns(String tableName) {
|
||||||
|
Integer count = flexibleMonitorMapper.checkTableExists(tableName);
|
||||||
|
if (count == null || count == 0) {
|
||||||
|
throw new IllegalArgumentException("表不存在: " + tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, String>> columns = flexibleMonitorMapper.getTableColumns(tableName);
|
||||||
|
|
||||||
|
List<String> excludeColumns = Arrays.asList(
|
||||||
|
"id", "create_time", "update_time", "delete_flag", "version",
|
||||||
|
"collect_time", "record_time", "device_id", "device_code",
|
||||||
|
"tenant_id", "org_id", "status", "sort", "remark",
|
||||||
|
"is_deleted", "creator", "modifier", "create_by", "update_by"
|
||||||
|
);
|
||||||
|
|
||||||
|
return columns.stream()
|
||||||
|
.map(col -> col.get("columnName"))
|
||||||
|
.filter(col -> !excludeColumns.contains(col.toLowerCase()))
|
||||||
|
.sorted()
|
||||||
|
.collect(java.util.stream.Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getTableStatistics(String tableName) {
|
||||||
|
return flexibleMonitorMapper.getStatistics(tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== 私有方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建查询对象
|
||||||
|
*/
|
||||||
|
private FlexibleTimeQueryDTO buildQuery(
|
||||||
|
String tableName,
|
||||||
|
String timeColumn,
|
||||||
|
String deviceColumn,
|
||||||
|
String deviceId,
|
||||||
|
List<String> valueColumns,
|
||||||
|
FlexibleTimeQueryDTO.TimeDimension dimension,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime) {
|
||||||
|
|
||||||
|
FlexibleTimeQueryDTO query = new FlexibleTimeQueryDTO();
|
||||||
|
query.setTableName(tableName);
|
||||||
|
query.setTimeColumn(timeColumn);
|
||||||
|
query.setDeviceColumn(deviceColumn);
|
||||||
|
query.setDeviceId(deviceId);
|
||||||
|
query.setValueColumns(valueColumns);
|
||||||
|
query.setDimension(dimension);
|
||||||
|
query.setStartTime(startTime);
|
||||||
|
query.setEndTime(endTime);
|
||||||
|
query.setFillEmpty(true);
|
||||||
|
query.setEmptyValue(0);
|
||||||
|
query.setAvgScale(2);
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据维度获取默认时间范围
|
||||||
|
*/
|
||||||
|
private int getDefaultTimeRange(FlexibleTimeQueryDTO.TimeDimension dimension) {
|
||||||
|
switch (dimension) {
|
||||||
|
case MINUTE: return 60;
|
||||||
|
case HOUR: return 24;
|
||||||
|
case DAY: return 30;
|
||||||
|
case MONTH: return 12;
|
||||||
|
case YEAR: return 5;
|
||||||
|
default: return 24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动补全参数
|
||||||
|
*/
|
||||||
|
private void autoCompleteParams(FlexibleTimeQueryDTO query) {
|
||||||
|
if (query.getFillEmpty() == null) query.setFillEmpty(true);
|
||||||
|
if (query.getEmptyValue() == null) query.setEmptyValue(0);
|
||||||
|
if (query.getTimeColumn() == null) query.setTimeColumn("DATA_DATE_TIME");
|
||||||
|
if (query.getAvgScale() == null) query.setAvgScale(2);
|
||||||
|
if (query.getDeviceColumn() == null && query.getDeviceId() != null) {
|
||||||
|
query.setDeviceColumn("DEPLOY_ID");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录日志
|
||||||
|
*/
|
||||||
|
private void logQueryInfo(FlexibleTimeQueryDTO query) {
|
||||||
|
log.info("=== 灵活时间统计查询 ===");
|
||||||
|
log.info("表: {}, 时间字段: {}, 设备: {}",
|
||||||
|
query.getTableName(), query.getTimeColumn(), query.getDeviceId());
|
||||||
|
log.info("维度: {}, 监测列: {}, 小数位数: {}",
|
||||||
|
query.getDimension(), query.getValueColumns(), query.getAvgScale());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package org.jeecg.system.applet.client;
|
package org.jeecg.system.applet.client;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import org.jeecg.common.dto.SurvSummaryDTO;
|
||||||
import org.jeecg.common.entity.PollutionSetVo;
|
import org.jeecg.common.entity.PollutionSetVo;
|
||||||
import org.jeecg.common.iot.common.VOIntegrateStatistic;
|
import org.jeecg.common.iot.common.VOIntegrateStatistic;
|
||||||
import org.jeecg.common.iot.common.VOSurvIntegrateParam;
|
import org.jeecg.common.iot.common.VOSurvIntegrateParam;
|
||||||
|
|
@ -11,6 +12,7 @@ import org.jeecg.common.vo.VOBigScreenSurvQ;
|
||||||
import org.jeecg.common.vo.VOHisResult;
|
import org.jeecg.common.vo.VOHisResult;
|
||||||
import org.jeecg.common.vo.inner.*;
|
import org.jeecg.common.vo.inner.*;
|
||||||
import org.jeecg.common.vo.iot.common.Result;
|
import org.jeecg.common.vo.iot.common.Result;
|
||||||
|
import org.jeecg.common.vo.statistic.SurvSummaryVo;
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
|
@ -93,4 +95,11 @@ public interface ZhAdminFeignClient {
|
||||||
*/
|
*/
|
||||||
@PostMapping("/appmana/inner/api/constructStink")
|
@PostMapping("/appmana/inner/api/constructStink")
|
||||||
List<CommonDataTrans> constructStink(@RequestBody ConstructStink constructStink);
|
List<CommonDataTrans> constructStink(@RequestBody ConstructStink constructStink);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 站点设备多项统计图
|
||||||
|
*/
|
||||||
|
@PostMapping("/appmana/inner/api/stationDeviceSummary")
|
||||||
|
SurvSummaryVo stationDeviceSummary(@RequestBody SurvSummaryDTO survSummaryDTO);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,12 @@ package org.jeecg.system.applet.controller;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jeecg.common.dto.SurvSummaryDTO;
|
||||||
import org.jeecg.common.util.R;
|
import org.jeecg.common.util.R;
|
||||||
import org.jeecg.common.vo.statistic.AlertSummaryVo;
|
import org.jeecg.common.vo.statistic.AlertSummaryVo;
|
||||||
|
import org.jeecg.common.vo.statistic.SurvSummaryVo;
|
||||||
import org.jeecg.system.applet.annotation.ApiLogin;
|
import org.jeecg.system.applet.annotation.ApiLogin;
|
||||||
|
import org.jeecg.system.applet.client.ZhAdminFeignClient;
|
||||||
import org.jeecg.system.applet.dto.AlertSummaryDTO;
|
import org.jeecg.system.applet.dto.AlertSummaryDTO;
|
||||||
import org.jeecg.system.applet.service.ISurvAlertRecordService;
|
import org.jeecg.system.applet.service.ISurvAlertRecordService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
@ -28,6 +31,21 @@ import org.springframework.web.bind.annotation.RestController;
|
||||||
public class SurvAlertRecordController {
|
public class SurvAlertRecordController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISurvAlertRecordService survAlertRecordService;
|
private ISurvAlertRecordService survAlertRecordService;
|
||||||
|
@Autowired
|
||||||
|
private ZhAdminFeignClient zhAdminFeignClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 站点设备
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value="预警曲线", notes="预警曲线")
|
||||||
|
@PostMapping(value = "/alertSummary")
|
||||||
|
@ApiLogin
|
||||||
|
public R<SurvSummaryVo> alertSummary(@RequestBody SurvSummaryDTO survSummaryDTO) {
|
||||||
|
SurvSummaryVo survSummaryVo = zhAdminFeignClient.stationDeviceSummary(survSummaryDTO);
|
||||||
|
return R.ok(survSummaryVo);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 监控摄像头分页列表查询
|
* 监控摄像头分页列表查询
|
||||||
|
|
@ -35,9 +53,9 @@ public class SurvAlertRecordController {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ApiOperation(value="预警曲线", notes="预警曲线")
|
@ApiOperation(value="预警曲线", notes="预警曲线")
|
||||||
@PostMapping(value = "/alertSummary")
|
@PostMapping(value = "/alertSummary2")
|
||||||
@ApiLogin
|
@ApiLogin
|
||||||
public R<AlertSummaryVo> alertSummary(@RequestBody AlertSummaryDTO alertSummaryDTO) {
|
public R<AlertSummaryVo> alertSummary2(@RequestBody AlertSummaryDTO alertSummaryDTO) {
|
||||||
AlertSummaryVo alertSummaryVo = survAlertRecordService.getSummary(alertSummaryDTO);
|
AlertSummaryVo alertSummaryVo = survAlertRecordService.getSummary(alertSummaryDTO);
|
||||||
return R.ok(alertSummaryVo);
|
return R.ok(alertSummaryVo);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,11 @@ public interface IotConstants {
|
||||||
*/
|
*/
|
||||||
String month_days = "monthDays";
|
String month_days = "monthDays";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1月日数据
|
||||||
|
*/
|
||||||
|
String custom_days = "customDays";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1年月数据
|
* 1年月数据
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.jeecg.common.constant;
|
||||||
|
|
||||||
|
public interface StatisticConstants {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 时间点列名
|
||||||
|
*/
|
||||||
|
String RESULT_TIME_COLUMN = "time_point";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 最大值后缀
|
||||||
|
*/
|
||||||
|
String RESULT_VALUE_MAX = "_max";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 最小值后缀
|
||||||
|
*/
|
||||||
|
String RESULT_VALUE_MIN = "_min";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 平均值后缀
|
||||||
|
*/
|
||||||
|
String RESULT_VALUE_AVG = "_avg";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package org.jeecg.common.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监测统计曲线统一参数
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class SurvSummaryDTO {
|
||||||
|
@ApiModelProperty("站点编号")
|
||||||
|
private String stationCode;
|
||||||
|
|
||||||
|
@ApiModelProperty("设备ID")
|
||||||
|
private String deployId;
|
||||||
|
|
||||||
|
@ApiModelProperty("统计模式")
|
||||||
|
private String summaryMode;
|
||||||
|
|
||||||
|
@ApiModelProperty("开始时间")
|
||||||
|
private String startTime;
|
||||||
|
|
||||||
|
@ApiModelProperty("结束时间")
|
||||||
|
private String endTime;
|
||||||
|
|
||||||
|
@ApiModelProperty("租户ID")
|
||||||
|
private String tenantId;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
package org.jeecg.common.dto.statistic;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 灵活时间维度查询参数
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class FlexibleTimeQueryDTO {
|
||||||
|
|
||||||
|
// ========== 基础配置 ==========
|
||||||
|
/** 表名 */
|
||||||
|
private String tableName;
|
||||||
|
|
||||||
|
/** 时间列名 */
|
||||||
|
private String timeColumn = "DATA_DATE_TIME";
|
||||||
|
|
||||||
|
/** 设备列名 */
|
||||||
|
private String deviceColumn;
|
||||||
|
|
||||||
|
/** 设备ID */
|
||||||
|
private String deviceId;
|
||||||
|
|
||||||
|
/** 监测列列表 */
|
||||||
|
private List<String> valueColumns;
|
||||||
|
|
||||||
|
// ========== 时间维度配置 ==========
|
||||||
|
/** 时间维度:HOUR, DAY, MONTH, YEAR, MINUTE, WEEK */
|
||||||
|
private TimeDimension dimension;
|
||||||
|
|
||||||
|
// ========== 方式一:时间范围模式 ==========
|
||||||
|
/** 开始时间 */
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
|
/** 结束时间 */
|
||||||
|
private LocalDateTime endTime;
|
||||||
|
|
||||||
|
// ========== 方式二:自定义时间点数组模式 ==========
|
||||||
|
/** 自定义时间点数组 */
|
||||||
|
private List<LocalDateTime> customTimePoints;
|
||||||
|
|
||||||
|
// ========== 其他配置 ==========
|
||||||
|
/** 是否填充空值,默认true */
|
||||||
|
private Boolean fillEmpty = true;
|
||||||
|
|
||||||
|
/** 空值填充内容,默认0 */
|
||||||
|
private Object emptyValue = 0;
|
||||||
|
|
||||||
|
/** 平均值保留小数位数,默认2 */
|
||||||
|
private Integer avgScale = 2;
|
||||||
|
|
||||||
|
// ========== 枚举:时间维度 ==========
|
||||||
|
public enum TimeDimension {
|
||||||
|
MINUTE, // 分钟
|
||||||
|
HOUR, // 小时
|
||||||
|
DAY, // 天
|
||||||
|
WEEK, // 周
|
||||||
|
MONTH, // 月
|
||||||
|
YEAR // 年
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.jeecg.common.dto.statistic;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多设备查询参数
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class MultiDeviceQueryDTO {
|
||||||
|
/** 表名 */
|
||||||
|
private String tableName;
|
||||||
|
|
||||||
|
/** 设备列名 */
|
||||||
|
private String deviceColumn = "DEPLOY_ID";
|
||||||
|
|
||||||
|
/** 时间列名 */
|
||||||
|
private String timeColumn = "DATA_DATE_TIME";
|
||||||
|
|
||||||
|
/** 设备ID列表 */
|
||||||
|
private List<String> deviceIds;
|
||||||
|
|
||||||
|
/** 监测列列表 */
|
||||||
|
private List<String> valueColumns;
|
||||||
|
|
||||||
|
/** 时间维度 */
|
||||||
|
private FlexibleTimeQueryDTO.TimeDimension dimension = FlexibleTimeQueryDTO.TimeDimension.HOUR;
|
||||||
|
|
||||||
|
/** 开始时间 */
|
||||||
|
private Date startTime;
|
||||||
|
|
||||||
|
/** 结束时间 */
|
||||||
|
private Date endTime;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.jeecg.common.dto.statistic;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间点DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TimePointDTO {
|
||||||
|
/** 时间 */
|
||||||
|
private Date time;
|
||||||
|
|
||||||
|
/** 格式化后的时间 */
|
||||||
|
private String formattedTime;
|
||||||
|
|
||||||
|
/** 时间戳 */
|
||||||
|
private Long timestamp;
|
||||||
|
|
||||||
|
/** 显示标签 */
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
public TimePointDTO() {}
|
||||||
|
|
||||||
|
public TimePointDTO(Date time, String formattedTime) {
|
||||||
|
this.time = time;
|
||||||
|
this.formattedTime = formattedTime;
|
||||||
|
this.timestamp = time != null ? time.getTime() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,440 @@
|
||||||
|
package org.jeecg.common.util;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.format.DateTimeParseException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期时间工具类
|
||||||
|
* 专注于日期时间的格式化和解析,所有异常内部处理
|
||||||
|
*
|
||||||
|
* @author Your Name
|
||||||
|
* @date 2026-07-28
|
||||||
|
*/
|
||||||
|
public class DateParseUtils {
|
||||||
|
|
||||||
|
// 常用日期时间格式常量
|
||||||
|
public static final String PATTERN_YMD = "yyyy-MM-dd";
|
||||||
|
public static final String PATTERN_YMDHMS = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
public static final String PATTERN_YMDHMS_SSS = "yyyy-MM-dd HH:mm:ss.SSS";
|
||||||
|
public static final String PATTERN_YMD_SLASH = "yyyy/MM/dd";
|
||||||
|
public static final String PATTERN_YMDHMS_SLASH = "yyyy/MM/dd HH:mm:ss";
|
||||||
|
public static final String PATTERN_YMD_CHINESE = "yyyy年MM月dd日";
|
||||||
|
public static final String PATTERN_YMDHMS_CHINESE = "yyyy年MM月dd日 HH时mm分ss秒";
|
||||||
|
public static final String PATTERN_YMD_NO_SEPARATOR = "yyyyMMdd";
|
||||||
|
public static final String PATTERN_YMDHMS_NO_SEPARATOR = "yyyyMMddHHmmss";
|
||||||
|
public static final String PATTERN_HMS = "HH:mm:ss";
|
||||||
|
public static final String PATTERN_HM = "HH:mm";
|
||||||
|
|
||||||
|
// 默认时区(系统默认时区)
|
||||||
|
private static final ZoneId DEFAULT_ZONE = ZoneId.systemDefault();
|
||||||
|
|
||||||
|
// ======================== LocalDateTime 格式化 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化 LocalDateTime
|
||||||
|
*
|
||||||
|
* @param dateTime LocalDateTime 对象
|
||||||
|
* @param pattern 日期时间格式
|
||||||
|
* @return 格式化后的字符串,如果参数为null则返回空字符串
|
||||||
|
*/
|
||||||
|
public static String formatLocalDateTime(LocalDateTime dateTime, String pattern) {
|
||||||
|
if (dateTime == null || pattern == null || pattern.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||||
|
return dateTime.format(formatter);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化 LocalDateTime(使用默认格式 yyyy-MM-dd HH:mm:ss)
|
||||||
|
*
|
||||||
|
* @param dateTime LocalDateTime 对象
|
||||||
|
* @return 格式化后的字符串,如果参数为null则返回空字符串
|
||||||
|
*/
|
||||||
|
public static String formatLocalDateTime(LocalDateTime dateTime) {
|
||||||
|
return formatLocalDateTime(dateTime, PATTERN_YMDHMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== LocalDate 格式化 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化 LocalDate
|
||||||
|
*
|
||||||
|
* @param date LocalDate 对象
|
||||||
|
* @param pattern 日期格式
|
||||||
|
* @return 格式化后的字符串,如果参数为null则返回空字符串
|
||||||
|
*/
|
||||||
|
public static String formatLocalDate(LocalDate date, String pattern) {
|
||||||
|
if (date == null || pattern == null || pattern.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||||
|
return date.format(formatter);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化 LocalDate(使用默认格式 yyyy-MM-dd)
|
||||||
|
*
|
||||||
|
* @param date LocalDate 对象
|
||||||
|
* @return 格式化后的字符串,如果参数为null则返回空字符串
|
||||||
|
*/
|
||||||
|
public static String formatLocalDate(LocalDate date) {
|
||||||
|
return formatLocalDate(date, PATTERN_YMD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== LocalTime 格式化 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化 LocalTime
|
||||||
|
*
|
||||||
|
* @param time LocalTime 对象
|
||||||
|
* @param pattern 时间格式
|
||||||
|
* @return 格式化后的字符串,如果参数为null则返回空字符串
|
||||||
|
*/
|
||||||
|
public static String formatLocalTime(LocalTime time, String pattern) {
|
||||||
|
if (time == null || pattern == null || pattern.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||||
|
return time.format(formatter);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化 LocalTime(使用默认格式 HH:mm:ss)
|
||||||
|
*
|
||||||
|
* @param time LocalTime 对象
|
||||||
|
* @return 格式化后的字符串,如果参数为null则返回空字符串
|
||||||
|
*/
|
||||||
|
public static String formatLocalTime(LocalTime time) {
|
||||||
|
return formatLocalTime(time, PATTERN_HMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== Date 格式化 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化 Date
|
||||||
|
*
|
||||||
|
* @param date Date 对象
|
||||||
|
* @param pattern 日期时间格式
|
||||||
|
* @return 格式化后的字符串,如果参数为null则返回空字符串
|
||||||
|
*/
|
||||||
|
public static String formatDate(Date date, String pattern) {
|
||||||
|
if (date == null || pattern == null || pattern.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
LocalDateTime dateTime = dateToLocalDateTime(date);
|
||||||
|
return formatLocalDateTime(dateTime, pattern);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化 Date(使用默认格式 yyyy-MM-dd HH:mm:ss)
|
||||||
|
*
|
||||||
|
* @param date Date 对象
|
||||||
|
* @return 格式化后的字符串,如果参数为null则返回空字符串
|
||||||
|
*/
|
||||||
|
public static String formatDate(Date date) {
|
||||||
|
return formatDate(date, PATTERN_YMDHMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 解析为 LocalDateTime ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析字符串为 LocalDateTime
|
||||||
|
*
|
||||||
|
* @param text 日期时间字符串
|
||||||
|
* @param pattern 日期时间格式
|
||||||
|
* @return LocalDateTime 对象,解析失败返回 null
|
||||||
|
*/
|
||||||
|
public static LocalDateTime parseToLocalDateTime(String text, String pattern) {
|
||||||
|
if (text == null || text.isEmpty() || pattern == null || pattern.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||||
|
return LocalDateTime.parse(text, formatter);
|
||||||
|
} catch (DateTimeParseException e) {
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析字符串为 LocalDateTime(支持多种格式尝试)
|
||||||
|
*
|
||||||
|
* @param text 日期时间字符串
|
||||||
|
* @param patterns 多种日期时间格式
|
||||||
|
* @return LocalDateTime 对象,所有格式都解析失败返回 null
|
||||||
|
*/
|
||||||
|
public static LocalDateTime parseToLocalDateTime(String text, String... patterns) {
|
||||||
|
if (text == null || text.isEmpty() || patterns == null || patterns.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (String pattern : patterns) {
|
||||||
|
LocalDateTime result = parseToLocalDateTime(text, pattern);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 解析为 LocalDate ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析字符串为 LocalDate
|
||||||
|
*
|
||||||
|
* @param text 日期字符串
|
||||||
|
* @param pattern 日期格式
|
||||||
|
* @return LocalDate 对象,解析失败返回 null
|
||||||
|
*/
|
||||||
|
public static LocalDate parseToLocalDate(String text, String pattern) {
|
||||||
|
if (text == null || text.isEmpty() || pattern == null || pattern.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||||
|
return LocalDate.parse(text, formatter);
|
||||||
|
} catch (DateTimeParseException e) {
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析字符串为 LocalDate(支持多种格式尝试)
|
||||||
|
*
|
||||||
|
* @param text 日期字符串
|
||||||
|
* @param patterns 多种日期格式
|
||||||
|
* @return LocalDate 对象,所有格式都解析失败返回 null
|
||||||
|
*/
|
||||||
|
public static LocalDate parseToLocalDate(String text, String... patterns) {
|
||||||
|
if (text == null || text.isEmpty() || patterns == null || patterns.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (String pattern : patterns) {
|
||||||
|
LocalDate result = parseToLocalDate(text, pattern);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 解析为 LocalTime ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析字符串为 LocalTime
|
||||||
|
*
|
||||||
|
* @param text 时间字符串
|
||||||
|
* @param pattern 时间格式
|
||||||
|
* @return LocalTime 对象,解析失败返回 null
|
||||||
|
*/
|
||||||
|
public static LocalTime parseToLocalTime(String text, String pattern) {
|
||||||
|
if (text == null || text.isEmpty() || pattern == null || pattern.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||||
|
return LocalTime.parse(text, formatter);
|
||||||
|
} catch (DateTimeParseException e) {
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析字符串为 LocalTime(支持多种格式尝试)
|
||||||
|
*
|
||||||
|
* @param text 时间字符串
|
||||||
|
* @param patterns 多种时间格式
|
||||||
|
* @return LocalTime 对象,所有格式都解析失败返回 null
|
||||||
|
*/
|
||||||
|
public static LocalTime parseToLocalTime(String text, String... patterns) {
|
||||||
|
if (text == null || text.isEmpty() || patterns == null || patterns.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (String pattern : patterns) {
|
||||||
|
LocalTime result = parseToLocalTime(text, pattern);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 解析为 Date ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析字符串为 Date
|
||||||
|
*
|
||||||
|
* @param text 日期时间字符串
|
||||||
|
* @param pattern 日期时间格式
|
||||||
|
* @return Date 对象,解析失败返回 null
|
||||||
|
*/
|
||||||
|
public static Date parseToDate(String text, String pattern) {
|
||||||
|
if (text == null || text.isEmpty() || pattern == null || pattern.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LocalDateTime dateTime = parseToLocalDateTime(text, pattern);
|
||||||
|
if (dateTime == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return localDateTimeToDate(dateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析字符串为 Date(支持多种格式尝试)
|
||||||
|
*
|
||||||
|
* @param text 日期时间字符串
|
||||||
|
* @param patterns 多种日期时间格式
|
||||||
|
* @return Date 对象,所有格式都解析失败返回 null
|
||||||
|
*/
|
||||||
|
public static Date parseToDate(String text, String... patterns) {
|
||||||
|
if (text == null || text.isEmpty() || patterns == null || patterns.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (String pattern : patterns) {
|
||||||
|
Date result = parseToDate(text, pattern);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== Date 和 LocalDateTime 互转 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date 转 LocalDateTime
|
||||||
|
*
|
||||||
|
* @param date Date 对象
|
||||||
|
* @return LocalDateTime 对象,如果参数为null则返回null
|
||||||
|
*/
|
||||||
|
public static LocalDateTime dateToLocalDateTime(Date date) {
|
||||||
|
if (date == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return LocalDateTime.ofInstant(date.toInstant(), DEFAULT_ZONE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LocalDateTime 转 Date
|
||||||
|
*
|
||||||
|
* @param dateTime LocalDateTime 对象
|
||||||
|
* @return Date 对象,如果参数为null则返回null
|
||||||
|
*/
|
||||||
|
public static Date localDateTimeToDate(LocalDateTime dateTime) {
|
||||||
|
if (dateTime == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Date.from(dateTime.atZone(DEFAULT_ZONE).toInstant());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date 转 LocalDate
|
||||||
|
*
|
||||||
|
* @param date Date 对象
|
||||||
|
* @return LocalDate 对象,如果参数为null则返回null
|
||||||
|
*/
|
||||||
|
public static LocalDate dateToLocalDate(Date date) {
|
||||||
|
if (date == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return dateToLocalDateTime(date).toLocalDate();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LocalDate 转 Date(时间部分为00:00:00)
|
||||||
|
*
|
||||||
|
* @param localDate LocalDate 对象
|
||||||
|
* @return Date 对象,如果参数为null则返回null
|
||||||
|
*/
|
||||||
|
public static Date localDateToDate(LocalDate localDate) {
|
||||||
|
if (localDate == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return localDateTimeToDate(localDate.atStartOfDay());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 常用便捷方法 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前时间的格式化字符串
|
||||||
|
*
|
||||||
|
* @param pattern 日期时间格式
|
||||||
|
* @return 格式化后的当前时间字符串
|
||||||
|
*/
|
||||||
|
public static String getCurrentTime(String pattern) {
|
||||||
|
return formatLocalDateTime(LocalDateTime.now(), pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前时间的格式化字符串(默认格式 yyyy-MM-dd HH:mm:ss)
|
||||||
|
*
|
||||||
|
* @return 格式化后的当前时间字符串
|
||||||
|
*/
|
||||||
|
public static String getCurrentTime() {
|
||||||
|
return getCurrentTime(PATTERN_YMDHMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前日期的格式化字符串
|
||||||
|
*
|
||||||
|
* @param pattern 日期格式
|
||||||
|
* @return 格式化后的当前日期字符串
|
||||||
|
*/
|
||||||
|
public static String getCurrentDate(String pattern) {
|
||||||
|
return formatLocalDate(LocalDate.now(), pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前日期的格式化字符串(默认格式 yyyy-MM-dd)
|
||||||
|
*
|
||||||
|
* @return 格式化后的当前日期字符串
|
||||||
|
*/
|
||||||
|
public static String getCurrentDate() {
|
||||||
|
return getCurrentDate(PATTERN_YMD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,271 @@
|
||||||
|
package org.jeecg.common.util;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库字段与驼峰命名转换工具类
|
||||||
|
* 支持下划线命名(snake_case)和驼峰命名(camelCase)之间的互相转换
|
||||||
|
*
|
||||||
|
* @author Your Name
|
||||||
|
* @date 2026-07-28
|
||||||
|
*/
|
||||||
|
public class NamingConverter {
|
||||||
|
|
||||||
|
private static final Pattern CAMEL_PATTERN = Pattern.compile("[A-Z]");
|
||||||
|
private static final Pattern UNDERSCORE_PATTERN = Pattern.compile("_([a-z])");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将数据库字段名(下划线命名)转换为驼峰命名
|
||||||
|
* 例如:user_name -> userName
|
||||||
|
*
|
||||||
|
* @param dbField 数据库字段名(下划线命名)
|
||||||
|
* @return 驼峰命名字符串
|
||||||
|
*/
|
||||||
|
public static String dbFieldToCamel(String dbField) {
|
||||||
|
if (dbField == null || dbField.isEmpty()) {
|
||||||
|
return dbField;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果字段名全部大写,先转为小写
|
||||||
|
String field = dbField.toLowerCase();
|
||||||
|
|
||||||
|
// 处理下划线后跟字母的情况
|
||||||
|
Matcher matcher = UNDERSCORE_PATTERN.matcher(field);
|
||||||
|
StringBuffer result = new StringBuffer();
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
String replacement = matcher.group(1).toUpperCase();
|
||||||
|
matcher.appendReplacement(result, replacement);
|
||||||
|
}
|
||||||
|
matcher.appendTail(result);
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将数据库字段名(下划线命名)转换为驼峰命名(首字母大写)
|
||||||
|
* 例如:user_name -> UserName
|
||||||
|
*
|
||||||
|
* @param dbField 数据库字段名(下划线命名)
|
||||||
|
* @return 首字母大写的驼峰命名字符串
|
||||||
|
*/
|
||||||
|
public static String dbFieldToPascal(String dbField) {
|
||||||
|
String camel = dbFieldToCamel(dbField);
|
||||||
|
if (camel == null || camel.isEmpty()) {
|
||||||
|
return camel;
|
||||||
|
}
|
||||||
|
return camel.substring(0, 1).toUpperCase() + camel.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将驼峰命名转换为数据库字段名(下划线命名)
|
||||||
|
* 例如:userName -> user_name
|
||||||
|
*
|
||||||
|
* @param camelName 驼峰命名字符串
|
||||||
|
* @return 数据库字段名(下划线命名)
|
||||||
|
*/
|
||||||
|
public static String camelToDbField(String camelName) {
|
||||||
|
if (camelName == null || camelName.isEmpty()) {
|
||||||
|
return camelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在大写字母前添加下划线,然后转为小写
|
||||||
|
Matcher matcher = CAMEL_PATTERN.matcher(camelName);
|
||||||
|
StringBuffer result = new StringBuffer();
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
String replacement = "_" + matcher.group().toLowerCase();
|
||||||
|
matcher.appendReplacement(result, replacement);
|
||||||
|
}
|
||||||
|
matcher.appendTail(result);
|
||||||
|
|
||||||
|
// 如果以大写字母开头,会多一个下划线,需要去掉
|
||||||
|
String dbField = result.toString();
|
||||||
|
if (dbField.startsWith("_")) {
|
||||||
|
dbField = dbField.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbField;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将驼峰命名转换为数据库字段名(下划线命名,全部大写)
|
||||||
|
* 例如:userName -> USER_NAME
|
||||||
|
*
|
||||||
|
* @param camelName 驼峰命名字符串
|
||||||
|
* @return 大写下划线命名字符串
|
||||||
|
*/
|
||||||
|
public static String camelToDbFieldUpper(String camelName) {
|
||||||
|
return camelToDbField(camelName).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 批量转换方法(数组) ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量转换:将下划线命名的数组转换为驼峰命名数组
|
||||||
|
*
|
||||||
|
* @param dbFields 数据库字段名数组
|
||||||
|
* @return 驼峰命名字符串数组
|
||||||
|
*/
|
||||||
|
public static String[] dbFieldsToCamels(String[] dbFields) {
|
||||||
|
if (dbFields == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String[] result = new String[dbFields.length];
|
||||||
|
for (int i = 0; i < dbFields.length; i++) {
|
||||||
|
result[i] = dbFieldToCamel(dbFields[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量转换:将驼峰命名的数组转换为下划线命名数组
|
||||||
|
*
|
||||||
|
* @param camelNames 驼峰命名字符串数组
|
||||||
|
* @return 数据库字段名数组
|
||||||
|
*/
|
||||||
|
public static String[] camelsToDbFields(String[] camelNames) {
|
||||||
|
if (camelNames == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String[] result = new String[camelNames.length];
|
||||||
|
for (int i = 0; i < camelNames.length; i++) {
|
||||||
|
result[i] = camelToDbField(camelNames[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 批量转换方法(List) ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量转换:将下划线命名的List转换为驼峰命名List
|
||||||
|
*
|
||||||
|
* @param dbFields 数据库字段名List
|
||||||
|
* @return 驼峰命名字符串List
|
||||||
|
*/
|
||||||
|
public static List<String> dbFieldsToCamels(List<String> dbFields) {
|
||||||
|
if (dbFields == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<String> result = new ArrayList<>(dbFields.size());
|
||||||
|
for (String dbField : dbFields) {
|
||||||
|
result.add(dbFieldToCamel(dbField));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量转换:将驼峰命名的List转换为下划线命名List
|
||||||
|
*
|
||||||
|
* @param camelNames 驼峰命名字符串List
|
||||||
|
* @return 数据库字段名List
|
||||||
|
*/
|
||||||
|
public static List<String> camelsToDbFields(List<String> camelNames) {
|
||||||
|
if (camelNames == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<String> result = new ArrayList<>(camelNames.size());
|
||||||
|
for (String camelName : camelNames) {
|
||||||
|
result.add(camelToDbField(camelName));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量转换:将下划线命名的List转换为驼峰命名List(首字母大写)
|
||||||
|
*
|
||||||
|
* @param dbFields 数据库字段名List
|
||||||
|
* @return 帕斯卡命名字符串List
|
||||||
|
*/
|
||||||
|
public static List<String> dbFieldsToPascals(List<String> dbFields) {
|
||||||
|
if (dbFields == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<String> result = new ArrayList<>(dbFields.size());
|
||||||
|
for (String dbField : dbFields) {
|
||||||
|
result.add(dbFieldToPascal(dbField));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量转换:将驼峰命名的List转换为大写下划线命名List
|
||||||
|
*
|
||||||
|
* @param camelNames 驼峰命名字符串List
|
||||||
|
* @return 大写下划线命名字符串List
|
||||||
|
*/
|
||||||
|
public static List<String> camelsToDbFieldsUpper(List<String> camelNames) {
|
||||||
|
if (camelNames == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<String> result = new ArrayList<>(camelNames.size());
|
||||||
|
for (String camelName : camelNames) {
|
||||||
|
result.add(camelToDbFieldUpper(camelName));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在原有的List上直接进行转换(修改原List)
|
||||||
|
* 将下划线命名的List转换为驼峰命名
|
||||||
|
*
|
||||||
|
* @param dbFields 数据库字段名List(会被修改)
|
||||||
|
* @return 转换后的List(原List的引用)
|
||||||
|
*/
|
||||||
|
public static List<String> convertDbFieldsToCamelsInPlace(List<String> dbFields) {
|
||||||
|
if (dbFields == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < dbFields.size(); i++) {
|
||||||
|
dbFields.set(i, dbFieldToCamel(dbFields.get(i)));
|
||||||
|
}
|
||||||
|
return dbFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在原有的List上直接进行转换(修改原List)
|
||||||
|
* 将驼峰命名的List转换为下划线命名
|
||||||
|
*
|
||||||
|
* @param camelNames 驼峰命名字符串List(会被修改)
|
||||||
|
* @return 转换后的List(原List的引用)
|
||||||
|
*/
|
||||||
|
public static List<String> convertCamelsToDbFieldsInPlace(List<String> camelNames) {
|
||||||
|
if (camelNames == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < camelNames.size(); i++) {
|
||||||
|
camelNames.set(i, camelToDbField(camelNames.get(i)));
|
||||||
|
}
|
||||||
|
return camelNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 格式判断方法 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断字符串是否为下划线命名格式
|
||||||
|
*
|
||||||
|
* @param str 要检查的字符串
|
||||||
|
* @return true表示是下划线命名格式
|
||||||
|
*/
|
||||||
|
public static boolean isUnderScoreFormat(String str) {
|
||||||
|
if (str == null || str.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return str.contains("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断字符串是否为驼峰命名格式
|
||||||
|
*
|
||||||
|
* @param str 要检查的字符串
|
||||||
|
* @return true表示是驼峰命名格式
|
||||||
|
*/
|
||||||
|
public static boolean isCamelFormat(String str) {
|
||||||
|
if (str == null || str.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !str.contains("_") && !str.equals(str.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.jeecg.common.vo.statistic;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.jeecg.common.vo.iot.common.SurvItemInfo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 监测统计曲线统一格式
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class SurvSummaryVo {
|
||||||
|
@ApiModelProperty("监测项信息")
|
||||||
|
private Map<String,SurvItemInfo> itemInfo;
|
||||||
|
@ApiModelProperty("监测项列表")
|
||||||
|
private List<SurvItemInfo> itemList;
|
||||||
|
@ApiModelProperty("X轴数据")
|
||||||
|
private List<String> timeList;
|
||||||
|
@ApiModelProperty("Y轴数据")
|
||||||
|
private Map<String,List<String>> dataMap;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.jeecg.common.vo.statistic;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间统计结果VO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TimeStatsVO {
|
||||||
|
/** 时间点 */
|
||||||
|
private Date timePoint;
|
||||||
|
|
||||||
|
/** 格式化后的时间 */
|
||||||
|
private String timePointFormatted;
|
||||||
|
|
||||||
|
/** 动态监测项数据 */
|
||||||
|
private Map<String, Object> metrics;
|
||||||
|
|
||||||
|
/** 记录数 */
|
||||||
|
private Long recordCount;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue