Compare commits

...

10 Commits

Author SHA1 Message Date
review512jwy@163.com a9913ba75b Merge branch 'zhc' into ppt-20260120 2 weeks ago
review512jwy@163.com f75630b639 Merge branch 'zhc' into ppt-20260120 2 weeks ago
review512jwy@163.com 89fe8f8c85 Merge branch 'zhc' into ppt-20260120 2 weeks ago
review512jwy@163.com fcf2417662 Merge branch 'zhc' into ppt-20260120 2 weeks ago
review512jwy@163.com c6a7648323 Merge branch 'zhc' into ppt-20260120 2 weeks ago
review512jwy@163.com 608a33d218 代码完善 3 weeks ago
review512jwy@163.com 5a398c0ffc 代码完善 3 weeks ago
review512jwy@163.com 356273f74d 计测设备,增加前日、前年数据 3 weeks ago
review512jwy@163.com c716f5e886 修改文件名 3 weeks ago
review512jwy@163.com 5d4d03ba1d 导出优化 3 weeks ago
  1. 23
      dongjian-dashboard-back-dao/src/main/resources/mappers/ex/OverviewInfoMapperExt.xml
  2. 2
      dongjian-dashboard-back-model/src/main/java/com/dongjian/dashboard/back/vo/device/SubLineData.java
  3. 102
      dongjian-dashboard-back-service/src/main/java/com/dongjian/dashboard/back/service/common/CommonOpt.java
  4. 146
      dongjian-dashboard-back-service/src/main/java/com/dongjian/dashboard/back/service/common/LineDataAggregator.java

23
dongjian-dashboard-back-dao/src/main/resources/mappers/ex/OverviewInfoMapperExt.xml

@ -19,15 +19,15 @@
INNER JOIN device_info dinfo ON dinfo.asset_id = basic_monitoring_asset.equipment_id INNER JOIN device_info dinfo ON dinfo.asset_id = basic_monitoring_asset.equipment_id
INNER JOIN alert_history ah ON dinfo.device_id = ah.device_id INNER JOIN alert_history ah ON dinfo.device_id = ah.device_id
LEFT JOIN basic_asset_class_big on basic_asset_class_big.id = basic_monitoring_asset.class_big_id LEFT JOIN basic_asset_class_big on basic_asset_class_big.id = basic_monitoring_asset.class_big_id
LEFT JOIN type ty ON dinfo.type_id = ty.id LEFT JOIN type ty ON dinfo.type_id = ty.id AND ty.flag = 0
AND ty.device_category_id in
<foreach collection="categoryIdList" item="categoryId" open="(" separator="," close=")">
#{categoryId}
</foreach>
WHERE WHERE
bbuilding.company_id = #{companyId} AND bbuilding.flag = 0 AND basic_floor.flag = 0 AND basic_space.flag = 0 bbuilding.company_id = #{companyId} AND bbuilding.flag = 0 AND basic_floor.flag = 0 AND basic_space.flag = 0
AND basic_monitoring_asset.flag = 0 AND dinfo.flag = 0 AND basic_asset_class_big.flag = 0 AND basic_monitoring_asset.flag = 0 AND dinfo.flag = 0 AND basic_asset_class_big.flag = 0
AND ah.confirm_status = 0 AND ah.handle_status = 1 AND ah.confirm_status = 0 AND ah.handle_status = 1
AND ty.device_category_id in
<foreach collection="categoryIdList" item="categoryId" open="(" separator="," close=")">
#{categoryId}
</foreach>
</select> </select>
<select id="getBuildingInfo" resultType="com.dongjian.dashboard.back.vo.data.OverviewVO"> <select id="getBuildingInfo" resultType="com.dongjian.dashboard.back.vo.data.OverviewVO">
@ -64,14 +64,15 @@
INNER JOIN basic_monitoring_asset on basic_monitoring_asset.space_id = basic_space.space_id INNER JOIN basic_monitoring_asset on basic_monitoring_asset.space_id = basic_space.space_id
INNER JOIN device_info dinfo ON dinfo.asset_id = basic_monitoring_asset.equipment_id INNER JOIN device_info dinfo ON dinfo.asset_id = basic_monitoring_asset.equipment_id
LEFT JOIN basic_asset_class_big on basic_asset_class_big.id = basic_monitoring_asset.class_big_id LEFT JOIN basic_asset_class_big on basic_asset_class_big.id = basic_monitoring_asset.class_big_id
LEFT JOIN type ty ON dinfo.type_id = ty.id LEFT JOIN type ty ON dinfo.type_id = ty.id AND ty.flag = 0
AND ty.device_category_id in
<foreach collection="categoryIdList" item="categoryId" open="(" separator="," close=")">
#{categoryId}
</foreach>
WHERE WHERE
bbuilding.company_id = #{companyId} AND bbuilding.flag = 0 AND basic_floor.flag = 0 AND basic_space.flag = 0 bbuilding.company_id = #{companyId} AND bbuilding.flag = 0 AND basic_floor.flag = 0 AND basic_space.flag = 0
AND basic_monitoring_asset.flag = 0 AND dinfo.flag = 0 AND basic_asset_class_big.flag = 0 AND ty.flag = 0 AND basic_monitoring_asset.flag = 0 AND dinfo.flag = 0 AND basic_asset_class_big.flag = 0
AND ty.device_category_id in
<foreach collection="categoryIdList" item="categoryId" open="(" separator="," close=")">
#{categoryId}
</foreach>
</select> </select>
</mapper> </mapper>

