Commit 07e631867ba9d5cb641f9dec3f74be7c4249eef6

Authored by 杨鸣坤
1 parent eb3a6158

feat: 新增智能灯统计稼动率查询接口

... ... @@ -82,4 +82,18 @@ public class HealthController {
82 82 @RequestParam(defaultValue = "20") Integer pageSize) {
83 83 return deviceSearchService.queryOeeTimeline(startDate, endDate, pageNo, pageSize);
84 84 }
  85 +
  86 + /**
  87 + * 智能灯统计稼动率查询(仪表盘)
  88 + * 支持日/周/月查询,返回总时长、稼动率、当前机台状态、异常排行榜、每设备状态时长
  89 + *
  90 + * @param startDate 开始日期 yyyy-MM-dd (day模式必填)
  91 + * @param endDate 结束日期 yyyy-MM-dd (day模式必填)
  92 + */
  93 + @GetMapping("/device/lampStatistics")
  94 + public Map<String, Object> lampStatistics(
  95 + @RequestParam(required = false) String startDate,
  96 + @RequestParam(required = false) String endDate) {
  97 + return deviceSearchService.queryLampStatistics(startDate, endDate);
  98 + }
85 99 }
... ...
... ... @@ -928,4 +928,197 @@ public class DeviceSearchService {
928 928 )
929 929 );
930 930 }
  931 +
  932 + // ==================== 智能灯统计稼动率查询(仪表盘) ====================
  933 +
  934 + /**
  935 + * 智能灯统计稼动率综合查询(仪表盘数据)
  936 + * 返回:总时长、稼动率、当前机台运行状态、异常排行榜、每设备状态时长
  937 + *
  938 + * @param startDate 开始日期 yyyy-MM-dd
  939 + * @param endDate 结束日期 yyyy-MM-dd
  940 + */
  941 + public Map<String, Object> queryLampStatistics(String startDate, String endDate) {
  942 + log.info("========== [智能灯统计查询] startDate={}, endDate={} ==========", startDate, endDate);
  943 +
  944 + // 1. 根据前端传入的起止日期构建日期范围
  945 + List<String> dateList = buildDayList(startDate, endDate);
  946 + if (dateList.isEmpty()) {
  947 + return buildEmptyLampStats();
  948 + }
  949 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
  950 + String todayStr = sdf.format(new Date());
  951 + boolean includeToday = dateList.contains(todayStr);
  952 + log.info("日期范围共 {} 天, 包含今日({}): {}", dateList.size(), todayStr, includeToday);
  953 +
  954 + // 2. 获取所有设备及名称
  955 + Map<String, String> deviceNameMap = queryAllDtuSnWithName();
  956 + List<String> allDtuSns = new ArrayList<>(deviceNameMap.keySet());
  957 + if (allDtuSns.isEmpty()) {
  958 + return buildEmptyLampStats();
  959 + }
  960 +
  961 + // 3. 批量从OEE表查询数据
  962 + List<OeeRecord> allRecords = queryOeeBatch(allDtuSns, dateList, includeToday ? todayStr : null);
  963 +
  964 + // 4. 补充今天的实时数据
  965 + if (includeToday) {
  966 + supplementTodayData(allDtuSns, todayStr, allRecords);
  967 + }
  968 +
  969 + // 5. 按 dtuSn 分组
  970 + Map<String, List<OeeRecord>> deviceMap = new LinkedHashMap<>();
  971 + for (OeeRecord r : allRecords) {
  972 + deviceMap.computeIfAbsent(r.dtuSn, k -> new ArrayList<>()).add(r);
  973 + }
  974 + for (String sn : allDtuSns) {
  975 + if (!deviceMap.containsKey(sn)) {
  976 + deviceMap.put(sn, new ArrayList<>());
  977 + }
  978 + }
  979 +
  980 + // 6. 计算各项统计数据
  981 + return buildLampStatisticsResult(allDtuSns, deviceMap, deviceNameMap, dateList);
  982 + }
  983 +
  984 + /** 构建空结果 */
  985 + private Map<String, Object> buildEmptyLampStats() {
  986 + return Map.of(
  987 + "totalDuration", Map.of(
  988 + "off", Map.of("duration", "0时0分0秒", "seconds", 0),
  989 + "red", Map.of("duration", "0时0分0秒", "seconds", 0),
  990 + "yellow", Map.of("duration", "0时0分0秒", "seconds", 0),
  991 + "green", Map.of("duration", "0时0分0秒", "seconds", 0),
  992 + "blue", Map.of("duration", "0时0分0秒", "seconds", 0)
  993 + ),
  994 + "availabilityRate", "0.00%",
  995 + "currentStatus", Map.of(
  996 + "off", 0, "red", 0, "yellow", 0, "green", 0, "blue", 0
  997 + ),
  998 + "abnormalRanking", List.of(),
  999 + "deviceList", List.of()
  1000 + );
  1001 + }
  1002 +
  1003 + /** 构建智能灯统计结果 */
  1004 + private Map<String, Object> buildLampStatisticsResult(List<String> allDtuSns,
  1005 + Map<String, List<OeeRecord>> deviceMap,
  1006 + Map<String, String> deviceNameMap,
  1007 + List<String> dateList) {
  1008 + // ---- ① 总时长:汇总所有设备各状态时长 ----
  1009 + long totalOff = 0, totalRed = 0, totalYellow = 0, totalGreen = 0, totalBlue = 0;
  1010 +
  1011 + // ---- ② 设备维度统计:用于异常排名和每设备详情 ----
  1012 + List<Map<String, Object>> deviceStatList = new ArrayList<>();
  1013 +
  1014 + for (String dtuSn : allDtuSns) {
  1015 + List<OeeRecord> dayRecords = deviceMap.getOrDefault(dtuSn, Collections.emptyList());
  1016 +
  1017 + long devOff = 0, devRed = 0, devYellow = 0, devGreen = 0, devBlue = 0;
  1018 + long devTotalDur = 0, devGreenDur = 0;
  1019 +
  1020 + for (OeeRecord rec : dayRecords) {
  1021 + if (rec.lampData != null) {
  1022 + for (int i = 0; i < rec.lampData.size(); i++) {
  1023 + JSONObject lamp = rec.lampData.getJSONObject(i);
  1024 + if (lamp == null) continue;
  1025 + int state = lamp.getIntValue("lampState");
  1026 + long dur = lamp.getLongValue("duration");
  1027 + switch (state) {
  1028 + case 0 -> devOff += dur;
  1029 + case 1 -> devRed += dur;
  1030 + case 2 -> devYellow += dur;
  1031 + case 3 -> { devGreen += dur; devGreenDur += dur; }
  1032 + case 4 -> devBlue += dur;
  1033 + }
  1034 + devTotalDur += dur;
  1035 + }
  1036 + }
  1037 + }
  1038 +
  1039 + // 稼动率 = 绿 / (红 + 黄 + 绿) * 100%
  1040 + long devRygDur = devRed + devYellow + devGreen;
  1041 + double rate = (devRygDur > 0) ? Math.round(devGreenDur * 10000.0 / devRygDur) / 100.0 : 0.0;
  1042 + long abnormalDur = devRed + devYellow; // 异常时长 = 红+黄
  1043 +
  1044 + Map<String, Object> devStat = new LinkedHashMap<>();
  1045 + devStat.put("dtuSn", dtuSn);
  1046 + devStat.put("deviceName", deviceNameMap.getOrDefault(dtuSn, ""));
  1047 + devStat.put("offDuration", formatDuration(devOff));
  1048 + devStat.put("offSeconds", devOff);
  1049 + devStat.put("redDuration", formatDuration(devRed));
  1050 + devStat.put("redSeconds", devRed);
  1051 + devStat.put("yellowDuration", formatDuration(devYellow));
  1052 + devStat.put("yellowSeconds", devYellow);
  1053 + devStat.put("greenDuration", formatDuration(devGreen));
  1054 + devStat.put("greenSeconds", devGreen);
  1055 + devStat.put("blueDuration", formatDuration(devBlue));
  1056 + devStat.put("blueSeconds", devBlue);
  1057 + devStat.put("availabilityRatio", String.format("%.2f%%", rate));
  1058 + devStat.put("rygTotalDuration", formatDuration(devRygDur)); // 红+黄+绿 总时长
  1059 + devStat.put("abnormalDuration", abnormalDur); // 用于排序
  1060 +
  1061 + deviceStatList.add(devStat);
  1062 +
  1063 + // 累加到全局总计
  1064 + totalOff += devOff;
  1065 + totalRed += devRed;
  1066 + totalYellow += devYellow;
  1067 + totalGreen += devGreen;
  1068 + totalBlue += devBlue;
  1069 + }
  1070 +
  1071 + // ---- ③ 稼动率 = 绿 / (红 + 黄 + 绿) ----
  1072 + long totalRygDur = totalRed + totalYellow + totalGreen;
  1073 + double overallRate = (totalRygDur > 0) ? Math.round(totalGreen * 10000.0 / totalRygDur) / 100.0 : 0.0;
  1074 +
  1075 + // ---- ④ 当前机台运行状态(从设备表实时查) ----
  1076 + Map<String, Integer> currentStatus = queryCurrentDeviceStatus();
  1077 +
  1078 + // ---- ⑤ 异常排行榜(按红+黄时长降序) ----
  1079 + deviceStatList.sort((a, b) -> Long.compare(
  1080 + ((Number) b.get("abnormalDuration")).longValue(),
  1081 + ((Number) a.get("abnormalDuration")).longValue()
  1082 + ));
  1083 + // 排序后移除辅助字段
  1084 + for (Map<String, Object> d : deviceStatList) {
  1085 + d.remove("abnormalDuration");
  1086 + }
  1087 +
  1088 + return Map.of(
  1089 + "totalDuration", Map.of(
  1090 + "off", Map.of("duration", formatDuration(totalOff), "seconds", totalOff),
  1091 + "red", Map.of("duration", formatDuration(totalRed), "seconds", totalRed),
  1092 + "yellow", Map.of("duration", formatDuration(totalYellow), "seconds", totalYellow),
  1093 + "green", Map.of("duration", formatDuration(totalGreen), "seconds", totalGreen),
  1094 + "blue", Map.of("duration", formatDuration(totalBlue), "seconds", totalBlue)
  1095 + ),
  1096 + "availabilityRate", String.format("%.2f%%", overallRate),
  1097 + "rygTotalDuration", formatDuration(totalRygDur), // 红黄绿总时长
  1098 + "currentStatus", currentStatus,
  1099 + "abnormalRanking", deviceStatList,
  1100 + "deviceList", deviceStatList
  1101 + );
  1102 + }
  1103 +
  1104 + /** 查询当前设备的运行状态分布(从设备表) */
  1105 + private Map<String, Integer> queryCurrentDeviceStatus() {
  1106 + String sql = "SELECT lampState, COUNT(*) as cnt FROM " + deviceTableName +
  1107 + " WHERE corp_code = ? GROUP BY lampState";
  1108 + List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, deviceCorpCode);
  1109 +
  1110 + int off = 0, red = 0, yellow = 0, green = 0, blue = 0;
  1111 + for (Map<String, Object> row : rows) {
  1112 + String state = String.valueOf(row.get("lampState"));
  1113 + int cnt = ((Number) row.get("cnt")).intValue();
  1114 + switch (state) {
  1115 + case "0" -> off = cnt;
  1116 + case "1" -> red = cnt;
  1117 + case "2" -> yellow = cnt;
  1118 + case "3" -> green = cnt;
  1119 + case "4" -> blue = cnt;
  1120 + }
  1121 + }
  1122 + return Map.of("off", off, "red", red, "yellow", yellow, "green", green, "blue", blue);
  1123 + }
931 1124 }
... ...