james 1 日 前
コミット
be19414f55
100 ファイル変更5864 行追加4 行削除
  1. 8 0
      pom.xml
  2. 1 1
      service-eg/service-eg-biz/pom.xml
  3. 122 0
      service-ems/README.md
  4. 20 0
      service-ems/pom.xml
  5. 24 0
      service-ems/service-ems-api/pom.xml
  6. 17 0
      service-ems/service-ems-api/src/main/java/com/usky/ems/RemoteEmsTaskService.java
  7. 39 0
      service-ems/service-ems-api/src/main/java/com/usky/ems/factory/RemoteEmsTaskFactory.java
  8. 97 0
      service-ems/service-ems-biz/pom.xml
  9. 46 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/EnergyApplication.java
  10. 107 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/MybatisGenerator.java
  11. 110 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/config/TdengineConfig.java
  12. 23 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/api/ServiceEmsTaskApi.java
  13. 21 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/web/EmsConsPlatformConfigController.java
  14. 23 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/web/EnergyDataController.java
  15. 86 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/DmpDevice.java
  16. 101 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsConsPlatformConfig.java
  17. 103 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/TdengineData.java
  18. 35 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/DmpDeviceMapper.java
  19. 19 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsConsPlatformConfigMapper.java
  20. 119 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/protocol/NetworkPacket.java
  21. 376 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/protocol/TcpClient.java
  22. 31 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/DmpDeviceService.java
  23. 23 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsConsPlatformConfigService.java
  24. 20 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EnergyDataService.java
  25. 30 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/TdengineService.java
  26. 32 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/DmpDeviceServiceImpl.java
  27. 29 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsConsPlatformConfigServiceImpl.java
  28. 290 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EnergyDataServiceImpl.java
  29. 115 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/TdengineServiceImpl.java
  30. 11 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EnergyDataRequestVO.java
  31. 57 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/util/AesUtil.java
  32. 40 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/util/Md5Util.java
  33. 180 0
      service-ems/service-ems-biz/src/main/java/com/usky/ems/util/XmlBuilder.java
  34. 25 0
      service-ems/service-ems-biz/src/main/resources/bootstrap.yml
  35. 38 0
      service-ems/service-ems-biz/src/main/resources/mapper/ems/DmpDeviceMapper.xml
  36. 28 0
      service-ems/service-ems-biz/src/main/resources/mapper/ems/EmsConsPlatformConfigMapper.xml
  37. 2 2
      service-iot/service-iot-biz/pom.xml
  38. 6 0
      service-job/pom.xml
  39. 21 0
      service-job/src/main/java/com/ruoyi/job/task/RyTask.java
  40. 1 1
      service-meeting/service-meeting-biz/pom.xml
  41. 17 0
      service-transfer/pom.xml
  42. 29 0
      service-transfer/service-transfer-api/pom.xml
  43. 33 0
      service-transfer/service-transfer-api/src/main/java/com/usky/transfer/RemoteTransferService.java
  44. 41 0
      service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/DeviceDataInfoVO.java
  45. 43 0
      service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/DeviceDataWriteVO.java
  46. 30 0
      service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/HistoryRequestVO.java
  47. 19 0
      service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/HistoryResultVO.java
  48. 20 0
      service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/LastRequestVO.java
  49. 20 0
      service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/LastResultVO.java
  50. 20 0
      service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/MetricVO.java
  51. 18 0
      service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/MqttDeviceDataVO.java
  52. 52 0
      service-transfer/service-transfer-api/src/main/java/com/usky/transfer/factory/RemoteTransferFallbackFactory.java
  53. 112 0
      service-transfer/service-transfer-biz/pom.xml
  54. 51 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/ApplicationRun.java
  55. 107 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/MybatisGenerator.java
  56. 56 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/controller/api/DataTransferControllerApi.java
  57. 209 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/controller/web/QueryDeviceDataController.java
  58. 43 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/controller/web/SendMessageController.java
  59. 151 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/domain/DmpDevice.java
  60. 96 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/domain/DmpDeviceCommand.java
  61. 66 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/domain/DmpDeviceStatus.java
  62. 141 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/domain/DmpProduct.java
  63. 137 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/domain/DmpProductAttribute.java
  64. 27 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/domain/QueryInfluxdbData.java
  65. 16 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/mapper/DmpDeviceCommandMapper.java
  66. 16 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/mapper/DmpDeviceMapper.java
  67. 19 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/mapper/DmpDeviceStatusMapper.java
  68. 16 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/mapper/DmpProductAttributeMapper.java
  69. 16 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/mapper/DmpProductMapper.java
  70. 16 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/mapper/QueryInfluxdbDataMapper.java
  71. 19 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/DmpDeviceCommandService.java
  72. 16 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/DmpDeviceService.java
  73. 16 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/DmpDeviceStatusService.java
  74. 16 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/DmpProductAttributeService.java
  75. 24 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/DmpProductService.java
  76. 26 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/QueryInfluxdbDataService.java
  77. 12 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/config/CodeCache.java
  78. 62 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/config/TsdbConfig.java
  79. 50 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/config/mqtt/MqttBaseConfig.java
  80. 56 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/config/mqtt/MqttInConfig.java
  81. 84 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/config/mqtt/MqttOutConfig.java
  82. 60 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/enums/TopListener.java
  83. 42 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/impl/DmpDeviceCommandServiceImpl.java
  84. 20 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/impl/DmpDeviceServiceImpl.java
  85. 22 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/impl/DmpDeviceStatusServiceImpl.java
  86. 20 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/impl/DmpProductAttributeServiceImpl.java
  87. 99 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/impl/DmpProductServiceImpl.java
  88. 339 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/impl/QueryInfluxdbDataServiceImpl.java
  89. 79 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/listener/MqttListener.java
  90. 21 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/MqttStrategy.java
  91. 26 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/SimpleContext.java
  92. 152 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/add/Add.java
  93. 40 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/alarm/alarm.java
  94. 53 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/control/control.java
  95. 65 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/datacollector/DataCollector.java
  96. 77 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/info/Info.java
  97. 21 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/rocketmq/MyConsumer.java
  98. 17 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/rocketmq/MyProducer.java
  99. 80 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/rocketmq/RocketMQSimpleContext.java
  100. 137 0
      service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/utils/HttpClientUtils.java

+ 8 - 0
pom.xml

@@ -97,8 +97,16 @@
 
     <module>service-ids</module>
 
+<<<<<<< HEAD
 
     <module>service-cdi</module>
+=======
+    <module>service-ems</module>
+
+    <module>service-transfer</module>
+
+    <module>service-tsdb</module>
+>>>>>>> usky-zyj
 
   </modules>
           

+ 1 - 1
service-eg/service-eg-biz/pom.xml

@@ -49,7 +49,7 @@
         </dependency>
         <dependency>
             <groupId>com.usky</groupId>
-            <artifactId>data-transfer-api</artifactId>
+            <artifactId>service-transfer-api</artifactId>
             <version>0.0.1</version>
         </dependency>
         <dependency>

+ 122 - 0
service-ems/README.md

@@ -0,0 +1,122 @@
+# 能耗数据上传服务
+
+## 功能说明
+
+本服务用于实现能耗数据上传功能,通过TCP协议与能耗监管系统进行通信,按照XML数据通讯协议规范进行数据传输。
+
+## 主要功能
+
+1. **TCP连接管理**:建立和维护与能耗监管系统的TCP连接
+2. **身份认证**:使用MD5算法进行身份验证
+3. **数据加密**:使用AES加密算法对能耗数据进行加密传输
+4. **定时推送**:每10分钟自动生成并推送能耗数据XML报文
+5. **心跳机制**:定期发送心跳包保持连接
+
+## 配置说明
+
+### 应用配置(application-dev.yml)
+
+在Nacos配置中心或本地配置文件中添加以下配置:
+
+```yaml
+# 能耗服务配置
+energy:
+  # 服务端配置
+  server:
+    host: 183.192.66.5
+    port: 9006
+  # 认证配置
+  auth:
+    key: 529f3d2bf15d4d5d  # 128bit密钥
+  # 建筑信息
+  building:
+    id: PT310107BZ4054
+  # 网关配置(多个网关用逗号分隔)
+  gateway:
+    ids: gateway1,gateway2
+  # 定时任务配置
+  push:
+    cron: "0 */10 * * * ?"  # 每10分钟执行一次
+  # TDengine配置
+  tdengine:
+    enabled: true
+    url: jdbc:TAOS://localhost:6030/
+    user: root
+    password: taosdata
+    super-table: super_715_332
+```
+
+### 数据库配置
+
+服务需要访问MySQL数据库查询设备信息,配置在Nacos配置中心的共享配置中。
+
+## 使用说明
+
+### 1. 网关配置
+
+在配置文件中设置要推送数据的网关ID列表,多个网关用逗号分隔:
+
+```yaml
+energy:
+  gateway:
+    ids: gateway1,gateway2
+```
+
+### 2. 启动服务
+
+启动服务后,定时任务会自动执行,每10分钟为每个配置的网关生成并推送XML报文。
+
+### 3. 手动推送
+
+也可以通过API接口手动触发推送:
+
+```java
+@Autowired
+private EnergyDataService energyDataService;
+
+// 推送指定网关的数据
+energyDataService.generateAndPushEnergyData("gatewayId");
+```
+
+## 数据流程
+
+1. 根据网关ID查询网关设备信息(`dmp_device`表,`category_type=2`)
+2. 根据网关UUID查询所有子设备(`dmp_device`表,`gateway_uuid`匹配,`category_type=3`)
+3. 从TDengine时序数据库查询每个设备的最新数据(`super_715_332`超级表)
+4. 构建XML报文(包含分项数据和仪表数据)
+5. 建立TCP连接并进行身份认证
+6. 使用AES加密XML数据
+7. 发送加密后的数据包
+8. 接收服务端响应
+
+## 协议说明
+
+### 网络层数据包格式
+
+- Head: 2字节,固定值0x1F1F
+- Type: 1字节,消息类型(0x01=身份认证,0x02=心跳,0x03=能耗数据)
+- Length: 4字节,Data长度(网络字节序)
+- Data: 变长,XML数据(UTF-8编码,能耗数据需AES加密)
+
+### 身份认证流程
+
+1. 客户端发送身份认证请求
+2. 服务端返回随机序列
+3. 客户端计算MD5(密钥+随机序列)并发送
+4. 服务端验证MD5并返回认证结果
+
+### 数据加密
+
+- 算法:AES
+- 模式:CBC
+- 填充:PKCS7/PKCS5
+- 密钥:128bit,与认证密钥相同
+- 向量:与密钥相同
+
+## 注意事项
+
+1. 确保TDengine数据源配置正确
+2. 确保网关ID在数据库中存在且为网关设备(`category_type=2`)
+3. 确保网关下有电表子设备(`category_type=3`)
+4. 确保TDengine超级表中存在对应设备的数据
+5. 网络连接异常时会自动重连

+ 20 - 0
service-ems/pom.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>usky-modules</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-ems</artifactId>
+
+    <packaging>pom</packaging>
+    <version>0.0.1</version>
+
+    <modules>
+        <module>service-ems-biz</module>
+        <module>service-ems-api</module>
+    </modules>
+</project>

+ 24 - 0
service-ems/service-ems-api/pom.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>service-ems</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-ems-api</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>usky-common-core</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 17 - 0
service-ems/service-ems-api/src/main/java/com/usky/ems/RemoteEmsTaskService.java

@@ -0,0 +1,17 @@
+package com.usky.ems;
+
+
+import com.usky.ems.factory.RemoteEmsTaskFactory;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+@FeignClient(contextId = "remoteEmsTaskService", value = "service-ems" , fallbackFactory = RemoteEmsTaskFactory.class)
+public interface RemoteEmsTaskService {
+
+    @GetMapping("/sendEnergyData")
+    void sendEnergyData();
+
+    @GetMapping("/sendEnergyHeartbeat")
+    void sendEnergyHeartbeat();
+}

+ 39 - 0
service-ems/service-ems-api/src/main/java/com/usky/ems/factory/RemoteEmsTaskFactory.java

@@ -0,0 +1,39 @@
+package com.usky.ems.factory;
+
+import com.usky.common.core.exception.FeignBadRequestException;
+import com.usky.ems.RemoteEmsTaskService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.openfeign.FallbackFactory;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * 用户服务降级处理
+ *
+ * @author ruoyi
+ */
+@Component
+public class RemoteEmsTaskFactory implements FallbackFactory<RemoteEmsTaskService>
+{
+    private static final Logger log = LoggerFactory.getLogger(RemoteEmsTaskFactory.class);
+
+    @Override
+    public RemoteEmsTaskService create(Throwable throwable)
+    {
+        log.error("用户服务调用失败:{}", throwable.getMessage());
+        return new RemoteEmsTaskService()
+        {
+            @Override
+            public void sendEnergyData() {
+                throw new FeignBadRequestException(500,"定时推送能耗数据到能耗平台"+throwable.getMessage());
+            }
+
+            @Override
+            public void sendEnergyHeartbeat() {
+                throw new FeignBadRequestException(500,"定时推送能耗心跳到能耗平台"+throwable.getMessage());
+            }
+
+        };
+    }
+}

+ 97 - 0
service-ems/service-ems-biz/pom.xml

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>service-ems</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-ems-biz</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>common-cloud-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>service-ems-api</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>service-iot-api</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>service-tsdb-api</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>ruoyi-common-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>ruoyi-common-swagger</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>usky-common-mybatis</artifactId>
+        </dependency>
+
+        <!-- XML处理 -->
+        <dependency>
+            <groupId>org.dom4j</groupId>
+            <artifactId>dom4j</artifactId>
+            <version>2.1.3</version>
+        </dependency>
+
+        <!-- AES加密 -->
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>1.70</version>
+        </dependency>
+        <!-- Hutool 加密解密工具类(5.8.41 版本) -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-crypto</artifactId>
+            <version>5.8.41</version>
+        </dependency>
+
+        <!-- TDengine JDBC -->
+        <dependency>
+            <groupId>com.taosdata.jdbc</groupId>
+            <artifactId>taos-jdbcdriver</artifactId>
+            <version>3.6.3</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.2.6.RELEASE</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 46 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/EnergyApplication.java

@@ -0,0 +1,46 @@
+package com.usky.ems;
+
+import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
+import org.mybatis.spring.annotation.MapperScan;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.core.env.Environment;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * 能耗数据上传服务
+ *
+ * @author system
+ */
+@EnableCustomSwagger2
+@EnableFeignClients(basePackages = "com.usky")
+@MapperScan(value = "com.usky.ems.mapper")
+@ComponentScan("com.usky")
+@SpringBootApplication
+@EnableScheduling
+public class EnergyApplication {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(EnergyApplication.class);
+
+    public static void main(String[] args) throws UnknownHostException {
+        ConfigurableApplicationContext application = SpringApplication.run(EnergyApplication.class, args);
+        Environment env = application.getEnvironment();
+        String ip = InetAddress.getLocalHost().getHostAddress();
+        String port = env.getProperty("server.port");
+        String path = env.getProperty("server.servlet.context-path");
+        LOGGER.info("\n----------------------------------------------------------\n\t" +
+                "Application is running! Access URLs:\n\t" +
+                "Local: \t\thttp://localhost:" + port + (null == path ? "" : path) + "/\n\t" +
+                "External: \thttp://" + ip + ":" + port + (null == path ? "" : path) + "/\n\t" +
+                "Api: \t\thttp://" + ip + ":" + port + (null == path ? "" : path) + "/swagger-ui/index.html\n\t" +
+                "----------------------------------------------------------");
+    }
+}

+ 107 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/MybatisGenerator.java

@@ -0,0 +1,107 @@
+package com.usky.ems;//package com.usky.demo.controller;//package com.usky.dm.controller.web.business;//package com.usky.dm.controller.web;
+
+
+
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.generator.AutoGenerator;
+import com.baomidou.mybatisplus.generator.InjectionConfig;
+import com.baomidou.mybatisplus.generator.config.*;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ */
+public class MybatisGenerator {
+    public static void main(String[] args) {
+
+            shell("service-ems","service-ems-biz");
+    }
+
+    private static void shell(String parentName,String model) {
+
+        AutoGenerator mpg = new AutoGenerator();
+        //1、全局配置
+        GlobalConfig gc = new GlobalConfig();
+//        File file = new File(model);
+//        String path = file.getAbsolutePath();
+        String projectPath = System.getProperty("user.dir");
+        projectPath+="/"+parentName;
+        projectPath+="/"+model;
+        gc.setOutputDir(projectPath+ "/src/main/java");  //生成路径(一般都是生成在此项目的src/main/java下面)
+        //修改为自己的名字
+        gc.setAuthor("ya"); //设置作者
+        gc.setOpen(false);
+        gc.setFileOverride(true); //第二次生成会把第一次生成的覆盖掉
+        gc.setServiceName("%sService"); //生成的service接口名字首字母是否为I,这样设置就没有
+        gc.setBaseResultMap(true); //生成resultMap
+        mpg.setGlobalConfig(gc);
+
+        //2、数据源配置
+        //修改数据源
+        DataSourceConfig dsc = new DataSourceConfig();
+        dsc.setUrl("jdbc:mysql://192.168.10.165:3306/usky-cloud?useUnicode=true&serverTimezone=GMT&useSSL=false&characterEncoding=utf8");
+        dsc.setDriverName("com.mysql.jdbc.Driver");
+        dsc.setUsername("root");
+        dsc.setPassword("yt123456");
+        mpg.setDataSource(dsc);
+
+        // 3、包配置
+        PackageConfig pc = new PackageConfig();
+        pc.setParent("com.usky.ems");
+        pc.setController("controller.web");
+        pc.setEntity("domain");
+        pc.setMapper("mapper");
+        pc.setService("service");
+        pc.setServiceImpl("service.impl");
+//        pc.setXml("mapper.demo");
+        //pc.setModuleName("test");
+        mpg.setPackageInfo(pc);
+
+        // 4、策略配置
+        StrategyConfig strategy = new StrategyConfig();
+        strategy.setNaming(NamingStrategy.underline_to_camel);
+        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
+        strategy.setSuperMapperClass("com.usky.common.mybatis.core.CrudMapper");
+        strategy.setSuperServiceClass("com.usky.common.mybatis.core.CrudService");
+        strategy.setSuperServiceImplClass("com.usky.common.mybatis.core.AbstractCrudService");
+        // strategy.setTablePrefix("t_"); // 表名前缀
+        strategy.setEntityLombokModel(true); //使用lombok
+        //修改自己想要生成的表
+        strategy.setInclude("ems_cons_platform_config");  // 逆向工程使用的表   如果要生成多个,这里可以传入String[]
+        mpg.setStrategy(strategy);
+
+        // 关闭默认 xml 生成,调整生成 至 根目录
+        //修改对应的模块名称
+        TemplateConfig tc = new TemplateConfig();
+        // 自定义配置
+        InjectionConfig cfg = new InjectionConfig() {
+            @Override
+            public void initMap() {
+                // to do nothing
+            }
+        };
+        //如果模板引擎是 velocity
+        String templatePath = "/templates/mapper.xml.vm";
+        // 自定义输出配置
+        List<FileOutConfig> focList = new ArrayList<>();
+        // 自定义配置会被优先输出
+        String finalProjectPath = projectPath;
+        focList.add(new FileOutConfig(templatePath) {
+            @Override
+            public String outputFile(TableInfo tableInfo) {
+                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
+                return finalProjectPath + "/src/main/resources/mapper/ems" + "/"
+                        + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
+            }
+        });
+        cfg.setFileOutConfigList(focList);
+        mpg.setCfg(cfg);
+        tc.setXml(null);
+        mpg.setTemplate(tc);
+        //5、执行
+        mpg.execute();
+    }
+}

+ 110 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/config/TdengineConfig.java

@@ -0,0 +1,110 @@
+//package com.usky.energy.config;
+//
+//import com.taosdata.jdbc.TSDBDriver;
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//
+//import javax.sql.DataSource;
+//import java.sql.Connection;
+//import java.sql.DriverManager;
+//import java.sql.SQLException;
+//import java.util.Properties;
+//
+///**
+// * TDengine数据源配置
+// *
+// * @author system
+// * @since 2024-01-01
+// */
+//@Configuration
+//@ConditionalOnProperty(prefix = "energy.tdengine", name = "enabled", havingValue = "true", matchIfMissing = false)
+//public class TdengineConfig {
+//
+//    private static final Logger logger = LoggerFactory.getLogger(TdengineConfig.class);
+//
+//    @Value("${energy.tdengine.url: jdbc:TAOS-RS://47.110.48.75:6041/}")
+//    private String url;
+//
+//    @Value("${energy.tdengine.user:root}")
+//    private String user;
+//
+//    @Value("${energy.tdengine.password:taosdata}")
+//    private String password;
+//
+//    @Bean(name = "tdengineDataSource")
+//    public DataSource tdengineDataSource() {
+//        return new TdengineDataSource(url, user, password);
+//    }
+//
+//    /**
+//     * 简单的TDengine数据源实现
+//     */
+//    private static class TdengineDataSource implements DataSource {
+//        private final String url;
+//        private final String user;
+//        private final String password;
+//
+//        public TdengineDataSource(String url, String user, String password) {
+//            this.url = url;
+//            this.user = user;
+//            this.password = password;
+//        }
+//
+//        @Override
+//        public Connection getConnection() throws SQLException {
+//            try {
+//                Class.forName("com.taosdata.jdbc.TSDBDriver");
+//            } catch (ClassNotFoundException e) {
+//                throw new SQLException("TDengine驱动未找到", e);
+//            }
+//
+//            Properties props = new Properties();
+//            props.setProperty("user", user);
+//            props.setProperty("password", password);
+//
+//            return DriverManager.getConnection(url, props);
+//        }
+//
+//        @Override
+//        public Connection getConnection(String username, String password) throws SQLException {
+//            return getConnection();
+//        }
+//
+//        @Override
+//        public <T> T unwrap(Class<T> iface) throws SQLException {
+//            throw new SQLException("不支持的操作");
+//        }
+//
+//        @Override
+//        public boolean isWrapperFor(Class<?> iface) throws SQLException {
+//            return false;
+//        }
+//
+//        @Override
+//        public java.io.PrintWriter getLogWriter() throws SQLException {
+//            return null;
+//        }
+//
+//        @Override
+//        public void setLogWriter(java.io.PrintWriter out) throws SQLException {
+//        }
+//
+//        @Override
+//        public void setLoginTimeout(int seconds) throws SQLException {
+//        }
+//
+//        @Override
+//        public int getLoginTimeout() throws SQLException {
+//            return 0;
+//        }
+//
+//        @Override
+//        public java.util.logging.Logger getParentLogger() {
+//            return null;
+//        }
+//    }
+//}

