Explorar o código

边缘端报警程序

lgy %!s(int64=4) %!d(string=hai) anos
pai
achega
9e7ead4591
Modificáronse 30 ficheiros con 3265 adicións e 0 borrados
  1. 35 0
      .gitignore
  2. 117 0
      .mvn/wrapper/MavenWrapperDownloader.java
  3. BIN=BIN
      .mvn/wrapper/maven-wrapper.jar
  4. 2 0
      .mvn/wrapper/maven-wrapper.properties
  5. 310 0
      mvnw
  6. 182 0
      mvnw.cmd
  7. 81 0
      pom.xml
  8. 13 0
      src/main/java/com/persagy/ZktProjectAlarmApplication.java
  9. 149 0
      src/main/java/com/persagy/client/GroupNettyClient.java
  10. 174 0
      src/main/java/com/persagy/client/GroupNettyClientHandler.java
  11. 101 0
      src/main/java/com/persagy/client/TerminalClient.java
  12. 67 0
      src/main/java/com/persagy/client/TerminalClientHandler.java
  13. 6 0
      src/main/java/com/persagy/client/dispatcher/OperationResultFuture.java
  14. 23 0
      src/main/java/com/persagy/client/dispatcher/RequestPendingCenter.java
  15. 31 0
      src/main/java/com/persagy/client/dispatcher/ResponseDispatcherHandler.java
  16. 48 0
      src/main/java/com/persagy/controller/TestController.java
  17. 74 0
      src/main/java/com/persagy/entity/CommandResult.java
  18. 52 0
      src/main/java/com/persagy/entity/NettyMessage.java
  19. 36 0
      src/main/java/com/persagy/init/InitRunner.java
  20. 266 0
      src/main/java/com/persagy/utils/DataUtils.java
  21. 281 0
      src/main/java/com/persagy/utils/DateUtils.java
  22. 168 0
      src/main/java/com/persagy/utils/IdWorker.java
  23. 95 0
      src/main/java/com/persagy/utils/ItvJsonUtil.java
  24. 473 0
      src/main/java/com/persagy/utils/JSONUtil.java
  25. 77 0
      src/main/java/com/persagy/utils/NetworkUtil.java
  26. 293 0
      src/main/java/com/persagy/utils/StringUtil.java
  27. 4 0
      src/main/resources/application.properties
  28. 6 0
      src/main/resources/application.yml
  29. 88 0
      src/main/resources/logback-spring.xml
  30. 13 0
      src/test/java/com/persagy/zktprojectalarm/ZktProjectAlarmApplicationTests.java

+ 35 - 0
.gitignore

@@ -0,0 +1,35 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
+
+### rebel ###
+**/src/main/resources/rebel.xml
+**/src/main/resources/rebel-remote.xml

+ 117 - 0
.mvn/wrapper/MavenWrapperDownloader.java

@@ -0,0 +1,117 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+    private static final String WRAPPER_VERSION = "0.5.6";
+    /**
+     * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+     */
+    private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+        + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+    /**
+     * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+     * use instead of the default one.
+     */
+    private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+            ".mvn/wrapper/maven-wrapper.properties";
+
+    /**
+     * Path where the maven-wrapper.jar will be saved to.
+     */
+    private static final String MAVEN_WRAPPER_JAR_PATH =
+            ".mvn/wrapper/maven-wrapper.jar";
+
+    /**
+     * Name of the property which should be used to override the default download url for the wrapper.
+     */
+    private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+    public static void main(String args[]) {
+        System.out.println("- Downloader started");
+        File baseDirectory = new File(args[0]);
+        System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+        // If the maven-wrapper.properties exists, read it and check if it contains a custom
+        // wrapperUrl parameter.
+        File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+        String url = DEFAULT_DOWNLOAD_URL;
+        if(mavenWrapperPropertyFile.exists()) {
+            FileInputStream mavenWrapperPropertyFileInputStream = null;
+            try {
+                mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+                Properties mavenWrapperProperties = new Properties();
+                mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+                url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+            } catch (IOException e) {
+                System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+            } finally {
+                try {
+                    if(mavenWrapperPropertyFileInputStream != null) {
+                        mavenWrapperPropertyFileInputStream.close();
+                    }
+                } catch (IOException e) {
+                    // Ignore ...
+                }
+            }
+        }
+        System.out.println("- Downloading from: " + url);
+
+        File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+        if(!outputFile.getParentFile().exists()) {
+            if(!outputFile.getParentFile().mkdirs()) {
+                System.out.println(
+                        "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+            }
+        }
+        System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+        try {
+            downloadFileFromURL(url, outputFile);
+            System.out.println("Done");
+            System.exit(0);
+        } catch (Throwable e) {
+            System.out.println("- Error downloading");
+            e.printStackTrace();
+            System.exit(1);
+        }
+    }
+
+    private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+        if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+            String username = System.getenv("MVNW_USERNAME");
+            char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+            Authenticator.setDefault(new Authenticator() {
+                @Override
+                protected PasswordAuthentication getPasswordAuthentication() {
+                    return new PasswordAuthentication(username, password);
+                }
+            });
+        }
+        URL website = new URL(urlString);
+        ReadableByteChannel rbc;
+        rbc = Channels.newChannel(website.openStream());
+        FileOutputStream fos = new FileOutputStream(destination);
+        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+        fos.close();
+        rbc.close();
+    }
+
+}

BIN=BIN
.mvn/wrapper/maven-wrapper.jar


+ 2 - 0
.mvn/wrapper/maven-wrapper.properties

@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar

+ 310 - 0
mvnw

@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        export JAVA_HOME="`/usr/libexec/java_home`"
+      else
+        export JAVA_HOME="/Library/Java/Home"
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+      PRG="$link"
+    else
+      PRG="`dirname "$PRG"`/$link"
+    fi
+  done
+
+  saveddir=`pwd`
+
+  M2_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  M2_HOME=`cd "$M2_HOME" && pwd`
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --unix "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="`(cd "$M2_HOME"; pwd)`"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`which java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    if [ -n "$MVNW_REPOURL" ]; then
+      jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+    else
+      jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+    fi
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $jarUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+    if $cygwin; then
+      wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+    fi
+
+    if command -v wget > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            wget "$jarUrl" -O "$wrapperJarPath"
+        else
+            wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+        fi
+    elif command -v curl > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            curl -o "$wrapperJarPath" "$jarUrl" -f
+        else
+            curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+        fi
+
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        # For Cygwin, switch paths to Windows format before running javac
+        if $cygwin; then
+          javaClass=`cygpath --path --windows "$javaClass"`
+        fi
+        if [ -e "$javaClass" ]; then
+            if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaClass")
+            fi
+            if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --path --windows "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

+ 182 - 0
mvnw.cmd

@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %DOWNLOAD_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%

+ 81 - 0
pom.xml

@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://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.2.2.RELEASE</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.persagy</groupId>
+    <artifactId>zkt-project-alarm</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>zkt-project-alarm</name>
+    <description>project alarm program</description>
+
+    <properties>
+        <java.version>1.8</java.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <!-- swagger -->
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-spring-boot-starter</artifactId>
+            <!--在引用时请在maven中央仓库搜索最新版本号 -->
+            <version>2.0.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>20.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.persagy.zkt</groupId>
+            <artifactId>zkt-brain</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.junit.vintage</groupId>
+                    <artifactId>junit-vintage-engine</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 13 - 0
src/main/java/com/persagy/ZktProjectAlarmApplication.java

@@ -0,0 +1,13 @@
+package com.persagy.zktprojectalarm;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class ZktProjectAlarmApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(ZktProjectAlarmApplication.class, args);
+    }
+
+}

+ 149 - 0
src/main/java/com/persagy/client/GroupNettyClient.java

