Commit 3abc706505588a0e6b5d01d7e86011f75dccd7e0

Authored by 杨鸣坤
1 parent 07e63186

feat: 添加开机率查询接口并启用定时同步任务

... ... @@ -96,4 +96,18 @@ public class HealthController {
96 96 @RequestParam(required = false) String endDate) {
97 97 return deviceSearchService.queryLampStatistics(startDate, endDate);
98 98 }
  99 +
  100 + /**
  101 + * 开机率查询(基于 dev_util 表)
  102 + * 灭灯(state=0)=未开机,其他灯=开机,每台设备单独计算,不分页
  103 + *
  104 + * @param startDate 开始日期 yyyy-MM-dd
  105 + * @param endDate 结束日期 yyyy-MM-dd
  106 + */
  107 + @GetMapping("/device/bootRate")
  108 + public Map<String, Object> bootRate(
  109 + @RequestParam String startDate,
  110 + @RequestParam String endDate) {
  111 + return deviceSearchService.queryBootRate(startDate, endDate);
  112 + }
99 113 }
... ...
... ... @@ -297,7 +297,7 @@ public class DevicePullService {
297 297 /**
298 298 * 每日增量同步:仅同步昨天的数据(定时任务自动触发)
299 299 */
300   -// @Scheduled(cron = "${scheduler.devUtil.cron:0 30 2 * * ?}")
  300 + @Scheduled(cron = "${scheduler.devUtil.cron:0 30 2 * * ?}")
301 301 public void pullDevUtilDaily() {
302 302 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
303 303 Calendar cal = Calendar.getInstance();
... ... @@ -456,7 +456,7 @@ public class DevicePullService {
456 456 /**
457 457 * 每日增量同步 OEE:仅同步昨天(定时任务自动触发)
458 458 */
459   -// @Scheduled(cron = "${scheduler.oee.cron:0 35 2 * * ?}")
  459 + @Scheduled(cron = "${scheduler.oee.cron:0 35 2 * * ?}")
460 460 public void pullOeeDaily() {
461 461 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
462 462 Calendar cal = Calendar.getInstance();
... ...
... ... @@ -24,6 +24,8 @@ public class DeviceSearchService {
24 24 private String deviceTableName;
25 25 @Value("${device.db.oeeTableName}")
26 26 private String oeeTableName;
  27 + @Value("${device.db.devUtilTableName}")
  28 + private String devUtilTableName;
27 29
28 30 @Resource
29 31 private JdbcTemplate jdbcTemplate;
... ... @@ -1121,4 +1123,233 @@ public class DeviceSearchService {
1121 1123 }
1122 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 }
... ...
... ... @@ -39,9 +39,9 @@ scheduler:
39 39 pull: "0 0/5 * * * ?"
40 40 push: "0 0/10 * * * ?"
41 41 devUtil:
42   - cron: "0 5 11 * * ?" # 每日凌晨 2:30 增量同步昨天数据
  42 + cron: "0 30 9 * * ?" # 每日凌晨 2:30 增量同步昨天数据
43 43 oee:
44   - cron: "0 5 11 * * ?" # 每日凌晨 2:35 增量同步昨天OEE数据
  44 + cron: "0 30 9 * * ?" # 每日凌晨 2:35 增量同步昨天OEE数据
45 45
46 46 device:
47 47 token:
... ...