+ 23 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/api/ServiceEmsTaskApi.java

@@ -0,0 +1,23 @@
+package com.usky.ems.controller.api;
+
+import com.usky.ems.RemoteEmsTaskService;
+import com.usky.ems.service.EnergyDataService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class ServiceEmsTaskApi implements RemoteEmsTaskService {
+    @Autowired
+    private EnergyDataService energyDatabaseService;
+
+    @Override
+    public void sendEnergyData(){
+        energyDatabaseService.sendEnergyData();
+    }
+
+    @Override
+    public void sendEnergyHeartbeat(){
+        energyDatabaseService.sendEnergyHeartbeat();
+    }
+
+}

+ 21 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/web/EmsConsPlatformConfigController.java

@@ -0,0 +1,21 @@
+package com.usky.ems.controller.web;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.stereotype.Controller;
+
+/**
+ * <p>
+ * 能耗_集成平台配置表 前端控制器
+ * </p>
+ *
+ * @author ya
+ * @since 2026-01-29
+ */
+@Controller
+@RequestMapping("/emsConsPlatformConfig")
+public class EmsConsPlatformConfigController {
+
+}
+

+ 23 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/controller/web/EnergyDataController.java

@@ -0,0 +1,23 @@
+package com.usky.ems.controller.web;
+
+import com.usky.common.core.bean.ApiResult;
+import com.usky.ems.service.EnergyDataService;
+import com.usky.ems.service.vo.EnergyDataRequestVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/energy-data")
+public class EnergyDataController {
+
+    @Autowired
+    private EnergyDataService energyDataService;
+
+
+    @GetMapping("sendEnergyData")
+    public ApiResult<Void> sendData() {
+        energyDataService.sendEnergyData();
+        return ApiResult.success();
+
+    }
+}

+ 86 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/DmpDevice.java

@@ -0,0 +1,86 @@
+package com.usky.ems.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * 设备信息表
+ * </p>
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("dmp_device")
+public class DmpDevice implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 设备ID;设备注册时系统自动生成一个唯一编号
+     */
+    private String deviceId;
+
+    /**
+     * 设备名称
+     */
+    private String deviceName;
+
+    /**
+     * 设备类型
+     */
+    private Integer deviceType;
+
+    /**
+     * 产品ID
+     */
+    private Integer productId;
+
+    /**
+     * 产品编码
+     */
+    private String productCode;
+
+    /**
+     * 设备uuid
+     */
+    private String deviceUuid;
+
+    /**
+     * 设备所属类型(1、普通设备  2、网关设备  3、网关子设备)
+     */
+    private Integer categoryType;
+
+    /**
+     * 所属网关
+     */
+    private String gatewayUuid;
+
+    /**
+     * 建筑ID
+     */
+    private Integer buildId;
+
+    /**
+     * 删除标识
+     */
+    private Integer deleteFlag;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+}

+ 101 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/EmsConsPlatformConfig.java

@@ -0,0 +1,101 @@
+package com.usky.ems.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 能耗_集成平台配置表
+ * </p>
+ *
+ * @author ya
+ * @since 2026-01-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class EmsConsPlatformConfig implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 平台名称
+     */
+    private String platformName;
+
+    /**
+     * 建筑Id
+     */
+    private String buildId;
+
+    /**
+     * 建筑名称
+     */
+    private String buildName;
+
+    /**
+     * 平台IP
+     */
+    private String platformIp;
+
+    /**
+     * 平台端口
+     */
+    private Integer platformPort;
+
+    /**
+     * 用户名
+     */
+    private String username;
+
+    /**
+     * 密钥
+     */
+    private String passwd;
+
+    /**
+     * 创建人
+     */
+    private String createdBy;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createdTime;
+
+    /**
+     * 更新人
+     */
+    private String updatedBy;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updatedTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+
+    /**
+     * 关联产品编码
+     */
+    private String productCode;
+
+    /**
+     * 删除标识
+     */
+    private Integer deleteFlag;
+
+
+}

+ 103 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/domain/TdengineData.java

@@ -0,0 +1,103 @@
+package com.usky.ems.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+
+/**
+ * TDengine数据实体
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+@Data
+public class TdengineData implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 时间戳
+     */
+    private Timestamp ts;
+
+    /**
+     * 设备ID
+     */
+    private String deviceId;
+
+    /**
+     * A相电压
+     */
+    private Double voltageA;
+
+    /**
+     * B相电压
+     */
+    private Double voltageB;
+
+    /**
+     * C相电压
+     */
+    private Double voltageC;
+
+    /**
+     * A相电流
+     */
+    private Double currentA;
+
+    /**
+     * B相电流
+     */
+    private Double currentB;
+
+    /**
+     * C相电流
+     */
+    private Double currentC;
+
+    /**
+     * A相有功功率
+     */
+    private Double activepowera;
+
+    /**
+     * B相有功功率
+     */
+    private Double activepowerb;
+
+    /**
+     * C相有功功率
+     */
+    private Double activepowerc;
+
+    /**
+     * 总有功功率
+     */
+    private Double totalActivePower;
+
+    /**
+     * 总功率因数
+     */
+    private Double totalPowerFactor;
+
+    /**
+     * 累计正向有功电量
+     */
+    private Double totalActiveEnergyF;
+
+    /**
+     * 累计反向有功电量
+     */
+    private Double totalActiveEnergyR;
+
+    /**
+     * 累计正向无功电量
+     */
+    private Double totalReactiveEnergyF;
+
+    /**
+     * 累计反向无功电量
+     */
+    private Double totalReactiveEnergyR;
+}

+ 35 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/DmpDeviceMapper.java

@@ -0,0 +1,35 @@
+package com.usky.ems.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.usky.ems.domain.DmpDevice;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 设备信息表 Mapper 接口
+ * </p>
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+@Mapper
+public interface DmpDeviceMapper extends BaseMapper<DmpDevice> {
+
+    /**
+     * 根据网关ID查询网关设备
+     * @param tenantId 租户ID
+     * @param productCode 产品编码
+     * @return 网关设备信息
+     */
+    List<DmpDevice> selectGatewayByDeviceId(@Param("tenantId") Integer tenantId ,@Param("productCode") String productCode);
+
+    /**
+     * 根据网关UUID查询所有子设备
+     * @param gatewayUuid 网关UUID
+     * @return 子设备列表
+     */
+    List<DmpDevice> selectDevicesByGatewayUuid(@Param("gatewayUuid") String gatewayUuid);
+}

+ 19 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/mapper/EmsConsPlatformConfigMapper.java

@@ -0,0 +1,19 @@
+package com.usky.ems.mapper;
+
+import com.usky.ems.domain.EmsConsPlatformConfig;
+import com.usky.common.mybatis.core.CrudMapper;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 能耗_集成平台配置表 Mapper 接口
+ * </p>
+ *
+ * @author ya
+ * @since 2026-01-29
+ */
+public interface EmsConsPlatformConfigMapper extends CrudMapper<EmsConsPlatformConfig> {
+
+    List<EmsConsPlatformConfig> getPlatformConfig();
+}

+ 119 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/protocol/NetworkPacket.java

@@ -0,0 +1,119 @@
+package com.usky.ems.protocol;
+
+import lombok.Data;
+
+/**
+ * 网络层数据包
+ * 格式:Head(2字节) + Type(1字节) + Length(4字节) + Data(变长)
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+@Data
+public class NetworkPacket {
+
+    /**
+     * 消息头,固定值0x1F1F
+     */
+    public static final short HEAD = 0x1F1F;
+
+    /**
+     * 消息类型:身份认证
+     */
+    public static final byte TYPE_AUTH = 0x01;
+
+    /**
+     * 消息类型:心跳
+     */
+    public static final byte TYPE_HEARTBEAT = 0x02;
+
+    /**
+     * 消息类型:能耗数据
+     */
+    public static final byte TYPE_ENERGY_DATA = 0x03;
+
+    /**
+     * 消息类型
+     */
+    private byte type;
+
+    /**
+     * 数据体(加密后的字节数组)
+     */
+    private byte[] data;
+
+    /**
+     * 将数据包编码为字节数组
+     *
+     * @return 编码后的字节数组
+     */
+    public byte[] encode() {
+        int dataLength = data != null ? data.length : 0;
+        int totalLength = 2 + 1 + 4 + dataLength; // Head + Type + Length + Data
+        
+        byte[] packet = new byte[totalLength];
+        int offset = 0;
+        
+        // Head (2字节,网络字节序,高位在前)
+        packet[offset++] = (byte) ((HEAD >> 8) & 0xFF);
+        packet[offset++] = (byte) (HEAD & 0xFF);
+        
+        // Type (1字节)
+        packet[offset++] = type;
+        
+        // Length (4字节,网络字节序,高位在前)
+        packet[offset++] = (byte) ((dataLength >> 24) & 0xFF);
+        packet[offset++] = (byte) ((dataLength >> 16) & 0xFF);
+        packet[offset++] = (byte) ((dataLength >> 8) & 0xFF);
+        packet[offset++] = (byte) (dataLength & 0xFF);
+        
+        // Data
+        if (data != null && dataLength > 0) {
+            System.arraycopy(data, 0, packet, offset, dataLength);
+        }
+        
+        return packet;
+    }
+
+    /**
+     * 从字节数组解码数据包
+     *
+     * @param bytes 字节数组
+     * @return 解码后的数据包
+     */
+    public static NetworkPacket decode(byte[] bytes) {
+        if (bytes == null || bytes.length < 7) {
+            throw new IllegalArgumentException("数据包长度不足");
+        }
+        
+        int offset = 0;
+        
+        // 验证Head
+        short head = (short) (((bytes[offset++] & 0xFF) << 8) | (bytes[offset++] & 0xFF));
+        if (head != HEAD) {
+            throw new IllegalArgumentException("无效的消息头: " + String.format("0x%04X", head));
+        }
+        
+        NetworkPacket packet = new NetworkPacket();
+        
+        // Type
+        packet.type = bytes[offset++];
+        
+        // Length
+        int length = ((bytes[offset++] & 0xFF) << 24) |
+                     ((bytes[offset++] & 0xFF) << 16) |
+                     ((bytes[offset++] & 0xFF) << 8) |
+                     (bytes[offset++] & 0xFF);
+        
+        // Data
+        if (length > 0) {
+            if (bytes.length < offset + length) {
+                throw new IllegalArgumentException("数据包长度不足,期望: " + (offset + length) + ", 实际: " + bytes.length);
+            }
+            packet.data = new byte[length];
+            System.arraycopy(bytes, offset, packet.data, 0, length);
+        }
+        
+        return packet;
+    }
+}

+ 376 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/protocol/TcpClient.java

@@ -0,0 +1,376 @@
+package com.usky.ems.protocol;
+
+import com.usky.ems.util.AesUtil;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * TCP客户端
+ * 用于与能耗监管系统建立连接并发送数据
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+public class TcpClient {
+
+    private static final Logger logger = LoggerFactory.getLogger(TcpClient.class);
+
+    private String host;
+    private int port;
+    private String authKey;
+    private Socket socket;
+    private InputStream inputStream;
+    private OutputStream outputStream;
+    private AtomicBoolean connected = new AtomicBoolean(false);
+    private AtomicBoolean authenticated = new AtomicBoolean(false);
+
+    public TcpClient(String host, int port, String authKey) {
+        this.host = host;
+        this.port = port;
+        this.authKey = authKey;
+    }
+
+    /**
+     * 建立TCP连接
+     *
+     * @return 是否连接成功
+     */
+    public boolean connect() {
+        try {
+            if (socket != null && !socket.isClosed()) {
+                socket.close();
+            }
+
+            socket = new Socket(host, port);
+            socket.setSoTimeout(30000); // 30秒超时
+            inputStream = socket.getInputStream();
+            outputStream = socket.getOutputStream();
+            connected.set(true);
+            authenticated.set(false);
+
+            logger.info("TCP连接成功: {}:{}", host, port);
+            return true;
+        } catch (IOException e) {
+            logger.error("TCP连接失败: {}:{}", host, port, e);
+            connected.set(false);
+            return false;
+        }
+    }
+
+    /**
+     * 关闭连接
+     */
+    public void close() {
+        try {
+            if (inputStream != null) {
+                inputStream.close();
+            }
+            if (outputStream != null) {
+                outputStream.close();
+            }
+            if (socket != null && !socket.isClosed()) {
+                socket.close();
+            }
+            connected.set(false);
+            authenticated.set(false);
+            logger.info("TCP连接已关闭");
+        } catch (IOException e) {
+            logger.error("关闭TCP连接失败", e);
+        }
+    }
+
+    /**
+     * 身份认证
+     *
+     * @param buildingId 建筑ID
+     * @param gatewayId  网关ID
+     * @return 是否认证成功
+     */
+    public boolean authenticate(String buildingId, String gatewayId) {
+        if (!connected.get()) {
+            logger.error("未建立TCP连接,无法进行身份认证");
+            return false;
+        }
+
+        try {
+            // 1. 发送身份认证请求
+            String requestXml = com.usky.ems.util.XmlBuilder.buildAuthRequest(buildingId, gatewayId);
+            sendPacket(NetworkPacket.TYPE_AUTH, requestXml, false);
+
+            // 2. 接收服务端返回的随机序列
+            NetworkPacket response = receivePacket();
+            if (response == null || response.getType() != NetworkPacket.TYPE_AUTH) {
+                logger.error("接收身份认证响应失败");
+                return false;
+            }
+
+            // 认证响应不加密
+            String responseXml = new String(response.getData(), java.nio.charset.StandardCharsets.UTF_8);
+            String sequence = parseSequenceFromXml(responseXml);
+            if (sequence == null || sequence.isEmpty()) {
+                logger.error("解析随机序列失败");
+                return false;
+            }
+
+            // 3. 计算MD5:密钥 + 随机序列
+            String md5 = com.usky.ems.util.Md5Util.md5(authKey + sequence);
+
+            // 4. 发送MD5值
+            String md5Xml = com.usky.ems.util.XmlBuilder.buildAuthMd5Request(buildingId, gatewayId, md5);
+            sendPacket(NetworkPacket.TYPE_AUTH, md5Xml, false);
+
+            // 5. 接收认证结果
+            NetworkPacket result = receivePacket();
+            if (result == null || result.getType() != NetworkPacket.TYPE_AUTH) {
+                logger.error("接收认证结果失败");
+                return false;
+            }
+
+            // 认证响应不加密
+            String resultXml = new String(result.getData(), java.nio.charset.StandardCharsets.UTF_8);
+            boolean success = parseAuthResult(resultXml);
+
+            if (success) {
+                authenticated.set(true);
+                logger.info("身份认证成功");
+            } else {
+                logger.error("身份认证失败");
+            }
+
+            return success;
+        } catch (Exception e) {
+            logger.error("身份认证过程异常", e);
+            return false;
+        }
+    }
+
+    /**
+     * 发送心跳
+     *
+     * @param buildingId 建筑ID
+     * @param gatewayId  网关ID
+     * @return 是否发送成功
+     */
+    public boolean sendHeartbeat(String buildingId, String gatewayId) {
+        if (!connected.get() || !authenticated.get()) {
+            logger.warn("连接未建立或未认证,无法发送心跳");
+            return false;
+        }
+
+        try {
+            String heartbeatXml = com.usky.ems.util.XmlBuilder.buildHeartbeatRequest(buildingId, gatewayId);
+            sendPacket(NetworkPacket.TYPE_HEARTBEAT, heartbeatXml, false);
+            logger.debug("心跳发送成功");
+            return true;
+        } catch (Exception e) {
+            logger.error("发送心跳失败", e);
+            return false;
+        }
+    }
+
+    /**
+     * 发送能耗数据
+     *
+     * @param xmlData XML数据(未加密)
+     * @return 是否发送成功
+     */
+    public boolean sendEnergyData(String xmlData) {
+        if (!connected.get() || !authenticated.get()) {
+            logger.warn("连接未建立或未认证,无法发送能耗数据");
+            return false;
+        }
+
+        try {
+            sendPacket(NetworkPacket.TYPE_ENERGY_DATA, xmlData, true);
+            logger.info("能耗数据发送成功");
+
+            // 接收服务端响应
+            NetworkPacket response = receivePacket();
+            if (response != null && response.getType() == NetworkPacket.TYPE_ENERGY_DATA) {
+                // 能耗数据响应需要解密
+                try {
+                    byte[] responseData = response.getData();
+                    String responseXml = null;
+                    Exception lastException = null;
+
+                    // 方法1:首先尝试直接解密字节数组
+                    try {
+                        responseXml = AesUtil.decrypt(authKey,responseData);
+                        logger.debug("直接解密字节数组成功");
+                    } catch (Exception e1) {
+                        lastException = e1;
+                        logger.debug("直接解密字节数组失败: {}", e1.getMessage());
+
+                        // 方法2:如果直接解密失败,尝试作为Base64字符串处理
+                        // 先检查数据是否可能是Base64字符串(只包含Base64字符)
+                        if (responseXml == null) {
+                            try {
+                                String dataString = new String(responseData, java.nio.charset.StandardCharsets.UTF_8);
+                                // 检查是否只包含Base64字符(A-Z, a-z, 0-9, +, /, =)
+                                if (dataString.matches("^[A-Za-z0-9+/=]+$")) {
+                                    // Base64解码后再解密
+                                    byte[] decodedBytes = java.util.Base64.getDecoder().decode(dataString);
+                                    responseXml = AesUtil.decrypt(authKey,decodedBytes);
+                                    logger.debug("Base64解码后解密成功");
+                                } else {
+                                    logger.debug("数据不是Base64格式,跳过Base64解码");
+                                }
+                            } catch (Exception e2) {
+                                lastException = e2;
+                                logger.debug("Base64解码后解密失败: {}", e2.getMessage());
+                            }
+                        }
+                    }
+
+                    if (responseXml != null) {
+                        logger.debug("服务端响应: {}", responseXml);
+                    } else {
+                        throw lastException != null ? lastException : new Exception("所有解密方法都失败");
+                    }
+                } catch (Exception e) {
+                    logger.warn("解密服务端响应失败", e);
+                    logger.debug("响应数据长度: {}, 前100字节: {}",
+                            response.getData() != null ? response.getData().length : 0,
+                            response.getData() != null && response.getData().length > 0
+                                    ? java.util.Arrays.toString(java.util.Arrays.copyOf(response.getData(), Math.min(100, response.getData().length)))
+                                    : "null");
+                }
+                return true;
+            }
+
+            return true;
+        } catch (Exception e) {
+            logger.error("发送能耗数据失败", e);
+            return false;
+        }
+    }
+
+    /**
+     * 发送数据包
+     *
+     * @param type     消息类型
+     * @param xmlData  XML数据
+     * @param encrypt  是否加密
+     */
+    private void sendPacket(byte type, String xmlData, boolean encrypt) throws IOException {
+        byte[] data;
+        if (encrypt) {
+            // 能耗数据需要AES加密
+            try {
+                data = AesUtil.encryptEnergyXml(xmlData, authKey);
+            } catch (Exception e) {
+                throw new IOException("加密能耗数据失败", e);
+            }
+        } else {
+            // 身份认证和心跳不加密
+            data = xmlData.getBytes(java.nio.charset.StandardCharsets.UTF_8);
+        }
+
+        NetworkPacket packet = new NetworkPacket();
+        packet.setType(type);
+        packet.setData(data);
+
+        byte[] packetBytes = packet.encode();
+        outputStream.write(packetBytes);
+        outputStream.flush();
+    }
+
+    /**
+     * 接收数据包
+     *
+     * @return 接收到的数据包
+     */
+    private NetworkPacket receivePacket() throws IOException {
+        // 先读取7字节(Head + Type + Length)
+        byte[] header = new byte[7];
+        int bytesRead = 0;
+        while (bytesRead < 7) {
+            int n = inputStream.read(header, bytesRead, 7 - bytesRead);
+            if (n == -1) {
+                throw new IOException("连接已关闭");
+            }
+            bytesRead += n;
+        }
+
+        // 解析Length
+        int length = ((header[3] & 0xFF) << 24) |
+                ((header[4] & 0xFF) << 16) |
+                ((header[5] & 0xFF) << 8) |
+                (header[6] & 0xFF);
+
+        // 读取Data
+        byte[] data = new byte[length];
+        bytesRead = 0;
+        while (bytesRead < length) {
+            int n = inputStream.read(data, bytesRead, length - bytesRead);
+            if (n == -1) {
+                throw new IOException("连接已关闭");
+            }
+            bytesRead += n;
+        }
+
+        // 组装完整数据包
+        byte[] packetBytes = new byte[7 + length];
+        System.arraycopy(header, 0, packetBytes, 0, 7);
+        System.arraycopy(data, 0, packetBytes, 7, length);
+
+        return NetworkPacket.decode(packetBytes);
+    }
+
+    /**
+     * 从XML中解析随机序列
+     */
+    private String parseSequenceFromXml(String xml) {
+        try {
+            Document document = DocumentHelper.parseText(xml);
+            Element root = document.getRootElement();
+            Element idValidate = root.element("id_validate");
+            if (idValidate != null) {
+                Element sequence = idValidate.element("sequence");
+                if (sequence != null) {
+                    return sequence.getTextTrim();
+                }
+            }
+        } catch (Exception e) {
+            logger.error("解析随机序列失败", e);
+        }
+        return null;
+    }
+
+    /**
+     * 从XML中解析认证结果
+     */
+    private boolean parseAuthResult(String xml) {
+        try {
+            Document document = DocumentHelper.parseText(xml);
+            Element root = document.getRootElement();
+            Element idValidate = root.element("id_validate");
+            if (idValidate != null && "result".equals(idValidate.attributeValue("operation"))) {
+                Element result = idValidate.element("result");
+                if (result != null) {
+                    return "pass".equals(result.getTextTrim());
+                }
+            }
+        } catch (Exception e) {
+            logger.error("解析认证结果失败", e);
+        }
+        return false;
+    }
+
+    public boolean isConnected() {
+        return connected.get() && socket != null && !socket.isClosed();
+    }
+
+    public boolean isAuthenticated() {
+        return authenticated.get();
+    }
+}

