Browse Source

实现物联网平台采集

欧阳劲驰 5 months ago
parent
commit
b8759a5cd0
53 changed files with 6795 additions and 163 deletions
  1. 3 1
      custom-gateway-app/pom.xml
  2. 1 1
      custom-gateway-app/src/main/resources/application-zydma.yml
  3. 5 1
      custom-gateway-app/src/main/resources/application.yml
  4. 20 15
      custom-gateway-core/pom.xml
  5. 235 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/components/DeviceIdGenerator.java
  6. 354 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/components/DeviceRegistry.java
  7. 83 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/components/InfluxDBRegistry.java
  8. 54 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/config/DeviceConfig.java
  9. 23 32
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/config/InfluxDbConfig.java
  10. 19 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/AreaCode.java
  11. 30 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/DeviceField.java
  12. 43 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/DeviceType.java
  13. 22 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/ExcelEnum.java
  14. 14 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/ExcelMetadata.java
  15. 14 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/InfluxdbMetadata.java
  16. 23 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/ProtocolType.java
  17. 1 1
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/TokenMetadata.java
  18. 41 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/ValueType.java
  19. 54 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/Device.java
  20. 40 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/DeviceExcel.java
  21. 95 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/DeviceKind.java
  22. 61 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/DeviceTag.java
  23. 27 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/InfluxDbClient.java
  24. 2 10
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/IntegrationKey.java
  25. 67 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/TypeDefine.java
  26. 24 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/mapper/DeviceKindMapper.java
  27. 24 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/mapper/TypeDefineMapper.java
  28. 25 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/properties/DeviceProperties.java
  29. 29 22
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/properties/InfluxDbProperties.java
  30. 1 1
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/properties/SecurityProperties.java
  31. 21 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/service/DeviceKindService.java
  32. 21 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/service/TypeDefineService.java
  33. 34 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/service/impl/DeviceKindServiceImpl.java
  34. 34 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/service/impl/TypeDefineServiceImpl.java
  35. 567 0
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/utils/ExcelUtil.java
  36. 35 34
      custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/utils/InfluxDbUtil.java
  37. 24 0
      custom-gateway-core/src/main/resources/mapper/DeviceKindMapper.xml
  38. 22 0
      custom-gateway-core/src/main/resources/mapper/TypeDefineMapper.xml
  39. 1 12
      custom-gateway-zydma/pom.xml
  40. 19 14
      custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/components/InfoSynchronizer.java
  41. 188 0
      custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/components/IotCollector.java
  42. 268 0
      custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/constants/IotPlatformMetadata.java
  43. 1 1
      custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/constants/MiddlePlatformMetadata.java
  44. 51 0
      custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/domain/IotPlatformData.java
  45. 53 0
      custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/domain/IotPlatformResult.java
  46. 3 3
      custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/domain/MiddlePlatformResult.java
  47. 1 1
      custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/bizmgr/InfoSyncManager.java
  48. 45 0
      custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/manager/IotManager.java
  49. 58 5
      custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/utils/CallingUtil.java
  50. 14 7
      db.yml
  51. 3895 0
      dev_map.yml
  52. 3 0
      dev_seq.json
  53. 3 2
      pom.xml

+ 3 - 1
custom-gateway-app/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>com.shkpr.service</groupId>
         <artifactId>kpr-custom-gateway</artifactId>
-        <version>1.0.0-dev</version>
+        <version>1.0.0</version>
     </parent>
 
     <!--工件名-->
@@ -76,6 +76,8 @@
                     <skipTests>true</skipTests>
                     <systemPropertyVariables>
                         <global.sql-config-path>../db.yml</global.sql-config-path>
+                        <device.map-path>../dev_map.yml</device.map-path>
+                        <device.seq-path>../dev_seq.json</device.seq-path>
                     </systemPropertyVariables>
                 </configuration>
             </plugin>

+ 1 - 1
custom-gateway-app/src/main/resources/application-zydma.yml

@@ -8,7 +8,7 @@ calling:
       url: http://223.75.194.87:8200/PandaCore/GCK
       access-key: lousunkongzhi
       secret-key: g+4UWJ6360SxDVu+9BRRQfOg0/tT+33o3S8Q5APMLIn+JQirprtdGd0cf5Y3WO7iiKo24T5mN1U697zHp/iGNA==
-    iot:
+    iot-platform:
       url: http://223.75.194.87:8200/pdserver
       access-key: Data
       secret-key: panda666.

+ 5 - 1
custom-gateway-app/src/main/resources/application.yml

@@ -47,4 +47,8 @@ temp-file:
   path-pattern: /common/temp-files
   resource-location: ./temp-files/
   cleanup-interval: PT1M
-  max-age: PT4H
+  max-age: PT4H
+#设备
+device:
+  map-path: ./dev_map.yml
+  seq-path: ./dev_seq.json

+ 20 - 15
custom-gateway-core/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>com.shkpr.service</groupId>
         <artifactId>kpr-custom-gateway</artifactId>
-        <version>1.0.0-dev</version>
+        <version>1.0.0</version>
     </parent>
 
     <!--工件名-->
@@ -50,16 +50,21 @@
             <groupId>org.postgresql</groupId>
             <artifactId>postgresql</artifactId>
         </dependency>
+        <!--influxdb-->
+        <dependency>
+            <groupId>org.influxdb</groupId>
+            <artifactId>influxdb-java</artifactId>
+        </dependency>
         <!--jjwt-->
         <dependency>
             <groupId>io.jsonwebtoken</groupId>
             <artifactId>jjwt</artifactId>
             <version>${jjwt.version}</version>
         </dependency>
-        <!--influxdb-->
+        <!--json-yml-->
         <dependency>
-            <groupId>org.influxdb</groupId>
-            <artifactId>influxdb-java</artifactId>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-yaml</artifactId>
         </dependency>
         <!--okhttp-->
         <dependency>
@@ -67,23 +72,23 @@
             <artifactId>okhttp</artifactId>
             <version>${okhttp.version}</version>
         </dependency>
+        <!--poi-->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
     </dependencies>
 
     <!--构建脚本-->
     <build>
         <!--插件项-->
         <plugins>
-            <!--maven-jar-->
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>default-jar</id>
-                        <phase>none</phase>
-                    </execution>
-                </executions>
-            </plugin>
             <!--maven-install-->
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>

+ 235 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/components/DeviceIdGenerator.java

@@ -0,0 +1,235 @@
+package com.shkpr.service.customgateway.core.components;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.customgateway.core.constants.AreaCode;
+import com.shkpr.service.customgateway.core.constants.DeviceType;
+import com.shkpr.service.customgateway.core.constants.LogFlagBusiType;
+import com.shkpr.service.customgateway.core.domain.Device;
+import com.shkpr.service.customgateway.core.domain.DeviceTag;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class DeviceIdGenerator {
+    /**
+     * log
+     */
+    private static final String CLASS_NAME = "DeviceIdGenerator";
+    private static final String BIZ_TYPE = LogFlagBusiType.BUSI_ALL.toStrValue();
+    /**
+     * 日期格式
+     */
+    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMdd");
+
+    /**
+     * 对象映射
+     */
+    private final ObjectMapper objectMapper;
+    /**
+     * 序列文件
+     */
+    private final File sequenceFile;
+    /**
+     * 每日序列
+     */
+    private final Map<LocalDate, Integer> dailySequences;
+
+    public DeviceIdGenerator(Builder builder) {
+        this.objectMapper = builder.objectMapper;
+        this.sequenceFile = builder.sequenceFile;
+        this.dailySequences = new ConcurrentHashMap<>();
+        if (builder.autoLoad) this.loadSequences();
+    }
+
+
+    /**
+     * @return 构建
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * 生成设备
+     *
+     * @param areaCode 区号
+     * @param type     类型
+     * @param sn       远传设备id
+     * @param name     设备名称
+     * @return 设备
+     */
+    public synchronized Device generateDevice(AreaCode areaCode, DeviceType type, List<DeviceTag> fields
+            , String sn, String name) {
+        if (areaCode == null || type == null || StringUtils.isAnyBlank(sn, name)) return null;
+
+        //构建设备
+        return new Device(generateDeviceId(areaCode, type), name, sn, type.getKey(), LocalDateTime.now(), fields);
+    }
+
+    /**
+     * 生成设备id
+     *
+     * @param areaCode 区号
+     * @param type     设备类型
+     * @return 设备id
+     */
+    public synchronized String generateDeviceId(AreaCode areaCode, DeviceType type) {
+        //当前日期
+        LocalDate now = LocalDate.now();
+        //下一个序列
+        int sequence = getNextSequence(now);
+        //组装id
+        return String.format("%03d%02d%s%04d", areaCode.getCode(), type.getCode(), now.format(formatter), sequence);
+    }
+
+    /**
+     * 获取下一个序号
+     *
+     * @param date 时间
+     * @return 序号
+     */
+    private int getNextSequence(LocalDate date) {
+        //当前序号
+        int currentSequence = dailySequences.getOrDefault(date, 0);
+        //下一个序号
+        int nextSequence = currentSequence + 1;
+
+        //检查序号是否超出范围
+        if (nextSequence > 9999) throw new IllegalStateException("当日设备序号已用尽,无法生成新的设备ID");
+
+        //存入序号,并同步
+        dailySequences.put(date, nextSequence);
+        syncSequences();
+        return nextSequence;
+    }
+
+    /**
+     * 加载序号数据
+     */
+    private void loadSequences() {
+        try {
+            if (sequenceFile.exists() && sequenceFile.isFile()) {
+                //配置文件存在,读取并设置设备序列
+                dailySequences.putAll(objectMapper.readValue(sequenceFile,
+                        objectMapper.getTypeFactory().constructMapType(Map.class, LocalDate.class, Integer.class)));
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME,
+                        "加载设备序号数据成功");
+            } else {
+                //不存在,则同步
+                syncSequences();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME,
+                        "配置文件不存在或为空,初始化空设备列表");
+            }
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_WARN, BIZ_TYPE, CLASS_NAME,
+                    String.format("加载设备序号数据失败,error:%s", e));
+        }
+    }
+
+    /**
+     * 同步序号
+     */
+    private void syncSequences() {
+        try {
+            //确保父目录存在
+            File parentDir = sequenceFile.getParentFile();
+            if (parentDir != null && !parentDir.exists()) {
+                boolean made = sequenceFile.getParentFile().mkdirs();
+                if (made) LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME
+                        , String.format("创建设备序列文件成功:%s", sequenceFile.getAbsoluteFile()));
+            }
+
+            objectMapper.writerWithDefaultPrettyPrinter().writeValue(sequenceFile, dailySequences);
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME
+                    , "同步设备序列成功");
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME,
+                    "保存设备序号数据失败,error:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 构建器
+     */
+    public static class Builder {
+        /**
+         * 映射
+         */
+        private ObjectMapper objectMapper;
+        /**
+         * 序列文件
+         */
+        private File sequenceFile;
+        /**
+         * 自动加序列置文件
+         */
+        private Boolean autoLoad = true;
+
+        public Builder() {
+            //默认的mapper
+            this.objectMapper = new ObjectMapper();
+        }
+
+        /**
+         * 配置文件路径
+         */
+        public Builder sequenceFile(String sequencePath) {
+            this.sequenceFile = new File(sequencePath);
+            return this;
+        }
+
+        /**
+         * 配置文件对象
+         */
+        public Builder sequenceFile(File sequenceFile) {
+            this.sequenceFile = sequenceFile;
+            return this;
+        }
+
+        /**
+         * object映射
+         */
+        public Builder objectMapper(ObjectMapper objectMapper) {
+            this.objectMapper = objectMapper;
+            return this;
+        }
+
+        /**
+         * 是否自动加载设备列表
+         */
+        public Builder autoLoad(boolean autoLoad) {
+            this.autoLoad = autoLoad;
+            return this;
+        }
+
+        /**
+         * 构建
+         */
+        public DeviceIdGenerator build() {
+            // 参数验证
+            if (sequenceFile == null) throw new IllegalStateException("配置文件路径不能为空");
+            if (objectMapper == null) throw new IllegalStateException("Object Mapper 不能为空");
+            if (autoLoad == null) autoLoad = true;
+
+            //文件验证
+            if (sequenceFile.exists()) {
+                if (!sequenceFile.isFile())
+                    throw new IllegalStateException("序列文件路径不是文件: " + sequenceFile.getAbsolutePath());
+                if (!sequenceFile.canRead() || !sequenceFile.canWrite())
+                    throw new IllegalStateException("序列文件无法读写: " + sequenceFile.getAbsolutePath());
+            } else LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME,
+                    "序列文件不存在,将在初始化时创建");
+
+            return new DeviceIdGenerator(this);
+        }
+    }
+}

+ 354 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/components/DeviceRegistry.java

@@ -0,0 +1,354 @@
+package com.shkpr.service.customgateway.core.components;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.customgateway.core.constants.DeviceField;
+import com.shkpr.service.customgateway.core.constants.DeviceType;
+import com.shkpr.service.customgateway.core.constants.ExcelEnum;
+import com.shkpr.service.customgateway.core.constants.LogFlagBusiType;
+import com.shkpr.service.customgateway.core.domain.Device;
+import com.shkpr.service.customgateway.core.domain.DeviceExcel;
+import com.shkpr.service.customgateway.core.utils.ExcelUtil;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 设备管理器
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+public class DeviceRegistry {
+    /**
+     * log
+     */
+    private static final String CLASS_NAME = "DeviceRegistry";
+    private static final String BIZ_TYPE = LogFlagBusiType.BUSI_ALL.toStrValue();
+
+    /**
+     * yml对象映射
+     */
+    private final ObjectMapper yamlMapper;
+    /**
+     * 配置文件
+     */
+    private final File configFile;
+    /**
+     * 设备列表
+     */
+    private List<Device> devices;
+
+    public DeviceRegistry(Builder builder) {
+        this.yamlMapper = builder.yamlMapper;
+        this.configFile = builder.configFile;
+        this.devices = new ArrayList<>();
+        if (builder.autoLoad) this.loadDevices();
+    }
+
+    /**
+     * @return 构建
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+
+    /**
+     * 注册设备
+     *
+     * @param device 设备
+     */
+    public boolean registerDevice(Device device) {
+        if (!ObjectUtils.allNotNull(device, device.getDeviceId(), device.getDeviceSn())) return false;
+
+        // 检查设备是否已存在
+        if (containsDevice(device.getDeviceId())) {
+            return updateDevice(device);
+        }
+
+        //添加设备并同步
+        devices.add(device);
+        syncDevices();
+
+
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME,
+                String.format("设备注册成功: %s", device.getDeviceId()));
+        return true;
+    }
+
+    /**
+     * 注销设备
+     *
+     * @param deviceId 设备id
+     */
+    public boolean unregisterDevice(String deviceId) {
+        if (StringUtils.isBlank(deviceId)) return false;
+
+        //删除设备
+        boolean removed = devices.removeIf(device -> Objects.equals(device.getDeviceId(), deviceId));
+        if (removed) {
+            syncDevices();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME,
+                    String.format("设备注销成功: %s", deviceId));
+        }
+
+        return removed;
+    }
+
+    /**
+     * 更新设备
+     *
+     * @param updatedDevice 设备
+     */
+    public boolean updateDevice(Device updatedDevice) {
+        if (!ObjectUtils.allNotNull(updatedDevice, updatedDevice.getDeviceId(), updatedDevice.getDeviceSn()))
+            return false;
+        //设备id
+        String deviceId = updatedDevice.getDeviceId();
+        //更新状态
+        boolean updated = false;
+
+        //查找并更新现有设备
+        for (int i = 0; i < devices.size(); i++) {
+            if (Objects.equals(devices.get(i).getDeviceId(), deviceId)) {
+                devices.set(i, updatedDevice);
+                updated = true;
+                break;
+            }
+        }
+
+        if (!updated) return false;
+
+        syncDevices();
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME,
+                String.format("设备更新成功: %s", deviceId));
+        return true;
+    }
+
+    /**
+     * 查询全部
+     *
+     * @return 设备列表
+     */
+    public List<Device> findAll() {
+        return new ArrayList<>(devices);
+    }
+
+
+    /**
+     * 根据设备id查询
+     *
+     * @return 设备
+     */
+    public Device findById(String deviceId) {
+        return devices.stream()
+                .filter(device -> Objects.equals(device.getDeviceId(), deviceId))
+                .findFirst()
+                .orElse(null);
+    }
+
+
+    /**
+     * 获取设备数量
+     */
+    public int getDeviceCount() {
+        return devices.size();
+    }
+
+    /**
+     * 检查设备是否存在
+     */
+    public boolean containsDevice(String deviceId) {
+        return devices.stream().anyMatch(device -> Objects.equals(device.getDeviceId(), deviceId));
+    }
+
+    /**
+     * 查找需要新增的设备
+     *
+     * @param deviceSns 新设备远传id
+     * @return 需要新增的设备远传id
+     */
+    public Set<String> findAddedDevices(Set<String> deviceSns) {
+        //已存在的设备id
+        Set<String> existingDeviceSns = devices.stream()
+                .map(Device::getDeviceSn)
+                .collect(Collectors.toSet());
+
+        //过滤不存在的设备
+        return deviceSns.stream()
+                .filter(StringUtils::isNoneBlank)
+                .filter(sn -> !existingDeviceSns.contains(sn))
+                .collect(Collectors.toSet());
+    }
+
+    /**
+     * 加载设备
+     */
+    public void loadDevices() {
+        try {
+            if (configFile.exists() && configFile.isFile()) {
+                //配置文件存在,读取并设置设备列表
+                devices = yamlMapper.readValue(configFile, yamlMapper.getTypeFactory()
+                        .constructCollectionType(List.class, Device.class));
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME
+                        , String.format("加载设备列表成功,设备数量:%d", devices.size()));
+            } else {
+                //不存在,设置空列表,并同步
+                devices = new ArrayList<>();
+                syncDevices();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME,
+                        "配置文件不存在或为空,初始化空设备列表");
+            }
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
+                    , String.format("加载设备列表失败,error:%s", e));
+        }
+    }
+
+    /**
+     * 同步设备
+     */
+    private void syncDevices() {
+        try {
+            //确保父目录存在
+            File parentDir = configFile.getParentFile();
+            if (parentDir != null && !parentDir.exists()) {
+                boolean made = configFile.getParentFile().mkdirs();
+                if (made) LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME
+                        , String.format("创建设备列表配置文件成功:%s", configFile.getAbsoluteFile()));
+            }
+
+            //配置文件存在,写入设备列表
+            yamlMapper.writerWithDefaultPrettyPrinter().writeValue(configFile, devices);
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME
+                    , String.format("同步设备列表成功,设备数量:%d", devices.size()));
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
+                    , String.format("同步设备列表失败,error:%s", e));
+        }
+    }
+
+    /**
+     * 导出设备
+     *
+     * @param path 路径
+     */
+    public void exportDevice(Path path) {
+        //类型映射
+        Map<String, String> typeMap = Arrays.stream(DeviceType.values())
+                .collect(Collectors.toMap(DeviceType::getKey, DeviceType::getName));
+        //字段映射
+        Map<String, String> fieldMap = Arrays.stream(DeviceField.values())
+                .collect(Collectors.toMap(DeviceField::getKey, DeviceField::getName));
+        List<DeviceExcel> excels  = this.devices.stream().map(device -> {
+            DeviceExcel deviceExcel = new DeviceExcel();
+
+            deviceExcel.setDeviceId(device.getDeviceId());
+            deviceExcel.setDeviceName(device.getDeviceName());
+            deviceExcel.setDeviceSn(device.getDeviceSn());
+            deviceExcel.setDeviceType(typeMap.get(device.getDeviceType()));
+
+            deviceExcel.setField(device.getTags().stream().map(it -> {
+                return fieldMap.get(it.getField());
+            }).collect(Collectors.joining("/")));
+
+            return deviceExcel;
+        }).collect(Collectors.toList());
+
+        try (OutputStream outputStream = Files.newOutputStream(path.toFile().toPath())) {
+
+            ExcelUtil.writeExcel(excels, DeviceExcel.class, outputStream, ExcelEnum.XLSX);
+
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 构建器
+     */
+    public static class Builder {
+        /**
+         * 映射
+         */
+        private ObjectMapper yamlMapper;
+        /**
+         * 配置文件
+         */
+        private File configFile;
+        /**
+         * 自动加载配置文件
+         */
+        private Boolean autoLoad = true;
+
+        public Builder() {
+            // 默认的mapper
+            YAMLFactory yamlFactory = new YAMLFactory();
+            this.yamlMapper = new ObjectMapper(yamlFactory);
+            this.yamlMapper.findAndRegisterModules();
+        }
+
+        /**
+         * 配置文件路径
+         */
+        public Builder configFile(String configPath) {
+            this.configFile = new File(configPath);
+            return this;
+        }
+
+        /**
+         * 配置文件对象
+         */
+        public Builder configFile(File configFile) {
+            this.configFile = configFile;
+            return this;
+        }
+
+        /**
+         * object映射
+         */
+        public Builder yamlMapper(ObjectMapper yamlMapper) {
+            this.yamlMapper = yamlMapper;
+            return this;
+        }
+
+        /**
+         * 是否自动加载设备列表
+         */
+        public Builder autoLoad(boolean autoLoad) {
+            this.autoLoad = autoLoad;
+            return this;
+        }
+
+        /**
+         * 构建
+         */
+        public DeviceRegistry build() {
+            // 参数验证
+            if (configFile == null) throw new IllegalStateException("配置文件路径不能为空");
+            if (yamlMapper == null) throw new IllegalStateException("YAML Mapper 不能为空");
+            if (autoLoad == null) autoLoad = true;
+
+            //文件验证
+            if (configFile.exists()) {
+                if (!configFile.isFile())
+                    throw new IllegalStateException("配置文件路径不是文件: " + configFile.getAbsolutePath());
+                if (!configFile.canRead() || !configFile.canWrite())
+                    throw new IllegalStateException("配置文件无法读写: " + configFile.getAbsolutePath());
+            } else LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME,
+                    "配置文件不存在,将在初始化时创建");
+
+            return new DeviceRegistry(this);
+        }
+    }
+}

+ 83 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/components/InfluxDBRegistry.java

@@ -0,0 +1,83 @@
+package com.shkpr.service.customgateway.core.components;
+
+import com.shkpr.service.customgateway.core.domain.InfluxDbClient;
+import org.apache.commons.collections4.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * influxDb客户端管理器
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+public class InfluxDBRegistry {
+    /**
+     * 客户端集合
+     */
+    List<InfluxDbClient> clients;
+
+
+    public InfluxDBRegistry(Builder builder) {
+        this.clients = builder.clients;
+    }
+
+    /**
+     * @return 构建
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * 查询客户端集合
+     *
+     * @return 客户端集合
+     */
+    public List<InfluxDbClient> clients() {
+        return new ArrayList<>(clients);
+    }
+
+
+    /**
+     * 构建器
+     */
+    public static class Builder {
+        /**
+         * 客户端
+         */
+        private final List<InfluxDbClient> clients;
+
+        public Builder() {
+            clients = new ArrayList<>();
+        }
+
+        /**
+         * 添加客户端
+         */
+        public Builder addClient(InfluxDbClient client) {
+            this.clients.add(client);
+            return this;
+        }
+
+        /**
+         * 添加客户端
+         */
+        public Builder addClients(List<InfluxDbClient> clients) {
+            this.clients.addAll(clients);
+            return this;
+        }
+
+
+        /**
+         * 构建
+         */
+        public InfluxDBRegistry build() {
+            // 参数验证
+            if (CollectionUtils.isEmpty(this.clients)) throw new IllegalStateException("客户端不能为空");
+
+            return new InfluxDBRegistry(this);
+        }
+    }
+}

+ 54 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/config/DeviceConfig.java

@@ -0,0 +1,54 @@
+package com.shkpr.service.customgateway.core.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.shkpr.service.customgateway.core.components.DeviceIdGenerator;
+import com.shkpr.service.customgateway.core.components.DeviceRegistry;
+import com.shkpr.service.customgateway.core.properties.DeviceProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 设备配置
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Configuration
+@EnableConfigurationProperties(DeviceProperties.class)
+public class DeviceConfig {
+    final
+    DeviceProperties deviceProperties;
+    final
+    ObjectMapper objectMapper;
+
+    public DeviceConfig(DeviceProperties deviceProperties, ObjectMapper objectMapper) {
+        this.deviceProperties = deviceProperties;
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * @return 设备注册器
+     */
+    @Bean
+    public DeviceRegistry deviceRegistry() {
+        return DeviceRegistry.builder()
+                .configFile(deviceProperties.getMapPath())
+                .yamlMapper(new ObjectMapper(new YAMLFactory()))
+                .autoLoad(true)
+                .build();
+    }
+
+    /**
+     * @return 设备id生成器
+     */
+    @Bean
+    public DeviceIdGenerator deviceIdGenerator() {
+        return DeviceIdGenerator.builder()
+                .sequenceFile(deviceProperties.getSeqPath())
+                .objectMapper(objectMapper)
+                .autoLoad(true)
+                .build();
+    }
+}

+ 23 - 32
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/config/InfluxDbConfig.java

@@ -1,18 +1,19 @@
 package com.shkpr.service.customgateway.core.config;
 
+import com.shkpr.service.customgateway.core.components.InfluxDBRegistry;
+import com.shkpr.service.customgateway.core.domain.InfluxDbClient;
 import com.shkpr.service.customgateway.core.io.YamlPropertySourceFactory;
 import com.shkpr.service.customgateway.core.properties.InfluxDbProperties;
 import okhttp3.OkHttpClient;
-import org.influxdb.InfluxDB;
-import org.influxdb.impl.InfluxDBImpl;
 import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.influx.InfluxDbOkHttpClientBuilderProvider;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.PropertySource;
 
-import java.time.Duration;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * influxDb配置
@@ -21,26 +22,19 @@ import java.time.Duration;
  * @since 1.0.0
  */
 @Configuration
+@EnableConfigurationProperties(InfluxDbProperties.class)
 @PropertySource(value = "file:${global.sql-config-path}", ignoreResourceNotFound = true, encoding = "utf-8", factory = YamlPropertySourceFactory.class)
 public class InfluxDbConfig {
     final
     OkHttpClient.Builder builder;
-
-    @Value("${spring.influx.url:}")
-    private String url = "";
-    @Value("${spring.influx.user:}")
-    private String user = "";
-    @Value("${spring.influx.password:}")
-    private String password = "";
-    @Value("${spring.influx.database:}")
-    private String database = "";
-    @Value("${spring.influx.read-timeout:}")
-    private String readTimeout = "";
+    final
+    InfluxDbProperties influxDbProperties;
 
     public InfluxDbConfig(ObjectProvider<InfluxDbOkHttpClientBuilderProvider> builder
-            , ObjectProvider<OkHttpClient.Builder> deprecatedBuilder) {
+            , ObjectProvider<OkHttpClient.Builder> deprecatedBuilder, InfluxDbProperties influxDbProperties) {
         this.builder = determineBuilder(builder.getIfAvailable(),
                 deprecatedBuilder.getIfAvailable());
+        this.influxDbProperties = influxDbProperties;
     }
 
     /**
@@ -57,25 +51,22 @@ public class InfluxDbConfig {
                 : new OkHttpClient.Builder();
     }
 
-
-    /**
-     * influxDb属性
-     *
-     * @return influxDb属性
-     */
-    @Bean
-    public InfluxDbProperties influxDbProperties() {
-        return new InfluxDbProperties(url, user, password, database, Duration.ofMillis(Long.parseLong(readTimeout)));
-    }
-
     /**
-     * influxDb客户端
+     * influxDb注册器
      *
-     * @return influxDb客户端
+     * @return influxDb注册器
      */
     @Bean
-    public InfluxDB influxDB() {
-        this.builder.readTimeout(Duration.ofMillis(Long.parseLong(readTimeout)));
-        return new InfluxDBImpl(url, user, password, this.builder);
+    public InfluxDBRegistry influxDBRegistry() {
+        this.builder.readTimeout(influxDbProperties.getReadTimeout());
+        //客户端集合
+        List<InfluxDbClient> clients = influxDbProperties.getClients().stream()
+                .map(client -> new InfluxDbClient(client.getUrl(), client.getUser(), client.getPassword()
+                        , client.getDatabase(), this.builder))
+                .collect(Collectors.toList());
+        //注册
+        return InfluxDBRegistry.builder()
+                .addClients(clients)
+                .build();
     }
 }

+ 19 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/AreaCode.java

@@ -0,0 +1,19 @@
+package com.shkpr.service.customgateway.core.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 区号
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Getter
+@AllArgsConstructor
+public enum AreaCode {
+    //枣阳
+    ZAO_YANG(710),
+    ;
+    private final Integer code;
+}

+ 30 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/DeviceField.java

@@ -0,0 +1,30 @@
+package com.shkpr.service.customgateway.core.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 设备字段
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Getter
+@AllArgsConstructor
+public enum DeviceField {
+    //瞬时流量
+    FLOW_CUR("flow_cur", "瞬时流量"),
+    //正向累积流量读数
+    FLOW_TOTAL_POS("flow_total_pos", "正向累计流量读数"),
+    //反向累积流量读数
+    FLOW_TOTAL_REV("flow_total_rev", "反向累计流量读数"),
+    ;
+    /**
+     * 标识
+     */
+    private final String key;
+    /**
+     * 名称
+     */
+    private final String name;
+}

+ 43 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/DeviceType.java

@@ -0,0 +1,43 @@
+package com.shkpr.service.customgateway.core.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static com.shkpr.service.customgateway.core.constants.DeviceField.*;
+
+/**
+ * 设备类型
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Getter
+@AllArgsConstructor
+public enum DeviceType {
+    //流量计
+    FLOW("flow", "流量计", 2, "WaterMeter", Arrays.asList(FLOW_CUR, FLOW_TOTAL_POS, FLOW_TOTAL_REV)),
+    ;
+    /**
+     * 标识
+     */
+    private final String key;
+    /**
+     * 名称
+     */
+    private final String name;
+    /**
+     * 代码
+     */
+    private final Integer code;
+    /**
+     * 表名
+     */
+    private final String measurement;
+    /**
+     * 包含字段
+     */
+    private final List<DeviceField> fields;
+}

+ 22 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/ExcelEnum.java

@@ -0,0 +1,22 @@
+package com.shkpr.service.customgateway.core.constants;
+
+/**
+ * excel枚举
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+public enum ExcelEnum {
+    /**
+     * csv
+     */
+    CSV,
+    /**
+     * xls
+     */
+    XLS,
+    /**
+     * xlsx
+     */
+    XLSX
+}

+ 14 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/ExcelMetadata.java

@@ -0,0 +1,14 @@
+package com.shkpr.service.customgateway.core.constants;
+
+/**
+ * excel元数据
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+public interface ExcelMetadata {
+    //头所在行
+    Integer HEADER_ROW_NUM = 1;
+    //数据开始行
+    Integer DATA_ROW_NUM = 2;
+}

+ 14 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/InfluxdbMetadata.java

@@ -22,6 +22,20 @@ public interface InfluxdbMetadata {
     }
 
     /**
+     * 标识
+     */
+    interface Tags {
+        /**
+         * 设备编码
+         */
+        String DEVICE_ID = "dev_id";
+        /**
+         * 设备编码
+         */
+        String DEVICE_SN = "dev_sn";
+    }
+
+    /**
      * Sql
      */
     interface Sql {

+ 23 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/ProtocolType.java

@@ -0,0 +1,23 @@
+package com.shkpr.service.customgateway.core.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 协议类型
+ *
+ * @author 欧阳劲驰
+ * @since 0.0.1
+ */
+@Getter
+@AllArgsConstructor
+public enum ProtocolType {
+    //json
+    JSON("json"),
+    //modbus
+    MODBUS("modbus");
+    /**
+     * 标识
+     */
+    private final String key;
+}

+ 1 - 1
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/TokenMetadata.java

@@ -4,7 +4,7 @@ package com.shkpr.service.customgateway.core.constants;
  * influxdb元数据
  *
  * @author 欧阳劲驰
- * @since 0.0.1-dev
+ * @since 1.0.0
  */
 public interface TokenMetadata {
     /**

+ 41 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/constants/ValueType.java

@@ -0,0 +1,41 @@
+package com.shkpr.service.customgateway.core.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 值类型枚举
+ *
+ * @author 欧阳劲驰
+ * @since 0.1.0
+ */
+@Getter
+@AllArgsConstructor
+public enum ValueType {
+    /**
+     * 无符号整型
+     */
+    UNSIGNED_INT("uint"),
+    /**
+     * 有符号整型
+     */
+    SIGNED_INT("int"),
+    /**
+     * 单经度浮点
+     */
+    SINGLE_FLOAT("float"),
+    /**
+     * 双精度浮点
+     */
+    DOUBLE_FLOAT("double"),
+    /**
+     * 字符串
+     */
+    ASCII("ascii"),
+    /**
+     * 布尔
+     */
+    BOOLEAN("bool"),
+    ;
+    private final String key;
+}

+ 54 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/Device.java

@@ -0,0 +1,54 @@
+package com.shkpr.service.customgateway.core.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 设备信息
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Device {
+    /**
+     * 设备id
+     */
+    private String deviceId;
+    /**
+     * 设备名称
+     */
+    private String deviceName;
+    /**
+     * 远传id
+     */
+    private String deviceSn;
+    /**
+     * 设备类型
+     */
+    private String deviceType;
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh_CN", timezone = "Asia/Shanghai")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonSerialize(using = LocalDateTimeSerializer.class)
+    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
+    private LocalDateTime createTime;
+    /**
+     * 字段
+     */
+    private List<DeviceTag> tags;
+}

+ 40 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/DeviceExcel.java

@@ -0,0 +1,40 @@
+package com.shkpr.service.customgateway.core.domain;
+
+import com.shkpr.service.customgateway.core.annotation.ExcelMapping;
+import lombok.Data;
+
+/**
+ * 设备excel信息
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Data
+public class DeviceExcel {
+    /**
+     * 设备名称
+     */
+    @ExcelMapping("设备名称")
+    private String deviceName;
+    /**
+     * 设备id
+     */
+    @ExcelMapping("设备ID")
+    private String deviceId;
+    /**
+     * 远传id
+     */
+    @ExcelMapping("远传ID")
+    private String deviceSn;
+    /**
+     * 设备类型
+     */
+    @ExcelMapping("设备类型")
+    private String deviceType;
+    /**
+     * 字段
+     */
+    @ExcelMapping("字段")
+    private String field;
+
+}

+ 95 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/DeviceKind.java

@@ -0,0 +1,95 @@
+package com.shkpr.service.customgateway.core.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+/**
+ * 设备种类
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Data
+public class DeviceKind {
+    /**
+     * 主键ID
+     */
+    private Integer id;
+
+    /**
+     * 全局唯一id
+     */
+    private String uid;
+
+    /**
+     * 设备种类名称
+     */
+    private String name;
+
+    /**
+     * 指定种类下的默认特征参数,取值格式为json串
+     */
+    private String feature;
+
+    /**
+     * 参数1
+     */
+    private String param1;
+
+    /**
+     * 参数2
+     */
+    private String param2;
+
+    /**
+     * 备注说明
+     */
+    private String remark;
+
+    /**
+     * 设备种类唯一标识符
+     */
+    private String key;
+
+    /**
+     * 对应gis中的点图层类型列表JSON串
+     */
+    private String gisType;
+
+    /**
+     * 标签
+     */
+    private String tags;
+
+    /**
+     * 对应的调度预案事件类型
+     */
+    private Integer warn;
+
+    /**
+     * 必须的采集字段列表
+     */
+    private String musts;
+
+    /**
+     * 对应influxdb中的表名
+     */
+    private String measurement;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh_CN", timezone = "Asia/Shanghai")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh_CN", timezone = "Asia/Shanghai")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+}

+ 61 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/DeviceTag.java

@@ -0,0 +1,61 @@
+package com.shkpr.service.customgateway.core.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 设备标签
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class DeviceTag {
+    /**
+     * 标签
+     */
+    private String tag;
+    /**
+     * 协议标识
+     */
+    private String protocol;
+    /**
+     * 表名
+     */
+    private String measurement;
+    /**
+     * 字段key
+     */
+    private String field;
+    /**
+     * 值类型
+     */
+    private String valueType;
+    /**
+     * 最小值
+     */
+    private Double minValue;
+    /**
+     * 最大值
+     */
+    private Double maxValue;
+    /**
+     * 同步key
+     */
+    private String syncField;
+    /**
+     * 计算
+     */
+    private String calcFormula;
+
+    public DeviceTag(String tag, String protocol,String measurement, String field, String valueType) {
+        this.tag = tag;
+        this.protocol = protocol;
+        this.measurement = measurement;
+        this.field = field;
+        this.valueType = valueType;
+    }
+}

+ 27 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/InfluxDbClient.java

@@ -0,0 +1,27 @@
+package com.shkpr.service.customgateway.core.domain;
+
+import lombok.Getter;
+import okhttp3.OkHttpClient;
+import org.influxdb.impl.InfluxDBImpl;
+
+/**
+ * influxDb客户端
+ * <p>
+ * {@link org.influxdb.impl.InfluxDBImpl}
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Getter
+public class InfluxDbClient extends InfluxDBImpl {
+    /**
+     * 数据库
+     */
+    private final String database;
+
+    public InfluxDbClient(String url, String username, String password, String database, OkHttpClient.Builder client) {
+        super(url, username, password, client);
+        super.setDatabase(database);
+        this.database = database;
+    }
+}

+ 2 - 10
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/IntegrationKey.java

@@ -11,13 +11,13 @@ import lombok.NoArgsConstructor;
  * @since 1.0.0
  */
 @Data
-@NoArgsConstructor(force = true)
+@NoArgsConstructor
 @AllArgsConstructor
 public class IntegrationKey {
     /**
      * 时间戳
      */
-    private final Long timestamp;
+    private Long timestamp;
     /**
      * key
      */
@@ -26,12 +26,4 @@ public class IntegrationKey {
      * Secret
      */
     private String secretKey;
-    /**
-     * 签名
-     */
-    private String sign;
-    /**
-     * 密钥
-     */
-    private String token;
 }

+ 67 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/domain/TypeDefine.java

@@ -0,0 +1,67 @@
+package com.shkpr.service.customgateway.core.domain;
+
+import lombok.Data;
+
+/**
+ * 自定义类型集合表
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Data
+public class TypeDefine {
+    /**
+     *
+     */
+    private Integer id;
+
+    /**
+     * 自定义类型中的唯一标识符
+     */
+    private String key;
+
+    /**
+     * 自定义类型所属种类;0 -- 设备标记字段;1 -- 分区主类型; 2 -- 分区子类型; 3 -- 用水类型;4 -- 站点类型
+     */
+    private Short kind;
+
+    /**
+     * 自定义类型名称
+     */
+    private String name;
+
+    /**
+     * 备注说明
+     */
+    private String remark;
+
+    /**
+     * 参数1
+     */
+    private String param1;
+
+    /**
+     * 参数2
+     */
+    private String param2;
+
+    /**
+     * 排列顺序
+     */
+    private Short ordering;
+
+    /**
+     *
+     */
+    private String alias;
+
+    /**
+     * 性质用途
+     */
+    private String nature;
+
+    /**
+     * 特性
+     */
+    private String feature;
+}

+ 24 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/mapper/DeviceKindMapper.java

@@ -0,0 +1,24 @@
+package com.shkpr.service.customgateway.core.mapper;
+
+import com.shkpr.service.customgateway.core.domain.DeviceKind;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 设备种类表mapper
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Mapper
+public interface DeviceKindMapper {
+    /**
+     * 根据key查询
+     *
+     * @param keys key集合
+     * @return 实体
+     */
+    List<DeviceKind> findByKeys(@Param("keys") List<String> keys);
+}

+ 24 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/mapper/TypeDefineMapper.java

@@ -0,0 +1,24 @@
+package com.shkpr.service.customgateway.core.mapper;
+
+import com.shkpr.service.customgateway.core.domain.TypeDefine;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 类型表mapper
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Mapper
+public interface TypeDefineMapper {
+    /**
+     * 根据key查询
+     *
+     * @param keys key集合
+     * @return 实体
+     */
+    List<TypeDefine> findByKeys(@Param("keys") List<String> keys);
+}

+ 25 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/properties/DeviceProperties.java

@@ -0,0 +1,25 @@
+package com.shkpr.service.customgateway.core.properties;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * 设备属性
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Getter
+@Setter
+@ConfigurationProperties(prefix = "device")
+public class DeviceProperties {
+    /**
+     * 映射文件路径
+     */
+    private String mapPath;
+    /**
+     * 序列文件路径
+     */
+    private String seqPath;
+}

+ 29 - 22
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/properties/InfluxDbProperties.java

@@ -1,46 +1,53 @@
 package com.shkpr.service.customgateway.core.properties;
 
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
+import lombok.*;
+import org.springframework.boot.context.properties.ConfigurationProperties;
 
 import java.time.Duration;
+import java.util.List;
 
 /**
  * influxdb属性
  * <p>spring自动装配有同名bean,务必不要写同名字段</p>
  *
  * @author 欧阳劲驰
- * @since 0.0.1-dev
+ * @since 1.0.0
  */
 @Getter
 @Setter
 @NoArgsConstructor
 @AllArgsConstructor
+@ConfigurationProperties(prefix = "spring.influx")
 public class InfluxDbProperties {
     /**
-     * 连接地址
-     */
-    private String url;
-
-    /**
-     * 用户名
-     */
-    private String user;
-
-    /**
-     * 密码
+     * 读取超时时间
      */
-    private String password;
-
+    private Duration readTimeout = Duration.ofSeconds(60);
     /**
-     * 数据库
+     * 客户端集合
      */
-    private String database;
+    private List<Client> clients;
 
     /**
-     * 读取超时时间
+     * 客户端
      */
-    private Duration readTimeout = Duration.ofSeconds(60);
+    @Data
+    public static class Client {
+        /**
+         * 连接地址
+         */
+        private String url;
+        /**
+         * 用户名
+         */
+        private String user;
+        /**
+         * 密码
+         */
+        private String password;
+        /**
+         * 数据库
+         */
+        private String database;
+    }
 }

+ 1 - 1
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/properties/SecurityProperties.java

@@ -13,7 +13,7 @@ import java.util.List;
  * 认证属性
  *
  * @author 欧阳劲驰
- * @since 0.0.1-dev
+ * @since 1.0.0
  */
 @Getter
 @Setter

+ 21 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/service/DeviceKindService.java

@@ -0,0 +1,21 @@
+package com.shkpr.service.customgateway.core.service;
+
+import com.shkpr.service.customgateway.core.domain.DeviceKind;
+
+import java.util.List;
+
+/**
+ * 设备种类表service
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+public interface DeviceKindService {
+    /**
+     * 根据key查询
+     *
+     * @param keys key集合
+     * @return 实体
+     */
+    List<DeviceKind> findByKeys(List<String> keys);
+}

+ 21 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/service/TypeDefineService.java

@@ -0,0 +1,21 @@
+package com.shkpr.service.customgateway.core.service;
+
+import com.shkpr.service.customgateway.core.domain.TypeDefine;
+
+import java.util.List;
+
+/**
+ * 类型表servie
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+public interface TypeDefineService {
+    /**
+     * 根据key查询
+     *
+     * @param keys key集合
+     * @return 实体
+     */
+    List<TypeDefine> findByKeys(List<String> keys);
+}

+ 34 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/service/impl/DeviceKindServiceImpl.java

@@ -0,0 +1,34 @@
+package com.shkpr.service.customgateway.core.service.impl;
+
+import com.shkpr.service.customgateway.core.domain.DeviceKind;
+import com.shkpr.service.customgateway.core.mapper.DeviceKindMapper;
+import com.shkpr.service.customgateway.core.service.DeviceKindService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 设备种类表service
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Service
+public class DeviceKindServiceImpl implements DeviceKindService {
+    final
+    DeviceKindMapper deviceKindMapper;
+
+    public DeviceKindServiceImpl(DeviceKindMapper deviceKindMapper) {
+        this.deviceKindMapper = deviceKindMapper;
+    }
+
+    /**
+     * 根据key查询
+     *
+     * @param keys key集合
+     * @return 实体
+     */
+    public List<DeviceKind> findByKeys(List<String> keys) {
+        return deviceKindMapper.findByKeys(keys);
+    }
+}

+ 34 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/service/impl/TypeDefineServiceImpl.java

@@ -0,0 +1,34 @@
+package com.shkpr.service.customgateway.core.service.impl;
+
+import com.shkpr.service.customgateway.core.domain.TypeDefine;
+import com.shkpr.service.customgateway.core.mapper.TypeDefineMapper;
+import com.shkpr.service.customgateway.core.service.TypeDefineService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 类型表servie
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Service
+public class TypeDefineServiceImpl implements TypeDefineService {
+    final
+    TypeDefineMapper typeDefineMapper;
+
+    public TypeDefineServiceImpl(TypeDefineMapper typeDefineMapper) {
+        this.typeDefineMapper = typeDefineMapper;
+    }
+
+    /**
+     * 根据key查询
+     *
+     * @param keys key集合
+     * @return 实体
+     */
+    public List<TypeDefine> findByKeys(List<String> keys) {
+        return typeDefineMapper.findByKeys(keys);
+    }
+}

+ 567 - 0
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/utils/ExcelUtil.java