2
dongjian-dashboard-back-model/src/main/java/com/dongjian/dashboard/back/vo/device/AccumulateLineData.java → dongjian-dashboard-back-model/src/main/java/com/dongjian/dashboard/back/vo/device/SubLineData.java

@ -9,7 +9,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
@Data @Data
public class AccumulateLineData { public class SubLineData {
@Schema(description = "属性编码, 默认:single,温湿度设备:temperature或者humidity") @Schema(description = "属性编码, 默认:single,温湿度设备:temperature或者humidity")
private String attrCode; private String attrCode;

102
dongjian-dashboard-back-service/src/main/java/com/dongjian/dashboard/back/service/common/CommonOpt.java

@ -25,7 +25,7 @@ import com.dongjian.dashboard.back.util.DateUtil;
import com.dongjian.dashboard.back.util.DurationData; import com.dongjian.dashboard.back.util.DurationData;
import com.dongjian.dashboard.back.vo.building.BindedBuildingVO; import com.dongjian.dashboard.back.vo.building.BindedBuildingVO;
import com.dongjian.dashboard.back.vo.company.AuroraInfo; import com.dongjian.dashboard.back.vo.company.AuroraInfo;
import com.dongjian.dashboard.back.vo.device.AccumulateLineData; import com.dongjian.dashboard.back.vo.device.SubLineData;
import com.dongjian.dashboard.back.vo.device.DeviceIncrement; import com.dongjian.dashboard.back.vo.device.DeviceIncrement;
import com.dongjian.dashboard.back.vo.device.LineData; import com.dongjian.dashboard.back.vo.device.LineData;
import com.dongjian.dashboard.back.vo.record.RecordAccumulateDto; import com.dongjian.dashboard.back.vo.record.RecordAccumulateDto;
@ -295,14 +295,14 @@ public class CommonOpt {
Double yesterdayBase = getDayAccumulate(conn, beforeYesterday, lineDataSearchParams.getDeviceId()); Double yesterdayBase = getDayAccumulate(conn, beforeYesterday, lineDataSearchParams.getDeviceId());
Double lastYearBase = getDayAccumulate(conn, lastYearPrev, lineDataSearchParams.getDeviceId()); Double lastYearBase = getDayAccumulate(conn, lastYearPrev, lineDataSearchParams.getDeviceId());
AccumulateLineData acc = new AccumulateLineData(); SubLineData acc = new SubLineData();
acc.setAttrCode(lineData.getAttrCode()); acc.setAttrCode(lineData.getAttrCode());
processAccumulateDay(conn, current, todayBase, "today", acc, deviceInfo); processAccumulateDay(conn, current, todayBase, "today", acc, deviceInfo);
if (searchType == 1) { if (searchType == 1) {
processAccumulateDay(conn, yesterday, yesterdayBase, "yesterday", acc, deviceInfo); processAccumulateDay(conn, yesterday, yesterdayBase, "yesterday", acc, deviceInfo);
processAccumulateDay(conn, lastYear, lastYearBase, "lastYear", acc, deviceInfo); processAccumulateDay(conn, lastYear, lastYearBase, "lastYear", acc, deviceInfo);
LineDataAggregator.convertToLineDataHour(acc, lineData); LineDataAggregator.convertToAccumulateLineData(acc, lineData);
} else { } else {
LineDataAggregator.convertToLineDataAll(acc, lineData); LineDataAggregator.convertToLineDataAll(acc, lineData);
} }
@ -376,10 +376,10 @@ public class CommonOpt {
LocalDate date, LocalDate date,
Double baseValue, Double baseValue,
String yKey, String yKey,
AccumulateLineData accumulateLineData, SubLineData SubLineData,
DeviceInfo deviceInfo) { DeviceInfo deviceInfo) {
AccumulateLineData.childLineData childLineData = new AccumulateLineData.childLineData(); SubLineData.childLineData childLineData = new SubLineData.childLineData();
String sql = "SELECT upload_value, upload_at FROM dashboard_record_accumulate " + String sql = "SELECT upload_value, upload_at FROM dashboard_record_accumulate " +
@ -387,7 +387,7 @@ public class CommonOpt {
try (PreparedStatement ps = conn.prepareStatement(sql)) { try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, deviceInfo.getDeviceId()); ps.setString(1, deviceInfo.getDeviceId());
ps.setString(2, accumulateLineData.getAttrCode()); ps.setString(2, SubLineData.getAttrCode());
ps.setInt(3, date.getYear()); ps.setInt(3, date.getYear());
ps.setInt(4, date.getMonthValue()); ps.setInt(4, date.getMonthValue());
ps.setInt(5, date.getDayOfMonth()); ps.setInt(5, date.getDayOfMonth());
@ -424,7 +424,7 @@ public class CommonOpt {
logger.error("processAccumulateDay error", e); logger.error("processAccumulateDay error", e);
} }
accumulateLineData.getSubData().put(yKey, childLineData); SubLineData.getSubData().put(yKey, childLineData);
} }
private Double getDayAccumulate(Connection conn, LocalDate date, String deviceId) { private Double getDayAccumulate(Connection conn, LocalDate date, String deviceId) {
@ -471,39 +471,77 @@ public class CommonOpt {
List<String> dateList = getPreDay(1); List<String> dateList = getPreDay(1);
for (String date : dateList) { for (String date : dateList) {
LocalDate localDate = LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy_MM_dd")); LocalDate today = LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy_MM_dd"));
LocalDate yesterday = today.minusDays(1);
String sql = "SELECT upload_value, upload_at FROM dashboard_record_measure " + LocalDate lastYear = DateUtil.getLastYearSameIsoWeekDay(today);
"WHERE device_id = ? AND attr_code = ? AND date_year = ? AND date_month = ? AND date_day = ? ORDER BY id";
SubLineData sub = new SubLineData();
try (PreparedStatement ps = conn.prepareStatement(sql)) { sub.setAttrCode(lineData.getAttrCode());
ps.setString(1, lineDataSearchParams.getDeviceId());
ps.setString(2, lineData.getAttrCode()); processMeasureDay(conn, today, "today", sub, deviceInfo);
ps.setInt(3, localDate.getYear()); if (searchType == 1) {
ps.setInt(4, localDate.getMonthValue()); processMeasureDay(conn, yesterday, "yesterday", sub, deviceInfo);
ps.setInt(5, localDate.getDayOfMonth()); processMeasureDay(conn, lastYear, "lastYear", sub, deviceInfo);
LineDataAggregator.convertToMeasureLineDataAvg(sub, lineData);
try (ResultSet rs = ps.executeQuery()) { } else {
if (rs.next()) { LineDataAggregator.convertToLineDataAll(sub, lineData);
processResult(rs, lineData, deviceInfo);
}
}
} catch (Exception e) {
logger.error("process measure data error", e);
} }
} }
}); });
if (searchType == 1) { return list;
list.forEach(ld -> { }
LineDataAggregator.aggregateAverageByHalfHour(ld);
LineDataAggregator.fillTo48HalfHour(ld); private void processMeasureDay(
}); Connection conn,
LocalDate date,
String yKey,
SubLineData sub,
DeviceInfo deviceInfo
) {
SubLineData.childLineData child = new SubLineData.childLineData();
String sql = "SELECT upload_value, upload_at FROM dashboard_record_measure " +
"WHERE device_id = ? AND attr_code = ? " +
"AND date_year = ? AND date_month = ? AND date_day = ? ORDER BY id";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, deviceInfo.getDeviceId());
ps.setString(2, sub.getAttrCode());
ps.setInt(3, date.getYear());
ps.setInt(4, date.getMonthValue());
ps.setInt(5, date.getDayOfMonth());
try (ResultSet rs = ps.executeQuery()) {
DateTimeFormatter formatter = Constants.FORMATTER_YMDHMS;
while (rs.next()) {
long ts = rs.getLong("upload_at");
String value = rs.getString("upload_value");
if (ts == 0) continue;
String time = Instant.ofEpochMilli(ts)
.atZone(Constants.ZONE_TOKYO)
.toLocalDateTime()
.format(formatter);
child.getXData().add(time);
child.getYData().add(
CommonUtil.formatDecimal(
StringUtils.isBlank(value) ? "0" : value,
deviceInfo.getDashboardDecimalPlaces()
)
);
}
}
} catch (Exception e) {
logger.error("processMeasureDay error", e);
} }
return list; sub.getSubData().put(yKey, child);
} }
private void processResult(ResultSet result, LineData lineData, DeviceInfo deviceInfo) { private void processResult(ResultSet result, LineData lineData, DeviceInfo deviceInfo) {
try { try {
// 用于存储 xData 和 yData // 用于存储 xData 和 yData

146
dongjian-dashboard-back-service/src/main/java/com/dongjian/dashboard/back/service/common/LineDataAggregator.java

@ -1,6 +1,6 @@
package com.dongjian.dashboard.back.service.common; package com.dongjian.dashboard.back.service.common;
import com.dongjian.dashboard.back.vo.device.AccumulateLineData; import com.dongjian.dashboard.back.vo.device.SubLineData;
import com.dongjian.dashboard.back.vo.device.LineData; import com.dongjian.dashboard.back.vo.device.LineData;
import java.time.LocalDate; import java.time.LocalDate;
@ -149,84 +149,104 @@ public class LineDataAggregator {
return LocalDateTime.parse(String.valueOf(x), TimeAxisUtil.DATETIME_FMT); return LocalDateTime.parse(String.valueOf(x), TimeAxisUtil.DATETIME_FMT);
} }
/* =============================== public static void convertToMeasureLineDataAvg(SubLineData accumulate, LineData lineData) {
* 按小时聚合平均值 if (accumulate == null || accumulate.getSubData() == null) return;
* =============================== */
public static void aggregateByHour(LineData lineData) {
aggregateByHourOrHalfHour(lineData, new AvgCollector(), true);
}
/* =============================== // 1. 每个 series 做「半小时平均」
* 按半小时聚合平均值 Map<String, HalfHourSeries> seriesMap = new LinkedHashMap<>();
* =============================== */ for (Map.Entry<String, SubLineData.childLineData> entry : accumulate.getSubData().entrySet()) {
public static void aggregateAverageByHalfHour(LineData lineData) { seriesMap.put(entry.getKey(), aggregateAvgByHalfHour(entry.getValue()));
aggregateByHourOrHalfHour(lineData, new AvgCollector(), false); }
// 2. today 作为 X 轴基准
HalfHourSeries today = seriesMap.get("today");
if (today == null) {
throw new IllegalStateException("subData 必须包含 today");
}
// 3. 构建最终 X(yyyy-MM-dd HH:mm:00)
List<String> finalX = new ArrayList<>();
for (String hm : today.xList) {
finalX.add(today.date + " " + hm + ":00");
}
// 4. 构建临时 map,用 today.xList 对齐
Map<String, Map<String, Object>> tmpMap = new HashMap<>();
for (Map.Entry<String, HalfHourSeries> entry : seriesMap.entrySet()) {
tmpMap.put(entry.getKey(), entry.getValue().toMap());
}
// 5. 写入 LineData
lineData.setXData(finalX);
lineData.setYData(LineDataAligner.alignY(today.xList, tmpMap));
lineData.setAttrCode(accumulate.getAttrCode());
// 6. 补齐 48 半小时
fillTo48HalfHour(lineData);
} }
/* 公共聚合方法 */ private static HalfHourSeries aggregateAvgByHalfHour(SubLineData.childLineData child) {
private static <T> void aggregateByHourOrHalfHour( if (child == null || child.getXData() == null || child.getYData() == null) {
LineData lineData, return new HalfHourSeries(null);
HalfHourCollector<T> collector, }
boolean byHour
) {
if (lineData == null) return;
List<String> xData = lineData.getXData(); Map<String, List<Double>> bucket = new HashMap<>();
Map<String, List<Object>> yDataMap = lineData.getYData(); String date = null;
if (xData == null || xData.isEmpty() || yDataMap == null || yDataMap.isEmpty()) return;
Set<String> allSeriesKeys = yDataMap.keySet(); int size = Math.min(child.getXData().size(), child.getYData().size());
Map<String, Map<String, T>> bucket = new TreeMap<>(); for (int i = 0; i < size; i++) {
for (int i = 0; i < xData.size(); i++) { Object x = child.getXData().get(i);
LocalDateTime time = parseTime(xData.get(i)); Object y = child.getYData().get(i);
String key = byHour ? TimeAxisUtil.hourKey(time) : TimeAxisUtil.halfHourKey(time); if (x == null || y == null) continue;
LocalDateTime time;
try {
time = LocalDateTime.parse(x.toString(), TimeAxisUtil.DATETIME_FMT);
} catch (Exception e) {
continue;
}
for (String series : allSeriesKeys) { if (date == null) {
Object yVal = safeGet(yDataMap.get(series), i); date = time.toLocalDate().toString();
if (yVal == null) continue; }
bucket.computeIfAbsent(key, k -> new HashMap<>()); double num;
collector.collect(bucket.get(key), series, time, yVal); try {
num = Double.parseDouble(y.toString());
} catch (Exception e) {
// 非数字直接丢
continue;
} }
String hm = TimeAxisUtil.alignToHalfHour(time.toLocalTime()); // HH:mm
bucket.computeIfAbsent(hm, k -> new ArrayList<>()).add(num);
} }
// 构建临时 Map 用于统一对齐 List<String> sortedHM = new ArrayList<>(bucket.keySet());
Map<String, Map<String, Object>> tmpMap = new HashMap<>(); Collections.sort(sortedHM);
boolean isAvg = collector instanceof AvgCollector;
List<Object> values = new ArrayList<>();
for (Map.Entry<String, Map<String, T>> entry : bucket.entrySet()) { for (String hm : sortedHM) {
String xKey = entry.getKey(); List<Double> list = bucket.get(hm);
Map<String, T> seriesMap = entry.getValue(); double avg = list.stream().mapToDouble(Double::doubleValue).average().orElse(0D);
for (String series : allSeriesKeys) { values.add(avg);
Object val;
T tVal = seriesMap.get(series);
if (isAvg && tVal instanceof List) {
List<Double> list = (List<Double>) tVal;
val = list.stream().mapToDouble(Double::doubleValue).average().orElse(0);
} else if (!isAvg && tVal instanceof TimeValue) {
val = ((TimeValue) tVal).value;
} else {
val = tVal;
}
tmpMap.computeIfAbsent(series, k -> new HashMap<>()).put(xKey, val);
}
} }
List<String> newX = new ArrayList<>(bucket.keySet()); return new HalfHourSeries(date, sortedHM, values);
lineData.setXData(newX);
lineData.setYData(LineDataAligner.alignY(newX, tmpMap));
} }
/* =============================== /* ===============================
* AccumulateLineData -> LineData * SubLineData -> LineData
* 半小时取最后一条 * 半小时取最后一条
* =============================== */ * =============================== */
public static void convertToLineDataHour(AccumulateLineData accumulate, LineData lineData) { public static void convertToAccumulateLineData(SubLineData accumulate, LineData lineData) {
if (accumulate == null || accumulate.getSubData() == null) return; if (accumulate == null || accumulate.getSubData() == null) return;
Map<String, HalfHourSeries> seriesMap = new LinkedHashMap<>(); Map<String, HalfHourSeries> seriesMap = new LinkedHashMap<>();
for (Map.Entry<String, AccumulateLineData.childLineData> entry : accumulate.getSubData().entrySet()) { for (Map.Entry<String, SubLineData.childLineData> entry : accumulate.getSubData().entrySet()) {
seriesMap.put(entry.getKey(), aggregateLastByHalfHour(entry.getValue())); seriesMap.put(entry.getKey(), aggregateLastByHalfHour(entry.getValue()));
} }
@ -250,7 +270,7 @@ public class LineDataAggregator {
fillTo48HalfHour(lineData); fillTo48HalfHour(lineData);
} }
private static HalfHourSeries aggregateLastByHalfHour(AccumulateLineData.childLineData child) { private static HalfHourSeries aggregateLastByHalfHour(SubLineData.childLineData child) {
if (child == null || child.getXData() == null || child.getYData() == null) if (child == null || child.getXData() == null || child.getYData() == null)
return new HalfHourSeries(null); return new HalfHourSeries(null);
@ -307,19 +327,19 @@ public class LineDataAggregator {
/* =============================== /* ===============================
* 全量对齐 * 全量对齐
* =============================== */ * =============================== */
public static void convertToLineDataAll(AccumulateLineData accumulate, LineData lineData) { public static void convertToLineDataAll(SubLineData accumulate, LineData lineData) {
if (accumulate == null || accumulate.getSubData() == null) return; if (accumulate == null || accumulate.getSubData() == null) return;
LinkedHashSet<String> xSet = new LinkedHashSet<>(); LinkedHashSet<String> xSet = new LinkedHashSet<>();
for (AccumulateLineData.childLineData child : accumulate.getSubData().values()) { for (SubLineData.childLineData child : accumulate.getSubData().values()) {
if (child.getXData() != null) xSet.addAll(child.getXData()); if (child.getXData() != null) xSet.addAll(child.getXData());
} }
List<String> mergedX = new ArrayList<>(xSet); List<String> mergedX = new ArrayList<>(xSet);
Map<String, Map<String, Object>> tmpMap = new LinkedHashMap<>(); Map<String, Map<String, Object>> tmpMap = new LinkedHashMap<>();
for (Map.Entry<String, AccumulateLineData.childLineData> entry : accumulate.getSubData().entrySet()) { for (Map.Entry<String, SubLineData.childLineData> entry : accumulate.getSubData().entrySet()) {
Map<String, Object> map = new LinkedHashMap<>(); Map<String, Object> map = new LinkedHashMap<>();
AccumulateLineData.childLineData child = entry.getValue(); SubLineData.childLineData child = entry.getValue();
if (child.getXData() != null && child.getYData() != null) { if (child.getXData() != null && child.getYData() != null) {
int size = Math.min(child.getXData().size(), child.getYData().size()); int size = Math.min(child.getXData().size(), child.getYData().size());
for (int i = 0; i < size; i++) map.put(child.getXData().get(i), child.getYData().get(i)); for (int i = 0; i < size; i++) map.put(child.getXData().get(i), child.getYData().get(i));

Loading…
Cancel
Save