Bläddra i källkod

Squashed commit of the following:

commit ea3eb2e370cbaea01cf888b6b3a86e35943a8cde
Author: luoshi <luoshi@qmth.com.cn>
Date:   Mon Nov 8 10:02:15 2021 +0800

    修改tools-device,增加多网卡和多硬盘的序列号排序

    Signed-off-by: luoshi <luoshi@qmth.com.cn>

commit 33ed6a1ac70c8df82c9e4b5893e973039c404de3
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Oct 21 13:43:45 2021 +0800

    修改core-solar的授权证书格式,兼容绑定设备与不绑定设备两种情况

    Signed-off-by: luoshi <luoshi@qmth.com.cn>

commit bdfe51fcdd57610fa29595ed605d7b5aeda7a570
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Oct 20 14:31:07 2021 +0800

    修改tools-device,增加硬盘序列号和mac地址的去重

    Signed-off-by: luoshi <luoshi@qmth.com.cn>

commit 63e8497d208eb01f7c2d225b0f81cc1af6ae94dd
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Oct 20 14:20:31 2021 +0800

    修改core-solar组件,修改AppLicenseParser为AppLicenseUtil,增加构造当前设备加密信息方法

    Signed-off-by: luoshi <luoshi@qmth.com.cn>

commit 5d667cc2eae38162b2a2128c2896f5c10bd9fa94
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Oct 20 11:28:47 2021 +0800

    修复tools-poi组件中,Excel解析时可能出现nullable注解失效的bug

    Signed-off-by: luoshi <luoshi@qmth.com.cn>

commit 1605b19a0577c92252024462c4a26aeb13f53c0d
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Oct 20 11:20:37 2021 +0800

    修改core-solar组件,去掉启动强制激活要求;增加在线/离线两种激活方法;离线激活加入是否绑定硬件信息开关

    Signed-off-by: luoshi <luoshi@qmth.com.cn>

commit b5662166e07bd03671f2c48a137e1ad9366d1239
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Oct 19 12:09:28 2021 +0800

    版本号升级到1.0.2;增加tools-device组件

    Signed-off-by: luoshi <luoshi@qmth.com.cn>
luoshi 3 år sedan
förälder
incheckning
aa9bc29f45
33 ändrade filer med 538 tillägg och 98 borttagningar
  1. 1 1
      core-cache/pom.xml
  2. 1 1
      core-concurrent/pom.xml
  3. 1 1
      core-fss/pom.xml
  4. 1 1
      core-logging/pom.xml
  5. 1 1
      core-metrics/pom.xml
  6. 1 1
      core-models/pom.xml
  7. 1 1
      core-rate-limit/pom.xml
  8. 1 1
      core-retrofit/pom.xml
  9. 1 1
      core-schedule/pom.xml
  10. 1 1
      core-security/pom.xml
  11. 5 1
      core-solar/pom.xml
  12. 0 10
      core-solar/src/main/java/com/qmth/boot/core/solar/config/SolarProperties.java
  13. 0 46
      core-solar/src/main/java/com/qmth/boot/core/solar/crypto/AppLicenseParser.java
  14. 88 0
      core-solar/src/main/java/com/qmth/boot/core/solar/crypto/AppLicenseUtil.java
  15. 2 1
      core-solar/src/main/java/com/qmth/boot/core/solar/enums/AppType.java
  16. 63 19
      core-solar/src/main/java/com/qmth/boot/core/solar/service/SolarService.java
  17. 1 1
      core-uid/pom.xml
  18. 1 1
      data-mybatis-plus/pom.xml
  19. 1 1
      data-redis/pom.xml
  20. 48 1
      pom.xml
  21. 1 1
      starter-api/pom.xml
  22. 1 1
      tools-common/pom.xml
  23. 30 0
      tools-device/pom.xml
  24. 108 0
      tools-device/src/main/java/com/qmth/boot/tools/device/DeviceInfo.java
  25. 47 0
      tools-device/src/main/java/com/qmth/boot/tools/device/entity/Baseboard.java
  26. 37 0
      tools-device/src/main/java/com/qmth/boot/tools/device/entity/Computer.java
  27. 17 0
      tools-device/src/main/java/com/qmth/boot/tools/device/entity/DiskStore.java
  28. 17 0
      tools-device/src/main/java/com/qmth/boot/tools/device/entity/NetworkInterface.java
  29. 37 0
      tools-device/src/main/java/com/qmth/boot/tools/device/entity/Processor.java
  30. 15 0
      tools-device/src/test/java/com/qmth/boot/test/tools/device/DeviceInfoTest.java
  31. 1 1
      tools-poi/pom.xml
  32. 7 3
      tools-poi/src/main/java/com/qmth/boot/tools/excel/handler/impl/ObjectHandler.java
  33. 1 1
      tools-signature/pom.xml

