reghao 4 rokov pred
rodič
commit
5dade0737a
67 zmenil súbory, kde vykonal 3645 pridanie a 0 odobranie
  1. 6 0
      .gitignore
  2. 19 0
      jdk/pom.xml
  3. 129 0
      jdk/src/main/java/cn/reghao/jutil/jdk/clazz/PackageScanner.java
  4. 54 0
      jdk/src/main/java/cn/reghao/jutil/jdk/converter/ByteConverter.java
  5. 9 0
      jdk/src/main/java/cn/reghao/jutil/jdk/converter/ByteType.java
  6. 257 0
      jdk/src/main/java/cn/reghao/jutil/jdk/converter/DateTimeConverter.java
  7. 14 0
      jdk/src/main/java/cn/reghao/jutil/jdk/db/BaseCrud.java
  8. 19 0
      jdk/src/main/java/cn/reghao/jutil/jdk/db/BaseMapper.java
  9. 67 0
      jdk/src/main/java/cn/reghao/jutil/jdk/db/BaseObject.java
  10. 24 0
      jdk/src/main/java/cn/reghao/jutil/jdk/db/BaseQuery.java
  11. 31 0
      jdk/src/main/java/cn/reghao/jutil/jdk/db/Page.java
  12. 93 0
      jdk/src/main/java/cn/reghao/jutil/jdk/db/PageList.java
  13. 46 0
      jdk/src/main/java/cn/reghao/jutil/jdk/http/DlResponse.java
  14. 11 0
      jdk/src/main/java/cn/reghao/jutil/jdk/http/HttpDownloader.java
  15. 24 0
      jdk/src/main/java/cn/reghao/jutil/jdk/http/ResStatus.java
  16. 47 0
      jdk/src/main/java/cn/reghao/jutil/jdk/http/UploadParam.java
  17. 68 0
      jdk/src/main/java/cn/reghao/jutil/jdk/http/WebClient.java
  18. 17 0
      jdk/src/main/java/cn/reghao/jutil/jdk/http/WebRequest.java
  19. 35 0
      jdk/src/main/java/cn/reghao/jutil/jdk/http/WebResponse.java
  20. 28 0
      jdk/src/main/java/cn/reghao/jutil/jdk/http/proxy/ProxyType.java
  21. 29 0
      jdk/src/main/java/cn/reghao/jutil/jdk/http/proxy/RequestProxy.java
  22. 126 0
      jdk/src/main/java/cn/reghao/jutil/jdk/http/util/UrlFormatter.java
  23. 40 0
      jdk/src/main/java/cn/reghao/jutil/jdk/http/util/UserAgents.java
  24. 10 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/MachineData.java
  25. 39 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/detail/CpuDetail.java
  26. 77 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/detail/DiskDetail.java
  27. 67 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/detail/MachineDetail.java
  28. 58 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/detail/MemoryDetail.java
  29. 55 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/detail/NetworkDetail.java
  30. 50 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/detail/OsDetail.java
  31. 8 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/stat/CpuStat.java
  32. 8 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/stat/DiskStat.java
  33. 43 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/stat/MachineStat.java
  34. 8 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/stat/MemoryStat.java
  35. 8 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/stat/NetworkStat.java
  36. 8 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/stat/OsStat.java
  37. 27 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/id/MachineId.java
  38. 77 0
      jdk/src/main/java/cn/reghao/jutil/jdk/machine/id/MachineIdLinux.java
  39. 66 0
      jdk/src/main/java/cn/reghao/jutil/jdk/result/Result.java
  40. 29 0
      jdk/src/main/java/cn/reghao/jutil/jdk/result/ResultStatus.java
  41. 81 0
      jdk/src/main/java/cn/reghao/jutil/jdk/result/WebBody.java
  42. 55 0
      jdk/src/main/java/cn/reghao/jutil/jdk/result/WebBody1.java
  43. 17 0
      jdk/src/main/java/cn/reghao/jutil/jdk/security/Base64Util.java
  44. 12 0
      jdk/src/main/java/cn/reghao/jutil/jdk/security/Cryptor.java
  45. 32 0
      jdk/src/main/java/cn/reghao/jutil/jdk/security/Md5Cryptor.java
  46. 32 0
      jdk/src/main/java/cn/reghao/jutil/jdk/security/Md5Util.java
  47. 21 0
      jdk/src/main/java/cn/reghao/jutil/jdk/security/Salt.java
  48. 32 0
      jdk/src/main/java/cn/reghao/jutil/jdk/security/Sha256Cryptor.java
  49. 38 0
      jdk/src/main/java/cn/reghao/jutil/jdk/serializer/JsonArrayDeserializer.java
  50. 97 0
      jdk/src/main/java/cn/reghao/jutil/jdk/serializer/JsonConverter.java
  51. 30 0
      jdk/src/main/java/cn/reghao/jutil/jdk/serializer/LocalDateTimeAdapter.java
  52. 104 0
      jdk/src/main/java/cn/reghao/jutil/jdk/shell/ShellExecutor.java
  53. 42 0
      jdk/src/main/java/cn/reghao/jutil/jdk/shell/ShellResult.java
  54. 215 0
      jdk/src/main/java/cn/reghao/jutil/jdk/text/TextFile.java
  55. 43 0
      jdk/src/main/java/cn/reghao/jutil/jdk/util/SingleInstance.java
  56. 28 0
      pom.xml
  57. 54 0
      tool/pom.xml
  58. 146 0
      tool/src/main/java/cn/reghao/jutil/tool/http/BaseWebRequest.java
  59. 212 0
      tool/src/main/java/cn/reghao/jutil/tool/http/DefaultHttpDownloader.java
  60. 128 0
      tool/src/main/java/cn/reghao/jutil/tool/http/DefaultWebRequest.java
  61. 99 0
      tool/src/main/java/cn/reghao/jutil/tool/http/JdkCrawlRequest.java
  62. 18 0
      tool/src/main/java/cn/reghao/jutil/tool/http/util/FakeDnsResolver.java
  63. 32 0
      tool/src/main/java/cn/reghao/jutil/tool/http/util/MyConnectionSocketFactory.java
  64. 75 0
      tool/src/main/java/cn/reghao/jutil/tool/http/util/MyRetryHandler.java
  65. 38 0
      tool/src/main/java/cn/reghao/jutil/tool/http/util/MySSLConnectionSocketFactory.java
  66. 40 0
      tool/src/main/java/cn/reghao/jutil/tool/id/IdGenerator.java
  67. 93 0
      tool/src/main/java/cn/reghao/jutil/tool/id/SnowFlake.java

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+
+.idea/
+
+tool/target/
+
+jdk/target/

+ 19 - 0
jdk/pom.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>jutil</artifactId>
+        <groupId>cn.reghao.jutil</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>jdk</artifactId>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+</project>

+ 129 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/clazz/PackageScanner.java

