#
Junjie
2 天以前 6c8ee211ae63de5f4ce0bd13b9c34f0524cd656f
src/main/java/com/zy/system/controller/DashboardController.java
@@ -3,10 +3,15 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.core.annotations.ManagerAuth;
import com.core.common.R;
import com.zy.ai.entity.AiAutoTuneJob;
import com.zy.ai.entity.AiChatSession;
import com.zy.ai.entity.AiTokenUsage;
import com.zy.ai.entity.LlmCallLog;
import com.zy.ai.entity.LlmRouteConfig;
import com.zy.ai.enums.AiPromptScene;
import com.zy.ai.mapper.AiChatSessionMapper;
import com.zy.ai.mapper.AiTokenUsageMapper;
import com.zy.ai.service.AiAutoTuneJobService;
import com.zy.ai.service.LlmCallLogService;
import com.zy.ai.service.LlmRouteConfigService;
import com.zy.asrs.entity.*;
@@ -25,6 +30,7 @@
import com.zy.core.thread.StationThread;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -58,13 +64,29 @@
    @Autowired
    private LlmCallLogService llmCallLogService;
    @Autowired
    private AiAutoTuneJobService aiAutoTuneJobService;
    @Autowired
    private AiChatSessionMapper aiChatSessionMapper;
    @Autowired
    private AiTokenUsageMapper aiTokenUsageMapper;
    @Autowired
    private DevicePingFileStorageService devicePingFileStorageService;
    @Value("${devicePingStorage.intervalMs:1000}")
    private int devicePingIntervalMs;
    @Value("${devicePingStorage.timeoutMs:800}")
    private int devicePingTimeoutMs;
    @Value("${devicePingStorage.probeCount:3}")
    private int devicePingProbeCount;
    @Value("${devicePingStorage.packetSize:-1}")
    private int devicePingPacketSize;
    @GetMapping("/summary/auth")
    @ManagerAuth(memo = "系统仪表盘统计")
    public R summary() {
        Map<String, Object> tasks = buildTaskStats();
        Map<String, Object> devices = buildDeviceStats();
        Map<String, Object> network = buildNetworkStats();
        Map<String, Object> ai = buildAiStats();
        Map<String, Object> overview = new LinkedHashMap<>();
@@ -82,6 +104,7 @@
        result.put("overview", overview);
        result.put("tasks", tasks);
        result.put("devices", devices);
        result.put("network", network);
        result.put("ai", ai);
        return R.ok(result);
    }
