yeg
6 天以前 e38aca8134570513435c003c25dc05f1ae51c9c9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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);
    }
}