@@ -0,0 +1,129 @@
+package cn.reghao.jutil.jdk.clazz;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * 扫描指定包路径下的所有 class 文件
+ *
+ * @author reghao
+ * @date 2020-09-24 15:41:07
+ */
+public class PackageScanner {
+    public Class findClassBySimpleName(Class<?> baseClass, String simpleName, String pkgPath) throws IOException, ClassNotFoundException {
+        List<Class<?>> classList = doScan(baseClass, pkgPath);
+        for (Class<?> clazz : classList) {
+            if (clazz.getSimpleName().equals(simpleName)) {
+                return clazz;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 扫描指定包中的所有类
+     *
+     * @param clazz 启动类
+     * @param basePackage 待扫描的包路径
+     * @return
+     * @date 2021-12-16 上午11:54
+     */
+    @Deprecated
+    public List<Class<?>> doScan(Class<?> clazz, String basePackage) throws IOException, ClassNotFoundException {
+        List<Class<?>> classList = new ArrayList<>();
+        return classList;
+    }
+
+    /**
+     * 扫描指定包中的所有类
+     *
+     * @param basePackage 指定的包路径
+     * @return
+     * @date 2021-12-21 下午3:41
+     */
+    public List<Class<?>> doScan(String basePackage) throws IOException, ClassNotFoundException {
+        List<Class<?>> classList = new ArrayList<>();
+        String baseFilePath = basePackage.replace(".", "/");
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        URL url = cl.getResource(baseFilePath);
+        assert url != null;
+        File file = new File(rootPath(url));
+        String protocol = url.getProtocol();
+        if ("jar".equals(protocol)) {
+            readFromJarFile(url, basePackage, classList);
+        } else if ("file".equals(protocol)) {
+            if (file.isDirectory()) {
+                readFromDir(file, classList);
+            } else {
+                addClazz(file.getAbsolutePath(), classList);
+            }
+        }
+        return classList;
+    }
+
+    private String rootPath(URL url) {
+        String path = url.getPath();
+        int pos = path.indexOf("!");
+        if (pos == -1) {
+            return path.replace("file:", "");
+        } else {
+            return path.substring(0, pos).replace("file:", "");
+        }
+    }
+
+    private void readFromDir(File dir, List<Class<?>> classList) {
+        File[] files = dir.listFiles();
+        assert files != null;
+        Arrays.asList(files).forEach(file -> {
+            if (file.isDirectory()) {
+                readFromDir(file, classList);
+            } else {
+                String path = file.getPath();
+                addClazz(path, classList);
+            }
+        });
+    }
+
+    private void addClazz(String filepath, List<Class<?>> classList) {
+        String tmp = filepath.split("target/classes/")[1];
+        String className = tmp.replace("/", ".").replace(".class", "");
+        try {
+            classList.add(Class.forName(className));
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 读取 jar 包中的 class 文件
+     *
+     * @param
+     * @return
+     * @date 2021-09-08 下午3:30
+     */
+    private void readFromJarFile(URL url, String basePackage, List<Class<?>> classList)
+            throws IOException, ClassNotFoundException {
+        JarFile jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
+        Enumeration<JarEntry> entries = jarFile.entries();
+        while (entries.hasMoreElements()) {
+            JarEntry jarEntry = entries.nextElement();
+            String name = jarEntry.getName();
+            String className = name.replace("/", ".");
+            if (className.startsWith(basePackage) && className.endsWith(".class")) {
+                String clazz = className.replace(".class", "");
+                // 使用当前线程的类加载器加载类
+                Class<?> loadClass = Thread.currentThread().getContextClassLoader().loadClass(clazz);
+                //Class<?> loadClass = Class.forName(clazz);
+                classList.add(loadClass);
+            }
+        }
+    }
+}

+ 54 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/converter/ByteConverter.java

@@ -0,0 +1,54 @@
+package cn.reghao.jutil.jdk.converter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 字节单位转换器
+ *
+ * @author reghao
+ * @date 2019-10-26 22:39:23
+ */
+public class ByteConverter {
+    private final Map<Integer, String> map = new HashMap<>();
+
+    public ByteConverter() {
+        for (ByteType byteType : ByteType.values()) {
+            map.put(byteType.ordinal(), byteType.name());
+        }
+    }
+
+    /**
+     * @date 2020-10-20 上午11:26
+     */
+    public String convert(ByteType byteType, long value) {
+        int base = 1024;
+        for (int i = byteType.ordinal(); i <= ByteType.TiB.ordinal(); i++) {
+            if (value < base) {
+                return value + map.get(i);
+            }
+            value = value >> 10;
+        }
+
+        if (value < base) {
+            return value + "PiB";
+        } else {
+            return "data too large...";
+        }
+    }
+
+    public long convert(ByteType src, ByteType dest, long value) {
+        for (int i = src.ordinal(); i < dest.ordinal(); i++) {
+            value = value >> 10;
+        }
+
+        return value;
+    }
+
+    public String convertStr(ByteType src, ByteType dest, long value) {
+        for (int i = src.ordinal(); i < dest.ordinal(); i++) {
+            value = value >> 10;
+        }
+        return value + dest.name();
+    }
+}

+ 9 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/converter/ByteType.java

@@ -0,0 +1,9 @@
+package cn.reghao.jutil.jdk.converter;
+
+/**
+ * @author reghao
+ * @date 2019-10-29 16:23:15
+ */
+public enum ByteType {
+    Bytes, KiB, MiB, GiB, TiB
+}

+ 257 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/converter/DateTimeConverter.java

@@ -0,0 +1,257 @@
+package cn.reghao.jutil.jdk.converter;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * 时间日期字符串格式转换器
+ *
+ * @author reghao
+ * @date 2020-03-20 10:20:01
+ */
+public class DateTimeConverter {
+    private final static String dateTimePattern = "yyyy-MM-dd HH:mm:ss";
+
+    /**
+     * 将 LocalDateTime 对象格式化为 yyyy-MM-dd HH:mm:ss 格式的字符串
+     *
+     * @param
+     * @return
+     * @date 2021-03-02 下午4:09
+     */
+    public static String format(LocalDateTime localDateTime) {
+        if (localDateTime == null) {
+            return "";
+        }
+
+        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(dateTimePattern);
+        return dtf.format(localDateTime);
+    }
+
+    /**
+     * 将 2021-02-23T13:24:51.519422470Z 格式化为 2021-02-23 13:24:51
+     *
+     * @param
+     * @return
+     * @date 2021-03-02 下午4:04
+     */
+    public static String format(String datetime) {
+        Instant instant = Instant.parse(datetime);
+        String zoneId = "Asia/Shanghai";
+        LocalDateTime localDateTime = instant.atZone(ZoneId.of(zoneId)).toLocalDateTime();
+        return format(localDateTime);
+    }
+
+    /**
+     * ms 时间戳格式化为 yyyy-MM-dd HH:mm:ss 格式的字符串
+     *
+     * @param
+     * @return
+     * @date 2021-03-02 下午5:05
+     */
+    public static String format(long timestamp) {
+        LocalDateTime localDateTime = localDateTime(timestamp);
+        return format(localDateTime);
+    }
+
+    public static String now() {
+        LocalDateTime now = LocalDateTime.now();
+        return format(now);
+    }
+
+    /**
+     * 将 2021-02-23T13:24:51.519422470Z 字符串转换为 LocalDateTime 对象
+     *
+     * @param
+     * @return
+     * @date 2021-03-02 下午4:13
+     */
+    public static LocalDateTime localDateTime(String datetime) {
+        Instant instant = Instant.parse(datetime);
+        String zoneId = "Asia/Shanghai";
+        return instant.atZone(ZoneId.of(zoneId)).toLocalDateTime();
+    }
+
+    /**
+     * @param
+     * @return
+     * @date 2021-03-09 上午3:30
+     */
+    public static LocalDateTime localDateTime1(String dateTime) throws ParseException {
+        // Mar 4, 2021, 11:01:52 PM 格式
+        String pattern = "MMM d, yyyy, h:m:s aa";
+        // Tue Mar 01 16:05:20 +0800 2022 格式
+        pattern = "EEE MMM d HH:mm:ss '+0800' yyyy";
+
+        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern, Locale.ENGLISH);
+        Date date = dateFormat.parse(dateTime);
+        Instant instant = date.toInstant();
+        return instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
+    }
+
+    public static LocalDateTime utc0ToUtc8(String dateTime) throws ParseException {
+        // Mar 4, 2021, 11:01:52 PM 格式
+        String pattern = "MMM d, yyyy, h:m:s aa";
+        // Sun Jan 26 16:43:14 +0000 2022 格式
+        pattern = "EEE MMM d HH:mm:ss '+0000' yyyy";
+
+        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern, Locale.ENGLISH);
+        Date date = dateFormat.parse(dateTime);
+        Instant instant = date.toInstant();
+        return instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
+    }
+
+    public static LocalDateTime localDateTime2(String dateTime) {
+        SimpleDateFormat dateFormat = new SimpleDateFormat(dateTimePattern);
+        try {
+            Date date = dateFormat.parse(dateTime);
+            Instant instant = date.toInstant();
+            return instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+
+        return LocalDateTime.now();
+    }
+
+    /**
+     * ms 时间戳转换为 LocalDateTime 对象
+     *
+     * @param
+     * @return
+     * @date 2021-03-02 下午4:59
+     */
+    public static LocalDateTime localDateTime(long timestamp) {
+        Instant instant = Instant.ofEpochMilli(timestamp);
+        String zoneId = "Asia/Shanghai";
+        ZonedDateTime dateTime = instant.atZone(ZoneId.of(zoneId));
+        return dateTime.toLocalDateTime();
+    }
+
+    /**
+     * 将 0 时区时间日期字符串转换为东 8 区时间日期字符串
+     *
+     * @param
+     * @return
+     * @date 2020-06-03 上午10:39
+     */
+    public static String convert(String dateTime) {
+        ZoneId zoneId = ZoneId.of("UTC+8");
+        ZonedDateTime zonedDateTime = Instant.parse(dateTime).atZone(zoneId);
+        return format(zonedDateTime.toLocalDateTime());
+    }
+
+    /**
+     * 2021-02-23T13:24:51.519422470Z
+     * 将日期时间转换为毫秒级时间戳
+     *
+     * @param
+     * @return
+     * @date 2021-02-25 上午11:00
+     */
+    public static long convertToTimestamp(String dateTime) {
+        Instant instant = Instant.parse(dateTime);
+        return instant.toEpochMilli();
+    }
+
+    /**
+     * LocalDateTime 转换为 ms 时间戳
+     *
+     * @param
+     * @return
+     * @date 2021-06-23 下午4:04
+     */
+    public static long msTimestamp(LocalDateTime localDateTime) {
+        String timeZone = "+8";
+        return localDateTime.toInstant(ZoneOffset.of(timeZone)).toEpochMilli();
+    }
+
+    /**
+     * 毫秒时间戳转换为日期时间
+     *
+     * @param
+     * @return
+     * @date 2020-03-20 上午10:20
+     */
+    public static String msTimestamp(long timestamp) {
+        Instant instant = Instant.ofEpochMilli(timestamp);
+        ZonedDateTime dateTime = instant.atZone(ZoneId.of("Asia/Shanghai"));
+        return dateTime.toLocalDateTime().toString();
+    }
+
+    /**
+     * 毫秒时间戳转换为指定格式的日期时间字符串
+     *
+     * @param
+     * @return
+     * @date 2020-06-02 下午5:22
+     */
+    public static String msTimestampFormat(long timestamp) {
+        Instant instant = Instant.ofEpochMilli(timestamp);
+        ZonedDateTime dateTime = instant.atZone(ZoneId.of("Asia/Shanghai"));
+        LocalDateTime localDateTime = dateTime.toLocalDateTime();
+        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        return dtf.format(localDateTime);
+    }
+
+    /**
+     * 秒时间戳转换为日期时间
+     *
+     * @param
+     * @return
+     * @date 2020-03-20 上午10:20
+     */
+    public static String timestamp(int timestamp) {
+        Instant instant = Instant.ofEpochMilli(timestamp);
+        ZonedDateTime dateTime = instant.atZone(ZoneId.of("Asia/Shanghai"));
+        return dateTime.toLocalDateTime().toString();
+    }
+
+    public static long duration(LocalDateTime future) {
+        LocalDateTime now = LocalDateTime.now();
+        long second = Duration.between(now, future).getSeconds();
+        return second * 1000;
+    }
+
+    /**
+     * 将 hh:mm:ss 转换为秒长度
+     *
+     * @param
+     * @return
+     * @date 2021-06-09 下午10:16
+     */
+    public static int toSecond(String len) {
+        String[] arr = len.split(":");
+        int h = 0, m = 0, s = 0;
+        if (arr.length == 2) {
+            s = Integer.parseInt(arr[1]);
+            m = convert(arr[0], 60);
+            return m + s;
+        } else if (arr.length == 3) {
+            s = Integer.parseInt(arr[2]);
+            m = convert(arr[1], 60);
+            h = convert(arr[0], 3600);
+            return h + m + s;
+        } else {
+            return 0;
+        }
+    }
+
+    private static int convert(String str, int base) {
+        int res = 0;
+        if (!str.equals("00")) {
+            if (str.startsWith("0")) {
+                // 03:22
+                res = Integer.parseInt(str.split("0")[1])*base;
+            } else {
+                // 10:23
+                res = Integer.parseInt(str)*base;
+            }
+        }
+        return res;
+    }
+}

+ 14 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/db/BaseCrud.java

@@ -0,0 +1,14 @@
+package cn.reghao.jutil.jdk.db;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2021-09-10 18:55:38
+ */
+public interface BaseCrud<T> {
+    T save(T t);
+    void saveAll(List<T> list);
+    void update(T t);
+    void delete(T t);
+}

+ 19 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/db/BaseMapper.java

@@ -0,0 +1,19 @@
+package cn.reghao.jutil.jdk.db;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2021-09-10 23:34:39
+ */
+public interface BaseMapper<T> {
+    int save(T t);
+    void saveAll(List<T> list);
+    void update(T t);
+    void delete(T t);
+
+    int count();
+    List<T> findAll();
+    List<T> findAllByPage(int page, int size);
+    T findById(int id);
+}

+ 67 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/db/BaseObject.java

@@ -0,0 +1,67 @@
+package cn.reghao.jutil.jdk.db;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author reghao
+ * @date 2019-10-18 14:42:48
+ */
+public class BaseObject<T> implements Serializable {
+    private static final long serialVersionUID = 1L;
+    protected T id;
+    // 逻辑删除
+    private Boolean deleted;
+    @Deprecated
+    private Boolean isDelete;
+    protected LocalDateTime createTime;
+    protected LocalDateTime updateTime;
+
+    public BaseObject() {
+        this.deleted = false;
+        this.isDelete = false;
+        LocalDateTime now = LocalDateTime.now();
+        this.createTime = now;
+        this.updateTime = now;
+    }
+
+    public T getId() {
+        return id;
+    }
+
+    public void setId(T id) {
+        this.id = id;
+    }
+
+    public Boolean getDeleted() {
+        return deleted;
+    }
+
+    public void setDeleted(Boolean deleted) {
+        this.deleted = deleted;
+    }
+
+    public Boolean getDelete() {
+        return isDelete;
+    }
+
+    public void setDelete(Boolean delete) {
+        isDelete = delete;
+    }
+
+    public LocalDateTime getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(LocalDateTime createTime) {
+        this.createTime = createTime;
+    }
+
+    public LocalDateTime getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(LocalDateTime updateTime) {
+        this.updateTime = updateTime;
+    }
+}

+ 24 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/db/BaseQuery.java

@@ -0,0 +1,24 @@
+package cn.reghao.jutil.jdk.db;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2021-07-12 15:32:26
+ */
+public interface BaseQuery<T> {
+    default int count() {
+        return 0;
+    }
+
+    default PageList<T> findAllByPage(int page, int size) {
+        return PageList.empty();
+    }
+    default List<T> findAll() {
+        return Collections.emptyList();
+    }
+    default T findById(int id) {
+        return null;
+    }
+}

+ 31 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/db/Page.java

@@ -0,0 +1,31 @@
+package cn.reghao.jutil.jdk.db;
+
+/**
+ * @author reghao
+ * @date 2021-08-08 20:35:32
+ */
+public class Page {
+    private int page;
+    private int size;
+
+    public Page(int page, int size) {
+        this.page = page;
+        this.size = size;
+    }
+
+    public int getPage() {
+        return page;
+    }
+
+    public void setPage(int page) {
+        this.page = page;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    public void setSize(int size) {
+        this.size = size;
+    }
+}

+ 93 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/db/PageList.java

@@ -0,0 +1,93 @@
+package cn.reghao.jutil.jdk.db;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 分页列表
+ *
+ * @author reghao
+ * @date 2020-03-05 17:39:53
+ */
+public class PageList<T> {
+    // 当前页
+    private int page;
+    // 当前页大小
+    private int size;
+    // 根据当前页大小计算得出的总页数
+    private int pages;
+    // 总记录数量
+    private int total;
+    // 每页大小
+    @Deprecated
+    private int pageSize;
+    // 总数量
+    @Deprecated
+    private long totalSize;
+    // 总页数
+    @Deprecated
+    private int totalPages;
+    // 是否最后一页
+    private boolean hasNext;
+    private List<T> list;
+
+    public int getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(int pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public long getTotalSize() {
+        return totalSize;
+    }
+
+    public void setTotalSize(long totalSize) {
+        this.totalSize = totalSize;
+    }
+
+    public int getTotalPages() {
+        return totalPages;
+    }
+
+    public void setTotalPages(int totalPages) {
+        this.totalPages = totalPages;
+    }
+
+    public boolean isHasNext() {
+        return hasNext;
+    }
+
+    public void setHasNext(boolean hasNext) {
+        this.hasNext = hasNext;
+    }
+
+    public List<T> getList() {
+        return list;
+    }
+
+    public void setList(List<T> list) {
+        this.list = list;
+    }
+
+    public static <T> PageList<T> empty() {
+        PageList<T> pageList = new PageList<>();
+        pageList.setPageSize(0);
+        pageList.setTotalSize(0);
+        pageList.setTotalPages(0);
+        pageList.setHasNext(false);
+        pageList.setList(new ArrayList<>());
+        return pageList;
+    }
+
+    public static <T> PageList<T> pageList(List<T> list) {
+        PageList<T> pageList = new PageList<>();
+        pageList.setPageSize(list.size());
+        pageList.setTotalSize(list.size());
+        pageList.setTotalPages(list.size());
+        pageList.setHasNext(true);
+        pageList.setList(list);
+        return pageList;
+    }
+}

+ 46 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/http/DlResponse.java

@@ -0,0 +1,46 @@
+package cn.reghao.jutil.jdk.http;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * @author reghao
+ * @date 2021-03-21 03:32:11
+ */
+public class DlResponse {
+    private final int statusCode;
+    // byte
+    private final long contentLength;
+    // ms
+    private final long costTime;
+    private final String contentRange;
+    private final ByteArrayOutputStream result;
+
+    public DlResponse(int statusCode, long contentLength, long costTime,
+                      String contentRange, ByteArrayOutputStream result) {
+        this.statusCode = statusCode;
+        this.contentLength = contentLength;
+        this.costTime = costTime;
+        this.contentRange = contentRange;
+        this.result = result;
+    }
+
+    public int getStatusCode() {
+        return statusCode;
+    }
+
+    public long getContentLength() {
+        return contentLength;
+    }
+
+    public long getCostTime() {
+        return costTime;
+    }
+
+    public String getContentRange() {
+        return contentRange;
+    }
+
+    public ByteArrayOutputStream getResult() {
+        return result;
+    }
+}

+ 11 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/http/HttpDownloader.java

@@ -0,0 +1,11 @@
+package cn.reghao.jutil.jdk.http;
+
+/**
+ * @author reghao
+ * @date 2021-07-31 22:38:08
+ */
+public interface HttpDownloader {
+    int head(String url);
+    DlResponse download(String url);
+    boolean download(String url, String dir);
+}

+ 24 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/http/ResStatus.java

@@ -0,0 +1,24 @@
+package cn.reghao.jutil.jdk.http;
+
+/**
+ * HTTP 资源状态
+ *
+ * @author reghao
+ * @date 2021-11-19 14:50:18
+ */
+public enum ResStatus {
+    avail(200), notFound(404), notAvail(600);
+
+    private final int value;
+    ResStatus(int value) {
+        this.value = value;
+    }
+
+    public String getName() {
+        return this.name();
+    }
+
+    public Integer getValue() {
+        return value;
+    }
+}

+ 47 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/http/UploadParam.java

@@ -0,0 +1,47 @@
+package cn.reghao.jutil.jdk.http;
+
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * @author reghao
+ * @date 2021-12-28 13:06:23
+ */
+public class UploadParam {
+    private final String filePath;
+    private final InputStream inputStream;
+    private final String mimeType;
+    private Map<String, String> textParams;
+
+    public UploadParam(String filePath, String mimeType) {
+        this.filePath = filePath;
+        this.inputStream = null;
+        this.mimeType = mimeType;
+    }
+
+    public UploadParam(InputStream inputStream, String mimeType) {
+        this.filePath = null;
+        this.inputStream = inputStream;
+        this.mimeType = mimeType;
+    }
+
+    public String getFilePath() {
+        return filePath;
+    }
+
+    public InputStream getInputStream() {
+        return inputStream;
+    }
+
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    public void setTextParams(Map<String, String> textParams) {
+        this.textParams = textParams;
+    }
+
+    public Map<String, String> getTextParams() {
+        return textParams;
+    }
+}

+ 68 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/http/WebClient.java

@@ -0,0 +1,68 @@
+package cn.reghao.jutil.jdk.http;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.Map;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 13:30:58
+ */
+public class WebClient implements WebRequest {
+    @Override
+    public int head(String url) {
+        HttpClient client = HttpClient.newHttpClient();
+        HttpRequest request = HttpRequest.newBuilder()
+                .uri(URI.create(url))
+                .build();
+        try {
+            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
+            return response.statusCode();
+        } catch (Exception e) {
+            return 0;
+        }
+    }
+
+    @Override
+    public WebResponse get(String url) {
+        HttpClient client = HttpClient.newHttpClient();
+        HttpRequest request = HttpRequest.newBuilder()
+                .uri(URI.create(url))
+                .build();
+        try {
+            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
+            return WebResponse.of(response);
+        } catch (Exception e) {
+            return WebResponse.error(e.getMessage());
+        }
+    }
+
+    @Override
+    public WebResponse postFormData(String url, Map<String, String> formData) {
+        return null;
+    }
+
+    @Override
+    public WebResponse postJson(String url, String json) {
+        HttpClient httpClient = HttpClient.newHttpClient();
+        HttpRequest httpRequest = HttpRequest.newBuilder()
+                .uri(URI.create(url))
+                .header("Content-Type", "application/json")
+                .POST(HttpRequest.BodyPublishers.ofString(json))
+                .build();
+
+        try {
+            HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+            return WebResponse.of(response);
+        } catch (Exception e) {
+            return WebResponse.error(e.getMessage());
+        }
+    }
+
+    @Override
+    public WebResponse upload(String url, UploadParam uploadParam) {
+        return null;
+    }
+}

+ 17 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/http/WebRequest.java

@@ -0,0 +1,17 @@
+package cn.reghao.jutil.jdk.http;
+
+import java.util.Map;
+
+/**
+ * HTTP 请求
+ *
+ * @author reghao
+ * @date 2019-08-01 16:27:55
+ */
+public interface WebRequest {
+    int head(String url);
+    WebResponse get(String url);
+    WebResponse postFormData(String url, Map<String, String> formData);
+    WebResponse postJson(String url, String json);
+    WebResponse upload(String url, UploadParam uploadParam);
+}

+ 35 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/http/WebResponse.java

@@ -0,0 +1,35 @@
+package cn.reghao.jutil.jdk.http;
+
+import java.net.http.HttpResponse;
+
+/**
+ * HTTP 响应
+ *
+ * @author reghao
+ * @date 2019-08-01 16:27:55
+ */
+public class WebResponse {
+    private final int statusCode;
+    private final String body;
+
+    public WebResponse(int statusCode, String body) {
+        this.statusCode = statusCode;
+        this.body = body;
+    }
+
+    public static WebResponse of(HttpResponse<String> response) {
+        return new WebResponse(response.statusCode(), response.body());
+    }
+
+    public static WebResponse error(String error) {
+        return new WebResponse(600, error);
+    }
+
+    public int getStatusCode() {
+        return statusCode;
+    }
+
+    public String getBody() {
+        return body;
+    }
+}

+ 28 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/http/proxy/ProxyType.java

@@ -0,0 +1,28 @@
+package cn.reghao.jutil.jdk.http.proxy;
+
+/**
+ * HTTP 请求代理类型
+ *
+ * @author reghao
+ * @date 2019-11-01 12:49:28
+ */
+public enum ProxyType {
+    // HTTP 代理
+    HTTP("http"),
+    // HTTPS 代理
+    HTTPS("https"),
+    // SOCKS4 代理
+    SOCKS4("socks4"),
+    // SOCKS5 代理
+    SOCKS5("socks5");
+
+    private String value;
+
+    ProxyType(String value) {
+        this.value = value;
+    }
+
+    public String getValue() {
+        return value;
+    }
+}

+ 29 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/http/proxy/RequestProxy.java

@@ -0,0 +1,29 @@
+package cn.reghao.jutil.jdk.http.proxy;
+
+/**
+ * HTTP 请求代理
+ *
+ * @author reghao
+ * @date 2019-12-17 13:21:38
+ */
+public class RequestProxy {
+    private ProxyType type;
+    private String host;
+    private int port;
+    private String usrename;
+    private String password;
+
+    public RequestProxy(String host, int port) {
+        this.host = host;
+        this.port = port;
+        this.type = ProxyType.SOCKS5;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+}

+ 126 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/http/util/UrlFormatter.java

@@ -0,0 +1,126 @@
+package cn.reghao.jutil.jdk.http.util;
+
+/**
+ * @author reghao
+ * @date 2021-03-15 12:33:44
+ */
+public class UrlFormatter {
+    /**
+     * 从 http/https url 中提取出域名
+     * https://twitter.com/Milkytutu/status/1264213127396114433 -> https://twitter.com
+     *
+     * @param
+     * @return
+     * @date 2021-03-15 下午12:13
+     */
+    public static String getDomain(String url) {
+        if (url.startsWith("https://")) {
+            String host = url.split("https://")[1].split("/")[0];
+            return "https://" + host;
+        } else {
+            String host = url.split("http://")[1].split("/")[0];
+            return "http://" + host;
+        }
+    }
+
+    /**
+     * 从 http/https url 中提取出主机
+     * https://twitter.com/Milkytutu/status/1264213127396114433 -> twitter.com
+     *
+     * @param
+     * @return
+     * @date 2021-03-15 下午12:13
+     */
+    public static String getHost(String url) {
+        if (url.startsWith("https://")) {
+            return url.split("https://")[1].split("/")[0];
+        } else {
+            return url.split("http://")[1].split("/")[0];
+        }
+    }
+
+    /**
+     * https://cdn.91p07.com//m3u8/442614/442614.m3u8?st=xHX459guiAmx5CsRJbLXyQ&e=1615796818
+     * -> https://cdn.91p07.com//m3u8/442614
+     *
+     * @param
+     * @return
+     * @date 2021-03-15 下午2:31
+     */
+    public static String getPrefix(String url) {
+        String noParamsUrl = url.split("\\?")[0];
+        return noParamsUrl.substring(0, noParamsUrl.lastIndexOf("/"));
+    }
+
+    /**
+     * https://cdn.91p07.com//m3u8/442614/442614.m3u8?st=xHX459guiAmx5CsRJbLXyQ&e=1615796818
+     * -> https://cdn.91p07.com//m3u8/442614/442614.m3u8
+     *
+     * @param
+     * @return
+     * @date 2021-03-15 下午2:33
+     */
+    public static String getUrlWithNoParams(String url) {
+        return url.split("\\?")[0];
+    }
+
+    /**
+     * 从 url 中取出指定参数的值
+     * http://91porn.com/uprofile.php?UID=b583fKPwaC8HuUDsIRbfEq9scGe1J90POTljHJwUJ998WRZq -> UID
+     *
+     * @param
+     * @return
+     * @date 2021-03-15 下午12:38
+     */
+    public static String getParamValue(String url, String name) {
+        String params = url.toLowerCase().split("\\?")[1];
+        String lname = name.toLowerCase();
+        for (String kv : params.split("&")) {
+            if (kv.startsWith(lname)) {
+                return kv.split("=")[1];
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * https://video.twimg.com/ext_tw_video/1351933827732525056/pu/vid/3000/6000/320x544/RTM9oDMcLYnTfg_k.ts?name=value
+     * -> RTM9oDMcLYnTfg_k.ts
+     *
+     * @param
+     * @return
+     * @date 2021-03-15 下午9:36
+     */
+    public static String getFilename(String url) {
+        String noParamsUrl = url.split("\\?")[0];
+        int index = noParamsUrl.lastIndexOf("/");
+        return noParamsUrl.substring(index+1);
+    }
+
+    /**
+     * https://cdn.91p07.com//m3u8/442614/442614.m3u8?st=xHX459guiAmx5CsRJbLXyQ&e=1615796818 ->
+     * https://cdn.91p07.com//m3u8/442614/442614.m3u8
+     *
+     * @param
+     * @return
+     * @date 2021-11-26 上午11:17
+     */
+    public static String getUrlWithoutParam(String url) {
+        return url.split("\\?")[0];
+    }
+
+    /**
+     * 提取 uri
+     * http://static.reghao.icu/tnb/vid/img/vidcover/eY9G0Zpnqe.jpg -> /tnb/vid/img/vidcover/eY9G0Zpnqe.jpg
+     *
+     * @param
+     * @return
+     * @date 2021-11-24 上午9:47
+     */
+    public static String getUri(String url) {
+        String url1 = url.replaceFirst("^(http|https)://", "");
+        int idx = url1.indexOf("/");
+        return url1.substring(idx);
+    }
+}

+ 40 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/http/util/UserAgents.java

@@ -0,0 +1,40 @@
+package cn.reghao.jutil.jdk.http.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * User-Agent 池
+ *
+ * @author reghao
+ * @date 2019-07-23 10:44:45
+ */
+public class UserAgents {
+    private static List<String> mobileAgents = new ArrayList<>();
+    private static List<String> desktopAgents = new ArrayList<>();
+
+    static {
+        mobileAgents.add("Mozilla/5.0 (Linux; U; Android 9; zh-CN; NX629J Build/PKQ1.190321.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.9.1.1071 Mobile Safari/537.36");
+        mobileAgents.add("Mozilla/5.0 (Linux; U; Android 9; zh-cn; Redmi Note 8 Pro Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.141 Mobile Safari/537.36 XiaoMi/MiuiBrowser/11.10.8");
+        mobileAgents.add("Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/13.0 MQQBrowser/8.9.1 Mobile/15B87 Safari/604.1 MttCustomUA/2 QBWebViewType/1 WKType/1");
+
+        desktopAgents.add("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169");
+        desktopAgents.add("Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0Safari/537.36");
+        desktopAgents.add("Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko");
+    }
+
+    public static String getMobileAgent() {
+        int len = mobileAgents.size();
+        Random rand = new Random();
+        int index = rand.nextInt(len);
+        return mobileAgents.get(index);
+    }
+
+    public static String getDesktopAgent() {
+        int len = desktopAgents.size();
+        Random rand = new Random();
+        int index = rand.nextInt(len);
+        return desktopAgents.get(0);
+    }
+}

+ 10 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/MachineData.java

@@ -0,0 +1,10 @@
+package cn.reghao.jutil.jdk.machine.data;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 18:42:28
+ */
+public interface MachineData<T, K> {
+    T detail();
+    K stat();
+}

+ 39 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/detail/CpuDetail.java

@@ -0,0 +1,39 @@
+package cn.reghao.jutil.jdk.machine.data.detail;
+
+import java.io.Serializable;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 18:15:16
+ */
+public class CpuDetail implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String vendor;
+    private String name;
+    private int physicalCore;
+    private int logicalCore;
+
+    public CpuDetail(String vendor, String name, int physicalCore, int logicalCore) {
+        this.vendor = vendor;
+        this.name = name;
+        this.physicalCore = physicalCore;
+        this.logicalCore = logicalCore;
+    }
+
+    public String getVendor() {
+        return vendor;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getPhysicalCore() {
+        return physicalCore;
+    }
+
+    public int getLogicalCore() {
+        return logicalCore;
+    }
+}

+ 77 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/detail/DiskDetail.java

@@ -0,0 +1,77 @@
+package cn.reghao.jutil.jdk.machine.data.detail;
+
+import java.io.Serializable;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 18:15:16
+ */
+public class DiskDetail implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String diskPath;
+    private String mountedOn;
+    private String fsType;
+    // bytes
+    private long total;
+    private long avail;
+    private long used;
+    private long inodeTotal;
+    private long inodeAvail;
+
+    public DiskDetail(String diskPath, String mountedOn, String fsType, long total, long avail, long used,
+                      long inodeTotal, long inodeAvail) {
+        this.diskPath = diskPath;
+        this.mountedOn = mountedOn;
+        this.fsType = fsType;
+        this.total = total;
+        this.avail = avail;
+        this.used = used;
+        this.inodeTotal = inodeTotal;
+        this.inodeAvail = inodeAvail;
+    }
+
+    public String getDiskPath() {
+        return diskPath;
+    }
+
+    public String getMountedOn() {
+        return mountedOn;
+    }
+
+    public String getFsType() {
+        return fsType;
+    }
+
+    public long getTotal() {
+        return total;
+    }
+
+    public void setAvail(long avail) {
+        this.avail = avail;
+    }
+
+    public long getAvail() {
+        return avail;
+    }
+
+    public void setUsed(long used) {
+        this.used = used;
+    }
+
+    public long getUsed() {
+        return used;
+    }
+
+    public long getInodeTotal() {
+        return inodeTotal;
+    }
+
+    public void setInodeAvail(long inodeAvail) {
+        this.inodeAvail = inodeAvail;
+    }
+
+    public long getInodeAvail() {
+        return inodeAvail;
+    }
+}

+ 67 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/detail/MachineDetail.java

@@ -0,0 +1,67 @@
+package cn.reghao.jutil.jdk.machine.data.detail;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 19:15:34
+ */
+public class MachineDetail implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String machineId;
+    private OsDetail osDetail;
+    private List<NetworkDetail> networkDetails;
+    private CpuDetail cpuDetail;
+    private MemoryDetail memoryDetail;
+    private List<DiskDetail> diskDetails;
+
+    public void setMachineId(String machineId) {
+        this.machineId = machineId;
+    }
+
+    public String getMachineId() {
+        return machineId;
+    }
+
+    public void setOsDetail(OsDetail osDetail) {
+        this.osDetail = osDetail;
+    }
+
+    public OsDetail getOsDetail() {
+        return osDetail;
+    }
+
+    public void setNetworkDetails(List<NetworkDetail> networkDetails) {
+        this.networkDetails = networkDetails;
+    }
+
+    public List<NetworkDetail> getNetworkDetails() {
+        return networkDetails;
+    }
+
+    public void setCpuDetail(CpuDetail cpuDetail) {
+        this.cpuDetail = cpuDetail;
+    }
+
+    public CpuDetail getCpuDetail() {
+        return cpuDetail;
+    }
+
+    public void setMemoryDetail(MemoryDetail memoryDetail) {
+        this.memoryDetail = memoryDetail;
+    }
+
+    public MemoryDetail getMemoryDetail() {
+        return memoryDetail;
+    }
+
+    public void setDiskDetails(List<DiskDetail> diskDetails) {
+        this.diskDetails = diskDetails;
+    }
+
+    public List<DiskDetail> getDiskDetails() {
+        return diskDetails;
+    }
+}

+ 58 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/detail/MemoryDetail.java

@@ -0,0 +1,58 @@
+package cn.reghao.jutil.jdk.machine.data.detail;
+
+import java.io.Serializable;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 18:15:16
+ */
+public class MemoryDetail implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    // byte
+    private long total;
+    private long avail;
+    private long used;
+    private long swapTotal;
+    private long swapAvail;
+
+    public MemoryDetail(long total, long avail, long used, long swapTotal, long swapAvail) {
+        this.total = total;
+        this.avail = avail;
+        this.used = used;
+        this.swapTotal = swapTotal;
+        this.swapAvail = swapAvail;
+    }
+
+    public long getTotal() {
+        return total;
+    }
+
+    public void setAvail(long avail) {
+        this.avail = avail;
+    }
+
+    public long getAvail() {
+        return avail;
+    }
+
+    public void setUsed(long used) {
+        this.used = used;
+    }
+
+    public long getUsed() {
+        return used;
+    }
+
+    public long getSwapTotal() {
+        return swapTotal;
+    }
+
+    public void setSwapAvail(long swapAvail) {
+        this.swapAvail = swapAvail;
+    }
+
+    public long getSwapAvail() {
+        return swapAvail;
+    }
+}

+ 55 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/detail/NetworkDetail.java

@@ -0,0 +1,55 @@
+package cn.reghao.jutil.jdk.machine.data.detail;
+
+import java.io.Serializable;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 18:15:16
+ */
+public class NetworkDetail implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String iface;
+    // 格式 e8-2a-ea-7c-4a-a2
+    private String mac;
+    private String ipv4;
+    private String pubicIpv4;
+    private String ipv6;
+
+    public NetworkDetail(String iface, String mac) {
+        this.iface = iface;
+        this.mac = mac;
+    }
+
+    public String getIface() {
+        return iface;
+    }
+
+    public String getMac() {
+        return mac;
+    }
+
+    public void setIpv4(String ipv4) {
+        this.ipv4 = ipv4;
+    }
+
+    public String getIpv4() {
+        return ipv4;
+    }
+
+    public void setPubicIpv4(String pubicIpv4) {
+        this.pubicIpv4 = pubicIpv4;
+    }
+
+    public String getPubicIpv4() {
+        return pubicIpv4;
+    }
+
+    public void setIpv6(String ipv6) {
+        this.ipv6 = ipv6;
+    }
+
+    public String getIpv6() {
+        return ipv6;
+    }
+}

+ 50 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/detail/OsDetail.java

@@ -0,0 +1,50 @@
+package cn.reghao.jutil.jdk.machine.data.detail;
+
+import java.io.Serializable;
+
+/**
+ * @author reghao
+ * @date 2020-10-20 23:17:30
+ */
+public class OsDetail implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String arch;
+    private String name;
+    // 系统版本或内核版本
+    private String version;
+    private String byteOrder;
+    private long bootTime;
+
+    public OsDetail() {
+        this.arch = System.getProperty("os.arch");
+        this.name = System.getProperty("os.name");
+        this.version = System.getProperty("os.version");
+        this.byteOrder = System.getProperty("sun.cpu.endian");
+        this.bootTime = 0;
+    }
+
+    public String getArch() {
+        return arch;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public String getByteOrder() {
+        return byteOrder;
+    }
+
+    public void setBootTime(long bootTime) {
+        this.bootTime = bootTime;
+    }
+
+    public long getBootTime() {
+        return bootTime;
+    }
+}

+ 8 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/stat/CpuStat.java

@@ -0,0 +1,8 @@
+package cn.reghao.jutil.jdk.machine.data.stat;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 18:44:01
+ */
+public class CpuStat {
+}

+ 8 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/stat/DiskStat.java

@@ -0,0 +1,8 @@
+package cn.reghao.jutil.jdk.machine.data.stat;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 18:44:09
+ */
+public class DiskStat {
+}

+ 43 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/stat/MachineStat.java

@@ -0,0 +1,43 @@
+package cn.reghao.jutil.jdk.machine.data.stat;
+
+import cn.reghao.jutil.jdk.machine.data.detail.DiskDetail;
+import cn.reghao.jutil.jdk.machine.data.detail.MemoryDetail;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 19:19:01
+ */
+public class MachineStat implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String machineId;
+    private MemoryDetail memoryDetail;
+    private List<DiskDetail> diskDetails;
+
+    public void setMachineId(String machineId) {
+        this.machineId = machineId;
+    }
+
+    public String getMachineId() {
+        return machineId;
+    }
+
+    public void setMemoryDetail(MemoryDetail memoryDetail) {
+        this.memoryDetail = memoryDetail;
+    }
+
+    public MemoryDetail getMemoryDetail() {
+        return memoryDetail;
+    }
+
+    public void setDiskDetails(List<DiskDetail> diskDetails) {
+        this.diskDetails = diskDetails;
+    }
+
+    public List<DiskDetail> getDiskDetails() {
+        return diskDetails;
+    }
+}

+ 8 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/stat/MemoryStat.java

@@ -0,0 +1,8 @@
+package cn.reghao.jutil.jdk.machine.data.stat;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 18:44:17
+ */
+public class MemoryStat {
+}

+ 8 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/stat/NetworkStat.java

@@ -0,0 +1,8 @@
+package cn.reghao.jutil.jdk.machine.data.stat;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 18:44:24
+ */
+public class NetworkStat {
+}

+ 8 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/data/stat/OsStat.java

@@ -0,0 +1,8 @@
+package cn.reghao.jutil.jdk.machine.data.stat;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 18:44:30
+ */
+public class OsStat {
+}

+ 27 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/id/MachineId.java

@@ -0,0 +1,27 @@
+package cn.reghao.jutil.jdk.machine.id;
+
+/**
+ * 唯一标识一台机器
+ *
+ * @author reghao
+ * @date 2021-10-16 18:33:58
+ */
+public interface MachineId {
+    /**
+     * 机器全局唯一 ID
+     *
+     * @param
+     * @return
+     * @date 2021-10-16 下午6:34
+     */
+    String id();
+
+    /**
+     * 只能在一个网络内唯一标识一台机器
+     *
+     * @param
+     * @return
+     * @date 2021-10-16 下午6:35
+     */
+    String ipv4();
+}

+ 77 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/machine/id/MachineIdLinux.java

@@ -0,0 +1,77 @@
+package cn.reghao.jutil.jdk.machine.id;
+
+import java.io.*;
+import java.net.*;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2021-05-20 15:45:28
+ */
+public class MachineIdLinux implements MachineId {
+    private String machineId = "";
+    private String machineIpv4 = "127.0.0.1";
+
+    @Override
+    public String id() {
+        String noMachineId = "no-machine-id";
+        if (!machineId.isBlank() && !machineId.equals(noMachineId)) {
+            return machineId;
+        }
+
+        File file = new File("/etc/machine-id");
+        try {
+            BufferedReader in =  new BufferedReader(new InputStreamReader(new FileInputStream(file)));
+            machineId = in.readLine();
+            in.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        return machineId.isBlank() ? noMachineId : machineId;
+    }
+
+    @Override
+    public String ipv4() {
+        String localhost = "127.0.0.1";
+        if (!machineIpv4.equals(localhost)) {
+            return machineIpv4;
+        }
+
+        List<String> ipv4List = detail();
+        machineIpv4 = ipv4List.isEmpty() ? localhost : ipv4List.get(0);
+        return machineIpv4;
+    }
+
+    private List<String> detail() {
+        List<String> list = new ArrayList<>();
+        try {
+            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+            // 遍历主机的网络接口
+            while (interfaces.hasMoreElements()) {
+                NetworkInterface iface = interfaces.nextElement();
+                String ifaceName = iface.getName();
+                // 过滤掉 localhost 和虚拟网卡
+                if (ifaceName.startsWith("lo") || ifaceName.startsWith("docker")
+                        || ifaceName.startsWith("v") || ifaceName.startsWith("br")) {
+                    continue;
+                }
+                Enumeration<InetAddress> inetAddrs = iface.getInetAddresses();
+                while (inetAddrs.hasMoreElements()) {
+                    InetAddress address = inetAddrs.nextElement();
+                    if (!address.isLoopbackAddress()) {
+                        if (address instanceof Inet4Address) {
+                            list.add(address.getHostAddress());
+                        }
+                    }
+                }
+
+            }
+        } catch (SocketException e) {
+            e.printStackTrace();
+        }
+        return list;
+    }
+}

+ 66 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/result/Result.java

@@ -0,0 +1,66 @@
+package cn.reghao.jutil.jdk.result;
+
+import static cn.reghao.jutil.jdk.result.ResultStatus.*;
+
+
+/**
+ * 调用结果
+ *
+ * @author reghao
+ * @date 2021-05-21 16:25:02
+ */
+public class Result {
+    private int code;
+    private String msg;
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public static Result result(ResultStatus resultStatus) {
+        Result result = new Result();
+        result.setCode(resultStatus.getCode());
+        result.setMsg(resultStatus.getMsg());
+        return result;
+    }
+
+    public static Result result(ResultStatus resultStatus, String msg) {
+        Result result = new Result();
+        result.setCode(resultStatus.getCode());
+        result.setMsg(msg);
+        return result;
+    }
+
+    public static Result success(String data) {
+        Result result = new Result();
+        result.setCode(SUCCESS.getCode());
+        result.setMsg(data);
+        return result;
+    }
+
+    public static Result fail(String data) {
+        Result result = new Result();
+        result.setCode(FAIL.getCode());
+        result.setMsg(data);
+        return result;
+    }
+
+    public static Result error(String data) {
+        Result result = new Result();
+        result.setCode(ERROR.getCode());
+        result.setMsg(data);
+        return result;
+    }
+}

+ 29 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/result/ResultStatus.java

@@ -0,0 +1,29 @@
+package cn.reghao.jutil.jdk.result;
+
+/**
+ * 结果状态
+ *
+ * @author reghao
+ * @date 2019-10-17 16:18:25
+ */
+public enum ResultStatus {
+    SUCCESS(0, "成功"),
+    FAIL(1, "失败"),
+    ERROR(-1, "错误");
+
+    private int code;
+    private String msg;
+
+    ResultStatus(int code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+}

+ 81 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/result/WebBody.java

@@ -0,0 +1,81 @@
+package cn.reghao.jutil.jdk.result;
+
+import cn.reghao.jutil.jdk.converter.DateTimeConverter;
+import cn.reghao.jutil.jdk.serializer.JsonConverter;
+
+/**
+ * HTTP 响应 body 数据
+ *
+ * @author reghao
+ * @date 2019-09-28 10:05:35
+ */
+public class WebBody {
+    private int code;
+    private String msg;
+    private String timestamp;
+    private Object data;
+
+    private WebBody(Integer code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    private void setTimestamp(String timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    private void setData(Object data) {
+        this.data = data;
+    }
+
+    public static String success() {
+        WebBody webBody = new WebBody(ResultStatus.SUCCESS.getCode(), ResultStatus.SUCCESS.getMsg());
+        webBody.setTimestamp(DateTimeConverter.now());
+        return JsonConverter.objectToJson(webBody);
+    }
+
+    public static String successWithMsg(String msg) {
+        WebBody webBody = new WebBody(ResultStatus.SUCCESS.getCode(), msg);
+        webBody.setTimestamp(DateTimeConverter.now());
+        return JsonConverter.objectToJson(webBody);
+    }
+
+    public static String result(Result result) {
+        WebBody webBody = new WebBody(result.getCode(), result.getMsg());
+        webBody.setTimestamp(DateTimeConverter.now());
+        return JsonConverter.objectToJson(webBody);
+    }
+
+    public static String success(Object data) {
+        WebBody webBody = new WebBody(ResultStatus.SUCCESS.getCode(), ResultStatus.SUCCESS.getMsg());
+        webBody.setTimestamp(DateTimeConverter.now());
+        webBody.setData(data);
+        return JsonConverter.objectToJson(webBody);
+    }
+
+    public static String fail(Object data) {
+        WebBody webBody = new WebBody(ResultStatus.FAIL.getCode(), ResultStatus.FAIL.getMsg());
+        webBody.setTimestamp(DateTimeConverter.now());
+        webBody.setData(data);
+        return JsonConverter.objectToJson(webBody);
+    }
+
+    public static String failWithMsg(String msg) {
+        WebBody webBody = new WebBody(ResultStatus.FAIL.getCode(), msg);
+        webBody.setTimestamp(DateTimeConverter.now());
+        return JsonConverter.objectToJson(webBody);
+    }
+
+    public static String error(Object data) {
+        WebBody webBody = new WebBody(ResultStatus.ERROR.getCode(), ResultStatus.ERROR.getMsg());
+        webBody.setTimestamp(DateTimeConverter.now());
+        webBody.setData(data);
+        return JsonConverter.objectToJson(webBody);
+    }
+
+    public static String errorMsg(String msg) {
+        WebBody webBody = new WebBody(ResultStatus.ERROR.getCode(), msg);
+        webBody.setTimestamp(DateTimeConverter.now());
+        return JsonConverter.objectToJson(webBody);
+    }
+}

+ 55 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/result/WebBody1.java

@@ -0,0 +1,55 @@
+package cn.reghao.jutil.jdk.result;
+
+import cn.reghao.jutil.jdk.converter.DateTimeConverter;
+import cn.reghao.jutil.jdk.serializer.JsonConverter;
+
+/**
+ * HTTP 响应 body 数据
+ *
+ * @author reghao
+ * @date 2022-01-07 14:05:35
+ */
+public class WebBody1<T> {
+    private int code;
+    private String msg;
+    private String timestamp;
+    private T data;
+
+    private WebBody1(Integer code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    private void setTimestamp(String timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    private void setData(T data) {
+        this.data = data;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public static <T> String success(T data) {
+        WebBody1<T> webBody = new WebBody1<>(ResultStatus.SUCCESS.getCode(), ResultStatus.SUCCESS.getMsg());
+        webBody.setTimestamp(DateTimeConverter.now());
+        webBody.setData(data);
+        return JsonConverter.objectToJson(webBody);
+    }
+
+    public static <T> String fail(T data) {
+        WebBody1<T> webBody = new WebBody1<>(ResultStatus.FAIL.getCode(), ResultStatus.FAIL.getMsg());
+        webBody.setTimestamp(DateTimeConverter.now());
+        webBody.setData(data);
+        return JsonConverter.objectToJson(webBody);
+    }
+
+    public static <T> String error(T data) {
+        WebBody1<T> webBody = new WebBody1<>(ResultStatus.ERROR.getCode(), ResultStatus.ERROR.getMsg());
+        webBody.setTimestamp(DateTimeConverter.now());
+        webBody.setData(data);
+        return JsonConverter.objectToJson(webBody);
+    }
+}

+ 17 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/security/Base64Util.java

@@ -0,0 +1,17 @@
+package cn.reghao.jutil.jdk.security;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+/**
+ * @author reghao
+ * @date 2020-01-13 22:54:32
+ */
+public class Base64Util {
+    private static final Base64.Encoder encoder = Base64.getEncoder();
+
+    public static String encode(String json) {
+        byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
+        return encoder.encodeToString(bytes);
+    }
+}

+ 12 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/security/Cryptor.java

@@ -0,0 +1,12 @@
+package cn.reghao.jutil.jdk.security;
+
+/**
+ * 加密/解密
+ *
+ * @author reghao
+ * @date 2020-04-27 14:03:49
+ */
+public interface Cryptor {
+    String encrypt(String str);
+    String decrypt(String str);
+}

+ 32 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/security/Md5Cryptor.java

@@ -0,0 +1,32 @@
+package cn.reghao.jutil.jdk.security;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @author reghao
+ * @date 2019-03-26 14:46:57
+ */
+public class Md5Cryptor implements Cryptor {
+    private MessageDigest md5;
+
+    public Md5Cryptor() throws NoSuchAlgorithmException {
+        this.md5 = MessageDigest.getInstance("MD5");
+    }
+
+    @Override
+    public String encrypt(String str) {
+        byte[] bytes = md5.digest(str.getBytes());
+        StringBuilder sb = new StringBuilder();
+        for (byte aByte : bytes) {
+            sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1));
+        }
+
+        return sb.toString();
+    }
+
+    @Override
+    public String decrypt(String str) {
+        return null;
+    }
+}

+ 32 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/security/Md5Util.java

@@ -0,0 +1,32 @@
+package cn.reghao.jutil.jdk.security;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @author reghao
+ * @date 2020-01-15 17:09:07
+ */
+public class Md5Util {
+    public static String md5(String data) throws NoSuchAlgorithmException {
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        md.update(data.getBytes());
+
+        StringBuilder sb = new StringBuilder();
+        byte[] bytes = md.digest();
+        for (int i = 0; i < bytes.length; i++) {
+            int a = bytes[i];
+            if (a < 0) {
+                a += 256;
+            }
+
+            if (a < 16) {
+                sb.append("0");
+            }
+
+            sb.append(Integer.toHexString(a));
+        }
+
+        return sb.toString();
+    }
+}

+ 21 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/security/Salt.java

@@ -0,0 +1,21 @@
+package cn.reghao.jutil.jdk.security;
+
+import java.security.SecureRandom;
+import java.util.Base64;
+
+/**
+ * @author reghao
+ * @date 2019-04-05 12:23:47
+ */
+public class Salt {
+    private static SecureRandom random = new SecureRandom();
+
+    /**
+     * @return 盐值
+     * @date 2019-04-05 12:28:08
+     */
+    public static String get(int len) {
+        byte[] seed = random.generateSeed(len);
+        return Base64.getEncoder().encodeToString(seed);
+    }
+}

+ 32 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/security/Sha256Cryptor.java

@@ -0,0 +1,32 @@
+package cn.reghao.jutil.jdk.security;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @author reghao
+ * @date 2019-03-26 14:46:57
+ */
+public class Sha256Cryptor implements Cryptor {
+    private MessageDigest sha256;
+
+    public Sha256Cryptor() throws NoSuchAlgorithmException {
+        this.sha256 = MessageDigest.getInstance("SHA-256");
+    }
+
+    @Override
+    public String encrypt(String str) {
+        byte[] bytes = sha256.digest(str.getBytes());
+        StringBuilder sb = new StringBuilder();
+        for (byte aByte : bytes) {
+            sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1));
+        }
+
+        return sb.toString();
+    }
+
+    @Override
+    public String decrypt(String str) {
+        return null;
+    }
+}

+ 38 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/serializer/JsonArrayDeserializer.java

@@ -0,0 +1,38 @@
+package cn.reghao.jutil.jdk.serializer;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonParser;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JSON 数组反序列化
+ *
+ * @author reghao
+ * @date 2020-11-11 16:57:04
+ */
+public class JsonArrayDeserializer<T> {
+    private final Gson gson = new GsonBuilder()
+            .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter())
+            .create();
+
+    /**
+     * JSON 数组转换为对象集合
+     *
+     * @param
+     * @return
+     * @date 2020-11-11 下午5:14
+     */
+    public List<T> fromJsonArray(String json, Class<T> clazz) {
+        JsonParser parser = new JsonParser();
+        List<T> list = new ArrayList<>();
+        parser.parse(json).getAsJsonArray().forEach(ele -> {
+            list.add(gson.fromJson(ele, clazz));
+        });
+
+        return list;
+    }
+}

+ 97 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/serializer/JsonConverter.java

@@ -0,0 +1,97 @@
+package cn.reghao.jutil.jdk.serializer;
+
+import cn.reghao.jutil.jdk.text.TextFile;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+
+import java.io.File;
+import java.lang.reflect.Type;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JSON 序列化/反序列化
+ *
+ * @author reghao
+ * @date 2020-11-11 16:57:04
+ */
+public class JsonConverter {
+    private static final Gson gson = new GsonBuilder()
+            .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter())
+            .create();
+    private static final JsonParser jsonParser = new JsonParser();
+
+    /**
+     * 对象转换为 JSON
+     *
+     * @param
+     * @return
+     * @date 2020-11-11 下午5:10
+     */
+    public static String objectToJson(Object object) {
+        return gson.toJson(object);
+    }
+
+    /**
+     * JSON 转换为对象
+     * TODO <T> 的含义
+     *
+     * @param
+     * @return
+     * @date 2020-11-11 下午5:11
+     */
+    public static <T> T jsonToObject(String json, Class<T> clazz) {
+        return gson.fromJson(json, clazz);
+    }
+
+    /**
+     * 对泛型数据的反序列化
+     *
+     * @param
+     * @return
+     * @date 2021-05-26 下午3:38
+     */
+    public static <T> T jsonToObject(String json, Type type) {
+        return gson.fromJson(json, type);
+    }
+
+    public static <T> List<T> jsonToObjects(String json, Class<T> clazz) {
+        JsonParser parser = new JsonParser();
+        List<T> list = new ArrayList<>();
+        parser.parse(json).getAsJsonArray().forEach(ele -> {
+            list.add(gson.fromJson(ele, clazz));
+        });
+
+        return list;
+    }
+
+    /**
+     * JSON 文件转换为对象
+     *
+     * @param
+     * @return
+     * @date 2020-11-11 下午5:11
+     */
+    public static JsonElement jsonToJsonElement(File jsonFile) {
+        String content = new TextFile().readFile(jsonFile.getAbsolutePath()).replace(System.lineSeparator(), "");
+        return jsonParser.parse(content);
+    }
+
+    public static JsonElement jsonToJsonElement(String json) {
+        return jsonParser.parse(json);
+    }
+
+    /**
+     * 对象集合转换为 JSON
+     *
+     * @param
+     * @return
+     * @date 2020-11-11 下午5:10
+     */
+    public static String listToJson(List<Object> objects) {
+        return gson.toJson(objects);
+    }
+}

+ 30 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/serializer/LocalDateTimeAdapter.java

@@ -0,0 +1,30 @@
+package cn.reghao.jutil.jdk.serializer;
+
+import com.google.gson.*;
+
+import java.lang.reflect.Type;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Gson 序列化/反序列化 Java8 LocalDateTime
+ *
+ * @author reghao
+ * @date 2021-03-04 19:34:31
+ */
+public class LocalDateTimeAdapter implements JsonSerializer<LocalDateTime>, JsonDeserializer<LocalDateTime> {
+    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    private final DateTimeFormatter formatter1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
+
+    @Override
+    public JsonElement serialize(LocalDateTime localDateTime, Type typeOfSrc, JsonSerializationContext context) {
+        return new JsonPrimitive(localDateTime.format(formatter));
+    }
+
+    @Override
+    public LocalDateTime deserialize(JsonElement element, Type type, JsonDeserializationContext context)
+            throws JsonParseException {
+        String timestamp = element.getAsJsonPrimitive().getAsString();
+        return LocalDateTime.parse(timestamp, formatter);
+    }
+}

+ 104 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/shell/ShellExecutor.java

@@ -0,0 +1,104 @@
+package cn.reghao.jutil.jdk.shell;
+
+import java.io.*;
+import java.util.UUID;
+
+/**
+ * @author reghao
+ * @date 2019-08-20 23:45:06
+ */
+public class ShellExecutor {
+    private final ProcessBuilder pb = new ProcessBuilder();;
+
+    /**
+     * 传入单个命令和其参数
+     * 或者传入 sh 命令和其参数,脚本应该是绝对路径
+     *
+     * Java 运行时不能执行 cd 命令
+     *
+     * @param commands shell 命令(脚本) 参数
+     * @return
+     * @date 2021-10-28 下午4:26
+     */
+    public ShellResult exec(String... commands) {
+        String output = System.getProperty("java.io.tmpdir") + "/" + UUID.randomUUID() + ".out";
+        File ofile = new File(output);
+
+        ShellResult shellResult;
+        try {
+            boolean ret = ofile.createNewFile();
+            // 单个命令和其参数
+            pb.command(commands)
+                    // 将标准错误合并到标准输出
+                    .redirectErrorStream(true)
+                    // 将所有输出重定向到文件
+                    .redirectOutput(ofile);
+            shellResult = exec(pb, ofile);
+        } catch (IOException | InterruptedException e) {
+            shellResult = new ShellResult(1);
+            shellResult.setResult(e.getMessage());
+        } finally {
+            ofile.delete();
+        }
+
+        return shellResult;
+    }
+
+    /**
+     * @param dir 工作目录
+     * @param commands shell 命令(脚本) 参数
+     * @return
+     * @date 2021-10-28 下午4:26
+     */
+    public ShellResult exec(String dir, String... commands) {
+        String output = System.getProperty("java.io.tmpdir") + "/" + UUID.randomUUID() + ".out";
+        File ofile = new File(output);
+
+        ShellResult shellResult;
+        try {
+            boolean ret = ofile.createNewFile();
+            // 命令和其参数
+            pb.command(commands)
+                    // 将标准错误合并到标准输出
+                    .redirectErrorStream(true)
+                    // 将所有输出重定向到文件
+                    .redirectOutput(ofile);
+            if (dir != null) {
+                pb.directory(new File(dir));
+            }
+            shellResult = exec(pb, ofile);
+        } catch (IOException | InterruptedException e) {
+            shellResult = new ShellResult(1);
+            shellResult.setResult(e.getMessage());
+        } finally {
+            ofile.delete();
+        }
+
+        return shellResult;
+    }
+
+    private ShellResult exec(ProcessBuilder pb, File ofile) throws IOException, InterruptedException {
+        Process newProcess = pb.start();
+        ProcessHandle handle = newProcess.toHandle();
+        // 子进程 PID
+        long pid = handle.pid();
+
+        // 父进程等待子进程结束
+        int exitCode = newProcess.waitFor();
+        ShellResult shellResult = new ShellResult(exitCode);
+        shellResult.setExitCode(exitCode);
+        shellResult.setResult(output(ofile));
+        return shellResult;
+    }
+
+    private String output(File ofile) throws IOException {
+        StringBuilder sb = new StringBuilder();
+        BufferedReader in =  new BufferedReader(new InputStreamReader(new FileInputStream(ofile)));
+        String line;
+        while ((line = in.readLine()) != null) {
+            sb.append(line).append(System.lineSeparator());
+        }
+        in.close();
+        return sb.toString();
+    }
+}

+ 42 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/shell/ShellResult.java

@@ -0,0 +1,42 @@
+package cn.reghao.jutil.jdk.shell;
+
+/**
+ * @author reghao
+ * @date 2019-08-24 14:47:57
+ */
+public class ShellResult {
+    private int exitCode;
+    // 包含 stdout 和/或 stderr
+    private String result;
+
+    public ShellResult(int exitCode) {
+        this.exitCode = exitCode;
+    }
+
+    public void setExitCode(int exitCode) {
+        this.exitCode = exitCode;
+    }
+
+    /**
+     * 是否成功执行
+     *
+     * @param
+     * @return
+     * @date 2021-05-22 上午11:11
+     */
+    public boolean isSuccess() {
+        return exitCode == 0;
+    }
+
+    public int getExitCode() {
+        return exitCode;
+    }
+
+    public void setResult(String result) {
+        this.result = result;
+    }
+
+    public String getResult() {
+        return result;
+    }
+}

+ 215 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/text/TextFile.java

@@ -0,0 +1,215 @@
+package cn.reghao.jutil.jdk.text;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * 文本文件工具类
+ *
+ * @author reghao
+ * @date 2019/02/27 15:49:32
+ */
+public class TextFile {
+    // 匹配所有空白符
+    private String whiteSpace = "\\s+";
+    // 8 MiB
+    private final int bufSize = 8*1024*1024;
+
+    /**
+     * 清空空白字符
+     *
+     * @param
+     * @return
+     * @date 2019-06-10 下午3:31
+     */
+    private String clearWhiteSpace(String str) {
+        return str.replace(whiteSpace, "");
+    }
+
+    /**
+     * 读取文件内容
+     *
+     * @param
+     * @return
+     * @date 2019-09-02 上午12:22
+     */
+    public List<String> read(String filePath) {
+        File file = new File(filePath);
+        if (!file.exists()) {
+            return null;
+        }
+
+        List<String> lines = new ArrayList<>();
+        try {
+            BufferedReader in =  new BufferedReader(new InputStreamReader(new FileInputStream(file)), bufSize);
+            String line;
+            while ((line = in.readLine()) != null) {
+                lines.add(clearWhiteSpace(line));
+            }
+
+            in.close();
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+
+        return lines;
+    }
+
+    public List<String> tailRead(String filePath, int count) throws IOException {
+        RandomAccessFile raf = new RandomAccessFile(filePath, "r");
+        long len = raf.length();
+        long next = len-1;
+        int ch;
+        String line;
+        List<String> list = new ArrayList<>();
+        while (next >= 0 && count > 0) {
+            ch = raf.read();
+            if (ch == '\n') {
+                line = raf.readLine();
+                checkAndAdd(line, list);
+                count--;
+            }
+
+            raf.seek(next);
+            if (next == 0) {
+                line = raf.readLine();
+                checkAndAdd(line, list);
+                count--;
+            }
+            next--;
+        }
+
+        Collections.reverse(list);
+        return list;
+    }
+
+    private void checkAndAdd(String line, List<String> list) {
+        if (line != null) {
+            list.add(line);
+        }
+    }
+
+    public String readFile(String filePath) {
+        File file = new File(filePath);
+        if (!file.exists()) {
+            return null;
+        }
+
+        StringBuilder content = new StringBuilder();
+        try {
+            BufferedReader in =  new BufferedReader(new InputStreamReader(new FileInputStream(file)), bufSize);
+            String line;
+            while ((line = in.readLine()) != null) {
+                content.append(line).append(System.lineSeparator());
+            }
+            in.close();
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+
+        int index = content.lastIndexOf(System.lineSeparator());
+        return content.substring(0, index);
+    }
+
+    /**
+     * 向文件写入内容
+     *
+     * @param
+     * @return
+     * @date 2019-09-02 上午12:22
+     */
+    public void write(String filePath, List<String> list) {
+        File file = new File(filePath);
+        try {
+            /*if (file.exists() || !file.createNewFile()) {
+                log.info("{} exists or create file failed...", filePath);
+                return;
+            }*/
+
+            if (list == null || list.size() == 0) {
+                return;
+            }
+
+            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)), bufSize);
+            list.forEach(str -> {
+                try {
+                    out.write(str + System.lineSeparator());
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            });
+            out.close();
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+    }
+
+    /**
+     * 向文件中写入内容
+     *
+     * @param
+     * @return
+     * @date 2020-03-10 上午10:47
+     */
+    public void write(File file, String content) throws IOException {
+        int bufSize = 8*1024*1024;
+        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)), bufSize);
+        for (String str : content.split(System.lineSeparator())) {
+            out.write(str + System.lineSeparator());
+        }
+
+        out.close();
+    }
+
+    /**
+     * 清空文件内容
+     *
+     * @param
+     * @return
+     * @date 2020-03-10 上午10:47
+     */
+    public static void empty(File file) throws IOException {
+        file.delete();
+        file.createNewFile();
+    }
+
+    /**
+     * 将空白字符分隔的 key-value 格式文本存放到 Map
+     *
+     * @param
+     * @return
+     * @date 2019-04-29 下午2:44
+     */
+    public Map<String, String> readByLineMap(String filePath) {
+        // 有序 map
+        Map<String, String> map = new LinkedHashMap<>();
+        // 4MiB
+        // TODO: 如何确认缓冲区大小
+        final int bufSize = 4*1024*1024;
+
+        try {
+            BufferedReader in =  new BufferedReader(new InputStreamReader(new FileInputStream(filePath)), bufSize);
+            String line;
+            while ((line = in.readLine()) != null) {
+                String[] ss = line.split(whiteSpace);
+                if (ss.length == 2 && !line.split(whiteSpace)[0].equals("")
+                        && ss[0].charAt(0) > 48 && ss[0].charAt(0) < 57) {
+                    map.put(ss[0], ss[1]);
+                }
+            }
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+
+        return map;
+    }
+
+    public void append(String filePath, List<String> lines) throws IOException {
+        int bufSize = 8*1024*1024;
+        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath)), bufSize);
+        for (String line : lines) {
+            out.append(line);
+        }
+        out.close();
+    }
+}

+ 43 - 0
jdk/src/main/java/cn/reghao/jutil/jdk/util/SingleInstance.java

@@ -0,0 +1,43 @@
+package cn.reghao.jutil.jdk.util;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.SocketAddress;
+import java.util.logging.Logger;
+
+/**
+ * 监听一个端口,保证一台机器只有一个应用实例
+ *
+ * @author reghao
+ * @date 2021-12-24 10:41:03
+ */
+public class SingleInstance implements Runnable {
+    private static final Logger log = Logger.getLogger(SingleInstance.class.getName());
+
+    private final int port;
+
+    private SingleInstance(int port) {
+        this.port = port;
+    }
+
+    public static void onlyOne(int port) {
+        SingleInstance singleInstance = new SingleInstance(port);
+        Thread t = new Thread(singleInstance);
+        t.start();
+    }
+
+    @Override
+    public void run() {
+        SocketAddress socketAddress = new InetSocketAddress("127.0.0.1", port);
+        try {
+            log.info(String.format("listening port %s to ensure only one instance running on os", port));
+            ServerSocket server = new ServerSocket();
+            server.bind(socketAddress);
+            server.accept();
+        } catch (IOException e) {
+            e.printStackTrace();
+            System.exit(-1);
+        }
+    }
+}

+ 28 - 0
pom.xml

@@ -0,0 +1,28 @@
+<?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>
+
+    <groupId>cn.reghao.jutil</groupId>
+    <artifactId>jutil</artifactId>
+    <packaging>pom</packaging>
+    <version>1.0.0</version>
+    <modules>
+        <module>jdk</module>
+        <module>tool</module>
+    </modules>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.8.5</version>
+        </dependency>
+    </dependencies>
+</project>

+ 54 - 0
tool/pom.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>jutil</artifactId>
+        <groupId>cn.reghao.jutil</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>tool</artifactId>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.reghao.jutil</groupId>
+            <artifactId>jdk</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.9</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpmime</artifactId>
+            <version>4.5.9</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.15</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.8.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hashids</groupId>
+            <artifactId>hashids</artifactId>
+            <version>1.0.3</version>
+        </dependency>
+    </dependencies>
+</project>

+ 146 - 0
tool/src/main/java/cn/reghao/jutil/tool/http/BaseWebRequest.java

@@ -0,0 +1,146 @@
+package cn.reghao.jutil.tool.http;
+
+import cn.reghao.jutil.jdk.http.proxy.RequestProxy;
+import cn.reghao.jutil.tool.http.util.FakeDnsResolver;
+import cn.reghao.jutil.tool.http.util.MyConnectionSocketFactory;
+import cn.reghao.jutil.tool.http.util.MySSLConnectionSocketFactory;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.config.CookieSpecs;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.ssl.SSLContexts;
+
+import java.net.InetSocketAddress;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2019-11-29 10:03:18
+ */
+public class BaseWebRequest {
+    protected final CloseableHttpClient client;
+    @Deprecated
+    protected final Charset charset;
+    protected final String bodyCharset;
+    protected final String userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko";
+
+    public BaseWebRequest() {
+        HttpClientBuilder builder = HttpClients.custom()
+                .setConnectionManager(connectionConfig())
+                .setDefaultRequestConfig(requestConfig());
+        //.setKeepAliveStrategy(keepAliveConfig())
+        this.client = builder.build();
+        this.charset = StandardCharsets.UTF_8;
+        this.bodyCharset = "utf8";
+    }
+
+    public BaseWebRequest(String charsetName, boolean enableProxy) {
+        HttpClientBuilder builder = HttpClients.custom()
+                .setConnectionManager(connectionConfig(enableProxy))
+                .setDefaultRequestConfig(requestConfig());
+        //.setKeepAliveStrategy(keepAliveConfig())
+        this.client = builder.build();
+        this.charset = StandardCharsets.UTF_8;
+        this.bodyCharset = charsetName;
+    }
+
+    /**
+     * 连接池配置
+     *
+     * @param
+     * @return
+     * @date 2021-03-23 下午6:21
+     */
+    private PoolingHttpClientConnectionManager connectionConfig(boolean enableProxy) {
+        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
+                .register("http", new MyConnectionSocketFactory())
+                .register("https", new MySSLConnectionSocketFactory(SSLContexts.createSystemDefault(),
+                        NoopHostnameVerifier.INSTANCE))
+                .build();
+
+        PoolingHttpClientConnectionManager cm;
+        if (enableProxy) {
+            cm = new PoolingHttpClientConnectionManager(registry, new FakeDnsResolver());
+        } else {
+            cm = new PoolingHttpClientConnectionManager();
+        }
+
+        cm.setMaxTotal(50);
+        cm.setDefaultMaxPerRoute(20);
+        return cm;
+    }
+
+    /**
+     * 连接池配置
+     *
+     * @param
+     * @return
+     * @date 2021-03-23 下午6:21
+     */
+    private PoolingHttpClientConnectionManager connectionConfig() {
+        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
+        cm.setMaxTotal(50);
+        cm.setDefaultMaxPerRoute(20);
+        return cm;
+    }
+
+    /**
+     * 请求配置
+     *
+     * @param
+     * @return
+     * @date 2021-03-23 下午6:21
+     */
+    private RequestConfig requestConfig() {
+        return RequestConfig.custom()
+                .setConnectionRequestTimeout(60_000)
+                .setConnectTimeout(60_000)
+                .setSocketTimeout(60_000)
+                .setCookieSpec(CookieSpecs.DEFAULT)
+                .build();
+    }
+
+    /**
+     * 请求上下文(每个 Website 一个上下文)
+     *
+     * @param
+     * @return
+     * @date 2021-03-24 上午2:04
+     */
+    protected HttpContext httpContext(List<Cookie> cookies, RequestProxy proxy) {
+        HttpContext context = HttpClientContext.create();
+        setCookies(context, cookies);
+        if (proxy != null) {
+            setProxy(context, proxy);
+        }
+        return context;
+    }
+
+    private void setCookies(HttpContext context, List<Cookie> cookies) {
+        // BasicClientCookie
+        CookieStore cookieStore = new BasicCookieStore();
+        cookies.forEach(cookieStore::addCookie);
+        // 设置 cookies
+        context.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
+    }
+
+    private void setProxy(HttpContext context, RequestProxy proxy) {
+        InetSocketAddress socketAddress = new InetSocketAddress(proxy.getHost(), proxy.getPort());
+        // 设置 SOCKS5 代理
+        context.setAttribute("socks.address", socketAddress);
+        // TODO 设置 HTTP/HTTPS 代理
+    }
+}

+ 212 - 0
tool/src/main/java/cn/reghao/jutil/tool/http/DefaultHttpDownloader.java

@@ -0,0 +1,212 @@
+package cn.reghao.jutil.tool.http;
+
+import cn.reghao.jutil.jdk.http.DlResponse;
+import cn.reghao.jutil.jdk.http.HttpDownloader;
+import cn.reghao.jutil.jdk.http.util.UrlFormatter;
+import cn.reghao.jutil.jdk.http.util.UserAgents;
+import cn.reghao.jutil.jdk.http.proxy.RequestProxy;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.protocol.HttpContext;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2019-11-29 10:03:18
+ */
+public class DefaultHttpDownloader extends BaseWebRequest implements HttpDownloader {
+    public DefaultHttpDownloader() {
+    }
+
+    public DefaultHttpDownloader(boolean enableProxy) {
+        super("utf8", enableProxy);
+    }
+
+    @Override
+    public int head(String url) {
+        HttpHead head = new HttpHead(url);
+        try (CloseableHttpResponse response = client.execute(head)) {
+            return response.getStatusLine().getStatusCode();
+        } catch (IOException e) {
+            System.out.format("%s head 请求失败 -> %s%n", url, e.getMessage());
+        }
+
+        // 资源无法访问
+        return 600;
+    }
+
+    @Override
+    public DlResponse download(String url) {
+        HttpGet get = new HttpGet(url);
+        get.setHeader("User-Agent", UserAgents.getDesktopAgent());
+        long start = System.currentTimeMillis();
+        try (CloseableHttpResponse response = client.execute(get)) {
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                return dlResponse(response, statusCode, start);
+            } else if (statusCode == 206) {
+                return dlResponse(response, statusCode, start);
+            } else if (statusCode == 302) {
+                // 请求重定向
+                String location = response.getFirstHeader("Location").getValue();
+            } else if (statusCode == 404) {
+                System.out.format("%s 资源不存在", url);
+                return new DlResponse(statusCode, 0, 0, null, null);
+            }
+        } catch (IOException e) {
+            System.out.format("%s 下载失败 -> %s%n", url, e.getMessage());
+        }
+        return null;
+    }
+
+    private DlResponse dlResponse(HttpResponse response, int statusCode, long start) throws IOException {
+        Header header = response.getFirstHeader("Content-Length");
+        long contentLength;
+        if (header == null) {
+            contentLength = 0;
+        } else {
+            contentLength = Long.parseLong(header.getValue());
+        }
+
+        Header contentRangeHeader = response.getFirstHeader("Content-Range");
+        String contentRange = null;
+        if (contentRangeHeader != null) {
+            contentRange = contentRangeHeader.getValue();
+        }
+
+        HttpEntity entity = response.getEntity();
+        int avail = entity.getContent().available();
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        entity.writeTo(byteArrayOutputStream);
+        long costTime = System.currentTimeMillis()-start;
+        return new DlResponse(statusCode, contentLength, costTime, contentRange, byteArrayOutputStream);
+    }
+
+    @Override
+    public boolean download(String url, String dir) {
+        HttpGet get = new HttpGet(url);
+        get.setHeader("User-Agent", UserAgents.getDesktopAgent());
+        long start = System.currentTimeMillis();
+        try (CloseableHttpResponse response = client.execute(get)) {
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity httpEntity = response.getEntity();
+                String contentType = httpEntity.getContentType().getValue();
+                String filename = UrlFormatter.getFilename(url);
+                File file = new File(dir + File.separator + filename);
+                FileOutputStream fout = new FileOutputStream(file);
+                // 持续写到本地文件,直到服务器没有数据
+                httpEntity.writeTo(fout);
+                return true;
+            }
+        } catch (IOException e) {
+            System.out.format("%s 下载失败 -> %s%n", url, e.getMessage());
+        }
+        return false;
+    }
+
+    public boolean download(String url, RequestProxy requestProxy, String dir) {
+        HttpGet get = new HttpGet(url);
+        get.setHeader("User-Agent", UserAgents.getDesktopAgent());
+        HttpContext context = httpContext(Collections.emptyList(), requestProxy);
+        long start = System.currentTimeMillis();
+        try (CloseableHttpResponse response = client.execute(get, context)) {
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity httpEntity = response.getEntity();
+                String contentType = httpEntity.getContentType().getValue();
+                String filename = UrlFormatter.getFilename(url);
+                File file = new File(dir + File.separator + filename);
+                FileOutputStream fout = new FileOutputStream(file);
+                // 持续写到本地文件,直到服务器没有数据
+                httpEntity.writeTo(fout);
+                return true;
+            }
+        } catch (IOException e) {
+            System.out.format("%s 下载失败 -> %s%n", url, e.getMessage());
+        }
+        return false;
+    }
+
+    /**
+     * 将字节数组流保存到文件
+     *
+     * @param
+     * @return
+     * @date 2021-03-15 下午10:13
+     */
+    private void saveFile(ByteArrayOutputStream byteArrayOutputStream, File file) throws IOException {
+        FileOutputStream fout = new FileOutputStream(file);
+        fout.write(byteArrayOutputStream.toByteArray());
+        fout.flush();
+        fout.close();
+    }
+
+    public long acceptRanges(String url) throws IOException {
+        HttpHead httpHead = new HttpHead(url);
+        HttpResponse response = client.execute(httpHead);
+        int statusCode = response.getStatusLine().getStatusCode();
+        if (statusCode == 200) {
+            if (response.getFirstHeader("Accept-Ranges") != null) {
+                Header contentLengthHeader = response.getFirstHeader("Content-Length");
+                return Long.parseLong(contentLengthHeader.getValue());
+            }
+        }
+
+        return 0;
+    }
+
+    public List<String> splitContent(long contentLength) {
+        // 100KiB
+        long maxFragment = 1024*100;
+        // 10MiB
+        //long maxFragment = 1024*1024*10;
+        List<String> list = new ArrayList<>();
+        if (contentLength < maxFragment) {
+            list.add("bytes=0-" + (contentLength - 1));
+        } else {
+            long i = 0;
+            for (;i + maxFragment < contentLength; i += maxFragment) {
+                list.add("bytes=" + i + "-" + (i+maxFragment-1));
+            }
+            list.add("bytes=" + i + "-" + (contentLength-1));
+        }
+        return list;
+    }
+
+    public DlResponse download(HttpGet httpGet) {
+        httpGet.setHeader("User-Agent", UserAgents.getDesktopAgent());
+        HttpContext context = httpContext(new ArrayList<>(), null);
+
+        long start = System.currentTimeMillis();
+        try (CloseableHttpResponse response = client.execute(httpGet, context)) {
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                return dlResponse(response, statusCode, start);
+            } else if (statusCode == 206) {
+                return dlResponse(response, statusCode, start);
+            } else if (statusCode == 302) {
+                //
+                String location = response.getFirstHeader("Location").getValue();
+            } else if (statusCode == 404) {
+                System.out.format("资源下载失败 -> %s%n", httpGet.getURI());
+                return new DlResponse(statusCode, 0, 0, null, null);
+            }
+        } catch (Exception e) {
+            System.out.format("资源下载失败 -> %s%n", httpGet.getURI());
+            e.printStackTrace();
+        }
+        return null;
+    }
+}

