From 0541c0791acea41b584f71fc59e22d4d21ba0883 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 15 四月 2026 11:10:46 +0800
Subject: [PATCH] ``` refactor(energy): 优化实时能耗数据服务实现

---
 src/main/java/com/ruoyi/http/service/impl/RealTimeEnergyConsumptionServiceImpl.java |  439 ++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 387 insertions(+), 52 deletions(-)

diff --git a/src/main/java/com/ruoyi/http/service/impl/RealTimeEnergyConsumptionServiceImpl.java b/src/main/java/com/ruoyi/http/service/impl/RealTimeEnergyConsumptionServiceImpl.java
index 6b10ba2..c529eb0 100644
--- a/src/main/java/com/ruoyi/http/service/impl/RealTimeEnergyConsumptionServiceImpl.java
+++ b/src/main/java/com/ruoyi/http/service/impl/RealTimeEnergyConsumptionServiceImpl.java
@@ -3,15 +3,22 @@
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.http.HttpUtils;
 import com.ruoyi.http.service.RealTimeEnergyConsumptionService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @author :yys
@@ -21,110 +28,438 @@
 @Slf4j
 public class RealTimeEnergyConsumptionServiceImpl implements RealTimeEnergyConsumptionService {
 
+    private static final long REMOTE_CACHE_TTL_SECONDS_30 = 30L;
+
     private static final String URL = "https://new.e-elitech.cn/api/data-api";
 
     private static final String TOKEN_URL = "/elitechAccess/getToken";
 
-    private static final String REAL_TIME_URL = "/elitechAccess/v2/getRealTimeData"; //鑾峰彇璁惧瀹炴椂鏁版嵁
+    private static final String REAL_TIME_URL = "/elitechAccess/v2/getRealTimeData";
+
+    private static final String REAL_HISTORY_URL = URL + "/elitechAccess/v2/getHistoryData";
 
     private static final String KET_ID = "75804708";
 
     private static final String KEY_SECRET = "xTUGToozKpYgUPqTsZzB";
 
-    private static final String USER_NAME = "鐢ㄦ埛30773662";
+    private static final String USER_NAME = "\u7528\u623730773662";
 
     private static final String PASS_WORD = "y17775163675";
 
-    private static final String DEVICE_GUID = "90444196515214284663";
+    private static final String REAL_TIME_CACHE_PREFIX = "JCLY:REAL_TIME:";
 
+    private static final String HISTORY_CACHE_PREFIX = "JCLY:HISTORY:";
 
-    /**
-     * 鏍规嵁paramCode鎻愬彇鎺㈠ご鍙傛暟
-     * @param paramList 璁惧鍙傛暟鏁扮粍
-     * @param targetCode 鐩爣鎺㈠ご缂栫爜
-     * @return 鎺㈠ご鍙傛暟瀵硅薄锛堝寘鍚玭ame/value/unit锛�
-     */
+    private static final String TOKEN_CACHE_KEY = "JCLY_TOKEN:";
+
+    private static final String STATUS_KEY = "status";
+
+    private static final String STATUS_MESSAGE_KEY = "statusMessage";
+
+    private static final String STATUS_ONLINE = "鍦ㄧ嚎";
+
+    private static final String STATUS_OFFLINE = "offline";
+
+    private static final String STATUS_ERROR = "error";
+
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
     private static JSONObject getProbeParam(JSONArray paramList, String targetCode) {
+        if (paramList == null) {
+            return new JSONObject();
+        }
         for (int i = 0; i < paramList.size(); i++) {
             JSONObject paramObj = paramList.getJSONObject(i);
             if (targetCode.equals(paramObj.getString("paramCode"))) {
                 return paramObj;
             }
         }
-        return new JSONObject(); // 鏈尮閰嶅埌杩斿洖绌哄璞★紝閬垮厤绌烘寚閽�
+        return new JSONObject();
     }
 
-    /**
-     * 瀹炴椂鑾峰彇娓╂箍搴︼紝浜屾哀鍖栫⒊鏁版嵁
-     */
-    public static void main(String[] args) {
-        String realTimeData = getRealTimeData(getToken());
-        Map<String, Object> map = JSON.parseObject(realTimeData, Map.class);
-        if(map.get("code").equals(0)){
-            // 1. 瑙f瀽澶栧眰data涓篔SON鏁扮粍锛堟帴鍙h繑鍥炵殑璁惧鍒楄〃锛�
-            JSONArray deviceList = JSON.parseArray(map.get("data").toString());
-            // 2. 閬嶅巻璁惧鍒楄〃锛堟澶勪粎鍙栫涓�涓澶囷紝鑻ユ湁澶氫釜璁惧鍙惊鐜鐞嗭級
-            if (!deviceList.isEmpty()) {
-                JSONObject deviceObj = deviceList.getJSONObject(0);
-                // 3. 瑙f瀽璁惧鍐呯殑鍙傛暟鏁扮粍锛堟墍鏈塸aramCode瀵瑰簲鐨勫弬鏁帮級
-                JSONArray paramList = deviceObj.getJSONArray("data");
+    public List<Map<String, String>> getHistoryData(String guid, long startTime, long endTime) {
+        List<Map<String, String>> resultList = new ArrayList<>();
+        String token = getToken();
+        try {
+            String historyData = requestHistoryData(token, guid, startTime, endTime);
+            JSONObject resultObj = JSON.parseObject(historyData);
+            if (resultObj == null) {
+                resultList.add(buildStatusItem(guid, STATUS_ERROR, "history response is empty"));
+                return resultList;
+            }
 
-                // 4. 瀹氫箟鐩爣鎺㈠ご鐨刾aramCode锛屾寜闇�鎵╁睍
-                String[] targetCodes = {"0100", "0110", "0120", "0130"};
-                for (String code : targetCodes) {
-                    // 5. 閬嶅巻鍙傛暟鏁扮粍锛屽尮閰嶇洰鏍噋aramCode
-                    for (int i = 0; i < paramList.size(); i++) {
-                        JSONObject paramObj = paramList.getJSONObject(i);
-                        String currentCode = paramObj.getString("paramCode");
-                        if (code.equals(currentCode)) {
-                            // 6. 鎻愬彇鏍稿績瀛楁锛堝�笺�佸崟浣嶃�佹帰澶村悕绉帮級
-                            String paramName = paramObj.getString("paramName"); // 鎺㈠ご1/鎺㈠ご2...
-                            String value = paramObj.getString("value");       // 鏁板�硷紙345.80/24.90...锛�
-                            String unitCode = paramObj.getString("unitCode"); // 鍗曚綅锛圠ux/鈩�/%RH/ppm锛�
+            Integer code = resultObj.getInteger("code");
+            if (!Integer.valueOf(0).equals(code)) {
+                resultList.add(buildStatusItem(guid, resolveStatusByCodeOrMessage(code, resultObj.getString("msg")), resultObj.getString("msg")));
+                return resultList;
+            }
 
-                            // 7. 涓氬姟澶勭悊锛氭墦鍗�/璧嬪��/瀛樺偍绛夛紙鎸夐渶淇敼锛�
-                            System.out.println(paramName + "锛�" + value + " " + unitCode);
-                            // 鍖归厤鍒板悗鐩存帴璺冲嚭鍐呭眰寰幆锛屾彁鍗囨晥鐜�
-                            break;
+            JSONArray historyList = resultObj.getJSONArray("data");
+            if (historyList == null || historyList.isEmpty()) {
+                resultList.add(buildStatusItem(guid, STATUS_OFFLINE, "no history data"));
+                return resultList;
+            }
+
+            for (int i = 0; i < historyList.size(); i++) {
+                JSONObject historyObj = historyList.getJSONObject(i);
+                Map<String, String> historyItem = new HashMap<>();
+                historyItem.put("guid", firstNonBlank(
+                        historyObj.getString("deviceGuid"),
+                        historyObj.getString("guid"),
+                        guid
+                ));
+                historyItem.put("subUId", stringValue(historyObj.get("subUid")));
+                historyItem.put("monitorTimeStamp", stringValue(historyObj.get("monitorTimeStamp")));
+                historyItem.put("monitorTimeStr", historyObj.getString("monitorTimeStr"));
+                historyItem.put("position", historyObj.getString("position"));
+                historyItem.put("address", historyObj.getString("address"));
+                historyItem.put(STATUS_KEY, resolveStatusByAlarmState(historyObj.get("alarmState"), STATUS_ONLINE));
+
+                JSONArray paramList = historyObj.getJSONArray("data");
+                if (paramList != null && !paramList.isEmpty()) {
+                    for (int j = 0; j < paramList.size(); j++) {
+                        JSONObject paramObj = paramList.getJSONObject(j);
+                        String paramCode = paramObj.getString("paramCode");
+                        String value = paramObj.getString("value");
+                        String unitCode = paramObj.getString("unitCode");
+                        String fullValue = concatValueWithUnit(value, unitCode);
+                        if ("0100".equals(paramCode)) {
+                            historyItem.put("light", fullValue);
+                        } else if ("0110".equals(paramCode)) {
+                            historyItem.put("temperature", fullValue);
+                        } else if ("0120".equals(paramCode)) {
+                            historyItem.put("humidity", fullValue);
+                        } else if ("0130".equals(paramCode)) {
+                            historyItem.put("co2", fullValue);
+                        } else if ("0042".equals(paramCode)) {
+                            historyItem.put("battery", fullValue);
+                        } else if (paramCode != null) {
+                            historyItem.put(paramCode, fullValue);
                         }
                     }
                 }
+                resultList.add(historyItem);
             }
+            return resultList;
+        } catch (Exception ex) {
+            log.error("history data parse/request failed, guid={}", guid, ex);
+            resultList.add(buildStatusItem(guid, STATUS_ERROR, ex.getMessage()));
+            return resultList;
         }
+    }
+
+    public List<Map<String, String>> getRealData(List<String> guidList) {
+        log.info("start get real data");
+        List<Map<String, String>> listMaps = new ArrayList<>();
+        if (guidList == null || guidList.isEmpty()) {
+            return listMaps;
+        }
+
+        String token = getToken();
+        try {
+            String realTimeData = getRealTimeData(token, guidList);
+            JSONObject batchResp = JSON.parseObject(realTimeData);
+            Integer code = batchResp == null ? null : batchResp.getInteger("code");
+            if (Integer.valueOf(0).equals(code)) {
+                parseRealDataResponse(batchResp, guidList, listMaps);
+                return listMaps;
+            }
+            log.warn("batch getRealData failed, fallback one by one. code={}, msg={}", code, batchResp == null ? null : batchResp.getString("msg"));
+        } catch (Exception ex) {
+            log.error("batch getRealData exception, fallback one by one", ex);
+        }
+
+        for (String guid : guidList) {
+            listMaps.add(fetchSingleDeviceRealData(token, guid));
+        }
+        return listMaps;
+    }
+
+    public static void main(String[] args) {
         System.out.println();
     }
 
-    public static String getToken(){
+    public String getToken() {
+        String cachedToken = sanitizeToken(redisTemplate.opsForValue().get(TOKEN_CACHE_KEY));
+        if (cachedToken != null) {
+            return cachedToken;
+        }
         Map<String, String> param = new HashMap<>();
         param.put("keyId", KET_ID);
         param.put("keySecret", KEY_SECRET);
         param.put("userName", USER_NAME);
         param.put("password", PASS_WORD);
-        log.info("璇锋眰鍙傛暟锛歿}", JSON.toJSONString( param));
+        log.info("request token payload: {}", JSON.toJSONString(param));
         String result = HttpUtils.sendPostJson(URL + TOKEN_URL, JSON.toJSONString(param));
-        log.info("杩斿洖缁撴灉锛歿}", result);
+        log.info("request token response: {}", result);
         Map<String, Object> map = JSON.parseObject(result, Map.class);
-        if (map.get("code").equals(0)) {
+        if (Integer.valueOf(0).equals(map.get("code"))) {
             Object token = map.get("data");
             log.info("token:{}", token);
+            redisTemplate.opsForValue().set(TOKEN_CACHE_KEY, token.toString(), 60 * 60 * 12);
             return token.toString();
+        }
+        log.error("get token failed, response={}", result);
+        return null;
+    }
+
+    private String sanitizeToken(String token) {
+        if (token == null) {
+            return null;
+        }
+        String cleanedToken = token.replace("\0", "").trim();
+        return cleanedToken.isEmpty() ? null : cleanedToken;
+    }
+
+    private String firstNonBlank(String... values) {
+        for (String value : values) {
+            if (value != null && !value.trim().isEmpty()) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    private String stringValue(Object value) {
+        return value == null ? null : String.valueOf(value);
+    }
+
+    private String refreshToken() {
+        redisTemplate.delete(TOKEN_CACHE_KEY);
+        return getToken();
+    }
+
+    private boolean isUnauthorizedException(Exception ex) {
+        if (ex == null || ex.getMessage() == null) {
+            return false;
+        }
+        String msg = ex.getMessage().toLowerCase();
+        return msg.contains("401") || msg.contains("unauthorized");
+    }
+
+    private boolean isUnauthorizedResponse(String result) {
+        if (result == null || result.trim().isEmpty()) {
+            return false;
+        }
+        try {
+            JSONObject obj = JSON.parseObject(result);
+            if (obj == null) {
+                return false;
+            }
+            Integer code = obj.getInteger("code");
+            if (code != null && (code == 401 || code == 403)) {
+                return true;
+            }
+            String msg = obj.getString("msg");
+            return msg != null && msg.toLowerCase().contains("unauthorized");
+        } catch (Exception ignore) {
+            return result.contains("401");
+        }
+    }
+
+    private String requestWithTokenRetry(String url, String payload, String token, String scene) {
+        String usedToken = sanitizeToken(token);
+        if (usedToken == null) {
+            usedToken = refreshToken();
+        }
+        try {
+            String result = HttpUtils.sendPostJson(url, payload, usedToken);
+            if (!isUnauthorizedResponse(result) && StringUtils.isNotEmpty(result)) {
+                return result;
+            }
+            log.warn("{} got unauthorized response, refresh token and retry once", scene);
+        } catch (Exception ex) {
+            if (!isUnauthorizedException(ex)) {
+                throw ex;
+            }
+            log.warn("{} got 401 exception, refresh token and retry once", scene);
+        }
+
+        String newToken = refreshToken();
+        if (newToken == null) {
+            throw new RuntimeException("refresh token failed");
+        }
+        return HttpUtils.sendPostJson(url, payload, newToken);
+    }
+
+    private String concatValueWithUnit(String value, String unitCode) {
+        if (value == null) {
+            return null;
+        }
+        return value + (unitCode == null ? "" : unitCode);
+    }
+
+    private Map<String, String> buildStatusItem(String guid, String status, String statusMessage) {
+        Map<String, String> result = new HashMap<>();
+        result.put("guid", guid);
+        result.put(STATUS_KEY, status);
+        if (statusMessage != null && !statusMessage.trim().isEmpty()) {
+            result.put(STATUS_MESSAGE_KEY, statusMessage);
         }
         return result;
     }
 
-    public static String getRealTimeData(String  token){
+    private String resolveStatusByCodeOrMessage(Integer code, String message) {
+        if (code != null && (code == 5 || code == 1003 || code == 1004)) {
+            return STATUS_OFFLINE;
+        }
+        if (message == null) {
+            return STATUS_ERROR;
+        }
+        String lowered = message.toLowerCase();
+        if (lowered.contains("offline") || message.contains("绂荤嚎")) {
+            return STATUS_OFFLINE;
+        }
+        return STATUS_ERROR;
+    }
+
+    private void parseRealDataResponse(JSONObject responseObj, List<String> requestedGuids, List<Map<String, String>> output) {
+        JSONArray deviceList = responseObj.getJSONArray("data");
+        Set<String> returnedGuids = new HashSet<>();
+        if (deviceList != null) {
+            for (int deviceIndex = 0; deviceIndex < deviceList.size(); deviceIndex++) {
+                JSONObject deviceObj = deviceList.getJSONObject(deviceIndex);
+                Map<String, String> deviceData = parseSingleDevice(deviceObj, null);
+                String guid = deviceData.get("guid");
+                if (guid != null) {
+                    returnedGuids.add(guid);
+                }
+                output.add(deviceData);
+            }
+        }
+
+        for (String requestGuid : requestedGuids) {
+            if (!returnedGuids.contains(requestGuid)) {
+                output.add(buildStatusItem(requestGuid, STATUS_OFFLINE, "device not returned by remote API"));
+            }
+        }
+    }
+
+    private Map<String, String> fetchSingleDeviceRealData(String token, String guid) {
+        try {
+            String singleResult = getRealTimeData(token, Collections.singletonList(guid));
+            JSONObject singleObj = JSON.parseObject(singleResult);
+            if (singleObj == null) {
+                return buildStatusItem(guid, STATUS_ERROR, "single device response is empty");
+            }
+            Integer code = singleObj.getInteger("code");
+            if (!Integer.valueOf(0).equals(code)) {
+                return buildStatusItem(guid, resolveStatusByCodeOrMessage(code, singleObj.getString("msg")), singleObj.getString("msg"));
+            }
+
+            JSONArray dataList = singleObj.getJSONArray("data");
+            if (dataList == null || dataList.isEmpty()) {
+                return buildStatusItem(guid, STATUS_OFFLINE, "single device response has no data");
+            }
+            return parseSingleDevice(dataList.getJSONObject(0), guid);
+        } catch (Exception ex) {
+            log.error("single getRealData failed, guid={}", guid, ex);
+            return buildStatusItem(guid, STATUS_ERROR, ex.getMessage());
+        }
+    }
+
+    private Map<String, String> parseSingleDevice(JSONObject deviceObj, String fallbackGuid) {
+        Map<String, String> deviceData = new HashMap<>();
+        String deviceGuid = firstNonBlank(
+                deviceObj.getString("deviceGuid"),
+                deviceObj.getString("guid"),
+                deviceObj.getString("devGuid"),
+                deviceObj.getString("sn"),
+                fallbackGuid
+        );
+        if (deviceGuid != null) {
+            deviceData.put("guid", deviceGuid);
+        }
+
+        JSONArray paramList = deviceObj.getJSONArray("data");
+        if (paramList == null || paramList.isEmpty()) {
+            deviceData.put(STATUS_KEY, STATUS_OFFLINE);
+            deviceData.put(STATUS_MESSAGE_KEY, "device data is empty");
+            return deviceData;
+        }
+
+        deviceData.put(STATUS_KEY, resolveStatusByAlarmState(deviceObj.get("alarmState"), STATUS_ONLINE));
+        for (int i = 0; i < paramList.size(); i++) {
+            JSONObject paramObj = paramList.getJSONObject(i);
+            String code = paramObj.getString("paramCode");
+            String value = paramObj.getString("value");
+            String unitCode = paramObj.getString("unitCode");
+            String fullValue = concatValueWithUnit(value, unitCode);
+            if ("0100".equals(code)) {
+                deviceData.put("light", fullValue);
+            } else if ("0110".equals(code)) {
+                deviceData.put("temperature", fullValue);
+            } else if ("0120".equals(code)) {
+                deviceData.put("humidity", fullValue);
+            } else if ("0130".equals(code)) {
+                deviceData.put("co2", fullValue);
+            } else if ("0042".equals(code)) {
+                deviceData.put("battery", fullValue);
+            }
+        }
+        return deviceData;
+    }
+
+    private String resolveStatusByAlarmState(Object alarmState, String defaultStatus) {
+        if (alarmState == null) {
+            return defaultStatus;
+        }
+        if (alarmState instanceof Boolean) {
+            return (Boolean) alarmState ? STATUS_OFFLINE : STATUS_ONLINE;
+        }
+        String normalized = String.valueOf(alarmState).trim().toLowerCase();
+        if ("true".equals(normalized) || "1".equals(normalized)) {
+            return STATUS_OFFLINE;
+        }
+        if ("false".equals(normalized) || "0".equals(normalized)) {
+            return STATUS_ONLINE;
+        }
+        return defaultStatus;
+    }
+
+    public String getRealTimeData(String token, List<String> guidList) {
         Map<String, Object> param = new HashMap<>();
         param.put("keyId", KET_ID);
         param.put("keySecret", KEY_SECRET);
-        param.put("deviceGuids", Collections.singletonList(DEVICE_GUID));
-        log.info("璇锋眰鍙傛暟锛歿}", JSON.toJSONString( param));
-        String result = HttpUtils.sendPostJson(URL + REAL_TIME_URL, JSON.toJSONString(param),token);
-        log.info("杩斿洖缁撴灉锛歿}", result);
+        param.put("deviceGuids", guidList);
+        log.info("request realtime payload: {}", JSON.toJSONString(param));
+        String cacheKey = REAL_TIME_CACHE_PREFIX + JSON.toJSONString(param);
+        String cachedResult = sanitizeToken(redisTemplate.opsForValue().get(cacheKey));
+        if (cachedResult != null) {
+            log.info("hit realtime cache: {}", cacheKey);
+            return cachedResult;
+        }
+        String result = requestWithTokenRetry(URL + REAL_TIME_URL, JSON.toJSONString(param), token, "getRealTimeData");
+        log.info("request realtime response: {}", result);
+        cacheRemoteResponse(cacheKey, result);
         return result;
     }
 
+    public String requestHistoryData(String token, String guid, long startTime, long endTime) {
+        Map<String, Object> param = new HashMap<>();
+        param.put("keyId", KET_ID);
+        param.put("keySecret", KEY_SECRET);
+        param.put("deviceGuid", guid);
+        param.put("startTime", startTime);
+        param.put("endTime", endTime);
+        log.info("request history payload: {}", JSON.toJSONString(param));
+        String cacheKey = HISTORY_CACHE_PREFIX + JSON.toJSONString(param);
+        String cachedResult = sanitizeToken(redisTemplate.opsForValue().get(cacheKey));
+        if (cachedResult != null) {
+            log.info("hit history cache: {}", cacheKey);
+            return cachedResult;
+        }
+        String result = requestWithTokenRetry(REAL_HISTORY_URL, JSON.toJSONString(param), token, "getHistoryData");
+        log.info("request history response: {}", result);
+        cacheRemoteResponse(cacheKey, result);
+        return result;
+    }
 
-
-
-
+    private void cacheRemoteResponse(String cacheKey, String result) {
+        if (result == null || result.trim().isEmpty()) {
+            return;
+        }
+        redisTemplate.opsForValue().set(cacheKey, result, REMOTE_CACHE_TTL_SECONDS_30, TimeUnit.SECONDS);
+    }
 }

--
Gitblit v1.9.3