@@ -0,0 +1,149 @@
+package com.persagy.commons.netty.client;
+
+import com.persagy.service.CommandService;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldPrepender;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author lqshi
+ * @ClassName: NettyClient
+ * @Description: netty启动、发送消息等
+ * @date 2020831日 上午10:34:45
+ */
+@Service
+public class GroupNettyClient {
+    @Autowired
+    CommandService commandService;
+
+    static Bootstrap groupBootstrap = new Bootstrap();
+    static Channel channelGroup;
+//    static Bootstrap terminalBootstrap = new Bootstrap();
+//    static final String HOST = System.getProperty("host", "127.0.0.1");
+    static final String HOST = System.getProperty("host", "192.168.3.4");
+    static final int PORT = Integer.parseInt(System.getProperty("port", "8081"));
+
+//    static final String TERMINAL_HOST = System.getProperty("host", "192.168.3.6");
+//    static final String TERMINAL_HOST = System.getProperty("host", "192.168.3.33");
+//    static final int TERMINAL_PORT = Integer.parseInt(System.getProperty("port", "8082"));
+//
+    //会话对象组
+    public static Map<String, Channel> channelsMap = new ConcurrentHashMap<>();
+
+    /**
+     * @Title: start
+     * @Description: 启动netty服务端
+     */
+    public void start() {
+        try {
+            // 启动辅助对象类
+            groupBootstrap.group(new NioEventLoopGroup())
+                    // 建立通道
+                    .channel(NioSocketChannel.class)
+                    // 初始化通道及进行配置
+                    .handler(new ChannelInitializer<SocketChannel>() {
+                        @Override
+                        protected void initChannel(SocketChannel ch) throws Exception {
+                            ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
+                            ch.pipeline().addLast(new LengthFieldPrepender(4));
+                            // 将分隔之后的字节数据转换为字符串
+                            ch.pipeline().addLast(new StringDecoder());
+                            ch.pipeline().addLast(new StringEncoder());
+                            // pipeline可以理解为所有handler的初始化容器
+                            ch.pipeline().addLast(new GroupNettyClientHandler(commandService));// 添加自定义handler
+                        }
+                    });
+
+//            terminalBootstrap.group(new NioEventLoopGroup())
+//                    // 建立通道
+//                    .channel(NioSocketChannel.class)
+//                    // 初始化通道及进行配置
+//                    .handler(new ChannelInitializer<SocketChannel>() {
+//                        @Override
+//                        protected void initChannel(SocketChannel ch) throws Exception {
+//                            // 将分隔之后的字节数据转换为字符串
+////                            ch.pipeline().addLast(new StringDecoder());
+////                            ch.pipeline().addLast(new StringEncoder());
+//                            // pipeline可以理解为所有handler的初始化容器
+//                            ch.pipeline().addLast(new NettyClientHandler(commandService));// 添加自定义handler
+//                        }
+//                    });
+            // 代表I/O操作的异步结果
+            // 连接远程节点,等待连接完成
+            // channel = b.connect().sync();//
+            // sync()代表同步等待连接,然后在f.channel().closeFuture().sync();,如果不加就不会等待,直接关闭
+
+                channelGroup = groupBootstrap.connect(HOST, PORT).channel();
+                channelsMap.put("group",channelGroup);
+
+//                Channel channelterminal = terminalBootstrap.connect(TERMINAL_HOST, TERMINAL_PORT).channel();
+//                channelsMap.put("terminal",channelterminal);
+
+
+          /*  System.out.println("-----客户端closeFuture前----");
+            channelFuture.channel().closeFuture().sync();
+            System.out.println("-----客户端closeFuture后----");*/
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            // 关闭主线程组
+            //回收group后下面的自动重连connect()无法直接使用
+            //group.shutdownGracefully();
+
+        }
+    }
+
+    static void connect(Channel channel) {
+        // 加入断线后自动重连监听器
+        System.out.println("try connect!");
+        for (Map.Entry<String, Channel> entry:channelsMap.entrySet()) {
+            if(entry.getValue() == channel && !channel.isActive()){
+                channelGroup = groupBootstrap.connect(HOST, PORT).addListener(new ChannelFutureListener() {
+                    @Override
+                    public void operationComplete(ChannelFuture future) throws Exception {
+                        if (future.cause() != null) {
+                            System.out.println("Failed to connect: " + future.cause());
+                        }
+                    }
+                }).channel();
+                channelsMap.put(entry.getKey(),channelGroup);
+            }
+        }
+    }
+
+    /**
+     * @param msg
+     * @Title: sendMessage
+     * @Description: 发送消息
+     */
+/*    public void sendMessage(Object msg,String type) {
+        for (Map.Entry<String, Channel> ch:channelsMap.entrySet() ) {
+            if(type.equals(ch.getKey())) {
+                System.out.println(msg);
+                System.out.println(ch.getKey());
+                ch.getValue().writeAndFlush(msg);
+            }else {
+                System.out.println("----to group --");
+                ch.getValue().writeAndFlush(msg);
+            }
+        }
+    }*/
+    public void sendMessage(Object msg) {
+        channelGroup.writeAndFlush(msg);
+    }
+
+}

+ 174 - 0
src/main/java/com/persagy/client/GroupNettyClientHandler.java

@@ -0,0 +1,174 @@
+package com.persagy.commons.netty.client;
+
+import cn.hutool.core.date.DateField;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import com.persagy.entity.CommandResult;
+import com.persagy.entity.NettyMessage;
+import com.persagy.service.CommandService;
+import com.persagy.utils.DateUtils;
+import com.persagy.utils.StringUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.quartz.JobDataMap;
+import org.quartz.SchedulerException;
+import org.springframework.util.CollectionUtils;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @ClassName: EchoClientHandler
+ * @Description: 客户端处理类
+ * @author lqshi
+ * @date 2020829日 下午8:31:59
+ */
+// 注意:SimpleChannelInboundHandler<ByteBuf>的<>中是什么,channelRead0第二参数是什么
+@Slf4j
+public class GroupNettyClientHandler extends ChannelInboundHandlerAdapter {
+    // Sleep 5 seconds before a reconnection attempt.
+    static final int RECONNECT_DELAY = Integer.parseInt(System.getProperty("reconnectDelay", "5"));
+    // Reconnect when the server sends nothing for 10 seconds.
+    private static final int READ_TIMEOUT = Integer.parseInt(System.getProperty("readTimeout", "10"));
+
+   private CommandService commandService;
+
+    public GroupNettyClientHandler(CommandService commandService) {
+        this.commandService = commandService;
+    }
+
+    public GroupNettyClientHandler() {
+    }
+
+    @Override
+    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
+
+        if (!(evt instanceof IdleStateEvent)) {
+            return;
+        }
+        IdleStateEvent e = (IdleStateEvent) evt;
+        if (e.state() == IdleState.READER_IDLE) {
+            System.out.println("no inbound traffic");
+            // The connection was OK but there was no traffic for last period.
+            // 长时间不操作的时候自动关闭连接; ctx.close();
+        }
+    }
+	 /**
+     * 在到服务器的连接已经建立之后将被调用
+     * @param ctx
+     * @throws Exception
+     */
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        System.out.println("Connected to: " + ctx.channel().remoteAddress());
+        //启动的时候发送消息
+       NettyMessage msg = new NettyMessage();
+        msg.setOpCode(3);
+        ctx.channel().writeAndFlush(msg.toString());
+    }
+    /**
+     * 当从服务器接收到一个消息时被调用
+     * @param ctx
+     * @param msg
+     * @throws Exception
+     */
+    @Override
+    public  void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+        System.out.println("Client received: "+ msg);
+        try {
+            handlerMsg(ctx, msg);
+        } catch (Exception e) {
+            log.error("channelRead",e);
+        }
+    }
+
+    private void handlerMsg(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception {
+        if(StringUtil.isJSONObject((String) msg)) {
+            NettyMessage message = StringUtil.tranferItemToDTO((String) msg, NettyMessage.class);
+            NettyMessage response = new NettyMessage();
+            response.setStreamId(message.getStreamId());
+            response.setSuccess(true);
+            response.setOpCode(2);
+            List<CommandResult> responseContent = new ArrayList<>();
+            List<CommandResult> content = message.getContent();
+            //删除这个时间点之后的所有工作内容
+            String timeFlag = message.getClearBeforeTimeFlag();
+            if(! CollectionUtils.isEmpty(content)){
+                if(StringUtils.isNotBlank(timeFlag)) {
+                    LocalDateTime timeFlagDateTime = DateUtils.parse(timeFlag);
+                    Date endDate = DateUtils.localDateTime2Date(LocalDateTime.now().minusHours(48));
+                    List<DateTime> dateTimes = DateUtil.rangeToList(new Date(), endDate, DateField.HOUR);
+                    for (DateTime dateTime : dateTimes) {
+                        System.out.println(dateTime);
+                    }
+                }
+                for (CommandResult command:content){
+                    CommandResult tmpNewcommand = new CommandResult();
+                    tmpNewcommand.setId(command.getId());
+                    tmpNewcommand.setCommandResult(0);
+                    responseContent.add(tmpNewcommand);
+                    LocalDateTime commandTime = DateUtils.parse(command.getCommandTime(), DateUtils.date_format_show_minute);
+                    Date startTime = DateUtils.localDateTime2Date(commandTime);
+                    String hour = DateUtils.format(commandTime, DateUtils.sdfHour);
+                    String jobName = command.getFuncId()+"_"+command.getMeterId()+DateUtils.format(commandTime);
+                    JobDataMap jobDataMap = new JobDataMap();
+                    jobDataMap.put("commandResult",command.toString());
+                    try {
+                        commandService.addCommand(startTime,jobName,hour,jobDataMap);
+                    } catch (SchedulerException e) {
+                        log.error("addCommand error: ",e);
+                    }
+
+                }
+            }
+            response.setContent(responseContent);
+            System.out.println(message.toString());
+            channelHandlerContext.write(response.toString());
+        }
+    }
+
+    @Override
+    public void channelReadComplete(ChannelHandlerContext ctx) {
+        ctx.flush();
+    }
+
+    /**
+     * 在处理过程中引发异常时被调用
+     * @param ctx
+     * @param cause
+     * @throws Exception
+     */
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        cause.printStackTrace();
+        ctx.close();
+    }
+    
+    /**
+     * 通道处于非活跃状态动作,该方法只会在失效时调用一次
+     */
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+    	//客户端自己不正常情况下自己在重连一次
+        System.out.println("Disconnected from: " + ctx.channel().remoteAddress());
+
+    }
+    @Override
+    public void channelUnregistered(final ChannelHandlerContext ctx) throws Exception {
+        System.out.println("Sleeping for: " + RECONNECT_DELAY + "s,Reconnecting to: " + GroupNettyClient.HOST + ':' + GroupNettyClient.PORT);
+        ctx.channel().eventLoop().schedule(new Runnable() {
+            @Override
+            public void run() {
+                System.out.println("Reconnecting to: " + GroupNettyClient.HOST + ':' + GroupNettyClient.PORT);
+                GroupNettyClient.connect(ctx.channel());
+            }
+        }, RECONNECT_DELAY, TimeUnit.SECONDS);
+    }
+}

