Commit 0fbac06e0e81af031866a5c3d3026d13b57618b8

Authored by 杨鸣坤
1 parent 24851b70

feat: 添加设备数据持久化与查询接口

... ... @@ -51,6 +51,15 @@
51 51 <version>42.7.3</version>
52 52 </dependency>
53 53 <dependency>
  54 + <groupId>com.mysql</groupId>
  55 + <artifactId>mysql-connector-j</artifactId>
  56 + <version>8.0.33</version>
  57 + </dependency>
  58 + <dependency>
  59 + <groupId>org.springframework.boot</groupId>
  60 + <artifactId>spring-boot-starter-jdbc</artifactId>
  61 + </dependency>
  62 + <dependency>
54 63 <groupId>org.apache.httpcomponents</groupId>
55 64 <artifactId>httpclient</artifactId>
56 65 <version>4.5.8</version>
... ...
1 1 package com.iot.scheduler.controller;
2 2
3 3 import com.iot.scheduler.service.DevicePullService;
  4 +import com.iot.scheduler.service.DeviceSearchService;
4 5 import jakarta.annotation.Resource;
5 6 import org.springframework.web.bind.annotation.GetMapping;
  7 +import org.springframework.web.bind.annotation.RequestParam;
6 8 import org.springframework.web.bind.annotation.RestController;
7 9
  10 +import java.util.Map;
  11 +