+ 31 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/DmpDeviceService.java

@@ -0,0 +1,31 @@
+package com.usky.ems.service;
+
+import com.usky.ems.domain.DmpDevice;
+
+import java.util.List;
+
+/**
+ * 设备信息服务接口
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+public interface DmpDeviceService {
+
+    /**
+     * 根据网关ID查询网关设备
+     *
+     * @param tenantId 租户ID
+     * @param productCode  产品编码
+     * @return 网关设备信息
+     */
+    List<DmpDevice> getGatewayByDeviceId(Integer tenantId, String productCode);
+
+    /**
+     * 根据网关UUID查询所有子设备
+     *
+     * @param gatewayUuid 网关UUID
+     * @return 子设备列表
+     */
+    List<DmpDevice> getDevicesByGatewayUuid(String gatewayUuid);
+}

+ 23 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EmsConsPlatformConfigService.java

@@ -0,0 +1,23 @@
+package com.usky.ems.service;
+
+import com.usky.ems.domain.EmsConsPlatformConfig;
+import com.usky.common.mybatis.core.CrudService;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 能耗_集成平台配置表 服务类
+ * </p>
+ *
+ * @author ya
+ * @since 2026-01-29
+ */
+public interface EmsConsPlatformConfigService extends CrudService<EmsConsPlatformConfig> {
+
+    /**
+     * 查询能耗平台信息
+     */
+    List<EmsConsPlatformConfig> getPlatformConfig();
+
+}

+ 20 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/EnergyDataService.java

@@ -0,0 +1,20 @@
+package com.usky.ems.service;
+
+/**
+ * 能耗数据服务接口
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+public interface EnergyDataService {
+
+    /**
+     * 定时推送能耗数据到能耗平台
+     */
+    void sendEnergyData();
+
+    /**
+     * 定时推送心跳数据到能耗平台
+     */
+    void sendEnergyHeartbeat();
+}

+ 30 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/TdengineService.java

@@ -0,0 +1,30 @@
+package com.usky.ems.service;
+
+import com.usky.ems.domain.TdengineData;
+
+import java.util.List;
+
+/**
+ * TDengine数据查询服务接口
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+public interface TdengineService {
+
+    /**
+     * 查询设备的最新数据
+     *
+     * @param deviceUuid 设备UUID
+     * @return 最新数据
+     */
+    TdengineData getLatestData(String deviceUuid);
+
+    /**
+     * 批量查询设备的最新数据
+     *
+     * @param deviceUuids 设备UUID列表
+     * @return 设备数据映射(key: deviceUuid, value: TdengineData)
+     */
+    java.util.Map<String, TdengineData> getLatestDataBatch(List<String> deviceUuids);
+}

+ 32 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/DmpDeviceServiceImpl.java

@@ -0,0 +1,32 @@
+package com.usky.ems.service.impl;
+
+import com.usky.ems.domain.DmpDevice;
+import com.usky.ems.mapper.DmpDeviceMapper;
+import com.usky.ems.service.DmpDeviceService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 设备信息服务实现类
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+@Service
+public class DmpDeviceServiceImpl implements DmpDeviceService {
+
+    @Autowired
+    private DmpDeviceMapper dmpDeviceMapper;
+
+    @Override
+    public List<DmpDevice> getGatewayByDeviceId(Integer tenantId, String productCode) {
+        return dmpDeviceMapper.selectGatewayByDeviceId(tenantId, productCode);
+    }
+
+    @Override
+    public List<DmpDevice> getDevicesByGatewayUuid(String gatewayUuid) {
+        return dmpDeviceMapper.selectDevicesByGatewayUuid(gatewayUuid);
+    }
+}

+ 29 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EmsConsPlatformConfigServiceImpl.java

@@ -0,0 +1,29 @@
+package com.usky.ems.service.impl;
+
+import com.usky.ems.domain.EmsConsPlatformConfig;
+import com.usky.ems.mapper.EmsConsPlatformConfigMapper;
+import com.usky.ems.service.EmsConsPlatformConfigService;
+import com.usky.common.mybatis.core.AbstractCrudService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 能耗_集成平台配置表 服务实现类
+ * </p>
+ *
+ * @author ya
+ * @since 2026-01-29
+ */
+@Service
+public class EmsConsPlatformConfigServiceImpl extends AbstractCrudService<EmsConsPlatformConfigMapper, EmsConsPlatformConfig> implements EmsConsPlatformConfigService {
+
+    @Autowired
+    private EmsConsPlatformConfigMapper emsConsPlatformConfigMapper;
+
+    public List<EmsConsPlatformConfig> getPlatformConfig(){
+        return emsConsPlatformConfigMapper.getPlatformConfig();
+    }
+}

+ 290 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/EnergyDataServiceImpl.java

@@ -0,0 +1,290 @@
+package com.usky.ems.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.usky.ems.domain.DmpDevice;
+import com.usky.ems.domain.EmsConsPlatformConfig;
+import com.usky.ems.domain.TdengineData;
+import com.usky.ems.protocol.TcpClient;
+import com.usky.ems.service.DmpDeviceService;
+import com.usky.ems.service.EmsConsPlatformConfigService;
+import com.usky.ems.service.EnergyDataService;
+import com.usky.ems.service.TdengineService;
+import com.usky.ems.util.XmlBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * 能耗数据服务实现类
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+@Service
+public class EnergyDataServiceImpl implements EnergyDataService {
+
+    private static final Logger logger = LoggerFactory.getLogger(EnergyDataServiceImpl.class);
+
+
+    private final DmpDeviceService dmpDeviceService;
+    private final TdengineService tdengineService;
+    private final EmsConsPlatformConfigService emsConsPlatformConfigService;
+
+    // TCP客户端连接池(每个网关一个连接)
+    private final Map<String, TcpClient> clientPool = new HashMap<>();
+
+    public EnergyDataServiceImpl(DmpDeviceService dmpDeviceService, TdengineService tdengineService, EmsConsPlatformConfigService emsConsPlatformConfigService) {
+        this.dmpDeviceService = dmpDeviceService;
+        this.tdengineService = tdengineService;
+        this.emsConsPlatformConfigService = emsConsPlatformConfigService;
+    }
+
+    @Override
+    public void sendEnergyData(){
+        logger.info("开始执行能耗数据推送任务");
+
+        List<EmsConsPlatformConfig> platformConfigList = emsConsPlatformConfigService.getPlatformConfig();
+        if(CollectionUtils.isNotEmpty(platformConfigList)){
+            for (EmsConsPlatformConfig platformConfig : platformConfigList) {
+                String serverHost = platformConfig.getPlatformIp();
+                int serverPort = platformConfig.getPlatformPort();
+                String authKey = platformConfig.getPasswd();
+                String buildingId = platformConfig.getBuildId();
+                Integer tenantId = platformConfig.getTenantId();
+                String productCode = platformConfig.getProductCode();
+
+
+                // 1. 查询网关设备
+                List<DmpDevice> gatewayList = dmpDeviceService.getGatewayByDeviceId(tenantId,productCode);
+                if (CollectionUtils.isEmpty(gatewayList)) {
+                    logger.error("{}产品下不存在网关设备", productCode);
+                }
+                for (DmpDevice dmpDevice : gatewayList) {
+                    String gatewayId = dmpDevice.getDeviceId();
+                    try {
+
+                        // 2. 查询网关下的所有电表设备
+                        List<DmpDevice> devices = dmpDeviceService.getDevicesByGatewayUuid(dmpDevice.getDeviceUuid());
+                        if (devices == null || devices.isEmpty()) {
+                            logger.warn("{}网关下没有电表设备", dmpDevice);
+                            continue;
+                        }
+
+                        // 3. 查询所有设备的最新数据
+                        List<String> deviceUuids = new ArrayList<>();
+                        for (DmpDevice device : devices) {
+                            if(!deviceUuids.contains(device.getDeviceUuid())){
+                                deviceUuids.add(device.getDeviceUuid());
+                            }
+
+                        }
+                        Map<String, TdengineData> dataMap = tdengineService.getLatestDataBatch(deviceUuids);
+
+                        // 4. 构建XML数据
+                        String xmlData = buildEnergyDataXml(buildingId, dmpDevice.getDeviceId(), devices, dataMap);
+
+                        // 5. 获取或创建TCP客户端连接
+                        TcpClient client = getOrCreateClient(serverHost, serverPort, authKey, buildingId, gatewayId);
+                        if (client == null) {
+                            logger.error("无法建立TCP连接: {}", gatewayId);
+                        }
+
+                        // 6. 发送数据
+                        boolean success = client.sendEnergyData(xmlData);
+
+                        if (success) {
+                            logger.info("能耗数据推送成功: gatewayId={}, deviceCount={}", gatewayId, devices.size());
+                        } else {
+                            logger.error("能耗数据推送失败: gatewayId={}", gatewayId);
+                        }
+
+                    } catch (Exception e) {
+                        logger.error("生成并推送能耗数据异常: gatewayId={}", gatewayId, e);
+                    }
+                }
+            }
+
+        }
+
+        logger.info("能耗数据推送任务执行完成");
+    }
+
+    @Override
+    public void sendEnergyHeartbeat(){
+        logger.debug("开始执行心跳任务");
+
+        List<EmsConsPlatformConfig> platformConfigList = emsConsPlatformConfigService.getPlatformConfig();
+        if(CollectionUtils.isNotEmpty(platformConfigList)){
+            for (EmsConsPlatformConfig platformConfig : platformConfigList) {
+                String serverHost = platformConfig.getPlatformIp();
+                int serverPort = platformConfig.getPlatformPort();
+                String authKey = platformConfig.getPasswd();
+                String buildingId = platformConfig.getBuildId();
+                Integer tenantId = platformConfig.getTenantId();
+                String productCode = platformConfig.getProductCode();
+
+
+                // 1. 查询网关设备
+                List<DmpDevice> gatewayList = dmpDeviceService.getGatewayByDeviceId(tenantId,productCode);
+                if (CollectionUtils.isEmpty(gatewayList)) {
+                    logger.error("{}产品下不存在网关设备", productCode);
+                }
+                for (DmpDevice dmpDevice : gatewayList) {
+                    String gatewayId = dmpDevice.getDeviceId();
+                    try {
+                        // 2. 获取TCP客户端连接
+                        TcpClient client = getOrCreateClient(serverHost, serverPort, authKey, buildingId, gatewayId);
+                        if (client == null || !client.isConnected() || !client.isAuthenticated()) {
+                            logger.warn("网关连接不存在或未认证,跳过心跳: gatewayId={}", gatewayId);
+                        }
+
+                        // 3. 发送心跳
+                        boolean success = client.sendHeartbeat(buildingId, gatewayId);
+
+                        if (success) {
+                            logger.debug("心跳发送成功: gatewayId={}", gatewayId);
+                        } else {
+                            logger.warn("心跳发送失败: gatewayId={}", gatewayId);
+                        }
+
+                    } catch (Exception e) {
+                        logger.error("发送心跳异常: gatewayId={}", gatewayId, e);
+                    }
+                }
+            }
+
+        }
+
+        logger.debug("心跳任务执行完成");
+    }
+
+    /**
+     * 构建能耗数据XML
+     */
+    private String buildEnergyDataXml(String buildingId, String gatewayId, List<DmpDevice> devices, Map<String, TdengineData> dataMap) {
+        String time = XmlBuilder.formatCurrentTime();
+
+        // 计算分项数据(总电量)
+        Map<String, Double> energyItems = new HashMap<>();
+        double totalEnergy = 0.0;
+//        for (TdengineData data : dataMap.values()) {
+//            if (data.getTotalActiveEnergyF() != null) {
+//                totalEnergy += data.getTotalActiveEnergyF();
+//            }
+//        }
+        energyItems.put("0", totalEnergy);
+
+        // 构建仪表数据
+        List<Map<String, Object>> meters = new ArrayList<>();
+        for (DmpDevice device : devices) {
+            TdengineData data = dataMap.get(device.getDeviceUuid());
+            if (data == null) {
+                continue;
+            }
+
+            Map<String, Object> meter = new HashMap<>();
+            meter.put("id", buildingId + device.getDeviceId().substring(device.getDeviceId().length() - 3));
+            meter.put("name", device.getDeviceName());
+
+            List<Map<String, String>> functions = new ArrayList<>();
+            
+            // 累计正向有功电量
+            if (data.getTotalActiveEnergyF() != null) {
+                functions.add(createFunction("WPP", String.valueOf(data.getTotalActiveEnergyF()), null));
+            }
+            
+            // 正向有功功率
+            if (data.getTotalActivePower() != null) {
+                functions.add(createFunction("Ps", String.valueOf(data.getTotalActivePower()), null));
+            }
+            
+            // 正向无功功率
+            if (data.getTotalReactiveEnergyF() != null) {
+                functions.add(createFunction("Qs", String.valueOf(data.getTotalReactiveEnergyF()), null));
+            }
+            
+            // A相电压
+            if (data.getVoltageA() != null) {
+                functions.add(createFunction("Ua", String.valueOf(data.getVoltageA()), null));
+            }
+            
+            // B相电压
+            if (data.getVoltageB() != null) {
+                functions.add(createFunction("Ub", String.valueOf(data.getVoltageB()), null));
+            }
+            
+            // C相电压
+            if (data.getVoltageC() != null) {
+                functions.add(createFunction("Uc", String.valueOf(data.getVoltageC()), null));
+            }
+            
+            // A相电流
+            if (data.getCurrentA() != null) {
+                functions.add(createFunction("Ia", String.valueOf(data.getCurrentA()), null));
+            }
+            
+            // B相电流
+            if (data.getCurrentB() != null) {
+                functions.add(createFunction("Ib", String.valueOf(data.getCurrentB()), null));
+            }
+            
+            // C相电流
+            if (data.getCurrentC() != null) {
+                functions.add(createFunction("Ic", String.valueOf(data.getCurrentC()), null));
+            }
+            
+            // 功率因数
+            if (data.getTotalPowerFactor() != null) {
+                functions.add(createFunction("PFs", String.valueOf(data.getTotalPowerFactor()), null));
+            }
+
+            meter.put("functions", functions);
+            meters.add(meter);
+        }
+
+        return XmlBuilder.buildEnergyDataRequest(buildingId, gatewayId, time, energyItems, meters);
+    }
+
+    /**
+     * 创建function节点数据
+     */
+    private Map<String, String> createFunction(String id, String value, String error) {
+        Map<String, String> function = new HashMap<>();
+        function.put("id", id);
+        function.put("value", value);
+        if (error != null) {
+            function.put("error", error);
+        }
+        return function;
+    }
+
+    /**
+     * 获取或创建TCP客户端
+     */
+    private synchronized TcpClient getOrCreateClient(String serverHost, int serverPort, String authKey, String buildingId, String gatewayId) {
+        TcpClient client = clientPool.get(gatewayId);
+        
+        if (client == null || !client.isConnected() || !client.isAuthenticated()) {
+            // 创建新连接
+            client = new TcpClient(serverHost, serverPort, authKey);
+            
+            // 建立连接
+            if (!client.connect()) {
+                return null;
+            }
+            
+            // 身份认证
+            if (!client.authenticate(buildingId, gatewayId)) {
+                client.close();
+                return null;
+            }
+            
+            clientPool.put(gatewayId, client);
+        }
+        
+        return client;
+    }
+}

+ 115 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/impl/TdengineServiceImpl.java

@@ -0,0 +1,115 @@
+package com.usky.ems.service.impl;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import com.usky.ems.domain.TdengineData;
+import com.usky.ems.service.TdengineService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.*;
+
+/**
+ * TDengine数据查询服务实现类
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+@Service
+@DS("taos")
+public class TdengineServiceImpl implements TdengineService {
+
+    private static final Logger logger = LoggerFactory.getLogger(TdengineServiceImpl.class);
+
+    @Autowired
+    private DataSource tdengineDataSource;
+
+    @Override
+    public TdengineData getLatestData(String deviceUuid) {
+        if (tdengineDataSource == null) {
+            logger.warn("TDengine数据源未配置");
+            return null;
+        }
+
+        // 子表名格式:_ + deviceUuid
+        String tableName = "_" + deviceUuid;
+        String sql = "SELECT ts, device_id, voltage_a, voltage_b, voltage_c, " +
+                     "current_a, current_b, current_c, activepowera, activepowerb, activepowerc, " +
+                     "totalActivePower, totalPowerFactor, totalActiveEnergyF, totalActiveEnergyR, " +
+                     "totalReactiveEnergyF, totalReactiveEnergyR " +
+                     "FROM " + tableName + " " +
+                     "ORDER BY ts DESC LIMIT 1";
+
+        try (Connection conn = tdengineDataSource.getConnection();
+             PreparedStatement stmt = conn.prepareStatement(sql)) {
+            
+            try (ResultSet rs = stmt.executeQuery()) {
+                if (rs.next()) {
+                    return mapResultSetToTdengineData(rs);
+                }
+            }
+        } catch (Exception e) {
+            logger.error("查询TDengine数据失败, deviceUuid: {}", deviceUuid, e);
+        }
+        
+        return null;
+    }
+
+    @Override
+    public Map<String, TdengineData> getLatestDataBatch(List<String> deviceUuids) {
+        Map<String, TdengineData> result = new HashMap<>();
+        
+        if (tdengineDataSource == null || deviceUuids == null || deviceUuids.isEmpty()) {
+            return result;
+        }
+
+        // 为每个设备查询最新数据
+        for (String deviceUuid : deviceUuids) {
+            TdengineData data = getLatestData(deviceUuid);
+            if (data != null) {
+                result.put(deviceUuid, data);
+            }
+        }
+        
+        return result;
+    }
+
+    /**
+     * 将ResultSet映射为TdengineData对象
+     */
+    private TdengineData mapResultSetToTdengineData(ResultSet rs) throws Exception {
+        TdengineData data = new TdengineData();
+        data.setTs(rs.getTimestamp("ts"));
+        data.setDeviceId(rs.getString("device_id"));
+        data.setVoltageA(getDouble(rs, "voltage_a"));
+        data.setVoltageB(getDouble(rs, "voltage_b"));
+        data.setVoltageC(getDouble(rs, "voltage_c"));
+        data.setCurrentA(getDouble(rs, "current_a"));
+        data.setCurrentB(getDouble(rs, "current_b"));
+        data.setCurrentC(getDouble(rs, "current_c"));
+        data.setActivepowera(getDouble(rs, "activepowera"));
+        data.setActivepowerb(getDouble(rs, "activepowerb"));
+        data.setActivepowerc(getDouble(rs, "activepowerc"));
+        data.setTotalActivePower(getDouble(rs, "totalactivepower"));
+        data.setTotalPowerFactor(getDouble(rs, "totalpowerfactor"));
+        data.setTotalActiveEnergyF(getDouble(rs, "totalactiveenergyf"));
+        data.setTotalActiveEnergyR(getDouble(rs, "totalactiveenergyr"));
+        data.setTotalReactiveEnergyF(getDouble(rs, "totalreactiveenergyf"));
+        data.setTotalReactiveEnergyR(getDouble(rs, "totalreactiveenergyr"));
+        return data;
+    }
+
+    private Double getDouble(ResultSet rs, String column) {
+        try {
+            double value = rs.getDouble(column);
+            return rs.wasNull() ? null : value;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+}

+ 11 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/service/vo/EnergyDataRequestVO.java

@@ -0,0 +1,11 @@
+package com.usky.ems.service.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class EnergyDataRequestVO implements Serializable {
+
+    private String gatewayId;
+}

+ 57 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/util/AesUtil.java

@@ -0,0 +1,57 @@
+package com.usky.ems.util;
+
+import cn.hutool.crypto.Mode;
+import cn.hutool.crypto.Padding;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import cn.hutool.crypto.symmetric.AES;
+import cn.hutool.crypto.Mode;
+import cn.hutool.crypto.Padding;
+
+/**
+ * AES加密工具类
+ * 采用CBC模式,PKCS5填充,密钥和向量相同
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+public class AesUtil {
+
+    /**
+     * 加密能耗XML数据
+     *
+     * @param xml XML字符串
+     * @param key 密钥
+     * @return 加密后的字节数组
+     * @throws Exception 加密异常
+     */
+    public static byte[] encryptEnergyXml(String xml, String key) throws Exception {
+        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+        byte[] uploadKeyBytes = key.getBytes();
+        SecretKeySpec secretKeySpec = new SecretKeySpec(uploadKeyBytes, "AES");
+        IvParameterSpec ivParameterSpec = new IvParameterSpec(uploadKeyBytes);
+        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
+        return cipher.doFinal(xml.getBytes(StandardCharsets.UTF_8));
+    }
+
+    /**
+     * 解密能耗XML数据
+     *
+     * @param encryptedData 加密后的字节数组
+     * @param key           密钥
+     * @return 解密后的XML字符串
+     * @throws Exception 解密异常
+     */
+    public static  String decrypt(String key, byte[] cipherText) {
+        try {
+            AES aes = new AES(Mode.CBC, Padding.PKCS5Padding,
+                    key.getBytes(), key.getBytes());
+            return aes.decryptStr(cipherText);
+        } catch (Exception e) {
+            throw new RuntimeException("密钥错误!");
+        }
+    }
+}

+ 40 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/util/Md5Util.java

@@ -0,0 +1,40 @@
+package com.usky.ems.util;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * MD5工具类
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+public class Md5Util {
+
+    /**
+     * 计算MD5值
+     *
+     * @param input 输入字符串
+     * @return MD5值(32位小写十六进制字符串)
+     */
+    public static String md5(String input) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] messageDigest = md.digest(input.getBytes());
+            
+            // 转换为十六进制字符串
+            StringBuilder hexString = new StringBuilder();
+            for (byte b : messageDigest) {
+                String hex = Integer.toHexString(0xff & b);
+                if (hex.length() == 1) {
+                    hexString.append('0');
+                }
+                hexString.append(hex);
+            }
+            
+            return hexString.toString();
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("MD5计算失败", e);
+        }
+    }
+}