@@ -308,20 +331,32 @@
    private Map<String, Object> buildAiStats() {
        Map<String, Object> result = new LinkedHashMap<>();
        // 从独立累计表读取 token 统计
        long tokenTotal = 0L;
        long promptTokenTotal = 0L;
        long completionTokenTotal = 0L;
        long llmCallCountTotal = 0L;
        try {
            AiTokenUsage tokenUsage = aiTokenUsageMapper.selectById(1);
            if (tokenUsage != null) {
                promptTokenTotal = safeCount(tokenUsage.getPromptTokens());
                completionTokenTotal = safeCount(tokenUsage.getCompletionTokens());
                tokenTotal = safeCount(tokenUsage.getTotalTokens());
                llmCallCountTotal = safeCount(tokenUsage.getLlmCallCount());
            }
        } catch (Exception e) {
            log.warn("dashboard ai token usage load failed: {}", safeMessage(e));
        }
        // 会话统计(保留用于显示会话数和提问轮次)
        long askCount = 0L;
        long sessionCount = 0L;
        try {
            List<AiChatSession> sessions = aiChatSessionMapper.selectList(new QueryWrapper<AiChatSession>()
                    .select("id", "sum_prompt_tokens", "sum_completion_tokens", "sum_total_tokens", "ask_count"));
                    .select("id", "ask_count"));
            sessionCount = sessions == null ? 0L : sessions.size();
            if (sessions != null) {
                for (AiChatSession session : sessions) {
                    promptTokenTotal += safeCount(session == null ? null : session.getSumPromptTokens());
                    completionTokenTotal += safeCount(session == null ? null : session.getSumCompletionTokens());
                    tokenTotal += safeCount(session == null ? null : session.getSumTotalTokens());
                    askCount += safeCount(session == null ? null : session.getAskCount());
                }
            }
@@ -401,6 +436,7 @@
        overview.put("tokenTotal", tokenTotal);
        overview.put("promptTokenTotal", promptTokenTotal);
        overview.put("completionTokenTotal", completionTokenTotal);
        overview.put("llmCallCountTotal", llmCallCountTotal);
        overview.put("askCount", askCount);
        overview.put("sessionCount", sessionCount);
        overview.put("routeTotal", routeTotal);
@@ -424,6 +460,74 @@
        return result;
    }
    @SuppressWarnings("unchecked")
    private Map<String, Object> buildNetworkStats() {
        Map<String, Object> result = new LinkedHashMap<>();
        Map<String, Object> overview = new LinkedHashMap<>();
        overview.put("totalDevices", 0L);
        overview.put("okDevices", 0L);
        overview.put("unstableDevices", 0L);
        overview.put("offlineDevices", 0L);
        overview.put("noDataDevices", 0L);
        overview.put("attentionDevices", 0L);
        overview.put("avgLatencyMs", null);
        overview.put("maxLatencyMs", null);
        Map<String, Object> samplingConfig = new LinkedHashMap<>();
        samplingConfig.put("intervalMs", devicePingIntervalMs);
        samplingConfig.put("timeoutMs", devicePingTimeoutMs);
        samplingConfig.put("probeCount", devicePingProbeCount);
        samplingConfig.put("packetSize", Math.max(devicePingPacketSize, -1));
        List<Map<String, Object>> statusStats = new ArrayList<>();
        statusStats.add(metric("正常", 0L));
        statusStats.add(metric("波动", 0L));
        statusStats.add(metric("超时/异常", 0L));
        statusStats.add(metric("暂无数据", 0L));
        List<Map<String, Object>> focusDevices = new ArrayList<>();
        try {
            Map<String, Object> overviewResult = devicePingFileStorageService.queryOverview(listPingConfigs());
            Map<String, Object> summary = overviewResult.get("summary") instanceof Map
                    ? (Map<String, Object>) overviewResult.get("summary")
                    : Collections.emptyMap();
            List<Map<String, Object>> devices = overviewResult.get("devices") instanceof List
                    ? (List<Map<String, Object>>) overviewResult.get("devices")
                    : Collections.emptyList();
            long okDevices = toLong(summary.get("okDevices"));
            long unstableDevices = toLong(summary.get("unstableDevices"));
            long offlineDevices = toLong(summary.get("offlineDevices"));
            long noDataDevices = toLong(summary.get("noDataDevices"));
            overview.put("totalDevices", toLong(summary.get("totalDevices")));
            overview.put("okDevices", okDevices);
            overview.put("unstableDevices", unstableDevices);
            overview.put("offlineDevices", offlineDevices);
            overview.put("noDataDevices", noDataDevices);
            overview.put("attentionDevices", unstableDevices + offlineDevices + noDataDevices);
            overview.put("avgLatencyMs", summary.get("avgLatencyMs"));
            overview.put("maxLatencyMs", summary.get("maxLatencyMs"));
            statusStats = new ArrayList<>();
            statusStats.add(metric("正常", okDevices));
            statusStats.add(metric("波动", unstableDevices));
            statusStats.add(metric("超时/异常", offlineDevices));
            statusStats.add(metric("暂无数据", noDataDevices));
            focusDevices = buildNetworkFocusDevices(devices);
        } catch (Exception e) {
            log.warn("dashboard network stats load failed: {}", safeMessage(e));
        }
        result.put("overview", overview);
        result.put("samplingConfig", samplingConfig);
        result.put("statusStats", statusStats);
        result.put("focusDevices", focusDevices);
        return result;
    }
    private List<DeviceConfig> listDeviceConfig(SlaveType type) {
        try {
            return deviceConfigService.list(new QueryWrapper<DeviceConfig>()
@@ -434,6 +538,93 @@
        }
    }
    private List<DeviceConfig> listPingConfigs() {
        try {
            return deviceConfigService.list(new QueryWrapper<DeviceConfig>()
                    .isNotNull("ip")
                    .ne("ip", "")
                    .orderBy(true, true, "device_type", "device_no"));
        } catch (Exception e) {
            log.warn("dashboard ping device config load failed: {}", safeMessage(e));
            return Collections.emptyList();
        }
    }
    private List<Map<String, Object>> buildNetworkFocusDevices(List<Map<String, Object>> devices) {
        if (devices == null || devices.isEmpty()) {
            return Collections.emptyList();
        }
        List<Map<String, Object>> candidates = new ArrayList<>();
        for (Map<String, Object> row : devices) {
            if (resolveNetworkFocusRank(row) < 3) {
                candidates.add(row);
            }
        }
        if (candidates.isEmpty()) {
            return Collections.emptyList();
        }
        candidates.sort((left, right) -> {
            int rankDiff = Integer.compare(resolveNetworkFocusRank(left), resolveNetworkFocusRank(right));
            if (rankDiff != 0) {
                return rankDiff;
            }
            int latencyDiff = Long.compare(toLong(right.get("avgLatencyMs")), toLong(left.get("avgLatencyMs")));
            if (latencyDiff != 0) {
                return latencyDiff;
            }
            return Long.compare(toLong(right.get("latestTime")), toLong(left.get("latestTime")));
        });
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map<String, Object> item : candidates) {
            if (item == null) {
                continue;
            }
            Map<String, Object> focus = new LinkedHashMap<>();
            focus.put("name", toText(item.get("deviceType")) + "-" + toText(item.get("deviceNo")));
            focus.put("ip", toText(item.get("ip")));
            focus.put("statusText", defaultText(toText(item.get("statusText")), "未知"));
            focus.put("statusType", resolveNetworkStatusTagType(toText(item.get("status"))));
            focus.put("avgLatencyMs", item.get("avgLatencyMs"));
            focus.put("latestTimeLabel", defaultText(toText(item.get("latestTimeLabel")), "--"));
            focus.put("message", defaultText(toText(item.get("message")), "暂无额外说明"));
            result.add(focus);
            if (result.size() >= 4) {
                break;
            }
        }
        return result;
    }
    private int resolveNetworkFocusRank(Map<String, Object> row) {
        String status = row == null ? "" : toText(row.get("status"));
        if ("TIMEOUT".equalsIgnoreCase(status) || "ERROR".equalsIgnoreCase(status)) {
            return 0;
        }
        if ("UNSTABLE".equalsIgnoreCase(status)) {
            return 1;
        }
        if ("NO_DATA".equalsIgnoreCase(status)) {
            return 2;
        }
        return 3;
    }
    private String resolveNetworkStatusTagType(String status) {
        if ("TIMEOUT".equalsIgnoreCase(status) || "ERROR".equalsIgnoreCase(status)) {
            return "danger";
        }
        if ("UNSTABLE".equalsIgnoreCase(status)) {
            return "warning";
        }
        if ("OK".equalsIgnoreCase(status)) {
            return "success";
        }
        return "info";
    }
    private boolean isInboundTask(Long wrkSts) {
        return wrkSts != null && wrkSts > 0 && wrkSts < 100;
    }