8 12 @RestController
9 13 public class HealthController {
10 14
11 15 @Resource
12 16 private DevicePullService devicePullService;
  17 + @Resource
  18 + private DeviceSearchService deviceSearchService;
13 19
14 20 @GetMapping("/health")
15   - public String health() {
16   - devicePullService.getEnergyInfo();
  21 + public String health() throws Exception {
  22 + devicePullService.pullDeviceAndPushToIot();
17 23 return "IoT Scheduler is running...";
18 24 }
  25 +
  26 + @GetMapping("/device/list")
  27 + public Map<String, Object> deviceList(
  28 + @RequestParam(required = false) String deviceName,
  29 + @RequestParam(required = false) String lampState,
  30 + @RequestParam(defaultValue = "1") Integer pageNo,
  31 + @RequestParam(defaultValue = "10") Integer pageSize) {
  32 + return deviceSearchService.queryDeviceList(deviceName, lampState, pageNo, pageSize);
  33 + }
  34 +
  35 + @GetMapping("/device/stats")
  36 + public Map<String, Object> deviceStats() {
  37 + return deviceSearchService.queryDeviceStats();
  38 + }
19 39 }
... ...
... ... @@ -19,6 +19,7 @@ import org.apache.http.impl.client.HttpClients;
19 19 import org.apache.http.util.EntityUtils;
20 20 import org.springframework.beans.factory.annotation.Value;
21 21 import org.springframework.data.redis.core.RedisTemplate;
  22 +import org.springframework.jdbc.core.JdbcTemplate;
22 23 import org.springframework.stereotype.Service;
23 24 import org.springframework.util.CollectionUtils;
24 25 import org.springframework.util.LinkedMultiValueMap;
... ... @@ -28,6 +29,7 @@ import org.springframework.web.util.UriComponentsBuilder;
28 29 import java.io.ByteArrayOutputStream;
29 30 import java.io.IOException;
30 31 import java.io.InputStream;
  32 +import java.text.ParseException;
31 33 import java.text.SimpleDateFormat;
32 34 import java.util.*;
33 35 import java.util.concurrent.TimeUnit;
... ... @@ -47,20 +49,91 @@ public class DevicePullService {
47 49 private String deviceInfoUrl;
48 50 @Value("${device.detail.url}")
49 51 private String deviceDetailUrl;
  52 + @Value("${device.snRate.url}")
  53 + private String deviceSnRateUrl;
50 54 @Value("${device.energyInfo.url}")
51 55 private String energyInfoUrl;
  56 + @Value("${device.db.corpCode}")
  57 + private String deviceCorpCode;
  58 + @Value("${device.db.tableName}")
  59 + private String deviceTableName;
52 60
53 61 @Resource
54 62 private RedisTemplate<String, String> redisTemplate;
  63 + @Resource
  64 + private JdbcTemplate jdbcTemplate;
55 65
56 66 final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
57 67
58   - public void pullDeviceAndPushToIot() {
  68 + public void pullDeviceAndPushToIot() throws ParseException {
59 69 String deviceResult = getDeviceInfo();
60 70 Map<String, Object> deviceInfos = JSON.parseObject(deviceResult, new TypeReference<>() {
61 71 });
62 72
63 73 JSONArray deviceInfoList = (JSONArray) deviceInfos.get("data");
  74 + for (Object o : deviceInfoList) {
  75 + JSONObject deviceInfoJson = (JSONObject) o;
  76 +
  77 + String projectState = deviceInfoJson.getString("projectState");
  78 + String projectType = deviceInfoJson.getString("projectType");
  79 + String deviceName = deviceInfoJson.getString("deviceName");
  80 + String dtuId = deviceInfoJson.getString("dtuId");
  81 + String deviceId = deviceInfoJson.getString("deviceId");
  82 + String dtuSn = deviceInfoJson.getString("dtuSn");
  83 +
  84 + String deviceInfoDetails = getDeviceInfoDetail(dtuSn);
  85 + if (StringUtils.isBlank(deviceInfoDetails)) {
  86 + return;
  87 + }
  88 +
  89 + Map<String, Object> deviceInfoDetailMap = JSON.parseObject(deviceInfoDetails, new TypeReference<>() {
  90 + });
  91 +
  92 + JSONArray deviceInfoDetailList = (JSONArray) deviceInfoDetailMap.get("data");
  93 + if (CollectionUtils.isEmpty(deviceInfoDetailList)) {
  94 + return;
  95 + }
  96 +
  97 + JSONObject deviceInfoDetailJson = (JSONObject) deviceInfoDetailList.get(0);
  98 +
  99 + String lampState = deviceInfoDetailJson.getString("lampState");
  100 + String startTime = deviceInfoDetailJson.getString("startTime");
  101 +
  102 + long startTimestamp = 0;
  103 + if (StringUtils.isNotBlank(startTime)) {
  104 + startTimestamp = dateFormat.parse(startTime).getTime();
  105 + }
  106 +
  107 + long currentTimestamp = System.currentTimeMillis();
  108 + long diffMillis = currentTimestamp - startTimestamp;
  109 + long hours = diffMillis / (1000 * 60 * 60);
  110 + long minutes = (diffMillis % (1000 * 60 * 60)) / (1000 * 60);
  111 + long seconds = (diffMillis % (1000 * 60)) / 1000;
  112 + StringBuilder durationBuilder = new StringBuilder();
  113 + if (hours > 0) {
  114 + durationBuilder.append(hours).append("时");
  115 + }
  116 +
  117 + if (minutes > 0) {
  118 + durationBuilder.append(minutes).append("分");
  119 + }
  120 +
  121 + durationBuilder.append(seconds).append("秒");
  122 + String duration = durationBuilder.toString();
  123 +
  124 + // 获取稼动率
  125 + String utilizationRate = getUtilizationRate(dtuSn);
  126 +
  127 + log.info("设备数据汇总 - projectState:{}, projectType:{}, deviceName:{}, dtuId:{}, deviceId:{}, dtuSn:{}, " +
  128 + "lampState:{}, duration:{}, utilizationRate:{}",
  129 + projectState, projectType, deviceName, dtuId, deviceId, dtuSn,
  130 + lampState, duration, utilizationRate);
  131 +
  132 + // 保存或更新数据库
  133 + saveOrUpdateDevice(projectState, projectType, deviceName, dtuId, deviceId, dtuSn,
  134 + lampState, startTime, duration, utilizationRate);
  135 + }
  136 +
64 137
65 138 }
66 139
... ... @@ -99,13 +172,100 @@ public class DevicePullService {
99 172 return deviceInfoDetail;
100 173 }
101 174
  175 + public String getDtuSnRateOfAction(String dtuSn) {
  176 + String accessToken = getAccessToken();
  177 + Map<String, String> headerMap = new HashMap<>(1);
  178 + headerMap.put("Authorization", "Bearer " + accessToken);
  179 +
  180 + Map<String, String> paramsMap = new HashMap<>();
  181 + String today = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
  182 + paramsMap.put("startDate", today);
  183 + paramsMap.put("endDate", today);
  184 + paramsMap.put("dtuSn", dtuSn);
  185 +
  186 + String rateResult = sendRequestGet(deviceSnRateUrl, paramsMap, headerMap);
  187 + if (StringUtils.isBlank(rateResult)) {
  188 + return null;
  189 + }
  190 +
  191 + Map<String, Object> rateMap = JSON.parseObject(rateResult, new TypeReference<>() {});
  192 + Integer rateCode = (Integer) rateMap.get("code");
  193 + if (rateCode != 200) {
  194 + return null;
  195 + }
  196 +
  197 + return rateResult;
  198 + }
  199 +
  200 + public String getUtilizationRate(String dtuSn) {
  201 + String rateResult = getDtuSnRateOfAction(dtuSn);
  202 + if (StringUtils.isBlank(rateResult)) {
  203 + return "0%";
  204 + }
  205 +
  206 + Map<String, Object> rateMap = JSON.parseObject(rateResult, new TypeReference<>() {});
  207 + JSONArray dataList = (JSONArray) rateMap.get("data");
  208 + if (CollectionUtils.isEmpty(dataList)) {
  209 + return "0%";
  210 + }
  211 +
  212 + JSONObject todayData = (JSONObject) dataList.get(0);
  213 + JSONArray realRateList = todayData.getJSONArray("realRate");
  214 + if (CollectionUtils.isEmpty(realRateList)) {
  215 + return "0%";
  216 + }
  217 +
  218 + JSONObject rateObj = (JSONObject) realRateList.get(0);
  219 + long state0 = rateObj.getLongValue("0");
  220 + long state1 = rateObj.getLongValue("1");
  221 + long state2 = rateObj.getLongValue("2");
  222 + long state3 = rateObj.getLongValue("3");
  223 + long state4 = rateObj.getLongValue("4");
  224 + long state5 = rateObj.getLongValue("5");
  225 + long totalTime = state0 + state1 + state2 + state3 + state4 + state5;
  226 + if (totalTime == 0) {
  227 + return "0%";
  228 + }
  229 +
  230 + double rate = (double) state3 / totalTime * 100;
  231 + return String.format("%.2f%%", rate);
  232 + }
  233 +
  234 + public void saveOrUpdateDevice(String projectState, String projectType, String deviceName, String dtuId,
  235 + String deviceId, String dtuSn, String lampState, String startTime,
  236 + String duration, String utilizationRate) {
  237 + // 查询是否已存在(按公司+dtuSn判断)
  238 + List<Map<String, Object>> existList = jdbcTemplate.queryForList(
  239 + "SELECT id FROM " + deviceTableName + " WHERE corp_code = ? AND dtuSn = ?", deviceCorpCode, dtuSn);
  240 +
  241 + Date now = new Date();
  242 + if (!existList.isEmpty()) {
  243 + // 更新
  244 + jdbcTemplate.update("UPDATE " + deviceTableName + " SET projectState = ?, projectType = ?, deviceName = ?, " +
  245 + "dtuId = ?, deviceId = ?, lampState = ?, startTime = ?, duration = ?, utilizationRate = ?, updated_at = ? WHERE dtuSn = ?",
  246 + projectState, projectType, deviceName, dtuId, deviceId, lampState,
  247 + startTime, duration, utilizationRate, now, dtuSn);
  248 + log.info("设备数据更新成功 - dtuSn:{}", dtuSn);
  249 + } else {
  250 + // 新增
  251 + String id = UUID.randomUUID().toString().replace("-", "");
  252 + jdbcTemplate.update("INSERT INTO " + deviceTableName + " (id, corp_code, created_at, created_by, updated_at, updated_by, " +
  253 + "deviceName, projectType, projectState, dtuSn, dtuId, deviceId, lampState, startTime, duration, utilizationRate) " +
  254 + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
  255 + id, deviceCorpCode, now, "system", now, "system",
  256 + deviceName, projectType, projectState, dtuSn, dtuId, deviceId, lampState,
  257 + startTime, duration, utilizationRate);
  258 + log.info("设备数据新增成功 - dtuSn:{}", dtuSn);
  259 + }
  260 + }
  261 +
102 262 public String getEnergyInfo() {
103 263 String accessToken = getAccessToken();
104 264 Map<String, String> headerMap = new HashMap<>(1);
105 265 headerMap.put("Authorization", "Bearer " + accessToken);
106 266
107 267 Map<String, String> paramsMap = new HashMap<>();
108   - paramsMap.put("groupName", "一期工厂");
  268 + paramsMap.put("groupName", "SHC");
109 269
110 270 String energyInfoResult = sendRequestGet(energyInfoUrl, paramsMap, headerMap);
111 271 if (StringUtils.isBlank(energyInfoResult)) {
... ... @@ -155,7 +315,7 @@ public class DevicePullService {
155 315 headerMap.put("Authorization", "Bearer " + accessToken);
156 316
157 317 Map<String, String> paramsMap = new HashMap<>();
158   - paramsMap.put("groupName", "一期工厂");
  318 + paramsMap.put("groupName", "SHC");
159 319
160 320 // 第一次请求设备信息
161 321 String deviceResult = sendRequestGet(deviceInfoUrl, paramsMap, headerMap);
... ...
  1 +package com.iot.scheduler.service;
  2 +
  3 +import lombok.extern.slf4j.Slf4j;
  4 +import org.springframework.beans.factory.annotation.Value;
  5 +import org.springframework.jdbc.core.JdbcTemplate;
  6 +import org.springframework.stereotype.Service;
  7 +import org.springframework.util.StringUtils;
  8 +
  9 +import jakarta.annotation.Resource;
  10 +import java.util.List;
  11 +import java.util.Map;
  12 +
  13 +@Slf4j
  14 +@Service
  15 +public class DeviceSearchService {
  16 +
  17 + @Value("${device.db.corpCode}")
  18 + private String deviceCorpCode;
  19 + @Value("${device.db.tableName}")
  20 + private String deviceTableName;
  21 +
  22 + @Resource
  23 + private JdbcTemplate jdbcTemplate;
  24 +
  25 + public Map<String, Object> queryDeviceList(String deviceName, String lampState, Integer pageNo, Integer pageSize) {
  26 + StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM " + deviceTableName + " WHERE corp_code = ?");
  27 + StringBuilder querySql = new StringBuilder("SELECT id, deviceName, projectType, projectState, dtuSn, dtuId, deviceId, " +
  28 + "lampState, startTime, duration, utilizationRate FROM " + deviceTableName + " WHERE corp_code = ?");
  29 + List<Object> params = new java.util.ArrayList<>();
  30 + params.add(deviceCorpCode);
  31 +
  32 + if (StringUtils.hasText(deviceName)) {
  33 + countSql.append(" AND deviceName LIKE ?");
  34 + querySql.append(" AND deviceName LIKE ?");
  35 + params.add("%" + deviceName + "%");
  36 + }
  37 + if (StringUtils.hasText(lampState)) {
  38 + countSql.append(" AND lampState = ?");
  39 + querySql.append(" AND lampState = ?");
  40 + params.add(lampState);
  41 + }
  42 +
  43 + Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class, params.toArray());
  44 + int offset = (pageNo - 1) * pageSize;
  45 + querySql.append(" ORDER BY created_at DESC LIMIT ?, ?");
  46 + params.add(offset);
  47 + params.add(pageSize);
  48 +
  49 + List<Map<String, Object>> list = jdbcTemplate.queryForList(querySql.toString(), params.toArray());
  50 +
  51 + return Map.of(
  52 + "total", total != null ? total : 0,
  53 + "pageNo", pageNo,
  54 + "pageSize", pageSize,
  55 + "list", list
  56 + );
  57 + }
  58 +
  59 + public Map<String, Object> queryDeviceStats() {
  60 + String sql = "SELECT lampState, COUNT(*) as cnt FROM " + deviceTableName +
  61 + " WHERE corp_code = ? GROUP BY lampState";
  62 + List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, deviceCorpCode);
  63 +
  64 + long all = 0;
  65 + long red = 0, yellow = 0, green = 0, blue = 0, off = 0;
  66 + for (Map<String, Object> row : rows) {
  67 + String state = (String) row.get("lampState");
  68 + long cnt = ((Number) row.get("cnt")).longValue();
  69 + all += cnt;
  70 + switch (state) {
  71 + case "0" -> off = cnt;
  72 + case "1" -> red = cnt;
  73 + case "2" -> yellow = cnt;
  74 + case "3" -> green = cnt;
  75 + case "4" -> blue = cnt;
  76 + }
  77 + }
  78 +
  79 + return Map.of(
  80 + "all", all,
  81 + "red", red,
  82 + "yellow", yellow,
  83 + "green", green,
  84 + "blue", blue,
  85 + "off", off
  86 + );
  87 + }
  88 +}
... ...
... ... @@ -3,6 +3,11 @@ spring:
3 3 name: iot-scheduler
4 4 main:
5 5 banner-mode: off
  6 + datasource:
  7 + url: jdbc:mysql://10.9.1.252:3306/qixiao-apaas?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
  8 + username: root
  9 + password: "qiXiao.20240826.mysql.com"
  10 + driver-class-name: com.mysql.cj.jdbc.Driver
6 11 data:
7 12 redis:
8 13 cluster:
... ... @@ -43,7 +48,12 @@ device:
43 48 url: "https://iotgc.cniot.vip/triColorLamp/userGroupDtuSns"
44 49 detail:
45 50 url: "https://iotgc.cniot.vip/triColorLamp/dtuSnState"
  51 + snRate:
  52 + url: "https://iotgc.cniot.vip/triColorLamp/dtuSnRateOfAction"
46 53 energyInfo:
47 54 url: "https://iotgc.cniot.vip/api/energy/userGroupDtuSns"
48 55 energyDetail:
49 56 url: "https://iotgc.cniot.vip/api/energy/dtuSnRateEnergy"
  57 + db:
  58 + corpCode: "ymk"
  59 + tableName: "t_auto_ymk_iot_device"
... ...