+ 180 - 0
service-ems/service-ems-biz/src/main/java/com/usky/ems/util/XmlBuilder.java

@@ -0,0 +1,180 @@
+package com.usky.ems.util;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * XML报文构建工具类
+ *
+ * @author system
+ * @since 2024-01-01
+ */
+public class XmlBuilder {
+
+    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss");
+
+    /**
+     * 构建身份认证请求XML
+     *
+     * @param buildingId 建筑ID
+     * @param gatewayId  网关ID
+     * @return XML字符串
+     */
+    public static String buildAuthRequest(String buildingId, String gatewayId) {
+        Document document = DocumentHelper.createDocument();
+        Element root = document.addElement("root");
+        
+        // common节点
+        Element common = root.addElement("common");
+        common.addElement("building_id").setText(buildingId);
+        common.addElement("gateway_id").setText(gatewayId);
+        common.addElement("type").setText("id_validate");
+        
+        // id_validate节点
+        Element idValidate = root.addElement("id_validate");
+        idValidate.addAttribute("operation", "request");
+        
+        return document.asXML();
+    }
+
+    /**
+     * 构建身份认证MD5请求XML
+     *
+     * @param buildingId 建筑ID
+     * @param gatewayId  网关ID
+     * @param md5        MD5值
+     * @return XML字符串
+     */
+    public static String buildAuthMd5Request(String buildingId, String gatewayId, String md5) {
+        Document document = DocumentHelper.createDocument();
+        Element root = document.addElement("root");
+        
+        // common节点
+        Element common = root.addElement("common");
+        common.addElement("building_id").setText(buildingId);
+        common.addElement("gateway_id").setText(gatewayId);
+        common.addElement("type").setText("id_validate");
+        
+        // id_validate节点
+        Element idValidate = root.addElement("id_validate");
+        idValidate.addAttribute("operation", "md5");
+        idValidate.addElement("md5").setText(md5);
+        
+        return document.asXML();
+    }
+
+    /**
+     * 构建心跳请求XML
+     *
+     * @param buildingId 建筑ID
+     * @param gatewayId  网关ID
+     * @return XML字符串
+     */
+    public static String buildHeartbeatRequest(String buildingId, String gatewayId) {
+        Document document = DocumentHelper.createDocument();
+        Element root = document.addElement("root");
+        
+        // common节点
+        Element common = root.addElement("common");
+        common.addElement("building_id").setText(buildingId);
+        common.addElement("gateway_id").setText(gatewayId);
+        common.addElement("type").setText("heart_beat");
+        
+        // heart_beat节点
+        Element heartBeat = root.addElement("heart_beat");
+        heartBeat.addAttribute("operation", "notify");
+        
+        return document.asXML();
+    }
+
+    /**
+     * 构建能耗数据上报XML
+     *
+     * @param buildingId  建筑ID
+     * @param gatewayId   网关ID
+     * @param time        时间(yyyyMMddHHmmss格式)
+     * @param energyItems 分项数据
+     * @param meters      仪表数据列表
+     * @return XML字符串
+     */
+    public static String buildEnergyDataRequest(String buildingId, String gatewayId, String time,
+                                                 Map<String, Double> energyItems, List<Map<String, Object>> meters) {
+        Document document = DocumentHelper.createDocument();
+        Element root = document.addElement("root");
+        
+        // common节点
+        Element common = root.addElement("common");
+        common.addElement("building_id").setText(buildingId);
+        common.addElement("gateway_id").setText(gatewayId);
+        common.addElement("type").setText("energy_data");
+        
+        // data节点
+        Element data = root.addElement("data");
+        data.addAttribute("operation", "report");
+        data.addElement("time").setText(time);
+        
+        // energy_items节点
+        if (energyItems != null && !energyItems.isEmpty()) {
+            Element energyItemsElement = data.addElement("energy_items");
+            for (Map.Entry<String, Double> entry : energyItems.entrySet()) {
+                Element item = energyItemsElement.addElement("energy_item");
+                item.addAttribute("code", entry.getKey());
+                item.setText(String.valueOf(entry.getValue()));
+            }
+        }
+        
+        // meters节点
+        if (meters != null && !meters.isEmpty()) {
+            Element metersElement = data.addElement("meters");
+            metersElement.addAttribute("total", String.valueOf(meters.size()));
+            
+            for (Map<String, Object> meter : meters) {
+                Element meterElement = metersElement.addElement("meter");
+                meterElement.addAttribute("id", (String) meter.get("id"));
+                meterElement.addAttribute("name", (String) meter.get("name"));
+                
+                // function节点
+                @SuppressWarnings("unchecked")
+                List<Map<String, String>> functions = (List<Map<String, String>>) meter.get("functions");
+                if (functions != null) {
+                    for (Map<String, String> function : functions) {
+                        Element functionElement = meterElement.addElement("function");
+                        functionElement.addAttribute("id", function.get("id"));
+                        String error = function.get("error");
+                        if (error != null && !error.isEmpty()) {
+                            functionElement.addAttribute("error", error);
+                        }
+                        functionElement.setText(function.get("value"));
+                    }
+                }
+            }
+        }
+        
+        return document.asXML();
+    }
+
+    /**
+     * 格式化时间为yyyyMMddHHmmss格式
+     *
+     * @param date 日期
+     * @return 格式化后的字符串
+     */
+    public static String formatTime(Date date) {
+        return DATE_FORMAT.format(date);
+    }
+
+    /**
+     * 格式化当前时间为yyyyMMddHHmmss格式
+     *
+     * @return 格式化后的字符串
+     */
+    public static String formatCurrentTime() {
+        return formatTime(new Date());
+    }
+}

+ 25 - 0
service-ems/service-ems-biz/src/main/resources/bootstrap.yml

@@ -0,0 +1,25 @@
+# Tomcat
+server:
+  port: 9888
+
+# Spring
+spring:
+  application:
+    # 应用名称
+    name: service-ems
+  profiles:
+    # 环境配置
+    active: dev
+  cloud:
+    nacos:
+      discovery:
+        # 服务注册地址
+        server-addr: 192.168.10.165:8848
+      config:
+        # 配置中心地址
+        server-addr: 192.168.10.165:8848
+        # 配置文件格式
+        file-extension: yml
+        # 共享配置
+        shared-configs:
+          - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

+ 38 - 0
service-ems/service-ems-biz/src/main/resources/mapper/ems/DmpDeviceMapper.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.usky.ems.mapper.DmpDeviceMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.usky.ems.domain.DmpDevice">
+        <id column="id" property="id" />
+        <result column="device_id" property="deviceId" />
+        <result column="device_name" property="deviceName" />
+        <result column="device_type" property="deviceType" />
+        <result column="product_id" property="productId" />
+        <result column="product_code" property="productCode" />
+        <result column="device_uuid" property="deviceUuid" />
+        <result column="category_type" property="categoryType" />
+        <result column="gateway_uuid" property="gatewayUuid" />
+        <result column="build_id" property="buildId" />
+        <result column="delete_flag" property="deleteFlag" />
+        <result column="tenant_id" property="tenantId" />
+    </resultMap>
+
+    <!-- 根据网关ID查询网关设备 -->
+    <select id="selectGatewayByDeviceId" resultMap="BaseResultMap">
+        SELECT * FROM dmp_device
+        WHERE tenant_id = #{tenantId}
+        AND product_code = #{productCode}
+        AND category_type = 2
+        AND delete_flag = 0
+    </select>
+
+    <!-- 根据网关UUID查询所有子设备 -->
+    <select id="selectDevicesByGatewayUuid" resultMap="BaseResultMap">
+        SELECT device_id,device_name,device_type,product_id,product_code,IFNULL(TRIM(sim_code), device_uuid) AS device_uuid,category_type,gateway_uuid,build_id,delete_flag,tenant_id FROM dmp_device
+        WHERE gateway_uuid = #{gatewayUuid}
+        AND category_type = 3
+        AND delete_flag = 0
+    </select>
+
+</mapper>

+ 28 - 0
service-ems/service-ems-biz/src/main/resources/mapper/ems/EmsConsPlatformConfigMapper.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.usky.ems.mapper.EmsConsPlatformConfigMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.usky.ems.domain.EmsConsPlatformConfig">
+        <id column="id" property="id" />
+        <result column="platform_name" property="platformName" />
+        <result column="build_id" property="buildId" />
+        <result column="build_name" property="buildName" />
+        <result column="platform_ip" property="platformIp" />
+        <result column="platform_port" property="platformPort" />
+        <result column="username" property="username" />
+        <result column="passwd" property="passwd" />
+        <result column="created_by" property="createdBy" />
+        <result column="created_time" property="createdTime" />
+        <result column="updated_by" property="updatedBy" />
+        <result column="updated_time" property="updatedTime" />
+        <result column="tenant_id" property="tenantId" />
+        <result column="product_code" property="productCode" />
+        <result column="delete_flag" property="deleteFlag" />
+    </resultMap>
+    <select id="getPlatformConfig" resultMap="BaseResultMap">
+        SELECT * FROM ems_cons_platform_config
+        WHERE delete_flag = 0
+    </select>
+
+</mapper>

+ 2 - 2
service-iot/service-iot-biz/pom.xml

@@ -21,12 +21,12 @@
         </dependency>
         <dependency>
             <groupId>com.usky</groupId>
-            <artifactId>data-tsdb-proxy-api</artifactId>
+            <artifactId>service-tsdb-api</artifactId>
             <version>0.0.1</version>
         </dependency>
         <dependency>
             <groupId>com.usky</groupId>
-            <artifactId>data-transfer-api</artifactId>
+            <artifactId>service-transfer-api</artifactId>
             <version>0.0.1</version>
         </dependency>
 

+ 6 - 0
service-job/pom.xml

@@ -81,6 +81,12 @@
             <version>0.0.1</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>service-ems-api</artifactId>
+            <version>0.0.1</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
     <build>

+ 21 - 0
service-job/src/main/java/com/ruoyi/job/task/RyTask.java

@@ -3,7 +3,12 @@ package com.ruoyi.job.task;
 import com.usky.cdi.AlarmDataSyncTaskService;
 import com.usky.cdi.RemotecdiTaskService;
 import com.usky.common.core.utils.StringUtils;
+<<<<<<< HEAD
 import com.usky.meeting.RemoteMeetingService;
+=======
+import com.usky.demo.RemoteMeetingService;
+import com.usky.ems.RemoteEmsTaskService;
+>>>>>>> usky-zyj
 import com.usky.fire.RemoteFireService;
 import com.usky.iot.RemoteIotTaskService;
 import com.usky.pm.RemotePmService;
@@ -30,10 +35,14 @@ public class RyTask {
     private RemoteMeetingService remoteMeetingService;
 
     @Autowired
+<<<<<<< HEAD
     private RemotecdiTaskService remoteCdiTaskService;
 
     @Autowired
     private AlarmDataSyncTaskService alarmDataSyncTaskService;
+=======
+    private RemoteEmsTaskService remoteEmsTaskService;
+>>>>>>> usky-zyj
 
     public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) {
         System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
@@ -104,4 +113,16 @@ public class RyTask {
         alarmDataSyncTaskService.synchronizeAlarmData(tenantId, engineeringId, username, password, status);
     }
 
+    // 推送设备数据到能耗平台
+    public void sendEnergyData(){
+        System.out.println("sendEnergyData start......");
+        remoteEmsTaskService.sendEnergyData();
+    }
+
+    // 推送设备心跳到能耗平台
+    public void sendEnergyHeartbeat(){
+        System.out.println("sendEnergyHeartbeat start......");
+        remoteEmsTaskService.sendEnergyHeartbeat();
+    }
+
 }

+ 1 - 1
service-meeting/service-meeting-biz/pom.xml

@@ -19,7 +19,7 @@
         </dependency>
         <dependency>
             <groupId>com.usky</groupId>
-            <artifactId>data-transfer-api</artifactId>
+            <artifactId>service-transfer-api</artifactId>
             <version>0.0.1</version>
         </dependency>
         <dependency>

+ 17 - 0
service-transfer/pom.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>usky-modules</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-transfer</artifactId>
+    <packaging>pom</packaging>
+    <modules>
+        <module>service-transfer-biz</module>
+        <module>service-transfer-api</module>
+  </modules>
+
+</project>

+ 29 - 0
service-transfer/service-transfer-api/pom.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>service-transfer</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-transfer-api</artifactId>
+    <!-- SpringCloud Openfeign -->
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>usky-common-core</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+
+</project>

+ 33 - 0
service-transfer/service-transfer-api/src/main/java/com/usky/transfer/RemoteTransferService.java

@@ -0,0 +1,33 @@
+package com.usky.transfer;
+
+
+import com.usky.common.core.bean.ApiResult;
+import com.usky.transfer.domain.*;
+import com.usky.transfer.factory.RemoteTransferFallbackFactory;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+@FeignClient(contextId = "remoteTransferService", value = "service-transfer", fallbackFactory = RemoteTransferFallbackFactory.class)
+public interface RemoteTransferService {
+
+    /**
+     * 单个设备数据写入
+     * @return
+     */
+    @PostMapping("/sendDeviceData")
+    ApiResult<Void> sendDeviceData(@RequestBody DeviceDataWriteVO writeVO);
+
+    @PostMapping("/deleteProductCache")
+    ApiResult<Void> deleteProductCache();
+    @PostMapping("/deleteDeviceCache")
+    ApiResult<Void> deleteDeviceCache(@RequestParam("productCode") String productCode);
+
+    /**
+     * 下发单个设备控制命令
+     */
+    @PostMapping("/deviceControl")
+    Map<String,Object> deviceControl(@RequestParam("productCode") String productCode, @RequestParam(value = "deviceUuid",required = false) String deviceUuid, @RequestParam("commandStr") String commandStr,@RequestParam(value = "tenantId") Integer tenantId, @RequestParam(value = "userId") Long userId, @RequestParam(value = "userName") String userName);
+}

+ 41 - 0
service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/DeviceDataInfoVO.java

@@ -0,0 +1,41 @@
+package com.usky.transfer.domain;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class DeviceDataInfoVO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 属性值集合
+     */
+    private Map<String,Object> metrics;
+
+    /**
+     * 标签集合
+     */
+    private Map<String,Object> tags;
+
+    /**
+     * 设备编号
+     */
+    private String deviceUUId;
+
+    /**
+     * 产品编码
+     */
+    private String productCode;
+
+    /**
+     * 数据上报时间
+     */
+    private long timestamp;
+}

+ 43 - 0
service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/DeviceDataWriteVO.java

@@ -0,0 +1,43 @@
+package com.usky.transfer.domain;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class DeviceDataWriteVO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 产品编码
+     */
+    private String productCode;
+
+    /**
+     * 设备UUId
+     */
+    private String deviceUUId;
+
+    /**
+     * 数据上报时间
+     */
+    private Long timestamp;
+
+    /**
+     * 标签集合
+     */
+    private Map<String,String> tags;
+
+    /**
+     * 属性值集合
+     */
+    private Map<String,Object> metrics;
+
+}
+

+ 30 - 0
service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/HistoryRequestVO.java

@@ -0,0 +1,30 @@
+package com.usky.transfer.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class HistoryRequestVO implements Serializable {
+
+    /**
+     * 产品编码
+     */
+    private String  productCode;
+
+    /**
+     * 设备Id
+     */
+    private List<String> deviceId;
+
+    /**
+     * 开始时间
+     */
+    private String startTime;
+
+    /**
+     * 结束时间
+     */
+    private String endTime;
+}

+ 19 - 0
service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/HistoryResultVO.java

@@ -0,0 +1,19 @@
+package com.usky.transfer.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class HistoryResultVO implements Serializable {
+    private String deviceId;
+    private List<MetricVO> metrics;
+
+    public HistoryResultVO(String devId,List<MetricVO> metrics){
+        this.deviceId = devId;
+        this.metrics = metrics;
+    }
+
+}

+ 20 - 0
service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/LastRequestVO.java

@@ -0,0 +1,20 @@
+package com.usky.transfer.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class LastRequestVO implements Serializable {
+
+    /**
+     * 产品编码
+     */
+    private String  productCode;
+
+    /**
+     * 设备Id
+     */
+    private List<String> deviceId;
+}

+ 20 - 0
service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/LastResultVO.java

@@ -0,0 +1,20 @@
+package com.usky.transfer.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class LastResultVO implements Serializable {
+
+    private String deviceId;
+    private List<Map<String,Object>> metrics;
+
+    public LastResultVO(String devId,List<Map<String,Object>> metrics){
+        this.deviceId = devId;
+        this.metrics = metrics;
+    }
+
+}

+ 20 - 0
service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/MetricVO.java

@@ -0,0 +1,20 @@
+package com.usky.transfer.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class MetricVO implements Serializable {
+
+    private String metric;
+    private List<Map<String,Object>> metricItems;
+
+    public MetricVO(String metric,List<Map<String,Object>> metricItems){
+        this.metric = metric;
+        this.metricItems = metricItems;
+    }
+
+}

+ 18 - 0
service-transfer/service-transfer-api/src/main/java/com/usky/transfer/domain/MqttDeviceDataVO.java

@@ -0,0 +1,18 @@
+package com.usky.transfer.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class MqttDeviceDataVO implements Serializable {
+    /**
+     * topic
+     */
+    private String topic;
+
+    /**
+     * 设备数据体
+     */
+    private String deviceData;
+}

+ 52 - 0
service-transfer/service-transfer-api/src/main/java/com/usky/transfer/factory/RemoteTransferFallbackFactory.java

@@ -0,0 +1,52 @@
+package com.usky.transfer.factory;
+
+import com.usky.common.core.bean.ApiResult;
+import com.usky.common.core.exception.BusinessException;
+import com.usky.transfer.RemoteTransferService;
+import com.usky.transfer.domain.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.cloud.openfeign.FallbackFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 用户服务降级处理
+ *
+ * @author ruoyi
+ */
+@Component
+public class RemoteTransferFallbackFactory implements FallbackFactory<RemoteTransferService>
+{
+    private static final Logger log = LoggerFactory.getLogger(RemoteTransferFallbackFactory.class);
+
+    @Override
+    public RemoteTransferService create(Throwable throwable)
+    {
+        log.error("用户服务调用失败:{}", throwable.getMessage());
+        return new RemoteTransferService() {
+            @Override
+            public ApiResult<Void> sendDeviceData(DeviceDataWriteVO writeVO) {
+                throw new BusinessException(throwable.getMessage());
+            }
+            @Override
+            public ApiResult<Void> deleteProductCache() {
+                throw new BusinessException(throwable.getMessage());
+            }
+            @Override
+            public ApiResult<Void> deleteDeviceCache(String productCode) {
+                throw new BusinessException(throwable.getMessage());
+            }
+            @Override
+            public Map<String,Object> deviceControl(String productCode, String deviceUuid, String commandStr,Integer tenantId, Long userId, String userName) {
+                throw new BusinessException(throwable.getMessage());
+            }
+        };
+    }
+}

+ 112 - 0
service-transfer/service-transfer-biz/pom.xml

@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>service-transfer</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-transfer-biz</artifactId>
+    <dependencies>
+        <!--MQTT依赖-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-integration</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.integration</groupId>
+            <artifactId>spring-integration-mqtt</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>common-cloud-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!-- Pagehelper -->
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>ruoyi-common-swagger</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.28</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.rocketmq</groupId>
+            <artifactId>rocketmq-spring-boot-starter</artifactId>
+            <version>2.1.1</version>
+        </dependency>
+        <!-- InfluxDB Java 客户端,用于连接和操作 InfluxDB 数据库 -->
+        <dependency>
+            <groupId>org.influxdb</groupId>
+            <artifactId>influxdb-java</artifactId>
+            <version>2.23</version>
+        </dependency>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>service-transfer-api</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>service-tsdb-api</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.2.6.RELEASE</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>com.github.shalousun</groupId>
+                <artifactId>smart-doc-maven-plugin</artifactId>
+                <version>2.1.1</version>
+                <configuration>
+                    <!--指定生成文档的使用的配置文件,配置文件放在自己的项目中-->
+                    <configFile>./src/main/resources/smart-doc.json</configFile>
+                    <!--指定项目名称-->
+                    <projectName>test</projectName>
+                    <!--                    <excludes>-->
+                    <!--                        <exclude>com.bizmatics:product-service-provider</exclude>-->
+                    <!--                        <exclude>cn.afterturn:easypoi-web</exclude>-->
+                    <!--                    </excludes>-->
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 51 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/ApplicationRun.java

@@ -0,0 +1,51 @@
+package com.usky.transfer;
+
+
+import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
+import org.mybatis.spring.annotation.MapperScan;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.core.env.Environment;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * 应用启动模块
+ * 
+ */
+
+
+@EnableCaching
+@EnableCustomSwagger2
+//@EnableSwagger2
+@EnableFeignClients(basePackages = "com.usky")
+@MapperScan(value = "com.usky.transfer.mapper")
+@ComponentScan("com.usky")
+@SpringBootApplication
+public class ApplicationRun
+{
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationRun.class);
+
+    public static void main(String[] args) throws UnknownHostException {
+        ConfigurableApplicationContext application = SpringApplication.run(ApplicationRun.class, args);
+        Environment env = application.getEnvironment();
+        String ip = InetAddress.getLocalHost().getHostAddress();
+        String port = env.getProperty("server.port");
+        String path = env.getProperty("server.servlet.context-path");
+        LOGGER.info("\n----------------------------------------------------------\n\t" +
+                "Application is running! Access URLs:\n\t" +
+                "Local: \t\thttp://localhost:" + port + (null==path?"":path) + "/\n\t" +
+                "External: \thttp://" + ip + ":" + port + (null==path?"":path) + "/\n\t" +
+                "Api: \t\thttp://" + ip + ":" + port + (null==path?"":path) + "/swagger-ui/index.html\n\t" +
+                "----------------------------------------------------------");
+    }
+}

+ 107 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/MybatisGenerator.java