+ 101 - 0
src/main/java/com/persagy/client/TerminalClient.java

@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.persagy.commons.netty.client;
+
+import com.persagy.commons.netty.client.dispatcher.OperationResultFuture;
+import com.persagy.commons.netty.client.dispatcher.RequestPendingCenter;
+import com.persagy.commons.netty.client.dispatcher.ResponseDispatcherHandler;
+import com.persagy.qotm.TerminalServer;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.DatagramPacket;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import io.netty.util.CharsetUtil;
+import io.netty.util.internal.SocketUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * A UDP broadcast client that asks for a quote of the moment (QOTM) to {@link TerminalServer}.
+ *
+ * Inspired by <a href="http://docs.oracle.com/javase/tutorial/networking/datagrams/clientServer.html">the official
+ * Java tutorial</a>.
+ */
+@Service
+public final class TerminalClient {
+
+    @Autowired
+    GroupNettyClient groupNettyClient;
+    static final String TERMINAL_HOST = System.getProperty("host", "192.168.3.6");
+//    static final String TERMINAL_HOST = System.getProperty("host", "192.168.3.33");
+    static final int TERMINAL_PORT = Integer.parseInt(System.getProperty("port", "8082"));
+    Channel ch;
+    RequestPendingCenter requestPendingCenter;
+    public void start() throws Exception {
+
+        EventLoopGroup group = new NioEventLoopGroup();
+        try {
+            Bootstrap b = new Bootstrap();
+            requestPendingCenter = new RequestPendingCenter();
+            b.group(group)
+             .channel(NioDatagramChannel.class)
+             .option(ChannelOption.SO_BROADCAST, true)
+             .handler(new ChannelInitializer<NioDatagramChannel>() {
+                 @Override
+                protected void initChannel(NioDatagramChannel ch)throws Exception {
+                    ChannelPipeline pipeline = ch.pipeline();
+                     //	    pipeline.addLast(new StringDecoder(ASCII))
+                     //      .addLast(new StringEncoder(ASCII))
+                     pipeline.addLast(new ResponseDispatcherHandler(requestPendingCenter));
+                     pipeline.addLast(new TerminalClientHandler(groupNettyClient));
+                 }});
+
+                ch = b.bind(0).sync().channel();
+            sendMessage("echo !");
+            ch.closeFuture().await();
+        } finally {
+//            group.shutdownGracefully();
+        }
+    }
+    /**
+     * @description:TODO
+     * @exception:
+     * @author: LuoGuangyi
+     * @company: Persagy Technology Co.,Ltd
+     * @param msg: 例如:"1101050029;1;pointset;20200919180200;706;ACATAH_1_EquipSwitchSet;903;1"
+     * @return: void
+     * @since: 2020/09/20 15:30
+     * @version: V1.0
+     */
+    public void sendMessage(String msg) throws Exception {
+        //1101050029;1;pointset;20200919180200;706;ACATAH_1_EquipSwitchSet;903;1
+        String[] split = msg.split(";");
+        if(split.length>=8) {
+            OperationResultFuture operationResultFuture = new OperationResultFuture();
+            requestPendingCenter.add(split[4], operationResultFuture);
+            ch.writeAndFlush(new DatagramPacket(
+                    Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8),
+                    SocketUtils.socketAddress(TERMINAL_HOST, TERMINAL_PORT))).sync();
+//            String operationResult = operationResultFuture.get(3, TimeUnit.SECONDS);
+//            System.out.println("结果为:"+operationResult);
+        }
+
+        }
+    
+
+}

+ 67 - 0
src/main/java/com/persagy/client/TerminalClientHandler.java

@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.persagy.commons.netty.client;
+
+import com.persagy.entity.CommandResult;
+import com.persagy.entity.NettyMessage;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.socket.DatagramPacket;
+import io.netty.util.CharsetUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Collections;
+@Slf4j
+public class TerminalClientHandler extends SimpleChannelInboundHandler<DatagramPacket> {
+
+    private GroupNettyClient groupNettyClient;
+
+    public TerminalClientHandler(GroupNettyClient groupNettyClient) {
+        this.groupNettyClient = groupNettyClient;
+    }
+
+    @Override
+    public void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
+        try {
+            handlerMessage(msg);
+        } catch (Exception e) {
+           log.error("handlerMessage error:",e);
+        }
+    }
+
+    private void handlerMessage(DatagramPacket msg) {
+        String response = msg.content().toString(CharsetUtil.UTF_8);
+        String[] split = response.split(";");
+
+        System.out.println(response);
+        CommandResult command = new CommandResult();
+        if(StringUtils.isNotBlank(split[9]) && split[9].contains("success")){
+            command.setCommandResult(2);
+        }
+        command.setId(split[4]);
+        NettyMessage message = new NettyMessage();
+        message.setContent(Collections.singletonList(command));
+        message.setOpCode(2);
+        groupNettyClient.sendMessage(message.toString());
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        cause.printStackTrace();
+        ctx.close();
+    }
+}

+ 6 - 0
src/main/java/com/persagy/client/dispatcher/OperationResultFuture.java

@@ -0,0 +1,6 @@
+package com.persagy.commons.netty.client.dispatcher;
+
+import io.netty.util.concurrent.DefaultPromise;
+
+public class OperationResultFuture extends DefaultPromise<String> {
+}

+ 23 - 0
src/main/java/com/persagy/client/dispatcher/RequestPendingCenter.java

@@ -0,0 +1,23 @@
+package com.persagy.commons.netty.client.dispatcher;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class RequestPendingCenter {
+
+    private Map<String, OperationResultFuture> map = new ConcurrentHashMap<>();
+
+    public void add(String streamId, OperationResultFuture future) {
+        this.map.put(streamId, future);
+    }
+
+    public void set(String streamId, String nettyMessage) {
+        OperationResultFuture operationResultFuture = this.map.get(streamId);
+        if (operationResultFuture != null) {
+            operationResultFuture.setSuccess(nettyMessage);
+            //this.map.remove(streamId);
+        }
+    }
+
+
+}

+ 31 - 0
src/main/java/com/persagy/client/dispatcher/ResponseDispatcherHandler.java

@@ -0,0 +1,31 @@
+package com.persagy.commons.netty.client.dispatcher;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.SimpleChannelInboundHandler;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ResponseDispatcherHandler extends ChannelInboundHandlerAdapter {
+
+    private RequestPendingCenter requestPendingCenter;
+
+    public ResponseDispatcherHandler(RequestPendingCenter requestPendingCenter) {
+        this.requestPendingCenter = requestPendingCenter;
+    }
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object responseMessage) throws Exception {
+        //1101050029;1;pointsetack;20200921112016;780;ACATAH_2_EquipSwitchSet;903;0.0;20200921112020;success
+        String[] split = responseMessage.toString().split(";");
+        if(split.length == 9 && "pointsetack".equals(split[2])) {
+            requestPendingCenter.set(split[4], responseMessage.toString());
+        }
+        ctx.fireChannelRead(responseMessage);
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+       log.error("ResponseDispatcherHandler error!",cause);
+    }
+}

+ 48 - 0
src/main/java/com/persagy/controller/TestController.java