+ 1 - 1
core-cache/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>qmth-boot</artifactId>
         <groupId>com.qmth.boot</groupId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>core-cache</artifactId>
     <name>core-uid</name>

+ 1 - 1
core-concurrent/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>qmth-boot</artifactId>
         <groupId>com.qmth.boot</groupId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>core-concurrent</artifactId>
     <name>core-concurrent</name>

+ 1 - 1
core-fss/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>qmth-boot</artifactId>
         <groupId>com.qmth.boot</groupId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>core-fss</artifactId>
     <name>core-fss</name>

+ 1 - 1
core-logging/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>qmth-boot</artifactId>
         <groupId>com.qmth.boot</groupId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>core-logging</artifactId>
     <name>core-logging</name>

+ 1 - 1
core-metrics/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <groupId>com.qmth.boot</groupId>
         <artifactId>qmth-boot</artifactId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>core-metrics</artifactId>
     <name>core-metrics</name>

+ 1 - 1
core-models/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>qmth-boot</artifactId>
         <groupId>com.qmth.boot</groupId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>core-models</artifactId>
     <name>core-models</name>

+ 1 - 1
core-rate-limit/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <groupId>com.qmth.boot</groupId>
         <artifactId>qmth-boot</artifactId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>core-rate-limit</artifactId>
     <name>core-rate-limit</name>

+ 1 - 1
core-retrofit/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>qmth-boot</artifactId>
         <groupId>com.qmth.boot</groupId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>core-retrofit</artifactId>
     <name>core-retrofit</name>

+ 1 - 1
core-schedule/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>qmth-boot</artifactId>
         <groupId>com.qmth.boot</groupId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>core-schedule</artifactId>
     <name>core-schedule</name>

+ 1 - 1
core-security/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <groupId>com.qmth.boot</groupId>
         <artifactId>qmth-boot</artifactId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>core-security</artifactId>
     <name>core-security</name>

+ 5 - 1
core-solar/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>qmth-boot</artifactId>
         <groupId>com.qmth.boot</groupId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>core-solar</artifactId>
     <name>core-solar</name>
@@ -24,6 +24,10 @@
             <groupId>com.qmth.boot</groupId>
             <artifactId>core-retrofit</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.qmth.boot</groupId>
+            <artifactId>tools-device</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot</artifactId>

+ 0 - 10
core-solar/src/main/java/com/qmth/boot/core/solar/config/SolarProperties.java