@@ -0,0 +1,107 @@
+package com.usky.transfer;//package com.usky.demo.controller;//package com.usky.dm.controller.web.business;//package com.usky.dm.controller.web;
+
+
+
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.generator.AutoGenerator;
+import com.baomidou.mybatisplus.generator.InjectionConfig;
+import com.baomidou.mybatisplus.generator.config.*;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ */
+public class MybatisGenerator {
+    public static void main(String[] args) {
+
+            shell("service-transfer","service-transfer-biz");
+    }
+
+    private static void shell(String parentName,String model) {
+
+        AutoGenerator mpg = new AutoGenerator();
+        //1、全局配置
+        GlobalConfig gc = new GlobalConfig();
+//        File file = new File(model);
+//        String path = file.getAbsolutePath();
+        String projectPath = System.getProperty("user.dir");
+        projectPath+="/"+parentName;
+        projectPath+="/"+model;
+        gc.setOutputDir(projectPath+ "/src/main/java");  //生成路径(一般都是生成在此项目的src/main/java下面)
+        //修改为自己的名字
+        gc.setAuthor("ya"); //设置作者
+        gc.setOpen(false);
+        gc.setFileOverride(true); //第二次生成会把第一次生成的覆盖掉
+        gc.setServiceName("%sService"); //生成的service接口名字首字母是否为I,这样设置就没有
+        gc.setBaseResultMap(true); //生成resultMap
+        mpg.setGlobalConfig(gc);
+
+        //2、数据源配置
+        //修改数据源
+        DataSourceConfig dsc = new DataSourceConfig();
+        dsc.setUrl("jdbc:mysql://192.168.10.165:3306/usky-cloud?useUnicode=true&serverTimezone=GMT&useSSL=false&characterEncoding=utf8");
+        dsc.setDriverName("com.mysql.jdbc.Driver");
+        dsc.setUsername("root");
+        dsc.setPassword("yt123456");
+        mpg.setDataSource(dsc);
+
+        // 3、包配置
+        PackageConfig pc = new PackageConfig();
+        pc.setParent("com.usky.transfer");
+        pc.setController("controller.web");
+        pc.setEntity("domain");
+        pc.setMapper("mapper");
+        pc.setService("service");
+        pc.setServiceImpl("service.impl");
+//        pc.setXml("mapper.demo");
+        //pc.setModuleName("test");
+        mpg.setPackageInfo(pc);
+
+        // 4、策略配置
+        StrategyConfig strategy = new StrategyConfig();
+        strategy.setNaming(NamingStrategy.underline_to_camel);
+        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
+        strategy.setSuperMapperClass("com.usky.common.mybatis.core.CrudMapper");
+        strategy.setSuperServiceClass("com.usky.common.mybatis.core.CrudService");
+        strategy.setSuperServiceImplClass("com.usky.common.mybatis.core.AbstractCrudService");
+        // strategy.setTablePrefix("t_"); // 表名前缀
+        strategy.setEntityLombokModel(true); //使用lombok
+        //修改自己想要生成的表
+        strategy.setInclude("dmp_product_attribute");  // 逆向工程使用的表   如果要生成多个,这里可以传入String[]
+        mpg.setStrategy(strategy);
+
+        // 关闭默认 xml 生成,调整生成 至 根目录
+        //修改对应的模块名称
+        TemplateConfig tc = new TemplateConfig();
+        // 自定义配置
+        InjectionConfig cfg = new InjectionConfig() {
+            @Override
+            public void initMap() {
+                // to do nothing
+            }
+        };
+        //如果模板引擎是 velocity
+        String templatePath = "/templates/mapper.xml.vm";
+        // 自定义输出配置
+        List<FileOutConfig> focList = new ArrayList<>();
+        // 自定义配置会被优先输出
+        String finalProjectPath = projectPath;
+        focList.add(new FileOutConfig(templatePath) {
+            @Override
+            public String outputFile(TableInfo tableInfo) {
+                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
+                return finalProjectPath + "/src/main/resources/mapper/transfer" + "/"
+                        + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
+            }
+        });
+        cfg.setFileOutConfigList(focList);
+        mpg.setCfg(cfg);
+        tc.setXml(null);
+        mpg.setTemplate(tc);
+        //5、执行
+        mpg.execute();
+    }
+}

+ 56 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/controller/api/DataTransferControllerApi.java

@@ -0,0 +1,56 @@
+package com.usky.transfer.controller.api;
+
+import com.usky.common.core.bean.ApiResult;
+import com.usky.transfer.RemoteTransferService;
+import com.usky.transfer.domain.*;
+import com.usky.transfer.service.DmpProductService;
+import com.usky.transfer.service.QueryInfluxdbDataService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+@RestController
+public class DataTransferControllerApi implements RemoteTransferService {
+
+    @Autowired
+    private QueryInfluxdbDataService queryInfluxdbDataService;
+    @Autowired
+    private DmpProductService dmpProductService;
+
+    /**
+     * 删除产品缓存信息
+     */
+    @Override
+    public ApiResult<Void> deleteProductCache(){
+        dmpProductService.deleteProductCache();
+        return ApiResult.success();
+    }
+
+    /**
+     * 删除设备缓存信息
+     */
+    @Override
+    public ApiResult<Void> deleteDeviceCache(String productCode){
+        dmpProductService.deleteDeviceCache(productCode);
+        return ApiResult.success();
+    }
+
+    /**
+     * 单个设备数据写入
+     * @return
+     */
+    @Override
+    public ApiResult<Void> sendDeviceData(DeviceDataWriteVO writeVO){
+        queryInfluxdbDataService.sendDeviceDataToMQ(writeVO);
+        return ApiResult.success();
+    }
+
+    /**
+     * 下发单个设备控制命令
+     */
+    @Override
+    public Map<String,Object> deviceControl(String productCode, String deviceUuid, String commandStr,Integer tenantId, Long userId, String userName){
+        return queryInfluxdbDataService.deviceControl(productCode, deviceUuid, commandStr, tenantId, userId, userName);
+    }
+}

+ 209 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/controller/web/QueryDeviceDataController.java

@@ -0,0 +1,209 @@
+package com.usky.transfer.controller.web;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.usky.common.core.bean.ApiResult;
+import com.usky.common.core.bean.CommonPage;
+import com.usky.common.core.exception.BusinessException;
+import com.usky.demo.RemoteTsdbProxyService;
+import com.usky.demo.domain.*;
+import com.usky.transfer.domain.DmpDevice;
+import com.usky.transfer.domain.DmpDeviceCommand;
+import com.usky.transfer.service.DmpDeviceCommandService;
+import com.usky.transfer.service.DmpDeviceService;
+import com.usky.transfer.service.vo.DeviceMapVO;
+import com.usky.transfer.service.vo.ExternalHistoryRequestVO;
+import com.usky.transfer.service.vo.ExternalLastRequestVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("dataQuery")
+public class QueryDeviceDataController {
+    @Autowired
+    private RemoteTsdbProxyService remoteTsdbProxyService;
+    @Autowired
+    private DmpDeviceService dmpDeviceService;
+    @Autowired
+    private DmpDeviceCommandService dmpDeviceCommandService;
+
+    @Cacheable(cacheNames = "externalDeviceList",sync = true)
+    public Map<String, DeviceMapVO> getExternalDeviceMap(){
+        Map<String,DeviceMapVO> deviceMap = new HashMap<>();
+        LambdaQueryWrapper<DmpDevice> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.eq(DmpDevice::getDeleteFlag,0)
+                .orderByDesc(DmpDevice::getId);
+        List<DmpDevice> list = dmpDeviceService.list(queryWrapper);
+        if(CollectionUtils.isNotEmpty(list)){
+            for (int i = 0; i < list.size(); i++) {
+                String deviceId = list.get(i).getDeviceId();
+                DeviceMapVO mapVO = new DeviceMapVO();
+                mapVO.setProductCode(list.get(i).getProductCode());
+                mapVO.setDeviceId(list.get(i).getDeviceId());
+                mapVO.setDeviceUuid(list.get(i).getDeviceUuid());
+                mapVO.setDeviceStatus(list.get(i).getServiceStatus());
+
+                deviceMap.put(deviceId,mapVO);
+            }
+        }
+
+        return deviceMap;
+    }
+
+//    /**
+//     * 单个设备实时数据查询(对外)
+//     * @param productCode
+//     * @param deviceId
+//     * @return
+//     */
+//    @GetMapping("/externalLast")
+//    public ApiResult<LastResultVO> queryLastDeviceData(@RequestParam(value = "productCode") String productCode,
+//                                                       @RequestParam(value = "deviceId") String deviceId){
+//        Map<String, DeviceMapVO> deviceMapList = this.getExternalDeviceMap();
+//        if(!deviceMapList.containsKey(deviceId)){
+//            throw new BusinessException(deviceId+"无设备信息");
+//        }
+//
+//        String deviceUUId = "";
+//
+//        for (Map.Entry<String,DeviceMapVO> map:deviceMapList.entrySet()) {
+//            String productCode1 = map.getValue().getProductCode();
+//            String deviceId1 = map.getKey();
+//            if((productCode.equals(productCode1)) && (deviceId.equals(deviceId1))){
+//                deviceUUId = map.getValue().getDeviceUuid();
+//            }
+//        }
+//
+//        return ApiResult.success(remoteTsdbProxyService.queryLastDeviceData(deviceUUId));
+//    }
+
+    /**
+     * 批量设备实时数据查询(对外)
+     * @param requestVO
+     * @return
+     */
+    @PostMapping("/externalLast")
+    public ApiResult<List<LastInnerResultVO>> queryLastDeviceData(@RequestBody ExternalLastRequestVO requestVO){
+        Map<String, DeviceMapVO> deviceMapList = this.getExternalDeviceMap();
+        LastInnerQueryVO reqVO = new LastInnerQueryVO();
+
+        String productCode = requestVO.getProductCode();
+        List<String> deviceIds = requestVO.getDeviceId();
+        List<String> deviceUUIds = new ArrayList<>();
+        for (int i = 0; i < deviceIds.size(); i++) {
+            String deviceId = deviceIds.get(i);
+            if(!deviceMapList.containsKey(deviceId)){
+                throw new BusinessException(deviceId+"无设备信息");
+            }
+
+            for (Map.Entry<String,DeviceMapVO> map:deviceMapList.entrySet()) {
+                String productCode1 = map.getValue().getProductCode();
+                String deviceId1 = map.getKey();
+                if((productCode.equals(productCode1)) && (deviceId.equals(deviceId1))){
+                    deviceUUIds.add(map.getValue().getDeviceUuid());
+
+                }
+            }
+        }
+        reqVO.setDeviceuuid(deviceUUIds);
+
+        return ApiResult.success(remoteTsdbProxyService.queryLastDeviceData(reqVO));
+    }
+
+//    /**
+//     * 单个设备历史数据查询(对外)
+//     * @param productCode
+//     * @param deviceId
+//     * @param startTime
+//     * @param endTime
+//     * @return
+//     */
+//    @GetMapping("/externalHistory")
+//    public ApiResult<HistoryResultVO> queryHistoryDeviceData(@RequestParam(value = "productCode") String productCode,
+//                                                             @RequestParam(value = "deviceId") String deviceId,
+//                                                             @RequestParam(value = "startTime") String startTime,
+//                                                             @RequestParam(value = "endTime") String endTime){
+//        Map<String, DeviceMapVO> deviceMapList = this.getExternalDeviceMap();
+//        if(!deviceMapList.containsKey(deviceId)){
+//            throw new BusinessException(deviceId+"无设备信息");
+//        }
+//
+//        String deviceUUId = "";
+//
+//        for (Map.Entry<String,DeviceMapVO> map:deviceMapList.entrySet()) {
+//            String productCode1 = map.getValue().getProductCode();
+//            String deviceId1 = map.getKey();
+//            if((productCode.equals(productCode1)) && (deviceId.equals(deviceId1))){
+//                deviceUUId = map.getValue().getDeviceUuid();
+//            }
+//        }
+//
+//        return ApiResult.success(remoteTsdbProxyService.queryHistoryDeviceData(deviceUUId,startTime,endTime));
+//    }
+
+    /**
+     * 批量设备历史数据查询(对外)
+     * @param requestVO
+     * @return
+     */
+    @PostMapping("/externalHistory")
+    public ApiResult<List<HistorysInnerResultVO>> queryHistoryDeviceData(@RequestBody ExternalHistoryRequestVO requestVO){
+        Map<String, DeviceMapVO> deviceMapList = this.getExternalDeviceMap();
+        HistorysInnerRequestVO reqVO = new HistorysInnerRequestVO();
+
+        String productCode = requestVO.getProductCode();
+        List<String> deviceIds = requestVO.getDeviceId();
+        List<String> deviceUUIds = new ArrayList<>();
+        for (int i = 0; i < deviceIds.size(); i++) {
+            String deviceId = deviceIds.get(i);
+            if(!deviceMapList.containsKey(deviceId)){
+                throw new BusinessException(deviceId+"无设备信息");
+            }
+
+            for (Map.Entry<String,DeviceMapVO> map:deviceMapList.entrySet()) {
+                String productCode1 = map.getValue().getProductCode();
+                String deviceId1 = map.getKey();
+                if((productCode.equals(productCode1)) && (deviceId.equals(deviceId1))){
+                    deviceUUIds.add(map.getValue().getDeviceUuid());
+
+                }
+            }
+        }
+        reqVO.setDeviceuuid(deviceUUIds);
+        reqVO.setStartTime(requestVO.getStartTime());
+        reqVO.setEndTime(requestVO.getEndTime());
+
+        return ApiResult.success(remoteTsdbProxyService.queryHistoryDeviceData(reqVO));
+    }
+
+    /**
+     * 指令记录
+     * @param commandStatus
+     * @param productCode
+     * @param deviceId
+     * @param userName
+     * @param startTime
+     * @param endTime
+     * @param pageNum
+     * @param pageSize
+     * @return
+     */
+    @GetMapping("deviceCommandRecord")
+    public ApiResult<CommonPage<DmpDeviceCommand>> deviceCommandRecord(@RequestParam(value = "commandStatus",required = false) Integer commandStatus,
+                                                                       @RequestParam(value = "productCode",required = false) String productCode,
+                                                                       @RequestParam(value = "deviceUuid",required = false) String deviceUuid,
+                                                                       @RequestParam(value = "userName",required = false) String userName,
+                                                                       @RequestParam(value = "startTime",required = false) String startTime,
+                                                                       @RequestParam(value = "endTime",required = false) String endTime,
+                                                                       @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
+                                                                       @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize){
+        return ApiResult.success(dmpDeviceCommandService.deviceCommandRecord(commandStatus,productCode,deviceUuid,userName,startTime,endTime,pageNum,pageSize));
+    }
+}

+ 43 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/controller/web/SendMessageController.java

@@ -0,0 +1,43 @@
+package com.usky.transfer.controller.web;
+
+import com.usky.common.core.bean.ApiResult;
+import com.usky.transfer.domain.DeviceDataWriteVO;
+import com.usky.transfer.domain.MqttDeviceDataVO;
+import com.usky.transfer.service.QueryInfluxdbDataService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * @Author :
+ * @CreateTime : 2024/2/29
+ * @Description :
+ **/
+@RestController
+@RequestMapping("/dataWrite")
+public class SendMessageController {
+    @Autowired
+    private QueryInfluxdbDataService queryInfluxdbDataService;
+
+    /**
+     * 单个设备数据写入
+     * @return
+     */
+    @PostMapping("/sendDeviceData")
+    public ApiResult<Map<String,Object>> sendDeviceData(@RequestBody DeviceDataWriteVO writeVO){
+        return ApiResult.success(queryInfluxdbDataService.sendDeviceDataToMQ(writeVO));
+    }
+
+    /**
+     * MQTT设备数据上报调试
+     * @param mqttDeviceDataVO
+     * @return
+     */
+    @PostMapping("/mqttDeviceData")
+    public ApiResult<Map<String,Object>> mqttDeviceData(@RequestBody MqttDeviceDataVO mqttDeviceDataVO){
+        return ApiResult.success(queryInfluxdbDataService.mqttDeviceData(mqttDeviceDataVO));
+    }
+
+
+}

+ 151 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/domain/DmpDevice.java

@@ -0,0 +1,151 @@
+package com.usky.transfer.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 设备信息表
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-19
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class DmpDevice implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 设备ID;设备注册时系统自动生成一个唯一编号
+     */
+    private String deviceId;
+
+    /**
+     * 设备名称
+     */
+    private String deviceName;
+
+    /**
+     * 设备类型(501、监控系统  502、门禁系统  503、梯控系统  504、机房系统  509、环境系统  510、照明系统)
+     */
+    private Integer deviceType;
+
+    /**
+     * 产品ID
+     */
+    private Integer productId;
+
+    /**
+     * 物联网卡号
+     */
+    private String simCode;
+
+    /**
+     * 国际移动用户识别码
+     */
+    private String imsiCode;
+
+    /**
+     * 自动订阅标识(0:否,1:是)
+     */
+    private Integer subscribeFlag;
+
+    /**
+     * 节点类型
+     */
+    private Integer nodeType;
+
+    /**
+     * 分组id
+     */
+    private Integer groupId;
+
+    /**
+     * 删除标识
+     */
+    private Integer deleteFlag;
+
+    /**
+     * 创建人
+     */
+    private String createdBy;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createdTime;
+
+    /**
+     * 更新人
+     */
+    private String updatedBy;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updatedTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+
+    /**
+     * 单位编号
+     */
+    private String companyCode;
+
+    /**
+     * 安装位置
+     */
+    private String installAddress;
+
+    /**
+     * 业务状态;1:未激活,2:已激活,3:禁用
+     */
+    private Integer serviceStatus;
+
+    /**
+     * 产品编码
+     */
+    private String productCode;
+
+    /**
+     * 设备uuid
+     */
+    private String deviceUuid;
+
+    /**
+     * 经度
+     */
+    private String longitude;
+
+    /**
+     * 纬度
+     */
+    private String latitude;
+
+    /**
+     * 设备所属类型(1、普通设备  2、网关设备  3、网关子设备)
+     */
+    private Integer categoryType;
+
+    /**
+     * 所属网关
+     */
+    private String gatewayUuid;
+
+
+}

+ 96 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/domain/DmpDeviceCommand.java

@@ -0,0 +1,96 @@
+package com.usky.transfer.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 创建下发命令历史记录表
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-24
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class DmpDeviceCommand implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 产品编码
+     */
+    private String productCode;
+
+    /**
+     * 设备Uuid
+     */
+    private String deviceUuid;
+
+    /**
+     * 下发命令内容
+     */
+    private String commandContent;
+
+    /**
+     * 下发命令响应内容
+     */
+    private String commandResponse;
+
+    /**
+     * 命令状态;0 命令执行中,1 命令成功,2 命令失败
+     */
+    private Integer commandStatus;
+
+    /**
+     * 创建人
+     */
+    private String createdBy;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createdTime;
+
+    /**
+     * 更新人
+     */
+    private String updatedBy;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updatedTime;
+
+    /**
+     * 组织结构ID
+     */
+    private Integer deptId;
+
+    /**
+     * 租户ID
+     */
+    private Integer tenantId;
+
+    /**
+     * 操作人
+     */
+    private String userName;
+
+    /**
+     * 操作人Id
+     */
+    private Long userId;
+
+
+}

+ 66 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/domain/DmpDeviceStatus.java

@@ -0,0 +1,66 @@
+package com.usky.transfer.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 设备状态表
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-19
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class DmpDeviceStatus implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 设备ID;设备注册时系统自动生成一个唯一编号
+     */
+    private String deviceId;
+
+    /**
+     * 产品ID
+     */
+    private Integer productId;
+
+    /**
+     * 设备状态;1:在线,2:离线
+     */
+    private Integer deviceStatus;
+
+    /**
+     * 最后上线时间
+     */
+    private LocalDateTime lastOnlineTime;
+
+    /**
+     * 最后离线时间
+     */
+    private LocalDateTime lastOfflineTime;
+
+    /**
+     * 产品编码
+     */
+    private String productCode;
+
+    /**
+     * 设备uuid
+     */
+    private String deviceUuid;
+
+
+}

+ 141 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/domain/DmpProduct.java

@@ -0,0 +1,141 @@
+package com.usky.transfer.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 产品信息表
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-19
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class DmpProduct implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 产品名称
+     */
+    private String productName;
+
+    /**
+     * 接入方式(1、设备直连  2、网关接入)
+     */
+    private Integer accessMode;
+
+    /**
+     * 网络类型(1、WIFI  2、移动蜂窝数据 3、NB-IoT 4、以太网)
+     */
+    private Integer networkType;
+
+    /**
+     * 设备类型(501、监控系统  502、门禁系统  503、梯控系统  504、机房系统  509、环境系统  510、照明系统)
+     */
+    private Integer deviceType;
+
+    /**
+     * 通信协议(1、MQTT  2、TCP设备直连 3、HTTP)
+     */
+    private Integer comProtocol;
+
+    /**
+     * 认证方式
+     */
+    private String authMode;
+
+    /**
+     * 设备型号
+     */
+    private String deviceModel;
+
+    /**
+     * 产品描述
+     */
+    private String productDescribe;
+
+    /**
+     * 厂家名称
+     */
+    private String factoryName;
+
+    /**
+     * 厂家联系人
+     */
+    private String factoryPerson;
+
+    /**
+     * 厂家联系电话
+     */
+    private String factoryPhone;
+
+    /**
+     * 资质证书1
+     */
+    private String certificateUrl1;
+
+    /**
+     * 资质证书2
+     */
+    private String certificateUrl2;
+
+    /**
+     * 资质证书3
+     */
+    private String certificateUrl3;
+
+    /**
+     * 协议文档
+     */
+    private String agreementUrl;
+
+    /**
+     * 删除标识
+     */
+    private Integer deleteFlag;
+
+    /**
+     * 创建人
+     */
+    private String createdBy;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createdTime;
+
+    /**
+     * 更新人
+     */
+    private String updatedBy;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updatedTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+
+    /**
+     * 产品编码
+     */
+    private String productCode;
+
+
+}

+ 137 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/domain/DmpProductAttribute.java

