#AI
zhou zhou
5 小时以前 51877df13075ad10ef51107f15bcd21f1661febe
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package com.vincent.rsf.server.ai.service.provider;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
import com.vincent.rsf.server.ai.model.AiPromptContext;
import com.vincent.rsf.server.manager.entity.DeviceSite;
import com.vincent.rsf.server.manager.mapper.DeviceSiteMapper;
import org.springframework.stereotype.Service;
 
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
 
@Service
public class AiDeviceSiteSummaryService implements AiDiagnosticDataProvider {
 
    private static final String TOOL_CODE = "device_site_summary";
    private static final String TOOL_NAME = "设备站点摘要";
 
    @Resource
    private DeviceSiteMapper deviceSiteMapper;
 
    /**
     * 返回设备站点工具默认顺序。
     */
    @Override
    public int getOrder() {
        return 30;
    }
 
    /**
     * 返回设备站点工具编码。
     */
    @Override
    public String getToolCode() {
        return TOOL_CODE;
    }
 
    /**
     * 返回设备站点工具展示名。
     */
    @Override
    public String getToolName() {
        return TOOL_NAME;
    }
 
    /**
     * 返回设备站点工具默认说明。
     */
    @Override
    public String getDefaultToolPrompt() {
        return "结合设备站点摘要判断设备状态、巷道分布和最近更新站点。";
    }
 
    /**
     * 汇总站点状态、设备分布和最近更新站点,生成设备站点摘要。
     */
    @Override
    public AiDiagnosticToolResult buildDiagnosticData(AiPromptContext context) {
        String summary = buildDeviceSiteSummary(context);
        String severity = summary.contains("未查询到") ? "WARN" : "INFO";
        return new AiDiagnosticToolResult()
                .setToolCode(getToolCode())
                .setToolName(getToolName())
                .setSeverity(severity)
                .setSummaryText(summary);
    }
 
    /**
     * 基于 man_device_site 生成设备站点总览。
     */
    private String buildDeviceSiteSummary(AiPromptContext context) {
        List<DeviceSite> deviceSites = deviceSiteMapper.selectList(new LambdaQueryWrapper<DeviceSite>()
                .select(DeviceSite::getSite, DeviceSite::getName, DeviceSite::getType, DeviceSite::getDevice,
                        DeviceSite::getDeviceCode, DeviceSite::getDeviceSite, DeviceSite::getStatus,
                        DeviceSite::getChannel, DeviceSite::getUpdateTime)
                .eq(DeviceSite::getTenantId, context.getTenantId())
                .orderByDesc(DeviceSite::getUpdateTime)
                .last("limit 50"));
 
        if (deviceSites.isEmpty()) {
            return "当前未查询到 man_device_site 设备站点数据,请提示用户核对基础数据或租户配置。";
        }
 
        Map<String, Long> statusCounters = new LinkedHashMap<>();
        Map<String, Long> deviceCounters = new LinkedHashMap<>();
        Map<String, Long> channelCounters = new LinkedHashMap<>();
        for (DeviceSite deviceSite : deviceSites) {
            String status = resolveStatus(deviceSite.getStatus());
            String device = emptyToDefault(deviceSite.getDevice$(), "未配置设备类型");
            String channel = deviceSite.getChannel() == null ? "未配置巷道" : "巷道" + deviceSite.getChannel();
            statusCounters.put(status, statusCounters.getOrDefault(status, 0L) + 1);
            deviceCounters.put(device, deviceCounters.getOrDefault(device, 0L) + 1);
            channelCounters.put(channel, channelCounters.getOrDefault(channel, 0L) + 1);
        }
 
        StringBuilder summary = new StringBuilder();
        summary.append("以下是基于 man_device_site 的实时设备站点摘要,请结合任务和库存数据综合判断:");
        summary.append("\n设备站点总览:共 ").append(deviceSites.size()).append(" 条有效站点记录。");
        summary.append("\n站点状态分布:").append(formatCounter(statusCounters)).append("。");
        summary.append("\n设备类型分布:").append(formatCounter(deviceCounters)).append("。");
        summary.append("\n巷道分布:").append(formatCounter(channelCounters)).append("。");
        summary.append("\n最近更新站点 TOP5:").append(formatLatestSites(deviceSites.subList(0, Math.min(deviceSites.size(), 5)))).append("。");
        return summary.toString();
    }
 
    /**
     * 格式化计数类摘要。
     */
    private String formatCounter(Map<String, Long> counter) {
        List<String> parts = new ArrayList<>();
        for (Map.Entry<String, Long> item : counter.entrySet()) {
            parts.add(item.getKey() + " " + item.getValue() + " 条");
        }
        return String.join(",", parts);
    }
 
    /**
     * 格式化最近更新站点列表。
     */
    private String formatLatestSites(List<DeviceSite> deviceSites) {
        List<String> parts = new ArrayList<>();
        for (DeviceSite deviceSite : deviceSites) {
            parts.add(emptyToDefault(deviceSite.getName(), emptyToDefault(deviceSite.getSite(), "未命名站点"))
                    + "("
                    + resolveStatus(deviceSite.getStatus())
                    + ",设备 "
                    + emptyToDefault(deviceSite.getDevice$(), "-")
                    + ",接驳位 "
                    + emptyToDefault(deviceSite.getDeviceCode(), "-")
                    + ",巷道 "
                    + (deviceSite.getChannel() == null ? "-" : deviceSite.getChannel())
                    + ")");
        }
        return String.join(";", parts);
    }
 
    /**
     * 将站点状态码转成可读文本。
     */
    private String resolveStatus(Integer status) {
        if (status == null) {
            return "未知状态";
        }
        return status == 1 ? "正常" : status == 0 ? "冻结" : "状态" + status;
    }
 
    /**
     * 统一处理空值展示。
     */
    private String emptyToDefault(String value, String defaultValue) {
        return value == null || value.trim().isEmpty() ? defaultValue : value;
    }
}