| | |
| | | import org.springframework.data.redis.core.RedisTemplate; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.time.Instant; |
| | | import java.time.temporal.ChronoUnit; |
| | | 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; |
| | | |
| | | /** |
| | |
| | | @Slf4j |
| | | public class RealTimeEnergyConsumptionServiceImpl implements RealTimeEnergyConsumptionService { |
| | | |
| | | private static final long REMOTE_CACHE_TTL_SECONDS = 10L; // 缓存TTL,10秒 |
| | | |
| | | private static final long REMOTE_CACHE_TTL_SECONDS_30 = 30L; // 缓存TTL,30秒 |
| | | 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 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 REAL_TIME_CACHE_PREFIX = "JCLY:REAL_TIME:"; |
| | | |
| | | private static final String HISTORY_CACHE_PREFIX = "JCLY:HISTORY:"; |
| | | |
| | | private static final String STATUS_KEY = "status"; |
| | | |
| | | private static final String STATUS_MESSAGE_KEY = "statusMessage"; |
| | | |
| | | private static final String STATUS_OK = "ok"; |
| | | |
| | | private static final String STATUS_OFFLINE = "offline"; |
| | | |
| | | private static final String STATUS_ERROR = "error"; |
| | | |
| | | @Autowired |
| | | private RedisTemplate<String, String> redisTemplate; |
| | |
| | | |
| | | public List<Map<String, String>> getHistoryData(String guid, long startTime, long endTime) { |
| | | List<Map<String, String>> resultList = new ArrayList<>(); |
| | | String historyData = requestHistoryData(getToken(), guid, startTime, endTime); |
| | | Map<String, Object> resultMap = JSON.parseObject(historyData, Map.class); |
| | | if (!Integer.valueOf(0).equals(resultMap.get("code"))) { |
| | | 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; |
| | | } |
| | | |
| | | JSONArray historyList = JSON.parseArray(String.valueOf(resultMap.get("data"))); |
| | | 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; |
| | | } |
| | | |
| | | JSONArray historyList = resultObj.getJSONArray("data"); |
| | | if (historyList == null || historyList.isEmpty()) { |
| | | resultList.add(buildStatusItem(guid, STATUS_OFFLINE, "no history data")); |
| | | return resultList; |
| | | } |
| | | |
| | |
| | | historyItem.put("monitorTimeStr", historyObj.getString("monitorTimeStr")); |
| | | historyItem.put("position", historyObj.getString("position")); |
| | | historyItem.put("address", historyObj.getString("address")); |
| | | historyItem.put(STATUS_KEY, STATUS_OK); |
| | | |
| | | JSONArray paramList = historyObj.getJSONArray("data"); |
| | | if (paramList != null && !paramList.isEmpty()) { |
| | | for (int j = 0; j < paramList.size(); j++) { |
| | | JSONObject paramObj = paramList.getJSONObject(j); |
| | | String paramName = paramObj.getString("paramName"); |
| | | String paramCode = paramObj.getString("paramCode"); |
| | | String value = paramObj.getString("value"); |
| | | String unitCode = paramObj.getString("unitCode"); |
| | | String fullValue = value == null ? null : value + (unitCode == null ? "" : unitCode); |
| | | switch (paramName) { |
| | | case "探头1": |
| | | String fullValue = concatValueWithUnit(value, unitCode); |
| | | if ("0100".equals(paramCode)) { |
| | | historyItem.put("light", fullValue); |
| | | break; |
| | | case "探头2": |
| | | } else if ("0110".equals(paramCode)) { |
| | | historyItem.put("temperature", fullValue); |
| | | break; |
| | | case "探头3": |
| | | } else if ("0120".equals(paramCode)) { |
| | | historyItem.put("humidity", fullValue); |
| | | break; |
| | | case "探头4": |
| | | } else if ("0130".equals(paramCode)) { |
| | | historyItem.put("co2", fullValue); |
| | | break; |
| | | default: |
| | | if (paramCode != null) { |
| | | } else if (paramCode != null) { |
| | | historyItem.put(paramCode, fullValue); |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | 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("开始获取实时数据"); |
| | | log.info("start get real data"); |
| | | List<Map<String, String>> listMaps = new ArrayList<>(); |
| | | String realTimeData = getRealTimeData(getToken(), guidList); |
| | | Map<String, Object> map = JSON.parseObject(realTimeData, Map.class); |
| | | if (!Integer.valueOf(0).equals(map.get("code"))) { |
| | | if (guidList == null || guidList.isEmpty()) { |
| | | return listMaps; |
| | | } |
| | | |
| | | JSONArray deviceList = JSON.parseArray(String.valueOf(map.get("data"))); |
| | | if (deviceList == null || deviceList.isEmpty()) { |
| | | 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); |
| | | } |
| | | |
| | | String[] targetCodes = {"0100", "0110", "0120", "0130"}; |
| | | for (int deviceIndex = 0; deviceIndex < deviceList.size(); deviceIndex++) { |
| | | JSONObject deviceObj = deviceList.getJSONObject(deviceIndex); |
| | | JSONArray paramList = deviceObj.getJSONArray("data"); |
| | | Map<String, String> deviceData = new HashMap<>(); |
| | | String deviceGuid = firstNonBlank( |
| | | deviceObj.getString("deviceGuid"), |
| | | deviceObj.getString("guid"), |
| | | deviceObj.getString("devGuid"), |
| | | deviceObj.getString("sn") |
| | | ); |
| | | if (deviceGuid != null) { |
| | | deviceData.put("guid", deviceGuid); |
| | | } |
| | | for (String code : targetCodes) { |
| | | JSONObject paramObj = getProbeParam(paramList, code); |
| | | if (paramObj.isEmpty()) { |
| | | continue; |
| | | } |
| | | String paramName = paramObj.getString("paramName"); |
| | | String value = paramObj.getString("value"); |
| | | String unitCode = paramObj.getString("unitCode"); |
| | | log.info("{}: {} {}", paramName, value, unitCode); |
| | | switch (paramName) { |
| | | case "探头1": |
| | | deviceData.put("light", value + unitCode); |
| | | break; |
| | | case "探头2": |
| | | deviceData.put("temperature", value + unitCode); |
| | | break; |
| | | case "探头3": |
| | | deviceData.put("humidity", value + unitCode); |
| | | break; |
| | | case "探头4": |
| | | deviceData.put("co2", value + unitCode); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | listMaps.add(deviceData); |
| | | for (String guid : guidList) { |
| | | listMaps.add(fetchSingleDeviceRealData(token, guid)); |
| | | } |
| | | return listMaps; |
| | | } |
| | |
| | | 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 (Integer.valueOf(0).equals(map.get("code"))) { |
| | | Object token = map.get("data"); |
| | |
| | | return value == null ? null : String.valueOf(value); |
| | | } |
| | | |
| | | 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; |
| | | } |
| | | |
| | | 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) { |
| | | String[] targetCodes = {"0100", "0110", "0120", "0130"}; |
| | | 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, STATUS_OK); |
| | | for (String code : targetCodes) { |
| | | JSONObject paramObj = getProbeParam(paramList, code); |
| | | if (paramObj.isEmpty()) { |
| | | continue; |
| | | } |
| | | 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); |
| | | } |
| | | } |
| | | return deviceData; |
| | | } |
| | | |
| | | 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", guidList); |
| | | log.info("请求参数:{}", JSON.toJSONString(param)); |
| | | 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("命中实时数据缓存:{}", cacheKey); |
| | | log.info("hit realtime cache: {}", cacheKey); |
| | | return cachedResult; |
| | | } |
| | | String result = HttpUtils.sendPostJson(URL + REAL_TIME_URL, JSON.toJSONString(param), token); |
| | | log.info("返回结果:{}", result); |
| | | log.info("request realtime response: {}", result); |
| | | cacheRemoteResponse(cacheKey, result); |
| | | return result; |
| | | } |
| | |
| | | param.put("deviceGuid", guid); |
| | | param.put("startTime", startTime); |
| | | param.put("endTime", endTime); |
| | | log.info("历史数据请求参数:{}", JSON.toJSONString(param)); |
| | | 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("命中历史数据缓存:{}", cacheKey); |
| | | log.info("hit history cache: {}", cacheKey); |
| | | return cachedResult; |
| | | } |
| | | String result = HttpUtils.sendPostJson(REAL_HISTORY_URL, JSON.toJSONString(param), token); |
| | | log.info("历史数据返回结果:{}", result); |
| | | log.info("request history response: {}", result); |
| | | cacheRemoteResponse(cacheKey, result); |
| | | return result; |
| | | } |