|
@@ -2,11 +2,13 @@ package com.iot.scheduler.service; |
|
@@ -2,11 +2,13 @@ package com.iot.scheduler.service; |
|
2
|
|
2
|
|
|
3
|
import com.alibaba.fastjson.JSON;
|
3
|
import com.alibaba.fastjson.JSON;
|
|
4
|
import com.alibaba.fastjson.JSONObject;
|
4
|
import com.alibaba.fastjson.JSONObject;
|
|
|
|
5
|
+import com.iot.scheduler.utils.DeviceDataStorage;
|
|
5
|
import com.zaxxer.hikari.HikariConfig;
|
6
|
import com.zaxxer.hikari.HikariConfig;
|
|
6
|
import com.zaxxer.hikari.HikariDataSource;
|
7
|
import com.zaxxer.hikari.HikariDataSource;
|
|
7
|
import jakarta.annotation.Resource;
|
8
|
import jakarta.annotation.Resource;
|
|
8
|
import lombok.extern.slf4j.Slf4j;
|
9
|
import lombok.extern.slf4j.Slf4j;
|
|
9
|
import org.apache.commons.collections4.CollectionUtils;
|
10
|
import org.apache.commons.collections4.CollectionUtils;
|
|
|
|
11
|
+import org.apache.commons.lang3.SerializationUtils;
|
|
10
|
import org.apache.commons.lang3.StringUtils;
|
12
|
import org.apache.commons.lang3.StringUtils;
|
|
11
|
import org.apache.http.HttpEntity;
|
13
|
import org.apache.http.HttpEntity;
|
|
12
|
import org.apache.http.NameValuePair;
|
14
|
import org.apache.http.NameValuePair;
|
|
@@ -22,10 +24,15 @@ import org.springframework.beans.factory.annotation.Value; |
|
@@ -22,10 +24,15 @@ import org.springframework.beans.factory.annotation.Value; |
|
22
|
import org.springframework.data.redis.core.RedisTemplate;
|
24
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
23
|
import org.springframework.stereotype.Service;
|
25
|
import org.springframework.stereotype.Service;
|
|
24
|
|
26
|
|
|
|
|
27
|
+import java.io.Serializable;
|
|
25
|
import java.nio.charset.StandardCharsets;
|
28
|
import java.nio.charset.StandardCharsets;
|
|
26
|
import java.sql.*;
|
29
|
import java.sql.*;
|
|
27
|
import java.sql.Date;
|
30
|
import java.sql.Date;
|
|
28
|
import java.text.SimpleDateFormat;
|
31
|
import java.text.SimpleDateFormat;
|
|
|
|
32
|
+import java.time.Instant;
|
|
|
|
33
|
+import java.time.LocalDateTime;
|
|
|
|
34
|
+import java.time.ZoneId;
|
|
|
|
35
|
+import java.time.format.DateTimeFormatter;
|
|
29
|
import java.util.*;
|
36
|
import java.util.*;
|
|
30
|
import java.util.concurrent.TimeUnit;
|
37
|
import java.util.concurrent.TimeUnit;
|
|
31
|
|
38
|
|
|
@@ -79,6 +86,25 @@ public class PjDeviceReportService { |
|
@@ -79,6 +86,25 @@ public class PjDeviceReportService { |
|
79
|
@Resource
|
86
|
@Resource
|
|
80
|
private RedisTemplate<String, String> redisTemplate;
|
87
|
private RedisTemplate<String, String> redisTemplate;
|
|
81
|
|
88
|
|
|
|
|
89
|
+ private static final DateTimeFormatter YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
|
90
|
+ private static final DateTimeFormatter YYYY_MM = DateTimeFormatter.ofPattern("yyyy-MM");
|
|
|
|
91
|
+
|
|
|
|
92
|
+ /**
|
|
|
|
93
|
+ * 时间戳(毫秒) -> yyyy-MM-dd
|
|
|
|
94
|
+ */
|
|
|
|
95
|
+ private static String toYyyyMmDd(long timestampMillis) {
|
|
|
|
96
|
+ return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestampMillis), ZoneId.systemDefault())
|
|
|
|
97
|
+ .format(YYYY_MM_DD);
|
|
|
|
98
|
+ }
|
|
|
|
99
|
+
|
|
|
|
100
|
+ /**
|
|
|
|
101
|
+ * 时间戳(毫秒) -> yyyy-MM
|
|
|
|
102
|
+ */
|
|
|
|
103
|
+ private static String toYyyyMm(long timestampMillis) {
|
|
|
|
104
|
+ return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestampMillis), ZoneId.systemDefault())
|
|
|
|
105
|
+ .format(YYYY_MM);
|
|
|
|
106
|
+ }
|
|
|
|
107
|
+
|
|
82
|
/**
|
108
|
/**
|
|
83
|
* 企业设备上报
|
109
|
* 企业设备上报
|
|
84
|
*/
|
110
|
*/
|
|
@@ -244,16 +270,13 @@ public class PjDeviceReportService { |
|
@@ -244,16 +270,13 @@ public class PjDeviceReportService { |
|
244
|
|
270
|
|
|
245
|
// 2. 准备上报数据
|
271
|
// 2. 准备上报数据
|
|
246
|
log.info("步骤2: 开始准备上报数据");
|
272
|
log.info("步骤2: 开始准备上报数据");
|
|
247
|
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
248
|
- String nowTime = simpleDateFormat.format(new java.util.Date());
|
|
|
|
249
|
- log.info("上报时间: {}", nowTime);
|
|
|
|
250
|
|
273
|
|
|
251
|
CloseableHttpClient httpclient = HttpClients.createDefault();
|
274
|
CloseableHttpClient httpclient = HttpClients.createDefault();
|
|
252
|
HttpPost httpPost = new HttpPost(reportUrl);
|
275
|
HttpPost httpPost = new HttpPost(reportUrl);
|
|
253
|
|
276
|
|
|
254
|
// 3. 获取并设置Token
|
277
|
// 3. 获取并设置Token
|
|
255
|
log.info("步骤3: 获取访问令牌");
|
278
|
log.info("步骤3: 获取访问令牌");
|
|
256
|
- String token = getToken();
|
279
|
+ String token = getTokenNoCache();
|
|
257
|
if (StringUtils.isBlank(token)) {
|
280
|
if (StringUtils.isBlank(token)) {
|
|
258
|
log.error("获取访问令牌失败,无法进行数据上报");
|
281
|
log.error("获取访问令牌失败,无法进行数据上报");
|
|
259
|
return;
|
282
|
return;
|
|
@@ -266,9 +289,10 @@ public class PjDeviceReportService { |
|
@@ -266,9 +289,10 @@ public class PjDeviceReportService { |
|
266
|
// 4. 构建上报数据
|
289
|
// 4. 构建上报数据
|
|
267
|
log.info("步骤4: 构建上报数据结构");
|
290
|
log.info("步骤4: 构建上报数据结构");
|
|
268
|
List<Map<String, Object>> dataList = new ArrayList<>(needSyncDataList.size());
|
291
|
List<Map<String, Object>> dataList = new ArrayList<>(needSyncDataList.size());
|
|
|
|
292
|
+ List<Map<String, Object>> saveDataList = new ArrayList<>(needSyncDataList.size());
|
|
269
|
int successCount = 0;
|
293
|
int successCount = 0;
|
|
270
|
int errorCount = 0;
|
294
|
int errorCount = 0;
|
|
271
|
-
|
295
|
+ DeviceDataStorage storage = new DeviceDataStorage();
|
|
272
|
for (int i = 0; i < needSyncDataList.size(); i++) {
|
296
|
for (int i = 0; i < needSyncDataList.size(); i++) {
|
|
273
|
try {
|
297
|
try {
|
|
274
|
Object needSyncData = needSyncDataList.get(i);
|
298
|
Object needSyncData = needSyncDataList.get(i);
|
|
@@ -281,14 +305,51 @@ public class PjDeviceReportService { |
|
@@ -281,14 +305,51 @@ public class PjDeviceReportService { |
|
281
|
}
|
305
|
}
|
|
282
|
|
306
|
|
|
283
|
Map<String, Object> data = new HashMap<>();
|
307
|
Map<String, Object> data = new HashMap<>();
|
|
|
|
308
|
+ String deviceCode = (String) dataObject.get(2);
|
|
|
|
309
|
+ String energyType = (String) dataObject.get(3);
|
|
|
|
310
|
+
|
|
284
|
data.put("entName", dataObject.get(0));
|
311
|
data.put("entName", dataObject.get(0));
|
|
285
|
data.put("deviceName", dataObject.get(1));
|
312
|
data.put("deviceName", dataObject.get(1));
|
|
286
|
- data.put("deviceCode", dataObject.get(2));
|
|
|
|
287
|
- data.put("energyType", dataObject.get(3));
|
|
|
|
288
|
- data.put("energyValue", dataObject.get(4));
|
|
|
|
289
|
- data.put("timeSpan", "日");
|
|
|
|
290
|
- data.put("collectTime", nowTime);
|
313
|
+ data.put("deviceCode", deviceCode);
|
|
|
|
314
|
+ data.put("energyType", energyType);
|
|
|
|
315
|
+ Long ts = (Long) dataObject.get(5);
|
|
|
|
316
|
+ DeviceDataStorage.DeviceRecord lastRecord = storage.getLastRecord(deviceCode);
|
|
|
|
317
|
+ if (lastRecord == null) {
|
|
|
|
318
|
+ log.warn("第 {} 条数据,设备:{} 未找到上一期上报数据", i + 1, deviceCode);
|
|
|
|
319
|
+ continue;
|
|
|
|
320
|
+ }
|
|
|
|
321
|
+ if (lastRecord.getTimestamp() >= ts) {
|
|
|
|
322
|
+ log.warn("第 {} 条数据,设备:{} 数据未更新无需上报", i + 1, deviceCode);
|
|
|
|
323
|
+ continue;
|
|
|
|
324
|
+ }
|
|
|
|
325
|
+ Double energyValue = toDouble(dataObject.get(4));
|
|
|
|
326
|
+
|
|
|
|
327
|
+ if ("电".equals(energyType) || "天然气".equals(energyType)) {
|
|
|
|
328
|
+ data.put("energyValue", energyValue);
|
|
|
|
329
|
+ data.put("timeSpan", "日");
|
|
|
|
330
|
+ data.put("collectTime", toYyyyMmDd(ts));
|
|
|
|
331
|
+ } else if ("水".equals(energyType)) {
|
|
|
|
332
|
+ Double reportValue = energyValue - lastRecord.getValue();
|
|
|
|
333
|
+ if (reportValue <= 0) {
|
|
|
|
334
|
+ log.warn("第 {} 条数据,设备:{} 水表抄表记录异常", i + 1, deviceCode);
|
|
|
|
335
|
+ }
|
|
|
|
336
|
+ data.put("energyValue", reportValue);
|
|
|
|
337
|
+ data.put("timeSpan", "月");
|
|
|
|
338
|
+ data.put("collectTime", toYyyyMm(ts));
|
|
|
|
339
|
+ } else {
|
|
|
|
340
|
+ log.warn("第 {} 条数据,设备:{} 物模型数据错误!", i + 1, deviceCode);
|
|
|
|
341
|
+ continue;
|
|
|
|
342
|
+ }
|
|
|
|
343
|
+
|
|
291
|
dataList.add(data);
|
344
|
dataList.add(data);
|
|
|
|
345
|
+ Map<String, Object> saveData = (Map<String, Object>) SerializationUtils.clone((Serializable) data);
|
|
|
|
346
|
+ saveData.put("ts",ts);
|
|
|
|
347
|
+ saveData.put("orignValue",energyValue);
|
|
|
|
348
|
+ saveDataList.add(saveData);
|
|
|
|
349
|
+
|
|
|
|
350
|
+ //处理历史数据放开
|
|
|
|
351
|
+// saveReportData(Collections.singletonList(saveData));
|
|
|
|
352
|
+
|
|
292
|
successCount++;
|
353
|
successCount++;
|
|
293
|
|
354
|
|
|
294
|
// 每100条记录输出一次进度
|
355
|
// 每100条记录输出一次进度
|
|
@@ -336,6 +397,8 @@ public class PjDeviceReportService { |
|
@@ -336,6 +397,8 @@ public class PjDeviceReportService { |
|
336
|
JSONObject jsonResult = JSON.parseObject(result);
|
397
|
JSONObject jsonResult = JSON.parseObject(result);
|
|
337
|
if (jsonResult != null && "true".equalsIgnoreCase(jsonResult.getString("success"))) {
|
398
|
if (jsonResult != null && "true".equalsIgnoreCase(jsonResult.getString("success"))) {
|
|
338
|
log.info("数据上报成功!共上报 {} 条设备数据", dataList.size());
|
399
|
log.info("数据上报成功!共上报 {} 条设备数据", dataList.size());
|
|
|
|
400
|
+ //记录上报数据
|
|
|
|
401
|
+ saveReportData(saveDataList);
|
|
339
|
} else {
|
402
|
} else {
|
|
340
|
log.error("数据上报失败!响应状态异常: {}", result);
|
403
|
log.error("数据上报失败!响应状态异常: {}", result);
|
|
341
|
}
|
404
|
}
|
|
@@ -353,6 +416,19 @@ public class PjDeviceReportService { |
|
@@ -353,6 +416,19 @@ public class PjDeviceReportService { |
|
353
|
}
|
416
|
}
|
|
354
|
}
|
417
|
}
|
|
355
|
|
418
|
|
|
|
|
419
|
+ private void saveReportData(List<Map<String, Object>> dataList) {
|
|
|
|
420
|
+ if (CollectionUtils.isEmpty(dataList)) {
|
|
|
|
421
|
+ return;
|
|
|
|
422
|
+ }
|
|
|
|
423
|
+ DeviceDataStorage storage = new DeviceDataStorage();
|
|
|
|
424
|
+ for (Map<String, Object> data : dataList) {
|
|
|
|
425
|
+ String deviceCode = (String) data.get("deviceCode");
|
|
|
|
426
|
+ Long ts = (Long) data.get("ts");
|
|
|
|
427
|
+ Double orignValue = (Double) data.get("orignValue");
|
|
|
|
428
|
+ storage.updateData(deviceCode, ts, orignValue);
|
|
|
|
429
|
+ }
|
|
|
|
430
|
+ }
|
|
|
|
431
|
+
|
|
356
|
/**
|
432
|
/**
|
|
357
|
* 获取token
|
433
|
* 获取token
|
|
358
|
*
|
434
|
*
|
|
@@ -432,6 +508,70 @@ public class PjDeviceReportService { |
|
@@ -432,6 +508,70 @@ public class PjDeviceReportService { |
|
432
|
}
|
508
|
}
|
|
433
|
}
|
509
|
}
|
|
434
|
|
510
|
|
|
|
|
511
|
+ /**
|
|
|
|
512
|
+ * 获取token
|
|
|
|
513
|
+ *
|
|
|
|
514
|
+ * @return
|
|
|
|
515
|
+ */
|
|
|
|
516
|
+ private String getTokenNoCache() {
|
|
|
|
517
|
+ log.info("开始获取访问令牌");
|
|
|
|
518
|
+ long startTime = System.currentTimeMillis();
|
|
|
|
519
|
+ String key = "pjjkq_report_token";
|
|
|
|
520
|
+
|
|
|
|
521
|
+ try {
|
|
|
|
522
|
+ String token;
|
|
|
|
523
|
+ // 2. 请求新的token
|
|
|
|
524
|
+ CloseableHttpClient httpclient = HttpClients.createDefault();
|
|
|
|
525
|
+ HttpPost httpPost = new HttpPost(tokenUrl);
|
|
|
|
526
|
+ httpPost.setHeader("Authorization", "Basic bWljcm8tcG9ydGFsOlBAc3MxMjM0");
|
|
|
|
527
|
+ httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
|
|
|
|
528
|
+
|
|
|
|
529
|
+ List<NameValuePair> params = new ArrayList<>();
|
|
|
|
530
|
+ params.add(new BasicNameValuePair("grant_type", "password"));
|
|
|
|
531
|
+ params.add(new BasicNameValuePair("username", username));
|
|
|
|
532
|
+ params.add(new BasicNameValuePair("password", password)); // 密码脱敏
|
|
|
|
533
|
+
|
|
|
|
534
|
+ log.info("Token请求URL: {}", tokenUrl);
|
|
|
|
535
|
+ log.info("请求参数 - grant_type: password, username: {}", username);
|
|
|
|
536
|
+
|
|
|
|
537
|
+ httpPost.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
|
|
|
|
538
|
+
|
|
|
|
539
|
+ try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
|
|
|
|
540
|
+ int statusCode = response.getStatusLine().getStatusCode();
|
|
|
|
541
|
+ log.info("Token接口响应状态码: {}", statusCode);
|
|
|
|
542
|
+
|
|
|
|
543
|
+ HttpEntity responseEntity = response.getEntity();
|
|
|
|
544
|
+ String result = EntityUtils.toString(responseEntity);
|
|
|
|
545
|
+ log.debug("Token接口原始响应: {}", result);
|
|
|
|
546
|
+
|
|
|
|
547
|
+ EntityUtils.consume(responseEntity);
|
|
|
|
548
|
+
|
|
|
|
549
|
+ if (statusCode == 200) {
|
|
|
|
550
|
+ JSONObject json = JSON.parseObject(result);
|
|
|
|
551
|
+ token = json.getString("access_token");
|
|
|
|
552
|
+ int expiresIn = json.getIntValue("expires_in");
|
|
|
|
553
|
+
|
|
|
|
554
|
+ log.info("成功获取Token,有效期: {} 秒", expiresIn);
|
|
|
|
555
|
+
|
|
|
|
556
|
+ log.info("Token已缓存到Redis,key: {},有效期: {} 秒", key, expiresIn);
|
|
|
|
557
|
+
|
|
|
|
558
|
+ return token;
|
|
|
|
559
|
+ } else {
|
|
|
|
560
|
+ log.error("获取Token失败,状态码: {}, 响应: {}", statusCode, result);
|
|
|
|
561
|
+ return null;
|
|
|
|
562
|
+ }
|
|
|
|
563
|
+
|
|
|
|
564
|
+ } catch (Exception e) {
|
|
|
|
565
|
+ log.error("调用Token接口时发生异常", e);
|
|
|
|
566
|
+ return null;
|
|
|
|
567
|
+ }
|
|
|
|
568
|
+
|
|
|
|
569
|
+ } finally {
|
|
|
|
570
|
+ long endTime = System.currentTimeMillis();
|
|
|
|
571
|
+ log.info("获取Token操作完成,耗时: {} 毫秒", (endTime - startTime));
|
|
|
|
572
|
+ }
|
|
|
|
573
|
+ }
|
|
|
|
574
|
+
|
|
435
|
private List<Object> initConnectAndSelectData() {
|
575
|
private List<Object> initConnectAndSelectData() {
|
|
436
|
Connection connection = null;
|
576
|
Connection connection = null;
|
|
437
|
PreparedStatement statement = null;
|
577
|
PreparedStatement statement = null;
|
|
@@ -674,4 +814,24 @@ public class PjDeviceReportService { |
|
@@ -674,4 +814,24 @@ public class PjDeviceReportService { |
|
674
|
throw e;
|
814
|
throw e;
|
|
675
|
}
|
815
|
}
|
|
676
|
}
|
816
|
}
|
|
|
|
817
|
+
|
|
|
|
818
|
+ public static Double toDouble(Object obj) {
|
|
|
|
819
|
+ if (obj == null) return null;
|
|
|
|
820
|
+ if (obj instanceof Double) {
|
|
|
|
821
|
+ return (Double) obj;
|
|
|
|
822
|
+ } else if (obj instanceof Long) {
|
|
|
|
823
|
+ return ((Long) obj).doubleValue();
|
|
|
|
824
|
+ } else if (obj instanceof Integer) {
|
|
|
|
825
|
+ return ((Integer) obj).doubleValue();
|
|
|
|
826
|
+ } else if (obj instanceof Number) {
|
|
|
|
827
|
+ return ((Number) obj).doubleValue();
|
|
|
|
828
|
+ } else if (obj instanceof String) {
|
|
|
|
829
|
+ try {
|
|
|
|
830
|
+ return Double.parseDouble((String) obj);
|
|
|
|
831
|
+ } catch (NumberFormatException e) {
|
|
|
|
832
|
+ return null;
|
|
|
|
833
|
+ }
|
|
|
|
834
|
+ }
|
|
|
|
835
|
+ return null;
|
|
|
|
836
|
+ }
|
|
677
|
} |
837
|
} |