|
@@ -24,6 +24,8 @@ public class DeviceSearchService { |
|
@@ -24,6 +24,8 @@ public class DeviceSearchService { |
|
24
|
private String deviceTableName;
|
24
|
private String deviceTableName;
|
|
25
|
@Value("${device.db.oeeTableName}")
|
25
|
@Value("${device.db.oeeTableName}")
|
|
26
|
private String oeeTableName;
|
26
|
private String oeeTableName;
|
|
|
|
27
|
+ @Value("${device.db.devUtilTableName}")
|
|
|
|
28
|
+ private String devUtilTableName;
|
|
27
|
|
29
|
|
|
28
|
@Resource
|
30
|
@Resource
|
|
29
|
private JdbcTemplate jdbcTemplate;
|
31
|
private JdbcTemplate jdbcTemplate;
|
|
@@ -1121,4 +1123,233 @@ public class DeviceSearchService { |
|
@@ -1121,4 +1123,233 @@ public class DeviceSearchService { |
|
1121
|
}
|
1123
|
}
|
|
1122
|
return Map.of("off", off, "red", red, "yellow", yellow, "green", green, "blue", blue);
|
1124
|
return Map.of("off", off, "red", red, "yellow", yellow, "green", green, "blue", blue);
|
|
1123
|
}
|
1125
|
}
|
|
|
|
1126
|
+
|
|
|
|
1127
|
+ // ==================== 开机率查询 ====================
|
|
|
|
1128
|
+
|
|
|
|
1129
|
+ /**
|
|
|
|
1130
|
+ * 查询每台设备的开机率(基于 dev_util 表)
|
|
|
|
1131
|
+ * 规则:灭灯(state=0)=未开机,其他灯(state=1/2/3/4)=开机
|
|
|
|
1132
|
+ * 开机率 = 非灭灯时长 / 总时长 * 100%
|
|
|
|
1133
|
+ *
|
|
|
|
1134
|
+ * @param startDate 开始日期 yyyy-MM-dd
|
|
|
|
1135
|
+ * @param endDate 结束日期 yyyy-MM-dd
|
|
|
|
1136
|
+ */
|
|
|
|
1137
|
+ public Map<String, Object> queryBootRate(String startDate, String endDate) {
|
|
|
|
1138
|
+ log.info("========== [开机率查询] startDate={}, endDate={} ==========", startDate, endDate);
|
|
|
|
1139
|
+
|
|
|
|
1140
|
+ if (!StringUtils.hasText(startDate) || !StringUtils.hasText(endDate)) {
|
|
|
|
1141
|
+ return Map.of("list", List.of(), "startDate", startDate, "endDate", endDate);
|
|
|
|
1142
|
+ }
|
|
|
|
1143
|
+
|
|
|
|
1144
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
1145
|
+ String todayStr = sdf.format(new Date());
|
|
|
|
1146
|
+ boolean includeToday = isDateInRange(todayStr, startDate, endDate);
|
|
|
|
1147
|
+ log.info("设备总数: {}, 包含今日({}): {}", includeToday);
|
|
|
|
1148
|
+
|
|
|
|
1149
|
+ // 1. 获取所有设备列表
|
|
|
|
1150
|
+ Map<String, String> deviceNameMap = queryAllDtuSnWithName();
|
|
|
|
1151
|
+ List<String> allDtuSns = new ArrayList<>(deviceNameMap.keySet());
|
|
|
|
1152
|
+ if (allDtuSns.isEmpty()) {
|
|
|
|
1153
|
+ return Map.of("list", List.of(), "startDate", startDate, "endDate", endDate);
|
|
|
|
1154
|
+ }
|
|
|
|
1155
|
+ log.info("设备总数: {}", allDtuSns.size());
|
|
|
|
1156
|
+
|
|
|
|
1157
|
+ // 2. 从 dev_util 表按 dtuSn 和日期范围汇总各状态时长(排除今天)
|
|
|
|
1158
|
+ String dbStartDate = includeToday && startDate.equals(todayStr) ? getNextDay(todayStr) : startDate;
|
|
|
|
1159
|
+ List<Map<String, Object>> rows;
|
|
|
|
1160
|
+ if (includeToday) {
|
|
|
|
1161
|
+ // 排除今天,今天走接口
|
|
|
|
1162
|
+ rows = queryDevUtilBatch(deviceCorpCode, allDtuSns, startDate, endDate, todayStr);
|
|
|
|
1163
|
+ } else {
|
|
|
|
1164
|
+ String sql = "SELECT dtuSn, SUM(`0`) as s0, SUM(`1`) as s1, SUM(`2`) as s2, SUM(`3`) as s3, SUM(`4`) as s4, COUNT(*) as days " +
|
|
|
|
1165
|
+ "FROM " + devUtilTableName +
|
|
|
|
1166
|
+ " WHERE corp_code = ? AND date_util BETWEEN ? AND ? GROUP BY dtuSn";
|
|
|
|
1167
|
+ rows = jdbcTemplate.queryForList(sql, deviceCorpCode, startDate, endDate);
|
|
|
|
1168
|
+ }
|
|
|
|
1169
|
+ log.info("dev_util 查询返回 {} 条记录", rows.size());
|
|
|
|
1170
|
+
|
|
|
|
1171
|
+ // 3. 按 dtuSn 构建查询结果 map
|
|
|
|
1172
|
+ Map<String, double[]> rawMap = new LinkedHashMap<>(); // [s0, s1, s2, s3, s4, dataDays]
|
|
|
|
1173
|
+ for (Map<String, Object> row : rows) {
|
|
|
|
1174
|
+ String sn = (String) row.get("dtuSn");
|
|
|
|
1175
|
+ double s0 = ((Number) row.get("s0")).doubleValue();
|
|
|
|
1176
|
+ double s1 = ((Number) row.get("s1")).doubleValue();
|
|
|
|
1177
|
+ double s2 = ((Number) row.get("s2")).doubleValue();
|
|
|
|
1178
|
+ double s3 = ((Number) row.get("s3")).doubleValue();
|
|
|
|
1179
|
+ double s4 = ((Number) row.get("s4")).doubleValue();
|
|
|
|
1180
|
+ int dataDays = ((Number) row.get("days")).intValue();
|
|
|
|
1181
|
+ rawMap.put(sn, new double[]{s0, s1, s2, s3, s4, dataDays});
|
|
|
|
1182
|
+ }
|
|
|
|
1183
|
+
|
|
|
|
1184
|
+ // 4. 如果包含今天,补充调用稼动率接口获取今天的实时数据
|
|
|
|
1185
|
+ if (includeToday) {
|
|
|
|
1186
|
+ supplementTodayDevUtil(allDtuSns, todayStr, rawMap);
|
|
|
|
1187
|
+ }
|
|
|
|
1188
|
+
|
|
|
|
1189
|
+ // 5. 构建最终结果
|
|
|
|
1190
|
+ Map<String, Map<String, Object>> rateMap = new LinkedHashMap<>();
|
|
|
|
1191
|
+ for (Map.Entry<String, double[]> entry : rawMap.entrySet()) {
|
|
|
|
1192
|
+ String sn = entry.getKey();
|
|
|
|
1193
|
+ double[] vals = entry.getValue();
|
|
|
|
1194
|
+ double totalDur = vals[0] + vals[1] + vals[2] + vals[3] + vals[4];
|
|
|
|
1195
|
+ double onDur = vals[1] + vals[2] + vals[3] + vals[4]; // 非灭灯时长 = 开机时长
|
|
|
|
1196
|
+ double bootRate = totalDur > 0 ? Math.round(onDur * 10000.0 / totalDur) / 100.0 : 0.0;
|
|
|
|
1197
|
+
|
|
|
|
1198
|
+ Map<String, Object> item = new LinkedHashMap<>();
|
|
|
|
1199
|
+ item.put("dtuSn", sn);
|
|
|
|
1200
|
+ item.put("deviceName", deviceNameMap.getOrDefault(sn, ""));
|
|
|
|
1201
|
+ item.put("totalDuration", formatDuration((long) totalDur));
|
|
|
|
1202
|
+ item.put("totalSeconds", Math.round(totalDur));
|
|
|
|
1203
|
+ item.put("onDuration", formatDuration((long) onDur));
|
|
|
|
1204
|
+ item.put("onSeconds", Math.round(onDur));
|
|
|
|
1205
|
+ item.put("offDuration", formatDuration((long) vals[0]));
|
|
|
|
1206
|
+ item.put("offSeconds", Math.round(vals[0]));
|
|
|
|
1207
|
+ item.put("bootRate", String.format("%.2f%%", bootRate));
|
|
|
|
1208
|
+ item.put("bootRateValue", bootRate);
|
|
|
|
1209
|
+ item.put("dataDays", (int) vals[5]);
|
|
|
|
1210
|
+
|
|
|
|
1211
|
+ rateMap.put(sn, item);
|
|
|
|
1212
|
+ }
|
|
|
|
1213
|
+
|
|
|
|
1214
|
+ // 6. 确保所有设备都在返回列表中(没有数据的设备开机率为0)
|
|
|
|
1215
|
+ List<Map<String, Object>> resultList = new ArrayList<>(allDtuSns.size());
|
|
|
|
1216
|
+ for (String sn : allDtuSns) {
|
|
|
|
1217
|
+ if (rateMap.containsKey(sn)) {
|
|
|
|
1218
|
+ resultList.add(rateMap.get(sn));
|
|
|
|
1219
|
+ } else {
|
|
|
|
1220
|
+ Map<String, Object> emptyItem = new LinkedHashMap<>();
|
|
|
|
1221
|
+ emptyItem.put("dtuSn", sn);
|
|
|
|
1222
|
+ emptyItem.put("deviceName", deviceNameMap.getOrDefault(sn, ""));
|
|
|
|
1223
|
+ emptyItem.put("totalDuration", "0时0分0秒");
|
|
|
|
1224
|
+ emptyItem.put("totalSeconds", 0);
|
|
|
|
1225
|
+ emptyItem.put("onDuration", "0时0分0秒");
|
|
|
|
1226
|
+ emptyItem.put("onSeconds", 0);
|
|
|
|
1227
|
+ emptyItem.put("offDuration", "0时0分0秒");
|
|
|
|
1228
|
+ emptyItem.put("offSeconds", 0);
|
|
|
|
1229
|
+ emptyItem.put("bootRate", "0.00%");
|
|
|
|
1230
|
+ emptyItem.put("bootRateValue", 0.0);
|
|
|
|
1231
|
+ emptyItem.put("dataDays", 0);
|
|
|
|
1232
|
+ resultList.add(emptyItem);
|
|
|
|
1233
|
+ }
|
|
|
|
1234
|
+ }
|
|
|
|
1235
|
+
|
|
|
|
1236
|
+ // 计算整体汇总
|
|
|
|
1237
|
+ double sumTotal = 0, sumOn = 0, sumOff = 0;
|
|
|
|
1238
|
+ for (Map<String, Object> r : rateMap.values()) {
|
|
|
|
1239
|
+ sumTotal += ((Number) r.get("totalSeconds")).doubleValue();
|
|
|
|
1240
|
+ sumOn += ((Number) r.get("onSeconds")).doubleValue();
|
|
|
|
1241
|
+ sumOff += ((Number) r.get("offSeconds")).doubleValue();
|
|
|
|
1242
|
+ }
|
|
|
|
1243
|
+ double overallBootRate = sumTotal > 0 ? Math.round(sumOn * 10000.0 / sumTotal) / 100.0 : 0.0;
|
|
|
|
1244
|
+
|
|
|
|
1245
|
+ return Map.of(
|
|
|
|
1246
|
+ "summary", Map.of(
|
|
|
|
1247
|
+ "totalDevices", allDtuSns.size(),
|
|
|
|
1248
|
+ "totalDuration", formatDuration((long) sumTotal),
|
|
|
|
1249
|
+ "totalSeconds", Math.round(sumTotal),
|
|
|
|
1250
|
+ "onDuration", formatDuration((long) sumOn),
|
|
|
|
1251
|
+ "onSeconds", Math.round(sumOn),
|
|
|
|
1252
|
+ "offDuration", formatDuration((long) sumOff),
|
|
|
|
1253
|
+ "offSeconds", Math.round(sumOff),
|
|
|
|
1254
|
+ "overallBootRate", String.format("%.2f%%", overallBootRate),
|
|
|
|
1255
|
+ "overallBootRateValue", overallBootRate
|
|
|
|
1256
|
+ ),
|
|
|
|
1257
|
+ "list", resultList,
|
|
|
|
1258
|
+ "startDate", startDate,
|
|
|
|
1259
|
+ "endDate", endDate
|
|
|
|
1260
|
+ );
|
|
|
|
1261
|
+ }
|
|
|
|
1262
|
+
|
|
|
|
1263
|
+ /** 批量从 dev_util 表查询(排除指定日期) */
|
|
|
|
1264
|
+ private List<Map<String, Object>> queryDevUtilBatch(String corpCode, List<String> dtuSns,
|
|
|
|
1265
|
+ String startDate, String endDate, String excludeToday) {
|
|
|
|
1266
|
+ StringBuilder sql = new StringBuilder(
|
|
|
|
1267
|
+ "SELECT dtuSn, SUM(`0`) as s0, SUM(`1`) as s1, SUM(`2`) as s2, SUM(`3`) as s3, SUM(`4`) as s4, COUNT(*) as days " +
|
|
|
|
1268
|
+ "FROM " + devUtilTableName +
|
|
|
|
1269
|
+ " WHERE corp_code = ? AND dtuSn IN (");
|
|
|
|
1270
|
+ List<Object> params = new ArrayList<>();
|
|
|
|
1271
|
+ params.add(corpCode);
|
|
|
|
1272
|
+
|
|
|
|
1273
|
+ for (String sn : dtuSns) { sql.append("?,"); params.add(sn); }
|
|
|
|
1274
|
+ sql.deleteCharAt(sql.length() - 1).append(")");
|
|
|
|
1275
|
+
|
|
|
|
1276
|
+ // 日期范围:如果开始日期=excludeToday,则从明天开始查
|
|
|
|
1277
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
1278
|
+ String actualStart = startDate.equals(excludeToday) ? getNextDay(excludeToday) : startDate;
|
|
|
|
1279
|
+
|
|
|
|
1280
|
+ try {
|
|
|
|
1281
|
+ Date startD = sdf.parse(actualStart);
|
|
|
|
1282
|
+ Date endD = sdf.parse(endDate);
|
|
|
|
1283
|
+ Date excD = sdf.parse(excludeToday);
|
|
|
|
1284
|
+ if (!startD.before(excD) || endD.before(startD)) {
|
|
|
|
1285
|
+ return Collections.emptyList(); // 只有今天一天或无效范围
|
|
|
|
1286
|
+ }
|
|
|
|
1287
|
+ } catch (Exception e) {
|
|
|
|
1288
|
+ log.error("日期解析失败: {} ~ {}, 排除: {}", actualStart, endDate, excludeToday, e);
|
|
|
|
1289
|
+ return Collections.emptyList();
|
|
|
|
1290
|
+ }
|
|
|
|
1291
|
+
|
|
|
|
1292
|
+ sql.append(" AND date_util BETWEEN ? AND ?");
|
|
|
|
1293
|
+ params.add(actualStart);
|
|
|
|
1294
|
+ params.add(endDate);
|
|
|
|
1295
|
+ sql.append(" GROUP BY dtuSn");
|
|
|
|
1296
|
+
|
|
|
|
1297
|
+ return jdbcTemplate.queryForList(sql.toString(), params.toArray());
|
|
|
|
1298
|
+ }
|
|
|
|
1299
|
+
|
|
|
|
1300
|
+ /** 补充今天的稼动率接口实时数据 */
|
|
|
|
1301
|
+ private void supplementTodayDevUtil(List<String> dtuSns, String todayStr, Map<String, double[]> rawMap) {
|
|
|
|
1302
|
+ log.info("开机率查询 - 开始补充今日({})实时dev_util数据...", todayStr);
|
|
|
|
1303
|
+ int apiCount = 0;
|
|
|
|
1304
|
+ for (String dtuSn : dtuSns) {
|
|
|
|
1305
|
+ try {
|
|
|
|
1306
|
+ String rateResult = devicePullService.getDtuSnRateOfAction(dtuSn, todayStr, todayStr);
|
|
|
|
1307
|
+ if (StringUtils.hasText(rateResult)) {
|
|
|
|
1308
|
+ Map<String, Object> res = JSON.parseObject(rateResult, new com.alibaba.fastjson.TypeReference<>() {});
|
|
|
|
1309
|
+ Integer code = (Integer) res.get("code");
|
|
|
|
1310
|
+ if (code == null || code != 200) continue;
|
|
|
|
1311
|
+
|
|
|
|
1312
|
+ JSONArray dataList = (JSONArray) res.get("data");
|
|
|
|
1313
|
+ if (dataList == null || dataList.isEmpty()) continue;
|
|
|
|
1314
|
+
|
|
|
|
1315
|
+ JSONObject dayData = (JSONObject) dataList.get(0);
|
|
|
|
1316
|
+ JSONArray realRateList = dayData.getJSONArray("realRate");
|
|
|
|
1317
|
+ if (realRateList == null || realRateList.isEmpty()) continue;
|
|
|
|
1318
|
+
|
|
|
|
1319
|
+ JSONObject rateObj = (JSONObject) realRateList.get(0);
|
|
|
|
1320
|
+ double s0 = rateObj.getDoubleValue("0");
|
|
|
|
1321
|
+ double s1 = rateObj.getDoubleValue("1");
|
|
|
|
1322
|
+ double s2 = rateObj.getDoubleValue("2");
|
|
|
|
1323
|
+ double s3 = rateObj.getDoubleValue("3");
|
|
|
|
1324
|
+ double s4 = rateObj.getDoubleValue("4");
|
|
|
|
1325
|
+
|
|
|
|
1326
|
+ // 累加到已有数据上(DB已有历史数据 + 今天实时数据)
|
|
|
|
1327
|
+ double[] existing = rawMap.get(dtuSn);
|
|
|
|
1328
|
+ if (existing != null) {
|
|
|
|
1329
|
+ existing[0] += s0; existing[1] += s1; existing[2] += s2;
|
|
|
|
1330
|
+ existing[3] += s3; existing[4] += s4; existing[5] += 1;
|
|
|
|
1331
|
+ } else {
|
|
|
|
1332
|
+ rawMap.put(dtuSn, new double[]{s0, s1, s2, s3, s4, 1});
|
|
|
|
1333
|
+ }
|
|
|
|
1334
|
+ apiCount++;
|
|
|
|
1335
|
+ }
|
|
|
|
1336
|
+ } catch (Exception e) {
|
|
|
|
1337
|
+ log.error("获取今日稼动率数据异常 - dtuSn:{}", dtuSn, e);
|
|
|
|
1338
|
+ }
|
|
|
|
1339
|
+ }
|
|
|
|
1340
|
+ log.info("开机率查询 - 今日实时数据补充完成, 新增 {} 条", apiCount);
|
|
|
|
1341
|
+ }
|
|
|
|
1342
|
+
|
|
|
|
1343
|
+ /** 获取下一天的日期字符串 */
|
|
|
|
1344
|
+ private static String getNextDay(String dateStr) {
|
|
|
|
1345
|
+ try {
|
|
|
|
1346
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
1347
|
+ Calendar cal = Calendar.getInstance();
|
|
|
|
1348
|
+ cal.setTime(sdf.parse(dateStr));
|
|
|
|
1349
|
+ cal.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
1350
|
+ return sdf.format(cal.getTime());
|
|
|
|
1351
|
+ } catch (Exception e) {
|
|
|
|
1352
|
+ return dateStr;
|
|
|
|
1353
|
+ }
|
|
|
|
1354
|
+ }
|
|
1124
|
} |
1355
|
} |