|
1
|
package com.iot.scheduler.service;
|
1
|
package com.iot.scheduler.service;
|
|
2
|
|
2
|
|
|
|
|
3
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
|
4
|
+import com.alibaba.fastjson.JSONObject;
|
|
3
|
import lombok.extern.slf4j.Slf4j;
|
5
|
import lombok.extern.slf4j.Slf4j;
|
|
4
|
import org.springframework.beans.factory.annotation.Value;
|
6
|
import org.springframework.beans.factory.annotation.Value;
|
|
5
|
import org.springframework.jdbc.core.JdbcTemplate;
|
7
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
@@ -7,8 +9,9 @@ import org.springframework.stereotype.Service; |
|
@@ -7,8 +9,9 @@ import org.springframework.stereotype.Service; |
|
7
|
import org.springframework.util.StringUtils;
|
9
|
import org.springframework.util.StringUtils;
|
|
8
|
|
10
|
|
|
9
|
import jakarta.annotation.Resource;
|
11
|
import jakarta.annotation.Resource;
|
|
10
|
-import java.util.List;
|
|
|
|
11
|
-import java.util.Map;
|
12
|
+
|
|
|
|
13
|
+import java.text.SimpleDateFormat;
|
|
|
|
14
|
+import java.util.*;
|
|
12
|
|
15
|
|
|
13
|
@Slf4j
|
16
|
@Slf4j
|
|
14
|
@Service
|
17
|
@Service
|
|
@@ -18,9 +21,13 @@ public class DeviceSearchService { |
|
@@ -18,9 +21,13 @@ public class DeviceSearchService { |
|
18
|
private String deviceCorpCode;
|
21
|
private String deviceCorpCode;
|
|
19
|
@Value("${device.db.tableName}")
|
22
|
@Value("${device.db.tableName}")
|
|
20
|
private String deviceTableName;
|
23
|
private String deviceTableName;
|
|
|
|
24
|
+ @Value("${device.db.oeeTableName}")
|
|
|
|
25
|
+ private String oeeTableName;
|
|
21
|
|
26
|
|
|
22
|
@Resource
|
27
|
@Resource
|
|
23
|
private JdbcTemplate jdbcTemplate;
|
28
|
private JdbcTemplate jdbcTemplate;
|
|
|
|
29
|
+ @Resource
|
|
|
|
30
|
+ private DevicePullService devicePullService;
|
|
24
|
|
31
|
|
|
25
|
public Map<String, Object> queryDeviceList(String deviceName, String lampState, Integer pageNo, Integer pageSize) {
|
32
|
public Map<String, Object> queryDeviceList(String deviceName, String lampState, Integer pageNo, Integer pageSize) {
|
|
26
|
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM " + deviceTableName + " WHERE corp_code = ?");
|
33
|
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM " + deviceTableName + " WHERE corp_code = ?");
|
|
@@ -85,4 +92,462 @@ public class DeviceSearchService { |
|
@@ -85,4 +92,462 @@ public class DeviceSearchService { |
|
85
|
"off", off
|
92
|
"off", off
|
|
86
|
);
|
93
|
);
|
|
87
|
}
|
94
|
}
|
|
|
|
95
|
+
|
|
|
|
96
|
+ public Map<String, Object> queryLampData(String dtuSn, String date) {
|
|
|
|
97
|
+ String result = devicePullService.getLampData(dtuSn, date);
|
|
|
|
98
|
+ Map<String, Object> res = com.alibaba.fastjson.JSON.parseObject(result, new com.alibaba.fastjson.TypeReference<>() {});
|
|
|
|
99
|
+ Integer code = (Integer) res.get("code");
|
|
|
|
100
|
+ if (code == null || code != 200) {
|
|
|
|
101
|
+ return Map.of("lampDurationStats", Map.of(), "list", List.of());
|
|
|
|
102
|
+ }
|
|
|
|
103
|
+
|
|
|
|
104
|
+ JSONArray dataList = (JSONArray) res.get("data");
|
|
|
|
105
|
+ if (dataList == null || dataList.isEmpty()) {
|
|
|
|
106
|
+ return Map.of("lampDurationStats", Map.of(), "list", List.of());
|
|
|
|
107
|
+ }
|
|
|
|
108
|
+
|
|
|
|
109
|
+ // 统计各灯状态时长
|
|
|
|
110
|
+ long offTotal = 0, redTotal = 0, yellowTotal = 0, greenTotal = 0, blueTotal = 0;
|
|
|
|
111
|
+ for (int i = 0; i < dataList.size(); i++) {
|
|
|
|
112
|
+ JSONObject item = (JSONObject) dataList.get(i);
|
|
|
|
113
|
+ JSONArray lampDataList = item.getJSONArray("lampData");
|
|
|
|
114
|
+ if (lampDataList == null) continue;
|
|
|
|
115
|
+
|
|
|
|
116
|
+ for (int j = 0; j < lampDataList.size(); j++) {
|
|
|
|
117
|
+ JSONObject lamp = (JSONObject) lampDataList.get(j);
|
|
|
|
118
|
+ int state = lamp.getIntValue("lampState");
|
|
|
|
119
|
+ long duration = lamp.getLongValue("duration");
|
|
|
|
120
|
+ switch (state) {
|
|
|
|
121
|
+ case 0 -> offTotal += duration;
|
|
|
|
122
|
+ case 1 -> redTotal += duration;
|
|
|
|
123
|
+ case 2 -> yellowTotal += duration;
|
|
|
|
124
|
+ case 3 -> greenTotal += duration;
|
|
|
|
125
|
+ case 4 -> blueTotal += duration;
|
|
|
|
126
|
+ }
|
|
|
|
127
|
+ }
|
|
|
|
128
|
+ }
|
|
|
|
129
|
+
|
|
|
|
130
|
+ Map<String, String> stats = new LinkedHashMap<>();
|
|
|
|
131
|
+ stats.put("off", formatDuration(offTotal));
|
|
|
|
132
|
+ stats.put("red", formatDuration(redTotal));
|
|
|
|
133
|
+ stats.put("yellow", formatDuration(yellowTotal));
|
|
|
|
134
|
+ stats.put("green", formatDuration(greenTotal));
|
|
|
|
135
|
+ stats.put("blue", formatDuration(blueTotal));
|
|
|
|
136
|
+
|
|
|
|
137
|
+ return Map.of(
|
|
|
|
138
|
+ "lampDurationStats", stats,
|
|
|
|
139
|
+ "list", dataList
|
|
|
|
140
|
+ );
|
|
|
|
141
|
+ }
|
|
|
|
142
|
+
|
|
|
|
143
|
+ private String formatDuration(long totalSeconds) {
|
|
|
|
144
|
+ long hours = totalSeconds / 3600;
|
|
|
|
145
|
+ long minutes = (totalSeconds % 3600) / 60;
|
|
|
|
146
|
+ long seconds = totalSeconds % 60;
|
|
|
|
147
|
+
|
|
|
|
148
|
+ if (hours > 0) {
|
|
|
|
149
|
+ return hours + "时" + minutes + "分" + seconds + "秒";
|
|
|
|
150
|
+ }
|
|
|
|
151
|
+ if (minutes > 0) {
|
|
|
|
152
|
+ return minutes + "分" + seconds + "秒";
|
|
|
|
153
|
+ }
|
|
|
|
154
|
+ return seconds + "秒";
|
|
|
|
155
|
+ }
|
|
|
|
156
|
+
|
|
|
|
157
|
+ /**
|
|
|
|
158
|
+ * 稼动率/OEE统计查询
|
|
|
|
159
|
+ * @param dtuSn 设备序列号(可选,为空查全部)
|
|
|
|
160
|
+ * @param type 查询类型:day-日(按日期段), week-周(今年第1周~本周), month-月(今年1月~本月)
|
|
|
|
161
|
+ * @param startDate 日模式下的开始日期(yyyy-MM-dd)
|
|
|
|
162
|
+ * @param endDate 日模式下的结束日期(yyyy-MM-dd)
|
|
|
|
163
|
+ */
|
|
|
|
164
|
+ public Map<String, Object> queryOeeStats(String dtuSn, String type, String startDate, String endDate) {
|
|
|
|
165
|
+ // 1. 根据类型确定日期范围
|
|
|
|
166
|
+ List<String> dates = buildDateRange(type, startDate, endDate);
|
|
|
|
167
|
+ log.info("OEE查询 - type:{}, dtuSn:{}, 日期范围:{}天, 起始:{}, 结束:{}", type, dtuSn, dates.size(), dates.get(0), dates.get(dates.size() - 1));
|
|
|
|
168
|
+
|
|
|
|
169
|
+ // 判断是否包含今天
|
|
|
|
170
|
+ String todayStr = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
|
|
|
|
171
|
+ boolean includeToday = dates.contains(todayStr);
|
|
|
|
172
|
+
|
|
|
|
173
|
+ // 2. 从数据库查询 oee 表数据
|
|
|
|
174
|
+ List<Map<String, Object>> dbRecords = queryOeeFromDb(dtuSn, dates, includeToday ? todayStr : null);
|
|
|
|
175
|
+ log.info("OEE查询 - DB返回记录数:{}, 排除today:{}", dbRecords.size(), includeToday ? todayStr : "无");
|
|
|
|
176
|
+
|
|
|
|
177
|
+ Map<String, JSONObject> dbDataMap = new LinkedHashMap<>();
|
|
|
|
178
|
+ for (Map<String, Object> record : dbRecords) {
|
|
|
|
179
|
+ String oeeDate = String.valueOf(record.get("oee_date"));
|
|
|
|
180
|
+ // 截断时间部分,只保留日期 yyyy-MM-dd
|
|
|
|
181
|
+ if (oeeDate.length() > 10) {
|
|
|
|
182
|
+ oeeDate = oeeDate.substring(0, 10);
|
|
|
|
183
|
+ }
|
|
|
|
184
|
+ String lamp1 = (String) record.get("triColorLamp1");
|
|
|
|
185
|
+ String lamp2 = (String) record.get("triColorLamp2");
|
|
|
|
186
|
+ String fullJson = (lamp1 != null ? lamp1 : "") + (lamp2 != null ? lamp2 : "");
|
|
|
|
187
|
+ if (!fullJson.isEmpty()) {
|
|
|
|
188
|
+ try {
|
|
|
|
189
|
+ JSONArray arr = JSONArray.parseArray(fullJson);
|
|
|
|
190
|
+ dbDataMap.put(oeeDate, new JSONObject().fluentPut("lampData", arr));
|
|
|
|
191
|
+ } catch (Exception e) {
|
|
|
|
192
|
+ log.warn("解析OEE JSON异常 - date:{}, dtuSn:{}", oeeDate, dtuSn, e);
|
|
|
|
193
|
+ }
|
|
|
|
194
|
+ }
|
|
|
|
195
|
+ }
|
|
|
|
196
|
+
|
|
|
|
197
|
+ // 3. 如果包含今天且数据库没有今天的实时数据,则调用接口获取
|
|
|
|
198
|
+ if (includeToday && !dbDataMap.containsKey(todayStr)) {
|
|
|
|
199
|
+ if (StringUtils.hasText(dtuSn)) {
|
|
|
|
200
|
+ String apiResult = devicePullService.getLampData(dtuSn, todayStr);
|
|
|
|
201
|
+ if (StringUtils.hasText(apiResult)) {
|
|
|
|
202
|
+ Map<String, Object> res = com.alibaba.fastjson.JSON.parseObject(apiResult,
|
|
|
|
203
|
+ new com.alibaba.fastjson.TypeReference<>() {});
|
|
|
|
204
|
+ Integer code = (Integer) res.get("code");
|
|
|
|
205
|
+ if (code != null && code == 200) {
|
|
|
|
206
|
+ JSONArray dataList = (JSONArray) res.get("data");
|
|
|
|
207
|
+ if (dataList != null && !dataList.isEmpty()) {
|
|
|
|
208
|
+ JSONObject dataObj = (JSONObject) dataList.get(0);
|
|
|
|
209
|
+ dbDataMap.put(todayStr, dataObj);
|
|
|
|
210
|
+ }
|
|
|
|
211
|
+ }
|
|
|
|
212
|
+ }
|
|
|
|
213
|
+ }
|
|
|
|
214
|
+ }
|
|
|
|
215
|
+ log.info("OEE查询 - 最终有效数据日期数:{}, 日期列表:{}", dbDataMap.size(), dbDataMap.keySet());
|
|
|
|
216
|
+
|
|
|
|
217
|
+ // 4. 根据 type 决定统计粒度
|
|
|
|
218
|
+ List<Map<String, Object>> statsList;
|
|
|
|
219
|
+ Map<String, Object> summary;
|
|
|
|
220
|
+
|
|
|
|
221
|
+ if ("day".equals(type)) {
|
|
|
|
222
|
+ Map<String, Object> result = aggregateByDay(dates, dbDataMap);
|
|
|
|
223
|
+ statsList = (List<Map<String, Object>>) result.get("list");
|
|
|
|
224
|
+ summary = (Map<String, Object>) result.get("summary");
|
|
|
|
225
|
+ } else if ("week".equals(type)) {
|
|
|
|
226
|
+ Map<String, Object> result = aggregateByWeek(dates, dbDataMap);
|
|
|
|
227
|
+ statsList = (List<Map<String, Object>>) result.get("list");
|
|
|
|
228
|
+ summary = (Map<String, Object>) result.get("summary");
|
|
|
|
229
|
+ } else {
|
|
|
|
230
|
+ Map<String, Object> result = aggregateByMonth(dates, dbDataMap);
|
|
|
|
231
|
+ statsList = (List<Map<String, Object>>) result.get("list");
|
|
|
|
232
|
+ summary = (Map<String, Object>) result.get("summary");
|
|
|
|
233
|
+ }
|
|
|
|
234
|
+
|
|
|
|
235
|
+ return Map.of(
|
|
|
|
236
|
+ "summary", summary,
|
|
|
|
237
|
+ "list", statsList
|
|
|
|
238
|
+ );
|
|
|
|
239
|
+ }
|
|
|
|
240
|
+
|
|
|
|
241
|
+ /**
|
|
|
|
242
|
+ * 按天统计
|
|
|
|
243
|
+ */
|
|
|
|
244
|
+ private Map<String, Object> aggregateByDay(List<String> dates, Map<String, JSONObject> dbDataMap) {
|
|
|
|
245
|
+ List<Map<String, Object>> dailyStats = new ArrayList<>();
|
|
|
|
246
|
+ long totalOffDur = 0, totalRedDur = 0, totalYellowDur = 0, totalGreenDur = 0, totalBlueDur = 0;
|
|
|
|
247
|
+ int totalOffCnt = 0, totalRedCnt = 0, totalYellowCnt = 0, totalGreenCnt = 0, totalBlueCnt = 0;
|
|
|
|
248
|
+
|
|
|
|
249
|
+ for (String d : dates) {
|
|
|
|
250
|
+ long[] counts = countLampData(dbDataMap.get(d));
|
|
|
|
251
|
+
|
|
|
|
252
|
+ totalOffDur += counts[0]; totalRedDur += counts[2]; totalYellowDur += counts[4];
|
|
|
|
253
|
+ totalGreenDur += counts[6]; totalBlueDur += counts[8];
|
|
|
|
254
|
+ totalOffCnt += (int)counts[1]; totalRedCnt += (int)counts[3]; totalYellowCnt += (int)counts[5];
|
|
|
|
255
|
+ totalGreenCnt += (int)counts[7]; totalBlueCnt += (int)counts[9];
|
|
|
|
256
|
+
|
|
|
|
257
|
+ Map<String, Object> dayStat = new LinkedHashMap<>();
|
|
|
|
258
|
+ dayStat.put("label", d);
|
|
|
|
259
|
+ dayStat.put("date", d);
|
|
|
|
260
|
+ dayStat.put("off", Map.of("duration", formatDuration(counts[0]), "seconds", counts[0], "count", (int)counts[1]));
|
|
|
|
261
|
+ dayStat.put("red", Map.of("duration", formatDuration(counts[2]), "seconds", counts[2], "count", (int)counts[3]));
|
|
|
|
262
|
+ dayStat.put("yellow", Map.of("duration", formatDuration(counts[4]), "seconds", counts[4], "count", (int)counts[5]));
|
|
|
|
263
|
+ dayStat.put("green", Map.of("duration", formatDuration(counts[6]), "seconds", counts[6], "count", (int)counts[7]));
|
|
|
|
264
|
+ dayStat.put("blue", Map.of("duration", formatDuration(counts[8]), "seconds", counts[8], "count", (int)counts[9]));
|
|
|
|
265
|
+ dailyStats.add(dayStat);
|
|
|
|
266
|
+ }
|
|
|
|
267
|
+
|
|
|
|
268
|
+ Map<String, Object> summary = buildSummary(totalOffDur, totalRedDur, totalYellowDur, totalGreenDur, totalBlueDur,
|
|
|
|
269
|
+ totalOffCnt, totalRedCnt, totalYellowCnt, totalGreenCnt, totalBlueCnt);
|
|
|
|
270
|
+
|
|
|
|
271
|
+ return Map.of("list", dailyStats, "summary", summary);
|
|
|
|
272
|
+ }
|
|
|
|
273
|
+
|
|
|
|
274
|
+ /**
|
|
|
|
275
|
+ * 按周统计
|
|
|
|
276
|
+ */
|
|
|
|
277
|
+ private Map<String, Object> aggregateByWeek(List<String> dates, Map<String, JSONObject> dbDataMap) {
|
|
|
|
278
|
+ // 按自然周(周一~周日)分组,第一周从1月1日开始(可能不足7天)
|
|
|
|
279
|
+ List<List<String>> weekBuckets = new ArrayList<>();
|
|
|
|
280
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
281
|
+
|
|
|
|
282
|
+ // 确定年份和该年第一个周一的日期
|
|
|
|
283
|
+ String yearStr = dates.get(0).substring(0, 4);
|
|
|
|
284
|
+ int year = Integer.parseInt(yearStr);
|
|
|
|
285
|
+ Calendar jan1 = Calendar.getInstance();
|
|
|
|
286
|
+ jan1.set(year, Calendar.JANUARY, 1);
|
|
|
|
287
|
+ int jan1Dow = jan1.get(Calendar.DAY_OF_WEEK);
|
|
|
|
288
|
+
|
|
|
|
289
|
+ // 找到第一个周一:如果1月1日就是周一,则firstMonday=1月1日;否则往后推到周一
|
|
|
|
290
|
+ Calendar firstMonday = Calendar.getInstance();
|
|
|
|
291
|
+ firstMonday.setTime(jan1.getTime());
|
|
|
|
292
|
+ int daysToAdd = jan1Dow == Calendar.MONDAY ? 0 : (Calendar.MONDAY - jan1Dow + 7) % 7;
|
|
|
|
293
|
+ firstMonday.add(Calendar.DATE, daysToAdd);
|
|
|
|
294
|
+
|
|
|
|
295
|
+ for (String d : dates) {
|
|
|
|
296
|
+ try {
|
|
|
|
297
|
+ Date date = sdf.parse(d);
|
|
|
|
298
|
+ Calendar cal = Calendar.getInstance();
|
|
|
|
299
|
+ cal.setTime(date);
|
|
|
|
300
|
+ cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
301
|
+ cal.set(Calendar.MINUTE, 0);
|
|
|
|
302
|
+ cal.set(Calendar.SECOND, 0);
|
|
|
|
303
|
+ cal.set(Calendar.MILLISECOND, 0);
|
|
|
|
304
|
+
|
|
|
|
305
|
+ int bucketIndex;
|
|
|
|
306
|
+ if (!cal.after(firstMonday)) {
|
|
|
|
307
|
+ // 在第一个周一之前(含当天),属于第1周(从1月1日开始)
|
|
|
|
308
|
+ bucketIndex = 0;
|
|
|
|
309
|
+ } else {
|
|
|
|
310
|
+ // 第一个周一之后,按完整自然周计算
|
|
|
|
311
|
+ long diffMs = cal.getTimeInMillis() - firstMonday.getTimeInMillis();
|
|
|
|
312
|
+ long diffDays = diffMs / (1000 * 60 * 60 * 24);
|
|
|
|
313
|
+ bucketIndex = 1 + (int)(diffDays / 7); // 第2周、第3周...
|
|
|
|
314
|
+ }
|
|
|
|
315
|
+
|
|
|
|
316
|
+ while (weekBuckets.size() <= bucketIndex) {
|
|
|
|
317
|
+ weekBuckets.add(new ArrayList<>());
|
|
|
|
318
|
+ }
|
|
|
|
319
|
+ weekBuckets.get(bucketIndex).add(d);
|
|
|
|
320
|
+ } catch (Exception e) {
|
|
|
|
321
|
+ log.warn("解析日期失败: {}", d);
|
|
|
|
322
|
+ }
|
|
|
|
323
|
+ }
|
|
|
|
324
|
+
|
|
|
|
325
|
+ List<Map<String, Object>> weeklyStats = new ArrayList<>();
|
|
|
|
326
|
+ long tOffD=0, tRedD=0, tYelD=0, tGrnD=0, tBluD=0;
|
|
|
|
327
|
+ int tOffC=0, tRedC=0, tYelC=0, tGrnC=0, tBluC=0;
|
|
|
|
328
|
+
|
|
|
|
329
|
+ for (int i = 0; i < weekBuckets.size(); i++) {
|
|
|
|
330
|
+ List<String> daysInWeek = weekBuckets.get(i);
|
|
|
|
331
|
+ if (daysInWeek.isEmpty()) continue;
|
|
|
|
332
|
+ long wOffD=0, wRedD=0, wYelD=0, wGrnD=0, wBluD=0;
|
|
|
|
333
|
+ int wOffC=0, wRedC=0, wYelC=0, wGrnC=0, wBluC=0;
|
|
|
|
334
|
+
|
|
|
|
335
|
+ for (String d : daysInWeek) {
|
|
|
|
336
|
+ long[] cnt = countLampData(dbDataMap.get(d));
|
|
|
|
337
|
+ wOffD += cnt[0]; wRedD += cnt[2]; wYelD += cnt[4]; wGrnD += cnt[6]; wBluD += cnt[8];
|
|
|
|
338
|
+ wOffC += (int)cnt[1]; wRedC += (int)cnt[3]; wYelC += (int)cnt[5]; wGrnC += (int)cnt[7]; wBluC += (int)cnt[9];
|
|
|
|
339
|
+ }
|
|
|
|
340
|
+
|
|
|
|
341
|
+ tOffD+=wOffD; tRedD+=wRedD; tYelD+=wYelD; tGrnD+=wGrnD; tBluD+=wBluD;
|
|
|
|
342
|
+ tOffC+=wOffC; tRedC+=wRedC; tYelC+=wYelC; tGrnC+=wGrnC; tBluC+=wBluC;
|
|
|
|
343
|
+
|
|
|
|
344
|
+ Collections.sort(daysInWeek);
|
|
|
|
345
|
+ Map<String, Object> ws = new LinkedHashMap<>();
|
|
|
|
346
|
+ ws.put("label", (i + 1) + "周");
|
|
|
|
347
|
+ ws.put("week", yearStr + "-W" + String.format("%02d", i + 1));
|
|
|
|
348
|
+ ws.put("startDate", daysInWeek.get(0));
|
|
|
|
349
|
+ ws.put("endDate", daysInWeek.get(daysInWeek.size() - 1));
|
|
|
|
350
|
+ ws.put("off", Map.of("duration", formatDuration(wOffD), "seconds", wOffD, "count", wOffC));
|
|
|
|
351
|
+ ws.put("red", Map.of("duration", formatDuration(wRedD), "seconds", wRedD, "count", wRedC));
|
|
|
|
352
|
+ ws.put("yellow", Map.of("duration", formatDuration(wYelD), "seconds", wYelD, "count", wYelC));
|
|
|
|
353
|
+ ws.put("green", Map.of("duration", formatDuration(wGrnD), "seconds", wGrnD, "count", wGrnC));
|
|
|
|
354
|
+ ws.put("blue", Map.of("duration", formatDuration(wBluD), "seconds", wBluD, "count", wBluC));
|
|
|
|
355
|
+ weeklyStats.add(ws);
|
|
|
|
356
|
+ }
|
|
|
|
357
|
+
|
|
|
|
358
|
+ Map<String, Object> summary = buildSummary(tOffD, tRedD, tYelD, tGrnD, tBluD,
|
|
|
|
359
|
+ tOffC, tRedC, tYelC, tGrnC, tBluC);
|
|
|
|
360
|
+ return Map.of("list", weeklyStats, "summary", summary);
|
|
|
|
361
|
+ }
|
|
|
|
362
|
+
|
|
|
|
363
|
+ /**
|
|
|
|
364
|
+ * 按月统计
|
|
|
|
365
|
+ */
|
|
|
|
366
|
+ private Map<String, Object> aggregateByMonth(List<String> dates, Map<String, JSONObject> dbDataMap) {
|
|
|
|
367
|
+ // 按 年-月 分组
|
|
|
|
368
|
+ Map<String, List<String>> monthGroups = new LinkedHashMap<>();
|
|
|
|
369
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
370
|
+
|
|
|
|
371
|
+ for (String d : dates) {
|
|
|
|
372
|
+ String monthKey = d.substring(0, 7); // yyyy-MM
|
|
|
|
373
|
+ monthGroups.computeIfAbsent(monthKey, k -> new ArrayList<>()).add(d);
|
|
|
|
374
|
+ }
|
|
|
|
375
|
+
|
|
|
|
376
|
+ List<Map<String, Object>> monthlyStats = new ArrayList<>();
|
|
|
|
377
|
+ long tOffD=0, tRedD=0, tYelD=0, tGrnD=0, tBluD=0;
|
|
|
|
378
|
+ int tOffC=0, tRedC=0, tYelC=0, tGrnC=0, tBluC=0;
|
|
|
|
379
|
+
|
|
|
|
380
|
+ for (Map.Entry<String, List<String>> entry : monthGroups.entrySet()) {
|
|
|
|
381
|
+ String monthKey = entry.getKey();
|
|
|
|
382
|
+ List<String> daysInMonth = entry.getValue();
|
|
|
|
383
|
+ long mOffD=0, mRedD=0, mYelD=0, mGrnD=0, mBluD=0;
|
|
|
|
384
|
+ int mOffC=0, mRedC=0, mYelC=0, mGrnC=0, mBluC=0;
|
|
|
|
385
|
+
|
|
|
|
386
|
+ for (String d : daysInMonth) {
|
|
|
|
387
|
+ long[] cnt = countLampData(dbDataMap.get(d));
|
|
|
|
388
|
+ mOffD += cnt[0]; mRedD += cnt[2]; mYelD += cnt[4]; mGrnD += cnt[6]; mBluD += cnt[8];
|
|
|
|
389
|
+ mOffC += (int)cnt[1]; mRedC += (int)cnt[3]; mYelC += (int)cnt[5]; mGrnC += (int)cnt[7]; mBluC += (int)cnt[9];
|
|
|
|
390
|
+ }
|
|
|
|
391
|
+
|
|
|
|
392
|
+ tOffD+=mOffD; tRedD+=mRedD; tYelD+=mYelD; tGrnD+=mGrnD; tBluD+=mBluD;
|
|
|
|
393
|
+ tOffC+=mOffC; tRedC+=mRedC; tYelC+=mYelC; tGrnC+=mGrnC; tBluC+=mBluC;
|
|
|
|
394
|
+
|
|
|
|
395
|
+ String monthNum = monthKey.substring(5); // "01", "02" ...
|
|
|
|
396
|
+ Map<String, Object> ms = new LinkedHashMap<>();
|
|
|
|
397
|
+ ms.put("label", Integer.parseInt(monthNum) + "月");
|
|
|
|
398
|
+ ms.put("month", monthKey);
|
|
|
|
399
|
+ ms.put("off", Map.of("duration", formatDuration(mOffD), "seconds", mOffD, "count", mOffC));
|
|
|
|
400
|
+ ms.put("red", Map.of("duration", formatDuration(mRedD), "seconds", mRedD, "count", mRedC));
|
|
|
|
401
|
+ ms.put("yellow", Map.of("duration", formatDuration(mYelD), "seconds", mYelD, "count", mYelC));
|
|
|
|
402
|
+ ms.put("green", Map.of("duration", formatDuration(mGrnD), "seconds", mGrnD, "count", mGrnC));
|
|
|
|
403
|
+ ms.put("blue", Map.of("duration", formatDuration(mBluD), "seconds", mBluD, "count", mBluC));
|
|
|
|
404
|
+ monthlyStats.add(ms);
|
|
|
|
405
|
+ }
|
|
|
|
406
|
+
|
|
|
|
407
|
+ Map<String, Object> summary = buildSummary(tOffD, tRedD, tYelD, tGrnD, tBluD,
|
|
|
|
408
|
+ tOffC, tRedC, tYelC, tGrnC, tBluC);
|
|
|
|
409
|
+ return Map.of("list", monthlyStats, "summary", summary);
|
|
|
|
410
|
+ }
|
|
|
|
411
|
+
|
|
|
|
412
|
+ /**
|
|
|
|
413
|
+ * 统计单日 lampData 各状态时长和次数
|
|
|
|
414
|
+ * 返回 [offDur, offCnt, redDur, redCnt, yelDur, yelCnt, grnDur, grnCnt, bluDur, bluCnt]
|
|
|
|
415
|
+ */
|
|
|
|
416
|
+ private long[] countLampData(JSONObject dayData) {
|
|
|
|
417
|
+ long[] result = new long[10];
|
|
|
|
418
|
+ if (dayData != null) {
|
|
|
|
419
|
+ JSONArray lampArr = dayData.getJSONArray("lampData");
|
|
|
|
420
|
+ if (lampArr != null) {
|
|
|
|
421
|
+ for (int i = 0; i < lampArr.size(); i++) {
|
|
|
|
422
|
+ JSONObject item = lampArr.getJSONObject(i);
|
|
|
|
423
|
+ int state = item.getIntValue("lampState");
|
|
|
|
424
|
+ long dur = item.getLongValue("duration");
|
|
|
|
425
|
+ switch (state) {
|
|
|
|
426
|
+ case 0 -> { result[0] += dur; result[1]++; }
|
|
|
|
427
|
+ case 1 -> { result[2] += dur; result[3]++; }
|
|
|
|
428
|
+ case 2 -> { result[4] += dur; result[5]++; }
|
|
|
|
429
|
+ case 3 -> { result[6] += dur; result[7]++; }
|
|
|
|
430
|
+ case 4 -> { result[8] += dur; result[9]++; }
|
|
|
|
431
|
+ }
|
|
|
|
432
|
+ }
|
|
|
|
433
|
+ }
|
|
|
|
434
|
+ }
|
|
|
|
435
|
+ return result;
|
|
|
|
436
|
+ }
|
|
|
|
437
|
+
|
|
|
|
438
|
+ /**
|
|
|
|
439
|
+ * 构建 summary 汇总对象
|
|
|
|
440
|
+ */
|
|
|
|
441
|
+ private Map<String, Object> buildSummary(long offD, long redD, long yelD, long grnD, long bluD,
|
|
|
|
442
|
+ int offC, int redC, int yelC, int grnC, int bluC) {
|
|
|
|
443
|
+ Map<String, Object> summary = new LinkedHashMap<>();
|
|
|
|
444
|
+ summary.put("off", Map.of("duration", formatDuration(offD), "seconds", offD, "count", offC));
|
|
|
|
445
|
+ summary.put("red", Map.of("duration", formatDuration(redD), "seconds", redD, "count", redC));
|
|
|
|
446
|
+ summary.put("yellow", Map.of("duration", formatDuration(yelD), "seconds", yelD, "count", yelC));
|
|
|
|
447
|
+ summary.put("green", Map.of("duration", formatDuration(grnD), "seconds", grnD, "count", grnC));
|
|
|
|
448
|
+ summary.put("blue", Map.of("duration", formatDuration(bluD), "seconds", bluD, "count", bluC));
|
|
|
|
449
|
+ return summary;
|
|
|
|
450
|
+ }
|
|
|
|
451
|
+
|
|
|
|
452
|
+ /**
|
|
|
|
453
|
+ * 根据查询类型构建日期列表
|
|
|
|
454
|
+ */
|
|
|
|
455
|
+ private List<String> buildDateRange(String type, String startDate, String endDate) {
|
|
|
|
456
|
+ List<String> result = new ArrayList<>();
|
|
|
|
457
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
458
|
+
|
|
|
|
459
|
+ switch (type) {
|
|
|
|
460
|
+ case "day": {
|
|
|
|
461
|
+ // 指定日期范围
|
|
|
|
462
|
+ if (StringUtils.hasText(startDate) && StringUtils.hasText(endDate)) {
|
|
|
|
463
|
+ try {
|
|
|
|
464
|
+ Date start = sdf.parse(startDate);
|
|
|
|
465
|
+ Date end = sdf.parse(endDate);
|
|
|
|
466
|
+ Calendar cur = Calendar.getInstance();
|
|
|
|
467
|
+ cur.setTime(start);
|
|
|
|
468
|
+ while (!cur.getTime().after(end)) {
|
|
|
|
469
|
+ result.add(sdf.format(cur.getTime()));
|
|
|
|
470
|
+ cur.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
471
|
+ }
|
|
|
|
472
|
+ } catch (Exception e) {
|
|
|
|
473
|
+ log.error("日期解析失败: {} ~ {}", startDate, endDate, e);
|
|
|
|
474
|
+ }
|
|
|
|
475
|
+ }
|
|
|
|
476
|
+ break;
|
|
|
|
477
|
+ }
|
|
|
|
478
|
+ case "week": {
|
|
|
|
479
|
+ // 今年1月1日 ~ 今天所在周的周日(自然周,不跨年)
|
|
|
|
480
|
+ Calendar now = Calendar.getInstance();
|
|
|
|
481
|
+ int currentYear = now.get(Calendar.YEAR);
|
|
|
|
482
|
+
|
|
|
|
483
|
+ Calendar startCal = Calendar.getInstance();
|
|
|
|
484
|
+ startCal.set(currentYear, Calendar.JANUARY, 1);
|
|
|
|
485
|
+
|
|
|
|
486
|
+ // 本周结束(周日)
|
|
|
|
487
|
+ Calendar endCal = (Calendar) now.clone();
|
|
|
|
488
|
+ int todayDow = endCal.get(Calendar.DAY_OF_WEEK);
|
|
|
|
489
|
+ int toSunday = todayDow == Calendar.SUNDAY ? 0 : (7 - todayDow);
|
|
|
|
490
|
+ endCal.add(Calendar.DATE, toSunday);
|
|
|
|
491
|
+
|
|
|
|
492
|
+ Calendar cur = Calendar.getInstance();
|
|
|
|
493
|
+ cur.setTime(startCal.getTime());
|
|
|
|
494
|
+ while (!cur.after(endCal)) {
|
|
|
|
495
|
+ result.add(sdf.format(cur.getTime()));
|
|
|
|
496
|
+ cur.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
497
|
+ }
|
|
|
|
498
|
+ break;
|
|
|
|
499
|
+ }
|
|
|
|
500
|
+ case "month": {
|
|
|
|
501
|
+ // 今年1月 ~ 本月最后一天
|
|
|
|
502
|
+ Calendar now = Calendar.getInstance();
|
|
|
|
503
|
+ int year = now.get(Calendar.YEAR);
|
|
|
|
504
|
+ int thisMonth = now.get(Calendar.MONTH); // 0-indexed
|
|
|
|
505
|
+
|
|
|
|
506
|
+ for (int m = 0; m <= thisMonth; m++) {
|
|
|
|
507
|
+ Calendar cal = Calendar.getInstance();
|
|
|
|
508
|
+ cal.set(year, m, 1);
|
|
|
|
509
|
+ int lastDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
|
|
|
510
|
+ for (int d = 1; d <= lastDay; d++) {
|
|
|
|
511
|
+ cal.set(year, m, d);
|
|
|
|
512
|
+ result.add(sdf.format(cal.getTime()));
|
|
|
|
513
|
+ }
|
|
|
|
514
|
+ }
|
|
|
|
515
|
+ break;
|
|
|
|
516
|
+ }
|
|
|
|
517
|
+ }
|
|
|
|
518
|
+ return result;
|
|
|
|
519
|
+ }
|
|
|
|
520
|
+
|
|
|
|
521
|
+ /**
|
|
|
|
522
|
+ * 从 oee 表查询指定日期范围的数据
|
|
|
|
523
|
+ */
|
|
|
|
524
|
+ private List<Map<String, Object>> queryOeeFromDb(String dtuSn, List<String> dates, String excludeToday) {
|
|
|
|
525
|
+ if (dates.isEmpty()) return Collections.emptyList();
|
|
|
|
526
|
+
|
|
|
|
527
|
+ StringBuilder sql = new StringBuilder(
|
|
|
|
528
|
+ "SELECT oee_date, triColorLamp1, triColorLamp2 FROM " + oeeTableName + " WHERE corp_code = ?");
|
|
|
|
529
|
+ List<Object> params = new ArrayList<>();
|
|
|
|
530
|
+ params.add(deviceCorpCode);
|
|
|
|
531
|
+
|
|
|
|
532
|
+ if (StringUtils.hasText(dtuSn)) {
|
|
|
|
533
|
+ sql.append(" AND dtuSn = ?");
|
|
|
|
534
|
+ params.add(dtuSn);
|
|
|
|
535
|
+ }
|
|
|
|
536
|
+
|
|
|
|
537
|
+ sql.append(" AND oee_date IN (");
|
|
|
|
538
|
+ List<Object> dateParams = new ArrayList<>();
|
|
|
|
539
|
+ for (String d : dates) {
|
|
|
|
540
|
+ if (d.equals(excludeToday)) continue; // 排除今天,今天走接口
|
|
|
|
541
|
+ sql.append("?,");
|
|
|
|
542
|
+ dateParams.add(d);
|
|
|
|
543
|
+ }
|
|
|
|
544
|
+ if (dateParams.isEmpty()) {
|
|
|
|
545
|
+ return Collections.emptyList(); // 所有日期都是今天
|
|
|
|
546
|
+ }
|
|
|
|
547
|
+ sql.deleteCharAt(sql.length() - 1).append(")");
|
|
|
|
548
|
+ params.addAll(dateParams);
|
|
|
|
549
|
+ sql.append(" ORDER BY oee_date ASC");
|
|
|
|
550
|
+
|
|
|
|
551
|
+ return jdbcTemplate.queryForList(sql.toString(), params.toArray());
|
|
|
|
552
|
+ }
|
|
88
|
} |
553
|
} |