@@ -0,0 +1,137 @@
+package com.usky.transfer.domain;
+
+import java.math.BigDecimal;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 产品属性表
+ * </p>
+ *
+ * @author ya
+ * @since 2025-02-10
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class DmpProductAttribute implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 产品ID
+     */
+    private Integer productId;
+
+    /**
+     * 属性名称
+     */
+    private String attributeName;
+
+    /**
+     * 属性标识
+     */
+    private String attributeCode;
+
+    /**
+     * 对应端口/属性ID
+     */
+    private Integer attributePort;
+
+    /**
+     * 属性类型;1:必选,2:可选
+     */
+    private Integer attributeType;
+
+    /**
+     * 数据类型(1、数值型 2、字符型 3、bool型)
+     */
+    private Integer dataType;
+
+    /**
+     * 绑定状态;1:已绑定,2:未绑定
+     */
+    private Integer bindStatus;
+
+    /**
+     * 长度
+     */
+    private Integer attributeLength;
+
+    /**
+     * 单位
+     */
+    private String attributeUnit;
+
+    /**
+     * 最大值
+     */
+    private BigDecimal maximum;
+
+    /**
+     * 最小值
+     */
+    private BigDecimal minimum;
+
+    /**
+     * 时间格式
+     */
+    private String timeFormat;
+
+    /**
+     * 布尔值false
+     */
+    private String boolFalse;
+
+    /**
+     * 布尔值true
+     */
+    private String boolTrue;
+
+    /**
+     * 删除标识;0:未删除,1:已删除
+     */
+    private Integer deleteFlag;
+
+    /**
+     * 描述
+     */
+    private String attributeDescribe;
+
+    /**
+     * 创建人
+     */
+    private String createdBy;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createdTime;
+
+    /**
+     * 更新人
+     */
+    private String updatedBy;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updatedTime;
+
+    /**
+     * 租户号
+     */
+    private Integer tenantId;
+
+
+}

+ 27 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/domain/QueryInfluxdbData.java

@@ -0,0 +1,27 @@
+package com.usky.transfer.domain;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author ya
+ * @since 2024-07-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class QueryInfluxdbData implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+
+    private String influxdbName;
+
+
+}

+ 16 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/mapper/DmpDeviceCommandMapper.java

@@ -0,0 +1,16 @@
+package com.usky.transfer.mapper;
+
+import com.usky.transfer.domain.DmpDeviceCommand;
+import com.usky.common.mybatis.core.CrudMapper;
+
+/**
+ * <p>
+ * 创建下发命令历史记录表 Mapper 接口
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-24
+ */
+public interface DmpDeviceCommandMapper extends CrudMapper<DmpDeviceCommand> {
+
+}

+ 16 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/mapper/DmpDeviceMapper.java

@@ -0,0 +1,16 @@
+package com.usky.transfer.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.transfer.domain.DmpDevice;
+
+/**
+ * <p>
+ * 设备信息表 Mapper 接口
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-19
+ */
+public interface DmpDeviceMapper extends CrudMapper<DmpDevice> {
+
+}

+ 19 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/mapper/DmpDeviceStatusMapper.java

@@ -0,0 +1,19 @@
+package com.usky.transfer.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.transfer.domain.DmpDeviceStatus;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * <p>
+ * 设备状态表 Mapper 接口
+ * </p>
+ *
+ * @author ya
+ * @since 2022-10-08
+ */
+public interface DmpDeviceStatusMapper extends CrudMapper<DmpDeviceStatus> {
+}

+ 16 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/mapper/DmpProductAttributeMapper.java

@@ -0,0 +1,16 @@
+package com.usky.transfer.mapper;
+
+import com.usky.transfer.domain.DmpProductAttribute;
+import com.usky.common.mybatis.core.CrudMapper;
+
+/**
+ * <p>
+ * 产品属性表 Mapper 接口
+ * </p>
+ *
+ * @author ya
+ * @since 2025-02-10
+ */
+public interface DmpProductAttributeMapper extends CrudMapper<DmpProductAttribute> {
+
+}

+ 16 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/mapper/DmpProductMapper.java

@@ -0,0 +1,16 @@
+package com.usky.transfer.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.transfer.domain.DmpProduct;
+
+/**
+ * <p>
+ * 产品信息表 Mapper 接口
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-19
+ */
+public interface DmpProductMapper extends CrudMapper<DmpProduct> {
+
+}

+ 16 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/mapper/QueryInfluxdbDataMapper.java

@@ -0,0 +1,16 @@
+package com.usky.transfer.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.transfer.domain.QueryInfluxdbData;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author ya
+ * @since 2024-07-29
+ */
+public interface QueryInfluxdbDataMapper extends CrudMapper<QueryInfluxdbData> {
+
+}

+ 19 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/DmpDeviceCommandService.java

@@ -0,0 +1,19 @@
+package com.usky.transfer.service;
+
+import com.usky.common.core.bean.CommonPage;
+import com.usky.transfer.domain.DmpDeviceCommand;
+import com.usky.common.mybatis.core.CrudService;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * <p>
+ * 创建下发命令历史记录表 服务类
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-24
+ */
+public interface DmpDeviceCommandService extends CrudService<DmpDeviceCommand> {
+
+    CommonPage<DmpDeviceCommand> deviceCommandRecord(Integer commandStatus,String productCode,String deviceUuid,String userName,String startTime,String endTime,Integer pageNum,Integer pageSize);
+}

+ 16 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/DmpDeviceService.java

@@ -0,0 +1,16 @@
+package com.usky.transfer.service;
+
+import com.usky.transfer.domain.DmpDevice;
+import com.usky.common.mybatis.core.CrudService;
+
+/**
+ * <p>
+ * 设备信息表 服务类
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-19
+ */
+public interface DmpDeviceService extends CrudService<DmpDevice> {
+
+}

+ 16 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/DmpDeviceStatusService.java

@@ -0,0 +1,16 @@
+package com.usky.transfer.service;
+
+import com.usky.transfer.domain.DmpDeviceStatus;
+import com.usky.common.mybatis.core.CrudService;
+
+/**
+ * <p>
+ * 设备状态表 服务类
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-19
+ */
+public interface DmpDeviceStatusService extends CrudService<DmpDeviceStatus> {
+
+}

+ 16 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/DmpProductAttributeService.java

@@ -0,0 +1,16 @@
+package com.usky.transfer.service;
+
+import com.usky.transfer.domain.DmpProductAttribute;
+import com.usky.common.mybatis.core.CrudService;
+
+/**
+ * <p>
+ * 产品属性表 服务类
+ * </p>
+ *
+ * @author ya
+ * @since 2025-02-10
+ */
+public interface DmpProductAttributeService extends CrudService<DmpProductAttribute> {
+
+}

+ 24 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/DmpProductService.java

@@ -0,0 +1,24 @@
+package com.usky.transfer.service;
+
+import com.usky.transfer.domain.DmpProduct;
+import com.usky.common.mybatis.core.CrudService;
+import com.usky.transfer.service.vo.DeviceMapVO;
+import com.usky.transfer.service.vo.ProductMapVO;
+
+import java.util.Map;
+
+/**
+ * <p>
+ * 产品信息表 服务类
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-19
+ */
+public interface DmpProductService extends CrudService<DmpProduct> {
+    void deleteProductCache();
+    void deleteDeviceCache(String productCode);
+
+    Map<String, ProductMapVO> getProductMap();
+    Map<String, DeviceMapVO> getDeviceMap(String productCode);
+}

+ 26 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/QueryInfluxdbDataService.java

@@ -0,0 +1,26 @@
+package com.usky.transfer.service;
+
+import com.usky.transfer.domain.*;
+import com.usky.common.mybatis.core.CrudService;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author ya
+ * @since 2024-07-29
+ */
+public interface QueryInfluxdbDataService extends CrudService<QueryInfluxdbData> {
+
+    Map<String,Object> sendDeviceDataToMQ(DeviceDataWriteVO writeVO);
+
+    Map<String,Object> mqttDeviceData(MqttDeviceDataVO mqttDeviceDataVO);
+
+    Map<String,Object> deviceControl(String productCode, String deviceUuid, String commandStr,Integer tenantId, Long userId, String userName);
+
+}

+ 12 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/config/CodeCache.java

@@ -0,0 +1,12 @@
+package com.usky.transfer.service.config;
+
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class CodeCache {
+    public static List<String> tablesList = new ArrayList<>();
+
+}

+ 62 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/config/TsdbConfig.java

@@ -0,0 +1,62 @@
+package com.usky.transfer.service.config;
+
+import com.usky.common.core.util.StringUtils;
+import okhttp3.OkHttpClient;
+import org.influxdb.InfluxDB;
+import org.influxdb.InfluxDBFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.concurrent.TimeUnit;
+
+@Configuration
+public class TsdbConfig {
+
+    @Value("${spring.influx.url}")
+    private String influxDBUrl;
+
+    @Value("${spring.influx.username}")
+    private String userName;
+
+    @Value("${spring.influx.password}")
+    private String password;
+    @Value("${spring.influx.retention_policy}")
+    private String retention_policy;
+
+    @Value("${spring.influx.database}")
+    private String database;
+
+    @Bean
+    public InfluxDB influxdb() {
+        InfluxDB influxDB = null;
+        if (StringUtils.isEmpty(userName)) {
+            influxDB = InfluxDBFactory.connect(influxDBUrl);
+        } else {
+            OkHttpClient.Builder client = new OkHttpClient.Builder();
+            client.readTimeout(300, TimeUnit.SECONDS);
+            influxDB = InfluxDBFactory.connect(influxDBUrl, userName, password, client);
+        }
+        try {
+
+            /**
+             * 异步插入:
+             * enableBatch这里第一个是point的个数,第二个是时间,单位毫秒
+             * point的个数和时间是联合使用的,如果满100条或者60 * 1000毫秒
+             * 满足任何一个条件就会发送一次写的请求。
+             */
+//            influxDB.setDatabase(database).enableBatch(100, 1000 * 60, TimeUnit.MILLISECONDS);
+            influxDB.setDatabase(database);
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            //设置默认策略
+            this.retention_policy = retention_policy == null || "".equals(retention_policy) ? "autogen" : retention_policy;
+            influxDB.setRetentionPolicy(retention_policy);
+        }
+        //设置日志输出级别
+        influxDB.setLogLevel(InfluxDB.LogLevel.BASIC);
+        return influxDB;
+    }
+
+}

+ 50 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/config/mqtt/MqttBaseConfig.java

@@ -0,0 +1,50 @@
+package com.usky.transfer.service.config.mqtt;
+
+import lombok.Data;
+import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
+import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
+import org.springframework.stereotype.Component;
+
+@ConditionalOnProperty(prefix = "mqtt", value = {"enabled"}, havingValue = "true")
+@Data
+@Component
+@ConfigurationProperties(prefix = "mqtt")
+public class MqttBaseConfig {
+
+	@Value("${mqtt.username}")
+	private String username;
+
+	@Value("${mqtt.password}")
+	private String password;
+
+	@Value("${mqtt.url}")
+	private String hostUrl;
+
+	@Value("${mqtt.sub-topics}")
+	private String msgTopic;
+
+	@Value("${mqtt.keep-alive-interval}")
+	//心跳间隔
+	private int keepAliveInterval;
+	@Value("${mqtt.completionTimeout}")
+	//心跳间隔
+	private int completionTimeout;
+
+
+	@Bean
+	public MqttPahoClientFactory mqttClientFactory() {
+		DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
+		MqttConnectOptions options = new MqttConnectOptions();
+		options.setServerURIs(new String[]{this.getHostUrl()});
+		options.setUserName(this.getUsername());
+		options.setPassword(this.getPassword().toCharArray());
+		factory.setConnectionOptions(options);
+		return factory;
+	}
+
+}

+ 56 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/config/mqtt/MqttInConfig.java

@@ -0,0 +1,56 @@
+package com.usky.transfer.service.config.mqtt;
+
+import com.usky.transfer.service.enums.TopListener;
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.core.MessageProducer;
+import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
+import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author yq
+ * @date 2021/11/1 16:37
+ */
+@ConditionalOnProperty(prefix = "mqtt", value = {"enabled"}, havingValue = "true")
+@Configuration
+public class MqttInConfig {
+
+    @Autowired
+    private MqttBaseConfig mqttBaseConfig;
+
+    public static final String CHANNEL_NAME_INPUT = "mqttInputChannel";
+
+    @Bean(name = CHANNEL_NAME_INPUT)
+    public MessageChannel mqttInputChannel() {
+        return new DirectChannel();
+    }
+
+
+    /**
+     * 消息订阅绑定-消费者
+     *
+     * @return
+     */
+    @Bean
+    public MessageProducer inbound() {
+        String[] tops = mqttBaseConfig.getMsgTopic().split(",");
+        String clientId = "h-transfer-mqtt-in-" + System.currentTimeMillis();
+        MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(clientId,
+                mqttBaseConfig.mqttClientFactory(), tops);
+        adapter.setCompletionTimeout(mqttBaseConfig.getCompletionTimeout());
+        adapter.setConverter(new DefaultPahoMessageConverter());
+        adapter.setQos(2);
+        adapter.setOutputChannel(mqttInputChannel());
+        return adapter;
+    }
+}

+ 84 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/config/mqtt/MqttOutConfig.java

@@ -0,0 +1,84 @@
+package com.usky.transfer.service.config.mqtt;
+
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.annotation.MessagingGateway;
+import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
+import org.springframework.integration.mqtt.support.MqttHeaders;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+import org.springframework.messaging.handler.annotation.Header;
+
+@ConditionalOnProperty(prefix = "mqtt", value = {"enabled"}, havingValue = "true")
+@Configuration
+public class MqttOutConfig {
+
+    @Autowired
+    public MqttBaseConfig mqttBaseConfig;
+
+    public static final String CHANNEL_NAME_OUT = "mqttOutboundChannel";
+
+    public static final String MESSAGE_NAME = "messageOut";
+
+    public static final String DEFAULT_TOPIC = "testTopic";
+
+    /**
+     * 连接通道
+     *
+     * @return
+     */
+    @Bean(name = CHANNEL_NAME_OUT)
+    public MessageChannel mqttOutboundChannel() {
+        return new DirectChannel();
+    }
+
+    /**
+     * 发送消息和消费消息Channel可以使用相同MqttPahoClientFactory
+     *
+     * @return
+     */
+    @Bean(name = MESSAGE_NAME)
+    @ServiceActivator(inputChannel = CHANNEL_NAME_OUT)
+    public MessageHandler outbound() {
+        // 在这里进行mqttOutboundChannel的相关设置
+        String clientId = "h-transfer-mqtt-in-" + System.currentTimeMillis();
+        MqttPahoMessageHandler messageHandler =
+                new MqttPahoMessageHandler(clientId, mqttBaseConfig.mqttClientFactory());
+        //如果设置成true,发送消息时将不会阻塞。
+        messageHandler.setAsync(true);
+        messageHandler.setDefaultTopic(DEFAULT_TOPIC);
+        return messageHandler;
+    }
+
+    @MessagingGateway(defaultRequestChannel = CHANNEL_NAME_OUT)
+    public interface MqttGateway {
+        /**
+         * 发送消息
+         *
+         * @param payload
+         */
+        void sendToMqtt(String payload);
+
+        /**
+         * 指定top发送消息
+         *
+         * @param topic
+         * @param payload
+         */
+        void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload);
+
+        /**
+         * 指定队列和qos
+         *
+         * @param topic
+         * @param qos
+         * @param payload
+         */
+        void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload);
+    }
+}

+ 60 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/enums/TopListener.java

@@ -0,0 +1,60 @@
+package com.usky.transfer.service.enums;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author ZYJ
+ * @date 2024/9/23 15:11
+ */
+public enum TopListener {
+
+    /**
+     * 对接设备info信息和下发命令响应信息
+     */
+    DEVICE_INFO("deviceInfo","/+/+/info",1),
+    DEVICE_CONTROLRESPONSE("deviceControlResponse","/+/+/controlResponse",1);
+
+
+
+
+    private String name;
+    private String code;
+    //0发送队列,1监听队列2都是
+    private Integer type;
+
+    TopListener(String name, String code, Integer type){
+        this.name = name;
+        this.code = code;
+        this.type = type;
+    }
+
+    public static TopListener parse(String code){
+        TopListener topListener = null;
+        for (TopListener t:TopListener.values()) {
+            if (t.getCode().equals(code)){
+                topListener = t;
+                break;
+            }
+        }
+        return topListener;
+    }
+    public static List<TopListener> parse(Integer type){
+        List<TopListener> listeners = new ArrayList<>();
+        for (TopListener t:TopListener.values()) {
+            if (t.getType().equals(type)){
+                listeners.add(t);
+            }
+        }
+        return listeners;
+    }
+    public String getCode(){
+        return code;
+    }
+    public String getName(){
+        return name;
+    }
+    public Integer getType(){
+        return type;
+    }
+}

+ 42 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/impl/DmpDeviceCommandServiceImpl.java

@@ -0,0 +1,42 @@
+package com.usky.transfer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.usky.common.core.bean.CommonPage;
+import com.usky.common.security.utils.SecurityUtils;
+import com.usky.transfer.domain.DmpDeviceCommand;
+import com.usky.transfer.mapper.DmpDeviceCommandMapper;
+import com.usky.transfer.service.DmpDeviceCommandService;
+import com.usky.common.mybatis.core.AbstractCrudService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 创建下发命令历史记录表 服务实现类
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-24
+ */
+@Service
+public class DmpDeviceCommandServiceImpl extends AbstractCrudService<DmpDeviceCommandMapper, DmpDeviceCommand> implements DmpDeviceCommandService {
+
+    @Override
+    public CommonPage<DmpDeviceCommand> deviceCommandRecord(Integer commandStatus,String productCode,String deviceUuid, String userName, String startTime, String endTime, Integer pageNum, Integer pageSize){
+        IPage<DmpDeviceCommand> page = new Page<>(pageNum,pageSize);
+        LambdaQueryWrapper<DmpDeviceCommand> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.eq(commandStatus != null,DmpDeviceCommand::getCommandStatus,commandStatus)
+                .like(StringUtils.isNotBlank(productCode),DmpDeviceCommand::getProductCode,productCode)
+                .like(StringUtils.isNotBlank(deviceUuid),DmpDeviceCommand::getDeviceUuid,deviceUuid)
+                .like(StringUtils.isNotBlank(userName),DmpDeviceCommand::getUserName,userName)
+                .between(StringUtils.isNotBlank(startTime)&&StringUtils.isNotBlank(endTime),DmpDeviceCommand::getCreatedTime,startTime,endTime)
+                .eq(DmpDeviceCommand::getTenantId, SecurityUtils.getTenantId())
+                .orderByDesc(DmpDeviceCommand::getId);
+        page = this.page(page,queryWrapper);
+
+        return new CommonPage<>(page.getRecords(),page.getTotal(),pageSize,pageNum);
+    }
+}

+ 20 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/impl/DmpDeviceServiceImpl.java

@@ -0,0 +1,20 @@
+package com.usky.transfer.service.impl;
+
+import com.usky.common.mybatis.core.AbstractCrudService;
+import com.usky.transfer.domain.DmpDevice;
+import com.usky.transfer.mapper.DmpDeviceMapper;
+import com.usky.transfer.service.DmpDeviceService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 设备信息表 服务实现类
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-19
+ */
+@Service
+public class DmpDeviceServiceImpl extends AbstractCrudService<DmpDeviceMapper, DmpDevice> implements DmpDeviceService {
+
+}

+ 22 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/impl/DmpDeviceStatusServiceImpl.java

@@ -0,0 +1,22 @@
+package com.usky.transfer.service.impl;
+
+import com.usky.common.mybatis.core.AbstractCrudService;
+import com.usky.transfer.domain.DmpDeviceStatus;
+import com.usky.transfer.mapper.DmpDeviceStatusMapper;
+import com.usky.transfer.service.DmpDeviceStatusService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ * 设备状态表 服务实现类
+ * </p>
+ *
+ * @author ya
+ * @since 2022-10-08
+ */
+@Service
+public class DmpDeviceStatusServiceImpl extends AbstractCrudService<DmpDeviceStatusMapper, DmpDeviceStatus> implements DmpDeviceStatusService {
+}

+ 20 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/impl/DmpProductAttributeServiceImpl.java

@@ -0,0 +1,20 @@
+package com.usky.transfer.service.impl;
+
+import com.usky.transfer.domain.DmpProductAttribute;
+import com.usky.transfer.mapper.DmpProductAttributeMapper;
+import com.usky.transfer.service.DmpProductAttributeService;
+import com.usky.common.mybatis.core.AbstractCrudService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 产品属性表 服务实现类
+ * </p>
+ *
+ * @author ya
+ * @since 2025-02-10
+ */
+@Service
+public class DmpProductAttributeServiceImpl extends AbstractCrudService<DmpProductAttributeMapper, DmpProductAttribute> implements DmpProductAttributeService {
+
+}

+ 99 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/impl/DmpProductServiceImpl.java