@@ -13,8 +13,6 @@ public class SolarProperties {
     @NotNull
     private String server = "https://solar.qmth.com.cn";
 
-    private boolean checkup = true;
-
     private String accessKey;
 
     private String accessSecret;
@@ -29,14 +27,6 @@ public class SolarProperties {
         this.server = server;
     }
 
-    public boolean isCheckup() {
-        return checkup;
-    }
-
-    public void setCheckup(boolean checkup) {
-        this.checkup = checkup;
-    }
-
     public String getAccessKey() {
         return accessKey;
     }

+ 0 - 46
core-solar/src/main/java/com/qmth/boot/core/solar/crypto/AppLicenseParser.java

@@ -1,46 +0,0 @@
-package com.qmth.boot.core.solar.crypto;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.qmth.boot.core.solar.model.AppLicense;
-import com.qmth.boot.tools.codec.CodecUtils;
-import com.qmth.boot.tools.crypto.RSA;
-import com.qmth.boot.tools.models.ByteArray;
-import org.apache.commons.lang3.ArrayUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.util.Arrays;
-
-/**
- * 应用许可证解析工具
- */
-public class AppLicenseParser {
-
-    private static final Logger log = LoggerFactory.getLogger(AppLicenseParser.class);
-
-    private static final String PUBLIC_KEY_PATH = "license/public.key";
-
-    /**
-     * 通过许可证文件路径尝试解析
-     *
-     * @param filePath
-     * @return
-     */
-    public static AppLicense parse(String filePath) {
-        try {
-            byte[] original = ByteArray.fromFile(new File(filePath)).value();
-            byte[] digest = ArrayUtils.subarray(original, 0, 16);
-            byte[] content = RSA.getPublicKey(ByteArray
-                    .fromInputStream(AppLicenseParser.class.getClassLoader().getResourceAsStream(PUBLIC_KEY_PATH))
-                    .value()).decrypt(ArrayUtils.subarray(original, 16, original.length)).value();
-            if (Arrays.equals(digest, CodecUtils.md5(content))) {
-                return new ObjectMapper().readValue(content, AppLicense.class);
-            }
-        } catch (Exception e) {
-            log.error("Solar license parse error", e);
-        }
-        return null;
-    }
-
-}

+ 88 - 0
core-solar/src/main/java/com/qmth/boot/core/solar/crypto/AppLicenseUtil.java

@@ -0,0 +1,88 @@
+package com.qmth.boot.core.solar.crypto;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.qmth.boot.core.solar.model.AppLicense;
+import com.qmth.boot.tools.codec.CodecUtils;
+import com.qmth.boot.tools.crypto.AES;
+import com.qmth.boot.tools.crypto.RSA;
+import com.qmth.boot.tools.device.DeviceInfo;
+import com.qmth.boot.tools.models.ByteArray;
+import org.apache.commons.lang3.ArrayUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.Arrays;
+
+/**
+ * 应用许可证解析工具
+ */
+public class AppLicenseUtil {
+
+    private static final Logger log = LoggerFactory.getLogger(AppLicenseUtil.class);
+
+    private static final String PUBLIC_KEY_PATH = "license/public.key";
+
+    /**
+     * 构造当前设备加密信息文件
+     *
+     * @return
+     */
+    public static ByteArray buildDeviceInfo() throws Exception {
+        return RSA.getPublicKey(
+                ByteArray.fromInputStream(AppLicenseUtil.class.getClassLoader().getResourceAsStream(PUBLIC_KEY_PATH))
+                        .value()).encrypt(new ObjectMapper().writeValueAsBytes(DeviceInfo.current()));
+    }
+
+    /**
+     * 通过许可证文件路径尝试解析
+     *
+     * @param filePath
+     * @return
+     */
+    public static AppLicense parseLicense(String filePath) {
+        try {
+            return parseLicense(ByteArray.fromFile(new File(filePath)).value());
+        } catch (Exception e) {
+            log.error("Solar license parse error", e);
+        }
+        return null;
+    }
+
+    /**
+     * 直接解析许可证文件内容
+     *
+     * @param data
+     * @return
+     */
+    public static AppLicense parseLicense(byte[] data) {
+        try {
+            byte[] original = ArrayUtils.subarray(data, 1, data.length);
+            //是否绑定硬件
+            if (data[0] == 0) {
+                //不绑定硬件
+            } else if (data[0] == 1) {
+                //获取当前设备标识
+                String deviceId = DeviceInfo.current().uuid();
+                //使用设备标识进行AES解密
+                original = AES.decrypt(original, deviceId.substring(0, 16), deviceId.substring(16)).value();
+            } else {
+                return null;
+            }
+            //截取前16位摘要信息
+            byte[] digest = ArrayUtils.subarray(original, 0, 16);
+            //进行RSA解密
+            byte[] content = RSA.getPublicKey(ByteArray
+                    .fromInputStream(AppLicenseUtil.class.getClassLoader().getResourceAsStream(PUBLIC_KEY_PATH))
+                    .value()).decrypt(ArrayUtils.subarray(original, 16, original.length)).value();
+            //比对摘要是否一致
+            if (Arrays.equals(digest, CodecUtils.md5(content))) {
+                return new ObjectMapper().readValue(content, AppLicense.class);
+            }
+        } catch (Exception e) {
+            log.error("Solar license parse error", e);
+        }
+        return null;
+    }
+
+}

+ 2 - 1
core-solar/src/main/java/com/qmth/boot/core/solar/enums/AppType.java

@@ -7,11 +7,12 @@ public enum AppType {
 
     MARKINGCLOUD("云阅卷"),
     EXAM_CLOUD("考试云平台"),
-    SKYNET("智能监考平台"),
     PRINT_CLOUD("印刷平台"),
     TEACH_CLOUD("逸教云"),
     ONLINE_EXAM("在线考试平台"),
+    EXAM_MONITOR("智能监考平台"),
     ITEM_BANK("题库系统"),
+    SCORE_ANALYSIS("成绩分析系统"),
     COMMON("公共服务");
 
     private String name;

+ 63 - 19
core-solar/src/main/java/com/qmth/boot/core/solar/service/SolarService.java

@@ -1,9 +1,8 @@
 package com.qmth.boot.core.solar.service;
 
-import com.qmth.boot.core.exception.StatusException;
 import com.qmth.boot.core.solar.api.SolarApiClient;
 import com.qmth.boot.core.solar.config.SolarProperties;
-import com.qmth.boot.core.solar.crypto.AppLicenseParser;
+import com.qmth.boot.core.solar.crypto.AppLicenseUtil;
 import com.qmth.boot.core.solar.model.AppControl;
 import com.qmth.boot.core.solar.model.AppInfo;
 import com.qmth.boot.core.solar.model.AppLicense;
@@ -32,9 +31,7 @@ public class SolarService {
     public SolarService(SolarProperties solarProperties, SolarApiClient solarApiClient) {
         this.solarProperties = solarProperties;
         this.solarApiClient = solarApiClient;
-        if (solarProperties.isCheckup()) {
-            checkup();
-        }
+        checkup();
     }
 
     /**
@@ -52,7 +49,7 @@ public class SolarService {
      * @return
      */
     public AppControl getAppControl() {
-        return appInfo.getControl();
+        return appInfo != null ? appInfo.getControl() : null;
     }
 
     /**
@@ -81,27 +78,74 @@ public class SolarService {
         }
     }
 
+    /**
+     * 使用AK信息在线激活
+     *
+     * @param accessKey
+     * @param accessSecret
+     * @return
+     */
+    public synchronized AppInfo update(String accessKey, String accessSecret) {
+        appInfo = null;
+        license = null;
+        solarProperties.setAccessKey(accessKey);
+        solarProperties.setAccessSecret(accessSecret);
+        solarProperties.setLicense(null);
+        try {
+            appInfo = solarApiClient.getAppInfo();
+        } catch (Exception e) {
+            log.error("Solar api invoke error", e);
+        }
+        return appInfo;
+    }
+
+    /**
+     * 使用授权文件离线激活,同时需要在有效期内
+     *
+     * @param licenseData
+     * @return
+     */
+    public synchronized AppInfo update(byte[] licenseData) {
+        appInfo = null;
+        license = null;
+        solarProperties.setAccessKey(null);
+        solarProperties.setAccessSecret(null);
+        AppLicense result = AppLicenseUtil.parseLicense(licenseData);
+        if (result != null && !result.getControl().hasExpired()) {
+            license = result;
+            appInfo = result;
+        }
+        return appInfo;
+    }
+
     private void checkup() {
-        log.info("Solar checkup starting...");
+        log.info("Solar startup checking...");
         if (StringUtils.isNotBlank(solarProperties.getAccessKey()) && StringUtils
                 .isNotBlank(solarProperties.getAccessSecret())) {
-            appInfo = solarApiClient.getAppInfo();
-            if (appInfo == null) {
-                throw new StatusException("Solar online validate faile!");
+            try {
+                appInfo = solarApiClient.getAppInfo();
+            } catch (Exception e) {
+                log.error("Solar api invoke error", e);
             }
         } else if (StringUtils.isNotBlank(solarProperties.getLicense())) {
-            license = AppLicenseParser.parse(solarProperties.getLicense());
-            if (license == null) {
-                throw new StatusException("Solar license invalid");
-            }
-            appInfo = license;
-            if (getAppControl().hasExpired()) {
-                throw new StatusException("Solar license has expired");
+            //启动授权文件默认绑定硬件信息
+            AppLicense license = AppLicenseUtil.parseLicense(solarProperties.getLicense());
+            if (license != null) {
+                if (license.getControl().hasExpired()) {
+                    log.error("Solar license has expired");
+                } else {
+                    this.license = license;
+                    this.appInfo = license;
+                }
             }
         } else {
-            throw new StatusException("Solar checkup faile, accessKey&accessSecret or license file is required!");
+            log.warn("Solar startup check exit, accessKey&accessSecret and license file not exist!");
+        }
+        if (appInfo != null) {
+            log.info("Solar startup check success, current app name: " + appInfo.getName());
+        } else {
+            log.info("Solar startup check faile, app unexist!");
         }
-        log.info("Solar checkup success, current app name: " + appInfo.getName());
     }
 
 }

+ 1 - 1
core-uid/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <groupId>com.qmth.boot</groupId>
         <artifactId>qmth-boot</artifactId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>core-uid</artifactId>
     <name>core-uid</name>

+ 1 - 1
data-mybatis-plus/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>qmth-boot</artifactId>
         <groupId>com.qmth.boot</groupId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>data-mybatis-plus</artifactId>
     <name>data-mybatis-plus</name>

+ 1 - 1
data-redis/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>qmth-boot</artifactId>
         <groupId>com.qmth.boot</groupId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>data-redis</artifactId>
     <name>data-redis</name>

+ 48 - 1
pom.xml

@@ -7,13 +7,14 @@
     <groupId>com.qmth.boot</groupId>
     <artifactId>qmth-boot</artifactId>
     <packaging>pom</packaging>
-    <version>1.0.1</version>
+    <version>1.0.2</version>
 
     <modules>
         <module>starter-api</module>
         <module>tools-poi</module>
         <module>tools-signature</module>
         <module>tools-common</module>
+        <module>tools-device</module>
         <module>data-redis</module>
         <module>data-mybatis-plus</module>
         <module>core-models</module>
@@ -58,6 +59,11 @@
                 <artifactId>tools-poi</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.qmth.boot</groupId>
+                <artifactId>tools-device</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>com.qmth.boot</groupId>
                 <artifactId>tools-common</artifactId>
@@ -190,6 +196,18 @@
                 <artifactId>commons-lang3</artifactId>
                 <version>3.11</version>
             </dependency>
+            <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
+            <dependency>
+                <groupId>commons-codec</groupId>
+                <artifactId>commons-codec</artifactId>
+                <version>1.15</version>
+            </dependency>
+            <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
+            <dependency>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-core</artifactId>
+                <version>2.11.3</version>
+            </dependency>
             <dependency>
                 <groupId>com.fasterxml.jackson.core</groupId>
                 <artifactId>jackson-databind</artifactId>
@@ -200,6 +218,11 @@
                 <artifactId>jackson-datatype-jsr310</artifactId>
                 <version>2.11.3</version>
             </dependency>
+            <dependency>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-annotations</artifactId>
+                <version>2.11.3</version>
+            </dependency>
             <dependency>
                 <groupId>com.github.ben-manes.caffeine</groupId>
                 <artifactId>caffeine</artifactId>
@@ -280,6 +303,30 @@
                 <artifactId>logging-interceptor</artifactId>
                 <version>${okhttp.version}</version>
             </dependency>
+            <!-- https://mvnrepository.com/artifact/com.github.oshi/oshi-core -->
+            <dependency>
+                <groupId>com.github.oshi</groupId>
+                <artifactId>oshi-core</artifactId>
+                <version>5.8.2</version>
+            </dependency>
+            <!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
+            <dependency>
+                <groupId>net.java.dev.jna</groupId>
+                <artifactId>jna</artifactId>
+                <version>5.9.0</version>
+            </dependency>
+            <!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna-platform -->
+            <dependency>
+                <groupId>net.java.dev.jna</groupId>
+                <artifactId>jna-platform</artifactId>
+                <version>5.9.0</version>
+            </dependency>
+            <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-api</artifactId>
+                <version>1.7.32</version>
+            </dependency>
             <dependency>
                 <groupId>junit</groupId>
                 <artifactId>junit</artifactId>

+ 1 - 1
starter-api/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <groupId>com.qmth.boot</groupId>
         <artifactId>qmth-boot</artifactId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>starter-api</artifactId>
     <name>starter-api</name>

+ 1 - 1
tools-common/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <groupId>com.qmth.boot</groupId>
         <artifactId>qmth-boot</artifactId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>tools-common</artifactId>
     <name>tools-common</name>

+ 30 - 0
tools-device/pom.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.qmth.boot</groupId>
+        <artifactId>qmth-boot</artifactId>
+        <version>1.0.2</version>
+    </parent>
+    <artifactId>tools-device</artifactId>
+    <name>tools-device</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.qmth.boot</groupId>
+            <artifactId>tools-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.oshi</groupId>
+            <artifactId>oshi-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 108 - 0
tools-device/src/main/java/com/qmth/boot/tools/device/DeviceInfo.java

@@ -0,0 +1,108 @@
+package com.qmth.boot.tools.device;
+
+import com.qmth.boot.tools.device.entity.*;
+import com.qmth.boot.tools.models.ByteArray;
+import org.apache.commons.lang3.StringUtils;
+import oshi.SystemInfo;
+import oshi.hardware.HWDiskStore;
+import oshi.hardware.HardwareAbstractionLayer;
+import oshi.hardware.NetworkIF;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 设备信息描述对象
+ */
+public class DeviceInfo {
+
+    private static DeviceInfo current;
+
+    private Computer computer;
+
+    private Baseboard baseboard;
+
+    private DiskStore diskStore;
+
+    private Processor processor;
+
+    private NetworkInterface networkInterface;
+
+    public DeviceInfo() {
+        computer = new Computer();
+        baseboard = new Baseboard();
+        processor = new Processor();
+        diskStore = new DiskStore();
+        networkInterface = new NetworkInterface();
+    }
+
+    /**
+     * 获取当前设备信息
+     *
+     * @return
+     */
+    public static DeviceInfo current() {
+        if (current == null) {
+            current = new DeviceInfo();
+            HardwareAbstractionLayer hardware = new SystemInfo().getHardware();
+            //computer
+            current.computer.setManufacturer(hardware.getComputerSystem().getManufacturer());
+            current.computer.setModel(hardware.getComputerSystem().getModel());
+            current.computer.setSerialNumber(hardware.getComputerSystem().getSerialNumber());
+            //baseboard
+            current.baseboard.setManufacturer(hardware.getComputerSystem().getBaseboard().getManufacturer());
+            current.baseboard.setModel(hardware.getComputerSystem().getBaseboard().getModel());
+            current.baseboard.setVersion(hardware.getComputerSystem().getBaseboard().getVersion());
+            current.baseboard.setSerialNumber(hardware.getComputerSystem().getBaseboard().getSerialNumber());
+            //processor
+            current.processor.setIdentifier(hardware.getProcessor().getProcessorIdentifier().getIdentifier());
+            current.processor.setId(hardware.getProcessor().getProcessorIdentifier().getProcessorID());
+            current.processor.setPhysicalCount(hardware.getProcessor().getPhysicalProcessorCount());
+            //diskstore
+            current.diskStore.setSerials(
+                    hardware.getDiskStores().stream().map(HWDiskStore::getSerial).distinct().sorted()
+                            .toArray(String[]::new));
+            //networkinterface
+            current.networkInterface.setMacAddress(
+                    hardware.getNetworkIFs().stream().map(NetworkIF::getMacaddr).distinct().sorted()
+                            .toArray(String[]::new));
+        }
+        return current;
+    }
+
+    /**
+     * 构造设备唯一标识,格式为长度32的字符串
+     *
+     * @return
+     */
+    public String uuid() {
+        List<String> list = new ArrayList<>();
+        list.add("computer.serialNumber=" + StringUtils.trimToEmpty(computer.getSerialNumber()));
+        list.add("baseboard.serialNumber=" + StringUtils.trimToEmpty(baseboard.getSerialNumber()));
+        list.add("processor.id=" + StringUtils.trimToEmpty(processor.getId()));
+        list.add("diskStore.serials=" + StringUtils.trimToEmpty(StringUtils.join(diskStore.getSerials(), ",")));
+        list.add("networkInterface.macAddress=" + StringUtils
+                .trimToEmpty(StringUtils.join(networkInterface.getMacAddress(), ",")));
+        return ByteArray.md5(StringUtils.join(list, "\n")).toHexString();
+    }
+
+    public Computer getComputer() {
+        return computer;
+    }
+
+    public Baseboard getBaseboard() {
+        return baseboard;
+    }
+
+    public DiskStore getDiskStore() {
+        return diskStore;
+    }
+
+    public Processor getProcessor() {
+        return processor;
+    }
+
+    public NetworkInterface getNetworkInterface() {
+        return networkInterface;
+    }
+}

+ 47 - 0
tools-device/src/main/java/com/qmth/boot/tools/device/entity/Baseboard.java

@@ -0,0 +1,47 @@
+package com.qmth.boot.tools.device.entity;
+
+/**
+ * 主板描述信息
+ */
+public class Baseboard {
+
+    private String manufacturer;
+
+    private String model;
+
+    private String version;
+
+    private String serialNumber;
+
+    public String getManufacturer() {
+        return manufacturer;
+    }
+
+    public void setManufacturer(String manufacturer) {
+        this.manufacturer = manufacturer;
+    }
+
+    public String getModel() {
+        return model;
+    }
+
+    public void setModel(String model) {
+        this.model = model;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getSerialNumber() {
+        return serialNumber;
+    }
+
+    public void setSerialNumber(String serialNumber) {
+        this.serialNumber = serialNumber;
+    }
+}

+ 37 - 0
tools-device/src/main/java/com/qmth/boot/tools/device/entity/Computer.java

@@ -0,0 +1,37 @@
+package com.qmth.boot.tools.device.entity;
+
+/**
+ * 整机描述信息
+ */
+public class Computer {
+
+    private String manufacturer;
+
+    private String model;
+
+    private String serialNumber;
+
+    public String getManufacturer() {
+        return manufacturer;
+    }
+
+    public void setManufacturer(String manufacturer) {
+        this.manufacturer = manufacturer;
+    }
+
+    public String getModel() {
+        return model;
+    }
+
+    public void setModel(String model) {
+        this.model = model;
+    }
+
+    public String getSerialNumber() {
+        return serialNumber;
+    }
+
+    public void setSerialNumber(String serialNumber) {
+        this.serialNumber = serialNumber;
+    }
+}

+ 17 - 0
tools-device/src/main/java/com/qmth/boot/tools/device/entity/DiskStore.java

@@ -0,0 +1,17 @@
+package com.qmth.boot.tools.device.entity;
+
+/**
+ * 磁盘描述信息
+ */
+public class DiskStore {
+
+    private String[] serials;
+
+    public String[] getSerials() {
+        return serials;
+    }
+
+    public void setSerials(String[] serials) {
+        this.serials = serials;
+    }
+}

+ 17 - 0
tools-device/src/main/java/com/qmth/boot/tools/device/entity/NetworkInterface.java

@@ -0,0 +1,17 @@
+package com.qmth.boot.tools.device.entity;
+
+/**
+ * 网卡描述信息
+ */
+public class NetworkInterface {
+
+    private String[] macAddress;
+
+    public String[] getMacAddress() {
+        return macAddress;
+    }
+
+    public void setMacAddress(String[] macAddress) {
+        this.macAddress = macAddress;
+    }
+}

+ 37 - 0
tools-device/src/main/java/com/qmth/boot/tools/device/entity/Processor.java

@@ -0,0 +1,37 @@
+package com.qmth.boot.tools.device.entity;
+
+/**
+ * 处理器描述信息
+ */
+public class Processor {
+
+    private String identifier;
+
+    private String id;
+
+    private int physicalCount;
+
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    public void setIdentifier(String identifier) {
+        this.identifier = identifier;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public int getPhysicalCount() {
+        return physicalCount;
+    }
+
+    public void setPhysicalCount(int physicalCount) {
+        this.physicalCount = physicalCount;
+    }
+}

+ 15 - 0
tools-device/src/test/java/com/qmth/boot/test/tools/device/DeviceInfoTest.java

@@ -0,0 +1,15 @@
+package com.qmth.boot.test.tools.device;
+
+import com.qmth.boot.tools.device.DeviceInfo;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class DeviceInfoTest {
+
+    @Test
+    public void test() {
+        String deviceId = DeviceInfo.current().uuid();
+        Assert.assertNotNull(deviceId);
+        Assert.assertEquals(deviceId.length(), 32);
+    }
+}

+ 1 - 1
tools-poi/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <artifactId>qmth-boot</artifactId>
         <groupId>com.qmth.boot</groupId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>tools-poi</artifactId>
     <name>tools-poi</name>

+ 7 - 3
tools-poi/src/main/java/com/qmth/boot/tools/excel/handler/impl/ObjectHandler.java

@@ -55,9 +55,13 @@ public class ObjectHandler<E> implements RowDataHandler {
             String value = rowData.getValue(i);
             if (param != null) {
                 try {
-                    if (value != null || !param.getAnnotation().nullable()) {
-                        param.getField()
-                                .set(obj, ValueConvertors.getConvertor(param.getField().getType()).parse(value, param));
+                    Object parseValue = value != null ?
+                            ValueConvertors.getConvertor(param.getField().getType()).parse(value, param) :
+                            null;
+                    if (parseValue == null && !param.getAnnotation().nullable()) {
+                        errorColumn.add(columnNames[i]);
+                    } else {
+                        param.getField().set(obj, parseValue);
                     }
                 } catch (Exception e) {
                     errorColumn.add(columnNames[i]);

+ 1 - 1
tools-signature/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <groupId>com.qmth.boot</groupId>
         <artifactId>qmth-boot</artifactId>
-        <version>1.0.1</version>
+        <version>1.0.2</version>
     </parent>
     <artifactId>tools-signature</artifactId>
     <name>tools-signature</name>