@@ -0,0 +1,48 @@
+package com.persagy.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.persagy.commons.netty.client.GroupNettyClient;
+import com.persagy.commons.netty.client.TerminalClient;
+import com.persagy.service.SimpleSchedule;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class TestController {
+
+	
+	@Autowired
+	private GroupNettyClient groupNettyClient;
+	@Autowired
+	SimpleSchedule simpleSchedule;
+	@Autowired
+	private TerminalClient terminalClient;
+
+	@RequestMapping("/test1")
+	public String test1 () throws SchedulerException {
+		for (int i = 0; i < 10; i++) {
+			simpleSchedule.init();
+		}
+		return "su";
+	}
+	
+	@RequestMapping("/test2")
+	public String test2 (@RequestBody JSONObject msg) {
+//		JSONObject a = new JSONObject();
+//		a.put("type","request");
+//		a.put("function","ddd");
+
+		groupNettyClient.sendMessage(msg.getString("msg"));
+		return null;
+	}
+
+	@RequestMapping("/test3")
+	public String test3 (@RequestBody JSONObject msg) throws Exception {
+		terminalClient.sendMessage(msg.getString("msg"));
+		return "success";
+	}
+
+}

+ 74 - 0
src/main/java/com/persagy/entity/CommandResult.java

@@ -0,0 +1,74 @@
+package com.persagy.entity;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 要下发的定时指令、手动指令及结果(command_result)实体类
+ *
+ * @author lixing
+ * @since 2020-09-10 15:54:33
+ * @description 由 Mybatisplus Code Generator 创建
+ */
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+public class CommandResult implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+	private String id;
+    /**
+     * projectId
+     */
+    private String projectId;
+    /**
+     * 功能号
+     */
+    private String funcId;
+    /**
+     * 表号id
+     */
+    private String meterId;
+    /**
+     * 执行指令的时间,手动时为空
+     */
+    private String commandTime;
+    /**
+     * 指令执行结果,0-成功;1-失败;2-超时不响应
+     */
+    private Integer commandResult;
+    /**
+     * createTime
+     */
+    private Date createTime;
+    /**
+     * updateTime
+     */
+	private Date updateTime;
+    /**
+     * 扩展字段
+     */
+    private String remark;
+    /**
+     * 信息点要执行的指令
+     */
+    private String pointAction;
+    /**
+     * 对象id
+     */
+    private String objectId;
+
+    @Override
+    public String toString() {
+        return JSONObject.toJSONString(this);
+    }
+
+}

+ 52 - 0
src/main/java/com/persagy/entity/NettyMessage.java

@@ -0,0 +1,52 @@
+package com.persagy.entity;
+
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class NettyMessage {
+    /*
+    唯一标识
+     */
+    @JSONField()
+    private long streamId;
+    @JSONField()
+    private int version = 1;
+    /*
+    操作类型编码  1-请求  2 -响应  3 通知
+     */
+    @JSONField()
+    private int opCode;
+    /*
+    请求来源
+     */
+    @JSONField()
+    private String source = "project";
+
+    /*
+     * 用于项目控制程序清除之前的时间和命令
+     */
+    @JSONField()
+    private String clearBeforeTimeFlag;
+    /*
+    传输内容
+     */
+    @JSONField(jsonDirect=true)
+    private List<CommandResult> content;
+    /*
+    成功标识
+     */
+    @JSONField()
+    private Boolean success;
+    @Override
+    public String toString() {
+        return JSONObject.toJSONString(this);
+    }
+}

+ 36 - 0
src/main/java/com/persagy/init/InitRunner.java

@@ -0,0 +1,36 @@
+package com.persagy.init;
+
+import com.persagy.commons.netty.client.GroupNettyClient;
+import com.persagy.commons.netty.client.TerminalClient;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+
+@Service
+@Order(1)
+@Slf4j
+public class InitRunner implements CommandLineRunner {
+
+
+	@Autowired
+	private GroupNettyClient groupNettyClient;
+
+	@Autowired
+	private TerminalClient terminalClient;
+	@Autowired
+	@Qualifier("quartzScheduler")
+	Scheduler quartzScheduler;
+
+
+	@Override
+	public void run(String... args) throws Exception {
+		groupNettyClient.start();
+		terminalClient.start();
+	}
+}

+ 266 - 0
src/main/java/com/persagy/utils/DataUtils.java

@@ -0,0 +1,266 @@
+package com.persagy.utils;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.util.CollectionUtils;
+
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 数值、集合处理工具类
+ *
+ * @author feng
+ */
+public class DataUtils {
+
+    /**
+     * 对象转Double类型
+     *
+     * @param object
+     * @return
+     */
+    public static Double parseDouble(Object object) {
+        if (object == null) {
+            return null;
+        }
+        if (object instanceof String) {
+            return Double.valueOf(object.toString());
+        }
+        if (object instanceof Integer) {
+            return ((Integer) object).doubleValue();
+        }
+        if (object instanceof Long) {
+            return ((Long) object).doubleValue();
+        }
+        if (object instanceof Double) {
+            return (Double) object;
+        }
+        return (Double) object;
+    }
+
+    /**
+     * 设置小数精度
+     *
+     * @param obj
+     * @param count 精确的小数位数
+     * @return
+     */
+    public static Double setDecimalScale(Double obj, int count) {
+        if (obj == null) {
+            return null;
+        }
+        BigDecimal bd = new BigDecimal(obj);
+        return bd.setScale(count, BigDecimal.ROUND_HALF_UP).doubleValue();
+    }
+
+    /**
+     * 将Double类型的数字向下取整
+     *
+     * @param num
+     * @return
+     */
+    public static String getInt(Object num) {
+        if (num == null) {
+            return null;
+        }
+        try {
+            Double dob = Double.valueOf(num + "");
+            DecimalFormat df = new DecimalFormat("#.#");
+            return df.format(dob);
+        } catch (Exception e) {
+            return num + "";
+        }
+    }
+
+    /**
+     * BigDecimal精确计算
+     *
+     * @param dividend
+     * @param divisor
+     * @param decimalScale 小数点精确范围
+     * @return
+     * @throws Exception
+     */
+    public static <T extends Number> Double divide(T dividend, T divisor, Integer decimalScale) throws Exception {
+        if (dividend == null || divisor == null) {
+            return null;
+        }
+        if (divisor.doubleValue() == 0) {
+            return null;
+        }
+        if (decimalScale == null) {
+            decimalScale = DECIMAL_SCALE;
+        }
+        BigDecimal dividendDecimal = new BigDecimal(dividend.toString());
+        BigDecimal divisorDecimal = new BigDecimal(divisor.toString());
+        return dividendDecimal.divide(divisorDecimal, decimalScale, BigDecimal.ROUND_HALF_UP).doubleValue();
+    }
+
+    private static final int DECIMAL_SCALE = 12;
+    public static <T extends Number> Double divide(T dividend, T divisor) throws Exception {
+        return divide(dividend,divisor,DECIMAL_SCALE);
+    }
+
+    public static Double subtract(Double minuend, Double subtrahend) {
+        if (minuend == null && subtrahend == null) {
+            return null;
+        }
+        if (minuend == null) {
+            return -subtrahend;
+        }
+        if (subtrahend == null) {
+            return minuend;
+        }
+        return minuend - subtrahend;
+    }
+
+    public static Double plus(Double data1, Double data2) {
+        if (data1 == null && data2 == null) {
+            return null;
+        }
+        if (data1 == null) {
+            return data2;
+        }
+        if (data2 == null) {
+            return data1;
+        }
+        return data1 + data2;
+    }
+
+    public static Double plus(Double... dataList) {
+        Double sum = null;
+        for (Double data : dataList) {
+            sum = plus(sum, data);
+        }
+        return sum;
+    }
+
+    public static Integer getInt(Double data) {
+        if (data == null) {
+            return null;
+        }
+        return (int) (data + 0.5);
+    }
+
+    public static Double formatDataNumber(Double energyData) {
+        if (energyData == null) {
+            return null;
+        }
+        if (energyData < 1) {
+            return DataUtils.setDecimalScale(energyData, 3);
+        } else if (energyData < 1000) {
+            return DataUtils.setDecimalScale(energyData, 1);
+        } else {
+            return Math.round(energyData) * 1.0;
+        }
+
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+	public static void sortList(List srcList, String sortField, boolean isAsc) throws Exception{
+        if(CollectionUtils.isEmpty(srcList)){
+            return;
+        }
+        Collections.sort(srcList, (o1, o2) -> {
+            JSONObject object1 = JSONObject.parseObject(JSONObject.toJSONString(o1));
+            JSONObject object2 = JSONObject.parseObject(JSONObject.toJSONString(o2));
+            Object name1 = object1.get(sortField);
+            Object name2 = object2.get(sortField);
+            int coefficient;
+            if (isAsc) {
+                coefficient = 1;
+            } else {
+                coefficient = -1;
+            }
+            if (name1 != null && name2 != null) {
+                if (name1 instanceof Double) {
+                    return ((Double) name1).compareTo((Double) name2) * coefficient;
+                } else if (name1 instanceof Integer) {
+                    return ((Integer) name1).compareTo((Integer) name2) * coefficient;
+                } else if (name1 instanceof BigDecimal) {
+                    return ((BigDecimal) name1).compareTo((BigDecimal) name2) * coefficient;
+                } else {
+                    return ((String) name1).compareTo(((String) name2)) * coefficient;
+                }
+            } else if (name1 != null) {
+                return coefficient;
+            } else if (name2 != null) {
+                return -coefficient;
+            } else {
+                return 0;
+            }
+
+        });
+    }
+
+
+
+    public static void sortList(List<Double> energyList) {
+        Collections.sort(energyList, (o1, o2) -> {
+            if (o1 == null && o2 == null) {
+                return 0;
+            } else if (o1 == null) {
+                return -1;
+            } else if (o2 == null) {
+                return 1;
+            } else {
+                return o1.compareTo(o2);
+            }
+        });
+    }
+
+    public static List<String> getPagingObjectList(List<String> allObjectList, Integer pageNum, Integer pageSize) {
+        if (CollectionUtils.isEmpty(allObjectList)) {
+            return new ArrayList<>();
+        }
+
+        int skipCount = (pageNum - 1) * pageSize;
+        int totalCount = allObjectList.size();
+        if (skipCount >= totalCount) {
+            return new ArrayList<>();
+        }
+        List<String> resultList = new ArrayList<>();
+        for (int i = 0; i < pageSize; i++) {
+            int index = skipCount + i;
+            if (index >= totalCount) {
+                break;
+            }
+            resultList.add(allObjectList.get(index));
+        }
+        return resultList;
+    }
+
+    /**
+     * description 筛选originMapList中包含keyWord的对象
+     * @param originMapList
+     * @param keyWord
+     * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
+     * @author feng
+     * @since 19:47 2020/6/4
+     * @version 1.0
+     */
+    public static List<Map<String, Object>> queryByKeyWord(List<Map<String, Object>> originMapList, String keyWord) {
+        if (StringUtils.isBlank(keyWord)) {
+            return originMapList;
+        }
+        if (CollectionUtils.isEmpty(originMapList)) {
+            return new ArrayList<>();
+        }
+        List<Map<String, Object>> resultMapList = new ArrayList<>();
+        for (Map<String, Object> originMap : originMapList) {
+            for (String key : originMap.keySet()) {
+                Object data = originMap.get(key);
+                if (data != null && data.toString().contains(keyWord)) {
+                    resultMapList.add(originMap);
+                    break;
+                }
+            }
+        }
+        return resultMapList;
+    }
+}

+ 281 - 0
src/main/java/com/persagy/utils/DateUtils.java

@@ -0,0 +1,281 @@
+package com.persagy.utils;
+
+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.Date;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+/**
+ * 
+ * Description: 基于Java8的时间工具类 Company: Persagy
+ * 
+ * @author luoguangyi
+ * @version 1.0
+ * @since: 201985日: 下午4:44:21 Update By luoguangyi 201985日: 下午4:44:21
+ */
+public class DateUtils {
+
+	private final static DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+	/**
+	 * 东八区时区偏移量
+	 */
+	private final static int ASIA_SHANGHAI = 8;
+	public static final String sdfDay = "yyyyMMdd";
+	public static final String sdfMonth = "yyyyMM";
+	public static final String sdfTime = "yyyyMMddHHmmss";
+	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";
+
+	/**
+	 * 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 201985日: 下午5:25:27 Update By luoguangyi 201985日: 下午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();
+	}
+
+	// 获取指定日期的秒
+	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));
+	}
+
+	/**
+	 * 解析字符串日期为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);
+	}
+
+	// 日期加上一个数,根据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 比如:668点到867点: 2 1 比如:668点到869点: 2 2 比如:65号到86号: 2 2
+	 * 比如:67号到86号: 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);
+	}
+
+	/**
+	 * 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 getPlusHour(String dateTime, long hour) {
+		LocalDateTime parse = parse(dateTime).plusHours(hour);
+		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();
+	}
+
+	/**
+	 * 获取月份差 不算满月的月份差(比如731号到81号仍然算一个月份差)
+	 */
+	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 2019109日: 下午2:29:23 Update By luoguangyi 2019109日: 下午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);
+	}
+
+	@SuppressWarnings("deprecation")
+	public static void main(String[] args) {
+		LocalDateTime parse1 = DateUtils.parse("20150612152611");
+		LocalDateTime parse2 = DateUtils.parse("20150613152610");
+		System.out.println(betweenTwoTime(parse1, parse2, ChronoUnit.HOURS));
+		test1();
+
+	}
+
+	private static void test1() {
+	}
+}

+ 168 - 0
src/main/java/com/persagy/utils/IdWorker.java

@@ -0,0 +1,168 @@
+package com.persagy.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.lang.management.ManagementFactory;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+
+/**
+ * <p>名称:IdWorker.java</p>
+ * <p>描述:分布式自增长ID</p>
+ * <pre>
+ *     Twitter的 Snowflake JAVA实现方案
+ * </pre>
+ * 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用:
+ * 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000
+ * 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,
+ * 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),
+ * 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。
+ * 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),
+ * 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
+ * <p>
+ * 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加))
+ *
+ */
+@Component
+@Slf4j
+public class IdWorker {
+
+    // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)初始值为2019-09-04 15:50.可以使用69年
+    private final static long twepoch = 1586258613087L;
+    // 机器标识位数
+    private final static long workerIdBits = 4L;
+    // 数据中心标识位数
+    private final static long datacenterIdBits = 4L;
+    // 机器ID最大值
+    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
+    // 数据中心ID最大值
+    private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
+    // 毫秒内自增位
+    private final static long sequenceBits = 12L;
+    // 机器ID偏左移12位
+    private final static long workerIdShift = sequenceBits;
+    // 数据中心ID左移17位
+    private final static long datacenterIdShift = sequenceBits + workerIdBits;
+    // 时间毫秒左移22位
+    private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
+
+    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
+    /* 上次生产id时间戳 */
+    private static long lastTimestamp = -1L;
+    // 0,并发控制
+    private long sequence = 0L;
+
+    private final long workerId;
+    // 数据标识id部分
+    private final long datacenterId;
+
+    public IdWorker(){
+        this.datacenterId = getDatacenterId(maxDatacenterId);
+        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
+    }
+    /**
+     * @param workerId
+     *            工作机器ID
+     * @param datacenterId
+     *            序列号
+     */
+    public IdWorker(long workerId, long datacenterId) {
+        if (workerId > maxWorkerId || workerId < 0) {
+            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
+        }
+        if (datacenterId > maxDatacenterId || datacenterId < 0) {
+            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
+        }
+        this.workerId = workerId;
+        this.datacenterId = datacenterId;
+    }
+    /**
+     * 获取下一个ID
+     *
+     * @return
+     */
+    public synchronized String nextId(String prefix) {
+        long timestamp = timeGen();
+        if (timestamp < lastTimestamp) {
+            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
+        }
+
+        if (lastTimestamp == timestamp) {
+            // 当前毫秒内,则+1
+            sequence = (sequence + 1) & sequenceMask;
+            if (sequence == 0) {
+                // 当前毫秒内计数满了,则等待下一秒
+                timestamp = tilNextMillis(lastTimestamp);
+            }
+        } else {
+            sequence = 0L;
+        }
+        lastTimestamp = timestamp;
+        // ID偏移组合生成最终的ID,并返回ID
+        long nextId = ((timestamp - twepoch) << timestampLeftShift)
+                | (datacenterId << datacenterIdShift)
+                | (workerId << workerIdShift) | sequence;
+
+        return prefix+Long.toString(nextId);
+    }
+
+    private long tilNextMillis(final long lastTimestamp) {
+        long timestamp = this.timeGen();
+        while (timestamp <= lastTimestamp) {
+            timestamp = this.timeGen();
+        }
+        return timestamp;
+    }
+
+    private long timeGen() {
+        return System.currentTimeMillis();
+    }
+
+    /**
+     * <p>
+     * 获取 maxWorkerId
+     * </p>
+     */
+    protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
+        StringBuffer mpid = new StringBuffer();
+        mpid.append(datacenterId);
+        String name = ManagementFactory.getRuntimeMXBean().getName();
+        if (!name.isEmpty()) {
+         /*
+          * GET jvmPid
+          */
+            mpid.append(name.split("@")[0]);
+        }
+      /*
+       * MAC + PID 的 hashcode 获取16个低位
+       */
+        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
+    }
+
+    /**
+     * <p>
+     * 数据标识id部分
+     * </p>
+     */
+    protected static long getDatacenterId(long maxDatacenterId) {
+        long id = 0L;
+        try {
+            InetAddress ip = InetAddress.getLocalHost();
+            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
+            if (network == null) {
+                id = 1L;
+            } else {
+                byte[] mac = network.getHardwareAddress();
+                id = ((0x000000FF & (long) mac[mac.length - 1])
+                        | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
+                id = id % (maxDatacenterId + 1);
+            }
+        } catch (Exception e) {
+            log.error(" getDatacenterId: " + e.getMessage());
+        }
+        return id;
+    }
+
+
+}

+ 95 - 0
src/main/java/com/persagy/utils/ItvJsonUtil.java

@@ -0,0 +1,95 @@
+package com.persagy.utils;
+
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+
+public class ItvJsonUtil {
+    private static final Logger logger = LoggerFactory.getLogger(ItvJsonUtil.class);
+
+    public static ObjectMapper mapper = new ObjectMapper();
+
+    // 序列化时null字段不输出
+    static {
+        mapper.setDefaultPropertyInclusion(Include.NON_NULL);
+    }
+
+    /**
+     * 将json字符串反序列号成对象
+     *
+     * @param <T>
+     * @param json
+     * @param clazz
+     * @return
+     */
+    public static <T> T readValue(String json, Class<T> clazz) {
+        try {
+            return mapper.readValue(json, clazz);
+        } catch (JsonParseException e) {
+            logger.error("readValue exception", e);
+            // e.printStackTrace();
+        } catch (JsonMappingException e) {
+            logger.error("readValue exception", e);
+            // e.printStackTrace();
+        } catch (IOException e) {
+            logger.error("readValue exception", e);
+            // e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    /**
+     * @param json
+     * @param valueTypeRef
+     * @return
+     * @Title: readValue
+     * @return: T
+     */
+    public static <T> T readValue(String json, TypeReference<T> valueTypeRef) {
+        try {
+            return mapper.readValue(json, valueTypeRef);
+        } catch (JsonParseException e) {
+            logger.error("readValue exception", e);
+            // e.printStackTrace();
+        } catch (JsonMappingException e) {
+            logger.error("readValue exception", e);
+            // e.printStackTrace();
+        } catch (IOException e) {
+            logger.error("readValue exception", e);
+            // e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    /**
+     * 将对象序列号json字符串
+     *
+     * @param obj
+     * @return
+     */
+    public static String writeValue(Object obj) {
+        try {
+            return mapper.writeValueAsString(obj);
+        } catch (JsonParseException e) {
+            logger.error("writeValue exception", e);
+            // e.printStackTrace();
+        } catch (JsonMappingException e) {
+            logger.error("writeValue exception", e);
+            // e.printStackTrace();
+        } catch (IOException e) {
+            logger.error("writeValue exception", e);
+            // e.printStackTrace();
+        }
+
+        return null;
+    }
+}

+ 473 - 0
src/main/java/com/persagy/utils/JSONUtil.java

@@ -0,0 +1,473 @@
+package com.persagy.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+
+public class JSONUtil {
+    /**
+     * 转换更新参数
+     *
+     * @param jsonObject 用户接口参数,除了过滤项,其它项默认为新增项
+     * @return 数据平台接口参数
+     */
+    public static JSONObject getAddParamJson(JSONObject jsonObject) {
+        JSONObject paramJson = new JSONObject();
+        paramJson.put("insertObject", jsonObject);
+        return paramJson;
+    }
+
+    /**
+     * 转换主键查询参数
+     *
+     * @param jsonObject 用户接口数据
+     * @param majors     主键
+     * @return 数据平台接口参数
+     */
+    public static JSONObject getKeyWithMajors(JSONObject jsonObject, String... majors) {
+        JSONObject paramJson = new JSONObject();
+        JSONObject keyJson = new JSONObject();
+        Object value;
+        for (String major : majors) {
+            value = jsonObject.get(major);
+            if (value != null && !"".equals(value.toString())) {
+                keyJson.put(major, value);
+            }
+        }
+        paramJson.put("Key", keyJson);
+        return paramJson;
+    }
+
+    /**
+     * 转换更新参数
+     *
+     * @param jsonObject 用户接口参数,除了过滤项,其它项默认为修改项
+     * @param majors     过滤项
+     * @return 数据平台接口参数
+     */
+    public static JSONObject getUpdateParamJson(JSONObject jsonObject, String... majors) {
+        JSONObject paramJson = new JSONObject();
+        JSONObject criteriaJson = new JSONObject();
+        Object value;
+        for (String major : majors) {
+            value = jsonObject.get(major);
+            if (value != null && !"".equals(value.toString())) {
+                criteriaJson.put(major, value);
+            }
+            jsonObject.remove(major);
+        }
+        paramJson.put("Criteria", criteriaJson);
+        paramJson.put("set", jsonObject);
+
+        return paramJson;
+    }
+
+    /**
+     * 获得除去某些项的条件
+     *
+     * @param jsonObject 用户接口参数
+     * @param majors     过滤项
+     * @return 数据平台接口参数
+     */
+    public static JSONObject getCriteriaRemoveMajors(JSONObject jsonObject, String... majors) {
+        JSONObject paramJson = new JSONObject();
+        for (String major : majors) {
+            jsonObject.remove(major);
+        }
+        paramJson.put("Criteria", jsonObject);
+        return paramJson;
+    }
+
+    /**
+     * 获得指定项的条件
+     *
+     * @param jsonObject 用户接口参数
+     * @param majors     过滤项
+     * @return 数据平台接口参数
+     */
+    public static JSONObject getCriteriaWithMajors(JSONObject jsonObject, String... majors) {
+        JSONObject paramJson = new JSONObject();
+        JSONObject criteria = new JSONObject();
+        for (String major : majors) {
+            criteria.put(major, jsonObject.get(major));
+        }
+        paramJson.put("Criteria", criteria);
+        return paramJson;
+    }
+
+    /**
+     * 获得指定项的条件-用于批量查询
+     *
+     * @param jsonArray 用户接口参数
+     * @param key       参数的code码
+     * @param code      用户接口参数code码
+     * @return 数据平台接口参数
+     */
+    public static JSONObject getCriteriasWithMajors(JSONArray jsonArray, String key, String code) {
+        JSONObject paramJson = new JSONObject();
+        JSONArray criterias = new JSONArray();
+        JSONObject criteria, json;
+        for (int i = 0; i < jsonArray.size(); i++) {
+            json = jsonArray.getJSONObject(i);
+            criteria = new JSONObject();
+            criteria.put(code, json.get(key));
+            criterias.add(criteria);
+        }
+        paramJson.put("criterias", criterias);
+        return paramJson;
+    }
+
+    /**
+     * 获得指定项的条件-用于批量查询
+     *
+     * @param jsonObject 用户接口参数
+     * @param key        参数的code码
+     * @param code       用户接口参数code码
+     * @return 数据平台接口参数
+     */
+    public static JSONObject getCriteriasWithMajors(JSONObject jsonObject, String key, String code) {
+        JSONObject paramJson = new JSONObject();
+        JSONArray criterias = new JSONArray();
+        JSONObject criteria = new JSONObject();
+        criteria.put(code, jsonObject.get(key));
+        criterias.add(criteria);
+        paramJson.put("criterias", criterias);
+        return paramJson;
+    }
+
+    /**
+     * 获得数据平台查询条件
+     *
+     * @param codeName
+     * @param jsonObject
+     * @param majors
+     * @return
+     */
+    public static JSONObject getDataPlatCriteriaWithMajors(String codeName, JSONObject jsonObject, String... majors) {
+        JSONObject paramJson = new JSONObject();
+        JSONObject criteria = new JSONObject();
+        if (codeName != null) {
+            criteria.put("id", jsonObject.get(codeName));
+        }
+        for (String major : majors) {
+            criteria.put(major, jsonObject.get(major));
+        }
+        paramJson.put("Criteria", criteria);
+        return paramJson;
+    }
+
+    /**
+     * 功能描述:处理查询返回结果,将结果中的内容项转化为JsonString
+     *
+     * @param queryResult
+     * @param majors
+     * @return
+     * @创建者 wanghailong
+     * @邮箱 wanghailong@persagy.com
+     * @修改描述
+     */
+    public static String prossesResultToJsonString(String queryResult, String... majors) {
+        if (queryResult.contains("Result") && queryResult.contains("Content")) {
+            JSONObject resultJson = JSONObject.parseObject(queryResult);
+            JSONArray array = (JSONArray) resultJson.get("Content");
+            for (int i = 0; i < array.size(); i++) {
+                JSONObject dataJson = array.getJSONObject(i);
+                for (String major : majors) {
+                    String dataString = dataJson.getString(major);
+                    if (!StringUtil.isNull(dataString)) {
+                        if (dataString.startsWith("{")) {
+                            dataJson.put(major, JSONObject.parseObject(dataJson.getString(major)));
+                        } else if (dataString.startsWith("[")) {
+                            dataJson.put(major, JSONArray.parseArray(dataJson.getString(major)));
+                        }
+                    }
+                }
+                array.set(i, dataJson);
+            }
+            resultJson.put("Content", array);
+            queryResult = resultJson.toString();
+        }
+        return queryResult;
+    }
+
+    /**
+     * 功能描述:处理插入参数,将参数中的内容项转化为JsonString
+     *
+     * @param jsonObject
+     * @param majors
+     * @return
+     * @创建者 wanghailong
+     * @邮箱 wanghailong@persagy.com
+     * @修改描述
+     */
+    public static JSONObject prossesParamToJsonString(JSONObject jsonObject, String... majors) {
+        for (String major : majors) {
+            if (!StringUtil.isNull(jsonObject, major)) {
+                jsonObject.put(major, JSON.toJSONString(jsonObject.get(major)));
+            }
+        }
+        return jsonObject;
+    }
+
+    /**
+     * 功能描述:处理查询返回结果,转换结果中Date类型的数据格式
+     *
+     * @param queryResult
+     * @param fromDateFormat
+     * @param toDateFormat
+     * @param majors
+     * @return
+     * @throws Exception
+     */
+    public static String prossesResultToDateString(String queryResult, String fromDateFormat, String toDateFormat, String... majors) throws Exception {
+        if (queryResult.contains("Result") && queryResult.contains("Content")) {
+            JSONObject resultJson = JSONObject.parseObject(queryResult);
+            JSONArray array = (JSONArray) resultJson.get("Content");
+            SimpleDateFormat fsdf = new SimpleDateFormat(fromDateFormat);
+            SimpleDateFormat tsdf = new SimpleDateFormat(toDateFormat);
+            for (int i = 0; i < array.size(); i++) {
+                JSONObject dataJson = array.getJSONObject(i);
+                for (String major : majors) {
+                    String dataString = dataJson.getString(major);
+                    if (!StringUtil.isNull(dataString)) {
+                        dataJson.put(major, tsdf.format(fsdf.parse(dataString)));
+                    }
+                }
+                array.set(i, dataJson);
+            }
+            resultJson.put("Content", array);
+            queryResult = resultJson.toString();
+        }
+        return queryResult;
+    }
+
+
+    /**
+     * 功能描述:返回查询结果中的第一条记录
+     *
+     * @param queryResult
+     * @return
+     * @创建者 wanghailong
+     * @邮箱 wanghailong@persagy.com
+     * @修改描述
+     */
+    public static String getFirstRecordfromResult(String queryResult) {
+        if (queryResult.contains("Result") && queryResult.contains("Content")) {
+            JSONObject resultJson = JSONObject.parseObject(queryResult);
+            JSONArray array = (JSONArray) resultJson.get("Content");
+            if (array.size() > 0 && null != array.get(0)) {
+                resultJson.put("Item", JSONObject.parseObject(array.get(0).toString()));
+                resultJson.remove("Content");
+                resultJson.remove("Count");
+            }
+
+            queryResult = resultJson.toString();
+        }
+        return queryResult;
+    }
+
+    /**
+     * 功能描述:返回查询结果中的第一条记录,Json格式
+     *
+     * @param queryResult
+     * @return
+     * @创建者 wanghailong
+     * @邮箱 wanghailong@persagy.com
+     * @修改描述
+     */
+    public static JSONObject getFirstRecordfromResultWithJson(String queryResult) {
+        JSONObject firstJson = null;
+        if (queryResult.contains("Result") && queryResult.contains("Content")) {
+            JSONObject resultJson = JSONObject.parseObject(queryResult);
+            JSONArray array = (JSONArray) resultJson.get("Content");
+            if (array.size() > 0 && null != array.get(0)) {
+                firstJson = JSONObject.parseObject(array.get(0).toString());
+            }
+        }
+        return firstJson;
+    }
+
+    /**
+     * 功能描述:处理返回结果,模糊查询
+     *
+     * @param queryResult
+     * @param infoName
+     * @param infoValue
+     * @return
+     */
+    public static JSONObject likeFilterRecordByInfo(String queryResult, String infoName, String infoValue) {
+        JSONObject queryResultJson = JSONObject.parseObject(queryResult);
+        JSONArray queryResultArray = queryResultJson.getJSONArray("Content");
+        if ("success".equals(queryResultJson.getString("Result")) && queryResultArray != null) {
+            JSONArray resultArray = new JSONArray();
+            JSONObject item;
+            for (int i = 0; i < queryResultArray.size(); i++) {
+                item = queryResultArray.getJSONObject(i);
+                if (!StringUtil.isNull(item, infoName) && item.getString(infoName).contains(infoValue)) {
+                    resultArray.add(item);
+                }
+            }
+            queryResultJson.put("Content", resultArray);
+            queryResultJson.put("Count", resultArray.size());
+        }
+        return queryResultJson;
+    }
+
+    /**
+     * 排序
+     *
+     * @param queryResult
+     * @param field       排序的字段
+     * @param order       -1:倒序  1:正序
+     * @return
+     */
+    public static JSONObject sortByField(String queryResult, String field, int order) {
+        JSONObject resultJson = JSONObject.parseObject(queryResult);
+        JSONArray contentArray = resultJson.getJSONArray("Content");
+        if (contentArray != null) {
+            Collections.sort(contentArray, new Comparator<Object>() {
+                public int compare(Object str1, Object str2) {
+                    JSONObject obj1 = JSONObject.parseObject(str1.toString());
+                    JSONObject obj2 = JSONObject.parseObject(str2.toString());
+                    if (obj1.getLongValue(field) > obj2.getLongValue(field)) {
+                        return order;
+                    }
+                    if (obj1.getLongValue(field) == obj2.getLongValue(field)) {
+                        return 0;
+                    }
+                    return -1 * order;
+                }
+            });
+        }
+        resultJson.put("Content", contentArray);
+        return resultJson;
+    }
+
+    /**
+     * 排序
+     *
+     * @param array
+     * @param field 排序的字段
+     * @param order -1:倒序  1:正序
+     * @return
+     */
+    public static JSONArray sortByField(JSONArray array, String field, int order) {
+        if (array != null) {
+            Collections.sort(array, new Comparator<Object>() {
+                public int compare(Object str1, Object str2) {
+                    JSONObject obj1 = JSONObject.parseObject(str1.toString());
+                    JSONObject obj2 = JSONObject.parseObject(str2.toString());
+                    if (obj1.getLongValue(field) > obj2.getLongValue(field)) {
+                        return order;
+                    }
+                    if (obj1.getLongValue(field) == obj2.getLongValue(field)) {
+                        return 0;
+                    }
+                    return -1 * order;
+                }
+            });
+        }
+        return array;
+    }
+
+    /**
+     * 排序
+     *
+     * @param array
+     * @param field 排序的字段
+     * @param order -1:倒序  1:正序
+     * @return
+     */
+    public static JSONArray sortByStringField(JSONArray array, String field, int order) {
+        if (array != null) {
+            Collections.sort(array, new Comparator<Object>() {
+                public int compare(Object str1, Object str2) {
+                    JSONObject obj1 = JSONObject.parseObject(str1.toString());
+                    JSONObject obj2 = JSONObject.parseObject(str2.toString());
+                    if (obj1.getString(field).compareTo(obj2.getString(field)) > -1) {
+                        return order;
+                    }
+                    return -1 * order;
+                }
+            });
+        }
+        return array;
+    }
+
+    /**
+     * 处理请求结果为map
+     *
+     * @param resultStr  请求结果
+     * @param keyCode    map的key的code
+     * @param valueCode  map的value的code	null表示整个对象为value
+     * @param keyClazz   key的数据类型
+     * @param valueClazz value的数据类型
+     * @return
+     */
+    public static <T, V> Map<T, V> proccessResultToMap(String resultStr, String keyCode, String valueCode, Class<T> keyClazz, Class<V> valueClazz) {
+        Map<T, V> map = null;
+        JSONObject result = JSONObject.parseObject(resultStr);
+        if ("success".equals(result.getString("Result"))) {
+            map = new HashMap<T, V>();
+            JSONArray content = (JSONArray) result.getOrDefault("Content", new JSONArray());
+            JSONObject item;
+            for (int i = 0; i < content.size(); i++) {
+                item = content.getJSONObject(i);
+                if (StringUtil.isNull(valueCode)) {
+                    map.put(keyClazz.cast(item.get(keyCode)), valueClazz.cast(item));
+                } else {
+                    map.put(keyClazz.cast(item.get(keyCode)), valueClazz.cast(item.get(valueCode)));
+                }
+            }
+        }
+        return map;
+    }
+
+    /**
+     * 根据key返回指定值
+     *
+     * @param jsObject
+     * @param key
+     * @return
+     */
+    public static JSONObject getJSONObject(JSONObject jsObject, String key) {
+        JSONObject Obj = jsObject.getJSONObject(key);
+        if (Obj != null) {
+            return Obj;
+        }
+        return new JSONObject();
+    }
+
+    /**
+     * 根据key返回指定值
+     *
+     * @param jsObject
+     * @param key
+     * @return
+     */
+    public static JSONArray getJSONArray(JSONObject jsObject, String key) {
+        JSONArray Obj = jsObject.getJSONArray(key);
+        if (Obj != null) {
+            return Obj;
+        }
+        return new JSONArray();
+    }
+
+    /**
+     * 转换批量插入参数
+     *
+     * @param jsonObject
+     * @return 数据平台接口参数
+     */
+    public static JSONObject getAddParamJson(JSONArray jsonArray) {
+        JSONObject paramJson = new JSONObject();
+        paramJson.put("insertObjects", jsonArray);
+        return paramJson;
+    }
+}

+ 77 - 0
src/main/java/com/persagy/utils/NetworkUtil.java

@@ -0,0 +1,77 @@
+package com.persagy.utils;
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * @author xingmaojun
+ * @version V1.0
+ * @ClassName NetworkUtil
+ * @Description:常用获取客户端信息的工具
+ * @date 2020/6/5 11:06
+ **/
+@Slf4j
+public class NetworkUtil {
+
+
+    /**
+     * 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址;
+     *
+     * @param request
+     * @return
+     * @throws IOException
+     */
+    public final static String getIpAddress(HttpServletRequest request) throws IOException {
+        // 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址
+
+        String ip = request.getHeader("X-Forwarded-For");
+        if (log.isInfoEnabled()) {
+            log.info("getIpAddress(HttpServletRequest) - X-Forwarded-For - String ip=" + ip);
+        }
+
+        if (ip == null || ip.length() == 0 || "unknown" .equalsIgnoreCase(ip)) {
+            if (ip == null || ip.length() == 0 || "unknown" .equalsIgnoreCase(ip)) {
+                ip = request.getHeader("Proxy-Client-IP");
+                if (log.isInfoEnabled()) {
+                    log.info("getIpAddress(HttpServletRequest) - Proxy-Client-IP - String ip=" + ip);
+                }
+            }
+            if (ip == null || ip.length() == 0 || "unknown" .equalsIgnoreCase(ip)) {
+                ip = request.getHeader("WL-Proxy-Client-IP");
+                if (log.isInfoEnabled()) {
+                    log.info("getIpAddress(HttpServletRequest) - WL-Proxy-Client-IP - String ip=" + ip);
+                }
+            }
+            if (ip == null || ip.length() == 0 || "unknown" .equalsIgnoreCase(ip)) {
+                ip = request.getHeader("HTTP_CLIENT_IP");
+                if (log.isInfoEnabled()) {
+                    log.info("getIpAddress(HttpServletRequest) - HTTP_CLIENT_IP - String ip=" + ip);
+                }
+            }
+            if (ip == null || ip.length() == 0 || "unknown" .equalsIgnoreCase(ip)) {
+                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+                if (log.isInfoEnabled()) {
+                    log.info("getIpAddress(HttpServletRequest) - HTTP_X_FORWARDED_FOR - String ip=" + ip);
+                }
+            }
+            if (ip == null || ip.length() == 0 || "unknown" .equalsIgnoreCase(ip)) {
+                ip = request.getRemoteAddr();
+                if (log.isInfoEnabled()) {
+                    log.info("getIpAddress(HttpServletRequest) - getRemoteAddr - String ip=" + ip);
+                }
+            }
+        } else if (ip.length() > 15) {
+            String[] ips = ip.split(",");
+            for (int index = 0; index < ips.length; index++) {
+                String strIp = (String) ips[index];
+                if (!("unknown" .equalsIgnoreCase(strIp))) {
+                    ip = strIp;
+                    break;
+                }
+            }
+        }
+        return ip;
+    }
+}

+ 293 - 0
src/main/java/com/persagy/utils/StringUtil.java

@@ -0,0 +1,293 @@
+package com.persagy.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.UUID;
+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 2018614日: 上午10:41:01
+     * Update By cuixubin 2018614日: 上午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);
+            if(jsobj instanceof JSONObject){
+                return true;
+            }
+            return false;
+        } 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);
+            if(jsobj instanceof JSONArray){
+                return true;
+            }
+            return false;
+        } 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();
+    }
+}

+ 4 - 0
src/main/resources/application.properties

@@ -0,0 +1,4 @@
+# 应用名称
+spring.application.name=zkt-project-alarm
+
+

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

@@ -0,0 +1,6 @@
+server:
+  port: 8891
+spring:
+  # 应用名称
+  application:
+    name: zkt-project-alarm

+ 88 - 0
src/main/resources/logback-spring.xml

@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--scan:  当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
+scanPeriod:  设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
+debug:  当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
+configuration 子节点为 appender、logger、root       -->
+<configuration scan="true" scanPeriod="60 seconds" debug="false">
+    <!--用于区分不同应用程序的记录-->
+    <contextName>zk-project-control</contextName>
+    <!--日志文件所在目录,如果是tomcat,如下写法日志文件会在则为${TOMCAT_HOME}/bin/logs/目录下-->
+    <property name="LOG_HOME" value="../logs/zk-project-control"/>
+    <!--控制台-->
+    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %logger输出日志的logger名 %msg:日志消息,%n是换行符 -->
+            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] %-5level %logger{36} : %msg%n</pattern>
+            <!--解决乱码问题-->
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+    <!--滚动文件-->
+    <appender name="infoFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- ThresholdFilter:临界值过滤器,过滤掉 TRACE 和 DEBUG 级别的日志 -->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>INFO</level>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_HOME}/log.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <maxHistory>30</maxHistory><!--保存最近30天的日志-->
+        </rollingPolicy>
+        <encoder>
+            <charset>UTF-8</charset>
+            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] %-5level %logger{36} : %msg%n</pattern>
+        </encoder>
+    </appender>
+    <!--滚动文件-->
+    <appender name="errorFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- ThresholdFilter:临界值过滤器,过滤掉 TRACE 和 DEBUG 级别的日志 -->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>error</level>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <maxHistory>30</maxHistory><!--保存最近30天的日志-->
+        </rollingPolicy>
+        <encoder>
+            <charset>UTF-8</charset>
+            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] %-5level %logger{36} : %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <!--将日志输出到logstack-->
+    <!--<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
+        <destination>47.93.173.81:7002</destination>
+        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
+            <charset>UTF-8</charset>
+        </encoder>
+        <keepAliveDuration>5 minutes</keepAliveDuration>
+    </appender>-->
+
+    <!--这里如果是info,spring、mybatis等框架则不会输出:TRACE < DEBUG < INFO <  WARN < ERROR-->
+    <!--root是所有logger的祖先,均继承root,如果某一个自定义的logger没有指定level,就会寻找
+    父logger看有没有指定级别,直到找到root。-->
+    <root level="debug">
+        <appender-ref ref="stdout"/>
+        <appender-ref ref="infoFile"/>
+        <appender-ref ref="errorFile"/>
+        <!--<appender-ref ref="logstash"/>-->
+    </root>
+
+    <!--为某个包单独配置logger
+
+    比如定时任务,写代码的包名为:com.seentao.task
+    步骤如下:
+    1、定义一个appender,取名为task(随意,只要下面logger引用就行了)
+    appender的配置按照需要即可
+
+
+    2、定义一个logger:
+    <logger name="com.seentao.task" level="DEBUG" additivity="false">
+      <appender-ref ref="task" />
+    </logger>
+    注意:additivity必须设置为false,这样只会交给task这个appender,否则其他appender也会打印com.seentao.task里的log信息。
+
+    3、这样,在com.seentao.task的logger就会是上面定义的logger了。
+    private static Logger logger = LoggerFactory.getLogger(Class1.class);
+    -->
+
+</configuration>

+ 13 - 0
src/test/java/com/persagy/zktprojectalarm/ZktProjectAlarmApplicationTests.java

@@ -0,0 +1,13 @@
+package com.persagy.zktprojectalarm;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class ZktProjectAlarmApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}