@@ -0,0 +1,99 @@
+package com.usky.transfer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.usky.common.mybatis.core.AbstractCrudService;
+import com.usky.common.security.utils.SecurityUtils;
+import com.usky.transfer.domain.DmpDevice;
+import com.usky.transfer.domain.DmpProduct;
+import com.usky.transfer.mapper.DmpProductMapper;
+import com.usky.transfer.service.DmpDeviceService;
+import com.usky.transfer.service.DmpProductService;
+import com.usky.transfer.service.vo.DeviceMapVO;
+import com.usky.transfer.service.vo.ProductMapVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ * 产品信息表 服务实现类
+ * </p>
+ *
+ * @author ya
+ * @since 2024-09-19
+ */
+@Service
+public class DmpProductServiceImpl extends AbstractCrudService<DmpProductMapper, DmpProduct> implements DmpProductService {
+
+    @Autowired
+    private DmpDeviceService dmpDeviceService;
+
+    //清除产品缓存
+    @CacheEvict(cacheNames = "productList")
+    public void deleteProductCache(){
+        System.out.println("deleteProductCache");
+    }
+
+    //清除设备缓存
+    @CacheEvict(cacheNames = "deviceList",key = "#productCode")
+    public void deleteDeviceCache(String productCode){
+        System.out.println("deleteDeviceCache");
+    }
+
+    @Cacheable(cacheNames = "productList",sync = true)
+    public Map<String, ProductMapVO> getProductMap(){
+        Map<String,ProductMapVO> productMap = new HashMap<>();
+        LambdaQueryWrapper<DmpProduct> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.eq(DmpProduct::getDeleteFlag,0)
+                .orderByDesc(DmpProduct::getId);
+        List<DmpProduct> list = this.list(queryWrapper);
+        if(CollectionUtils.isNotEmpty(list)){
+            for (int i = 0; i < list.size(); i++) {
+                String productCode = list.get(i).getProductCode();
+                ProductMapVO mapVO = new ProductMapVO();
+                mapVO.setProductId(list.get(i).getId());
+                mapVO.setProductCode(list.get(i).getProductCode());
+                mapVO.setCreatedBy(list.get(i).getCreatedBy());
+                mapVO.setTenantId(list.get(i).getTenantId());
+                mapVO.setDeviceType(list.get(i).getDeviceType());
+
+                productMap.put(productCode,mapVO);
+            }
+        }
+
+
+        return productMap;
+    }
+
+    @Cacheable(cacheNames = "deviceList",key = "#productCode",sync = true)
+    public Map<String, DeviceMapVO> getDeviceMap(String productCode){
+        Map<String,DeviceMapVO> deviceMap = new HashMap<>();
+        LambdaQueryWrapper<DmpDevice> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.eq(DmpDevice::getDeleteFlag,0)
+                .eq(DmpDevice::getProductCode,productCode)
+                .orderByDesc(DmpDevice::getId);
+        List<DmpDevice> list = dmpDeviceService.list(queryWrapper);
+        if(CollectionUtils.isNotEmpty(list)){
+            for (int i = 0; i < list.size(); i++) {
+                String deviceId = list.get(i).getDeviceId();
+                DeviceMapVO mapVO = new DeviceMapVO();
+                mapVO.setProductCode(list.get(i).getProductCode());
+                mapVO.setDeviceId(list.get(i).getDeviceId());
+                mapVO.setDeviceUuid(list.get(i).getDeviceUuid());
+                mapVO.setDeviceStatus(list.get(i).getServiceStatus());
+
+                deviceMap.put(deviceId,mapVO);
+            }
+        }
+
+        return deviceMap;
+    }
+}

+ 339 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/impl/QueryInfluxdbDataServiceImpl.java

@@ -0,0 +1,339 @@
+package com.usky.transfer.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.nacos.shaded.com.google.protobuf.Internal;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.usky.common.core.exception.BusinessException;
+import com.usky.common.core.util.UUIDUtils;
+import com.usky.common.security.utils.SecurityUtils;
+import com.usky.transfer.domain.*;
+import com.usky.transfer.mapper.QueryInfluxdbDataMapper;
+import com.usky.transfer.service.*;
+import com.usky.common.mybatis.core.AbstractCrudService;
+import com.usky.transfer.service.config.mqtt.MqttOutConfig;
+import com.usky.transfer.service.rocketmq.MyProducer;
+import com.usky.transfer.service.utils.TsdbUtils;
+import com.usky.transfer.service.vo.DeviceMapVO;
+import com.usky.transfer.service.vo.ProductMapVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.*;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author ya
+ * @since 2024-07-29
+ */
+@Slf4j
+@Service
+public class QueryInfluxdbDataServiceImpl extends AbstractCrudService<QueryInfluxdbDataMapper, QueryInfluxdbData> implements QueryInfluxdbDataService {
+    @Autowired
+    private TsdbUtils tsdbUtils;
+    @Autowired
+    private DmpProductService dmpProductService;
+    @Autowired
+    private DmpDeviceService dmpDeviceService;
+    @Autowired
+    private DmpDeviceStatusService dmpDeviceStatusService;
+    @Autowired
+    private DmpDeviceCommandService dmpDeviceCommandService;
+    @Autowired
+    private DmpProductAttributeService dmpProductAttributeService;
+
+    @Resource
+    private MyProducer myProducer;
+    @Resource
+    private MqttOutConfig.MqttGateway mqttGateway;
+
+    @Override
+    public Map<String,Object> deviceControl(String productCode, String deviceUuid, String commandStr,Integer tenantId, Long userId, String userName){
+        Map<String,Object> rec_map = new HashMap<>();
+
+        JSONObject dataJson = JSONObject.parseObject(commandStr);
+
+        //存储下发设备控制命令到数据库表中
+        DmpDeviceCommand command = new DmpDeviceCommand();
+        command.setProductCode(productCode);
+        command.setDeviceUuid(dataJson.get("deviceUuid").toString());
+
+        command.setCommandContent(commandStr);
+        command.setCreatedTime(LocalDateTime.now());
+//        if (Objects.nonNull(SecurityUtils.getLoginUser().getSysUser().getDeptId())){
+//            command.setDeptId(SecurityUtils.getLoginUser().getSysUser().getDeptId().intValue());
+//        }
+
+        command.setTenantId(tenantId);
+        command.setUserId(userId);
+        command.setUserName(userName);
+        dmpDeviceCommandService.save(command);
+        int commandId = command.getId();
+
+        dataJson.put("id",commandId);
+
+        command.setCommandContent(dataJson.toJSONString());
+        dmpDeviceCommandService.updateById(command);
+        //推送下发设备控制mqtt
+        if(StringUtils.isNotBlank(commandStr)){
+            String topic = "/usky/devices/"+deviceUuid+"/control";
+            mqttGateway.sendToMqtt(topic,dataJson.toJSONString());
+        }
+
+        long startTimeStamp = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
+        while (true){
+            LambdaQueryWrapper<DmpDeviceCommand> queryWrapper = Wrappers.lambdaQuery();
+            queryWrapper.eq(DmpDeviceCommand::getId,commandId);
+            DmpDeviceCommand one = dmpDeviceCommandService.getOne(queryWrapper);
+            if(Objects.nonNull(one.getCommandResponse())){
+                rec_map.put("code",200);
+                rec_map.put("message","下发命令成功");
+                rec_map.put("data",one.getCommandResponse());
+                break;
+            }
+
+            long endTimeStamp = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
+            if((endTimeStamp - startTimeStamp) >= 3){ //请求超时3秒,返回失败
+                rec_map.put("code",-1);
+                rec_map.put("message","下发命令响应超时");
+
+                dmpDeviceCommandService.lambdaUpdate().set(DmpDeviceCommand::getCommandStatus,2).eq(DmpDeviceCommand::getId,commandId).update();
+                break;
+            }
+        }
+
+        return rec_map;
+    }
+
+    @Override
+    public Map<String,Object> sendDeviceDataToMQ(DeviceDataWriteVO writeVO){
+        Map<String,Object> rec_map = new HashMap<>();
+        DeviceDataInfoVO dataInfo = new DeviceDataInfoVO();
+        Map<String,Object> metrics = writeVO.getMetrics();
+        Map<String,String> tags = writeVO.getTags();
+        if(metrics.size() > 0){
+            Map<String,Object> mp = new HashMap<>();
+            Map<String,Object> mp_tag = new HashMap<>();
+            for(Map.Entry<String,Object> map:metrics.entrySet()){
+                mp.put(map.getKey(),map.getValue());
+            }
+            dataInfo.setMetrics(mp);
+            if(tags != null && tags.size() > 0){
+                for(Map.Entry<String,String> map:tags.entrySet()){
+                    mp_tag.put(map.getKey(),map.getValue());
+                }
+                dataInfo.setTags(mp_tag);
+            }else{
+                dataInfo.setTags(new HashMap<>());
+            }
+
+            String productCode = writeVO.getProductCode();
+            String deviceUUId = writeVO.getDeviceUUId();
+            String deviceId = tags.get("device_id");
+            log.info("sendDeviceDataToMQ "+deviceId+" start222");
+
+            //判断上报数据对应产品是否注册,如未注册则为非法
+            Map<String,ProductMapVO> productMapList = dmpProductService.getProductMap();
+            if(!productMapList.containsKey(productCode)){
+                //通过查询数据库再确认下产品是否注册
+                LambdaQueryWrapper<DmpProduct> queryWrapper = Wrappers.lambdaQuery();
+                queryWrapper.eq(DmpProduct::getDeleteFlag,0)
+                        .eq(DmpProduct::getProductCode,productCode);
+                DmpProduct one = dmpProductService.getOne(queryWrapper);
+                if(one == null){
+                    rec_map.put("code",201) ;
+                    rec_map.put("message","产品未注册!");
+                    log.info("产品未注册");
+                    return rec_map;
+                }else{
+                    dmpProductService.deleteProductCache();
+                }
+            }
+
+            //判断上报数据设备是否已注册(要判断注册过的设备是不是属于本产品的),未注册自动注册
+            Map<String,DeviceMapVO> deviceMapList = dmpProductService.getDeviceMap(productCode);
+            if(!deviceMapList.containsKey(deviceId)){
+                DmpDevice dmpDeviceInfo = new DmpDevice();
+                ProductMapVO productMapVO = productMapList.get(productCode);
+                dmpDeviceInfo.setDeviceId(deviceId);
+                dmpDeviceInfo.setDeviceName("");
+                dmpDeviceInfo.setDeviceType(productMapVO.getDeviceType());
+                dmpDeviceInfo.setProductId(productMapVO.getProductId());
+                dmpDeviceInfo.setProductCode(productCode);
+                dmpDeviceInfo.setCreatedBy(productMapVO.getCreatedBy());
+                dmpDeviceInfo.setCreatedTime(LocalDateTime.now());
+                dmpDeviceInfo.setTenantId(productMapVO.getTenantId());
+                dmpDeviceInfo.setServiceStatus(1);
+                if(StringUtils.isBlank(deviceUUId)){
+                    dmpDeviceInfo.setDeviceUuid(UUIDUtils.uuid().substring(0,16));
+                }else{
+                    dmpDeviceInfo.setDeviceUuid(deviceUUId);
+                }
+
+                dmpDeviceService.save(dmpDeviceInfo);
+                dmpProductService.deleteDeviceCache(productCode);
+
+                deviceUUId = dmpDeviceInfo.getDeviceUuid();
+
+                DmpDeviceStatus dmpDeviceStatus = new DmpDeviceStatus();
+                dmpDeviceStatus.setDeviceId(dmpDeviceInfo.getDeviceId());
+                dmpDeviceStatus.setProductId(dmpDeviceInfo.getProductId());
+                dmpDeviceStatus.setDeviceStatus(2);
+                dmpDeviceStatus.setLastOfflineTime(LocalDateTime.now());
+                dmpDeviceStatus.setProductCode(dmpDeviceInfo.getProductCode());
+                dmpDeviceStatus.setDeviceUuid(deviceUUId);
+                dmpDeviceStatusService.save(dmpDeviceStatus);
+
+                deviceMapList = dmpProductService.getDeviceMap(productCode);
+            }else if(deviceMapList.containsKey(deviceId)){
+                LambdaQueryWrapper<DmpDevice> queryWrapper = Wrappers.lambdaQuery();
+                queryWrapper.eq(DmpDevice::getDeleteFlag,0)
+                        .eq(DmpDevice::getProductCode,productCode)
+                        .eq(DmpDevice::getDeviceId,deviceId);
+                DmpDevice one = dmpDeviceService.getOne(queryWrapper);
+                if(one == null){
+                    DmpDevice dmpDeviceInfo = new DmpDevice();
+                    ProductMapVO productMapVO = productMapList.get(productCode);
+                    dmpDeviceInfo.setDeviceId(deviceId);
+                    dmpDeviceInfo.setDeviceName("");
+                    dmpDeviceInfo.setDeviceType(productMapVO.getDeviceType());
+                    dmpDeviceInfo.setProductId(productMapVO.getProductId());
+                    dmpDeviceInfo.setProductCode(productCode);
+                    dmpDeviceInfo.setCreatedBy(productMapVO.getCreatedBy());
+                    dmpDeviceInfo.setCreatedTime(LocalDateTime.now());
+                    dmpDeviceInfo.setTenantId(productMapVO.getTenantId());
+                    dmpDeviceInfo.setServiceStatus(1);
+                    if(StringUtils.isBlank(deviceUUId)){
+                        dmpDeviceInfo.setDeviceUuid(UUIDUtils.uuid().substring(0,16));
+                    }else{
+                        dmpDeviceInfo.setDeviceUuid(deviceUUId);
+                    }
+                    dmpDeviceService.save(dmpDeviceInfo);
+                    dmpProductService.deleteDeviceCache(productCode);
+
+                    deviceUUId = dmpDeviceInfo.getDeviceUuid();
+
+                    DmpDeviceStatus dmpDeviceStatus = new DmpDeviceStatus();
+                    dmpDeviceStatus.setDeviceId(dmpDeviceInfo.getDeviceId());
+                    dmpDeviceStatus.setProductId(dmpDeviceInfo.getProductId());
+                    dmpDeviceStatus.setDeviceStatus(2);
+                    dmpDeviceStatus.setLastOfflineTime(LocalDateTime.now());
+                    dmpDeviceStatus.setProductCode(dmpDeviceInfo.getProductCode());
+                    dmpDeviceStatus.setDeviceUuid(deviceUUId);
+                    dmpDeviceStatusService.save(dmpDeviceStatus);
+
+                    deviceMapList = dmpProductService.getDeviceMap(productCode);
+                }
+            }
+
+            if(StringUtils.isBlank(deviceUUId)){
+                for (Map.Entry<String,DeviceMapVO> map:deviceMapList.entrySet()) {
+                    String productCode1 = map.getValue().getProductCode();
+                    String deviceId1 = map.getKey();
+                    if((productCode.equals(productCode1)) && (deviceId.equals(deviceId1))){
+                        deviceUUId = map.getValue().getDeviceUuid();
+                        break;
+                    }
+                }
+            }
+
+            dataInfo.setProductCode(productCode);
+            dataInfo.setDeviceUUId(deviceUUId);
+            dataInfo.setTimestamp(writeVO.getTimestamp());
+            log.info("推送tsdb "+JSONArray.toJSON(dataInfo).toString()+" start");
+
+            myProducer.sendMessage("data-tsdb", JSONArray.toJSON(dataInfo).toString());
+            log.info("推送tsdb "+JSONArray.toJSON(dataInfo).toString()+" end");
+        }
+
+        rec_map.put("code",200);
+        rec_map.put("message","操作成功!");
+        return rec_map;
+    }
+
+    @Override
+    public Map<String,Object> mqttDeviceData(MqttDeviceDataVO mqttDeviceDataVO){
+        Map<String,Object> rec_map = new HashMap<>();
+
+        String topic = mqttDeviceDataVO.getTopic();
+        String[] topics = topic.split("/");
+        //String productCode1 = topics[1];
+        //String deviceId1 = topics[2];
+        String deviceUuid = topics[3];
+
+        String payload = mqttDeviceDataVO.getDeviceData();
+
+        JSONObject deviceDataJson = JSONObject.parseObject(payload);
+        String productCode2 = deviceDataJson.get("productCode").toString();
+        Object tag = JSONObject.toJSONString(deviceDataJson.get("tags"));
+        JSONObject tagJson = JSON.parseObject(tag.toString());
+        String deviceId2 = tagJson.get("device_id").toString();
+        Object metric = JSONObject.toJSONString(deviceDataJson.get("metrics"));
+        JSONObject metricJson = JSON.parseObject(metric.toString());
+
+//        if(!productCode1.equals(productCode2) || !deviceId1.equals(deviceId2)){
+//            throw new BusinessException("Topic和请求体中的产品编码、设备Id不一致,请修改");
+//        }
+
+        LambdaQueryWrapper<DmpProduct> lambdaQuery = Wrappers.lambdaQuery();
+        lambdaQuery.eq(DmpProduct::getProductCode,productCode2)
+                .eq(DmpProduct::getDeleteFlag,0);
+        List<DmpProduct> dmpProductList = dmpProductService.list(lambdaQuery);
+        if(dmpProductList.size() <= 0){
+            throw new BusinessException("产品编码不存在,请修改");
+        }
+        LambdaQueryWrapper<DmpDevice> lambdaQuery2 = Wrappers.lambdaQuery();
+        lambdaQuery2.eq(DmpDevice::getDeviceUuid,deviceUuid)
+                .eq(DmpDevice::getProductCode,productCode2)
+                .eq(DmpDevice::getServiceStatus,2)
+                .eq(DmpDevice::getDeleteFlag,0);
+        List<DmpDevice> dmpDeviceList = dmpDeviceService.list(lambdaQuery2);
+        if(dmpDeviceList.size() <= 0){
+            throw new BusinessException("topic主题中的设备Uuid不存在或在本产品下不存在,请修改");
+        }
+        LambdaQueryWrapper<DmpDevice> lambdaQuery3 = Wrappers.lambdaQuery();
+        lambdaQuery3.eq(DmpDevice::getDeviceId,deviceId2)
+                .eq(DmpDevice::getProductCode,productCode2)
+                .eq(DmpDevice::getServiceStatus,2)
+                .eq(DmpDevice::getDeleteFlag,0);
+        List<DmpDevice> dmpDeviceList1 = dmpDeviceService.list(lambdaQuery3);
+        if(dmpDeviceList1.size() <= 0){
+            throw new BusinessException("设备Id不存在或在本产品下不存在,请修改");
+        }
+
+        List<String> metricList = new ArrayList<>();
+        if(Objects.nonNull(metricJson)){
+            for(String entry: metricJson.keySet()){
+                metricList.add(entry.toLowerCase());
+            }
+        }
+        int count1 = metricList.size();
+        LambdaQueryWrapper<DmpProductAttribute> lambdaQuery4 = Wrappers.lambdaQuery();
+        lambdaQuery4.eq(DmpProductAttribute::getProductId,dmpProductList.get(0).getId())
+                .in(DmpProductAttribute::getAttributeCode,metricList);
+        int count2 = dmpProductAttributeService.count(lambdaQuery4);
+        if(count1 > count2){
+            throw new BusinessException("设备属性编码不存在,请检查");
+        }
+
+
+        mqttGateway.sendToMqtt(topic, deviceDataJson.toJSONString());
+
+        rec_map.put("code",200);
+        rec_map.put("message","发送成功");
+
+        return rec_map;
+    }
+}

+ 79 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/listener/MqttListener.java

@@ -0,0 +1,79 @@
+package com.usky.transfer.service.listener;
+
+
+import com.usky.transfer.service.config.mqtt.MqttInConfig;
+import com.usky.transfer.service.enums.TopListener;
+import com.usky.transfer.service.mqtt.SimpleContext;
+import com.usky.transfer.service.vo.MqttBaseVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageHandler;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+/**
+ * @author yq
+ * @date 2021/11/3 8:13
+ */
+@ConditionalOnProperty(prefix = "mqtt", value = {"enabled"}, havingValue = "true")
+@Slf4j
+@Component
+public class MqttListener {
+
+    public static final String MESSAGE_NAME = "messageInput";
+
+    @Autowired
+    private SimpleContext simpleContext;
+
+    /**
+     * 处理消息-消费者
+     *
+     * @return
+     */
+    @Bean(MESSAGE_NAME)
+    @ServiceActivator(inputChannel = MqttInConfig.CHANNEL_NAME_INPUT)
+    public MessageHandler handler() {
+        return message -> {
+            if (message.getPayload() == null || message.getPayload().toString().isEmpty()) {
+                System.out.println("Received empty message, skipping processing.");
+                return;
+            }
+            String payload = message.getPayload().toString();
+            //进行接口推送
+//            String[] infoCode = TopListener.DEVICE_INFO.getCode().split("/");
+//            String[] controlCode = TopListener.DEVICE_CONTROLRESPONSE.getCode().split("/");
+            Object mqttReceivedTopic = message.getHeaders().get("mqtt_receivedTopic");
+            if (null != mqttReceivedTopic) {
+                String topic = mqttReceivedTopic.toString();
+                MqttBaseVO mqttBaseVO = new MqttBaseVO();
+                mqttBaseVO.setTopic(topic);
+                if (topic.indexOf("info") != -1 ) {
+                    mqttBaseVO.setDescribe("info");
+                    mqttBaseVO.setData(payload);
+                }else if(topic.indexOf("controlResponse") != -1 ) {
+                    mqttBaseVO.setDescribe("controlResponse");
+                    mqttBaseVO.setData(payload);
+                }
+                else if(topic.indexOf("add") != -1){
+                    mqttBaseVO.setDescribe("add");
+                    mqttBaseVO.setData(payload);
+                }
+                else if(topic.indexOf("data-collector") != -1){
+                    mqttBaseVO.setDescribe("dataCollector");
+                    mqttBaseVO.setData(payload);
+                }
+                else if(topic.indexOf("alarm") != -1){
+                    mqttBaseVO.setDescribe("alarm");
+                    mqttBaseVO.setData(payload);
+                }
+                //统一处理数据
+                simpleContext.getResource(mqttBaseVO);
+            }
+        };
+    }
+}

+ 21 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/MqttStrategy.java

@@ -0,0 +1,21 @@
+package com.usky.transfer.service.mqtt;
+
+
+import com.usky.transfer.service.vo.MqttBaseVO;
+
+/**
+ * 策略类
+ *
+ * @author yq
+ * @date 2021/11/3 8:27
+ */
+public interface MqttStrategy {
+    /**
+     * 处理消息(策略模式由子类实现)
+     *
+     * @param mqttBaseVO
+     * @return
+     */
+    String disposeMessage(MqttBaseVO mqttBaseVO);
+
+}

+ 26 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/SimpleContext.java

@@ -0,0 +1,26 @@
+package com.usky.transfer.service.mqtt;
+
+
+import com.usky.transfer.service.vo.MqttBaseVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 中间处理消息转发
+ */
+@Service
+public class SimpleContext {
+    @Autowired
+    private final Map<String, MqttStrategy> strategyMap = new ConcurrentHashMap<>();
+
+    public SimpleContext(Map<String, MqttStrategy> strategyMap) {
+        strategyMap.forEach(this.strategyMap::put);
+    }
+
+    public String getResource(MqttBaseVO mqttBaseVO) {
+        return strategyMap.get(mqttBaseVO.getDescribe()).disposeMessage(mqttBaseVO);
+    }
+}

+ 152 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/add/Add.java

