package com.battery.business.service;
|
|
import java.math.BigDecimal;
|
import java.time.*;
|
import java.util.*;
|
import javax.sql.DataSource;
|
import com.battery.business.domain.*;
|
import com.battery.business.mapper.TTestMapper;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.stereotype.Service;
|
|
/** 首页只读聚合已有事实,不发起设备控制或为展示新增采样。 */
|
@Service
|
public class DashboardService {
|
private static final ZoneId BUSINESS_ZONE = ZoneId.of("Asia/Hong_Kong");
|
private final ITTestService tests;
|
private final ITDetectionCabinetService cabinets;
|
private final TTestMapper testMapper;
|
private final JdbcTemplate jdbc;
|
public DashboardService(ITTestService tests, ITDetectionCabinetService cabinets, TTestMapper testMapper, DataSource source) {
|
this.tests = tests; this.cabinets = cabinets; this.testMapper = testMapper; jdbc = new JdbcTemplate(source);
|
}
|
public record Summary(long testCount, long batteryGroupCount, long todayCount, long monthCount) {}
|
public record Trend(List<String> labels, List<Long> values) {}
|
public record TestRow(long id, String name, String type, String status, String receptionStatus, String step, long warning) {}
|
public record CabinetRow(long id, String name, BigDecimal capacity, int channelCount, long usedChannels, long testCount, long batteryCount, String onlineStatus) {}
|
public record Warning(long id, long testId, String testName, String item, long occurredAt) {}
|
public record Snapshot(Summary summary, Map<String, Trend> trends, List<TestRow> tests, List<CabinetRow> cabinets, List<Warning> warnings) {}
|
|
/** 使用全部未删除测试计算总量及香港自然日/月新增数,统计不受页面分页限制。 */
|
public Snapshot read(Instant now) {
|
var allTests = tests.selectTestList(new TTest());
|
var today = now.atZone(BUSINESS_ZONE).toLocalDate();
|
Map<LocalDate, Long> dates = new HashMap<>();
|
allTests.stream().filter(test -> test.getCreateTime() != null).forEach(test ->
|
dates.merge(test.getCreateTime().toInstant().atZone(BUSINESS_ZONE).toLocalDate(), 1L, Long::sum));
|
var summary = new Summary(allTests.size(), jdbc.queryForObject("select count(*) from t_battery_group where del_flag=0", Long.class),
|
dates.getOrDefault(today, 0L), dates.entrySet().stream().filter(entry -> YearMonth.from(entry.getKey()).equals(YearMonth.from(today))).mapToLong(Map.Entry::getValue).sum());
|
var trends = new LinkedHashMap<String, Trend>();
|
trends.put("week", days(today.minusDays(6), today, dates));
|
trends.put("month", days(today.withDayOfMonth(1), today, dates));
|
List<String> months = new ArrayList<>(); List<Long> counts = new ArrayList<>();
|
for (int month = 1; month <= today.getMonthValue(); month++) {
|
final var key = YearMonth.of(today.getYear(), month); months.add(month + "月");
|
counts.add(dates.entrySet().stream().filter(entry -> YearMonth.from(entry.getKey()).equals(key)).mapToLong(Map.Entry::getValue).sum());
|
}
|
trends.put("year", new Trend(months, counts));
|
var open = testMapper.selectWarningExceptions(null, "OPEN");
|
Map<Long, Long> warningCounts = new HashMap<>();
|
List<Warning> warnings = open.stream().map(item -> {
|
warningCounts.merge(item.getTestId(), 1L, Long::sum);
|
return new Warning(item.getExceptionId(), item.getTestId(), item.getTestName(),
|
"DEVICE_OFFLINE".equals(item.getExceptionType()) ? "设备离线" : metricName(item.getMetricCode()),
|
item.getLastOccurredAtUtc() == null ? 0 : item.getLastOccurredAtUtc().getTime());
|
}).sorted(Comparator.comparingLong(Warning::occurredAt).reversed()).toList();
|
// 首页明确展示“最近记录”,不把持久化历史冒充设备当前工步;多电池分别保留记录身份。
|
Map<Long, List<String>> latest = new HashMap<>();
|
jdbc.query("select r.test_id,b.battery_no,r.step_no,r.cycle_no from t_test_realtime_record r join t_test_battery b on b.test_battery_id=r.test_battery_id and b.del_flag=0 join (select test_battery_id,max(record_id) record_id from t_test_realtime_record group by test_battery_id) last_record on last_record.record_id=r.record_id",
|
(org.springframework.jdbc.core.RowCallbackHandler) rs -> {
|
if (rs.getObject(3) != null && rs.getObject(4) != null)
|
latest.computeIfAbsent(rs.getLong(1), id -> new ArrayList<>()).add(rs.getString(2) + ":工步" + rs.getInt(3) + " / 循环" + rs.getInt(4));
|
});
|
var rows = allTests.stream().map(test -> new TestRow(test.getTestId(), test.getTestName(), test.getTestTypeName(), test.getTestStatus(),
|
test.getReceptionStatus(), String.join(";", latest.getOrDefault(test.getTestId(), List.of())), warningCounts.getOrDefault(test.getTestId(), 0L))).toList();
|
Map<Long, long[]> usage = new HashMap<>();
|
// 端口、电池和关联测试按未完成测试去重统计;在线事实仍由设备服务独立提供。
|
jdbc.query("select c.cabinet_id,count(distinct binding.channel_id),count(distinct t.test_id),count(distinct b.test_battery_id) from t_test_channel_binding binding join t_detection_channel c on c.channel_id=binding.channel_id and c.del_flag=0 join t_test t on t.test_id=binding.test_id and t.del_flag=0 and t.test_status<>'COMPLETED' join t_test_battery b on b.test_battery_id=binding.test_battery_id and b.del_flag=0 group by c.cabinet_id",
|
(org.springframework.jdbc.core.RowCallbackHandler) rs -> usage.put(rs.getLong(1), new long[] { rs.getLong(2), rs.getLong(3), rs.getLong(4) }));
|
var cabinetRows = cabinets.selectDetectionCabinetList(new TDetectionCabinet()).stream().map(cabinet -> {
|
var values = usage.getOrDefault(cabinet.getCabinetId(), new long[3]);
|
return new CabinetRow(cabinet.getCabinetId(), cabinet.getCabinetName(), cabinet.getCapacity(),
|
cabinet.getChannelCount() == null ? 0 : cabinet.getChannelCount(), values[0], values[1], values[2],
|
"0".equals(cabinet.getOnlineStatus()) ? "在线" : "离线");
|
}).toList();
|
return new Snapshot(summary, trends, rows, cabinetRows, warnings);
|
}
|
|
private Trend days(LocalDate begin, LocalDate end, Map<LocalDate, Long> dates) {
|
List<String> labels = new ArrayList<>(); List<Long> values = new ArrayList<>();
|
for (LocalDate day = begin; !day.isAfter(end); day = day.plusDays(1)) {
|
labels.add(day.getMonthValue() + "/" + day.getDayOfMonth()); values.add(dates.getOrDefault(day, 0L));
|
}
|
return new Trend(labels, values);
|
}
|
|
private String metricName(String code) {
|
if (code == null) return "阈值异常";
|
return Map.of("VOLTAGE", "电压", "CURRENT", "电流", "TEMPERATURE", "温度",
|
"POSITIVE_POTENTIAL", "正电位", "NEGATIVE_POTENTIAL", "负电位").getOrDefault(code, code);
|
}
|
}
|