+ 128 - 0
tool/src/main/java/cn/reghao/jutil/tool/http/DefaultWebRequest.java

@@ -0,0 +1,128 @@
+package cn.reghao.jutil.tool.http;
+
+import cn.reghao.jutil.jdk.http.UploadParam;
+import cn.reghao.jutil.jdk.http.WebRequest;
+import cn.reghao.jutil.jdk.http.WebResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.StatusLine;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.*;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.entity.mime.content.InputStreamBody;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+/**
+ * @author reghao
+ * @date 2019-11-29 10:03:18
+ */
+public class DefaultWebRequest extends BaseWebRequest implements WebRequest {
+    private Map<String, String> headers;
+
+    public DefaultWebRequest() {
+        super();
+    }
+
+    public DefaultWebRequest(Map<String, String> headers) {
+        super();
+        this.headers = headers;
+    }
+
+    @Override
+    public int head(String url) {
+        HttpHead head = new HttpHead(url);
+        WebResponse webResponse = execRequest(head);
+        return webResponse.getStatusCode();
+    }
+
+    @Override
+    public WebResponse get(String url) {
+        HttpGet get = new HttpGet(url);
+        return execRequest(get);
+    }
+
+    @Override
+    public WebResponse postFormData(String url, Map<String, String> formData) {
+        List<NameValuePair> params = new ArrayList<>();
+        formData.forEach((k, v) -> {
+            params.add(new BasicNameValuePair(k, v));
+        });
+        UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(params, StandardCharsets.UTF_8);
+
+        HttpPost post = new HttpPost(url);
+        if (headers != null) {
+            headers.forEach(post::addHeader);
+        }
+        post.setEntity(urlEncodedFormEntity);
+        return execRequest(post);
+    }
+
+    @Override
+    public WebResponse postJson(String url, String json) {
+        StringEntity entity = new StringEntity(json, StandardCharsets.UTF_8);
+        entity.setContentEncoding("UTF-8");
+
+        HttpPost post = new HttpPost(url);
+        if (headers != null) {
+            headers.forEach(post::addHeader);
+        }
+        post.addHeader("Content-Type", "application/json;charset=UTF-8");
+        post.setEntity(entity);
+        return execRequest(post);
+    }
+
+    @Override
+    public WebResponse upload(String url, UploadParam uploadParam) {
+        String filePath = uploadParam.getFilePath();
+        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+        builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
+        builder.setCharset(StandardCharsets.UTF_8);
+
+        if (filePath != null) {
+            File file = new File(uploadParam.getFilePath());
+            FileBody fileBody = new FileBody(file, uploadParam.getMimeType());
+            builder.addPart("file", fileBody);
+        } else {
+            InputStreamBody inputStreamBody = new InputStreamBody(uploadParam.getInputStream(), uploadParam.getMimeType());
+            builder.addPart("file", inputStreamBody);
+        }
+
+        Map<String, String> map = uploadParam.getTextParams();
+        if (map != null) {
+            map.forEach(builder::addTextBody);
+        }
+
+        HttpPost post = new HttpPost(url);
+        if (headers != null) {
+            headers.forEach(post::addHeader);
+        }
+        post.setEntity(builder.build());
+        try (CloseableHttpResponse response = client.execute(post)) {
+            int statusCode = response.getStatusLine().getStatusCode();
+            String body = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);;
+            return new WebResponse(statusCode, body);
+        } catch (Exception e) {
+            return new WebResponse(600, e.getMessage());
+        }
+    }
+
+    private WebResponse execRequest(HttpRequestBase request) {
+        try (CloseableHttpResponse response = client.execute(request)) {
+            StatusLine statusLine = response.getStatusLine();
+            int statusCode = statusLine.getStatusCode();
+            //String body = EntityUtils.toString(response.getEntity(), charset);
+            String body = EntityUtils.toString(response.getEntity(), charset);
+            return new WebResponse(statusCode, body);
+        } catch (Exception e) {
+            // TODO 是否应该放在 finally 块中?
+            return new WebResponse(600, e.getMessage());
+        }
+    }
+}

+ 99 - 0
tool/src/main/java/cn/reghao/jutil/tool/http/JdkCrawlRequest.java

@@ -0,0 +1,99 @@
+package cn.reghao.jutil.tool.http;
+
+import cn.reghao.jutil.jdk.http.WebResponse;
+import cn.reghao.jutil.jdk.http.util.UserAgents;
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.text.MessageFormat;
+import java.time.Duration;
+import java.util.logging.Logger;
+
+/**
+ * 爬虫请求
+ *
+ * @author reghao
+ * @date 2022-02-28 15:27:55
+ */
+public class JdkCrawlRequest {
+    private static final Logger log = Logger.getLogger(JdkCrawlRequest.class.getName());
+
+    private final HttpClient client = HttpClient.newBuilder()
+            .version(HttpClient.Version.HTTP_1_1)
+            .build();
+    
+    public int head(String url) {
+        HttpRequest.Builder builder = HttpRequest.newBuilder()
+                .uri(URI.create(url))
+                .timeout(Duration.ofSeconds(5))
+                .GET();
+        builder.setHeader("User-Agent", UserAgents.getDesktopAgent());
+
+        try {
+            HttpResponse<String> response = client.send(builder.build(), HttpResponse.BodyHandlers.ofString());
+            return response.statusCode();
+        } catch (Exception e) {
+            log.info(MessageFormat.format("{0} 请求失败 -> {1}", url, e.getMessage()));
+            return 600;
+        }
+    }
+
+    public WebResponse get(String url) {
+        HttpRequest.Builder builder = HttpRequest.newBuilder()
+                .uri(URI.create(url))
+                .timeout(Duration.ofSeconds(5))
+                .GET();
+        builder.setHeader("User-Agent", UserAgents.getDesktopAgent());
+
+        try {
+            HttpResponse<String> response = client.send(builder.build(), HttpResponse.BodyHandlers.ofString());
+            int statusCode = response.statusCode();
+            String body = response.body();
+            return new WebResponse(statusCode, body);
+        } catch (Exception e) {
+            log.info(MessageFormat.format("{0} 请求失败 -> {1}", url, e.getMessage()));
+            return new WebResponse(600, e.getMessage());
+        }
+    }
+
+    public boolean download(String url, File file) {
+        HttpRequest.Builder builder = HttpRequest.newBuilder()
+                .uri(URI.create(url))
+                .timeout(Duration.ofSeconds(30))
+                .GET();
+        builder.setHeader("User-Agent", UserAgents.getDesktopAgent());
+
+        try {
+            HttpResponse<InputStream> in = client.send(builder.build(), HttpResponse.BodyHandlers.ofInputStream());
+            saveFile(in.body(), file);
+            return true;
+        } catch (Exception e) {
+            log.info(MessageFormat.format("{0} 下载失败 -> {1}", url, e.getMessage()));
+        }
+        return false;
+    }
+
+    private void saveFile(InputStream in, File file) throws IOException {
+        File parentDir = file.getParentFile();
+        if (!parentDir.exists()) {
+            FileUtils.forceMkdir(parentDir);
+        }
+
+        FileOutputStream fos = new FileOutputStream(file);
+        // 1MiB
+        int len = 1024*1024;
+        byte[] buf = new byte[len];
+        int readLen;
+        while ((readLen = in.read(buf, 0, len)) != -1) {
+            fos.write(buf, 0, readLen);
+        }
+        fos.close();
+    }
+}

+ 18 - 0
tool/src/main/java/cn/reghao/jutil/tool/http/util/FakeDnsResolver.java

@@ -0,0 +1,18 @@
+package cn.reghao.jutil.tool.http.util;
+
+import org.apache.http.conn.DnsResolver;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * @author reghao
+ * @date 2021-03-15 21:51:03
+ */
+public class FakeDnsResolver implements DnsResolver {
+    @Override
+    public InetAddress[] resolve(String host) throws UnknownHostException {
+        // Return some fake DNS record for every request, we won't be using it
+        return new InetAddress[] { InetAddress.getByAddress(new byte[] { 1, 1, 1, 1 }) };
+    }
+}

+ 32 - 0
tool/src/main/java/cn/reghao/jutil/tool/http/util/MyConnectionSocketFactory.java

@@ -0,0 +1,32 @@
+package cn.reghao.jutil.tool.http.util;
+
+import org.apache.http.HttpHost;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.protocol.HttpContext;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.Socket;
+
+/**
+ * @author reghao
+ * @date 2021-03-15 21:50:26
+ */
+public class MyConnectionSocketFactory extends PlainConnectionSocketFactory {
+    @Override
+    public Socket createSocket(final HttpContext context) throws IOException {
+        InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address");
+        Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
+        return new Socket(proxy);
+    }
+
+    @Override
+    public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress,
+                                InetSocketAddress localAddress, HttpContext context) throws IOException {
+        // Convert address to unresolved
+        InetSocketAddress unresolvedRemote = InetSocketAddress
+                .createUnresolved(host.getHostName(), remoteAddress.getPort());
+        return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
+    }
+}

+ 75 - 0
tool/src/main/java/cn/reghao/jutil/tool/http/util/MyRetryHandler.java

@@ -0,0 +1,75 @@
+package cn.reghao.jutil.tool.http.util;
+
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpRequest;
+import org.apache.http.NoHttpResponseException;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.protocol.HttpContext;
+
+import javax.net.ssl.SSLException;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+
+/**
+ * @author reghao
+ * @date 2021-03-23 22:22:35
+ */
+public class MyRetryHandler implements HttpRequestRetryHandler {
+    private final int MAX_FAIL_RETRY_COUNT = 3;
+
+    @Override
+    public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
+        HttpClientContext clientContext = HttpClientContext.adapt(context);
+        HttpRequest request = clientContext.getRequest();
+        String uri = request.getRequestLine().getUri();
+
+        if (executionCount >= MAX_FAIL_RETRY_COUNT) {
+            System.out.format("%s-%s重试次数大于等于3次%n", uri, executionCount);
+            return false;
+        }
+
+        boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
+        if (idempotent) {
+            // 如果请求被认为是幂等的,则重试
+            System.out.format("幂等接口重试:%s,次数:%s%n", uri, executionCount);
+            return true;
+        }
+
+        //NoHttpResponseException 重试
+        if (exception instanceof NoHttpResponseException) {
+            System.out.format("NoHttpResponseException 异常重试,接口:%s,次数:%s%n ", uri, executionCount);
+            return true;
+        }
+
+        //连接超时重试
+        if (exception instanceof ConnectTimeoutException) {
+            System.out.format("ConnectTimeoutException异常重试 ,接口:%s,次数:%s%n", uri, executionCount);
+            return true;
+        }
+
+        // 响应超时不重试,避免造成业务数据不一致
+        if (exception instanceof SocketTimeoutException) {
+            return false;
+        }
+
+        if (exception instanceof InterruptedIOException) {
+            // 超时
+            return false;
+        }
+        if (exception instanceof UnknownHostException) {
+            // 未知主机
+            return false;
+        }
+
+        if (exception instanceof SSLException) {
+            // SSL handshake exception
+            return false;
+        }
+
+        return false;
+    }
+}

+ 38 - 0
tool/src/main/java/cn/reghao/jutil/tool/http/util/MySSLConnectionSocketFactory.java

@@ -0,0 +1,38 @@
+package cn.reghao.jutil.tool.http.util;
+
+import org.apache.http.HttpHost;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.protocol.HttpContext;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.Socket;
+
+/**
+ * @author reghao
+ * @date 2021-03-15 21:51:28
+ */
+public class MySSLConnectionSocketFactory extends SSLConnectionSocketFactory {
+    public MySSLConnectionSocketFactory(final SSLContext sslContext, final HostnameVerifier hostnameVerifier) {
+        super(sslContext, hostnameVerifier);
+    }
+
+    @Override
+    public Socket createSocket(final HttpContext context) throws IOException {
+        InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address");
+        Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
+        return new Socket(proxy);
+    }
+
+    @Override
+    public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress,
+                                InetSocketAddress localAddress, HttpContext context) throws IOException {
+        // Convert address to unresolved
+        InetSocketAddress unresolvedRemote = InetSocketAddress
+                .createUnresolved(host.getHostName(), remoteAddress.getPort());
+        return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
+    }
+}

+ 40 - 0
tool/src/main/java/cn/reghao/jutil/tool/id/IdGenerator.java

@@ -0,0 +1,40 @@
+package cn.reghao.jutil.tool.id;
+
+import org.hashids.Hashids;
+
+/**
+ * @author reghao
+ * @date 2021-11-22 14:05:13
+ */
+public class IdGenerator {
+    private final int len;
+    private final Hashids hashids;
+    private final SnowFlake snowFlake = new SnowFlake(1, 1);
+
+    public IdGenerator(String salt) {
+        this.len = 10;
+        this.hashids = new Hashids(salt, len);
+    }
+
+    public IdGenerator(int len, String salt) {
+        this.len = len;
+        this.hashids = new Hashids(salt, len);
+    }
+
+    public String getUuid() {
+        long nextId = snowFlake.nextId();
+        return hashids.encode(nextId);
+    }
+
+    /**
+     * 字符串形式的 ID
+     *
+     * @param
+     * @return
+     * @date 2021-12-24 上午11:30
+     */
+    public String stringId() {
+        long nextId = snowFlake.nextId();
+        return hashids.encode(nextId);
+    }
+}

+ 93 - 0
tool/src/main/java/cn/reghao/jutil/tool/id/SnowFlake.java

@@ -0,0 +1,93 @@
+package cn.reghao.jutil.tool.id;
+
+/**
+ * @author reghao
+ * @date 2021-11-18 09:08:56
+ */
+public class SnowFlake {
+
+    /**
+     * 起始的时间戳
+     */
+    private final static long START_STMP = System.currentTimeMillis();
+
+    /**
+     * 每一部分占用的位数
+     */
+    private final static long SEQUENCE_BIT = 12; //序列号占用的位数
+    private final static long MACHINE_BIT = 5;   //机器标识占用的位数
+    private final static long DATACENTER_BIT = 5;//数据中心占用的位数
+
+    /**
+     * 每一部分的最大值
+     */
+    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
+    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
+    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
+
+    /**
+     * 每一部分向左的位移
+     */
+    private final static long MACHINE_LEFT = SEQUENCE_BIT;
+    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
+    private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
+
+    private long datacenterId;  //数据中心
+    private long machineId;     //机器标识
+    private long sequence = 0L; //序列号
+    private long lastStmp = -1L;//上一次时间戳
+
+    public SnowFlake(long datacenterId, long machineId) {
+        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
+            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
+        }
+        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
+            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
+        }
+        this.datacenterId = datacenterId;
+        this.machineId = machineId;
+    }
+
+    /**
+     * 产生下一个ID
+     *
+     * @return
+     */
+    public synchronized long nextId() {
+        long currStmp = getNewstmp();
+        if (currStmp < lastStmp) {
+            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
+        }
+
+        if (currStmp == lastStmp) {
+            //相同毫秒内,序列号自增
+            sequence = (sequence + 1) & MAX_SEQUENCE;
+            //同一毫秒的序列数已经达到最大
+            if (sequence == 0L) {
+                currStmp = getNextMill();
+            }
+        } else {
+            //不同毫秒内,序列号置为0
+            sequence = 0L;
+        }
+
+        lastStmp = currStmp;
+
+        return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
+                | datacenterId << DATACENTER_LEFT       //数据中心部分
+                | machineId << MACHINE_LEFT             //机器标识部分
+                | sequence;                             //序列号部分
+    }
+
+    private long getNextMill() {
+        long mill = getNewstmp();
+        while (mill <= lastStmp) {
+            mill = getNewstmp();
+        }
+        return mill;
+    }
+
+    private long getNewstmp() {
+        return System.currentTimeMillis();
+    }
+}