@@ -0,0 +1,152 @@
+package com.usky.transfer.service.mqtt.add;
+
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.nacos.shaded.com.google.gson.JsonObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.usky.common.core.util.UUIDUtils;
+import com.usky.transfer.domain.DmpDevice;
+import com.usky.transfer.domain.DmpDeviceStatus;
+import com.usky.transfer.domain.DmpProduct;
+import com.usky.transfer.service.DmpDeviceService;
+import com.usky.transfer.service.DmpDeviceStatusService;
+import com.usky.transfer.service.DmpProductService;
+import com.usky.transfer.service.mqtt.MqttStrategy;
+import com.usky.transfer.service.vo.DeviceMapVO;
+import com.usky.transfer.service.vo.MqttBaseVO;
+import com.usky.transfer.service.vo.ProductMapVO;
+import lombok.extern.slf4j.Slf4j;
+import org.checkerframework.checker.units.qual.A;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+@Slf4j
+@Service("add")
+public class Add implements MqttStrategy {
+    @Autowired
+    private DmpProductService dmpProductService;
+
+    @Autowired
+    private DmpDeviceService dmpDeviceService;
+
+    @Autowired
+    private DmpDeviceStatusService dmpDeviceStatusService;
+
+    public String disposeMessage(MqttBaseVO mqttBaseVO){
+        String[] topics = mqttBaseVO.getTopic().toString().split("/");
+        String topic_deviceUUID = topics[3];
+        String deviceInfoStr = mqttBaseVO.getData().toString();
+        JSONObject deviceInfoJson = JSONObject.parseObject(deviceInfoStr);
+        String productCode = deviceInfoJson.get("productCode").toString();
+        String deviceId = deviceInfoJson.get("deviceId").toString();
+        Integer categoryType = Integer.valueOf(deviceInfoJson.get("categoryType").toString());
+
+        //判断上报数据对应产品是否注册,如未注册则为非法
+        Map<String, ProductMapVO> productMapList = dmpProductService.getProductMap();
+        if(!productMapList.containsKey(productCode)){
+            //通过查询数据库再确认下产品是否注册
+            LambdaQueryWrapper<DmpProduct> queryWrapper = Wrappers.lambdaQuery();
+            queryWrapper.eq(DmpProduct::getDeleteFlag,0)
+                    .eq(DmpProduct::getProductCode,productCode);
+            DmpProduct one = dmpProductService.getOne(queryWrapper);
+            if(one == null){
+                log.error(productCode+" 产品未注册");
+                return null;
+            }else{
+                dmpProductService.deleteProductCache();
+            }
+        }
+
+        //判断上报数据设备是否已注册(要判断注册过的设备是不是属于本产品的),未注册自动注册
+        Map<String, DeviceMapVO> deviceMapList = dmpProductService.getDeviceMap(productCode);
+        if(!deviceMapList.containsKey(deviceId)){
+            DmpDevice dmpDeviceInfo = new DmpDevice();
+            ProductMapVO productMapVO = productMapList.get(productCode);
+            dmpDeviceInfo.setDeviceId(deviceId);
+            if(Objects.nonNull(deviceInfoJson.get("deviceName"))){
+                dmpDeviceInfo.setDeviceName(deviceInfoJson.get("deviceName").toString());
+            }
+            if(Objects.nonNull(deviceInfoJson.get("simCode"))){
+                dmpDeviceInfo.setSimCode(deviceInfoJson.get("simCode").toString());
+            }
+            if(Objects.nonNull(deviceInfoJson.get("installAddress"))){
+                dmpDeviceInfo.setInstallAddress(deviceInfoJson.get("installAddress").toString());
+            }
+            dmpDeviceInfo.setDeviceType(productMapVO.getDeviceType());
+            dmpDeviceInfo.setProductId(productMapVO.getProductId());
+            dmpDeviceInfo.setProductCode(productCode);
+            dmpDeviceInfo.setCreatedBy(productMapVO.getCreatedBy());
+            dmpDeviceInfo.setCreatedTime(LocalDateTime.now());
+            dmpDeviceInfo.setTenantId(productMapVO.getTenantId());
+            dmpDeviceInfo.setServiceStatus(1);
+            dmpDeviceInfo.setDeviceUuid(deviceInfoJson.get("deviceUuid").toString());
+            dmpDeviceInfo.setCategoryType(categoryType);
+            if(categoryType == 3){
+                dmpDeviceInfo.setGatewayUuid(topic_deviceUUID);
+            }
+            dmpDeviceService.save(dmpDeviceInfo);
+
+            DmpDeviceStatus dmpDeviceStatus = new DmpDeviceStatus();
+            dmpDeviceStatus.setDeviceId(dmpDeviceInfo.getDeviceId());
+            dmpDeviceStatus.setProductId(dmpDeviceInfo.getProductId());
+            dmpDeviceStatus.setDeviceStatus(2);
+            dmpDeviceStatus.setLastOfflineTime(LocalDateTime.now());
+            dmpDeviceStatus.setProductCode(dmpDeviceInfo.getProductCode());
+            dmpDeviceStatus.setDeviceUuid(dmpDeviceInfo.getDeviceUuid());
+            dmpDeviceStatusService.save(dmpDeviceStatus);
+
+            dmpProductService.deleteDeviceCache(productCode);
+        }else if(deviceMapList.containsKey(deviceId)){
+            LambdaQueryWrapper<DmpDevice> queryWrapper = Wrappers.lambdaQuery();
+            queryWrapper.eq(DmpDevice::getDeleteFlag,0)
+                    .eq(DmpDevice::getProductCode,productCode)
+                    .eq(DmpDevice::getDeviceId,deviceId);
+            DmpDevice one = dmpDeviceService.getOne(queryWrapper);
+            if(one == null){
+                DmpDevice dmpDeviceInfo = new DmpDevice();
+                ProductMapVO productMapVO = productMapList.get(productCode);
+                dmpDeviceInfo.setDeviceId(deviceId);
+                if(Objects.nonNull(deviceInfoJson.get("deviceName"))){
+                    dmpDeviceInfo.setDeviceName(deviceInfoJson.get("deviceName").toString());
+                }
+                if(Objects.nonNull(deviceInfoJson.get("simCode"))){
+                    dmpDeviceInfo.setSimCode(deviceInfoJson.get("simCode").toString());
+                }
+                if(Objects.nonNull(deviceInfoJson.get("installAddress"))){
+                    dmpDeviceInfo.setInstallAddress(deviceInfoJson.get("installAddress").toString());
+                }
+                dmpDeviceInfo.setDeviceType(productMapVO.getDeviceType());
+                dmpDeviceInfo.setProductId(productMapVO.getProductId());
+                dmpDeviceInfo.setProductCode(productCode);
+                dmpDeviceInfo.setCreatedBy(productMapVO.getCreatedBy());
+                dmpDeviceInfo.setCreatedTime(LocalDateTime.now());
+                dmpDeviceInfo.setTenantId(productMapVO.getTenantId());
+                dmpDeviceInfo.setServiceStatus(1);
+                dmpDeviceInfo.setDeviceUuid(deviceInfoJson.get("deviceUuid").toString());
+                dmpDeviceInfo.setCategoryType(categoryType);
+                if(categoryType == 3){
+                    dmpDeviceInfo.setGatewayUuid(topic_deviceUUID);
+                }
+                dmpDeviceService.save(dmpDeviceInfo);
+
+                DmpDeviceStatus dmpDeviceStatus = new DmpDeviceStatus();
+                dmpDeviceStatus.setDeviceId(dmpDeviceInfo.getDeviceId());
+                dmpDeviceStatus.setProductId(dmpDeviceInfo.getProductId());
+                dmpDeviceStatus.setDeviceStatus(2);
+                dmpDeviceStatus.setLastOfflineTime(LocalDateTime.now());
+                dmpDeviceStatus.setProductCode(dmpDeviceInfo.getProductCode());
+                dmpDeviceStatus.setDeviceUuid(dmpDeviceInfo.getDeviceUuid());
+                dmpDeviceStatusService.save(dmpDeviceStatus);
+
+                dmpProductService.deleteDeviceCache(productCode);
+            }
+        }
+
+        return null;
+    }
+}

+ 40 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/alarm/alarm.java

@@ -0,0 +1,40 @@
+package com.usky.transfer.service.mqtt.alarm;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.usky.common.core.util.JsonUtils;
+import com.usky.transfer.domain.DeviceDataWriteVO;
+import com.usky.transfer.service.QueryInfluxdbDataService;
+import com.usky.transfer.service.mqtt.MqttStrategy;
+import com.usky.transfer.service.rocketmq.MyProducer;
+import com.usky.transfer.service.utils.HttpClientUtils;
+import com.usky.transfer.service.vo.MqttBaseVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author zyj
+ * @date 2025/1/13 15:07
+ */
+@Service("alarm")
+public class alarm implements MqttStrategy {
+    @Value("${alarm.url}")
+    private String alarmUrl;
+
+    public String disposeMessage(MqttBaseVO mqttBaseVO) {
+        String data = mqttBaseVO.getData().toString();
+        JSONObject jsonObject = JSONObject.parseObject(data);
+        String resultString = HttpClientUtils.doPostJson(alarmUrl,jsonObject.toJSONString());
+
+        System.out.println("resultString: "+resultString);
+
+        return null;
+    }
+
+
+}

+ 53 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/control/control.java

@@ -0,0 +1,53 @@
+package com.usky.transfer.service.mqtt.control;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.usky.common.core.util.JsonUtils;
+import com.usky.common.security.utils.SecurityUtils;
+import com.usky.transfer.domain.DeviceDataWriteVO;
+import com.usky.transfer.domain.DmpDeviceCommand;
+import com.usky.transfer.service.DmpDeviceCommandService;
+import com.usky.transfer.service.QueryInfluxdbDataService;
+import com.usky.transfer.service.mqtt.MqttStrategy;
+import com.usky.transfer.service.rocketmq.MyProducer;
+import com.usky.transfer.service.vo.MqttBaseVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.HashMap;
+import java.util.Map;
+
+@Service("controlResponse")
+public class control implements MqttStrategy {
+    @Autowired
+    private DmpDeviceCommandService dmpDeviceCommandService;
+
+    //处理下发命令响应消息
+    public String disposeMessage(MqttBaseVO mqttBaseVO) {
+
+        //存储下发设备控制命令到数据库表中
+        DmpDeviceCommand command = new DmpDeviceCommand();
+
+        String recData = mqttBaseVO.getData().toString();
+        JSONObject dataJson = JSONObject.parseObject(recData);
+        Integer commandId = Integer.valueOf(dataJson.get("id").toString());
+        command.setId(commandId);
+        command.setCommandResponse(recData);
+        if(recData.contains("error")){
+            command.setCommandStatus(2);
+        }else{
+            command.setCommandStatus(1);
+        }
+
+        command.setUpdatedTime(LocalDateTime.now());
+
+        dmpDeviceCommandService.updateById(command);
+
+        return null;
+    }
+
+
+}

+ 65 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/datacollector/DataCollector.java

@@ -0,0 +1,65 @@
+package com.usky.transfer.service.mqtt.datacollector;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.usky.common.core.util.JsonUtils;
+import com.usky.transfer.domain.DeviceDataWriteVO;
+import com.usky.transfer.service.QueryInfluxdbDataService;
+import com.usky.transfer.service.mqtt.MqttStrategy;
+import com.usky.transfer.service.rocketmq.MyProducer;
+import com.usky.transfer.service.vo.MqttBaseVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author zyj
+ * @date 2022/12/6 15:07
+ */
+@Slf4j
+@Service("dataCollector")
+public class DataCollector implements MqttStrategy {
+    @Resource
+    private MyProducer myProducer;
+    @Autowired
+    private QueryInfluxdbDataService queryInfluxdbDataService;
+
+    public String disposeMessage(MqttBaseVO mqttBaseVO) {
+        try {
+            Map<String, String> tags = new HashMap<>();
+            Map<String, Object> fields = new HashMap<>();
+            Map map_data = JsonUtils.fromJson(mqttBaseVO.getData().toString(), Map.class);
+            String productCode = map_data.get("product_id").toString().toLowerCase();
+            long timestamp = Long.valueOf(map_data.get("timestamp").toString())*1000L+1L;
+            String deviceId = map_data.get("device_id").toString();
+
+            tags.put("device_id",deviceId);
+            log.info("disposeMessage "+deviceId+" start111");
+
+            Object met = JSONObject.toJSONString(map_data.get("metrics"));
+            JSONObject metrics = JSON.parseObject(met.toString());
+            for(String entry : metrics.keySet()){
+                fields.put(entry.toLowerCase(),metrics.get(entry));
+            }
+
+            DeviceDataWriteVO deviceDataWriteVO = new DeviceDataWriteVO();
+            deviceDataWriteVO.setDeviceUUId("");
+            deviceDataWriteVO.setProductCode(productCode);
+            deviceDataWriteVO.setTimestamp(timestamp);
+            deviceDataWriteVO.setTags(tags);
+            deviceDataWriteVO.setMetrics(metrics);
+
+            queryInfluxdbDataService.sendDeviceDataToMQ(deviceDataWriteVO);
+        } catch (Exception e) {
+            log.error(e.getMessage());
+        }
+
+        return null;
+    }
+
+
+}

+ 77 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/mqtt/info/Info.java

@@ -0,0 +1,77 @@
+package com.usky.transfer.service.mqtt.info;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.usky.common.core.exception.BusinessException;
+import com.usky.common.core.util.JsonUtils;
+import com.usky.transfer.domain.*;
+import com.usky.transfer.service.*;
+import com.usky.transfer.service.mqtt.MqttStrategy;
+import com.usky.transfer.service.rocketmq.MyProducer;
+import com.usky.transfer.service.vo.MqttBaseVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.*;
+
+/**
+ * @author zyj
+ * @date 2022/12/6 15:07
+ */
+@Slf4j
+@Service("info")
+public class Info implements MqttStrategy {
+    @Resource
+    private MyProducer myProducer;
+    @Autowired
+    private QueryInfluxdbDataService queryInfluxdbDataService;
+
+    public String disposeMessage(MqttBaseVO mqttBaseVO) {
+        try {
+            String[] topics = mqttBaseVO.getTopic().toString().split("/");
+            String topic_deviceUUID = topics[3];
+            Map<String, String> tags = new HashMap<>();
+            Map<String, Object> fields = new HashMap<>();
+            Map map_data = JsonUtils.fromJson(mqttBaseVO.getData().toString(), Map.class);
+            String productCode = map_data.get("productCode").toString().toLowerCase();
+
+            long timestamp = Long.valueOf(map_data.get("timestamp").toString());
+            String deviceUuid = map_data.get("deviceUuid").toString();
+
+            Object tg = JSONObject.toJSONString(map_data.get("tags"));
+            JSONObject tag = JSON.parseObject(tg.toString());
+            for (String entry : tag.keySet()){
+                tags.put(entry.toLowerCase(),tag.get(entry).toString());
+            }
+
+            Object met = JSONObject.toJSONString(map_data.get("metrics"));
+            JSONObject metrics = JSON.parseObject(met.toString());
+            for(String entry : metrics.keySet()){
+                fields.put(entry.toLowerCase(),metrics.get(entry));
+            }
+
+            DeviceDataWriteVO deviceDataWriteVO = new DeviceDataWriteVO();
+            deviceDataWriteVO.setDeviceUUId(deviceUuid);
+            deviceDataWriteVO.setProductCode(productCode);
+            deviceDataWriteVO.setTimestamp(timestamp);
+            deviceDataWriteVO.setTags(tags);
+            deviceDataWriteVO.setMetrics(metrics);
+
+            queryInfluxdbDataService.sendDeviceDataToMQ(deviceDataWriteVO);
+        } catch (Exception e) {
+            log.error(e.getMessage());
+        }
+
+        return null;
+    }
+
+
+}

+ 21 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/rocketmq/MyConsumer.java

@@ -0,0 +1,21 @@
+package com.usky.transfer.service.rocketmq;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+import org.apache.rocketmq.spring.core.RocketMQListener;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+//@Slf4j
+//@Component
+//@RocketMQMessageListener(consumerGroup = "${rocketmq.consumer.group}", topic = "${rocketmq.consumer.topic}")
+//public class MyConsumer implements RocketMQListener<String> {
+//    @Autowired
+//    private RocketMQSimpleContext simpleContext;
+//
+//    @Override
+//    public void onMessage(String message){
+//        System.out.println("DirectReceiver消费者收到消息: " + message);
+//        simpleContext.disposeMessageToDB(message);
+//    }
+//}

+ 17 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/rocketmq/MyProducer.java

@@ -0,0 +1,17 @@
+package com.usky.transfer.service.rocketmq;
+
+import org.apache.rocketmq.spring.core.RocketMQTemplate;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+@Component
+public class MyProducer {
+    @Resource
+    private RocketMQTemplate rocketMQTemplate;
+    //发送普通消息的示例
+    public void sendMessage(String topic, String msg) {
+        rocketMQTemplate.convertAndSend(topic, msg);
+
+    }
+}

+ 80 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/rocketmq/RocketMQSimpleContext.java

@@ -0,0 +1,80 @@
+//package com.usky.transfer.service.rocketmq;
+//
+//import com.alibaba.fastjson.JSON;
+//import com.alibaba.fastjson.JSONObject;
+//import com.alibaba.nacos.shaded.com.google.gson.JsonArray;
+//import com.usky.common.core.util.JsonUtils;
+//import com.usky.transfer.domain.DeviceDataInfoVO;
+//import com.usky.transfer.domain.DeviceDataWriteVO;
+//import com.usky.transfer.service.QueryInfluxdbDataService;
+//import com.usky.transfer.service.utils.TsdbUtils;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.stereotype.Repository;
+//import org.springframework.stereotype.Service;
+//
+//import javax.annotation.Resource;
+//import java.time.LocalDateTime;
+//import java.time.ZoneOffset;
+//import java.util.*;
+//
+///**
+// * 中间处理消息转发
+// */
+//@Service
+//@Repository
+//public class RocketMQSimpleContext {
+//    @Resource
+//    private MyProducer myProducer;
+//    @Autowired
+//    private QueryInfluxdbDataService queryInfluxdbDataService;
+//
+//    /**
+//     * 设备数据推送到MQ队列
+//     * @param
+//     */
+//    public void disposeMessageToDB(String message){
+//        Map<String, String> tags = new HashMap<>();
+//        Map<String, Object> fields = new HashMap<>();
+//        Map map_data = JsonUtils.fromJson(message,Map.class);
+//        String deviceId = map_data.get("deviceUUId").toString();
+//        String productCode = map_data.get("productCode").toString().toLowerCase();
+//        Long timestamp = Long.valueOf(map_data.get("timestamp").toString());
+//
+//        String tableName = deviceId;
+//
+//        Object tg = JSONObject.toJSONString(map_data.get("tags"));
+//        JSONObject tag = JSON.parseObject(tg.toString());
+//        for (String entry : tag.keySet()){
+//            tags.put(entry.toLowerCase(),tag.get(entry).toString());
+//        }
+//
+//        Object met = JSONObject.toJSONString(map_data.get("metrics"));
+//        JSONObject metrics = JSON.parseObject(met.toString());
+//        for(String entry : metrics.keySet()){
+//            fields.put(entry.toLowerCase(),metrics.get(entry));
+//        }
+//
+//        DeviceDataWriteVO deviceDataWriteVO = new DeviceDataWriteVO();
+//        deviceDataWriteVO.setDeviceUUId(deviceId);
+//        deviceDataWriteVO.setProductCode(productCode);
+//        deviceDataWriteVO.setTimestamp(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli());
+//        deviceDataWriteVO.setTags(tags);
+//        deviceDataWriteVO.setMetrics(metrics);
+//
+//        queryInfluxdbDataService.sendDeviceDataToMQ(deviceDataWriteVO);
+//    }
+//}
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//

+ 137 - 0
service-transfer/service-transfer-biz/src/main/java/com/usky/transfer/service/utils/HttpClientUtils.java

@@ -0,0 +1,137 @@
+package com.usky.transfer.service.utils;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ 1. 跨域请求工具类
+ */
+public class HttpClientUtils {
+
+    public static String doGet(String url, Map<String, String> param) {
+
+        // 创建Httpclient对象
+        CloseableHttpClient httpclient = HttpClients.createDefault();
+
+        String resultString = "";
+        CloseableHttpResponse response = null;
+        try {
+            // 创建uri
+            URIBuilder builder = new URIBuilder(url);
+            if (param != null) {
+                for (String key : param.keySet()) {
+                    builder.addParameter(key, param.get(key));
+                }
+            }
+            URI uri = builder.build();
+
+            // 创建http GET请求
+            HttpGet httpGet = new HttpGet(uri);
+
+            // 执行请求
+            response = httpclient.execute(httpGet);
+            // 判断返回状态是否为200
+            if (response.getStatusLine().getStatusCode() == 200) {
+                resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (response != null) {
+                    response.close();
+                }
+                httpclient.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return resultString;
+    }
+
+    public static String doGet(String url) {
+        return doGet(url, null);
+    }
+
+    public static String doPost(String url, Map<String, Object> param) {
+        // 创建Httpclient对象
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        CloseableHttpResponse response = null;
+        String resultString = "";
+        try {
+            // 创建Http Post请求
+            HttpPost httpPost = new HttpPost(url);
+            httpPost.setHeader("Content-Type", "charset=utf-8");
+            // 创建参数列表
+            if (param != null) {
+                List<NameValuePair> paramList = new ArrayList<>();
+                for (String key : param.keySet()) {
+                    paramList.add(new BasicNameValuePair(key, param.get(key).toString()));
+                }
+                // 模拟表单
+                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
+                httpPost.setEntity(entity);
+            }
+            // 执行http请求
+            response = httpClient.execute(httpPost);
+            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                response.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return resultString;
+    }
+
+    public static String doPost(String url) {
+        return doPost(url, null);
+    }
+
+    public static String doPostJson(String url, String json) {
+        // 创建Httpclient对象
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        CloseableHttpResponse response = null;
+        String resultString = "";
+        try {
+            // 创建Http Post请求
+            HttpPost httpPost = new HttpPost(url);
+            // 创建请求内容
+            StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
+            httpPost.setEntity(entity);
+            // 执行http请求
+            response = httpClient.execute(httpPost);
+            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                response.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return resultString;
+    }
+}

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません