Explorar o código

增加excel在线导入发送模拟数据功能

luoguangyi %!s(int64=2) %!d(string=hai) anos
pai
achega
b525e457dd
Modificáronse 36 ficheiros con 2724 adicións e 116 borrados
  1. 43 32
      pom.xml
  2. 41 8
      src/main/java/com/SimulatorApplication.java
  3. 85 0
      src/main/java/com/persagy/common/BusinessException.java
  4. 52 0
      src/main/java/com/persagy/common/ErrorCode.java
  5. 275 0
      src/main/java/com/persagy/common/ErrorCodeConstants.java
  6. 283 0
      src/main/java/com/persagy/common/R.java
  7. 39 0
      src/main/java/com/persagy/config/AsyncProperties.java
  8. 56 0
      src/main/java/com/persagy/config/DefaultAsyncTaskConfig.java
  9. 65 0
      src/main/java/com/persagy/config/SwaggerAutoConfiguration.java
  10. 43 0
      src/main/java/com/persagy/config/SwaggerProperties.java
  11. 37 0
      src/main/java/com/persagy/entity/InsertData.java
  12. 52 0
      src/main/java/com/persagy/excel/ExcelData.java
  13. 273 0
      src/main/java/com/persagy/excel/ExcelListener.java
  14. 138 0
      src/main/java/com/persagy/excel/ExcelUtils.java
  15. 15 8
      src/main/java/com/persagy/parser/ParserEntity.java
  16. 8 3
      src/main/java/com/persagy/parser/RecordData.java
  17. 208 0
      src/main/java/com/persagy/rest/RestApi.java
  18. 12 3
      src/main/java/com/persagy/simulator/MeterSimulatorLexer.java
  19. 20 2
      src/main/java/com/persagy/simulator/MeterSimulatorParser.java
  20. 2 0
      src/main/java/com/persagy/simulator/MeterSimulatorScanner.java
  21. 12 2
      src/main/java/com/persagy/simulator/MeterSimulatorWalker.java
  22. 2 2
      src/main/java/com/persagy/simulator_gaopin/Constant.java
  23. 7 4
      src/main/java/com/persagy/simulator_gaopin/MyMqttCallback.java
  24. 2 1
      src/main/java/com/persagy/simulator_gaopin/SimulatorMain_collector.java
  25. 4 3
      src/main/java/com/persagy/simulator_gaopin/SimulatorUtil.java
  26. 7 4
      src/main/java/com/persagy/simulator_gaopin/Thread_report.java
  27. 4 3
      src/main/java/com/persagy/simulator_gaopin/Thread_set.java
  28. 409 0
      src/main/java/com/persagy/util/DateUtil.java
  29. 9 13
      src/main/java/com/persagy/util/ExcelUtil.java
  30. 17 11
      src/main/java/com/persagy/util/FastJsonUtil.java
  31. 16 15
      src/main/java/com/persagy/util/FunctionUtil.java
  32. 457 0
      src/main/java/com/persagy/util/StringUtil.java
  33. 29 0
      src/main/resources/application.yml
  34. 2 2
      src/main/resources/config.xml
  35. BIN=BIN
      src/main/resources/report.xlsx
  36. BIN=BIN
      src/main/resources/set.xlsx

+ 43 - 32
pom.xml

@@ -1,7 +1,12 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
-
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.3.12.RELEASE</version>
+        <relativePath/>
+    </parent>
     <groupId>com.persagy</groupId>
     <artifactId>simulator-collector</artifactId>
     <version>0.0.1-SNAPSHOT</version>
@@ -17,6 +22,12 @@
         <knife4j.version>3.0.3</knife4j.version>
         <swagger-annotations.version>1.5.22</swagger-annotations.version>
         <spring-boot.version>2.4.2</spring-boot.version>
+        <!--日志框架有安全漏洞,升级最新版本-->
+        <logback.version>1.2.9</logback.version>
+        <log4j2.version>2.17.0</log4j2.version>
+        <xstream.version>1.4.18</xstream.version>
+        <easyexcel.version>3.0.5</easyexcel.version>
+        <fastjson.version>1.2.83</fastjson.version>
     </properties>
     <repositories>
         <repository>
@@ -48,19 +59,7 @@
     <dependencies>
         <dependency>
             <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter</artifactId>