@@ -0,0 +1,567 @@
+package com.shkpr.service.customgateway.core.utils;
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.customgateway.core.annotation.ExcelMapping;
+import com.shkpr.service.customgateway.core.constants.ExcelEnum;
+import com.shkpr.service.customgateway.core.constants.ExcelMetadata;
+import com.shkpr.service.customgateway.core.constants.LogFlagBusiType;
+import org.apache.commons.collections4.map.CaseInsensitiveMap;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigDecimal;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * excel工具类
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.0
+ */
+public class ExcelUtil {
+    /**
+     * log
+     */
+    private static final String CLASS_NAME = "ExcelUtil";
+    private static final String BIZ_TYPE = LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue();
+    /**
+     * 默认页名
+     */
+    private static final String DEFAULT_SHEET_NAME = "页1";
+
+    /**
+     * 解析excel
+     *
+     * @param path 文件地址
+     * @return 数据(key : 页名, v : 数据)
+     */
+    public static Map<String, List<Map<String, String>>> parseExcel(String path) throws InterruptedException {
+        //excel枚举
+        ExcelEnum excelEnum = null;
+        //文件后缀
+        String ext = path.substring(path.lastIndexOf("."));
+        //根据后缀名指定枚举
+        if (".xls".equalsIgnoreCase(ext)) excelEnum = ExcelEnum.XLS;
+        else if (".xlsx".equalsIgnoreCase(ext)) excelEnum = ExcelEnum.XLS;
+        if (excelEnum == null) return null;
+        //解析文件
+        try {
+            return parseExcel(Files.newInputStream(Paths.get(path)), excelEnum);
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
+                    , String.format("excel文件解释失败 msg:%s", e.getMessage())
+            );
+            return null;
+        }
+    }
+
+    /**
+     * 解析excel
+     *
+     * @param inputStream 输入流
+     * @param excelEnum   excel类型
+     * @return 数据(key : 页名, v : 数据)
+     */
+    public static Map<String, List<Map<String, String>>> parseExcel(InputStream inputStream, ExcelEnum excelEnum) throws InterruptedException {
+        if (inputStream == null || excelEnum == null || excelEnum == ExcelEnum.CSV) return null;
+        //读取输入流
+        try (Workbook workbook = excelEnum == ExcelEnum.XLSX ? new XSSFWorkbook(inputStream) : new HSSFWorkbook(inputStream)) {
+            if (workbook.getNumberOfSheets() <= 0) return null;
+            //结果
+            Map<String, List<Map<String, String>>> results = new HashMap<>(workbook.getNumberOfSheets());
+            //遍历页,并解析
+            for (Sheet sheet : workbook) results.put(sheet.getSheetName().trim(), parseSheet(sheet));
+            return results;
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
+                    , String.format("excel文件解释失败 msg:%s", e.getMessage())
+            );
+            return null;
+        }
+    }
+
+    /**
+     * 解析页
+     *
+     * @param sheet 页
+     * @return 数据
+     */
+    private static List<Map<String, String>> parseSheet(Sheet sheet) throws InterruptedException {
+        //数据集合
+        List<Map<String, String>> dataList = new ArrayList<>();
+        if (sheet == null) return dataList;
+        //行数
+        int rowsNum = sheet.getPhysicalNumberOfRows();
+        if (rowsNum <= 1) return dataList;
+        //页头行
+        Row headerRow = sheet.getRow(Math.max(ExcelMetadata.HEADER_ROW_NUM - 1, 0));
+        //字段映射
+        Map<Integer, String> fieldMap = new HashMap<>();
+        for (Cell cell : headerRow) {
+            //检查线程中断,并响应
+            if (Thread.interrupted()) throw new InterruptedException();
+            //获取值,如不为空,则存入索引关系
+            String cellValue = getCellValue(cell);
+            if (!StringUtils.isEmpty(cellValue)) {
+                fieldMap.put(cell.getColumnIndex(), cellValue);
+            }
+        }
+        //遍历行
+        for (Row row : sheet) {
+            //检查线程中断,并响应
+            if (Thread.interrupted()) throw new InterruptedException();
+            //跳过非数据行
+            if (row == null || row.getRowNum() < (ExcelMetadata.DATA_ROW_NUM - 1)) continue;
+            //跳过空行
+            if (IntStream.range(row.getFirstCellNum(), row.getLastCellNum())
+                    .mapToObj(row::getCell)
+                    .allMatch(cell -> cell == null || cell.getCellTypeEnum() == CellType.BLANK)) continue;
+            //数据
+            Map<String, String> data = new CaseInsensitiveMap<>();
+            //遍历字段映射
+            for (Map.Entry<Integer, String> entry : fieldMap.entrySet()) {
+                //获取单元格
+                Cell cell = row.getCell(entry.getKey());
+                //设置值
+                data.put(entry.getValue(), getCellValue(cell));
+            }
+            //添加数据
+            dataList.add(data);
+        }
+
+        return dataList;
+    }
+
+    /**
+     * 解析excel
+     *
+     * @param path  文件地址
+     * @param clazz 数据类型
+     * @param <E>   数据泛形
+     * @return 数据集合
+     */
+    public static <E> Map<String, List<E>> parseExcel(String path, Class<E> clazz) {
+        //excel枚举
+        ExcelEnum excelEnum = null;
+        //文件后缀
+        String ext = path.substring(path.lastIndexOf("."));
+        //根据后缀名指定枚举
+        if (".xls".equalsIgnoreCase(ext)) excelEnum = ExcelEnum.XLS;
+        else if (".xlsx".equalsIgnoreCase(ext)) excelEnum = ExcelEnum.XLS;
+        if (excelEnum == null) return null;
+        //解析文件
+        try {
+            return parseExcel(Files.newInputStream(Paths.get(path)), clazz, excelEnum);
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
+                    , String.format("excel文件解释失败 msg:%s", e.getMessage())
+            );
+            return null;
+        }
+    }
+
+    /**
+     * 解析excel
+     *
+     * @param inputStream input流
+     * @param clazz       数据类型
+     * @param excelEnum   excel枚举
+     * @param <E>         数据泛形
+     * @return 数据集合
+     */
+    public static <E> Map<String, List<E>> parseExcel(InputStream inputStream, Class<E> clazz, ExcelEnum excelEnum) {
+        if (inputStream == null || excelEnum == null || excelEnum == ExcelEnum.CSV) return null;
+        //读取表
+        try (Workbook workbook = excelEnum == ExcelEnum.XLSX ? new XSSFWorkbook(inputStream) : new HSSFWorkbook(inputStream)) {
+            if (workbook.getNumberOfSheets() <= 0) return null;
+            //结果
+            Map<String, List<E>> results = new HashMap<>(workbook.getNumberOfSheets());
+            //遍历页,并解析
+            for (Sheet sheet : workbook)
+                results.put(sheet.getSheetName().trim(), parseSheet(sheet, clazz));
+            return results;
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
+                    , String.format("excel文件解释失败 msg:%s", e.getMessage())
+            );
+            return null;
+        }
+    }
+
+    /**
+     * 解析页
+     *
+     * @param sheet 页
+     * @param clazz 类型
+     * @param <E>   数据泛形
+     * @return 数据
+     */
+    private static <E> List<E> parseSheet(Sheet sheet, Class<E> clazz) {
+        try {
+            //数据集合
+            List<E> dataList = new ArrayList<>();
+            if (sheet == null) return dataList;
+            //行数
+            int rowsNum = sheet.getPhysicalNumberOfRows();
+            if (rowsNum <= 1) return dataList;
+            //页头行
+            Row headerRow = sheet.getRow(Math.max(ExcelMetadata.HEADER_ROW_NUM - 1, 0));
+            //字段映射
+            Map<Integer, Field> fieldMap = Arrays.stream(clazz.getDeclaredFields())
+                    //过滤需要导出的字段
+                    .filter(f -> f.isAnnotationPresent(ExcelMapping.class))
+                    //设置字段公开
+                    .peek(f -> f.setAccessible(true))
+                    .collect(Collectors.toMap(
+                            f -> {
+                                //获取excel映射值
+                                String excelMappingValue = f.getAnnotation(ExcelMapping.class).value();
+                                //获取对应的索引
+                                return IntStream.range(0, headerRow.getLastCellNum())
+                                        //过滤相同的值
+                                        .filter(index -> headerRow.getCell(index).getStringCellValue().equals(excelMappingValue))
+                                        .findFirst().orElse(-1);
+                            }, Function.identity(),
+                            (it1, it2) -> it2,
+                            CaseInsensitiveMap::new
+                    ));
+            //遍历行
+            for (Row row : sheet) {
+                //跳过非数据
+                if (row == null || row.getRowNum() < (ExcelMetadata.DATA_ROW_NUM - 1)) continue;
+                //跳过空行
+                if (IntStream.range(row.getFirstCellNum(), row.getLastCellNum())
+                        .mapToObj(row::getCell)
+                        .allMatch(cell -> cell == null || cell.getCellTypeEnum() == CellType.BLANK)) continue;
+                //实列化数据
+                E data = clazz.getDeclaredConstructor().newInstance();
+                //遍历字段映射
+                for (Map.Entry<Integer, Field> fieldEntry : fieldMap.entrySet()) {
+                    //跳过未找到的字段
+                    if (fieldEntry.getKey() == -1) continue;
+                    //获取单元格
+                    Cell cell = row.getCell(fieldEntry.getKey());
+                    //设置值
+                    setObjValue(data, fieldEntry.getValue(), getCellValue(cell));
+                }
+                //添加数据
+                dataList.add(data);
+            }
+            return dataList;
+        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
+                 IllegalAccessException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
+                    , String.format("excel页解释失败 msg:%s", e.getMessage())
+            );
+            return null;
+        }
+    }
+
+    /**
+     * 写入excel
+     *
+     * @param dates        数据
+     * @param headers      表头信息
+     * @param outputStream 输出流
+     * @param excelEnum    excel枚举
+     */
+    public static void writeExcel(Map<String, List<Map<String, Object>>> dates, Map<String, Map<String, String>> headers
+            , OutputStream outputStream, ExcelEnum excelEnum) {
+        //创建表
+        try (Workbook workbook = excelEnum == ExcelEnum.XLSX ? new SXSSFWorkbook() : new HSSFWorkbook()) {
+            //表头样式
+            CellStyle headerStyle = workbook.createCellStyle();
+            headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+            headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+
+            Font headerFont = workbook.createFont();
+            headerFont.setBold(true);
+
+            headerStyle.setFont(headerFont);
+
+            //遍历页
+            for (Map.Entry<String, Map<String, String>> headersEntry : headers.entrySet()) {
+                //创建页
+                Sheet sheet = workbook.createSheet(headersEntry.getKey());
+                if (!dates.containsKey(headersEntry.getKey())) continue;
+                //写入页
+                writeSheet(dates.get(headersEntry.getKey()), headersEntry.getValue(), sheet, headerStyle);
+            }
+            //写入excel
+            workbook.write(outputStream);
+            //清理临时文件
+            if (workbook instanceof SXSSFWorkbook) ((SXSSFWorkbook) workbook).dispose();
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
+                    , String.format("excel文件写入失败 msg:%s", e.getMessage())
+            );
+        }
+    }
+
+    /**
+     * 写入excel
+     *
+     * @param dates        数据
+     * @param header       表头信息
+     * @param outputStream 输出流
+     * @param excelEnum    excel枚举
+     */
+    public static void writeExcel(List<Map<String, Object>> dates, Map<String, String> header, OutputStream outputStream, ExcelEnum excelEnum) {
+        //创建表
+        try (Workbook workbook = excelEnum == ExcelEnum.XLSX ? new XSSFWorkbook() : new HSSFWorkbook()) {
+            //表头样式
+            CellStyle headerStyle = workbook.createCellStyle();
+            headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+            headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+
+            Font headerFont = workbook.createFont();
+            headerFont.setBold(true);
+
+            headerStyle.setFont(headerFont);
+
+            //创建页
+            Sheet sheet = workbook.createSheet(DEFAULT_SHEET_NAME);
+            //写入页
+            writeSheet(dates, header, sheet, headerStyle);
+            //写入excel
+            workbook.write(outputStream);
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
+                    , String.format("excel文件写入失败 msg:%s", e.getMessage())
+            );
+        }
+
+    }
+
+    /**
+     * 写入页
+     *
+     * @param dates       数据
+     * @param header      表头
+     * @param sheet       页
+     * @param headerStyle 头样式
+     */
+    public static void writeSheet(List<Map<String, Object>> dates, Map<String, String> header, Sheet sheet, CellStyle headerStyle) {
+        //表头键
+        List<String> headerKeys = new ArrayList<>();
+        //表头行
+        Row headRow = sheet.createRow(Math.max(ExcelMetadata.HEADER_ROW_NUM - 1, 0));
+        //遍历表头
+        for (Map.Entry<String, String> headerEntry : header.entrySet()) {
+            //缓存键
+            headerKeys.add(headerEntry.getKey());
+            //设置值和样式
+            Cell cell = headRow.createCell(headerKeys.size() - 1);
+            setCellValue(cell, headerEntry.getValue());
+            cell.setCellStyle(headerStyle);
+        }
+        //遍历数据
+        for (int i = 0; i < dates.size(); i++) {
+            Map<String, Object> data = dates.get(i);
+            Row row = sheet.createRow(i + ExcelMetadata.DATA_ROW_NUM - 1);
+            //遍历表头键
+            for (int j = 0, headerKeysSize = headerKeys.size(); j < headerKeysSize; j++) {
+                //根据表头键,设置值
+                String headerKey = headerKeys.get(j);
+                Cell cell = row.createCell(j);
+                setCellValue(cell, data.get(headerKey));
+            }
+        }
+    }
+
+    /**
+     * 写入excel
+     *
+     * @param dates        数据
+     * @param clazz        类型
+     * @param outputStream 输出流
+     * @param excelEnum    excel枚举
+     * @param <E>          数据泛形
+     */
+    public static <E> void writeExcel(List<E> dates, Class<E> clazz, OutputStream outputStream, ExcelEnum excelEnum) {
+        //创建表
+        try (Workbook workbook = excelEnum == ExcelEnum.XLSX ? new XSSFWorkbook() : new HSSFWorkbook()) {
+            //表头样式
+            CellStyle headerStyle = workbook.createCellStyle();
+            headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+            headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+
+            Font headerFont = workbook.createFont();
+            headerFont.setBold(true);
+
+            headerStyle.setFont(headerFont);
+
+            //创建页
+            Sheet sheet = workbook.createSheet(DEFAULT_SHEET_NAME);
+            //写入页
+            writeSheet(dates, clazz, sheet, headerStyle);
+            //写入excel
+            workbook.write(outputStream);
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
+                    , String.format("excel文件写入失败 msg:%s", e.getMessage())
+            );
+        }
+
+    }
+
+    /**
+     * 写入页
+     *
+     * @param dates       数据
+     * @param clazz       类型
+     * @param sheet       页
+     * @param headerStyle 头样式
+     * @param <E>         数据泛形
+     */
+    public static <E> void writeSheet(List<E> dates, Class<E> clazz, Sheet sheet, CellStyle headerStyle) {
+        //表头行
+        Row headRow = sheet.createRow(Math.max(ExcelMetadata.HEADER_ROW_NUM - 1, 0));
+        //需要导出的字段
+        List<Field> fieldList = Arrays.stream(clazz.getDeclaredFields())
+                //过滤需要导出的字段
+                .filter(f -> f.isAnnotationPresent(ExcelMapping.class))
+                .peek(f -> {
+                    //设置字段公开
+                    f.setAccessible(true);
+                    //填入表头
+                    Cell cell = headRow.createCell(headRow.getLastCellNum() == -1 ? 0 : headRow.getLastCellNum());
+                    setCellValue(cell, f.getAnnotation(ExcelMapping.class).value());
+                    cell.setCellStyle(headerStyle);
+                })
+                .collect(Collectors.toList());
+        //遍历数据
+        for (int i = 0; i < dates.size(); i++) {
+            E data = dates.get(i);
+            Row row = sheet.createRow(i + ExcelMetadata.DATA_ROW_NUM - 1);
+            //遍历字段
+            for (int j = 0, fieldListSize = fieldList.size(); j < fieldListSize; j++) {
+                Field f = fieldList.get(j);
+                //设置字段值
+                Cell cell = row.createCell(j);
+                Object value;
+                try {
+                    value = f.get(data);
+                } catch (IllegalAccessException e) {
+                    value = "";
+                }
+                setCellValue(cell, value);
+            }
+        }
+    }
+
+    /**
+     * 获取单元格值
+     *
+     * @param cell 单元格
+     * @return 值
+     */
+    private static String getCellValue(Cell cell) {
+        if (cell == null) return null;
+        //值
+        String value = null;
+        try {
+            switch (cell.getCellTypeEnum()) {
+                case NUMERIC: {
+                    if (DateUtil.isCellDateFormatted(cell)) {
+                        value = TimeTool.convertDateObj2DateStr(cell.getDateCellValue(), cell.getCellStyle().getDataFormatString());
+                    } else if (String.valueOf(cell.getNumericCellValue()).contains(".")) {
+                        value = BigDecimal.valueOf(cell.getNumericCellValue()).stripTrailingZeros().toPlainString();
+                    } else
+                        value = String.valueOf(cell.getNumericCellValue());
+                }
+                break;
+                case STRING:
+                    value = cell.getRichStringCellValue().toString();
+                    break;
+                case BOOLEAN:
+                    value = String.valueOf(cell.getBooleanCellValue());
+                    break;
+                case FORMULA: {
+                    //根据结果类型设置值
+                    CellType cellType = cell.getCachedFormulaResultTypeEnum();
+                    if (cellType == CellType.NUMERIC) value = String.valueOf(cell.getNumericCellValue());
+                    else if (cellType == CellType.STRING) value = cell.getRichStringCellValue().toString();
+                    else value = cell.getCellFormula();
+                }
+                break;
+                default:
+                    break;
+            }
+        } catch (Exception e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
+                    , String.format("excel单元格解释失败 error:%s", e)
+            );
+        }
+        return value == null ? "" : value.trim();
+    }
+
+    /**
+     * 设置单元格值
+     *
+     * @param cell  单元格
+     * @param value 值
+     */
+    private static void setCellValue(Cell cell, Object value) {
+        if (value == null) cell.setCellValue("");
+        else if (value instanceof String) cell.setCellValue((String) value);
+        else if (value instanceof Integer) cell.setCellValue((Integer) value);
+        else if (value instanceof Double) cell.setCellValue((Double) value);
+        else if (value instanceof Boolean) cell.setCellValue((Boolean) value);
+        else if (value instanceof Long) cell.setCellValue((Long) value);
+        else if (value instanceof Float) cell.setCellValue((Float) value);
+        else if (value instanceof Short) cell.setCellValue((Short) value);
+        else if (value instanceof Byte) cell.setCellValue((Byte) value);
+        else if (value instanceof Character) cell.setCellValue(value.toString());
+        else if (value instanceof Collection<?>)
+            cell.setCellValue(((Collection<?>) value).stream().map(Objects::toString).collect(Collectors.joining(",")));
+        else cell.setCellValue(value.toString());
+    }
+
+    /**
+     * 设置数据值
+     *
+     * @param data  数据
+     * @param field 字段
+     * @param value 值
+     * @param <E>   数据泛形
+     * @throws IllegalAccessException 非法访问异常
+     */
+    private static <E> void setObjValue(E data, Field field, Object value) throws IllegalAccessException {
+        //值判断空
+        if (value != null) {
+            //判断数据类型是否相同,如相同直接设置值,如不同,则转换值
+            if (field.getType().isAssignableFrom(value.getClass())) {
+                //设置值
+                field.set(data, value);
+            } else {
+                //如值为double类型
+                if (value instanceof Double) {
+                    //对double类型兼容的类型处理
+                    if (field.getType() == short.class || field.getType() == Short.class)
+                        field.set(data, ((Double) value).shortValue());
+                    else if (field.getType() == int.class || field.getType() == Integer.class)
+                        field.set(data, ((Double) value).intValue());
+                    else if (field.getType() == long.class || field.getType() == Long.class)
+                        field.set(data, ((Double) value).longValue());
+                    else if (field.getType() == float.class || field.getType() == Float.class)
+                        field.set(data, ((Double) value).floatValue());
+                }
+            }
+        }
+    }
+}

+ 35 - 34
custom-gateway-core/src/main/java/com/shkpr/service/customgateway/core/utils/InfluxDbUtil.java

@@ -3,12 +3,11 @@ package com.shkpr.service.customgateway.core.utils;
 import com.global.base.log.LogLevelFlag;
 import com.global.base.log.LogPrintMgr;
 import com.shkpr.service.customgateway.core.annotation.InfluxDbMapping;
-import com.shkpr.service.customgateway.core.constants.InfluxdbMetadata;
+import com.shkpr.service.customgateway.core.components.InfluxDBRegistry;
 import com.shkpr.service.customgateway.core.constants.LogFlagBusiType;
+import com.shkpr.service.customgateway.core.domain.InfluxDbClient;
 import com.shkpr.service.customgateway.core.properties.InfluxDbProperties;
 import lombok.extern.slf4j.Slf4j;
-import org.influxdb.InfluxDB;
-import org.influxdb.InfluxDBException;
 import org.influxdb.dto.BatchPoints;
 import org.influxdb.dto.Point;
 import org.influxdb.dto.Query;
@@ -37,33 +36,16 @@ public class InfluxDbUtil {
     /**
      * log
      */
-    private static final String mStrClassName = "InfluxDbUtil";
-    private static final String mBizType = LogFlagBusiType.BUSI_ALL.toStrValue();
+    private static final String CLASS_NAME = "InfluxDbUtil";
+    private static final String BIZ_TYPE = LogFlagBusiType.BUSI_ALL.toStrValue();
     final
-    InfluxDB influxDb;
+    InfluxDBRegistry influxDBRegistry;
     final
     InfluxDbProperties properties;
 
-    public InfluxDbUtil(InfluxDbProperties properties, InfluxDB influxDb) {
-        this.influxDb = influxDb;
+    public InfluxDbUtil(InfluxDbProperties properties, InfluxDBRegistry influxDBRegistry) {
+        this.influxDBRegistry = influxDBRegistry;
         this.properties = properties;
-        try {
-            //查询数据库信息
-            QueryResult queryResult = influxDb.query(new Query(InfluxdbMetadata.Command.SHOW_DATABASE, null));
-            List<String> databases = getValues(queryResult).stream()
-                    .map(database -> database.get(0).toString())
-                    .collect(Collectors.toList());
-            //数据库不存在,则创建数据库
-            if (databases.isEmpty() || !databases.contains(properties.getDatabase()))
-                influxDb.query(new Query(InfluxdbMetadata.Command.CREATE_DATABASE + properties.getDatabase(), null));
-        } catch (InfluxDBException e) {
-            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
-                    , String.format("尝试初始化数据库失败 error:%s", e)
-            );
-        }
-
-        //设置要使用的数据库
-        influxDb.setDatabase(properties.getDatabase());
     }
 
     /**
@@ -115,10 +97,12 @@ public class InfluxDbUtil {
      */
     public Boolean insert(Point point) {
         try {
-            influxDb.write(point);
+            for (InfluxDbClient influxDb : influxDBRegistry.clients()) {
+                influxDb.write(point);
+            }
             return true;
         } catch (Exception e) {
-            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
                     , String.format("插入InfluxDb失败 error:%s", e)
             );
             return false;
@@ -131,12 +115,28 @@ public class InfluxDbUtil {
      * @param points 批量点
      * @return 插入状态
      */
-    public Boolean insertBatch(BatchPoints points) {
+    public Boolean insertBatch(List<Point> points) {
         try {
-            influxDb.write(points);
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME
+                    , String.format("开始批量写入InfluxDb,数据量:%d", points.size()));
+            long begin = System.currentTimeMillis();
+
+            for (InfluxDbClient influxDb : influxDBRegistry.clients()) {
+                //转批量对象
+                BatchPoints batchPoints = BatchPoints
+                        .database(influxDb.getDatabase())
+                        .points(points.toArray(new Point[0]))
+                        .build();
+
+                influxDb.write(batchPoints);
+            }
+
+            long end = System.currentTimeMillis();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME
+                    , String.format("批量写入InfluxDb成功 用时(毫秒):%d", (end - begin)));
             return true;
         } catch (Exception e) {
-            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
                     , String.format("插入InfluxDb失败 error:%s", e)
             );
             return false;
@@ -152,11 +152,12 @@ public class InfluxDbUtil {
      * @return 实体类集合
      */
     public <E> List<E> query(String sql, Class<E> clazz) {
+        InfluxDbClient influxDb = influxDBRegistry.clients().get(0);
         //执行查询
-        QueryResult queryResult = influxDb.query(new Query(sql, properties.getDatabase()));
+        QueryResult queryResult = influxDb.query(new Query(sql, influxDb.getDatabase()));
         QueryResult.Series series = getSeries(queryResult);
         if (series == null) return Collections.emptyList();
-        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME
                 , String.format(
                         "读取InfluxDb成功,数据量:%s,开始解析"
                         , series.getValues().size()
@@ -211,14 +212,14 @@ public class InfluxDbUtil {
                 return data;
             } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                      NoSuchMethodException e) {
-                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, BIZ_TYPE, CLASS_NAME
                         , String.format("构建数据失败 error:%s", e)
                 );
                 return null;
             }
         }).filter(Objects::nonNull).collect(Collectors.toList());
 
-        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME
                 , String.format(
                         "解析InfluxDb成功,数据量:%s"
                         , series.getValues().size()

+ 24 - 0
custom-gateway-core/src/main/resources/mapper/DeviceKindMapper.xml

@@ -0,0 +1,24 @@
+<?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.shkpr.service.customgateway.core.mapper.DeviceKindMapper">
+    <select id="findByKeys" resultType="com.shkpr.service.customgateway.core.domain.DeviceKind">
+        select id,
+        uid,
+        name,
+        feature,
+        param1,
+        param2,
+        remark,
+        key,
+        gis_type,
+        tags,
+        warn,
+        musts,
+        measurement
+        from watersmart.public.k2_device_kind
+        where key in
+        <foreach collection="keys" item="key" open="(" close=")" separator=",">
+            #{key, jdbcType=VARCHAR}
+        </foreach>
+    </select>
+</mapper>

+ 22 - 0
custom-gateway-core/src/main/resources/mapper/TypeDefineMapper.xml

@@ -0,0 +1,22 @@
+<?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.shkpr.service.customgateway.core.mapper.TypeDefineMapper">
+    <select id="findByKeys" resultType="com.shkpr.service.customgateway.core.domain.TypeDefine">
+        select id,
+        key,
+        kind,
+        name,
+        remark,
+        param1,
+        param2,
+        ordering,
+        alias,
+        nature,
+        feature
+        from k2_type_define
+        where key in
+        <foreach collection="keys" item="key" open="(" close=")" separator=",">
+            #{key, jdbcType=VARCHAR}
+        </foreach>
+    </select>
+</mapper>

+ 1 - 12
custom-gateway-zydma/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>com.shkpr.service</groupId>
         <artifactId>kpr-custom-gateway</artifactId>
-        <version>1.0.0-dev</version>
+        <version>1.0.0</version>
     </parent>
 
     <!--工件名-->
@@ -42,17 +42,6 @@
     <build>
         <!--插件项-->
         <plugins>
-            <!--maven-jar-->
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>default-jar</id>
-                        <phase>none</phase>
-                    </execution>
-                </executions>
-            </plugin>
             <!--maven-install-->
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>

+ 19 - 14
custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/components/InfoSynchronizer.java

@@ -15,6 +15,7 @@ import com.shkpr.service.customgateway.zydma.service.PersonnelInfoService;
 import com.shkpr.service.customgateway.zydma.utils.CallingUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.http.message.BasicHeader;
+import org.springframework.http.HttpMethod;
 import org.springframework.stereotype.Component;
 
 import java.util.Arrays;
@@ -24,6 +25,9 @@ import java.util.stream.Collectors;
 
 /**
  * 信息同步器
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
  */
 @Component
 @Slf4j
@@ -61,20 +65,21 @@ public class InfoSynchronizer {
         //请求地址
         String url = endpoint.getUrl() + MiddlePlatformMetadata.Uri.GET_USERS;
         //请求获取用户
-        List<MiddlePlatformUser> users = callingUtil.scrollRequest(url, new TypeReference<MiddlePlatformResult<MiddlePlatformPage<MiddlePlatformUser>>>() {
-        }, pageable -> new HashMap<String, String>() {{
-            put(MiddlePlatformMetadata.Params.PAGE_NUMBER, pageable.getPageNumber() + "");
-            put(MiddlePlatformMetadata.Params.PAGE_SIZE, pageable.getPageSize() + "");
-        }}, params -> {
-            //获取密钥
-            IntegrationKey key = MiddlePlatformMetadata.getKey(endpoint.getAccessKey(), endpoint.getSecretKey(), params);
-            //存入请求头
-            return Arrays.asList(
-                    new BasicHeader(MiddlePlatformMetadata.Headers.APP_KEY, key.getAccessKey()),
-                    new BasicHeader(MiddlePlatformMetadata.Headers.TIMESTAMP, key.getTimestamp() + ""),
-                    new BasicHeader(MiddlePlatformMetadata.Headers.SIGN, key.getSign())
-            );
-        });
+        List<MiddlePlatformUser> users = callingUtil.scrollRequest(url, HttpMethod.GET,
+                new TypeReference<MiddlePlatformResult<MiddlePlatformPage<MiddlePlatformUser>>>() {
+                }, pageable -> new HashMap<String, String>() {{
+                    put(MiddlePlatformMetadata.Params.PAGE_NUMBER, pageable.getPageNumber() + "");
+                    put(MiddlePlatformMetadata.Params.PAGE_SIZE, pageable.getPageSize() + "");
+                }}, params -> {
+                    //获取密钥
+                    IntegrationKey key = MiddlePlatformMetadata.getKey(endpoint.getAccessKey(), endpoint.getSecretKey(), params);
+                    //存入请求头
+                    return Arrays.asList(
+                            new BasicHeader(MiddlePlatformMetadata.Headers.APP_KEY, key.getAccessKey()),
+                            new BasicHeader(MiddlePlatformMetadata.Headers.TIMESTAMP, key.getTimestamp() + ""),
+                            new BasicHeader(MiddlePlatformMetadata.Headers.SIGN, key.getSecretKey())
+                    );
+                });
 
         //转换用户对象
         List<PersonnelInfo> dates = users.stream()

+ 188 - 0
custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/components/IotCollector.java

@@ -0,0 +1,188 @@
+package com.shkpr.service.customgateway.zydma.components;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.customgateway.core.components.DeviceIdGenerator;
+import com.shkpr.service.customgateway.core.components.DeviceRegistry;
+import com.shkpr.service.customgateway.core.constants.LogFlagBusiType;
+import com.shkpr.service.customgateway.core.domain.Device;
+import com.shkpr.service.customgateway.core.domain.DeviceTag;
+import com.shkpr.service.customgateway.core.domain.IntegrationKey;
+import com.shkpr.service.customgateway.core.properties.CallingProperties;
+import com.shkpr.service.customgateway.core.utils.InfluxDbUtil;
+import com.shkpr.service.customgateway.zydma.domain.IotPlatformData;
+import com.shkpr.service.customgateway.zydma.domain.IotPlatformResult;
+import com.shkpr.service.customgateway.zydma.utils.CallingUtil;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpHeaders;
+import org.apache.http.message.BasicHeader;
+import org.influxdb.dto.Point;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.shkpr.service.customgateway.zydma.constants.IotPlatformMetadata.*;
+
+/**
+ * 物联网平台采集器
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Component
+public class IotCollector {
+    /**
+     * log
+     */
+    private static final String CLASS_NAME = "IotCollector";
+    private static final String BIZ_TYPE = LogFlagBusiType.BUSI_ALL.toStrValue();
+
+    final
+    CallingProperties callingProperties;
+    final
+    CallingProperties.CallingEndpoint endpoint;
+    final
+    DeviceRegistry deviceRegistry;
+    final
+    DeviceIdGenerator deviceIdGenerator;
+    final
+    InfluxDbUtil influxDbUtil;
+    final
+    CallingUtil callingUtil;
+
+    //密钥
+    public volatile IntegrationKey key = null;
+
+    public IotCollector(CallingProperties callingProperties, DeviceRegistry deviceRegistry, DeviceIdGenerator deviceIdGenerator, InfluxDbUtil influxDbUtil, CallingUtil callingUtil) {
+        this.callingProperties = callingProperties;
+        this.endpoint = callingProperties.getEndpoints().get(NAME);
+        this.deviceRegistry = deviceRegistry;
+        this.deviceIdGenerator = deviceIdGenerator;
+        this.influxDbUtil = influxDbUtil;
+        this.callingUtil = callingUtil;
+    }
+
+
+    /**
+     * 采集流量
+     *
+     * @param previousHours 往前回溯小时数
+     */
+    public void collectFlow(int previousHours) {
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME
+                , "开始采集流量数据,开始拉取数据");
+        long begin = System.currentTimeMillis();
+
+        //===================获取密钥===================
+        try {
+            if (key == null) key = getKey(endpoint);
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_WARN, BIZ_TYPE, CLASS_NAME,
+                    String.format("加载设备序号数据失败,error:%s", e));
+            return;
+        }
+        if (key == null) return;
+
+        //请求地址
+        String url = endpoint.getUrl() + Uri.GET_DEVICE_HISTORY_DATA;
+        //请求头
+        List<Header> headers = Arrays.asList(
+                new BasicHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE),
+                new BasicHeader(Headers.PANDA_TOKEN, key.getAccessKey()),
+                new BasicHeader(Headers.AUTHORIZATION, key.getSecretKey())
+        );
+
+        //查询时间
+        LocalDateTime beginTime = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS).minusHours(previousHours);
+        LocalDateTime endTime = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS);
+
+        //===================按设备类型遍历===================
+        for (DeviceTypeMapping deviceTypeMapping : DeviceTypeMapping.values()) {
+            //参数
+            Map<String, Object> params = getHistoryDataParams(deviceTypeMapping, beginTime, endTime);
+            //请求结果项
+            List<IotPlatformData> items = callingUtil.request(url, HttpMethod.POST, params, headers,
+                    new TypeReference<IotPlatformResult<List<IotPlatformData>>>() {
+                    });
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME
+                    , String.format("拉取数据成功,数据量:%d", items.size()));
+
+            //按设备(远传id)分组
+            Map<String, List<IotPlatformData>> snGroup = items.stream()
+                    .collect(Collectors.groupingBy(IotPlatformData::getCode));
+            //注册设备
+            registryDevices(deviceTypeMapping, snGroup);
+
+            //设备列表
+            List<Device> devices = deviceRegistry.findAll();
+            //===================按设备遍历===================
+            for (Device device : devices) {
+                //当前设备项
+                List<IotPlatformData> deviceItems = snGroup.get(device.getDeviceSn());
+
+                //根据指标分组
+                Map<String, List<IotPlatformData>> sensorGroup = deviceItems.stream()
+                        .collect(Collectors.groupingBy(IotPlatformData::getSensorName));
+
+                //采集标签列表
+                List<DeviceTag> tags = device.getTags();
+                //===================按采集标签遍历===================
+                for (DeviceTag tag : tags) {
+                    //当前采集标签项
+                    List<IotPlatformData> tagItems = sensorGroup.get(tag.getTag());
+
+                    //数据集合
+                    List<IotPlatformData.Data> dates = tagItems.stream()
+                            .flatMap(it -> it.getDataModel().stream())
+                            .collect(Collectors.toList());
+                    //===================写入influxdb===================
+                    List<Point> points = dates.stream().map(it -> dataToPoint(it, device, tag)).collect(Collectors.toList());
+                    influxDbUtil.insertBatch(points);
+                }
+            }
+        }
+
+        long end = System.currentTimeMillis();
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, BIZ_TYPE, CLASS_NAME
+                , String.format("结束采集流量数据 用时(毫秒):%d", (end - begin))
+        );
+    }
+
+    /**
+     * 注册设备
+     *
+     * @param deviceTypeMapping 设备类型映射
+     * @param snGroup           数据集合
+     */
+    private void registryDevices(DeviceTypeMapping deviceTypeMapping, Map<String, List<IotPlatformData>> snGroup) {
+        //需要添加的设备
+        Set<String> addedDevices = deviceRegistry.findAddedDevices(snGroup.keySet());
+        //处理需要添加的设备:生成设备,并注册
+        addedDevices.stream()
+                //过滤无效设备
+                .filter(sn -> CollectionUtils.isNotEmpty(snGroup.get(sn)) &&
+                        snGroup.get(sn).get(0) != null &&
+                        StringUtils.isNoneBlank(snGroup.get(sn).get(0).getName()))
+                //生成设备
+                .map(sn -> deviceIdGenerator.generateDevice(
+                        Devices.AREA_CODE,
+                        deviceTypeMapping.getType(),
+                        deviceTypeMapping.defaultTags(),
+                        sn, snGroup.get(sn).get(0).getName()
+                ))
+                //注册
+                .forEach(deviceRegistry::registerDevice);
+    }
+}

+ 268 - 0
custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/constants/IotPlatformMetadata.java

@@ -0,0 +1,268 @@
+package com.shkpr.service.customgateway.zydma.constants;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.shkpr.service.customgateway.core.constants.*;
+import com.shkpr.service.customgateway.core.domain.Device;
+import com.shkpr.service.customgateway.core.domain.DeviceTag;
+import com.shkpr.service.customgateway.core.domain.IntegrationKey;
+import com.shkpr.service.customgateway.core.properties.CallingProperties;
+import com.shkpr.service.customgateway.zydma.domain.IotPlatformData;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.client.fluent.Response;
+import org.influxdb.dto.Point;
+import org.springframework.http.MediaType;
+
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * 物联网平台集成元数据
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+public abstract class IotPlatformMetadata {
+
+    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    private static final ObjectMapper objectMapper = new ObjectMapper();
+
+    //系统名称
+    public static String NAME = "iotPlatform";
+
+    /**
+     * 获取密钥
+     */
+    public static IntegrationKey getKey(CallingProperties.CallingEndpoint endpoint) throws IOException {
+        //请求token
+        Response response = Request.Post(endpoint.getUrl() + Uri.GENERATE_TOKEN)
+                .addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+                .bodyByteArray(objectMapper.writeValueAsBytes(
+                        new HashMap<String, String>() {{
+                            put(Params.USERNAME, endpoint.getAccessKey());
+                            put(Params.PASSWORD, endpoint.getSecretKey());
+                        }}
+                ))
+                .execute();
+        //解析token
+        TokenResult tokenResult = objectMapper.readValue(response.returnContent().asString(), TokenResult.class);
+        TokenResult.TokenData data = tokenResult.getData();
+        if (!tokenResult.isOk() || !ObjectUtils.allNotNull(data, data.getUserToken(), data.getTokenType(), data.getAccessToken()))
+            return null;
+
+        return new IntegrationKey(null, data.getUserToken(), data.getTokenType() + " " + data.getAccessToken());
+    }
+
+    /**
+     * 获取历史数据参数
+     *
+     * @param deviceTypeMapping 设备类型映射
+     * @param beginTime         开始时间
+     * @param endTime           结束时间
+     * @return 参数
+     */
+    public static Map<String, Object> getHistoryDataParams(
+            DeviceTypeMapping deviceTypeMapping, LocalDateTime beginTime, LocalDateTime endTime
+    ) {
+        return new HashMap<String, Object>() {{
+            put(Params.SITE_CODE, DefaultValues.SITE_CODE);
+            put(Params.STREAM, Collections.singletonList(
+                    new HashMap<String, String>() {{
+                        put(Params.DEVICE_TYPE, deviceTypeMapping.mapping);
+                        put(Params.SENSORS, deviceTypeMapping.fieldMappings.stream()
+                                .map(DeviceTypeMapping.FieldMapping::getMapping)
+                                .collect(Collectors.joining(DefaultValues.SENSORS_DELIMITER))
+                        );
+                        put(Params.DATE_FROM, beginTime.format(formatter));
+                        put(Params.DATE_TO, endTime.format(formatter));
+                    }}
+            ));
+        }};
+    }
+
+    /**
+     * 数据转influxdb
+     *
+     * @param data 数据
+     * @param tag  采集标签
+     * @return influxdb记录
+     */
+    public static Point dataToPoint(IotPlatformData.Data data, Device device, DeviceTag tag) {
+        //获取时间戳
+        long timestamp = data.getPt()
+                .truncatedTo(ChronoUnit.MINUTES)
+                .atZone(ZoneId.systemDefault())
+                .toInstant()
+                .toEpochMilli();
+        //构建influxdb
+        return Point
+                //表名
+                .measurement(tag.getMeasurement())
+                //设备id
+                .tag(InfluxdbMetadata.Tags.DEVICE_ID, device.getDeviceId())
+                //时间
+                .time(timestamp, TimeUnit.MILLISECONDS)
+                //值
+                .addField(tag.getField(), data.getPv())
+                //
+                .addField(InfluxdbMetadata.Tags.DEVICE_SN, device.getDeviceSn())
+                .build();
+    }
+
+    /**
+     * 设备类型映射
+     */
+    @Getter
+    @AllArgsConstructor
+    public enum DeviceTypeMapping {
+        //流量计
+        FLOW(DeviceType.FLOW, "水表", Arrays.asList(
+                new FieldMapping(DeviceField.FLOW_CUR, "瞬时流量",
+                        new DeviceTag("瞬时流量", ProtocolType.JSON.getKey(), DeviceType.FLOW.getMeasurement(),
+                                DeviceField.FLOW_CUR.getKey(), ValueType.DOUBLE_FLOAT.getKey()
+                        )
+                ),
+                new FieldMapping(DeviceField.FLOW_TOTAL_POS, "正向累计流量",
+                        new DeviceTag("正向累计流量", ProtocolType.JSON.getKey(), DeviceType.FLOW.getMeasurement(),
+                                DeviceField.FLOW_TOTAL_POS.getKey(), ValueType.DOUBLE_FLOAT.getKey()
+                        )
+                ),
+                new FieldMapping(DeviceField.FLOW_TOTAL_REV, "反向累计流量",
+                        new DeviceTag("反向累计流量", ProtocolType.JSON.getKey(), DeviceType.FLOW.getMeasurement(),
+                                DeviceField.FLOW_TOTAL_REV.getKey(), ValueType.DOUBLE_FLOAT.getKey()
+                        )
+                )
+        ));
+        //类型
+        private final DeviceType type;
+        //映射
+        private final String mapping;
+        //字段映射
+        private final List<FieldMapping> fieldMappings;
+
+        //获取默认采集标签配置
+        public List<DeviceTag> defaultTags() {
+            return fieldMappings.stream()
+                    .map(IotPlatformMetadata.DeviceTypeMapping.FieldMapping::getDefaultTag)
+                    .collect(Collectors.toList());
+        }
+
+        @Getter
+        @Setter
+        @AllArgsConstructor
+        public static class FieldMapping {
+            //字段
+            private DeviceField field;
+            //映射
+            private String mapping;
+            //默认标签
+            private DeviceTag defaultTag;
+        }
+
+    }
+
+    /**
+     * 接口地址
+     */
+    public interface Uri {
+        //生成token
+        String GENERATE_TOKEN = "/GenerateToken";
+        //获取设备历史数据
+        String GET_DEVICE_HISTORY_DATA = "/iot/api/DataPublish/GetDeviceHistoryData";
+    }
+
+
+    /**
+     * 头
+     */
+    public interface Headers {
+        //token
+        String PANDA_TOKEN = "panda-token";
+        //认证
+        String AUTHORIZATION = "Authorization";
+    }
+
+
+    /**
+     * 参数
+     */
+    public interface Params {
+        //用户名
+        String USERNAME = "username";
+        //密码
+        String PASSWORD = "password";
+        //站点编码
+        String SITE_CODE = "SiteCode";
+        //查询条件
+        String STREAM = "stream";
+        //设备类型
+        String DEVICE_TYPE = "DeviceType";
+        //指标
+        String SENSORS = "sensors";
+        //开始时间
+        String DATE_FROM = "DateFrom";
+        //结束时间
+        String DATE_TO = "DateTo";
+    }
+
+
+    /**
+     * 默认值
+     */
+    public interface DefaultValues {
+        //站点编码
+        String SITE_CODE = "site_yf2611mh";
+        //指标分隔符
+        String SENSORS_DELIMITER = ",";
+    }
+
+    /**
+     * 设备
+     */
+    public interface Devices {
+        //区号
+        AreaCode AREA_CODE = AreaCode.ZAO_YANG;
+    }
+
+    /**
+     * token结果
+     */
+    @Data
+    private static class TokenResult {
+        private Integer code;
+        private String msg;
+        private TokenData data;
+
+        public Boolean isOk() {
+            return this.code != null && this.code == 0;
+        }
+
+        @Data
+        private static class TokenData {
+            @JsonAlias("user_token")
+            private String userToken;
+            @JsonAlias("access_token")
+            private String accessToken;
+            @JsonAlias("token_type")
+            private String tokenType;
+            @JsonAlias("refresh_token")
+            private String refreshToken;
+            @JsonAlias("scope")
+            private String scope;
+            @JsonAlias("expires_in")
+            private Long expiresIn;
+        }
+    }
+}