-            <version>${spring-boot.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
-            <version>${spring-boot.version}</version>
-            <exclusions>
-                <exclusion>
-                    <artifactId>hibernate-validator</artifactId>
-                    <groupId>org.hibernate.validator</groupId>
-                </exclusion>
-            </exclusions>
         </dependency>
 
         <!--		<dependency>-->
@@ -70,7 +69,12 @@
         <!--			<scope>system</scope>-->
         <!--			<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>-->
         <!--		</dependency>-->
-
+        <!-- 工具类相关 -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>${easyexcel.version}</version>
+        </dependency>
         <dependency>
             <groupId>dom4j</groupId>
             <artifactId>dom4j</artifactId>
@@ -99,16 +103,16 @@
             <version>5.5.4</version>
             <scope>compile</scope>
         </dependency>
-        <dependency>
-            <groupId>org.apache.poi</groupId>
-            <artifactId>poi</artifactId>
-            <version>3.9</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.poi</groupId>
-            <artifactId>poi-ooxml</artifactId>
-            <version>3.9</version>
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi</artifactId>-->
+<!--            <version>3.9</version>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi-ooxml</artifactId>-->
+<!--            <version>3.9</version>-->
+<!--        </dependency>-->
 
         <dependency>
             <groupId>org.antlr</groupId>
@@ -118,7 +122,7 @@
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>fastjson</artifactId>
-            <version>1.2.70</version>
+            <version>${fastjson.version}</version>
         </dependency>
         <!-- netty 实现websocket -->
         <dependency>
@@ -143,6 +147,20 @@
             <version>3.11</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-spring-boot-starter</artifactId>
+            <version>${knife4j.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>${swagger-annotations.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
     </dependencies>
     <build>
         <finalName>${project.artifactId}</finalName>
@@ -214,13 +232,6 @@
             </plugin>
         </plugins>
     </build>
-
-
-
-
-
-
-
     <distributionManagement>
         <repository>
             <id>SagaCloudRelease</id>

+ 41 - 8
src/main/java/com/SimulatorApplication.java

@@ -1,28 +1,61 @@
 package com;
 
+import cn.hutool.core.thread.ThreadUtil;
+import com.persagy.constant.Constant;
 import com.persagy.simulator_gaopin.SimulatorMain_collector;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.web.servlet.ServletComponentScan;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.env.Environment;
 
 import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 
 @SpringBootApplication
 @ServletComponentScan
+@Slf4j
 public class SimulatorApplication implements ServletContextListener {
 
-    public static void main(String[] args) {
-        SpringApplication.run(SimulatorApplication.class, args);
+    public static void main(String[] args) throws UnknownHostException {
+
+        ConfigurableApplicationContext application = SpringApplication.run(SimulatorApplication.class, args);
+        Environment env = application.getEnvironment();
+        log.info("\n----------------------------------------------------------\n\t" +
+                        "应用 '{}' 运行成功! 访问连接:\n\t" +
+                        "Swagger文档: \t\thttp://{}:{}{}{}/doc.html\n\t" +
+                        "----------------------------------------------------------",
+                env.getProperty("spring.application.name"),
+                InetAddress.getLocalHost().getHostAddress(),
+                env.getProperty("server.port"),
+                env.getProperty("server.servlet.context-path", ""),
+                env.getProperty("spring.mvc.servlet.path", ""));
+    }
+
+    /**
+     * 打印全量日志
+     */
+    @Value("${iot.log:false}")
+    public void setIotLog(boolean value) {
+        Constant.iotLog = value;
     }
+
     @Override
     public void contextInitialized(ServletContextEvent sce) {
-        try {
-            SimulatorMain_collector.main(null);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
+        ThreadUtil.execAsync(()->{
+            try {
+                System.out.println("Constant.iotLog:"+Constant.iotLog);
+                SimulatorMain_collector.main(null);
+                log.warn("----------SimulatorMain_collector main start--------------");
+            } catch (Exception e) {
+               log.error(e.getMessage(),e);
+            }
+        },true);
 
-        System.out.println("----------contextInitialize end--------------");
+        log.warn("----------contextInitialize end--------------");
     }
 }

+ 85 - 0
src/main/java/com/persagy/common/BusinessException.java

@@ -0,0 +1,85 @@
+package com.persagy.common;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import static com.persagy.common.ErrorCodeConstants.SYSTEM_ERROR_B0001;
+
+/**
+ * 业务逻辑异常 Exception
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class BusinessException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+    /**
+     * 业务错误码
+     *
+     * @see ErrorCodeConstants
+     */
+    private String code;
+    /**
+     * 错误提示
+     */
+    private String message;
+
+    /**
+     * 空构造方法,避免反序列化问题
+     */
+    public BusinessException(Throwable cause) {
+        super(cause);
+        code = SYSTEM_ERROR_B0001.getCode();
+        message = SYSTEM_ERROR_B0001.getMessage();
+    }
+
+    public BusinessException(String code, String message, Throwable cause) {
+        super(message, cause);
+        this.code = code;
+        this.message = message;
+    }
+
+    public BusinessException(ErrorCode errorCode) {
+        super(errorCode.getMessage());
+        code = errorCode.getCode();
+        message = errorCode.getMessage();
+    }
+
+    public BusinessException(ErrorCode errorCode, Throwable cause) {
+        super(errorCode.getMessage(), cause);
+        code = errorCode.getCode();
+        message = errorCode.getMessage();
+    }
+
+    public BusinessException(String code, String message) {
+        super(message);
+        this.code = code;
+        this.message = message;
+    }
+
+    public static BusinessException wrap(String code, String errorMsgTemplate, Object... args) {
+        return new BusinessException(code, StrUtil.format(errorMsgTemplate, args));
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public BusinessException setCode(String code) {
+        this.code = code;
+        return this;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+
+    public BusinessException setMessage(String message) {
+        this.message = message;
+        return this;
+    }
+
+
+}

+ 52 - 0
src/main/java/com/persagy/common/ErrorCode.java

@@ -0,0 +1,52 @@
+package com.persagy.common;
+
+import lombok.Data;
+
+import java.util.Objects;
+
+/**
+ * 错误码对象
+ * <p>
+ * 全局错误码,参见 {@link ErrorCodeConstants}
+ * 错误码设计成对象的原因,为未来的 i18 国际化做准备
+ * <p>
+ */
+@Data
+public class ErrorCode {
+
+    /**
+     * 错误码
+     */
+    private final String code;
+    /**
+     * 错误提示
+     */
+    private final String message;
+
+    public ErrorCode(Integer code, String message) {
+        this.code = String.valueOf(code);
+        this.message = message;
+    }
+
+    public ErrorCode(String code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof ErrorCode)) {
+            return false;
+        }
+        ErrorCode errorCode = (ErrorCode) o;
+        return code.equals(errorCode.code) && message.equals(errorCode.message);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(code, message);
+    }
+}

+ 275 - 0
src/main/java/com/persagy/common/ErrorCodeConstants.java

@@ -0,0 +1,275 @@
+package com.persagy.common;
+
+/**
+ * 阿里巴巴Java开发手册-崇山版-2020.08.03 错误码整理
+ * 一般情况下,使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
+ * 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的
+ * 错误码:
+ * 1. 五位组成
+ * 2. A代表用户端错误
+ * 3. B代表当前系统异常
+ * 4. C代表第三方服务异常
+ * 4. 若无法确定具体错误,选择宏观错误
+ * 6. 大的错误类间的步长间距预留100
+ *
+ * @author lgy
+ */
+public interface ErrorCodeConstants {
+
+
+    /**
+     * 成功
+     */
+    ErrorCode SUCCESS = new ErrorCode("00000", "成功");
+    ErrorCode DEF_ERROR = new ErrorCode("Z9999", "系统繁忙,请稍候再试");
+
+    /**
+     * 一级宏观错误码
+     */
+    ErrorCode USER_ERROR_A0001 = new ErrorCode("A0001", "用户端错误");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode USER_ERROR_A0100 = new ErrorCode("A0100", "用户注册错误");
+    ErrorCode USER_ERROR_A0101 = new ErrorCode("A0101", "用户未同意隐私协议");
+    ErrorCode USER_ERROR_A0102 = new ErrorCode("A0102", "注册国家或地区受限");
+    ErrorCode USER_ERROR_A0110 = new ErrorCode("A0110", "用户名校验失败");
+    ErrorCode USER_ERROR_A0111 = new ErrorCode("A0111", "用户名已存在");
+    ErrorCode USER_ERROR_A0112 = new ErrorCode("A0112", "用户名包含敏感词");
+    ErrorCode USER_ERROR_A0113 = new ErrorCode("A0113", "用户名包含特殊字符");
+    ErrorCode USER_ERROR_A0120 = new ErrorCode("A0120", "密码校验失败");
+    ErrorCode USER_ERROR_A0121 = new ErrorCode("A0121", "密码长度不够");
+    ErrorCode USER_ERROR_A0122 = new ErrorCode("A0122", "密码强度不够");
+    ErrorCode USER_ERROR_A0130 = new ErrorCode("A0130", "校验码输入错误");
+    ErrorCode USER_ERROR_A0131 = new ErrorCode("A0131", "短信校验码输入错误");
+    ErrorCode USER_ERROR_A0132 = new ErrorCode("A0132", "邮件校验码输入错误");
+    ErrorCode USER_ERROR_A0133 = new ErrorCode("A0133", "语音校验码输入错误");
+    ErrorCode USER_ERROR_A0140 = new ErrorCode("A0140", "用户证件异常");
+    ErrorCode USER_ERROR_A0141 = new ErrorCode("A0141", "用户证件类型未选择");
+    ErrorCode USER_ERROR_A0142 = new ErrorCode("A0142", "大陆身份证编号校验非法");
+    ErrorCode USER_ERROR_A0143 = new ErrorCode("A0143", "护照编号校验非法");
+    ErrorCode USER_ERROR_A0144 = new ErrorCode("A0144", "军官证编号校验非法");
+    ErrorCode USER_ERROR_A0150 = new ErrorCode("A0150", "用户基本信息校验失败");
+    ErrorCode USER_ERROR_A0151 = new ErrorCode("A0151", "手机格式校验失败");
+    ErrorCode USER_ERROR_A0152 = new ErrorCode("A0152", "地址格式校验失败");
+    ErrorCode USER_ERROR_A0153 = new ErrorCode("A0153", "邮箱格式校验失败");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode USER_ERROR_A0200 = new ErrorCode("A0200", "用户登录异常");
+    ErrorCode USER_ERROR_A0201 = new ErrorCode("A0201", "用户账户不存在");
+    ErrorCode USER_ERROR_A0202 = new ErrorCode("A0202", "用户账户被冻结");
+    ErrorCode USER_ERROR_A0203 = new ErrorCode("A0203", "用户账户已作废");
+    ErrorCode USER_ERROR_A0210 = new ErrorCode("A0210", "用户密码错误");
+    ErrorCode USER_ERROR_A0211 = new ErrorCode("A0211", "用户输入密码错误次数超限");
+    ErrorCode USER_ERROR_A0220 = new ErrorCode("A0220", "用户身份校验失败");
+    ErrorCode USER_ERROR_A0221 = new ErrorCode("A0221", "用户指纹识别失败");
+    ErrorCode USER_ERROR_A0222 = new ErrorCode("A0222", "用户面容识别失败");
+    ErrorCode USER_ERROR_A0223 = new ErrorCode("A0223", "用户未获得第三方登录授权");
+    ErrorCode USER_ERROR_A0230 = new ErrorCode("A0230", "用户登录已过期");
+    ErrorCode USER_ERROR_A0240 = new ErrorCode("A0240", "用户验证码错误");
+    ErrorCode USER_ERROR_A0241 = new ErrorCode("A0241", "用户验证码尝试次数超限");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode USER_ERROR_A0300 = new ErrorCode("A0300", "访问权限异常");
+    ErrorCode USER_ERROR_A0301 = new ErrorCode("A0301", "访问未授权");
+    ErrorCode USER_ERROR_A0302 = new ErrorCode("A0302", "正在授权中");
+    ErrorCode USER_ERROR_A0303 = new ErrorCode("A0303", "用户授权申请被拒绝");
+    ErrorCode USER_ERROR_A0310 = new ErrorCode("A0310", "因访问对象隐私设置被拦截");
+    ErrorCode USER_ERROR_A0311 = new ErrorCode("A0311", "授权已过期");
+    ErrorCode USER_ERROR_A0312 = new ErrorCode("A0312", "无权限使用 API");
+    ErrorCode USER_ERROR_A0320 = new ErrorCode("A0320", "用户访问被拦截");
+    ErrorCode USER_ERROR_A0321 = new ErrorCode("A0321", "黑名单用户");
+    ErrorCode USER_ERROR_A0322 = new ErrorCode("A0322", "账号被冻结");
+    ErrorCode USER_ERROR_A0323 = new ErrorCode("A0323", "非法 IP 地址");
+    ErrorCode USER_ERROR_A0324 = new ErrorCode("A0324", "网关访问受限");
+    ErrorCode USER_ERROR_A0325 = new ErrorCode("A0325", "地域黑名单");
+    ErrorCode USER_ERROR_A0330 = new ErrorCode("A0330", "服务已欠费");
+    ErrorCode USER_ERROR_A0340 = new ErrorCode("A0340", "用户签名异常");
+    ErrorCode USER_ERROR_A0341 = new ErrorCode("A0341", "RSA 签名错误");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode USER_ERROR_A0400 = new ErrorCode("A0400", "用户请求参数错误");
+    ErrorCode USER_ERROR_A0405 = new ErrorCode("A0405", "请求方法不正确");
+    ErrorCode USER_ERROR_A0401 = new ErrorCode("A0401", "包含非法恶意跳转链接");
+    ErrorCode USER_ERROR_A0402 = new ErrorCode("A0402", "无效的用户输入");
+    ErrorCode USER_ERROR_A0410 = new ErrorCode("A0410", "请求必填参数为空");
+    ErrorCode USER_ERROR_A0411 = new ErrorCode("A0411", "用户订单号为空");
+    ErrorCode USER_ERROR_A0412 = new ErrorCode("A0412", "订购数量为空");
+    ErrorCode USER_ERROR_A0413 = new ErrorCode("A0413", "缺少时间戳参数");
+    ErrorCode USER_ERROR_A0414 = new ErrorCode("A0414", "非法的时间戳参数");
+    ErrorCode USER_ERROR_A0420 = new ErrorCode("A0420", "请求参数值超出允许的范围");
+    ErrorCode USER_ERROR_A0421 = new ErrorCode("A0421", "参数格式不匹配");
+    ErrorCode USER_ERROR_A0422 = new ErrorCode("A0422", "地址不在服务范围");
+    ErrorCode USER_ERROR_A0423 = new ErrorCode("A0423", "时间不在服务范围");
+    ErrorCode USER_ERROR_A0424 = new ErrorCode("A0424", "金额超出限制");
+    ErrorCode USER_ERROR_A0425 = new ErrorCode("A0425", "数量超出限制");
+    ErrorCode USER_ERROR_A0426 = new ErrorCode("A0426", "请求批量处理总个数超出限制");
+    ErrorCode USER_ERROR_A0427 = new ErrorCode("A0427", "请求 JSON 解析失败");
+    ErrorCode USER_ERROR_A0430 = new ErrorCode("A0430", "用户输入内容非法");
+    ErrorCode USER_ERROR_A0431 = new ErrorCode("A0431", "包含违禁敏感词");
+    ErrorCode USER_ERROR_A0432 = new ErrorCode("A0432", "图片包含违禁信息");
+    ErrorCode USER_ERROR_A0433 = new ErrorCode("A0433", "文件侵犯版权");
+    ErrorCode USER_ERROR_A0440 = new ErrorCode("A0440", "用户操作异常");
+    ErrorCode USER_ERROR_A0441 = new ErrorCode("A0441", "用户支付超时");
+    ErrorCode USER_ERROR_A0442 = new ErrorCode("A0442", "确认订单超时");
+    ErrorCode USER_ERROR_A0443 = new ErrorCode("A0443", "订单已关闭");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode USER_ERROR_A0500 = new ErrorCode("A0500", "用户请求服务异常");
+    ErrorCode USER_ERROR_A0501 = new ErrorCode("A0501", "请求次数超出限制");
+    ErrorCode USER_ERROR_A0502 = new ErrorCode("A0502", "请求并发数超出限制");
+    ErrorCode USER_ERROR_A0503 = new ErrorCode("A0503", "用户操作请等待");
+    ErrorCode USER_ERROR_A0504 = new ErrorCode("A0504", "WebSocket 连接异常");
+    ErrorCode USER_ERROR_A0505 = new ErrorCode("A0505", "WebSocket 连接断开");
+    ErrorCode USER_ERROR_A0506 = new ErrorCode("A0506", "用户重复请求");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode USER_ERROR_A0600 = new ErrorCode("A0600", "用户资源异常");
+    ErrorCode USER_ERROR_A0601 = new ErrorCode("A0601", "账户余额不足");
+    ErrorCode USER_ERROR_A0602 = new ErrorCode("A0602", "用户磁盘空间不足");
+    ErrorCode USER_ERROR_A0603 = new ErrorCode("A0603", "用户内存空间不足");
+    ErrorCode USER_ERROR_A0604 = new ErrorCode("A0604", "用户 OSS 容量不足");
+    /**
+     * 例如:每天抽奖数
+     */
+    ErrorCode USER_ERROR_A0605 = new ErrorCode("A0605", "用户配额已用光");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode USER_ERROR_A0700 = new ErrorCode("A0700", "用户上传文件异常");
+    ErrorCode USER_ERROR_A0701 = new ErrorCode("A0701", "用户上传文件类型不匹配");
+    ErrorCode USER_ERROR_A0702 = new ErrorCode("A0702", "用户上传文件太大");
+    ErrorCode USER_ERROR_A0703 = new ErrorCode("A0703", "用户上传图片太大");
+    ErrorCode USER_ERROR_A0704 = new ErrorCode("A0704", "用户上传视频太大");
+    ErrorCode USER_ERROR_A0705 = new ErrorCode("A0705", "用户上传压缩文件太大");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode USER_ERROR_A0800 = new ErrorCode("A0800", "用户当前版本异常");
+    ErrorCode USER_ERROR_A0801 = new ErrorCode("A0801", "用户安装版本与系统不匹配");
+    ErrorCode USER_ERROR_A0802 = new ErrorCode("A0802", "用户安装版本过低");
+    ErrorCode USER_ERROR_A0803 = new ErrorCode("A0803", "用户安装版本过高");
+    ErrorCode USER_ERROR_A0804 = new ErrorCode("A0804", "用户安装版本已过期");
+    ErrorCode USER_ERROR_A0805 = new ErrorCode("A0805", "用户 API 请求版本不匹配");
+    ErrorCode USER_ERROR_A0806 = new ErrorCode("A0806", "用户 API 请求版本过高");
+    ErrorCode USER_ERROR_A0807 = new ErrorCode("A0807", "用户 API 请求版本过低");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode USER_ERROR_A0900 = new ErrorCode("A0900", "用户隐私未授权");
+    ErrorCode USER_ERROR_A0901 = new ErrorCode("A0901", "用户隐私未签署");
+    ErrorCode USER_ERROR_A0902 = new ErrorCode("A0902", "用户摄像头未授权");
+    ErrorCode USER_ERROR_A0903 = new ErrorCode("A0903", "用户相机未授权");
+    ErrorCode USER_ERROR_A0904 = new ErrorCode("A0904", "用户图片库未授权");
+    ErrorCode USER_ERROR_A0905 = new ErrorCode("A0905", "用户文件未授权");
+    ErrorCode USER_ERROR_A0906 = new ErrorCode("A0906", "用户位置信息未授权");
+    ErrorCode USER_ERROR_A0907 = new ErrorCode("A0907", "用户通讯录未授权");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode USER_ERROR_A1000 = new ErrorCode("A1000", "用户设备异常");
+    ErrorCode USER_ERROR_A1001 = new ErrorCode("A1001", "用户相机异常");
+    ErrorCode USER_ERROR_A1002 = new ErrorCode("A1002", "用户麦克风异常");
+    ErrorCode USER_ERROR_A1003 = new ErrorCode("A1003", "用户听筒异常");
+    ErrorCode USER_ERROR_A1004 = new ErrorCode("A1004", "用户扬声器异常");
+    ErrorCode USER_ERROR_A1005 = new ErrorCode("A1005", "用户 GPS 定位异常");
+
+
+    /**
+     * 系统异常
+     * 一级宏观错误码
+     */
+    ErrorCode SYSTEM_ERROR_B0001 = new ErrorCode("B0001", "系统执行出错");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode SYSTEM_ERROR_B0100 = new ErrorCode("B0100", "系统执行超时");
+    ErrorCode SYSTEM_ERROR_B0101 = new ErrorCode("B0101", "系统订单处理超时");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode SYSTEM_ERROR_B0200 = new ErrorCode("B0200", "系统容灾功能被触发");
+    ErrorCode SYSTEM_ERROR_B0210 = new ErrorCode("B0210", "系统限流");
+    ErrorCode SYSTEM_ERROR_B0220 = new ErrorCode("B0220", "系统功能降级");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode SYSTEM_ERROR_B0300 = new ErrorCode("B0300", "系统资源异常");
+    ErrorCode SYSTEM_ERROR_B0310 = new ErrorCode("B0310", "系统资源耗尽");
+    ErrorCode SYSTEM_ERROR_B0311 = new ErrorCode("B0311", "系统磁盘空间耗尽");
+    ErrorCode SYSTEM_ERROR_B0312 = new ErrorCode("B0312", "系统内存耗尽");
+    ErrorCode SYSTEM_ERROR_B0313 = new ErrorCode("B0313", "文件句柄耗尽");
+    ErrorCode SYSTEM_ERROR_B0314 = new ErrorCode("B0314", "系统连接池耗尽");
+    ErrorCode SYSTEM_ERROR_B0315 = new ErrorCode("B0315", "系统线程池耗尽");
+    ErrorCode SYSTEM_ERROR_B0320 = new ErrorCode("B0320", "系统资源访问异常");
+    ErrorCode SYSTEM_ERROR_B0321 = new ErrorCode("B0321", "系统读取磁盘文件失败");
+
+
+    /**
+     * 调用第三方服务
+     * 一级宏观错误码
+     */
+    ErrorCode SERVICE_ERROR_C0001 = new ErrorCode("C0001", "调用第三方服务出错");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode SERVICE_ERROR_C0100 = new ErrorCode("C0100", "中间件服务出错");
+    ErrorCode SERVICE_ERROR_C0110 = new ErrorCode("C0110", "RPC 服务出错");
+    ErrorCode SERVICE_ERROR_C0111 = new ErrorCode("C0111", "RPC 服务未找到");
+    ErrorCode SERVICE_ERROR_C0112 = new ErrorCode("C0112", "RPC 服务未注册");
+    ErrorCode SERVICE_ERROR_C0113 = new ErrorCode("C0113", "接口不存在");
+    ErrorCode SERVICE_ERROR_C0120 = new ErrorCode("C0120", "消息服务出错");
+    ErrorCode SERVICE_ERROR_C0121 = new ErrorCode("C0121", "消息投递出错");
+    ErrorCode SERVICE_ERROR_C0122 = new ErrorCode("C0122", "消息消费出错");
+    ErrorCode SERVICE_ERROR_C0123 = new ErrorCode("C0123", "消息订阅出错");
+    ErrorCode SERVICE_ERROR_C0124 = new ErrorCode("C0124", "消息分组未查到");
+    ErrorCode SERVICE_ERROR_C0130 = new ErrorCode("C0130", "缓存服务出错");
+    ErrorCode SERVICE_ERROR_C0131 = new ErrorCode("C0131", "key 长度超过限制");
+    ErrorCode SERVICE_ERROR_C0132 = new ErrorCode("C0132", "value 长度超过限制");
+    ErrorCode SERVICE_ERROR_C0133 = new ErrorCode("C0133", "存储容量已满");
+    ErrorCode SERVICE_ERROR_C0134 = new ErrorCode("C0134", "不支持的数据格式");
+    ErrorCode SERVICE_ERROR_C0140 = new ErrorCode("C0140", "配置服务出错");
+    ErrorCode SERVICE_ERROR_C0150 = new ErrorCode("C0150", "网络资源服务出错");
+    ErrorCode SERVICE_ERROR_C0151 = new ErrorCode("C0151", "VPN 服务出错");
+    ErrorCode SERVICE_ERROR_C0152 = new ErrorCode("C0152", "CDN 服务出错");
+    ErrorCode SERVICE_ERROR_C0153 = new ErrorCode("C0153", "域名解析服务出错");
+    ErrorCode SERVICE_ERROR_C0154 = new ErrorCode("C0154", "网关服务出错");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode SERVICE_ERROR_C0200 = new ErrorCode("C0200", "第三方系统执行超时");
+    ErrorCode SERVICE_ERROR_C0210 = new ErrorCode("C0210", "RPC 执行超时");
+    ErrorCode SERVICE_ERROR_C0220 = new ErrorCode("C0220", "消息投递超时");
+    ErrorCode SERVICE_ERROR_C0230 = new ErrorCode("C0230", "缓存服务超时");
+    ErrorCode SERVICE_ERROR_C0240 = new ErrorCode("C0240", "配置服务超时");
+    ErrorCode SERVICE_ERROR_C0250 = new ErrorCode("C0250", "数据库服务超时");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode SERVICE_ERROR_C0300 = new ErrorCode("C0300", "数据库服务出错");
+    ErrorCode SERVICE_ERROR_C0311 = new ErrorCode("C0311", "表不存在");
+    ErrorCode SERVICE_ERROR_C0312 = new ErrorCode("C0312", "列不存在");
+    ErrorCode SERVICE_ERROR_C0321 = new ErrorCode("C0321", "多表关联中存在多个相同名称的列");
+    ErrorCode SERVICE_ERROR_C0331 = new ErrorCode("C0331", "数据库死锁");
+    ErrorCode SERVICE_ERROR_C0341 = new ErrorCode("C0341", "主键冲突");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode SERVICE_ERROR_C0400 = new ErrorCode("C0400", "第三方容灾系统被触发");
+    ErrorCode SERVICE_ERROR_C0401 = new ErrorCode("C0401", "第三方系统限流");
+    ErrorCode SERVICE_ERROR_C0402 = new ErrorCode("C0402", "第三方功能降级");
+    /**
+     * 二级宏观错误码
+     */
+    ErrorCode SERVICE_ERROR_C0500 = new ErrorCode("C0500", "通知服务出错");
+    ErrorCode SERVICE_ERROR_C0501 = new ErrorCode("C0501", "短信提醒服务失败");
+    ErrorCode SERVICE_ERROR_C0502 = new ErrorCode("C0502", "语音提醒服务失败");
+    ErrorCode SERVICE_ERROR_C0503 = new ErrorCode("C0503", "邮件提醒服务失败");
+
+
+}

+ 283 - 0
src/main/java/com/persagy/common/R.java

@@ -0,0 +1,283 @@
+package com.persagy.common;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.util.Assert;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 通用返回
+ *
+ * @param <T> 数据泛型
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ApiModel(value = "接口返回对象", description = "接口返回对象")
+public class R<T> implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 成功
+     */
+    private static final String SUCCESS = "success";
+    /**
+     * 失败
+     */
+    private static final String FAIL = "fail";
+
+    /**
+     * 错误码
+     *
+     * @see ErrorCode#getCode()
+     */
+    @ApiModelProperty(value = "结果码")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String code;
+    /**
+     * success、fail
+     */
+    @ApiModelProperty(value = "结果标识:成功标识:success,失败标识:fail")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String result;
+    /**
+     * 返回数据
+     */
+    @ApiModelProperty(value = "响应数据")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private T data;
+    /**
+     * 错误提示,用户可阅读
+     *
+     * @see ErrorCode#getMessage() ()
+     */
+    @ApiModelProperty(value = "错误提示信息")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String message;
+    /**
+     * 返回记录条数或者操作记录条数
+     */
+    @ApiModelProperty(value = "返回记录条数或者操作记录条数")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Integer count;
+    /**
+     * 记录总条数
+     */
+    @ApiModelProperty(value = "记录总条数")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Long total;
+    /**
+     * 时间戳
+     */
+    @ApiModelProperty(value = "响应时间戳")
+    private long timestamp = System.currentTimeMillis();
+
+    /**
+     * 附加数据
+     */
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    @ApiModelProperty(value = "附加数据")
+    private Map<Object, Object> extra;
+
+    /**
+     * 链路traceId
+     */
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    @ApiModelProperty(value = "链路traceId")
+    private String traceId;
+
+    /**
+     * 通过 withColumns 可以控制返回的数据内容,可以一定程度上提供服务性能.
+     * withColumns 粒度比较粗,一般由服务提供者决定哪些字段可以通过 withColumns 来控制.
+     */
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    @ApiModelProperty(value = "扩展字段数据")
+    private List<String> withColumns;
+
+    /**
+     * 通过 includeColumns指定返回字段,可以一定程度上提供服务性能.
+     * 由服务提供者过滤字段,提高接口效率
+     */
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    @ApiModelProperty(value = "指定返回字段")
+    private List<String> includeColumns;
+
+    /**
+     * 系统报错时,抛出的原生信息
+     */
+    @ApiModelProperty(value = "异常消息")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String cause;
+    /**
+     * 是否执行默认操作
+     */
+    @JsonIgnore
+    private Boolean defExec = true;
+
+    /**
+     * 将传入的 result 对象,转换成另外一个泛型结果的对象
+     * <p>
+     * 因为 A 方法返回的 R 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
+     *
+     * @param result 传入的 result 对象
+     * @param <T>    返回的泛型
+     * @return 新的 R 对象
+     */
+    public static <T> R<T> fail(R<?> result) {
+        return fail(result.getCode(), result.getMessage());
+    }
+
+
+    public static <T> R<T> fail(String code, String message, String cause) {
+        Assert.isTrue(!ErrorCodeConstants.SUCCESS.getCode().equals(code), "code 必须是错误的!");
+        R<T> result = new R<>();
+        result.code = code;
+        result.message = message;
+        result.cause = cause;
+        result.result = FAIL;
+        return result;
+    }
+
+    public static <T> R<T> fail(String code, String message) {
+        return fail(code, message, null);
+    }
+
+    public static <T> R<T> fail(ErrorCode errorCode) {
+        return fail(errorCode.getCode(), errorCode.getMessage());
+    }
+
+    public static <T> R<T> success(T data) {
+        R<T> result = new R<>();
+        result.code = ErrorCodeConstants.SUCCESS.getCode();
+        result.data = data;
+        result.result = SUCCESS;
+        return result;
+    }
+
+    public static boolean isSuccess(String code) {
+        return Objects.equals(code, ErrorCodeConstants.SUCCESS.getCode());
+    }
+
+    public static <T> R<T> fail(BusinessException businessException) {
+        return fail(businessException.getCode(), businessException.getMessage());
+    }
+
+    /**
+     * 多个数据
+     *
+     * @param records
+     * @param total
+     * @param <T>
+     * @return
+     */
+    public static <T> R<Collection<T>> successMulti(Collection<T> records, Long total) {
+        R<Collection<T>> result = success(records);
+        result.setCount(CollUtil.size(records));
+        result.setTotal(total);
+        return result;
+    }
+
+    /**
+     * 多个数据
+     *
+     * @param records
+     * @param <T>
+     * @return
+     */
+    public static <T> R<Collection<T>> successMulti(Collection<T> records) {
+        return successMulti(records, null);
+    }
+
+
+    public static <T> R<T> validFail(String msg) {
+        return R.fail(ErrorCodeConstants.USER_ERROR_A0421.getCode(), (msg == null || msg.isEmpty()) ? ErrorCodeConstants.USER_ERROR_A0421.getMessage() : msg);
+    }
+
+    public static <T> R<T> validFail(String msg, Object... args) {
+        String message = (msg == null || msg.isEmpty()) ? ErrorCodeConstants.USER_ERROR_A0421.getMessage() : msg;
+        return R.fail(ErrorCodeConstants.USER_ERROR_A0421.getCode(), StrUtil.format(message, args));
+    }
+
+    @JsonIgnore // 避免 jackson 序列化
+    public boolean getIsFail() {
+        return !isSuccess(code);
+    }
+
+    @JsonIgnore // 避免 jackson 序列化
+    public boolean getIsSuccess() {
+        return isSuccess(code);
+    }
+
+    /**
+     * 判断是否有异常。如果有,则抛出 {@link BusinessException} 异常
+     */
+    public void checkError() throws BusinessException {
+        if (isSuccess(code)) {
+            return;
+        }
+        // 业务异常
+        throw new BusinessException(code, message);
+    }
+
+    public R<T> setCode(String code) {
+        this.code = code;
+        return this;
+    }
+
+    public R<T> setResult(String result) {
+        this.result = result;
+        return this;
+    }
+
+    public R<T> setData(T data) {
+        this.data = data;
+        return this;
+    }
+
+    public R<T> setMessage(String message) {
+        this.message = message;
+        return this;
+    }
+
+    public R<T> setCount(Integer count) {
+        this.count = count;
+        return this;
+    }
+
+    public R<T> setTotal(Long total) {
+        this.total = total;
+        return this;
+    }
+
+    public R<T> setExtra(Map<Object, Object> extra) {
+        this.extra = extra;
+        return this;
+    }
+
+    public R<T> setCause(String cause) {
+        this.cause = cause;
+        return this;
+    }
+
+    public R<T> setTraceId(String traceId) {
+        this.traceId = traceId;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return JSON.toJSONString(this);
+    }
+}

+ 39 - 0
src/main/java/com/persagy/config/AsyncProperties.java

@@ -0,0 +1,39 @@
+package com.persagy.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+
+/**
+ * @description:异步线程配置
+ * @author:lgy
+ * @data:2021/12/28 18:25
+ */
+@Getter
+@Setter
+@ConfigurationProperties(AsyncProperties.PREFIX)
+public class AsyncProperties {
+    public static final String PREFIX = "paas.async";
+    private boolean enabled = true;
+    /**
+     * 异步核心线程数,默认:5
+     */
+    private int corePoolSize = 5;
+    /**
+     * 异步最大线程数,默认:50
+     */
+    private int maxPoolSize = 50;
+    /**
+     * 队列容量,默认:10000
+     */
+    private int queueCapacity = 10000;
+    /**
+     * 线程存活时间,默认:300
+     */
+    private int keepAliveSeconds = 300;
+    /**
+     * 线程名前缀
+     */
+    private String threadNamePrefix = "paas-async-executor-";
+}

+ 56 - 0
src/main/java/com/persagy/config/DefaultAsyncTaskConfig.java

@@ -0,0 +1,56 @@
+package com.persagy.config;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+
+/**
+ * @description:异步任务配置
+ * @author:lgy
+ * @data:2021/12/28 18:25
+ */
+@Setter
+@Getter
+@AllArgsConstructor
+@Configuration
+@EnableAsync(proxyTargetClass = true)
+@EnableConfigurationProperties({AsyncProperties.class})
+@ConditionalOnProperty(prefix = AsyncProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
+public class DefaultAsyncTaskConfig extends AsyncConfigurerSupport {
+    private final AsyncProperties asyncProperties;
+
+    @Override
+    @Bean
+    public Executor getAsyncExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(asyncProperties.getCorePoolSize());
+        executor.setMaxPoolSize(asyncProperties.getMaxPoolSize());
+        executor.setQueueCapacity(asyncProperties.getQueueCapacity());
+        executor.setKeepAliveSeconds(asyncProperties.getKeepAliveSeconds());
+        executor.setThreadNamePrefix(asyncProperties.getThreadNamePrefix());
+        /*
+           rejection-policy:当pool已经达到max size的时候,如何处理新任务
+           CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
+        */
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        return executor;
+    }
+
+    @Override
+    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+        return new SimpleAsyncUncaughtExceptionHandler();
+    }
+}

+ 65 - 0
src/main/java/com/persagy/config/SwaggerAutoConfiguration.java

@@ -0,0 +1,65 @@
+package com.persagy.config;
+
+import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import static springfox.documentation.builders.RequestHandlerSelectors.basePackage;
+
+/**
+ * Swagger2 自动配置类
+ */
+@Configuration
+@EnableSwagger2
+@EnableKnife4j
+@ConditionalOnClass({Docket.class, ApiInfoBuilder.class})
+@ConditionalOnProperty(prefix = "swagger", value = "enable", matchIfMissing = true)
+// 允许使用 swagger.enable=false 禁用 Swagger
+@EnableConfigurationProperties(SwaggerProperties.class)
+public class SwaggerAutoConfiguration {
+
+    /**
+     * API 摘要信息
+     */
+    private static ApiInfo apiInfo(SwaggerProperties properties) {
+        return new ApiInfoBuilder()
+                .title(properties.getTitle())
+                .description(properties.getDescription())
+                .contact(new Contact(properties.getAuthor(), null, null))
+                .version(properties.getVersion())
+                .build();
+    }
+
+
+    @Bean
+    @ConditionalOnMissingBean
+    public SwaggerProperties swaggerProperties() {
+        return new SwaggerProperties();
+    }
+
+    @Bean
+    public Docket createRestApi() {
+        SwaggerProperties properties = swaggerProperties();
+        // 创建 Docket 对象
+        return new Docket(DocumentationType.SWAGGER_2)
+                // 用来创建该 API 的基本信息,展示在文档的页面中(自定义展示的信息)
+                .apiInfo(apiInfo(properties))
+                // 设置扫描指定 package 包下的
+                .select()
+                .apis(basePackage(properties.getBasePackage()))
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+}

+ 43 - 0
src/main/java/com/persagy/config/SwaggerProperties.java

@@ -0,0 +1,43 @@
+package com.persagy.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * Swagger 配置属性
+ *
+ * @author
+ */
+@ConfigurationProperties("swagger")
+@Data
+public class SwaggerProperties {
+
+    /**
+     * 标题
+     */
+    @NotEmpty(message = "标题不能为空")
+    private String title;
+    /**
+     * 描述
+     */
+    @NotEmpty(message = "描述不能为空")
+    private String description;
+    /**
+     * 作者
+     */
+    @NotEmpty(message = "作者不能为空")
+    private String author;
+    /**
+     * 版本
+     */
+    @NotEmpty(message = "版本不能为空")
+    private String version;
+    /**
+     * 扫描的包
+     */
+    @NotEmpty(message = "扫描的 package 不能为空")
+    private String basePackage;
+
+}

+ 37 - 0
src/main/java/com/persagy/entity/InsertData.java

@@ -0,0 +1,37 @@
+package com.persagy.entity;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ * @description: TODO
+ * @author:LuoGuangyi
+ * @company:PersagyTechnologyCo.,Ltd
+ * @since:2021/04/01 09:57
+ * @version:V1.0
+ **/
+@NoArgsConstructor
+@Data
+@Builder
+@Accessors(chain = true)
+public class InsertData {
+    public String prefix;
+    public String building;
+    public String meter;
+    public Long funcid;
+    public Date receivetime;
+    public Double data;
+
+    public InsertData(String prefix, String building, String sign, Long funcid, Date receivetime, double data) {
+        this.prefix = prefix;
+        this.building = building;
+        this.meter = sign;
+        this.funcid = funcid;
+        this.receivetime = receivetime;
+        this.data = data;
+    }
+}

+ 52 - 0
src/main/java/com/persagy/excel/ExcelData.java

@@ -0,0 +1,52 @@
+package com.persagy.excel;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+/**
+ * @description: 处理excel导入是用到的的解析后的excel数据
+ * @author:LuoGuangyi
+ * @company:PersagyTechnologyCo.,Ltd
+ * @since:2021/04/27 18:29
+ * @version:V1.0
+ **/
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@Accessors
+public class ExcelData {
+    /**
+     * 建筑
+     */
+    private String buildding;
+    /**
+     * 表号列表
+     */
+    private List<String> meterList;
+    /**
+     * 功能号列表
+     */
+    private List<String> funcidList;
+    /**
+     * 描述列表
+     */
+    private List<String> descriptionList;
+    /**
+     * 时间数值列表
+     */
+    private List<List<String>> dataList;
+
+    /**
+     * 是否动态导入
+     * 动态导入需要websocket实时推送和回包,分精度计算应该依赖分精度计算程序
+     * 静态导入瞬时数据量大, 不适合websocket推送和回包处理,容易导致北向服务处理不过来挂掉,需要走直接插入表的逻辑;
+     * 静态导入可以直接生成分精度,同时考虑到分精度服务在同步计算,不修改分精度计算的表号-功能号和项目的的计算进度回拨,只是单纯插入数据
+     */
+    private Boolean dynamic = false;
+}

+ 273 - 0
src/main/java/com/persagy/excel/ExcelListener.java

@@ -0,0 +1,273 @@
+package com.persagy.excel;
+
+import cn.hutool.core.thread.ThreadUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import com.alibaba.excel.exception.ExcelDataConvertException;
+import com.alibaba.excel.metadata.CellExtra;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.persagy.util.DateUtil;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+/***
+ *
+ * 单元格合并参考连接:
+ * https://developer.aliyun.com/article/852802
+ * https://blog.csdn.net/qq_40823831/article/details/120553658
+ * @author lgy
+ * @date 2022/4/27 10:15
+ */
+@Getter
+@Setter
+@Slf4j
+@NoArgsConstructor
+@Builder
+@AllArgsConstructor
+public class ExcelListener<T> extends AnalysisEventListener<T> {
+
+    /**
+     * 数据列
+     */
+    private List<List<String>> dataList = new ArrayList<>();
+    /**
+     * 表号列
+     */
+    private List<String> meterList = new ArrayList<>();
+    /**
+     * 功能号列表
+     */
+    private List<String> funcidList = new ArrayList<>();
+    /**
+     * 描述列表
+     */
+    private List<String> descriptionList = new ArrayList<>();
+    /**
+     * 根据sheetName读取项目ID
+     */
+    private String buildding;
+    /**
+     * 数据处理逻辑
+     * 发数入库
+     */
+    private Consumer<ExcelData> consumer;
+    /**
+     * 每次批量处理的条数
+     */
+    private Integer batchSize;
+    /**
+     * 控制是否继续读取
+     */
+    private Boolean hasNext = true;
+    /**
+     * 是否只读取前3行的表头
+     */
+    private Boolean onlyReadHeader = false;
+
+    /**
+     * 是否动态导入
+     * 动态导入需要websocket实时推送和回包,分精度计算应该依赖分精度计算程序
+     * 静态导入瞬时数据量大, 不适合websocket推送和回包处理,容易导致北向服务处理不过来挂掉,需要走直接插入表的逻辑;
+     * 静态导入可以直接生成分精度,同时考虑到分精度服务在同步计算,不修改分精度计算的表号-功能号和项目的的计算进度回拨,只是单纯插入数据
+     */
+    private Boolean dynamic = false;
+
+    /**
+     * 是否读取结束了
+     */
+    private Boolean readAll = false;
+    /**
+     * 数据的开始时间
+     */
+    private String startTime;
+    /**
+     * 数据的结束时间
+     */
+    private String endTime;
+    /**
+     * 最后一次读取的行数
+     */
+    private Integer lastRowNumber;
+    /**
+     * 上一次保存数据的时间
+     */
+    private LocalDateTime latestSaveTime = LocalDateTime.now();
+    /**
+     * 总处理数据行数
+     * (不含表头)
+     */
+    private AtomicLong totalCount = new AtomicLong();
+
+    private List<String> latestData;
+
+    private Integer sleepMillis = 1000;
+    /**
+     * 预留,暂时不使用
+     * 第几行读取 默认为空,如果又合并的单元格就去获取EasyExcel 内置ReadSheetHolder 然后获取读取的开始行
+     * 从没有合并的单元格,则用不上该属性
+     */
+//    private Integer headRowNumber = null;
+    public ExcelListener(Consumer<ExcelData> consumer, Integer batchSize) {
+        this.consumer = consumer;
+        this.batchSize = batchSize;
+    }
+
+    @Override
+    public void invoke(T object, AnalysisContext context) {
+        lastRowNumber = context.readRowHolder().getRowIndex();
+        if (ObjectUtil.isEmpty(buildding)) {
+            buildding = context.readSheetHolder().getSheetName();
+        }
+        if (ObjectUtil.isEmpty(meterList)) {
+            meterList = new ArrayList<>(JSON.parseObject(JSON.toJSONString(object, SerializerFeature.WriteMapNullValue), new TypeReference<HashMap<Integer, String>>() {
+            }).values());
+            return;
+        }
+        if (ObjectUtil.isEmpty(funcidList)) {
+            funcidList = new ArrayList<String>(JSON.parseObject(JSON.toJSONString(object, SerializerFeature.WriteMapNullValue), new TypeReference<HashMap<Integer, String>>() {
+            }).values());
+            return;
+        }
+        if (ObjectUtil.isEmpty(descriptionList)) {
+            descriptionList = new ArrayList<String>(JSON.parseObject(JSON.toJSONString(object, SerializerFeature.WriteMapNullValue), new TypeReference<HashMap<Integer, String>>() {
+            }).values());
+            return;
+        }
+        if (onlyReadHeader) {
+            hasNext = false;
+            return;
+        }
+        List<String> data = new ArrayList<String>(JSON.parseObject(JSON.toJSONString(object, SerializerFeature.WriteMapNullValue), new TypeReference<HashMap<Integer, String>>() {
+        }).values());
+
+        String dataTime = data.get(0);
+        try {
+            DateUtil.parse(dataTime);
+            if (dynamic && ObjectUtil.isNotEmpty(latestData) && dataTime.compareTo(latestData.get(0)) < 0) {
+                log.error("Excel行数:[{}]行数据非单调递增,当前行[{}],上一行[{}]", context.readRowHolder().getRowIndex(), data, latestData);
+            }
+        } catch (Exception e) {
+            log.info("Excel行数:[{}]行数据异常,数据内容:[{}],解析后格式:{}", context.readRowHolder().getRowIndex(), JSON.toJSONString(object), data);
+            return;
+        }
+        latestData = data;
+        if (StrUtil.isAllNotEmpty(dataTime, startTime, endTime)) {
+            if (startTime.compareTo(dataTime) > 0) {
+                //比开始时间还小的数据直接过滤掉
+                return;
+            }
+            if (endTime.compareTo(dataTime) < 0) {
+                //比结束时间还大,停止读取
+                hasNext = false;
+                if (consumer != null) {
+                    consumer.accept(ExcelData.builder().dataList(dataList).descriptionList(descriptionList).funcidList(funcidList).meterList(meterList).buildding(buildding).dynamic(dynamic).build());
+                    dataList = new ArrayList<>();
+                    latestSaveTime = LocalDateTime.now();
+                }
+                return;
+            }
+        }
+        dataList.add(data);
+        if (totalCount.incrementAndGet() % 1000 == 0) {
+            log.info("已经处理Excel行数:[{}]行", totalCount.get());
+        }
+        //批量处理的,数据大于batchSize或者时间超过10秒保存一次
+        if (ObjectUtil.isAllNotEmpty(batchSize, consumer) && (dataList.size() >= batchSize || Math.abs(DateUtil.betweenTwoTime(latestSaveTime, LocalDateTime.now(), ChronoUnit.SECONDS)) > 10)) {
+            consumer.accept(ExcelData.builder().dataList(dataList).descriptionList(descriptionList).funcidList(funcidList).meterList(meterList).buildding(buildding).dynamic(dynamic).build());
+            dataList = new ArrayList<>();
+            latestSaveTime = LocalDateTime.now();
+            ThreadUtil.safeSleep(sleepMillis);
+        }
+    }
+
+    @Override
+    public void doAfterAllAnalysed(AnalysisContext context) {
+        if (consumer != null) {
+            consumer.accept(ExcelData.builder().dataList(dataList).descriptionList(descriptionList).funcidList(funcidList).meterList(meterList).buildding(buildding).build());
+            dataList = new ArrayList<>();
+            latestSaveTime = LocalDateTime.now();
+        }
+        readAll = true;
+        log.warn("所有数据解析完成!");
+    }
+
+    /**
+     * Verify that there is another piece of data.You can stop the read by returning false
+     *
+     * @param context
+     * @return
+     */
+    @Override
+    public boolean hasNext(AnalysisContext context) {
+        return hasNext;
+    }
+
+    /**
+     * The current method is called when extra information is returned
+     *
+     * @param extra   extra information
+     * @param context analysis context
+     */
+    @Override
+    public void extra(CellExtra extra, AnalysisContext context) {
+        super.extra(extra, context);
+    }
+
+
+    // 这个是读取异常是的回调
+    @Override
+    public void onException(Exception exception, AnalysisContext context) throws Exception {
+        // 如果是某一个单元格的转换异常 能获取到具体行号
+        if (exception instanceof ExcelDataConvertException) {
+            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
+            log.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(), excelDataConvertException.getColumnIndex());
+        }
+        log.error("解析失败,但是继续解析下一行:{} ", exception.getMessage(), exception);
+//            super.onException(exception, context);
+    }
+
+
+//    // 这个是读取单元格和并时的信息
+//    @Override
+//    public void extra(CellExtra extra, AnalysisContext context) {
+//        // 解析合并单元格的信息 利用反射给合并的单元格读不到值时,进行赋值
+//        if(headRowNumber==null){
+//            headRowNumber = context.readSheetHolder().getHeadRowNumber();
+//        }
+//        // 获取合并后的第一个索引
+//        Integer index = extra.getFirstRowIndex() - headRowNumber;
+//        // 获取合并后的最后一个索引
+//        Integer lastRowIndex = extra.getLastRowIndex() - headRowNumber;
+//        // 获取合并后的第一个值
+//        T t = excels.get(index);
+//        // 利用反射获取所有私有属性
+//        Field[] fields = t.getClass().getDeclaredFields();
+//        for (Field field:fields) {
+//            // 让该属性可操作
+//            field.setAccessible(true);
+//            // 获取EasyExcel 原有注解
+//            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
+//            // 循环遍历空缺的值
+//            for (int i = index+1; i <= lastRowIndex; i++) {
+//                // 当读取的列索引等于注解的索引时,表示就是该属性赋值
+//                if (extra.getFirstColumnIndex().equals(annotation.index())){
+//                    // 利用反射赋值
+//                    field.set(excels.get(i),field.get(t));
+//                }
+//            }
+//        }
+//
+//    }
+}

+ 138 - 0
src/main/java/com/persagy/excel/ExcelUtils.java

@@ -0,0 +1,138 @@
+package com.persagy.excel;
+
+import cn.hutool.core.thread.ThreadUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.fastjson.JSON;
+import com.persagy.common.ErrorCodeConstants;
+import com.persagy.common.R;
+import com.persagy.communication.entity.Packet;
+import com.persagy.communication.util.IClientManager;
+import com.persagy.parser.ParserEntity;
+import com.persagy.parser.RecordData;
+import com.persagy.util.DateUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.formula.functions.T;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+/**
+ * Excel 工具类
+ *
+ * @author
+ */
+@Slf4j
+public class ExcelUtils {
+
+    public static AtomicLong totalCount = new AtomicLong(0);
+    public static IClientManager ClientManager;
+
+    public static final Consumer<ExcelData> consumeIot = (t) -> {
+        String buildding = t.getBuildding();
+        List<List<String>> dataList = t.getDataList();
+        List<String> descriptionList = t.getDescriptionList();
+        List<String> meterList = t.getMeterList();
+        List<String> funcidList = t.getFuncidList();
+        Boolean dynamic = t.getDynamic();
+        try {
+            ParserEntity parserEntity = ParserEntity.builder().building(buildding).gateway("0").datatype("report").packageId("0").dataList(new ArrayList<>()).build();
+            for (List<String> item : dataList) {
+                Date time = DateUtil.parseDate(item.get(0));
+                for (int i = 1; i < item.size(); i++) {
+                    String value = item.get(i);
+                    String meter = meterList.get(i);
+                    String funcid = funcidList.get(i);
+                    if (StrUtil.isAllNotEmpty(value, meter, funcid)) {
+                        RecordData recordData = RecordData.builder().meter(meter).value(value).funcid(funcid).time(time).build();
+                        parserEntity.dataList.add(recordData);
+                    }
+                }
+            }
+            log.info("保存数据:{}", JSON.toJSONString(parserEntity));
+            if (ObjectUtil.isNotEmpty(parserEntity.dataList)) {
+                StringBuffer sb = new StringBuffer();
+                for (int i = 0; i < parserEntity.dataList.size(); i++) {
+                    RecordData recordData = parserEntity.dataList.get(i);
+                    if (sb.length() > 0) {
+                        sb.append("&");
+                    }
+                    sb.append(parserEntity.building).append(";").append(parserEntity.gateway).append(";report;").append(DateUtil.formatDate(recordData.time))
+                            .append(";").append(i).append(";").append(recordData.meter).append(";").append("1").append(";" + recordData.funcid + ";" + recordData.value);
+                    if(sb.length()>1500){
+                        ClientManager.AppendToSend(new Packet(sb.toString()));
+                        sb.setLength(0);
+                    }
+                }
+                if(sb.length()>0){
+                    ClientManager.AppendToSend(new Packet(sb.toString()));
+                    sb.setLength(0);
+                }
+            }
+
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+    };
+
+    public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException {
+        return EasyExcel.read(file.getInputStream(), head, null)
+                .autoCloseStream(false)  // 不要自动关闭,交给 Servlet 自己处理
+                .doReadAllSync();
+    }
+
+    /**
+     * 异步读取excel
+     *
+     * @param filePath      文件觉得路径
+     * @param sheetNo       从0开始
+     * @param head          Annotate the class for configuration information.
+     * @param headRowNumber Count the number of added heads when read sheet.
+     *                      0 - This Sheet has no head ,since the first row are the data
+     *                      1 - This Sheet has one row head , this is the default
+     *                      2 - This Sheet has two row head ,since the third row is the data
+     * @param excelListener
+     * @return 读取的数据
+     */
+    public static R<ExcelListener> read(String filePath, MultipartFile simpleFile, InputStream simpleFileInputStream, Integer sheetNo, Class<T> head, Integer headRowNumber, ExcelListener<T> excelListener) {
+        if (!ObjectUtil.isAllEmpty(simpleFile, simpleFileInputStream)) {
+            //异步
+            try {
+                if (ObjectUtil.isNotEmpty(simpleFile)) {
+                    simpleFileInputStream = simpleFile.getInputStream();
+                }
+                log.info("开始时间:[{}],结束时间[{}],全部信息[{}]", excelListener.getStartTime(), excelListener.getEndTime(), JSON.toJSONString(excelListener));
+                EasyExcel.read(simpleFileInputStream, head, excelListener).sheet(sheetNo).autoTrim(true).headRowNumber(headRowNumber).doRead();
+            } catch (Exception e) {
+                log.error("读取异常:{}", filePath, e);
+                return R.fail(ErrorCodeConstants.USER_ERROR_A0001.getCode(), e.getMessage());
+            }
+            log.info("读到的数据信息:[{}]", JSON.toJSONString(excelListener));
+            return R.success(excelListener);
+        }
+        if (StrUtil.isNotBlank(filePath)) {
+            try (InputStream fileStream = new FileInputStream(filePath);) {
+                //异步
+                EasyExcel.read(fileStream, head, excelListener).sheet(sheetNo).autoTrim(true).headRowNumber(headRowNumber).doRead();
+                //同步
+                // return EasyExcel.read(fileStream).sheet(1).doReadSync();
+                return R.success(excelListener);
+            } catch (Exception e) {
+                log.error("读取异常:{}", filePath, e);
+                return R.fail(ErrorCodeConstants.USER_ERROR_A0001.getCode(), e.getMessage());
+            }
+        }
+        return R.fail(ErrorCodeConstants.USER_ERROR_A0001.getCode(), "文件为空");
+    }
+
+
+
+}

+ 15 - 8
src/main/java/com/persagy/parser/ParserEntity.java

@@ -1,5 +1,10 @@
 package com.persagy.parser;
 
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -10,13 +15,15 @@ import java.util.List;
  * @since:2021/04/01 09:59
  * @version:V1.0
  **/
+@Builder
+@Accessors
+@AllArgsConstructor
+@NoArgsConstructor
 public class ParserEntity {
-	public String building;
-	public String gateway;
-	public String packageId;
-	public String datatype; // report text senddownreadack senddownsetack reportpointset virtualpointset
-	public List<RecordData> dataList = new ArrayList<RecordData>();
+    public String building;
+    public String gateway;
+    public String packageId;
+    public String datatype; // report text senddownreadack senddownsetack reportpointset virtualpointset
+    public List<RecordData> dataList = new ArrayList<RecordData>();
+}
 
-	public ParserEntity() {
-	}
-}

+ 8 - 3
src/main/java/com/persagy/parser/RecordData.java

@@ -3,6 +3,10 @@ package com.persagy.parser;
 import java.util.Date;
 
 import com.alibaba.fastjson.JSONObject;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
 
 /**
  * @description: TODO
@@ -11,6 +15,10 @@ import com.alibaba.fastjson.JSONObject;
  * @since:2021/04/01 09:58
  * @version:V1.0
  **/
+@Builder
+@Accessors
+@AllArgsConstructor
+@NoArgsConstructor
 public class RecordData {
 	public String meter;
 	public String funcid;
@@ -22,7 +30,4 @@ public class RecordData {
 
 	public String happeningType;
 	public JSONObject happeningContent;
-
-	public RecordData() {
-	}
 }

+ 208 - 0
src/main/java/com/persagy/rest/RestApi.java

@@ -0,0 +1,208 @@
+package com.persagy.rest;
+
+import cn.hutool.core.thread.ThreadUtil;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.persagy.common.R;
+import com.persagy.excel.ExcelData;
+import com.persagy.excel.ExcelListener;
+import com.persagy.excel.ExcelUtils;
+import com.persagy.util.DateUtil;
+import com.persagy.util.StringUtil;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.formula.functions.T;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.InputStream;
+import java.util.function.Consumer;
+
+@ApiOperation(value = "采集/控制接口")
+@RestController
+@Slf4j
+public class RestApi {
+
+    public static boolean iotExcelImport = true;
+
+    @Value("${excel.sleep.millis:1000}")
+    private int sleepMillis;
+
+    @ApiOperation(tags = {"iot数据导入"}, value = "终止所有动态导入")
+    @PostMapping(value = "/stopImportIot")
+    public R importIot() {
+        iotExcelImport = false;
+        return R.success("终止动态导入");
+    }
+
+
+    @ApiOperation(tags = {"iot数据导入"}, value = "根据配置路径Excel导入")
+    @PostMapping(value = "/importIot")
+    public R importIot(@RequestBody JSONObject param) {
+        String filePath = param.getString("filePath");
+        Integer sheetNo = ObjectUtil.isEmpty(param.getInteger("sheetNo")) ? 0 : param.getInteger("sheetNo");
+        Integer headRowNumber = ObjectUtil.isEmpty(param.getInteger("headRowNumber")) ? 0 : param.getInteger("headRowNumber");
+        Integer consumerBatchSize = ObjectUtil.isEmpty(param.getInteger("consumerBatchSize")) ? 10 : param.getInteger("consumerBatchSize");
+        String startTime = param.getString("startTime");
+        String endTime = param.getString("endTime");
+        String dynamicTime = param.getString("dynamicTime");
+        Boolean dynamicTimeEnable = ObjectUtil.isEmpty(param.getBoolean("dynamicTimeEnable")) ? false : param.getBoolean("dynamicTimeEnable");
+        return importIotData(null, filePath, null, sheetNo, headRowNumber, consumerBatchSize, startTime, dynamicTimeEnable, dynamicTime, endTime, false);
+    }
+
+    /**
+     * 导入文件
+     *
+     * @param simpleFile
+     * @param filePath
+     * @param sheetNo
+     * @param headRowNumber
+     * @param consumerBatchSize
+     * @return com.yushu.common.R
+     * @author lgy
+     * @date 2022/4/26 17:48
+     */
+    @ApiOperation(tags = {"iot数据导入"}, value = "Excel在线导入")
+    @PostMapping(value = "/importOnline")
+    public R importExcel(
+            HttpServletRequest request,
+            @RequestParam(value = "file") MultipartFile simpleFile,
+            @RequestParam(value = "filePath", required = false) String filePath,
+            @RequestParam(value = "sheetNo", defaultValue = "0") Integer sheetNo,
+            @RequestParam(value = "sheetNos", defaultValue = "") String sheetNos,
+            @RequestParam(value = "headRowNumber", defaultValue = "0") Integer headRowNumber,
+            @RequestParam(value = "consumerBatchSize", defaultValue = "10") Integer consumerBatchSize,
+            @RequestParam(value = "startTime", required = false) String startTime,
+            @RequestParam(value = "dynamicTimeEnable", defaultValue = "false") Boolean dynamicTimeEnable,
+            @RequestParam(value = "dynamicTime", required = false) String dynamicTime,
+            @RequestParam(value = "endTime", required = false) String endTime
+    ) throws Exception {
+        if(StrUtil.isNotEmpty(sheetNos)){
+            String[] sheetNoArray = sheetNos.split(",");
+            for (String s : sheetNoArray) {
+                int num = NumberUtil.parseInt(s);
+                ThreadUtil.execAsync(()->importIotData(simpleFile, filePath, null, num, headRowNumber, consumerBatchSize, startTime, dynamicTimeEnable, dynamicTime, endTime, false));
+            }
+            return R.success("excel读取中....");
+        }
+        return importIotData(simpleFile, filePath, null, sheetNo, headRowNumber, consumerBatchSize, startTime, dynamicTimeEnable, dynamicTime, endTime, false);
+    }
+
+    public R importIotData(MultipartFile simpleFile, String filePath, InputStream inputStream, Integer sheetNo, Integer headRowNumber, Integer consumerBatchSize, String startTime, Boolean dynamicTimeEnable,
+                           String dynamicTime, String endTime, boolean readFromHbase) {
+        if (dynamicTimeEnable && StringUtil.isEmpty(dynamicTime)) {
+            dynamicTime = DateUtil.formatNow();
+        }
+        if (ObjectUtil.isNotEmpty(dynamicTime)) {
+            if (StrUtil.isEmpty(startTime)) {
+                startTime = "20180101000000";
+            }
+            if (StrUtil.isEmpty(endTime)) {
+                endTime = "20500101000000";
+            }
+
+            if (dynamicTime.compareTo(startTime) <= 0) {
+                //全是动态
+                return dynamicRead(filePath, simpleFile, inputStream, sheetNo, null, headRowNumber, ExcelUtils.consumeIot, consumerBatchSize, startTime, endTime, false);
+            }
+            if (dynamicTime.compareTo(endTime) >= 0) {
+                //全是静态
+                ExcelListener<T> excelListener1 = new ExcelListener<>();
+                excelListener1.setConsumer(ExcelUtils.consumeIot);
+                excelListener1.setSleepMillis(sleepMillis);
+                excelListener1.setDynamic(false);
+                excelListener1.setStartTime(startTime);
+                excelListener1.setEndTime(endTime);
+                excelListener1.setOnlyReadHeader(false);
+                excelListener1.setBatchSize(consumerBatchSize);
+                return ExcelUtils.read(filePath, simpleFile, inputStream, sheetNo, null, headRowNumber, excelListener1);
+            }
+            //startTime--dynamicTime 静态
+            ExcelListener<T> excelListener2 = new ExcelListener<>();
+            excelListener2.setConsumer(ExcelUtils.consumeIot);
+            excelListener2.setDynamic(false);
+            excelListener2.setSleepMillis(sleepMillis);
+            excelListener2.setStartTime(startTime);
+            excelListener2.setEndTime(dynamicTime);
+            excelListener2.setOnlyReadHeader(false);
+            excelListener2.setBatchSize(consumerBatchSize);
+            R<ExcelListener> staticResult = ExcelUtils.read(filePath, simpleFile, null, sheetNo, null, headRowNumber, excelListener2);
+            if (staticResult.getIsFail()) {
+                return staticResult;
+            }
+            //dynamicTime--endTime 动态
+            return dynamicRead(filePath, simpleFile, inputStream, sheetNo, null, headRowNumber, ExcelUtils.consumeIot, consumerBatchSize, dynamicTime, endTime, readFromHbase);
+        }
+        ExcelListener<T> excelListener3 = new ExcelListener<>();
+        excelListener3.setConsumer(ExcelUtils.consumeIot);
+        excelListener3.setDynamic(false);
+        excelListener3.setStartTime(startTime);
+        excelListener3.setSleepMillis(sleepMillis);
+        excelListener3.setEndTime(endTime);
+        excelListener3.setOnlyReadHeader(false);
+        excelListener3.setBatchSize(consumerBatchSize);
+        return ExcelUtils.read(filePath, simpleFile, inputStream, sheetNo, null, headRowNumber, excelListener3);
+    }
+
+    public R dynamicRead(String filePath, MultipartFile simpleFile, InputStream inputStream, Integer sheetNo, Class<T> head, Integer headRowNumber, Consumer<ExcelData> consumer,
+                         Integer consumerBatchSize, String startTime, String endTime, boolean readFromHbase) {
+
+        ExcelListener<T> excelListener = new ExcelListener<>();
+        excelListener.setConsumer(ExcelUtils.consumeIot);
+        excelListener.setDynamic(false);
+        excelListener.setStartTime(startTime);
+        excelListener.setSleepMillis(sleepMillis);
+        excelListener.setEndTime(endTime);
+        excelListener.setOnlyReadHeader(true);
+        excelListener.setBatchSize(consumerBatchSize);
+        R<ExcelListener> resultHead = ExcelUtils.read(filePath, simpleFile, inputStream, sheetNo, null, headRowNumber, excelListener);
+        if (resultHead.getIsFail()) {
+            return resultHead;
+        }
+        ExcelListener header = resultHead.getData();
+        Boolean readAll = header.getReadAll();
+        Integer lastRowNumber = header.getLastRowNumber() + 1;
+        R<ExcelListener> resultData = R.success(null);
+        iotExcelImport = true;
+        while (!readAll && iotExcelImport) {
+            String now = DateUtil.formatNow();
+            if (startTime.compareTo(now) < 0) {
+                ExcelListener listener = new ExcelListener<>();
+                listener.setDynamic(true);
+                listener.setBuildding(header.getBuildding());
+                listener.setMeterList(header.getMeterList());
+                listener.setFuncidList(header.getFuncidList());
+                listener.setDescriptionList(header.getDescriptionList());
+                listener.setBatchSize(consumerBatchSize);
+                listener.setConsumer(ExcelUtils.consumeIot);
+                listener.setStartTime(startTime);
+                listener.setEndTime(now);
+                listener.setLastRowNumber(lastRowNumber);
+                resultData = ExcelUtils.read(filePath, simpleFile, inputStream, sheetNo, null, lastRowNumber, listener);
+                if (resultData.getIsFail()) {
+                    return resultData;
+                }
+                readAll = resultData.getData().getReadAll();
+                lastRowNumber = resultData.getData().getLastRowNumber();
+                startTime = now;
+                if (startTime.compareTo(endTime) >= 0) {
+                    readAll = true;
+                }
+            }
+            if (!readAll) {
+                long timeMillis = NumberUtil.max(DateUtil.betweenTwoTimeMillis(DateUtil.formatNow(), DateUtil.getplusMinutes(now, 5)), 60000);
+                ThreadUtil.safeSleep(timeMillis);
+            }
+        }
+        return resultData;
+    }
+
+
+}

+ 12 - 3
src/main/java/com/persagy/simulator/MeterSimulatorLexer.java

@@ -45,12 +45,14 @@ public class MeterSimulatorLexer extends Lexer {
     public static final int F1DATE=11;
     public static final int F2=12;
 
-    	public Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow) throws RecognitionException
+    	@Override
+        public Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow) throws RecognitionException
     	{
     		throw new MismatchedTokenException(ttype, input);
     	}
 
-    	public Object recoverFromMismatchedSet(IntStream input,RecognitionException e,BitSet follow) throws RecognitionException
+    	@Override
+        public Object recoverFromMismatchedSet(IntStream input, RecognitionException e, BitSet follow) throws RecognitionException
     	{
     		throw e;
     	}
@@ -67,6 +69,7 @@ public class MeterSimulatorLexer extends Lexer {
         super(input,state);
 
     }
+    @Override
     public String getGrammarFileName() { return "MeterSimulator.g"; }
 
     // $ANTLR start "T__14"
@@ -1252,7 +1255,9 @@ public class MeterSimulatorLexer extends Lexer {
             	    break;
 
             	default :
-            	    if ( cnt11 >= 1 ) break loop11;
+            	    if ( cnt11 >= 1 ) {
+                        break loop11;
+                    }
                         EarlyExitException eee =
                             new EarlyExitException(11, input);
                         throw eee;
@@ -1272,6 +1277,7 @@ public class MeterSimulatorLexer extends Lexer {
     }
     // $ANTLR end "WS"
 
+    @Override
     public void mTokens() throws RecognitionException {
         // MeterSimulator.g:1:8: ( T__14 | T__15 | T__16 | T__17 | T__18 | T__19 | T__20 | T__21 | T__22 | T__23 | T__24 | T__25 | T__26 | T__27 | T__28 | T__29 | T__30 | T__31 | T__32 | T__33 | T__34 | T__35 | T__36 | T__37 | F0 | F1 | F1DATE | F2 | CONSTANT | ID | DOUBLE | INTEGER | NEWLINE | WS )
         int alt12=34;
@@ -1579,6 +1585,7 @@ public class MeterSimulatorLexer extends Lexer {
             this.special = DFA1_special;
             this.transition = DFA1_transition;
         }
+        @Override
         public String getDescription() {
             return "120:6: ( 'abs' | 'sin' | 'cos' | 'tan' | 'asin' | 'acos' | 'atan' | 'ceil' )";
         }
@@ -1650,6 +1657,7 @@ public class MeterSimulatorLexer extends Lexer {
             this.special = DFA2_special;
             this.transition = DFA2_transition;
         }
+        @Override
         public String getDescription() {
             return "121:10: ( 'dateyear' | 'datemonth' | 'dateday' | 'datehour' | 'dateminute' | 'datesecond' | 'datedayofweek' | 'datedayofmonth' | 'datedayofyear' )";
         }
@@ -1867,6 +1875,7 @@ public class MeterSimulatorLexer extends Lexer {
             this.special = DFA12_special;
             this.transition = DFA12_transition;
         }
+        @Override
         public String getDescription() {
             return "1:1: Tokens : ( T__14 | T__15 | T__16 | T__17 | T__18 | T__19 | T__20 | T__21 | T__22 | T__23 | T__24 | T__25 | T__26 | T__27 | T__28 | T__29 | T__30 | T__31 | T__32 | T__33 | T__34 | T__35 | T__36 | T__37 | F0 | F1 | F1DATE | F2 | CONSTANT | ID | DOUBLE | INTEGER | NEWLINE | WS );";
         }

+ 20 - 2
src/main/java/com/persagy/simulator/MeterSimulatorParser.java

@@ -69,16 +69,20 @@ public class MeterSimulatorParser extends Parser {
         return adaptor;
     }
 
+    @Override
     public String[] getTokenNames() { return MeterSimulatorParser.tokenNames; }
+    @Override
     public String getGrammarFileName() { return "MeterSimulator.g"; }
 
 
-    	public Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow) throws RecognitionException
+    	@Override
+        public Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow) throws RecognitionException
     	{
     		throw new MismatchedTokenException(ttype, input);
     	}
 
-    	public Object recoverFromMismatchedSet(IntStream input,RecognitionException e,BitSet follow) throws RecognitionException
+    	@Override
+        public Object recoverFromMismatchedSet(IntStream input, RecognitionException e, BitSet follow) throws RecognitionException
     	{
     		throw e;
     	}
@@ -86,6 +90,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class prog_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -137,6 +142,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class expr_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -220,6 +226,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class fourexpr_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -344,6 +351,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class multexpr_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -489,6 +497,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class atom_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -661,6 +670,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class ifcondition_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -783,6 +793,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class elseifcondition_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -905,6 +916,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class elsecondition_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -965,6 +977,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class condition_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -1057,6 +1070,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class andcondition_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -1149,6 +1163,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class notcondition_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -1244,6 +1259,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class itemcondition_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -1333,6 +1349,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class comparecondition_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 
@@ -1511,6 +1528,7 @@ public class MeterSimulatorParser extends Parser {
 
     public static class func_return extends ParserRuleReturnScope {
         CommonTree tree;
+        @Override
         public Object getTree() { return tree; }
     };
 

+ 2 - 0
src/main/java/com/persagy/simulator/MeterSimulatorScanner.java

@@ -62,7 +62,9 @@ public class MeterSimulatorScanner extends TreeParser {
         }
         
 
+    @Override
     public String[] getTokenNames() { return MeterSimulatorScanner.tokenNames; }
+    @Override
     public String getGrammarFileName() { return "MeterSimulatorScanner.g"; }
 
 

+ 12 - 2
src/main/java/com/persagy/simulator/MeterSimulatorWalker.java

@@ -66,7 +66,9 @@ public class MeterSimulatorWalker extends TreeParser {
         }
         
 
+    @Override
     public String[] getTokenNames() { return MeterSimulatorWalker.tokenNames; }
+    @Override
     public String getGrammarFileName() { return "MeterSimulatorWalker.g"; }
 
 
@@ -551,7 +553,11 @@ public class MeterSimulatorWalker extends TreeParser {
 
 
             match(input, Token.UP, null); 
-            if(con)value = a;else value =b;
+            if(con) {
+                value = a;
+            } else {
+                value =b;
+            }
 
             }
 
@@ -620,7 +626,11 @@ public class MeterSimulatorWalker extends TreeParser {
 
 
                     match(input, Token.UP, null); 
-                    if(con)value = a;else value =b;
+                    if(con) {
+                        value = a;
+                    } else {
+                        value =b;
+                    }
 
                     }
                     break;

+ 2 - 2
src/main/java/com/persagy/simulator_gaopin/Constant.java

@@ -319,7 +319,7 @@ public class Constant {
                 log.warn("building:" + id + "\t" + building.reportGroupList.size());
             }
         } catch (Exception e) {
-            e.printStackTrace();
+            log.error(e.getMessage(),e);
         }
 
         try {
@@ -450,7 +450,7 @@ public class Constant {
                 }
             }
         } catch (Exception e) {
-            e.printStackTrace();
+            log.error(e.getMessage(),e);
         }
     }
 

+ 7 - 4
src/main/java/com/persagy/simulator_gaopin/MyMqttCallback.java

@@ -25,11 +25,13 @@ public class MyMqttCallback implements MqttCallback {
 		this.topic_prefix = topic_prefix;
 	}
 
-	public void connectionLost(Throwable cause) {
+	@Override
+    public void connectionLost(Throwable cause) {
 		System.out.println("connection lost: " + cause.getMessage());
 	}
 
-	public void deliveryComplete(IMqttDeliveryToken token) {
+	@Override
+    public void deliveryComplete(IMqttDeliveryToken token) {
 	}
 
 	/**
@@ -37,7 +39,8 @@ public class MyMqttCallback implements MqttCallback {
 	 * @param message
 	 * @throws Exception
 	 */
-	public void messageArrived(String topic, MqttMessage message) throws Exception {
+	@Override
+    public void messageArrived(String topic, MqttMessage message) throws Exception {
 		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
 		try {
 			// int id = message.getId();
@@ -77,7 +80,7 @@ public class MyMqttCallback implements MqttCallback {
 				}
 			}
 		} catch (Exception e) {
-			e.printStackTrace();
+			log.error(e.getMessage(),e);
 			log.error(e.getMessage());
 		}
 	}

+ 2 - 1
src/main/java/com/persagy/simulator_gaopin/SimulatorMain_collector.java

@@ -1,5 +1,6 @@
 package com.persagy.simulator_gaopin;
 
+import com.persagy.excel.ExcelUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.eclipse.paho.client.mqttv3.MqttTopic;
 
@@ -14,7 +15,7 @@ public class SimulatorMain_collector {
 
 		MixClientManager MixClientManager = new MixClientManager();
 		MixClientManager.Start();
-
+		ExcelUtils.ClientManager = MixClientManager.ClientManager;
 		List<Thread_report> thread_reportList = new ArrayList<Thread_report>();
 		List<Thread_set> thread_setList = new ArrayList<Thread_set>();
 		for (int index = 0; index < Constant.buildingList.size(); index++) {

+ 4 - 3
src/main/java/com/persagy/simulator_gaopin/SimulatorUtil.java

@@ -11,6 +11,7 @@ import java.util.logging.Handler;
 
 import cn.hutool.core.util.NumberUtil;
 import cn.hutool.core.util.RandomUtil;
+import lombok.extern.slf4j.Slf4j;
 import org.antlr.runtime.ANTLRInputStream;
 import org.antlr.runtime.CommonTokenStream;
 import org.antlr.runtime.tree.CommonTree;
@@ -23,7 +24,7 @@ import com.persagy.simulator.MeterSimulatorLexer;
 import com.persagy.simulator.MeterSimulatorParser;
 import com.persagy.simulator.MeterSimulatorScanner;
 import com.persagy.simulator.MeterSimulatorWalker;
-
+@Slf4j
 public class SimulatorUtil {
 	public static final Map<String, Double> accMap = new HashMap<>();
 	static DecimalFormat df = new DecimalFormat("#.##");
@@ -103,7 +104,7 @@ public class SimulatorUtil {
 			return "";
 		} catch (Exception e) {
 			// 如果表达式不符合规则,会抛出异常
-			e.printStackTrace();
+			log.error(e.getMessage(),e);
 			return "错误:" + e.getMessage();
 		}
 	}
@@ -127,7 +128,7 @@ public class SimulatorUtil {
 			nodes.setTokenStream(tokens);
 		} catch (Exception e) {
 			// 如果表达式不符合规则,会抛出异常
-			e.printStackTrace();
+			log.error(e.getMessage(),e);
 			throw new Exception("公式格式错误:" + expressions);
 		}
 		// // 扫描变量

+ 7 - 4
src/main/java/com/persagy/simulator_gaopin/Thread_report.java

@@ -9,7 +9,9 @@ import java.util.List;
 import com.persagy.parser.ParserEntity;
 import com.persagy.parser.RecordData;
 import com.persagy.util.PacketNumGenerator;
+import lombok.extern.slf4j.Slf4j;
 
+@Slf4j
 public class Thread_report extends Thread {
 	EntityBuilding building;
 
@@ -24,12 +26,13 @@ public class Thread_report extends Thread {
 	SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
 	DecimalFormat df = new DecimalFormat("#.##");
 
+	@Override
 	public void run() {
 		while (true) {
 			try {
 				Thread.sleep(1L);
 			} catch (Exception e) {
-				e.printStackTrace();
+				log.error(e.getMessage(),e);
 			}
 
 			Date curr_time = Constant.getTime();
@@ -50,7 +53,7 @@ public class Thread_report extends Thread {
 						this.doOne(group, group.meter, funcid, group.data_type, packetList, curr_time);
 					}
 				} catch (Exception e) {
-					e.printStackTrace();
+					log.error(e.getMessage(),e);
 				}
 			} // end for
 			if (packetList.size() > 0) {
@@ -73,7 +76,7 @@ public class Thread_report extends Thread {
 			try {
 				Thread.sleep(1L);
 			} catch (InterruptedException e) {
-				e.printStackTrace();
+				log.error(e.getMessage(),e);
 			}
 		}
 	}
@@ -90,7 +93,7 @@ public class Thread_report extends Thread {
 			try {
 				Thread.sleep(1L);
 			} catch (InterruptedException e) {
-				e.printStackTrace();
+				log.error(e.getMessage(),e);
 			}
 		}
 	}

+ 4 - 3
src/main/java/com/persagy/simulator_gaopin/Thread_set.java

@@ -34,13 +34,14 @@ public class Thread_set extends Thread {
 
 	public Thread_report thread_report;
 
+	@Override
 	public void run() {
 		Date last_upload_time = null;
 		while (true) {
 			try {
 				Thread.sleep(1L);
 			} catch (Exception e) {
-				e.printStackTrace();
+				log.error(e.getMessage(),e);
 			}
 
 			Date curr_time = new Date((new Date()).getTime() / 1000L * 1000L);
@@ -49,7 +50,7 @@ public class Thread_set extends Thread {
 				try {
 					this.pointupload();
 				} catch (Exception e) {
-					e.printStackTrace();
+					log.error(e.getMessage(),e);
 				}
 			}
 
@@ -68,7 +69,7 @@ public class Thread_set extends Thread {
 							ProcessUtil.Process_senddownset(MixClientManager, building.id, ParserEntity);
 						}
 					} catch (Exception e) {
-						e.printStackTrace();
+						log.error(e.getMessage(),e);
 					}
 				}
 			}

+ 409 - 0
src/main/java/com/persagy/util/DateUtil.java

@@ -0,0 +1,409 @@
+package com.persagy.util;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+import java.util.Calendar;
+import java.util.Date;
+
+public class DateUtil {
+    public static final String sdfDay = "yyyyMMdd";
+    public static final String sdfMonth = "yyyyMM";
+    public static final String sdfTime = "yyyyMMddHHmmss";
+    public static final String sdfTimeNotDate = "HHmmss";
+    public static final String sdfHour = "yyyyMMddHH";
+    public static final String sdfMinute = "yyyyMMddHHmm";
+    // 时间格式-显示
+    public final static String date_format_show = "yyyy-MM-dd HH:mm:ss";
+    public final static String date_format_show_minute = "yyyy-MM-dd HH:mm";
+    public final static DateTimeFormatter FORMATTER_PRETTY = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    public final static DateTimeFormatter FORMATTER_MILLI_SECOND = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
+    public final static DateTimeFormatter FORMATTER_DAY_PRETTY = DateTimeFormatter.ofPattern("yyyy/MM/dd");
+    private final static DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+    /**
+     * 东八区时区偏移量
+     */
+    private final static int ASIA_SHANGHAI = 8;
+
+    /**
+     * LocalDate类型转为Date
+     *
+     * @param localDate LocalDate object
+     * @return Date object
+     */
+    public static Date localDate2Date(LocalDate localDate) {
+
+        ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
+        return Date.from(zonedDateTime.toInstant());
+    }
+
+    /**
+     * LocalDateTime类型转为Date
+     *
+     * @param localDateTime LocalDateTime object
+     * @return Date object
+     */
+    public static Date localDateTime2Date(LocalDateTime localDateTime) {
+        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
+    }
+
+    /**
+     * Description: Date转换为LocalDateTime
+     *
+     * @param date
+     * @return LocalDateTime
+     * @author luoguangyi
+     * @since 2019年8月5日: 下午5:25:27 Update By luoguangyi 2019年8月5日: 下午5:25:27
+     */
+    public static LocalDateTime date2LocalDateTime(Date date) {
+        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
+    }
+
+    // 获取指定日期的毫秒
+    public static Long getMilliByLocalDateTime(LocalDateTime time) {
+        return time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+    }
+
+    /**
+     * 获取时间戳
+     *
+     * @param dateTime
+     * @return
+     */
+    public static Long getMilliByLocalDateTime(String dateTime) {
+        return parse(dateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+    }
+
+    // 获取指定日期的秒
+    public static Long getSecondsByLocalDateTime(LocalDateTime time) {
+        return time.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
+    }
+
+    /**
+     * 格式化LocalDateTime为指定格式 字符串
+     *
+     * @param pattern 格式
+     * @return 日期字符串
+     */
+    public static String format(LocalDateTime time, String pattern) {
+        return time.format(DateTimeFormatter.ofPattern(pattern));
+    }
+
+    public static String format(LocalDateTime time, DateTimeFormatter pattern) {
+        return time.format(pattern);
+    }
+
+    public static String format(LocalDateTime time) {
+        return time.format(FORMATTER);
+    }
+
+    public static String formatDate(Date date) {
+        return format(date2LocalDateTime(date));
+    }
+
+    public static String formatDate(Date date, DateTimeFormatter formatter) {
+        return date2LocalDateTime(date).format(formatter);
+    }
+
+    /**
+     * 解析字符串日期为Date
+     *
+     * @param dateStr 日期字符串
+     * @param pattern 格式
+     * @return Date
+     */
+    public static LocalDateTime parse(String dateStr, String pattern) {
+        return LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(pattern));
+    }
+
+    public static LocalDateTime parse(String dateStr, DateTimeFormatter pattern) {
+        return LocalDateTime.parse(dateStr, pattern);
+    }
+
+    public static LocalDateTime parse(String dateStr) {
+        return LocalDateTime.parse(dateStr, FORMATTER);
+    }
+
+    public static Date parseDate(String dateStr) {
+        return localDateTime2Date(LocalDateTime.parse(dateStr, FORMATTER));
+    }
+
+    // 获取当前时间的指定格式
+    public static String formatNow(String pattern) {
+        return format(LocalDateTime.now(), pattern);
+    }
+
+    public static String formatNow() {
+        return LocalDateTime.now().format(FORMATTER);
+    }
+
+    // 日期加上一个数,根据field不同加不同值,field为ChronoUnit.*
+    public static LocalDateTime plus(LocalDateTime time, long number, TemporalUnit field) {
+        return time.plus(number, field);
+    }
+
+    // 日期减去一个数,根据field不同减不同值,field参数为ChronoUnit.*
+    public static LocalDateTime minus(LocalDateTime time, long number, TemporalUnit field) {
+        return time.minus(number, field);
+    }
+
+    /**
+     * 获取两个日期的差 field参数为ChronoUnit.* 默认累加的方式 重要说明: 计算月份差 计算的是否满月,年份同理, 区别1:Period只考虑到日期,ChronoUnit.between()计算到具体时分秒
+     * 区别2:Period只计算月份差,不考虑年份是否一样,ChronoUnit.between()计算考虑年份 :Period只考虑到日期,field.between(startTime, endTime)会计算到具体时间 period.getMonths(); field.between
+     * 比如:6月6号8点到8月6号7点: 2 1 比如:6月6号8点到8月6号9点: 2 2 比如:6月5号到8月6号: 2 2 比如:6月7号到8月6号: 1 1
+     *
+     * @param startTime
+     * @param endTime
+     * @param field     单位(年月日时分秒)
+     * @return
+     */
+    public static long betweenTwoTime(LocalDateTime startTime, LocalDateTime endTime, ChronoUnit field) {
+
+        // Period period = Period.between(LocalDate.from(startTime),
+        // LocalDate.from(endTime));
+        // startTime : 1993-10-19
+        // endTime : 2019-08-06
+        // 年龄 : 25 年 9 月 18 日
+        // period.getYears(); period.getMonths(); period.getDays();
+        // 25 年 9 月 18 日
+        // field.between(startTime, endTime);
+        // 25 年 25*12+9 ( 25*12+9)个月+18 日
+
+        return field.between(startTime, endTime);
+    }
+
+    /**
+     * 获取间隔时间
+     *
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public static long betweenTwoTime(String startTime, String endTime, ChronoUnit field) {
+        return betweenTwoTime(parse(startTime), parse(endTime), field);
+    }
+
+    /**
+     * 获取间隔秒数
+     *
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public static long betweenTwoTimeSecond(String startTime, String endTime) {
+        return betweenTwoTime(startTime, endTime, ChronoUnit.SECONDS);
+    }
+    public static long betweenTwoTimeMillis(String startTime, String endTime) {
+        return betweenTwoTime(startTime, endTime, ChronoUnit.MILLIS);
+    }
+    /**
+     * Description:获得当前时间的字符串.
+     *
+     * @return String 当前时间的字符串格式.格式是yyyyMMddHHmmss
+     * @author lijie
+     */
+    public static String getNowTimeStr() {
+        return LocalDateTime.now().format(FORMATTER);
+    }
+
+    public static String getTimeStr(LocalDateTime date) {
+        return date.format(FORMATTER);
+    }
+
+    public static String getMinusHour(String dateTime, long hour) {
+        LocalDateTime parse = parse(dateTime).minusHours(hour);
+        return format(parse);
+    }
+
+    public static String getSdfTimeNotDate(String dateTime) {
+        return dateTime.substring(8, 14);
+    }
+
+    public static String getPlusHour(String dateTime, long hour) {
+        LocalDateTime parse = parse(dateTime).plusHours(hour);
+        return format(parse);
+    }
+    public static String getplusMinutes(String dateTime, long minutes) {
+        LocalDateTime parse = parse(dateTime).plusMinutes(minutes);
+        return format(parse);
+    }
+    public static String getPlusDay(String dateTime, long days) {
+        LocalDateTime parse = parse(dateTime).plusDays(days);
+        return format(parse);
+    }
+
+    public static String getPlusHourNow(long hour) {
+        LocalDateTime parse = LocalDateTime.now().plusHours(hour);
+        return format(parse);
+    }
+
+    /**
+     * 获取年
+     *
+     * @return 年
+     */
+    public static int getYear(LocalDateTime ldt) {
+        return ldt.getYear();
+    }
+
+    /**
+     * 获取月份
+     */
+    public static int getMonth(LocalDateTime ldt) {
+        return ldt.getMonthValue();
+    }
+
+    /**
+     * 获取月份差 不算满月的月份差(比如7月31号到8月1号仍然算一个月份差)
+     */
+    public static int getBetweenMonth(LocalDateTime startLdt, LocalDateTime endLdt) {
+        int minusMonth = (endLdt.getYear() - startLdt.getYear()) * 12 - endLdt.getMonthValue() - startLdt.getMonthValue();
+        return minusMonth;
+    }
+
+    /**
+     * 获取月份最大天数
+     */
+    public static int getDaylengthOfMonth() {
+        LocalDateTime localTime = LocalDateTime.now();
+        return localTime.toLocalDate().lengthOfMonth();
+    }
+
+    /**
+     * 获取年份最大天数
+     */
+    public static int getDaylengthOfYear() {
+        LocalDateTime localTime = LocalDateTime.now();
+        return localTime.toLocalDate().lengthOfYear();
+    }
+
+    /**
+     * Description: 日期格式转换
+     *
+     * @param srcDateStr      源日期字符串
+     * @param srcDatePattern  源日期字符串格式
+     * @param destDatePattern 目标日期字符串格式
+     * @return String
+     * @author luoguangyi
+     * @since 2019年10月9日: 下午2:29:23 Update By luoguangyi 2019年10月9日: 下午2:29:23
+     */
+    public static String transferDateFormat(String srcDateStr, String srcDatePattern, String destDatePattern) {
+        DateTimeFormatter srcFommater = DateTimeFormatter.ofPattern(srcDatePattern);
+        DateTimeFormatter destFommater = DateTimeFormatter.ofPattern(destDatePattern);
+        return LocalDateTime.parse(srcDateStr, srcFommater).format(destFommater);
+    }
+
+
+
+    public static int compare(String time_period_1, String time_period_2) {
+        Long mill_1 = get_about(time_period_1);
+        Long mill_2 = get_about(time_period_2);
+        return mill_1.compareTo(mill_2);
+    }
+
+    public static long get_about(String time_period) {
+        long result = 0L;
+        if ("1min".equals(time_period)) {
+            result = 1000L * 60;
+        } else if ("5min".equals(time_period)) {
+            result = 1000L * 60 * 5;
+        } else if ("10min".equals(time_period)) {
+            result = 1000L * 60 * 10;
+        } else if ("15min".equals(time_period)) {
+            result = 1000L * 60 * 15;
+        } else if ("1h".equals(time_period)) {
+            result = 1000L * 60 * 60;
+        } else if ("1d".equals(time_period)) {
+            result = 1000L * 60 * 60 * 24;
+        } else if ("1m".equals(time_period)) {
+            result = 1000L * 60 * 60 * 24 * 30;
+        } else if ("1y".equals(time_period)) {
+            result = 1000L * 60 * 60 * 24 * 365;
+        }
+        return result;
+    }
+
+    public Date get_floor(Date time, String time_period) {
+        Date result = null;
+        if ("1min".equals(time_period)) {
+            long milliseconds_to = 1000L * 60;
+            result = new Date(time.getTime() / milliseconds_to * milliseconds_to);
+        } else if ("5min".equals(time_period)) {
+            long milliseconds_to = 1000L * 60 * 5;
+            result = new Date(time.getTime() / milliseconds_to * milliseconds_to);
+        } else if ("10min".equals(time_period)) {
+            long milliseconds_to = 1000L * 60 * 10;
+            result = new Date(time.getTime() / milliseconds_to * milliseconds_to);
+        } else if ("15min".equals(time_period)) {
+            long milliseconds_to = 1000L * 60 * 15;
+            result = new Date(time.getTime() / milliseconds_to * milliseconds_to);
+        } else if ("1h".equals(time_period)) {
+            long milliseconds_to = 1000L * 60 * 60;
+            result = new Date(time.getTime() / milliseconds_to * milliseconds_to);
+        } else if ("1d".equals(time_period)) {
+            Calendar calendar = Calendar.getInstance();
+            calendar.setTime(time);
+            calendar.set(Calendar.HOUR_OF_DAY, 0);
+            calendar.set(Calendar.MINUTE, 0);
+            calendar.set(Calendar.SECOND, 0);
+            calendar.set(Calendar.MILLISECOND, 0);
+            result = calendar.getTime();
+        } else if ("1m".equals(time_period)) {
+            Calendar calendar = Calendar.getInstance();
+            calendar.setTime(time);
+            calendar.set(Calendar.DAY_OF_MONTH, 1);
+            calendar.set(Calendar.HOUR_OF_DAY, 0);
+            calendar.set(Calendar.MINUTE, 0);
+            calendar.set(Calendar.SECOND, 0);
+            calendar.set(Calendar.MILLISECOND, 0);
+            result = calendar.getTime();
+        } else if ("1y".equals(time_period)) {
+            Calendar calendar = Calendar.getInstance();
+            calendar.setTime(time);
+            calendar.set(Calendar.MONTH, 0);
+            calendar.set(Calendar.DAY_OF_MONTH, 1);
+            calendar.set(Calendar.HOUR_OF_DAY, 0);
+            calendar.set(Calendar.MINUTE, 0);
+            calendar.set(Calendar.SECOND, 0);
+            calendar.set(Calendar.MILLISECOND, 0);
+            result = calendar.getTime();
+        }
+        return result;
+    }
+
+    public Date GetDate_offset(Date currentDate, String time_period, int count) {
+        Date nextDate = null;
+        if ("1min".equals(time_period)) {
+            nextDate = new Date(currentDate.getTime() + 1000L * 60 * count);
+        } else if ("5min".equals(time_period)) {
+            nextDate = new Date(currentDate.getTime() + 1000L * 60 * 5 * count);
+        } else if ("10min".equals(time_period)) {
+            nextDate = new Date(currentDate.getTime() + 1000L * 60 * 10 * count);
+        } else if ("15min".equals(time_period)) {
+            nextDate = new Date(currentDate.getTime() + 1000L * 60 * 15 * count);
+        } else if ("1h".equals(time_period)) {
+            nextDate = new Date(currentDate.getTime() + 1000L * 60 * 60 * count);
+        } else if ("1d".equals(time_period)) {
+            nextDate = new Date(currentDate.getTime() + 1000L * 60 * 60 * 24 * count);
+        } else if ("1m".equals(time_period)) {
+            Calendar calendar = Calendar.getInstance();
+            calendar.setTime(currentDate);
+            calendar.add(Calendar.MONTH, count);
+            nextDate = calendar.getTime();
+        } else if ("1y".equals(time_period)) {
+            Calendar calendar = Calendar.getInstance();
+            calendar.setTime(currentDate);
+            calendar.add(Calendar.YEAR, count);
+            nextDate = calendar.getTime();
+        }
+        return nextDate;
+    }
+
+}

+ 9 - 13
src/main/java/com/persagy/util/ExcelUtil.java

@@ -13,11 +13,7 @@ import java.util.Map;
 import java.util.logging.Filter;
 
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 
 public class ExcelUtil {
@@ -262,7 +258,7 @@ public class ExcelUtil {
 
 		try {
 			switch (cell.getCellType()) {
-			case Cell.CELL_TYPE_NUMERIC:
+				case NUMERIC:
 				short format = cell.getCellStyle().getDataFormat();
 				if (isDateType(cell,format)) {
 					//format == 14 || format == 22 || format == 31 || format == 57 || format == 58 || format == 176 || format == 177 || format == 178
@@ -280,23 +276,23 @@ public class ExcelUtil {
 					cellText = "" + cell.getNumericCellValue();
 				}
 				break;
-			case Cell.CELL_TYPE_STRING:
+				case STRING:
 				cellText = cell.getStringCellValue();
 				break;
-			case Cell.CELL_TYPE_FORMULA:
-				cell.setCellType(Cell.CELL_TYPE_NUMERIC);
+				case FORMULA:
 				cellText = "" + cell.getNumericCellValue();
 				break;
-			case Cell.CELL_TYPE_BLANK:
+				case BLANK:
 				break;
-			case Cell.CELL_TYPE_BOOLEAN:
+				case BOOLEAN:
 				break;
-			case Cell.CELL_TYPE_ERROR:
+				case ERROR:
 				break;
 			default:
+				break;
 			}
 		} catch (Exception e) {
-			// e.printStackTrace();
+			// log.error(e.getMessage(),e);
 		}
 
 		return cellText;

+ 17 - 11
src/main/java/com/persagy/util/FastJsonUtil.java

@@ -88,8 +88,9 @@ public class FastJsonUtil {
 	}
 
 	private static String toStringInner(Object value, boolean has_enter) {
-		if (value == null)
+		if (value == null) {
 			return "null";
+		}
 
 		if (value instanceof String) {
 			StringBuffer sb = new StringBuffer();
@@ -98,25 +99,28 @@ public class FastJsonUtil {
 		}
 
 		if (value instanceof Double) {
-			if (((Double) value).isInfinite() || ((Double) value).isNaN())
+			if (((Double) value).isInfinite() || ((Double) value).isNaN()) {
 				return "null";
-			else
+			} else {
 				return value.toString();
+			}
 		}
 
 		if (value instanceof Float) {
-			if (((Float) value).isInfinite() || ((Float) value).isNaN())
+			if (((Float) value).isInfinite() || ((Float) value).isNaN()) {
 				return "null";
-			else {
+			} else {
 				return value.toString();
 			}
 		}
 
-		if (value instanceof Number)
+		if (value instanceof Number) {
 			return value.toString();
+		}
 
-		if (value instanceof Boolean)
+		if (value instanceof Boolean) {
 			return value.toString();
+		}
 
 		if (value instanceof JSONObject) {
 			JSONObject valueJSON = (JSONObject) value;
@@ -128,10 +132,11 @@ public class FastJsonUtil {
 			while (iter.hasNext()) {
 				String key = iter.next();
 
-				if (first)
+				if (first) {
 					first = false;
-				else
+				} else {
 					sb.append(',');
+				}
 
 				if (has_enter) {
 					sb.append("\r\n\t");
@@ -157,10 +162,11 @@ public class FastJsonUtil {
 
 			sb.append('[');
 			for (int i = 0; i < valueJSON.size(); i++) {
-				if (first)
+				if (first) {
 					first = false;
-				else
+				} else {
 					sb.append(',');
+				}
 
 				if (has_enter) {
 					sb.append("\r\n\t");

+ 16 - 15
src/main/java/com/persagy/util/FunctionUtil.java

@@ -1,19 +1,20 @@
 package com.persagy.util;
 
 import com.persagy.entity.ValueObject;
+import lombok.extern.slf4j.Slf4j;
 
 import java.lang.reflect.Method;
 import java.util.Calendar;
 import java.util.Date;
-
+@Slf4j
 public class FunctionUtil {
 
 	public static ValueObject constant(String constant) {
 		ValueObject result = new ValueObject();
 		result.type = 1;
-		if (constant.equals("PI")) {
+		if ("PI".equals(constant)) {
 			result.doubleValue = Math.PI;
-		} else if (constant.equals("E")) {
+		} else if ("E".equals(constant)) {
 			result.doubleValue = Math.E;
 		}
 		return result;
@@ -28,7 +29,7 @@ public class FunctionUtil {
 			result.doubleValue = (Double) method.invoke(mathClass,
 					new Object[] {});
 		} catch (Exception e) {
-			e.printStackTrace();
+			log.error(e.getMessage(),e);
 		}
 		return result;
 	}
@@ -49,7 +50,7 @@ public class FunctionUtil {
 			result.doubleValue = (Double) method.invoke(mathClass,
 					new Object[] { valuea });
 		} catch (Exception e) {
-			e.printStackTrace();
+			log.error(e.getMessage(),e);
 		}
 		return result;
 	}
@@ -77,7 +78,7 @@ public class FunctionUtil {
 			result.doubleValue = (Double) method.invoke(mathClass,
 					new Object[] { valuea, valueb });
 		} catch (Exception e) {
-			e.printStackTrace();
+			log.error(e.getMessage(),e);
 		}
 		return result;
 	}
@@ -90,23 +91,23 @@ public class FunctionUtil {
 
 		Calendar calendar = Calendar.getInstance();
 		calendar.setTime(new Date(a.intValue));
-		if (function.equals("dateyear")) {
+		if ("dateyear".equals(function)) {
 			result.intValue = calendar.get(Calendar.YEAR);
-		} else if (function.equals("datemonth")) {
+		} else if ("datemonth".equals(function)) {
 			result.intValue = calendar.get(Calendar.MONTH) + 1;
-		} else if (function.equals("dateday")) {
+		} else if ("dateday".equals(function)) {
 			result.intValue = calendar.get(Calendar.DAY_OF_MONTH);
-		} else if (function.equals("datedayofweek")) {
+		} else if ("datedayofweek".equals(function)) {
 			result.intValue = calendar.get(Calendar.DAY_OF_WEEK);
-		} else if (function.equals("datedayofmonth")) {
+		} else if ("datedayofmonth".equals(function)) {
 			result.intValue = calendar.get(Calendar.DAY_OF_MONTH);
-		} else if (function.equals("datedayofyear")) {
+		} else if ("datedayofyear".equals(function)) {
 			result.intValue = calendar.get(Calendar.DAY_OF_YEAR);
-		} else if (function.equals("datehour")) {
+		} else if ("datehour".equals(function)) {
 			result.intValue = calendar.get(Calendar.HOUR_OF_DAY);
-		} else if (function.equals("dateminute")) {
+		} else if ("dateminute".equals(function)) {
 			result.intValue = calendar.get(Calendar.MINUTE);
-		} else if (function.equals("datesecond")) {
+		} else if ("datesecond".equals(function)) {
 			result.intValue = calendar.get(Calendar.SECOND);
 		}
 		return result;

+ 457 - 0
src/main/java/com/persagy/util/StringUtil.java

@@ -0,0 +1,457 @@
+package com.persagy.util;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.util.CollectionUtils;
+
+import java.lang.reflect.Array;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Created by gsc on 17/5/16.
+ */
+public class StringUtil {
+    public static boolean isNull(JSONArray lackFields, JSONObject jsonObject, String... params) {
+        lackFields = lackFields == null ? new JSONArray() : lackFields;
+        if (jsonObject == null) {
+            return false;
+        }
+        Object value;
+        for (String param : params) {
+            value = jsonObject.get(param);
+            if (value instanceof JSONObject) {
+                if (value == null || ((JSONObject) value).isEmpty()) {
+                    if (!lackFields.contains(param)) {
+                        lackFields.add(param);
+                    }
+                }
+            } else if (value instanceof JSONArray) {
+                if (value == null || ((JSONArray) value).isEmpty()) {
+                    if (!lackFields.contains(param)) {
+                        lackFields.add(param);
+                    }
+                }
+            } else {
+                if (value == null || "".equals(JSON.toJSON(value))) {
+                    if (!lackFields.contains(param)) {
+                        lackFields.add(param);
+                    }
+                }
+            }
+        }
+        return lackFields.size() != 0;
+    }
+
+    /**
+     * 判断String是否为null或空
+     *
+     * @param params
+     * @return
+     */
+    public static boolean isNull(String... params) {
+        for (String param : params) {
+            if (param == null || "".equals(param)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断JSONObects是否包含parmas中的字段
+     *
+     * @param jsonObject
+     * @param params
+     * @return
+     */
+    public static boolean isNull(JSONObject jsonObject, String... params) {
+        for (String param : params) {
+            if (isNull(jsonObject.getString(param))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断JSONObects是否包含parmas中的字段
+     *
+     * @param jsonObject
+     * @param params
+     * @return
+     */
+    public static boolean isExist(JSONObject jsonObject, String... params) {
+        for (String param : params) {
+            if (jsonObject.containsKey(param)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断JSONObect中时候包含parmas中字段的为空数组
+     *
+     * @param jsonObject
+     * @param params
+     * @return
+     */
+    public static boolean isEmptyList(JSONObject jsonObject, String... params) {
+        JSONArray jsonArray;
+        for (String param : params) {
+            jsonArray = jsonObject.getJSONArray(param);
+            if (jsonArray == null || jsonArray.isEmpty()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /*
+     * Description: 判断字符串中source是否含有字符串specialChars
+     * @param source
+     * @param specialChars
+     * @return boolean
+     * @author cuixubin
+     * @since 2018年6月14日: 上午10:41:01
+     * Update By cuixubin 2018年6月14日: 上午10:41:01
+     */
+    public static boolean strContainStr(String source, String specialChars) {
+        String regEx = "[" + specialChars + "]";
+        Pattern p = Pattern.compile(regEx);
+        Matcher m = p.matcher(source);
+        return m.find();
+    }
+
+    public static String getUUID() {
+        return UUID.randomUUID().toString().replace("-", "");
+    }
+
+
+    /**
+     * 右侧补齐String长度
+     *
+     * @param str
+     * @param length
+     * @param sign
+     * @return
+     */
+    public static String completLengthFromRight(String str, int length, String sign) {
+        if (str == null) {
+            str = "";
+        }
+        while (str.length() < length) {
+            str = str + sign;
+        }
+        return str;
+    }
+
+    /**
+     * 随机生成字符串
+     *
+     * @param length 随机字符串长度
+     * @return
+     */
+    public static String randomString(int length) {
+        String baseStr = "abcdefghrgklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ0123456789";
+        StringBuffer buffer = new StringBuffer();
+        Random random = new Random();
+        for (int i = 0; i < length; i++) {
+            int index = random.nextInt(baseStr.length());
+            buffer.append(baseStr.charAt(index));
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * 根据key返回字符串  无值则返回"" 有则返回值
+     *
+     * @param jsObject
+     * @param key
+     * @return
+     */
+    public static String getJSONString(JSONObject jsObject, String key) {
+        String value = jsObject.getString(key);
+        if (StringUtils.isNotEmpty(value)) {
+            return value;
+        }
+        return "";
+    }
+
+    /**
+     * 过滤null null转化为空字符串
+     *
+     * @param str
+     */
+    public static String nullChangeEmptyString(String str) {
+        return str == null ? "" : str;
+    }
+
+    /**
+     * String
+     * 用于修改时  根据传参检查是否为null
+     * 不为null 则赋值  否则不赋值
+     *
+     * @param setObject
+     * @param jsObject
+     * @param key
+     */
+    public static void setUpdateStringVal(JSONObject setObject, JSONObject jsObject, String key) {
+        String value = jsObject.getString(key);
+        if (value != null) {
+            setObject.put(key, value);
+        }
+    }
+
+    /**
+     * Jsonarray
+     * 用于修改时  根据传参检查是否为null
+     * 不为null 则赋值  否则不赋值
+     *
+     * @param setObject
+     * @param jsObject
+     * @param key
+     */
+    public static void setUpdateArrayVal(JSONObject setObject, JSONObject jsObject, String key) {
+        JSONArray value = jsObject.getJSONArray(key);
+        if (value != null) {
+            setObject.put(key, value);
+        }
+    }
+
+    /**
+     * Jsonobject
+     * 用于修改时  根据传参检查是否为null
+     * 不为null 则赋值  否则不赋值
+     *
+     * @param setObject
+     * @param jsObject
+     * @param key
+     */
+    public static void setUpdateObjectVal(JSONObject setObject, JSONObject jsObject, String key) {
+        JSONObject value = jsObject.getJSONObject(key);
+        if (value != null) {
+            setObject.put(key, value);
+        }
+    }
+
+    /**
+     * Is json object boolean.
+     *
+     * @param content the content
+     * @return the boolean
+     */
+    public static boolean isJSONObject(String content) {
+        try {
+            Object jsobj = JSONObject.parse(content);
+            return jsobj instanceof JSONObject;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    /**
+     * Is json array boolean.
+     *
+     * @param content the content
+     * @return the boolean
+     */
+    public static boolean isJSONArray(String content) {
+        try {
+            Object jsobj = JSONObject.parse(content);
+            return jsobj instanceof JSONArray;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    public static <T, R> List<T> tranferContentToDTO(String content, Class<T> t) {
+        if (StringUtils.isNotBlank(content)) {
+            // 把字符串转成json对象
+            return JSONArray.parseArray(content, t);
+        }
+        return new ArrayList<T>();
+    }
+
+
+    public static <T, R> T tranferItemToDTO(String content, Class<T> t) throws Exception {
+        if (StringUtils.isNotBlank(content)) {
+            return JSONObject.parseObject(content, t);
+        }
+        return t.newInstance();
+    }
+
+    public static String getStringValue(Object value, Object defaultValue) {
+        if (value == null) {
+            if (defaultValue != null) {
+                return defaultValue.toString();
+            }
+            return "";
+        }
+        return value.toString();
+    }
+
+    public static String getString(Object obj) {
+        return getString(obj, "");
+    }
+
+    public static String getString(Object obj, String defaultValue) {
+        return obj != null ? obj.toString() : defaultValue;
+    }
+
+    /**
+     * 判断字符串是否是整数
+     */
+    public static boolean isInteger(Object obj) {
+        try {
+            Integer.parseInt(String.valueOf(obj));
+            return true;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
+    /**
+     * 获取整数 ,不是则返回0
+     */
+    public static int getInt(Object obj) {
+        return getInt(obj, 0);
+    }
+
+    public static int getInt(Object obj, int defaultValue) {
+        return (obj != null) && (isInteger(obj)) ? Integer.parseInt(String.valueOf(obj)) : defaultValue;
+    }
+
+    /**
+     * 判断字符串是否是浮点数
+     */
+    public static boolean isLong(Object obj) {
+        try {
+            Long.parseLong(String.valueOf(obj));
+            return true;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
+    /**
+     * 判断字符串是否是数字
+     */
+    public static boolean isNumber(Object obj) {
+        return isInteger(obj) || isDouble(obj) || isLong(obj);
+    }
+
+    /**
+     * 获取长整数 ,不是长整数则返回0
+     */
+    public static long getLong(Object obj) {
+        return getLong(obj, 0L);
+    }
+
+    public static long getLong(Object obj, long defaultValue) {
+        return (obj != null) && (isLong(obj)) ? Long.parseLong(String.valueOf(obj)) : defaultValue;
+    }
+
+    /**
+     * 判断字符串是否是单精度浮点数
+     */
+    public static boolean isFloat(Object obj) {
+        try {
+            Float.parseFloat(String.valueOf(obj));
+            return true;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
+    /**
+     * 获取单精度浮点数
+     */
+    public static float getFloat(Object obj) {
+        return getFloat(obj, 0.0F);
+    }
+
+    public static float getFloat(Object obj, float defaultValue) {
+        return (obj != null) && (isFloat(obj)) ? Float.parseFloat(String.valueOf(obj)) : defaultValue;
+    }
+
+    /**
+     * 判断字符串是否是浮点数
+     */
+    public static boolean isDouble(Object obj) {
+        try {
+            Double.parseDouble(String.valueOf(obj));
+            return true;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
+    /**
+     * 获取双精度浮点数
+     */
+    public static Double getDouble(Object obj) {
+        return getDouble(obj, 0.0D);
+    }
+
+    public static Double getDouble(Object obj, Double defaultValue) {
+        return (obj != null) && (isDouble(obj)) ? Double.valueOf(String.valueOf(obj)) : defaultValue;
+    }
+
+    public static boolean isNotEmpty(Object obj) {
+        return !isEmpty(obj);
+    }
+
+    public static boolean isEmpty(Object obj) {
+        if (obj == null) {
+            return true;
+        }
+        if ((obj instanceof CharSequence)) {
+            return ((CharSequence) obj).length() == 0;
+        }
+        if ((obj instanceof Collection)) {
+            return CollectionUtils.isEmpty((Collection) obj);
+        }
+        if ((obj instanceof Map)) {
+            return (((Map) obj).isEmpty());
+        }
+        if (obj.getClass().isArray()) {
+            return Array.getLength(obj) == 0;
+        }
+        /*
+         * 和上面的判断数组效果一样,但是不会报空指针 if (obj instanceof Object[]) { return
+         * (((Object[])obj).length == 0); }
+         */
+        return false;
+    }
+
+    public static String defaultString(String str) {
+        return defaultString(str, "");
+    }
+
+    public static String defaultString(String str, String defaultStr) {
+        return isEmptyOrNullStr(str) ? defaultStr : str;
+    }
+
+
+    public static boolean isEmptyOrNullStr(String str) {
+        if (str == null) {
+            return true;
+        }
+        return str == null || str.length() == 0 || "null".equalsIgnoreCase(str.trim());
+    }
+
+    /**
+     * 只有true 或者 "true" 返回 true,否则返回false
+     *
+     * @param obj
+     * @return true or false
+     */
+    public static boolean getBoolean(Object obj) {
+        return !isEmpty(obj) && Boolean.valueOf(obj.toString());
+    }
+}

+ 29 - 0
src/main/resources/application.yml

@@ -0,0 +1,29 @@
+server:
+  #需要更改
+  port: 8080
+
+spring:
+  application:
+    name: simulator-collector
+  servlet:
+    multipart:
+      max-file-size: 1024MB
+      max-request-size: 1024MB
+swagger:
+  enable: true
+  title: iot管理后台
+  description: 提供iot服务管理员管理的所有功能
+  version: 2.0
+  base-package: com.persagy
+knife4j:
+  # 开启增强配置
+  enable: true
+  # 开启生产环境屏蔽
+  production: false
+  # 开启Swagger的Basic认证功能,默认是false
+  basic:
+    enable: true
+    # Basic认证用户名
+    username: iot
+    # Basic认证密码
+    password: iot

+ 2 - 2
src/main/resources/config.xml

@@ -5,8 +5,8 @@
 	<mqtt url="tcp://127.0.0.1:61613" clientId="simulator-collector-4403070003" username="admin" password="password" topic_prefix="project" />
 	
 	<PROTOCOL>TCP</PROTOCOL>
-	<ServerIP>192.168.100.102</ServerIP>
-	<ServerPort>30054</ServerPort>
+	<ServerIP>127.0.0.1</ServerIP>
+	<ServerPort>8851</ServerPort>
 	<Mode>mina</Mode>
 	<Max_size>10000</Max_size>
 	<SleepCount>1000</SleepCount>

BIN=BIN
src/main/resources/report.xlsx


BIN=BIN
src/main/resources/set.xlsx