+ 1 - 1
custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/constants/MiddlePlatformMetadata.java

@@ -36,7 +36,7 @@ public abstract class MiddlePlatformMetadata extends IntegrationMetadata {
                 .collect(Collectors.joining("&"));
         //签名密钥
         String sign = DigestUtils.md5Hex(secretKey + "&" + timestamp + "&" + paramsStr).toUpperCase();
-        return new IntegrationKey(timestamp, accessKey, secretKey, sign, null);
+        return new IntegrationKey(timestamp, accessKey, sign);
     }
 
     /**

+ 51 - 0
custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/domain/IotPlatformData.java

@@ -0,0 +1,51 @@
+package com.shkpr.service.customgateway.zydma.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 物联网平台数据
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Data
+public class IotPlatformData {
+    /**
+     * 设备编码
+     */
+    private String code;
+    /**
+     * 设备名称
+     */
+    private String name;
+    /**
+     * 数据类型
+     */
+    private String sensorName;
+    /**
+     * 数据集合
+     */
+    private List<Data> dataModel;
+
+    /**
+     * 数据
+     */
+    @lombok.Data
+    public static class Data {
+        /**
+         * 时间
+         */
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh_CN", timezone = "Asia/Shanghai")
+        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+        private LocalDateTime pt;
+        /**
+         * 值
+         */
+        private Double pv;
+    }
+}

+ 53 - 0
custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/domain/IotPlatformResult.java

@@ -0,0 +1,53 @@
+package com.shkpr.service.customgateway.zydma.domain;
+
+import com.shkpr.service.customgateway.core.domain.ResultResponse;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * iot平台结果
+ *
+ * @param <T> 数据类型
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class IotPlatformResult<T> extends ResultResponse<T> {
+    /**
+     * 响应码
+     */
+    private Integer status;
+    /**
+     * 状态
+     */
+    private Boolean success;
+    /**
+     * 消息
+     */
+    private String msg;
+    /**
+     * 数据
+     */
+    private T response;
+
+    @Override
+    public Integer getCode() {
+        return this.status;
+    }
+
+    @Override
+    public String getMessage() {
+        return this.msg;
+    }
+
+    @Override
+    public T getData() {
+        return this.response;
+    }
+
+    @Override
+    public Boolean isOk() {
+        return this.status != null && this.status == 200;
+    }
+}

+ 3 - 3
custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/domain/MiddlePlatformResult.java

@@ -17,15 +17,15 @@ public class MiddlePlatformResult<T> extends ResultResponse<T> {
     /**
      * 响应码
      */
-    Integer code;
+    private Integer code;
     /**
      * 消息
      */
-    String msg;
+    private String msg;
     /**
      * 数据
      */
-    T data;
+    private T data;
 
     @Override
     public Integer getCode() {

+ 1 - 1
custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/bizmgr/InfoSyncManager.java

@@ -1,4 +1,4 @@
-package com.shkpr.service.customgateway.zydma.bizmgr;
+package com.shkpr.service.customgateway.zydma.manager;
 
 import com.shkpr.service.customgateway.zydma.components.InfoSynchronizer;
 import org.springframework.scheduling.annotation.Scheduled;

+ 45 - 0
custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/manager/IotManager.java

@@ -0,0 +1,45 @@
+package com.shkpr.service.customgateway.zydma.manager;
+
+import com.shkpr.service.customgateway.zydma.components.IotCollector;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * 物联网管理
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.0
+ */
+@Component
+public class IotManager {
+    final
+    ThreadPoolTaskExecutor taskScheduler;
+    final
+    IotCollector iotCollector;
+
+    public IotManager(ThreadPoolTaskExecutor taskScheduler, IotCollector iotCollector) {
+        this.taskScheduler = taskScheduler;
+        this.iotCollector = iotCollector;
+    }
+
+    /**
+     * 初始化
+     */
+    @PostConstruct
+    public void init() {
+        //采集流量数据
+        taskScheduler.execute(() -> iotCollector.collectFlow(4 * 24));
+    }
+
+    /**
+     * 小时任务
+     */
+    @Scheduled(cron = "0 15 0,6,12,18 * * ?")
+    public void minuteTask() {
+        //采集流量数据
+        taskScheduler.execute(() -> iotCollector.collectFlow(12));
+    }
+}

+ 58 - 5
custom-gateway-zydma/src/main/java/com/shkpr/service/customgateway/zydma/utils/CallingUtil.java

@@ -1,5 +1,6 @@
 package com.shkpr.service.customgateway.zydma.utils;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.global.base.log.LogLevelFlag;
@@ -13,6 +14,7 @@ import org.apache.http.Header;
 import org.apache.http.client.fluent.Request;
 import org.apache.http.client.fluent.Response;
 import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpMethod;
 import org.springframework.stereotype.Component;
 
 import java.io.IOException;
@@ -48,21 +50,71 @@ public class CallingUtil {
     }
 
     /**
+     * 构建请求
+     *
+     * @param url    请求地址
+     * @param method 请求方法
+     * @param params 参数
+     * @return 请求
+     */
+    public Request buildRequest(String url, HttpMethod method, Map<String, ?> params) throws JsonProcessingException {
+        if (method == HttpMethod.GET) return Request.Get(url + "?" + joinParams(params));
+        else if (method == HttpMethod.POST)
+            return Request.Post(url).bodyByteArray(objectMapper.writeValueAsBytes(params));
+        else return Request.Get(url);
+    }
+
+    /**
      * 拼接参数
      *
      * @param params 参数
      * @return 拼接字符串
      */
-    public String joinParams(Map<String, String> params) {
+    public String joinParams(Map<String, ?> params) {
         return params.entrySet().stream()
                 .map(entry -> entry.getKey() + "=" + entry.getValue())
                 .collect(Collectors.joining("&"));
     }
 
     /**
+     * 发起请求
+     *
+     * @param url        请求地址
+     * @param method     请求方法
+     * @param params     参数
+     * @param headers    请求头
+     * @param resultType 结果类型
+     * @param <R>        结果
+     * @param <T>        数据
+     * @return 返回所有分页数据的集合
+     */
+    public <R extends Result<List<T>>, T> List<T> request(
+            String url, HttpMethod method, Map<String, ?> params, List<Header> headers, TypeReference<R> resultType
+    ) {
+        try {
+            //发起请求
+            Response response = buildRequest(url, method, params)
+                    .setHeaders(headers.toArray(new Header[0]))
+                    .execute();
+            //解析结果
+            R result = objectMapper.readValue(response.returnContent().toString(), resultType);
+            if (!result.isOk()) throw new IOException("请求失败: " + result.getMessage());
+
+            //返回数据
+            return result.getData();
+        } catch (IOException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                    , String.format("拉取数据失败 error:%s", e)
+            );
+            return Collections.emptyList();
+        }
+    }
+
+    /**
      * 发起滚动请求
      *
      * @param url               请求地址
+     * @param method            请求方法
      * @param resultType        结果类型
      * @param onGenerateParams  生成参数回调
      * @param onGenerateHeaders 生成请求头回调
@@ -72,9 +124,10 @@ public class CallingUtil {
      * @return 返回所有分页数据的集合
      */
     public <R extends Result<P>, P extends PageResponse<T>, T> List<T> scrollRequest(
-            String url, TypeReference<R> resultType,
+            String url, HttpMethod method, TypeReference<R> resultType,
             Function<Pageable, Map<String, String>> onGenerateParams,
-            Function<Map<String, String>, List<Header>> onGenerateHeaders) {
+            Function<Map<String, String>, List<Header>> onGenerateHeaders
+    ) {
         //数据集合
         List<T> dates = new ArrayList<>();
         //分页请求
@@ -88,7 +141,7 @@ public class CallingUtil {
                 List<Header> headers = onGenerateHeaders.apply(params);
 
                 //发起请求
-                Response response = Request.Get(url + "?" + joinParams(params))
+                Response response = buildRequest(url, method, params)
                         .setHeaders(headers.toArray(new Header[0]))
                         .connectTimeout((int) callingProperties.getConnectTimeout().toMillis())
                         .socketTimeout((int) callingProperties.getReadTimeout().toMillis())
@@ -110,7 +163,7 @@ public class CallingUtil {
             }
         } catch (IOException e) {
             LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
-                    , String.format("滚动拉去数据失败 error:%s", e)
+                    , String.format("拉取拉去数据失败 error:%s", e)
             );
             return Collections.emptyList();
         }

+ 14 - 7
db.yml

@@ -1,7 +1,7 @@
 spring:
   datasource:
     data:
-      jdbc-url: jdbc:postgresql://140.246.183.164:5432/water_smart_test_zydma?useSSL=false&useAffectedRows=false&allowMultiQueries=true&rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai
+      jdbc-url: jdbc:postgresql://223.75.194.87:6057/watersmart?useSSL=false&useAffectedRows=false&allowMultiQueries=true&rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai
       username: postgres
       password: kpr.23417.postgres
       driver-class-name: org.postgresql.Driver
@@ -18,10 +18,17 @@ spring:
       connection-timeout: 30000
       time-between-eviction-runs-millis: 60000
       minEvictableIdleTimeMillis: 300000
-#influxdb
+  #influxdb
   influx:
-    url: http://119.96.165.176:8086/
-    user: kpr
-    password: kpr.2024@117.influxdb
-    database: iot
-    read-timeout: 30000
+    read-timeout: 30000
+    clients:
+      #枣阳
+      - url: http://192.168.44.58:8086/
+        user: kpr
+        password: kpr.2025@117.influxdb
+        database: iot
+      #测试
+      - url: http://119.96.165.176:8086/
+        user: kpr
+        password: kpr.2024@117.influxdb
+        database: iot

File diff suppressed because it is too large
+ 3895 - 0
dev_map.yml


+ 3 - 0
dev_seq.json

@@ -0,0 +1,3 @@
+{
+  "2025-11-06" : 122
+}

+ 3 - 2
pom.xml

@@ -6,7 +6,7 @@
     <!--工件名-->
     <artifactId>kpr-custom-gateway</artifactId>
     <!--版本号-->
-    <version>1.0.0-dev</version>
+    <version>1.0.0</version>
     <!--打包方式-->
     <packaging>pom</packaging>
     <!--项目名-->
@@ -37,7 +37,7 @@
         <maven.compiler.target>1.8</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-        <project.version>1.0.0-dev</project.version>
+        <project.version>1.0.0</project.version>
         <java.version>1.8</java.version>
 
         <gbase.version>1.0.5</gbase.version>
@@ -47,6 +47,7 @@
         <common.text.version>1.14.0</common.text.version>
         <jjwt.version>0.9.0</jjwt.version>
         <okhttp.version>3.14.9</okhttp.version>
+        <poi.version>3.17</poi.version>
     </properties>
 
     <!--依赖项-->