Browse Source

first commit

felixyin 7 months ago
commit
f46cb2adcc
100 changed files with 4592 additions and 0 deletions
  1. 33 0
      .gitignore
  2. 50 0
      jzo2o-canal-sync/pom.xml
  3. 11 0
      jzo2o-canal-sync/src/main/java/com/jzo2o/canal/constants/FieldConstants.java
  4. 27 0
      jzo2o-canal-sync/src/main/java/com/jzo2o/canal/constants/OperateType.java
  5. 22 0
      jzo2o-canal-sync/src/main/java/com/jzo2o/canal/core/CanalDataHandler.java
  6. 136 0
      jzo2o-canal-sync/src/main/java/com/jzo2o/canal/listeners/AbstractCanalRabbitMqMsgListener.java
  7. 31 0
      jzo2o-canal-sync/src/main/java/com/jzo2o/canal/model/CanalMqInfo.java
  8. 46 0
      jzo2o-canal-sync/src/main/java/com/jzo2o/canal/model/dto/CanalBaseDTO.java
  9. 49 0
      jzo2o-common/pom.xml
  10. 11 0
      jzo2o-common/src/main/java/com/jzo2o/common/constants/CommonRedisConstants.java
  11. 13 0
      jzo2o-common/src/main/java/com/jzo2o/common/constants/CommonStatusConstants.java
  12. 100 0
      jzo2o-common/src/main/java/com/jzo2o/common/constants/ErrorInfo.java
  13. 51 0
      jzo2o-common/src/main/java/com/jzo2o/common/constants/HeaderConstants.java
  14. 82 0
      jzo2o-common/src/main/java/com/jzo2o/common/constants/MqConstants.java
  15. 28 0
      jzo2o-common/src/main/java/com/jzo2o/common/constants/UserType.java
  16. 20 0
      jzo2o-common/src/main/java/com/jzo2o/common/enums/EnableStatusEnum.java
  17. 12 0
      jzo2o-common/src/main/java/com/jzo2o/common/enums/SmsBussinessTypeEnum.java
  18. 29 0
      jzo2o-common/src/main/java/com/jzo2o/common/expcetions/BadRequestException.java
  19. 36 0
      jzo2o-common/src/main/java/com/jzo2o/common/expcetions/CommonException.java
  20. 26 0
      jzo2o-common/src/main/java/com/jzo2o/common/expcetions/DBException.java
  21. 21 0
      jzo2o-common/src/main/java/com/jzo2o/common/expcetions/ElasticSearchException.java
  22. 28 0
      jzo2o-common/src/main/java/com/jzo2o/common/expcetions/ForbiddenOperationException.java
  23. 31 0
      jzo2o-common/src/main/java/com/jzo2o/common/expcetions/MqException.java
  24. 29 0
      jzo2o-common/src/main/java/com/jzo2o/common/expcetions/RequestForbiddenException.java
  25. 29 0
      jzo2o-common/src/main/java/com/jzo2o/common/expcetions/RequestTimeoutException.java
  26. 30 0
      jzo2o-common/src/main/java/com/jzo2o/common/expcetions/RequestUnauthorizedException.java
  27. 29 0
      jzo2o-common/src/main/java/com/jzo2o/common/expcetions/ServerErrorException.java
  28. 27 0
      jzo2o-common/src/main/java/com/jzo2o/common/expcetions/ServerUnavailableException.java
  29. 12 0
      jzo2o-common/src/main/java/com/jzo2o/common/expcetions/StartException.java
  30. 14 0
      jzo2o-common/src/main/java/com/jzo2o/common/handler/ConvertHandler.java
  31. 9 0
      jzo2o-common/src/main/java/com/jzo2o/common/handler/RequestIdHandler.java
  32. 8 0
      jzo2o-common/src/main/java/com/jzo2o/common/handler/UserInfoHandler.java
  33. 31 0
      jzo2o-common/src/main/java/com/jzo2o/common/model/CurrentUserInfo.java
  34. 24 0
      jzo2o-common/src/main/java/com/jzo2o/common/model/Location.java
  35. 119 0
      jzo2o-common/src/main/java/com/jzo2o/common/model/PageResult.java
  36. 79 0
      jzo2o-common/src/main/java/com/jzo2o/common/model/Result.java
  37. 39 0
      jzo2o-common/src/main/java/com/jzo2o/common/model/dto/PageQueryDTO.java
  38. 21 0
      jzo2o-common/src/main/java/com/jzo2o/common/model/dto/StorageUploadResDTO.java
  39. 72 0
      jzo2o-common/src/main/java/com/jzo2o/common/model/msg/TradeStatusMsg.java
  40. 53 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/ArrayUtils.java
  41. 66 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/AspectUtils.java
  42. 156 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/AssertUtils.java
  43. 33 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/Base64Utils.java
  44. 140 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/BeanUtils.java
  45. 6 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/BooleanUtils.java
  46. 18 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/ByteUtils.java
  47. 9 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/ClassUtils.java
  48. 199 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/CollUtils.java
  49. 72 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/ComparatorUtils.java
  50. 226 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/DateUtils.java
  51. 6 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/EnumUtils.java
  52. 6 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/HttpUtils.java
  53. 10 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/IdUtils.java
  54. 6 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/IoUtils.java
  55. 6 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/JsonUtils.java
  56. 85 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/JwtTool.java
  57. 19 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/LambdaUtils.java
  58. 50 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/LocationUtils.java
  59. 151 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/NumberUtils.java
  60. 61 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/ObjectUtils.java
  61. 20 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/ReflectUtils.java
  62. 58 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/SpelUtils.java
  63. 28 0
      jzo2o-common/src/main/java/com/jzo2o/common/utils/StringUtils.java
  64. 53 0
      jzo2o-es/pom.xml
  65. 60 0
      jzo2o-es/src/main/java/com/jzo2o/es/config/EsConfiguration.java
  66. 5 0
      jzo2o-es/src/main/java/com/jzo2o/es/constants/FieldConstants.java
  67. 9 0
      jzo2o-es/src/main/java/com/jzo2o/es/core/ElasticSearchTemplate.java
  68. 28 0
      jzo2o-es/src/main/java/com/jzo2o/es/core/impl/ElasticSearchTemplateImpl.java
  69. 333 0
      jzo2o-es/src/main/java/com/jzo2o/es/core/operations/DefaultDocumentOperations.java
  70. 16 0
      jzo2o-es/src/main/java/com/jzo2o/es/core/operations/DefaultIndexOperations.java
  71. 85 0
      jzo2o-es/src/main/java/com/jzo2o/es/core/operations/DocumentOperations.java
  72. 4 0
      jzo2o-es/src/main/java/com/jzo2o/es/core/operations/IndexOperations.java
  73. 9 0
      jzo2o-es/src/main/java/com/jzo2o/es/model/query/EsPageQuery.java
  74. 19 0
      jzo2o-es/src/main/java/com/jzo2o/es/properties/EsProperties.java
  75. 38 0
      jzo2o-es/src/main/java/com/jzo2o/es/utils/DocumentUtils.java
  76. 45 0
      jzo2o-es/src/main/java/com/jzo2o/es/utils/SearchResponseUtils.java
  77. 21 0
      jzo2o-es/src/main/java/com/jzo2o/es/utils/TermUtils.java
  78. 2 0
      jzo2o-es/src/main/resources/META-INF/spring.factories
  79. 4 0
      jzo2o-es/src/test/resources/application.yml
  80. 40 0
      jzo2o-knife4j-web/pom.xml
  81. 53 0
      jzo2o-knife4j-web/src/main/java/com/jzo2o/knife4j/config/Knife4jConfiguration.java
  82. 29 0
      jzo2o-knife4j-web/src/main/java/com/jzo2o/knife4j/filter/SwaggerFilter.java
  83. 53 0
      jzo2o-knife4j-web/src/main/java/com/jzo2o/knife4j/properties/SwaggerProperties.java
  84. 145 0
      jzo2o-knife4j-web/src/main/java/com/jzo2o/knife4j/response/SwaggerTransformServletResponse.java
  85. 99 0
      jzo2o-knife4j-web/src/main/java/com/jzo2o/knife4j/utils/ResponseWrapper.java
  86. 9 0
      jzo2o-knife4j-web/src/main/resources/META-INF/spring-configuration-metadata.json
  87. 2 0
      jzo2o-knife4j-web/src/main/resources/META-INF/spring.factories
  88. 74 0
      jzo2o-mvc/pom.xml
  89. 95 0
      jzo2o-mvc/src/main/java/com/jzo2o/mvc/advice/CommonExceptionAdvice.java
  90. 11 0
      jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/AutoConfiguration.java
  91. 34 0
      jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/CrosConfig.java
  92. 13 0
      jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/FilterConfiguration.java
  93. 43 0
      jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/JsonConfig.java
  94. 31 0
      jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/MvcConfig.java
  95. 27 0
      jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/UserContextConfiguration.java
  96. 10 0
      jzo2o-mvc/src/main/java/com/jzo2o/mvc/constants/HeaderConstants.java
  97. 52 0
      jzo2o-mvc/src/main/java/com/jzo2o/mvc/filter/PackResultFilter.java
  98. 31 0
      jzo2o-mvc/src/main/java/com/jzo2o/mvc/handler/RequestIdHandlerImpl.java
  99. 14 0
      jzo2o-mvc/src/main/java/com/jzo2o/mvc/handler/UserInfoHandlerImpl.java
  100. 47 0
      jzo2o-mvc/src/main/java/com/jzo2o/mvc/interceptor/UserContextInteceptor.java

+ 33 - 0
.gitignore

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

+ 50 - 0
jzo2o-canal-sync/pom.xml

@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.jzo2o</groupId>
+        <artifactId>jzo2o-parent</artifactId>
+        <version>1.0-SNAPSHOT</version>
+        <relativePath>../jzo2o-parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>jzo2o-canal-sync</artifactId>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.jzo2o</groupId>
+            <artifactId>jzo2o-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.otter</groupId>
+            <artifactId>canal.client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.otter</groupId>
+            <artifactId>canal.protocol</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.jzo2o</groupId>
+            <artifactId>jzo2o-rabbitmq</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.jzo2o</groupId>
+            <artifactId>jzo2o-redis</artifactId>
+        </dependency>
+        <!--单元测试-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 11 - 0
jzo2o-canal-sync/src/main/java/com/jzo2o/canal/constants/FieldConstants.java

@@ -0,0 +1,11 @@
+package com.jzo2o.canal.constants;
+
+/**
+ * 字段常量
+ */
+public class FieldConstants {
+    /**
+     * 字段 - id
+     */
+    public static final String ID = "id";
+}

+ 27 - 0
jzo2o-canal-sync/src/main/java/com/jzo2o/canal/constants/OperateType.java

@@ -0,0 +1,27 @@
+package com.jzo2o.canal.constants;
+
+/**
+ * @author itcast
+ */
+public class OperateType {
+    /**
+     * 新增操作
+     */
+    public static final String INSERT = "INSERT";
+    /**
+     * 修改操作
+     */
+    public static final String UPDATE = "UPDATE";
+    /**
+     * 删除操作
+     */
+    public static final String DELETE = "DELETE";
+
+    public static boolean isSave(String operateType) {
+        return !(!INSERT.equals(operateType) && !UPDATE.equals(operateType));
+    }
+
+    public static boolean canHandle(String operateType) {
+        return INSERT.equals(operateType) || UPDATE.equals(operateType) || DELETE.equals(operateType);
+    }
+}

+ 22 - 0
jzo2o-canal-sync/src/main/java/com/jzo2o/canal/core/CanalDataHandler.java

@@ -0,0 +1,22 @@
+package com.jzo2o.canal.core;
+
+import java.util.List;
+
+/**
+ *
+ * @param <T>
+ */
+public interface CanalDataHandler<T> {
+
+    /**
+     * 批量保存
+     * @param data
+     */
+    void batchSave(List<T> data);
+
+    /**
+     * 批量删除
+     * @param ids
+     */
+    void batchDelete(List<Long> ids);
+}

+ 136 - 0
jzo2o-canal-sync/src/main/java/com/jzo2o/canal/listeners/AbstractCanalRabbitMqMsgListener.java

@@ -0,0 +1,136 @@
+package com.jzo2o.canal.listeners;
+
+import com.jzo2o.canal.constants.FieldConstants;
+import com.jzo2o.canal.constants.OperateType;
+import com.jzo2o.canal.core.CanalDataHandler;
+import com.jzo2o.canal.model.CanalMqInfo;
+import com.jzo2o.canal.model.dto.CanalBaseDTO;
+import com.jzo2o.common.utils.BeanUtils;
+import com.jzo2o.common.utils.CollUtils;
+import com.jzo2o.common.utils.JsonUtils;
+import com.jzo2o.common.utils.NumberUtils;
+import org.springframework.amqp.core.Message;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+public abstract class AbstractCanalRabbitMqMsgListener<T> implements CanalDataHandler<T> {
+
+    public void parseMsg(Message message) throws Exception {
+
+        try {
+            // 1.数据格式转换
+            CanalMqInfo canalMqInfo = JsonUtils.toBean(new String(message.getBody()), CanalMqInfo.class);
+            // 2.过滤数据,没有数据或者非插入、修改、删除的操作均不处理
+            if (CollUtils.isEmpty(canalMqInfo.getData()) || !(OperateType.canHandle(canalMqInfo.getType()))) {
+                return;
+            }
+
+            if (canalMqInfo.getData().size() > 1) {
+                // 3.多条数据处理
+                batchHandle(canalMqInfo);
+            } else {
+                // 4.单条数据处理
+                singleHandle(canalMqInfo);
+            }
+        } catch (Exception e) {
+            //出现错误延迟1秒重试
+            Thread.sleep(1000);
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 单条数据处理
+     *
+     * @param canalMqInfo
+     */
+    private void singleHandle(CanalMqInfo canalMqInfo) {
+        // 1.数据转换
+        CanalBaseDTO canalBaseDTO = BeanUtils.toBean(canalMqInfo, CanalBaseDTO.class);
+        Map<String, Object> fieldMap = CollUtils.getFirst(canalMqInfo.getData());
+        canalBaseDTO.setId(parseId(fieldMap));
+        canalBaseDTO.setFieldMap(fieldMap);
+        canalBaseDTO.setIsSave(canalMqInfo.getIsSave());
+
+        Class<T> messageType = getMessageType();
+        if (messageType == null) {
+            return;
+        }
+        if (canalBaseDTO.getIsSave()) {
+            T t1 = JsonUtils.toBean(JsonUtils.toJsonStr(canalBaseDTO.getFieldMap()), messageType);
+            List<T> ts = Arrays.asList(t1);
+            batchSave(ts);
+        } else {
+            Long id = canalBaseDTO.getId();
+            List<Long> ids = Arrays.asList(id);
+            batchDelete(ids);
+        }
+    }
+
+
+    private void batchHandle(CanalMqInfo canalMqInfo) {
+        Class<T> messageType = getMessageType();
+        if (messageType == null) {
+            return;
+        }
+
+        if(canalMqInfo.getIsSave()){
+            List<T> collect = canalMqInfo.getData().stream().map(fieldMap -> {
+                CanalBaseDTO canalBaseDTO = CanalBaseDTO.builder()
+                        .id(parseId(fieldMap))
+                        .database(canalMqInfo.getDatabase())
+                        .table(canalMqInfo.getTable())
+                        .isSave(canalMqInfo.getIsSave())
+                        .fieldMap(fieldMap).build();
+                return JsonUtils.toBean(JsonUtils.toJsonStr(canalBaseDTO.getFieldMap()), messageType);
+            }).collect(Collectors.toList());
+            batchSave(collect);
+        }else{
+            List<Long> ids = canalMqInfo.getData().stream().map(fieldMap -> {
+                return parseId(fieldMap);
+            }).collect(Collectors.toList());
+
+            batchDelete(ids);
+        }
+
+    }
+
+    private Long parseId(Map<String, Object> fieldMap) {
+        Object objectId = fieldMap.get(FieldConstants.ID);
+        return NumberUtils.parseLong(objectId.toString());
+    }
+
+    /**
+     * 批量保存
+     *
+     * @param data
+     */
+    public abstract void batchSave(List<T> data);
+
+    /**
+     * 批量删除
+     *
+     * @param ids
+     */
+    public abstract void batchDelete(List<Long> ids);
+
+
+    //获取泛型参数
+    public Class<T> getMessageType() {
+        Type superClass = getClass().getGenericSuperclass();
+        if (superClass instanceof ParameterizedType) {
+            ParameterizedType parameterizedType = (ParameterizedType) superClass;
+            Type[] typeArgs = parameterizedType.getActualTypeArguments();
+            if (typeArgs.length > 0 && typeArgs[0] instanceof Class) {
+                return (Class<T>) typeArgs[0];
+            }
+        }
+        return null;
+    }
+}

+ 31 - 0
jzo2o-canal-sync/src/main/java/com/jzo2o/canal/model/CanalMqInfo.java

@@ -0,0 +1,31 @@
+package com.jzo2o.canal.model;
+
+import com.jzo2o.canal.constants.OperateType;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author itcast
+ */
+@Data
+public class CanalMqInfo implements Serializable {
+
+    private String database;
+    private String table;
+    private Boolean isDd1;
+    private String type;
+    private Long es;
+    private Long ts;
+
+    /**
+     * 数据列表
+     */
+    private List<Map<String, Object>> data;
+
+    public boolean getIsSave() {
+        return !OperateType.DELETE.equals(type);
+    }
+}

+ 46 - 0
jzo2o-canal-sync/src/main/java/com/jzo2o/canal/model/dto/CanalBaseDTO.java

@@ -0,0 +1,46 @@
+package com.jzo2o.canal.model.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * canal监听传递参数
+ * @author itcast
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class CanalBaseDTO implements Serializable {
+
+    /**
+     * 数据id
+     */
+    private Long id;
+    /**
+     * 所属数据库
+     */
+    private String database;
+    /**
+     * 所在表
+     */
+    private String table;
+    /**
+     * 是否是数据保存,两种类型,一种是保存数据,另一种是删除数据
+     */
+    private Boolean isSave;
+
+    /**
+     * 数据变更字段
+     */
+    private Map<String, Object> fieldMap;
+
+    private Long es;
+
+}

+ 49 - 0
jzo2o-common/pom.xml

@@ -0,0 +1,49 @@
+<?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>jzo2o-parent</artifactId>
+        <groupId>com.jzo2o</groupId>
+        <version>1.0-SNAPSHOT</version>
+        <relativePath>../jzo2o-parent</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>jzo2o-common</artifactId>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-expression</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.aspectj</groupId>
+            <artifactId>aspectjweaver</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 11 - 0
jzo2o-common/src/main/java/com/jzo2o/common/constants/CommonRedisConstants.java

@@ -0,0 +1,11 @@
+package com.jzo2o.common.constants;
+
+public class CommonRedisConstants {
+
+    public static class RedisKey {
+        /**
+         * 手机短信验证码,
+         */
+        public static final String VERIFY_CODE = "PHONE:CODE:VERIFY_CODE_%s_%s";
+    }
+}

+ 13 - 0
jzo2o-common/src/main/java/com/jzo2o/common/constants/CommonStatusConstants.java

@@ -0,0 +1,13 @@
+package com.jzo2o.common.constants;
+
+public class CommonStatusConstants {
+    /**
+     * 用户冻结
+     */
+    public static final int USER_STATUS_FREEZE = 1;
+
+    /**
+     * 用户状态正常
+     */
+    public static final int USER_STATUS_NORMAL = 0;
+}

+ 100 - 0
jzo2o-common/src/main/java/com/jzo2o/common/constants/ErrorInfo.java

@@ -0,0 +1,100 @@
+package com.jzo2o.common.constants;
+
+/**
+ * 通用异常信息,只放系统相关异常信息
+ *
+ * @author itcast
+ */
+public class ErrorInfo {
+    public static class Msg {
+        public static final String PROCESS_FAILD = "失败";
+        /**
+         * 请求异常默认异常信息
+         */
+        public static final String REQUEST_PARAM_ILLEGAL = "请求参数不合法";
+        /**
+         * 频繁请求
+         */
+        public static final String REQUEST_OPERATE_FREQUENTLY = "操作频繁,请稍后重试";
+        /**
+         * 请求超时
+         */
+        public static final String REQUEST_TIME_OUT = "请求超时";
+
+        /**
+         * 请求失败
+         */
+        public static final String REQUEST_FAILD = "请求失败";
+
+        public static final String REQUEST_FORBIDDEN = "拒绝访问";
+
+        public static final String REQUEST_UNAUTHORIZED = "身份未识别";
+
+        public static final String NO_PERMISSIONS = "无访问权限";
+
+        public static final String FORBIDDEN_OPERATION = "禁止操作";
+    }
+
+    public static class Code {
+
+        /**
+         * 未登录
+         */
+        public static final int NOT_LOGIN = 600;
+
+        /**
+         * 登录过期
+         */
+        public static final int LOGIN_TIMEOUT = 601;
+
+        /**
+         * token不合法
+         */
+        public static final int ILLEGAL_TOKEN = 602;
+
+        /**
+         * 无权限访问
+         */
+        public static final int NO_PERMISSIONS = 603;
+
+        /**
+         * 禁止操作
+         */
+        public static final int FORBIDDEN_OPERATION = 604;
+
+        /**
+         * 账号冻结
+         */
+        public static final int ACCOUNT_FREEZED = 605;
+
+        /**
+         * 派单拒单
+         */
+        public static final int DISPATCH_REJECT = 606;
+
+        /**
+         * 机构/服务端取消订单失败
+         */
+        public static final int ORDERS_CANCEL = 607;
+
+        /**
+         * 抢单失败
+         */
+        public static final int SEIZE_ORDERS_FAILD = 608;
+
+        /**
+         * 交易失败
+         */
+        public static final int TRADE_FAILED = 609;
+
+        /**
+         * 对接评价系统http请求失败
+         */
+        public static final int HTTP_EVALUATION_FAILED = 610;
+
+        /**
+         * 抢券失败
+         */
+        public static final int SEIZE_COUPON_FAILD = 611;
+    }
+}

+ 51 - 0
jzo2o-common/src/main/java/com/jzo2o/common/constants/HeaderConstants.java

@@ -0,0 +1,51 @@
+package com.jzo2o.common.constants;
+
+/**
+ * 请求/响应header常量
+ */
+public class HeaderConstants {
+    /**
+     * 当前用户信息
+     */
+    public static final String USER_INFO = "USER-INFO";
+
+    /**
+     * 当前用户类型
+     */
+    public static final String USER_TYPE = "USER-TYPE";
+
+    /**
+     * 异常捕获标识位
+     */
+    public static final String EXCEPTION_CATCH_FLAG = "EXCEPTION-CATCH-FLAG";
+
+    /**
+     * 异常标识 1 - 未见异常
+     */
+    public static final String EXCEPTION_CATCH_FLAG_1 = "1";
+    /**
+     * 异常标识 2 - 异常
+     */
+    public static final String EXCEPTION_CATCH_FLAG_2 = "2";
+
+
+    /**
+     * 请求id
+     */
+    public static final String REQUEST_ID = "REQUEST-ID";
+
+    /**
+     * 请求来源标识,1:外界访问,2:内部访问
+     */
+    public static final String REQUEST_ORIGIN_FLAG = "REQUEST-ORIGIN-FLAG";
+
+    /**
+     * 访问来源标识 1: 外界访问
+     */
+    public static final String REQUEST_ORIGIN_FLAG_OUTSIDE = "1";
+
+    /**
+     * 访问来源标识 2:内部访问
+     */
+    public static final String REQUEST_ORIGIN_FLAG_INNER = "2";
+}

+ 82 - 0
jzo2o-common/src/main/java/com/jzo2o/common/constants/MqConstants.java

@@ -0,0 +1,82 @@
+package com.jzo2o.common.constants;
+
+/**
+ * 静态变量
+ *
+ * @author zzj
+ * @version 1.0
+ */
+public interface MqConstants {
+    /**
+     * 默认延时时间为-1
+     */
+    int DEFAULT_DELAY = -1;
+
+    /**
+     * 低延迟时间:5秒
+     */
+    int LOW_DELAY = 5000;
+
+    /**
+     * 标准延迟时间:10秒
+     */
+    int NORMAL_DELAY = 10000;
+
+    /**
+     * 延迟交换机关键字
+     */
+    String DELAYED_KEYWORD = "delayed";
+
+    /**
+     * 表明是延迟队列
+     */
+    String DELAYED = "true";
+
+    /**
+     * 定义消息交换机,约定:1:类型都为topic,2:延迟队列命名由.delayed结尾
+     */
+    interface Exchanges {
+
+        /**
+         * 交易(支付)
+         */
+        String TRADE = "jzo2o.exchange.topic.trade";
+
+        /**
+         * 评分
+         */
+        String EVALUATION_SCORE = "evaluation.score";
+    }
+
+    /**
+     * 定义消息队列
+     */
+    interface Queues {
+
+        /**
+         * 订单微服务:更新支付状态
+         */
+        String ORDERS_TRADE_UPDATE_STATUS = "jzo2o.queue.orders.trade.update.Status";
+
+        /**
+         * 订单微服务:更新支付状态
+         */
+        String SCORE_STATISTICS = "jzo2o.queue.customer.score.statistics";
+    }
+
+    /**
+     * 定义路由key
+     */
+    interface RoutingKeys {
+
+        /**
+         * 更新支付状态
+         */
+        String TRADE_UPDATE_STATUS = "UPDATE_STATUS";
+
+        /**
+         * 分数统计
+         */
+        String SCORE_STATISTICS = "evaluation.score.statistics";
+    }
+}

+ 28 - 0
jzo2o-common/src/main/java/com/jzo2o/common/constants/UserType.java

@@ -0,0 +1,28 @@
+package com.jzo2o.common.constants;
+
+public class UserType {
+
+    /**
+     * c端用户
+     */
+    public static final int C_USER = 1;
+
+    /**
+     * 服务人员
+     */
+    public static final int WORKER = 2;
+
+    /**
+     * 机构人员
+     */
+    public static final int INSTITUTION = 3;
+    /**
+     * 运营人员
+     */
+    public static final int OPERATION = 4;
+
+    /**
+     * 系统
+     */
+    public static final int SYSTEM = 0;
+}

+ 20 - 0
jzo2o-common/src/main/java/com/jzo2o/common/enums/EnableStatusEnum.java

@@ -0,0 +1,20 @@
+package com.jzo2o.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public enum EnableStatusEnum {
+    UNKNOWAL(-1,"未知"),ENABLE(1,"启用"),DISABLE(0, "禁用");
+    private int status;
+    private String description;
+
+    public boolean equals(Integer status) {
+        return this.status == status;
+    }
+
+    public boolean equals(EnableStatusEnum enableStatusEnum) {
+        return enableStatusEnum != null && enableStatusEnum.status == this.getStatus();
+    }
+}

+ 12 - 0
jzo2o-common/src/main/java/com/jzo2o/common/enums/SmsBussinessTypeEnum.java

@@ -0,0 +1,12 @@
+package com.jzo2o.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public enum SmsBussinessTypeEnum {
+    INSTITION_REGISTER(1), INSTITUTION_RESET_PASSWORD(2), SERVE_STAFF_LOGIN(3);
+
+    private int type;
+}

+ 29 - 0
jzo2o-common/src/main/java/com/jzo2o/common/expcetions/BadRequestException.java

@@ -0,0 +1,29 @@
+package com.jzo2o.common.expcetions;
+
+import static com.jzo2o.common.constants.ErrorInfo.Msg.REQUEST_FAILD;
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+
+/**
+ * 请求异常,
+ * 使用场景:请求参数不合法,频繁请求
+ *
+ * @author itcast
+ */
+public class BadRequestException extends CommonException {
+
+    public BadRequestException() {
+        this(REQUEST_FAILD);
+    }
+
+    public BadRequestException(String message) {
+        super(HTTP_BAD_REQUEST, message);
+    }
+
+    public BadRequestException(Throwable throwable, String message) {
+        super(throwable, HTTP_BAD_REQUEST, message);
+    }
+
+    public BadRequestException(Throwable throwable) {
+        super(throwable, HTTP_BAD_REQUEST, REQUEST_FAILD);
+    }
+}

+ 36 - 0
jzo2o-common/src/main/java/com/jzo2o/common/expcetions/CommonException.java

@@ -0,0 +1,36 @@
+package com.jzo2o.common.expcetions;
+
+import cn.hutool.http.HttpStatus;
+import com.jzo2o.common.constants.ErrorInfo;
+import lombok.Data;
+
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+
+/**
+ * @author itcast
+ */
+@Data
+public class CommonException extends RuntimeException {
+    private int code;
+    private String message;
+
+    public CommonException() {
+        this.code = HTTP_BAD_REQUEST;
+        this.message = ErrorInfo.Msg.PROCESS_FAILD;
+    }
+
+    public CommonException(int code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public CommonException(Throwable throwable, int code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public CommonException(String message) {
+        this(HttpStatus.HTTP_INTERNAL_ERROR, message);
+    }
+
+}

+ 26 - 0
jzo2o-common/src/main/java/com/jzo2o/common/expcetions/DBException.java

@@ -0,0 +1,26 @@
+package com.jzo2o.common.expcetions;
+
+
+import static com.jzo2o.common.constants.ErrorInfo.Msg.PROCESS_FAILD;
+import static java.net.HttpURLConnection.HTTP_SERVER_ERROR;
+
+/**
+ * @author itcast
+ */
+public class DBException extends CommonException {
+    public DBException() {
+        super(HTTP_SERVER_ERROR, PROCESS_FAILD);
+    }
+
+    public DBException( String message) {
+        super(HTTP_SERVER_ERROR, message);
+    }
+
+    public DBException(Throwable throwable, String message) {
+        super(throwable, HTTP_SERVER_ERROR, message);
+    }
+
+    public DBException(Throwable throwable) {
+        super(throwable, HTTP_SERVER_ERROR, PROCESS_FAILD);
+    }
+}

+ 21 - 0
jzo2o-common/src/main/java/com/jzo2o/common/expcetions/ElasticSearchException.java

@@ -0,0 +1,21 @@
+package com.jzo2o.common.expcetions;
+
+/**
+ * @author itcast
+ */
+public class ElasticSearchException extends CommonException {
+    public ElasticSearchException() {
+    }
+
+    public ElasticSearchException(int code, String message) {
+        super(code, message);
+    }
+
+    public ElasticSearchException(Throwable throwable, int code, String message) {
+        super(throwable, code, message);
+    }
+
+    public ElasticSearchException(String message) {
+        super(message);
+    }
+}

+ 28 - 0
jzo2o-common/src/main/java/com/jzo2o/common/expcetions/ForbiddenOperationException.java

@@ -0,0 +1,28 @@
+package com.jzo2o.common.expcetions;
+
+import com.jzo2o.common.constants.ErrorInfo;
+
+/**
+ * 禁止操作异常
+ *
+ * @author itheima
+ */
+public class ForbiddenOperationException extends CommonException {
+
+    public ForbiddenOperationException() {
+        this(ErrorInfo.Msg.FORBIDDEN_OPERATION);
+    }
+
+    public ForbiddenOperationException(String message) {
+        super(ErrorInfo.Code.FORBIDDEN_OPERATION, message);
+    }
+
+    public ForbiddenOperationException(Throwable throwable, String message) {
+        super(throwable, ErrorInfo.Code.FORBIDDEN_OPERATION, message);
+    }
+
+    public ForbiddenOperationException(Throwable throwable) {
+        super(throwable, ErrorInfo.Code.FORBIDDEN_OPERATION, ErrorInfo.Msg.FORBIDDEN_OPERATION);
+    }
+
+}

+ 31 - 0
jzo2o-common/src/main/java/com/jzo2o/common/expcetions/MqException.java

@@ -0,0 +1,31 @@
+package com.jzo2o.common.expcetions;
+
+import lombok.Data;
+
+/**
+ * @author itcast
+ */
+@Data
+public class MqException extends CommonException{
+
+    /**
+     * mq失败消息
+     */
+    private String msg;
+    private Long mqId;
+
+    public MqException() {
+    }
+
+    public MqException(int code, String message) {
+        super(code, message);
+    }
+
+    public MqException(Throwable throwable, int code, String message) {
+        super(throwable, code, message);
+    }
+
+    public MqException(String message) {
+        super(message);
+    }
+}

+ 29 - 0
jzo2o-common/src/main/java/com/jzo2o/common/expcetions/RequestForbiddenException.java

@@ -0,0 +1,29 @@
+package com.jzo2o.common.expcetions;
+
+import static com.jzo2o.common.constants.ErrorInfo.Msg.REQUEST_FORBIDDEN;
+import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
+
+/**
+ * 权限校验被拒
+ *
+ * @author itheima
+ */
+public class RequestForbiddenException extends CommonException{
+
+    public RequestForbiddenException() {
+        this(REQUEST_FORBIDDEN);
+    }
+
+    public RequestForbiddenException(String message) {
+        super(HTTP_FORBIDDEN, message);
+    }
+
+    public RequestForbiddenException(Throwable throwable, String message) {
+        super(throwable, HTTP_FORBIDDEN, message);
+    }
+
+    public RequestForbiddenException(Throwable throwable) {
+        super(throwable, HTTP_FORBIDDEN, REQUEST_FORBIDDEN);
+    }
+
+}

+ 29 - 0
jzo2o-common/src/main/java/com/jzo2o/common/expcetions/RequestTimeoutException.java

@@ -0,0 +1,29 @@
+package com.jzo2o.common.expcetions;
+
+import static com.jzo2o.common.constants.ErrorInfo.Msg.REQUEST_TIME_OUT;
+import static java.net.HttpURLConnection.HTTP_CLIENT_TIMEOUT;
+
+/**
+ * 请求超时异常
+ *
+ * @author itheima
+ */
+public class RequestTimeoutException extends CommonException {
+
+    public RequestTimeoutException() {
+        this(REQUEST_TIME_OUT);
+    }
+
+    public RequestTimeoutException(String message) {
+        super(HTTP_CLIENT_TIMEOUT, message);
+    }
+
+    public RequestTimeoutException(Throwable throwable, String message) {
+        super(throwable, HTTP_CLIENT_TIMEOUT, message);
+    }
+
+    public RequestTimeoutException(Throwable throwable) {
+        super(throwable, HTTP_CLIENT_TIMEOUT, REQUEST_TIME_OUT);
+    }
+
+}

+ 30 - 0
jzo2o-common/src/main/java/com/jzo2o/common/expcetions/RequestUnauthorizedException.java

@@ -0,0 +1,30 @@
+package com.jzo2o.common.expcetions;
+
+import static com.jzo2o.common.constants.ErrorInfo.Msg.REQUEST_UNAUTHORIZED;
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+
+/**
+ * 身份校验异常,错误码401
+ * 使用场景:网关校验token,token不合法或token过期
+ *
+ * @author itheima
+ */
+public class RequestUnauthorizedException extends CommonException {
+
+    public RequestUnauthorizedException() {
+        this(REQUEST_UNAUTHORIZED);
+    }
+
+    public RequestUnauthorizedException(String message) {
+        super(HTTP_UNAUTHORIZED, message);
+    }
+
+    public RequestUnauthorizedException(Throwable throwable, String message) {
+        super(throwable, HTTP_UNAUTHORIZED, message);
+    }
+    public RequestUnauthorizedException(Throwable throwable) {
+        super(throwable, HTTP_UNAUTHORIZED, REQUEST_UNAUTHORIZED);
+    }
+
+
+}

+ 29 - 0
jzo2o-common/src/main/java/com/jzo2o/common/expcetions/ServerErrorException.java

@@ -0,0 +1,29 @@
+package com.jzo2o.common.expcetions;
+
+import static com.jzo2o.common.constants.ErrorInfo.Msg.PROCESS_FAILD;
+import static java.net.HttpURLConnection.HTTP_SERVER_ERROR;
+
+/**
+ * 服务器异常
+ *
+ * @author itcast
+ */
+public class ServerErrorException extends CommonException {
+
+    public ServerErrorException() {
+        this(PROCESS_FAILD);
+    }
+
+    public ServerErrorException(String message) {
+        super(HTTP_SERVER_ERROR, message);
+    }
+
+    public ServerErrorException(Throwable throwable, String message) {
+        super(throwable, HTTP_SERVER_ERROR, message);
+    }
+
+    public ServerErrorException(Throwable throwable) {
+        super(throwable, HTTP_SERVER_ERROR, PROCESS_FAILD);
+    }
+
+}

+ 27 - 0
jzo2o-common/src/main/java/com/jzo2o/common/expcetions/ServerUnavailableException.java

@@ -0,0 +1,27 @@
+package com.jzo2o.common.expcetions;
+
+import static com.jzo2o.common.constants.ErrorInfo.Msg.PROCESS_FAILD;
+import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
+
+/**
+ * 服务不可用,注册中心找不到对应服务
+ *
+ * @author itcast
+ */
+public class ServerUnavailableException extends CommonException {
+    public ServerUnavailableException() {
+        this(PROCESS_FAILD);
+    }
+
+    public ServerUnavailableException(String message) {
+        super(HTTP_UNAVAILABLE, message);
+    }
+
+    public ServerUnavailableException(Throwable throwable, String message) {
+        super(throwable, HTTP_UNAVAILABLE, message);
+    }
+
+    public ServerUnavailableException(Throwable throwable) {
+        super(throwable, HTTP_UNAVAILABLE, PROCESS_FAILD);
+    }
+}

+ 12 - 0
jzo2o-common/src/main/java/com/jzo2o/common/expcetions/StartException.java

@@ -0,0 +1,12 @@
+package com.jzo2o.common.expcetions;
+
+/**
+ * 应用启动异常
+ * @author itcast
+ */
+public class StartException extends CommonException{
+
+    public StartException(String message) {
+        super(message);
+    }
+}

+ 14 - 0
jzo2o-common/src/main/java/com/jzo2o/common/handler/ConvertHandler.java

@@ -0,0 +1,14 @@
+package com.jzo2o.common.handler;
+
+/**
+ * 特殊类型转换器
+ */
+public interface ConvertHandler<O, T> {
+    /**
+     * 特殊对象类型转换
+     *
+     * @param originObject 源对象
+     * @param targetObject 目标对象
+     */
+    void map(O originObject, T targetObject);
+}

+ 9 - 0
jzo2o-common/src/main/java/com/jzo2o/common/handler/RequestIdHandler.java

@@ -0,0 +1,9 @@
+package com.jzo2o.common.handler;
+
+/**
+ * 请求id获取
+ */
+public interface RequestIdHandler {
+
+    String getRequestId();
+}

+ 8 - 0
jzo2o-common/src/main/java/com/jzo2o/common/handler/UserInfoHandler.java

@@ -0,0 +1,8 @@
+package com.jzo2o.common.handler;
+
+import com.jzo2o.common.model.CurrentUserInfo;
+
+public interface UserInfoHandler {
+
+    CurrentUserInfo currentUserInfo();
+}

+ 31 - 0
jzo2o-common/src/main/java/com/jzo2o/common/model/CurrentUserInfo.java

@@ -0,0 +1,31 @@
+package com.jzo2o.common.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 当前用户信息
+ */
+@Data
+@AllArgsConstructor
+public class CurrentUserInfo implements Serializable {
+    /**
+     * 当前用户id
+     */
+    private Long id;
+    /**
+     * 用户名/昵称
+     */
+    private String name;
+    /**
+     * 头像
+     */
+    private String avatar;
+
+    /**
+     * 用户类型
+     */
+    private Integer userType;
+}

+ 24 - 0
jzo2o-common/src/main/java/com/jzo2o/common/model/Location.java

@@ -0,0 +1,24 @@
+package com.jzo2o.common.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 经纬度
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Location {
+    /**
+     * 经度
+     */
+    private Double lon;
+
+    /**
+     * 纬度
+     */
+    private Double lat;
+
+}

+ 119 - 0
jzo2o-common/src/main/java/com/jzo2o/common/model/PageResult.java

@@ -0,0 +1,119 @@
+package com.jzo2o.common.model;
+
+import cn.hutool.core.collection.CollUtil;
+import com.jzo2o.common.expcetions.CommonException;
+import com.jzo2o.common.handler.ConvertHandler;
+import com.jzo2o.common.utils.BeanUtils;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+@ApiModel(value = "分页数据消息体", description = "分页数据统一对象")
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class PageResult<T> {
+    /**
+     * 总页数
+     */
+    @ApiModelProperty(value = "总页数", required = true)
+    private Long pages = 0L;
+
+    /**
+     * 总条数
+     */
+    @ApiModelProperty(value = "总条数", required = true)
+    private Long total;
+
+    /**
+     * 数据列表
+     */
+    @ApiModelProperty(value = "数据列表", required = true)
+    private List<T> list = Collections.EMPTY_LIST;
+
+
+    /**
+     * 返回一个分页对象实例
+     *
+     * @return 分页数据对象
+     */
+    public static <T> PageResult<T> getInstance() {
+        return PageResult.<T>builder().build();
+    }
+
+
+    /**
+     * 对items进行类型转换
+     *
+     * @param origin 源分页数据对象
+     * @param clazz  指定items 属性的类型,不能为null
+     * @return 目标分页数据对象
+     */
+    public static <O, T> PageResult<T> of(PageResult<O> origin, Class<T> clazz) {
+        return of(origin, clazz, null);
+    }
+
+    /**
+     * 对items进行类型转换
+     *
+     * @param origin         源分页数据对象
+     * @param clazz          指定items 属性的类型,不能为null
+     * @param convertHandler 特殊对象类型转换器,可传null,即不进行特殊处理
+     * @return 目标分页数据对象
+     */
+    public static <O, T> PageResult<T> of(PageResult<O> origin, Class<T> clazz, ConvertHandler<O, T> convertHandler) {
+        //断言目标转换类型不为null
+        if (null == clazz) {
+            throw new CommonException("目标转换类型不能为null!");
+        }
+
+        //复制除items外的属性
+        PageResult<T> target = PageResult.getInstance();
+        BeanUtils.copyProperties(origin, target, "items");
+
+        //items为空,直接返回
+        if (CollUtil.isEmpty(origin.getList())) {
+            return target;
+        }
+
+        //对items进行类型转换
+        List<T> targetList = BeanUtils.copyToList(origin.getList(), clazz, convertHandler);
+        target.setList(targetList);
+
+        //封装分页数据
+        return target;
+    }
+
+    /**
+     * List{@link List}封装为分页数据对象
+     *
+     * @param list  item数据
+     * @param pages 页尺寸,可不传,数据不为空时默认为1
+     * @param total 总条数
+     * @return 目标分页数据对象
+     */
+    public static <T> PageResult<T> of(List<T> list, Integer pageSize, Long pages, Long total) {
+        PageResult pageResult = PageResult.<T>builder().pages(Optional.ofNullable(pages).orElse(0L))
+                .total(Optional.ofNullable(total).orElse(0L)).build();
+
+
+        if (CollUtil.isEmpty(list)) {
+            pageResult.setList(new ArrayList());
+            return pageResult;
+        }
+
+        pageResult.setList(list);
+        return pageResult;
+    }
+
+
+}

+ 79 - 0
jzo2o-common/src/main/java/com/jzo2o/common/model/Result.java

@@ -0,0 +1,79 @@
+package com.jzo2o.common.model;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.http.HttpStatus;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.nio.charset.StandardCharsets;
+
+@Data
+public class Result<T> {
+
+    public static final int SUCCESS = 200;
+    public static final String OK = "OK";
+    public static final int FAILED = 1;
+
+    public static final byte[] OK_BYTES =
+            String.format("{\"code\":%d,\"msg\":\"%s\",\"data\": {}}", SUCCESS, OK).getBytes(StandardCharsets.UTF_8);
+    public static final byte[] OK_PREFIX =
+            String.format("{\"code\":%d,\"msg\":\"%s\",\"data\": ", SUCCESS, OK).getBytes(StandardCharsets.UTF_8);
+    public static final byte[] OK_SUFFIX = "}".getBytes(StandardCharsets.UTF_8);
+    public static final byte[] OK_STR_PREFIX =
+            String.format("{\"code\":%d,\"msg\":\"%s\",\"data\":", SUCCESS, OK).getBytes(StandardCharsets.UTF_8);
+    public static final byte[] OK_STR_SUFFIX = "}".getBytes(StandardCharsets.UTF_8);
+    public static final String REQUEST_OK = "OK";
+
+    @ApiModelProperty(value = "业务状态码,200-成功,其它-失败")
+    private int code;
+    @ApiModelProperty(value = "响应消息", example = "OK")
+    private String msg;
+    @ApiModelProperty(value = "响应数据")
+    private T data;
+    @ApiModelProperty(value = "请求id", example = "1af123c11412e")
+    private String requestId;
+
+    public static Result<Void> ok() {
+        return new Result<Void>(HttpStatus.HTTP_OK, REQUEST_OK, null);
+    }
+
+    public static <T> Result<T> ok(T data) {
+        return new Result<>(HttpStatus.HTTP_OK, REQUEST_OK, data);
+    }
+
+    public static <T> Result<T> error(String msg) {
+        return new Result<>(HttpStatus.HTTP_BAD_REQUEST, msg, null);
+    }
+
+    public static <T> Result<T> error(int code, String msg) {
+        return new Result<>(code, msg, null);
+    }
+
+    public Result() {
+    }
+
+    public Result(int code, String msg, T data) {
+        this.code = code;
+        this.msg = msg;
+        this.data = data;
+    }
+
+    public boolean success() {
+        return code == HttpStatus.HTTP_OK;
+    }
+
+    public static byte[] plainOk() {
+        return OK_BYTES;
+    }
+
+    public static byte[] plainOk(byte[] data) {
+        if(data == null || data.length <= 0){
+            return OK_BYTES;
+        }
+        byte b = data[0];
+        if (b == 91 || b == 123) {
+            return ArrayUtil.addAll(OK_PREFIX, data, OK_SUFFIX);
+        }
+        return ArrayUtil.addAll(OK_STR_PREFIX, data, OK_STR_SUFFIX);
+    }
+}

+ 39 - 0
jzo2o-common/src/main/java/com/jzo2o/common/model/dto/PageQueryDTO.java

@@ -0,0 +1,39 @@
+package com.jzo2o.common.model.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+
+@Data
+@ApiModel("分页查询数据")
+public class PageQueryDTO {
+    @ApiModelProperty("页码数")
+    private Long pageNo=1L;
+    @ApiModelProperty("每页条数")
+    private Long pageSize=10L;
+    @ApiModelProperty("排序字段1")
+    private String orderBy1;
+    @ApiModelProperty("排序字段1是否升序")
+    private Boolean isAsc1 = false;
+
+    @ApiModelProperty("排序字段2,排序顺序排在排序字段1后边,如果排序字段1未设置,该字段也可以排序")
+    private String orderBy2;
+    @ApiModelProperty("排序字段2是否升序")
+    private Boolean isAsc2 = false;
+
+    /**
+     * 计算起始条数
+     * @return
+     */
+    public Long calFrom() {
+        return pageNo.intValue() * pageSize.intValue() * 1L;
+    }
+    @AllArgsConstructor
+    @Getter
+    public static class OrderBy {
+        private String orderBy;
+        private Boolean isAsc;
+    }
+}

+ 21 - 0
jzo2o-common/src/main/java/com/jzo2o/common/model/dto/StorageUploadResDTO.java

@@ -0,0 +1,21 @@
+package com.jzo2o.common.model.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 文件上传响应值
+ *
+ * @author itcast
+ * @create 2023/7/6 15:29
+ **/
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class StorageUploadResDTO {
+    /**
+     * 文件地址
+     */
+    private String url;
+}

+ 72 - 0
jzo2o-common/src/main/java/com/jzo2o/common/model/msg/TradeStatusMsg.java

@@ -0,0 +1,72 @@
+package com.jzo2o.common.model.msg;
+
+import cn.hutool.json.JSONUtil;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+/**
+ * 交易状态消息
+ *
+ * @author itcast
+ */
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TradeStatusMsg {
+
+    /**
+     * 交易单号
+     */
+    private Long tradingOrderNo;
+
+    /**
+     * 业务系统标识
+     */
+    private String productAppId;
+    /**
+     * 订单号
+     */
+    private Long productOrderNo;
+
+    /**
+     * 第三方支付的交易号
+     */
+    private String transactionId;
+
+    /**
+     * 支付渠道
+     */
+    private String tradingChannel;
+
+    /**
+     * 退款单号
+     */
+    private Long refundNo;
+
+    /**
+     * 支付/退款 状态名称
+     */
+    private String statusName;
+
+    /**
+     * 支付/退款 状态编码
+     */
+    private Integer statusCode;
+
+    /**
+     * 扩展信息
+     */
+    private String info;
+
+    /**
+     * 创建时间
+     */
+    private Long created;
+
+    public String toJson() {
+        return JSONUtil.toJsonStr(this);
+    }
+}

+ 53 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/ArrayUtils.java

@@ -0,0 +1,53 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.util.ArrayUtil;
+import com.jzo2o.common.handler.ConvertHandler;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 数组工具类
+ * @ClassName ArrayUtils
+ * @author itheima
+ * @since 2022/7/10 12:02
+ * @version 1.0.0
+ **/
+public class ArrayUtils extends ArrayUtil {
+
+    /**
+     * 将源数组转换成指定类型的列表
+     *
+     * @param originList  原始列表
+     * @param targetClazz 转换后列表元素的类型
+     * @param <R>         原始列表元素的类型
+     * @param <T>         目标列表元素的类型
+     * @return 目标类型的集合
+     */
+    public static <R, T> List<T> convert(R[] originList, Class<T> targetClazz) {
+       return convert(originList, targetClazz, null);
+
+    }
+
+    /**
+     * 将源数组转换成指定类型的列表
+     *
+     * @param originList  原始列表
+     * @param targetClazz 转换后列表元素的类型
+     * @param convert     转换特殊字段接口
+     * @param <R>         原始列表元素的类型
+     * @param <T>         目标列表元素的类型
+     * @return 目标类型的集合
+     */
+    public static <R, T> List<T> convert(R[] originList, Class<T> targetClazz, ConvertHandler<R, T> convert) {
+        if (isEmpty(originList)) {
+            return null;
+        }
+
+        return Arrays.stream(originList)
+                .map(origin -> BeanUtils.copyBean(origin, targetClazz, convert))
+                .collect(Collectors.toList());
+
+    }
+}

+ 66 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/AspectUtils.java

@@ -0,0 +1,66 @@
+package com.jzo2o.common.utils;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author itcast
+ */
+public class AspectUtils {
+
+    /**
+     * 获取被拦截方法对象
+     * MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象
+     * 所以应该使用反射获取当前对象的方法对象
+     */
+    public static Method getMethod(ProceedingJoinPoint pjp) {
+        //获取参数的类型
+        Signature sig = pjp.getSignature();
+        if (sig instanceof MethodSignature) {
+            MethodSignature methodSignature = (MethodSignature) sig;
+            return methodSignature.getMethod();
+        } else {
+            throw new IllegalArgumentException("It's not method");
+        }
+    }
+
+    /**
+     * 在aop切面中SPEL表达式对formatter进行格式化,
+     * 转换出指定的值
+     *
+     * @param formatter
+     * @param method
+     * @param args
+     * @return
+     */
+    public static String parse(String formatter, Method method, Object[] args) {
+        if(formatter == null || formatter.indexOf("{") < 0){
+            return formatter;
+        }
+        LocalVariableTableParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
+        return SpelUtils.parse(formatter, nameDiscoverer.getParameterNames(method), args);
+    }
+
+    /**
+     * 在aop切面中批量SPEL表达式对formatter进行格式化,
+     * @param formatters
+     * @param method
+     * @param args
+     * @return
+     */
+    public static List<Object> parseList(String[] formatters, Method method, Object[] args) {
+        if (ArrayUtils.isEmpty(formatters)) {
+            return null;
+        }
+        return Arrays.stream(formatters)
+                .map(formatter -> AspectUtils.parse(formatter, method, args))
+                .collect(Collectors.toList());
+    }
+}

+ 156 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/AssertUtils.java

@@ -0,0 +1,156 @@
+package com.jzo2o.common.utils;
+
+import com.jzo2o.common.constants.ErrorInfo;
+import com.jzo2o.common.expcetions.BadRequestException;
+
+import java.util.Map;
+
+/**
+ * 断言工具类,符合条件不会抛出异常,不符合条件则会抛出异常
+ *
+ * @author itheima
+ */
+public class AssertUtils {
+    /**
+     * 两个对象相同,不同则会抛出异常
+     *
+     * @param obj1    duixiang1
+     * @param obj2    对象2
+     * @param message 异常信息
+     */
+    public static void equals(Object obj1, Object obj2, String... message) {
+        if (obj1 == null || obj2 == null) {
+            handleException(message);
+            return;
+        }
+        if (obj1 == obj2) {
+            return;
+        }
+        if (!obj1.equals(obj2)) {
+            handleException(message);
+        }
+    }
+
+    /**
+     * 判断对象不为null,为null抛出异常
+     *
+     * @param obj     判断对象
+     * @param message 异常信息
+     */
+    public static void isNotNull(Object obj, String... message) {
+        if (obj == null) {
+            handleException(message);
+        }
+    }
+
+    /**
+     * 判断字符串不为空,为空抛出异常
+     *
+     * @param str     判空字符串
+     * @param message 异常信息
+     */
+    public static void isNotBlank(String str, String... message) {
+        if (StringUtils.isBlank(str)) {
+            handleException(message);
+        }
+    }
+
+    /**
+     * 判断boolean对象是否为真,为真不抛出异常
+     *
+     * @param boo     boolean对象
+     * @param message 异常信息
+     */
+    public static void isTrue(Boolean boo, String... message) {
+        if (!BooleanUtils.isTrue(boo)) {
+            handleException(message);
+        }
+    }
+
+    /**
+     * 判断集合是否不为空,为空抛出异常
+     *
+     * @param coll    判空集合对象
+     * @param message 异常信息
+     */
+    public static void isNotEmpty(Iterable<?> coll, String... message) {
+        if (CollUtils.isEmpty(coll)) {
+            handleException(message);
+        }
+    }
+
+    /**
+     * 判断map是否不为空,为空抛出异常
+     *
+     * @param map     判不为空map
+     * @param message 异常信息
+     */
+    public static void isNotEmpty(Map<?, ?> map, String... message) {
+        if (CollUtils.isEmpty(map)) {
+            handleException(message);
+        }
+    }
+
+
+    /**
+     * 判断boolean对象是否为假,为假则不抛出异常
+     *
+     * @param boo     boolean对象
+     * @param message 异常信息
+     */
+    public static void isFalse(Boolean boo, String... message) {
+        if (!BooleanUtils.isFalse(boo)) {
+            handleException(message);
+        }
+    }
+
+    /**
+     * 判断一个值是否是指定值中的一个,没有匹配到抛出异常信息
+     *
+     * @param t 需要比较的数据
+     * @param message 抛出异常信息
+     * @param targets 比较集合
+     * @param <T> 比较数据类型
+     */
+    public static <T> void in(T t,String message, T ... targets) {
+        for (T target : targets) {
+            if(t.equals(target)) {
+               return;
+            }
+        }
+        // 没有匹配到抛出指定信息
+        handleException(message);
+    }
+
+    /**
+     * 判断一个值是否是指定值不被包含在集合中,没有匹配到抛出异常信息
+     *
+     * @param t 需要比较的数据
+     * @param message 抛出异常信息
+     * @param targets 比较集合中
+     * @param <T> 比较数据类型
+     */
+    public static <T> void notIn(T t,String message, T ... targets) {
+        for (T target : targets) {
+            if(t.equals(target)) {
+                // 信息被匹配到抛出异常信息
+                handleException(message);
+            }
+        }
+
+    }
+    /**
+     * 异常信息处理
+     *
+     * @param message 异常信息
+     */
+    private static void handleException(String... message) {
+        String msg = ErrorInfo.Msg.REQUEST_PARAM_ILLEGAL;
+        if (message != null && message.length > 0) {
+            msg = message[0];
+        }
+        throw new BadRequestException(msg);
+    }
+
+
+}

+ 33 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/Base64Utils.java

@@ -0,0 +1,33 @@
+package com.jzo2o.common.utils;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+/**
+ * @author itcast
+ */
+public class Base64Utils {
+
+    /**
+     * 字符串编码
+     *
+     * @param content
+     * @return
+     */
+    public static byte[] encode(String content) {
+        if (null == content) {
+            return "".getBytes();
+        }
+        return Base64.getEncoder().encode(content.getBytes(StandardCharsets.UTF_8));
+    }
+
+    public static String encodeStr(String content) {
+        return new String(encode(content), StandardCharsets.UTF_8);
+    }
+
+    public static String decodeStr(String content) {
+        return new String(Base64.getDecoder().decode(content.getBytes(StandardCharsets.UTF_8)));
+    }
+
+
+}

+ 140 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/BeanUtils.java

@@ -0,0 +1,140 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.bean.copier.CopyOptions;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.jzo2o.common.handler.ConvertHandler;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 继承自 hutool 的BeanUtil,增加了bean转换时自定义转换器的功能
+ *
+ * @author itcast
+ */
+public class BeanUtils extends BeanUtil {
+
+    /**
+     * 将原对象转换成目标对象,对于字段不匹配的字段可以使用转换器处理
+     *
+     * @param source         原对象
+     * @param clazz          目标对象的class
+     * @param convertHandler 转换器
+     * @param <O>            原对象类型
+     * @param <T>            目标对象类型
+     * @return 目标对象
+     */
+    public static <O, T> T copyBean(O source, Class<T> clazz, ConvertHandler<O, T> convertHandler) {
+        T target = copyBean(source, clazz);
+        if (convertHandler != null) {
+            convertHandler.map(source, target);
+        }
+        return target;
+    }
+
+    /**
+     * 复制set集合中的属性到目标对象中
+     *
+     * @param originSet 原始对象集合
+     * @param targetType 目标对象类型
+     * @param convertHandler 目标对象数据转换器
+     * @return
+     * @param <O> 原始对象类型
+     * @param <T> 目标对象类型
+     */
+    public static <O, T> List<T> copyToList(Set<O> originSet, Class<T> targetType, ConvertHandler<O, T> convertHandler) {
+        if (CollUtils.isEmpty(originSet)) {
+            return null;
+        }
+        return originSet.stream().map(o -> copyBean(o, targetType, convertHandler)).collect(Collectors.toList());
+    }
+
+    /**
+     * 复制set集合中的属性到目标对象中
+     *
+     * @param originSet 原始对象集合
+     * @param targetType 目标对象类型
+     * @return
+     * @param <O> 原始对象类型
+     * @param <T> 目标对象类型
+     */
+    public static <O, T> List<T> copyToList(Set<O> originSet, Class<T> targetType) {
+        return copyToList(originSet, targetType, (ConvertHandler<O, T>) null);
+    }
+
+    /**
+     * 复制集合中的Bean属性
+     *
+     * @param originList     原Bean集合
+     * @param targetType     目标Bean类型
+     * @param convertHandler 特殊对象类型转换器,可传null,即不进行特殊处理
+     * @return 复制后的List
+     */
+    public static <O, T> List<T> copyToList(List<O> originList, Class<T> targetType, ConvertHandler<O, T> convertHandler) {
+        List<T> targetList = cn.hutool.core.bean.BeanUtil.copyToList(originList, targetType);
+        //特殊类型转换
+        if (CollUtil.isNotEmpty(targetList) && ObjectUtil.isNotEmpty(convertHandler)) {
+            for (int i = 0; i < originList.size(); i++) {
+                convertHandler.map(originList.get(i), targetList.get(i));
+            }
+        }
+        return targetList;
+    }
+
+    /**
+     * 将原对象转换成目标对象,对于字段不匹配的字段可以使用转换器处理
+     *
+     * @param source 原对象
+     * @param clazz  目标对象的class
+     * @param <R>    原对象类型
+     * @param <T>    目标对象类型
+     * @return 目标对象
+     */
+    public static <R, T> T copyBean(R source, Class<T> clazz) {
+        if (source == null) {
+            return null;
+        }
+        return toBean(source, clazz);
+    }
+
+    /**
+     * 将列表转换另一种类型的列表
+     *
+     * @param originList  原列表
+     * @param targetClass 目标类型class
+     * @param <R>         原列表元素类型
+     * @param <T>         目标列表元素类型
+     * @return 目标列表
+     */
+    @Deprecated
+    public static <R, T> List<T> copyList(List<R> originList, Class<T> targetClass) {
+        if (CollUtils.isEmpty(originList)) {
+            return CollUtils.emptyList();
+        }
+        return copyToList(originList, targetClass);
+    }
+
+    @Deprecated
+    public static <O, T> List<T> copyList(List<O> list, Class<T> clazz, ConvertHandler<O, T> convertHandler) {
+        if (list == null || list.size() == 0) {
+            return CollUtils.emptyList();
+        }
+        return list.stream().map(r -> copyBean(r, clazz, convertHandler)).collect(Collectors.toList());
+    }
+
+    public static <T> T copyIgnoreNull(T source,T target, Class<T> clazz){
+        //1.源数据和目标数据都转为map
+        Map<String, Object> oldData = BeanUtil.beanToMap(target,false,true);
+        Map<String, Object> newData = BeanUtil.beanToMap(source,false,true);
+
+        //2.用新数据覆盖旧数据
+        oldData.putAll(newData);
+
+        //3.map转为bean返回
+        return BeanUtil.mapToBean(oldData, clazz, false, new CopyOptions());
+    }
+}

+ 6 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/BooleanUtils.java

@@ -0,0 +1,6 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.util.BooleanUtil;
+
+public class BooleanUtils extends BooleanUtil {
+}

+ 18 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/ByteUtils.java

@@ -0,0 +1,18 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.util.ByteUtil;
+
+public class ByteUtils extends ByteUtil {
+
+    /**
+     * 将byte[] 数组转换成字符串,如果为空返回 ""
+     * @param content 字节内容
+     * @return 字符串值
+     */
+    public static String parse(byte[] content){
+        if(content == null || content.length <= 0) {
+            return StringUtils.EMPTY;
+        }
+        return new String(content);
+    }
+}

+ 9 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/ClassUtils.java

@@ -0,0 +1,9 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.util.ClassUtil;
+
+import java.lang.annotation.Annotation;
+
+public class ClassUtils extends ClassUtil {
+
+}

+ 199 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/CollUtils.java

@@ -0,0 +1,199 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.collection.IterUtil;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 继承自 hutool 的集合工具类
+ */
+public class CollUtils extends CollectionUtil {
+
+    public static <T> List<T> emptyList() {
+        return Collections.emptyList();
+    }
+
+    public static <T> Set<T> emptySet() {
+        return Collections.emptySet();
+    }
+
+    public static <K, V> Map<K, V> emptyMap() {
+        return Collections.emptyMap();
+    }
+
+    public static <T> Set<T> singletonSet(T t) {
+        return Collections.singleton(t);
+    }
+
+    public static <T> List<T> singletonList(T t) {
+        return Collections.singletonList(t);
+    }
+
+    public static List<Integer> convertToInteger(List<String> originList) {
+        return CollUtils.isNotEmpty(originList) ? originList.stream().map(NumberUtils::parseInt).collect(Collectors.toList()) : null;
+    }
+
+    public static List<Long> convertToLong(List<String> originLIst) {
+        return CollUtils.isNotEmpty(originLIst) ? originLIst.stream().map(NumberUtils::parseLong).collect(Collectors.toList()) : null;
+    }
+
+    /**
+     * 以 conjunction 为分隔符将集合转换为字符串 如果集合元素为数组、Iterable或Iterator,则递归组合其为字符串
+     *
+     * @param collection  集合
+     * @param conjunction 分隔符
+     * @param <T>         集合元素类型
+     * @return 连接后的字符串
+     * See Also: IterUtil.join(Iterator, CharSequence)
+     */
+    public static <T> String join(Collection<T> collection, CharSequence conjunction) {
+        if (null == collection || collection.isEmpty()) {
+            return null;
+        }
+        return IterUtil.join(collection.iterator(), conjunction);
+    }
+
+    public static <T> String joinIgnoreNull(Collection<T> collection, CharSequence conjunction) {
+        if (null == collection || collection.isEmpty()) {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        for (T t : collection) {
+            if (t == null) continue;
+            sb.append(t).append(",");
+        }
+        if (sb.length() <= 0) {
+            return null;
+        }
+        return sb.deleteCharAt(sb.length() - 1).toString();
+    }
+
+    /**
+     * 将元素加入到集合中,为null的过滤掉
+     *
+     * @param list 集合
+     * @param data 要添加的数据
+     * @param <T>  元素类型
+     */
+    public static <T> void add(Collection<T> list, T... data) {
+        if (list == null || ArrayUtils.isEmpty(data)) {
+            return;
+        }
+        for (T t : data) {
+            if (ObjectUtils.isNotEmpty(t)) {
+                list.add(t);
+            }
+        }
+    }
+
+    //将两个集合出现次数相加
+    public static Map<Long, Integer> union(Map<Long, Integer> map1, Map<Long, Integer> map2) {
+        if (CollUtils.isEmpty(map1)) {
+            return map2;
+        } else if (CollUtils.isEmpty(map2)) {
+            return map1;
+        }
+        for (Map.Entry<Long, Integer> entry : map1.entrySet()) {
+            Integer num = map2.get(entry.getKey());
+            map2.put(entry.getKey(), NumberUtils.null2Zero(num) + entry.getValue());
+        }
+        return map2;
+    }
+
+    public static <T, R> R getFiledOfFirst(List<T> list, Function<T, R> function) {
+        if (CollUtils.isEmpty(list)) {
+            return null;
+        }
+        return function.apply(list.get(0));
+    }
+
+    /**
+     * 将枚举集合表转换成列表
+     * @param enumeration 枚举集合
+     * @return 列表
+     * @param <T> 元素类型
+     */
+    public static <T> List<T> toList(Enumeration<T> enumeration) {
+        if (!enumeration.hasMoreElements()) {
+            return new ArrayList<>();
+        }
+        List<T> data = new ArrayList<>();
+        T current = null;
+        while ((current = enumeration.nextElement()) != null) {
+            data.add(current);
+        }
+        return data;
+    }
+
+    /**
+     * 获取数组为空的序号列表
+     *
+     * @param list
+     * @return
+     */
+    public static List<Integer> getIndexsOfNullData(List<?> list) {
+        if (isEmpty(list)) {
+            return null;
+        }
+        AtomicInteger counter = new AtomicInteger(0);
+        // filter 中的过滤条件不能调换,和表达式的执行顺序有关系
+        return list.stream().filter(x -> counter.incrementAndGet() >= 0 && x == null)
+                .map(x -> Integer.valueOf(counter.intValue() - 1))
+                .collect(Collectors.toList());
+    }
+
+    public static <T> List<T> valueofIndexs(List<T> list, List<Integer> indexs) {
+        if(CollUtils.isEmpty(indexs) || CollUtils.isEmpty(list)){
+            return null;
+        }
+
+        return indexs.stream()
+                .map(index -> list.get(index))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 获取集合中某一列的值集合
+     *
+     * @param list
+     * @param function
+     * @return
+     * @param <T>
+     * @param <R>
+     */
+    public static <T,R> List<R> getFieldValues(List<T> list, Function<T,R> function) {
+        if(isEmpty(list)){
+            return null;
+        }
+        return list.stream().map(function)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 将两个列表合并出第三个列表
+     * @param list1
+     * @param list2
+     * @return
+     * @param <T>
+     */
+    public static <T> List<T> union(List<T> list1, List<T> list2) {
+        if(isEmpty(list1)) {
+            return list2;
+        }
+        if(isEmpty(list2)){
+            return list1;
+        }
+        List<T> result = new ArrayList<>();
+        result.addAll(list1);
+        result.addAll(list2);
+        return result;
+    }
+
+    public static <K,V> Map<K,V> defaultIfEmpty(Map<K,V> originMap, Map<K,V> defaultMap) {
+        return CollUtils.isEmpty(originMap) ? defaultMap : originMap;
+    }
+}

+ 72 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/ComparatorUtils.java

@@ -0,0 +1,72 @@
+package com.jzo2o.common.utils;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.Objects;
+import java.util.function.Function;
+
+/**
+ * 原始比较器无法处理空值问题,特写此工具类
+ */
+public class ComparatorUtils {
+
+
+    /**
+     * 比较器,可以将空值元素移动到头部
+     *
+     * @param keyExtractor
+     * @param <T>
+     * @param <U>
+     * @return
+     */
+    public static <T, U extends Comparable<? super U>> Comparator<T> nullToFirstComparing(Function<? super T, ? extends U> keyExtractor) {
+        return nullComparing(true, keyExtractor);
+    }
+
+    /**
+     * 比较器,可以将空值元素移动到尾部
+     *
+     * @param keyExtractor
+     * @param <T>
+     * @param <U>
+     * @return
+     */
+    public static <T, U extends Comparable<? super U>> Comparator<T> nullToLastComparing(Function<? super T, ? extends U> keyExtractor) {
+        return nullComparing(false, keyExtractor);
+    }
+
+    /**
+     * 比较器,可以处理空值元素,将空值元素移动到头部或尾部
+     *
+     * @param nullFirst 空元素是否排到头部
+     * @return 比较器
+     */
+    public static <T, U extends Comparable<? super U>> Comparator<T> nullComparing(boolean nullFirst,
+                                                                                   Function<? super T, ? extends U> keyExtractor) {
+        Objects.requireNonNull(keyExtractor);
+
+        return (Comparator<T> & Serializable)
+                (c1, c2) -> {
+                    boolean c1NUll = ObjectUtils.isNull(c1) || ObjectUtils.isNull(keyExtractor.apply(c1));
+                    boolean c2NUll = ObjectUtils.isNull(c2) || ObjectUtils.isNull(keyExtractor.apply(c2));
+
+                    if (c1NUll) {
+                        return c2NUll ? 0 : (nullFirst ? -1 : 1);
+                    } else if (c2NUll) {
+                        return nullFirst ? 1 : -1;
+                    }
+                    return keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
+                };
+    }
+
+    /**
+     * 判断comparator是否反转过
+     *
+     * @param comparator
+     * @return
+     */
+    public static boolean isAsc(Comparator<?> comparator) {
+        Objects.requireNonNull(comparator);
+        return !comparator.getClass().getName().contains("ReverseComparator");
+    }
+}

+ 226 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/DateUtils.java

@@ -0,0 +1,226 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.date.LocalDateTimeUtil;
+
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+/**
+ * 时间工具类,用于本地时间操作,包含LocalDateTimeUtil的所有方法和自定义的LocalDateTime的操作方法及常量
+ *
+ * @author itheima
+ * @version 1.0.0 1.0
+ * @see 1.0
+ * @since 从哪个版本开始支持该类的功能
+ */
+public class DateUtils extends LocalDateTimeUtil {
+
+    public static final String DEFAULT_YEAR_FORMAT = "yyyy";
+    public static final String DEFAULT_MONTH_FORMAT = "yyyy-MM";
+    public static final String DEFAULT_MONTH_FORMAT_SLASH = "yyyy/MM";
+    public static final String DEFAULT_MONTH_FORMAT_EN = "yyyy年MM月";
+    public static final String DEFAULT_MONTH_FORMAT_COMPACT = "yyyyMM";
+    public static final String DEFAULT_WEEK_FORMAT = "yyyy-ww";
+    public static final String DEFAULT_WEEK_FORMAT_EN = "yyyy年ww周";
+    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
+    public static final String DEFAULT_DATE_FORMAT_EN = "yyyy年MM月dd日";
+    public static final String DEFAULT_DATE_FORMAT_COMPACT = "yyyyMMdd";
+    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
+    public static final String DEFAULT_DATE_TIME_COMPACT = "yyyyMMddHHmmss";
+    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
+    public static final String DATE_HOUR_FORMAT = "ddHH";
+    public static final String DAY = "DAY";
+    public static final String MONTH = "MONTH";
+    public static final String WEEK = "WEEK";
+    public static final long MAX_MONTH_DAY = 30L;
+    public static final long MAX_3_MONTH_DAY = 90L;
+    public static final long MAX_YEAR_DAY = 365L;
+
+
+    public static final DateTimeFormatter SIGN_DATE_SUFFIX_FORMATTER =
+            DateTimeFormatter.ofPattern(":yyyyMM");
+    public static final DateTimeFormatter POINTS_BOARD_SUFFIX_FORMATTER =
+            DateTimeFormatter.ofPattern("yyyyMM");
+
+    public static final String TIME_ZONE_8 = "GMT+8";
+
+    /**
+     * 获取utc时间
+     *
+     * @param localDateTime 转化时间
+     * @return utc时间
+     */
+    public static LocalDateTime getUTCTime(LocalDateTime localDateTime) {
+        ZoneId australia = ZoneId.of("Asia/Shanghai");
+        ZonedDateTime dateAndTimeInSydney = ZonedDateTime.of(localDateTime, australia);
+        ZonedDateTime utcDate = dateAndTimeInSydney.withZoneSameInstant(ZoneOffset.UTC);
+        return utcDate.toLocalDateTime();
+    }
+
+    /**
+     * 获取Asia时间
+     *
+     * @param localDateTime 转化时间
+     * @return Asia时间
+     */
+    public static LocalDateTime getAsiaTime(LocalDateTime localDateTime) {
+        ZoneId australia = ZoneId.of("Asia/Shanghai");
+        ZonedDateTime dateAndTimeInSydney = ZonedDateTime.of(localDateTime, ZoneOffset.UTC);
+        ZonedDateTime utcDate = dateAndTimeInSydney.withZoneSameInstant(australia);
+        return utcDate.toLocalDateTime();
+    }
+
+    /**
+     * 获取某一天的开始:0点0分
+     *
+     * @param localDateTime 指定日期
+     * @return 转换后的时间
+     */
+    public static LocalDateTime getDayStartTime(LocalDateTime localDateTime) {
+        if (localDateTime == null) {
+            return null;
+        }
+        return localDateTime.toLocalDate().atStartOfDay();
+    }
+
+    /**
+     * 获取某一天的结束:23点 59分 59秒的时间
+     *
+     * @param localDateTime 指定日期
+     * @return 转换后的时间
+     */
+    public static LocalDateTime getDayEndTime(LocalDateTime localDateTime) {
+        if (localDateTime == null) {
+            return null;
+        }
+        return LocalDateTime.of(localDateTime.toLocalDate(), LocalTime.MAX);
+    }
+
+    public static Date addDays(int i) {
+        Calendar c = Calendar.getInstance(TimeZone.getTimeZone(TIME_ZONE_8));
+        c.add(Calendar.DAY_OF_MONTH, i);
+        return c.getTime();
+    }
+
+
+    public static LocalDate getMonthBegin(LocalDate date) {
+        return LocalDate.of(date.getYear(), date.getMonth(), 1);
+    }
+
+    public static LocalDate getMonthEnd(LocalDate date) {
+        return LocalDate.of(date.getYear(), date.getMonthValue() + 1, 1).minusDays(1);
+    }
+
+    public static LocalDateTime getMonthBeginTime(LocalDate date) {
+        return LocalDate.of(date.getYear(), date.getMonth(), 1).atStartOfDay();
+    }
+
+    public static LocalDateTime getMonthEndTime(LocalDate date) {
+        return LocalDate.of(date.getYear(), date.getMonthValue() + 1, 1)
+                .minusDays(1).atTime(LocalTime.MAX);
+    }
+
+    public static LocalDateTime getWeekBeginTime(LocalDate now) {
+        return now.minusDays(now.getDayOfWeek().getValue() - 1).atStartOfDay();
+    }
+
+    public static LocalDateTime getWeekEndTime(LocalDate now) {
+        return LocalDateTime.of(now.plusDays(8 - now.getDayOfWeek().getValue()), LocalTime.MAX);
+    }
+
+    /**
+     * 获取最近15天日期(不包含当天),格式MM.dd
+     *
+     * @return
+     */
+    public static List<String> last15Day() {
+        // 1.定义日期列表
+        List<String> days = new ArrayList<>();
+        // 2.获取15天前的时间
+        LocalDateTime time = now().minusDays(15);
+        // 3.for循环遍历
+        for (int count = 0; count < 15; count++) {
+            // 3.1.格式化时间
+            days.add(String.format("%s.%s",
+                    NumberUtils.repair0(time.getMonthValue(), 2), NumberUtils.repair0(time.getDayOfMonth(), 2)));
+            // 3.2.日期加1天
+            time = time.plusDays(1);
+        }
+        // 4.返回结果
+        return days;
+    }
+
+    /**
+     * 获取当前时间s
+     *
+     * @return
+     */
+    public static int getCurrentTime() {
+        return (int) (System.currentTimeMillis() / 1000);
+    }
+
+    public static int getDay() {
+        return getDay(null);
+    }
+
+    public static int getDay(LocalDateTime localDateTime) {
+        if (localDateTime == null) {
+            localDateTime = now();
+        }
+        String format = format(localDateTime, DEFAULT_DATE_FORMAT_COMPACT);
+        return NumberUtils.parseInt(format);
+    }
+
+    /**
+     * 获取数字格式的日志
+     *
+     * @param localDateTime 日期
+     * @param format 格式模板,只支持纯数字模板
+     * @return
+     */
+    public static Long getFormatDate(LocalDateTime localDateTime, String format) {
+        String date = format(localDateTime, format);
+        return date == null ? null : NumberUtils.parseLong(date);
+    }
+
+    /**
+     * 获取数字格式的日志
+     *
+     * @param localDateTime 日期
+     * @param format 格式模板,只支持纯数字模板
+     * @return
+     */
+    public static Integer getIntFormatDate(LocalDateTime localDateTime, String format) {
+        String date = format(localDateTime, format);
+        return date == null ? null : NumberUtils.parseInt(date);
+    }
+
+    /**
+     * 获取最小的一个时间
+     * @param localDateTimes
+     * @return
+     */
+    public static LocalDateTime getMin(LocalDateTime... localDateTimes) {
+        if(localDateTimes == null || localDateTimes.length <= 0) {
+            return null;
+        }
+        if(localDateTimes.length == 1) {
+            return localDateTimes[0];
+        }
+        List<LocalDateTime> localDateTimeList = Arrays.asList(localDateTimes);
+        return localDateTimeList.stream().sorted().findFirst().orElse(null);
+    }
+
+    public static LocalDateTime getMax(LocalDateTime ... localDateTimes) {
+        if(localDateTimes == null || localDateTimes.length <= 0) {
+            return null;
+        }
+        if(localDateTimes.length == 1) {
+            return localDateTimes[0];
+        }
+        List<LocalDateTime> localDateTimeList = Arrays.asList(localDateTimes);
+        return localDateTimeList.stream().sorted(Comparator.reverseOrder()).findFirst().orElse(null);
+    }
+
+}

+ 6 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/EnumUtils.java

@@ -0,0 +1,6 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.util.EnumUtil;
+
+public class EnumUtils extends EnumUtil {
+}

+ 6 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/HttpUtils.java

@@ -0,0 +1,6 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.http.HttpUtil;
+
+public class HttpUtils extends HttpUtil {
+}

+ 10 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/IdUtils.java

@@ -0,0 +1,10 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.util.IdUtil;
+
+/**
+ * @author itcast
+ */
+public class IdUtils extends IdUtil {
+    public static final String ID = "id";
+}

+ 6 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/IoUtils.java

@@ -0,0 +1,6 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.io.IoUtil;
+
+public class IoUtils extends IoUtil {
+}

+ 6 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/JsonUtils.java

@@ -0,0 +1,6 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.json.JSONUtil;
+
+public class JsonUtils extends JSONUtil {
+}

+ 85 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/JwtTool.java

@@ -0,0 +1,85 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.json.JSON;
+import cn.hutool.jwt.JWT;
+import com.jzo2o.common.model.CurrentUserInfo;
+
+import java.nio.charset.Charset;
+import java.time.Duration;
+import java.util.Base64;
+import java.util.Date;
+
+public class JwtTool {
+    // 便于联调token有效期设置一个月 todo
+    private static final Duration JWT_TOKEN_TTL = Duration.ofMinutes(24 * 60 * 30);
+    private static final String PAYLOAD_USER_KEY = "user";
+    private static final String UserType = "userType";
+
+    private byte[] key;
+
+    public JwtTool(String keyStr) {
+        key = keyStr.getBytes();
+    }
+
+    /**
+     * 创建 jwttoken
+     *
+     * @param currentUserId 用户id
+     * @param name          用户姓名/昵称
+     * @param avatar        用户头像
+     * @return jwt token
+     */
+    public String createToken(Long currentUserId, String name, String avatar, int userType) {
+        // 名称base64编码,防止token无法解析
+        String encodeName = StringUtils.isEmpty(name) ? null : Base64Utils.encodeStr(name);
+        // 1.生成jws
+        return JWT.create()
+                .setPayload(PAYLOAD_USER_KEY, new CurrentUserInfo(currentUserId, encodeName, avatar, userType))
+                .setExpiresAt(new Date(System.currentTimeMillis() + JWT_TOKEN_TTL.toMillis()))
+                .setCharset(Charset.forName("UTF-8"))
+                .setKey(key)
+                .sign();
+    }
+
+    /**
+     * 从访问token中获取用户信息
+     *
+     * @param token 访问token
+     * @return 用户信息
+     */
+    public CurrentUserInfo parseToken(String token) {
+        try {
+            JWT jwt = JWT.of(token)
+                    .setKey(key);
+            Object payload = jwt.getPayload(PAYLOAD_USER_KEY);
+            return BeanUtils.isEmpty(payload) ? null : BeanUtils.copyBean(payload, CurrentUserInfo.class,((originObject, targetObject) -> {
+                // base64解码
+                if(StringUtils.isNotEmpty(targetObject.getName())) {
+                    targetObject.setName(Base64Utils.decodeStr(targetObject.getName()));
+                }
+            }));
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+
+    /**
+     * 从token中获取服务获取用户类型
+     *
+     * @param token 访问token
+     * @return 用户类型
+     */
+    public static Integer getUserType(String token) {
+        // 1.解码
+        byte[] decode = Base64.getMimeDecoder().decode(token.split("\\.")[1]);
+        // token明文字符串
+        String tokenPlainText = new String(decode);
+        // token详情字符串
+        String tokenInfo = tokenPlainText.substring(0, tokenPlainText.lastIndexOf('}') + 1);
+        JSON json = JsonUtils.parse(tokenInfo);
+        JSON user = json.getByPath(PAYLOAD_USER_KEY, JSON.class);
+        return user.getByPath(UserType, Integer.class);
+    }
+
+}

+ 19 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/LambdaUtils.java

@@ -0,0 +1,19 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.lang.func.Func1;
+import cn.hutool.core.lang.func.LambdaUtil;
+
+public class LambdaUtils extends LambdaUtil {
+
+    /**
+     * 获取lambda表达式Getter或Setter函数(方法)对应的字段名称,并转换成下划线格式,规则如下:
+     * getXxxx获取为xxxx,如getName得到name。
+     * setXxxx获取为xxxx,如setName得到name。
+     * isXxxx获取为xxxx,如isName得到name。
+     * 其它不满足规则的方法名抛出IllegalArgumentException
+     */
+    public static <T> String getUnderLineFieldName(Func1<T, ?> func) throws IllegalArgumentException {
+        String fieldName = getFieldName(func);
+        return StringUtils.isEmpty(fieldName) ? null : StringUtils.toUnderlineCase(fieldName);
+    }
+}

+ 50 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/LocationUtils.java

@@ -0,0 +1,50 @@
+package com.jzo2o.common.utils;
+
+/**
+ * 坐标相关工具类,处理经纬度
+ */
+public class LocationUtils {
+
+
+    /**
+     * 反转经纬度
+     *
+     * @param location
+     * @return
+     */
+    public static String reversLatLon(String location) {
+        return reversLatLon(location, ",");
+    }
+
+    /**
+     * 反转经纬度
+     *
+     * @param location
+     * @return
+     */
+    public static String reversLatLon(String location, String symbol) {
+        if (StringUtils.isEmpty(location) || StringUtils.isEmpty(symbol)) {
+            return location;
+        }
+        String[] locationSplit = location.split(symbol);
+        if (locationSplit.length != 2) {
+            return location;
+        }
+        return locationSplit[1] + symbol + locationSplit[0];
+    }
+
+    /**
+     * 数字经纬度返回字符串经纬度
+     *
+     * @param lon
+     * @param lat
+     * @return
+     */
+    public static String getLocation(Double lon, Double lat) {
+        if (lon == null || lat == null) {
+            return null;
+        }
+        return String.format("%s,%s", lon, lat);
+    }
+
+}

+ 151 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/NumberUtils.java

@@ -0,0 +1,151 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.util.NumberUtil;
+
+import java.math.BigDecimal;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class NumberUtils extends NumberUtil {
+
+
+    /**
+     * 如果number为空,将number转换为0,否则原数字返回
+     *
+     * @param number 原数值
+     * @return 整型数字,0或原数字
+     */
+    public static Integer null2Zero(Integer number){
+        return number == null ? 0 : number;
+    }
+
+    /**
+     * 如果number为空,将number转换为0,否则原数字返回
+     *
+     * @param number 原数值
+     * @return 整型数字,0或原数字
+     */
+    public static Double null2Zero(Double number){
+        return number == null ? 0 : number;
+    }
+
+    /**
+     * 如果是空值,返回默认数据;如果有值直接返回
+     * @param number
+     * @param defaultNumber
+     * @return
+     */
+    public static double null2Default(Double number, double defaultNumber) {
+        return number == null ? defaultNumber : number;
+    }
+
+    /**
+     * 如果number为空,将number转换为0L,否则原数字返回
+     *
+     * @param number  原数值
+     * @return 长整型数字,0L或原数字
+     */
+    public static Long null2Zero(Long number){
+        return number == null ? 0L : number;
+    }
+
+
+    public static Double setScale(Double number) {
+        return new BigDecimal(number)
+                .setScale(2, BigDecimal.ROUND_HALF_UP)
+                .doubleValue();
+    }
+    /**
+     * 比较两个数字是否相同,
+     * @param number1 数值1
+     * @param number2 数值2
+     * @return 是否一致
+     */
+    public static boolean equals(Integer number1, Integer number2) {
+        if(number1 == null || number2 == null){
+            return false;
+        }
+        return number1.equals(number2);
+    }
+
+    /**
+     * 数字除法保留指定小数位
+     * @param num1 被除数
+     * @param num2 除数
+     * @param scale 小数点位数
+     * @return 结果
+     */
+    public static Double divToDouble(Integer num1, Integer num2, int scale){
+        if(num2 == null || num2 ==0 || num1 == null || num1 == 0) {
+            return 0d;
+        }
+        return div(num1, num2, scale).doubleValue();
+    }
+
+    public static  Double max(List<Double> data){
+        if(CollUtils.isEmpty(data)){
+            return null;
+        }
+        return data.stream()
+                .max(Comparator.comparingDouble(num -> num))
+                .orElse(0d);
+    }
+    public static  Double min(List<Double> data){
+        if(CollUtils.isEmpty(data)){
+            return null;
+        }
+        return data.stream()
+                .min(Comparator.comparingDouble(num -> num))
+                .orElse(0d);
+    }
+
+    public static Double average(List<Double> data){
+        if(CollUtils.isEmpty(data)){
+            return 0d;
+        }
+        return data.stream()
+                .collect(Collectors.averagingDouble(Double::doubleValue));
+
+    }
+
+    public static Integer toInt(Object obj) {
+        return obj == null ? null
+                : obj instanceof Integer
+                ? (int) obj : null;
+    }
+
+    /**
+     * 取绝对值,如果为null,返回0
+     * @param number 数值
+     * @return 绝对值
+     */
+    public static int abs(Integer number) {
+        return number == null
+                ? 0
+                : Math.abs(number);
+    }
+
+    /**
+     * 数字格式化字符串,不足位数补0
+     *
+     * @param originNumber 原始数字
+     * @param digit 数字位数
+     * @return 字符串
+     */
+    public static String  repair0(Integer originNumber, Integer digit){
+        StringBuilder number = new StringBuilder(originNumber + "");
+        while (number.length() < digit) {
+            number.insert(0, "0");
+        }
+        return number.toString();
+    }
+
+    public static Integer null2Default(Integer originNumber, int defaultNumber) {
+        return originNumber == null ? defaultNumber : originNumber;
+    }
+
+    public static Long null2Default(Long originNumber, long defaultNumber) {
+        return originNumber == null ? defaultNumber : originNumber;
+    }
+}

+ 61 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/ObjectUtils.java

@@ -0,0 +1,61 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Object操作工具
+ **/
+public class ObjectUtils extends ObjectUtil {
+
+    /**
+     * 获取对象t的某个字段值
+     *
+     * @param t 获取对象t
+     * @param function labda表达式,例如Orders::get
+     * @return 对象t的某个字段值
+     * @param <T> 对象t的类型
+     * @param <R> 对象t对应字段的类型
+     */
+    public static <T,R> R get(T t, Function<T,R> function) {
+        if(t == null) {
+            return null;
+        }
+        return function.apply(t);
+    }
+
+    /**
+     * 转换成指定类型数据
+     * @param t 转换前对象t
+     * @param clazz 转换目标对象class
+     * @return 转换后的对象或数据
+     * @param <T> 转换前对象类型
+     * @param <R> 转换后对象类型
+     */
+    public static <T,R> R parse(T t, Class<R> clazz) {
+        if(t == null) {
+            return null;
+        }
+        if(!ClassUtils.equals(clazz, ClassUtils.getClassName(t, false), false)){
+            throw new RuntimeException("数据转换异常,数据转换类型错误");
+        }
+        return (R)t;
+    }
+
+    /**
+     * 如果有值直接返回,无值返回默认值
+     *
+     * @param originObject 原始值
+     * @param defaultObject 默认值
+     * @return T类型的对象
+     * @param <T> 返回值和入参的类型
+     */
+    public static <T> T defaultIfEmpty(final T originObject, final T defaultObject) {
+        return isNull(originObject) ? defaultObject : originObject;
+    }
+}

+ 20 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/ReflectUtils.java

@@ -0,0 +1,20 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.util.ReflectUtil;
+
+/**
+ * 反射工具
+ **/
+public class ReflectUtils extends ReflectUtil {
+
+    /**
+     * 判断一个类中是否含有指定字段
+     *
+     * @param fieldName 指定字段名称
+     * @param clazz     类class
+     * @return 是否包含 true/false
+     */
+    public static boolean containField(String fieldName, Class<?> clazz) {
+        return getField(clazz, fieldName) != null;
+    }
+}

+ 58 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/SpelUtils.java

@@ -0,0 +1,58 @@
+package com.jzo2o.common.utils;
+
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SpelUtils {
+
+    private static final Pattern PATTERN = Pattern.compile("(\\#\\{([^\\}]*)\\})");
+    /**
+     * 将模板中的表达式替换成args参数中的值
+     *
+     * @param formatter   模板
+     * @param paraNameArr 方法对应的参数名称
+     * @param args        方法参数值value,用来进行退换对应的表达式
+     * @return 模板替换后的字符串
+     *
+     * 例    format : counter:#{user.id}
+     *      paraNameAddr [user]
+     *      args [{"user":{"id":1}}]
+     *
+     *      转换后结果 -> counter:1
+     */
+    public static String parse(String formatter, String[] paraNameArr, Object[] args) {
+        if (StringUtils.isNotBlank(formatter) && formatter.indexOf("#") > -1) {
+
+            Matcher matcher = PATTERN.matcher(formatter);
+            //将正则表达式中#{}的值取出放在keys中
+            List<String> keys = new ArrayList<>();
+            while (matcher.find()) {
+                keys.add(matcher.group());
+            }
+            if (!CollUtils.isEmpty(keys)) {
+                //SPEL表达式对象
+                ExpressionParser parser = new SpelExpressionParser();
+                StandardEvaluationContext context = new StandardEvaluationContext();
+                //将名称和value一一对应
+                for (int i = 0; i < paraNameArr.length; i++) {
+                    context.setVariable(paraNameArr[i], args[i]);
+                }
+
+                for (String tmp : keys) {
+                    formatter = formatter.replace(tmp,
+                            //通过SPEL表达式获取对应的值,然后再替换掉原有值
+                            parser.parseExpression("#" + tmp.substring(2, tmp.length() - 1)).getValue(context, String.class));
+                }
+                return formatter;
+            }
+        }
+        return null;
+    }
+
+}

+ 28 - 0
jzo2o-common/src/main/java/com/jzo2o/common/utils/StringUtils.java

@@ -0,0 +1,28 @@
+package com.jzo2o.common.utils;
+
+import cn.hutool.core.util.StrUtil;
+
+/**
+ * 字符串工具类,继承了{@link StrUtil}
+ **/
+public class StringUtils extends StrUtil {
+    private static final byte[] TRUE = new byte[]{'T','R','U','E'};
+    /**
+     * 判断是否是字符串
+     * @param bytes
+     * @return
+     */
+    public static boolean isStr(byte[] bytes) {
+        if(TRUE.equals(bytes)) {
+            return false;
+        }
+        for (byte word : bytes) {
+            //判断是数字
+            if(word >= 48 && word <= 57) {
+                continue;
+            }
+            return true;
+        }
+        return false;
+    }
+}

+ 53 - 0
jzo2o-es/pom.xml

@@ -0,0 +1,53 @@
+<?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>jzo2o-parent</artifactId>
+        <groupId>com.jzo2o</groupId>
+        <version>1.0-SNAPSHOT</version>
+        <relativePath>../jzo2o-parent/pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>jzo2o-es</artifactId>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.jzo2o</groupId>
+            <artifactId>jzo2o-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.elasticsearch.client</groupId>
+            <artifactId>elasticsearch-rest-high-level-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-autoconfigure</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>co.elastic.clients</groupId>
+            <artifactId>elasticsearch-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 60 - 0
jzo2o-es/src/main/java/com/jzo2o/es/config/EsConfiguration.java

@@ -0,0 +1,60 @@
+package com.jzo2o.es.config;
+
+import cn.hutool.core.date.DatePattern;
+import com.jzo2o.es.core.ElasticSearchTemplate;
+import com.jzo2o.es.core.impl.ElasticSearchTemplateImpl;
+import com.jzo2o.es.properties.EsProperties;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.json.jackson.JacksonJsonpMapper;
+import co.elastic.clients.transport.ElasticsearchTransport;
+import co.elastic.clients.transport.rest_client.RestClientTransport;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import org.apache.http.HttpHost;
+import org.elasticsearch.client.RestClient;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+@Configuration
+@EnableConfigurationProperties(EsProperties.class)
+public class EsConfiguration {
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    static {
+        MAPPER.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
+        MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+
+        JavaTimeModule javaTimeModule = new JavaTimeModule();
+        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);
+        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
+        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
+        MAPPER.registerModule(javaTimeModule);
+
+    }
+
+    @Bean
+    public ElasticsearchClient esClient(EsProperties esProperties) {
+        RestClient restClient = RestClient.builder(new HttpHost(esProperties.getHost(), esProperties.getPort())).build();
+        // Create the transport with a Jackson mapper
+        // 使用自定义json序列化
+        JacksonJsonpMapper jacksonJsonpMapper = new JacksonJsonpMapper(MAPPER);
+
+        ElasticsearchTransport transport = new RestClientTransport(restClient, jacksonJsonpMapper);
+        // And create the API client
+        return new ElasticsearchClient(transport);
+    }
+
+    @Bean
+    public ElasticSearchTemplate template(ElasticsearchClient elasticsearchClient) {
+        return new ElasticSearchTemplateImpl(elasticsearchClient);
+    }
+}

+ 5 - 0
jzo2o-es/src/main/java/com/jzo2o/es/constants/FieldConstants.java

@@ -0,0 +1,5 @@
+package com.jzo2o.es.constants;
+
+public class FieldConstants {
+    public static final String ID = "id";
+}

+ 9 - 0
jzo2o-es/src/main/java/com/jzo2o/es/core/ElasticSearchTemplate.java

@@ -0,0 +1,9 @@
+package com.jzo2o.es.core;
+
+import com.jzo2o.es.core.operations.DocumentOperations;
+import com.jzo2o.es.core.operations.IndexOperations;
+
+public interface ElasticSearchTemplate {
+    DocumentOperations opsForDoc();
+    IndexOperations opsForIndex();
+}

+ 28 - 0
jzo2o-es/src/main/java/com/jzo2o/es/core/impl/ElasticSearchTemplateImpl.java

@@ -0,0 +1,28 @@
+package com.jzo2o.es.core.impl;
+
+import com.jzo2o.es.core.ElasticSearchTemplate;
+import com.jzo2o.es.core.operations.DefaultDocumentOperations;
+import com.jzo2o.es.core.operations.DefaultIndexOperations;
+import com.jzo2o.es.core.operations.DocumentOperations;
+import com.jzo2o.es.core.operations.IndexOperations;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ElasticSearchTemplateImpl implements ElasticSearchTemplate {
+    private final ElasticsearchClient elasticsearchClient;
+
+    public ElasticSearchTemplateImpl(ElasticsearchClient elasticsearchClient) {
+        this.elasticsearchClient = elasticsearchClient;
+    }
+
+    @Override
+    public DocumentOperations opsForDoc() {
+        return new DefaultDocumentOperations(elasticsearchClient);
+    }
+
+    @Override
+    public IndexOperations opsForIndex() {
+        return new DefaultIndexOperations(elasticsearchClient);
+    }
+}

+ 333 - 0
jzo2o-es/src/main/java/com/jzo2o/es/core/operations/DefaultDocumentOperations.java

@@ -0,0 +1,333 @@
+package com.jzo2o.es.core.operations;
+
+
+import co.elastic.clients.elasticsearch._types.Result;
+import com.jzo2o.common.expcetions.CommonException;
+import com.jzo2o.common.expcetions.ElasticSearchException;
+import com.jzo2o.common.model.PageResult;
+import com.jzo2o.common.model.dto.PageQueryDTO;
+import com.jzo2o.common.utils.*;
+import com.jzo2o.es.constants.FieldConstants;
+import com.jzo2o.es.utils.TermUtils;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.GeoDistanceType;
+import co.elastic.clients.elasticsearch._types.SortOrder;
+import co.elastic.clients.elasticsearch._types.WriteResponseBase;
+import co.elastic.clients.elasticsearch._types.query_dsl.FieldAndFormat;
+import co.elastic.clients.elasticsearch._types.query_dsl.TermsQuery;
+import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField;
+import co.elastic.clients.elasticsearch.core.*;
+import co.elastic.clients.elasticsearch.core.search.Hit;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 后期优化分页功能 todo,临时使用
+ */
+@Slf4j
+public class DefaultDocumentOperations implements DocumentOperations {
+    private final ElasticsearchClient elasticsearchClient;
+
+    public DefaultDocumentOperations(ElasticsearchClient elasticsearchClient) {
+        this.elasticsearchClient = elasticsearchClient;
+    }
+
+    @Override
+    public <T> Boolean insert(String index, T document) {
+
+        try {
+            CreateResponse createResponse = elasticsearchClient.create(builder -> builder.id(getId(document)).document(document).index(index));
+            log.debug("create document response : {}", createResponse);
+            boolean success = isSuccess(createResponse);
+            return success;
+
+        } catch (IOException e) {
+//            e.printStackTrace();
+            log.error(e.getMessage(),e);
+            throw new CommonException(500,e.getMessage());
+        }
+//        return false;
+    }
+
+    @Override
+    public <T> Boolean batchInsert(String index, List<T> documents) {
+        BulkRequest.Builder br = new BulkRequest.Builder();
+        for (T document : documents) {
+            br.operations(op -> op.index(idx -> idx.index(index)
+                    .id(getId(document))
+                    .document(document)));
+        }
+        try {
+            BulkResponse bulk = elasticsearchClient.bulk(br.build());
+            Boolean success = isSuccess(bulk);
+            return success;
+        } catch (IOException e) {
+            log.error(e.getMessage(),e);
+            throw new CommonException(500,e.getMessage());
+        }
+    }
+
+    @Override
+    public <T> Boolean batchUpsert(String index, List<T> documents) {
+        if (CollUtils.isEmpty(documents)) {
+            return false;
+        }
+        List<String> ids = documents.stream().map(document -> getId(document)).collect(Collectors.toList());
+        List<?> documentInEs = this.findByIds(index, ids, Arrays.asList(FieldConstants.ID), documents.get(0).getClass());
+        List<String> idsInEs = CollUtils.isEmpty(documentInEs) ? new ArrayList<>() : documentInEs.stream().map(document -> getId(document)).collect(Collectors.toList());
+
+        BulkRequest.Builder builder = new BulkRequest.Builder();
+        for (T document : documents) {
+            String id = getId(document);
+            boolean exists = idsInEs.contains(id);
+
+            builder.operations(op -> {
+                if (exists) {
+                    op.update(u -> u.action(a -> a.doc(document)).index(index).id(id));
+                } else {
+                    op.index(idx -> idx.index(index)
+                            .id(id)
+                            .document(document));
+                }
+                return op;
+            });
+        }
+        try {
+            BulkResponse bulk = elasticsearchClient.bulk(builder.build());
+            Boolean success = isSuccess(bulk);
+            return success;
+        } catch (IOException e) {
+            log.error(e.getMessage(),e);
+            throw new CommonException(500,e.getMessage());
+        }
+    }
+
+    @Override
+    public <T> Boolean updateById(String index, T document) {
+        Object id = ReflectUtils.getFieldValue(document, IdUtils.ID);
+        if (id == null) {
+            throw new ElasticSearchException("es更新失败,id为空");
+        }
+        try {
+            // 2.数据更新
+            UpdateResponse<?> response = elasticsearchClient.update(u -> u
+                            .index(index)
+                            .id(id.toString())
+                            .doc(document)
+                    , document.getClass());
+            Boolean success = isSuccess(response);
+            return success;
+
+        } catch (IOException e) {
+            log.error(e.getMessage(),e);
+            throw new CommonException(500,e.getMessage());
+        }
+    }
+
+    @Override
+    public <ID> Boolean deleteById(String index, ID id) {
+
+        try {
+            // 2.数据更新
+            DeleteResponse response = elasticsearchClient.delete(builder -> builder.id(id.toString()).index(index));
+            Boolean success = isSuccess(response);
+            return success;
+        } catch (IOException e) {
+            log.error(e.getMessage(),e);
+            throw new CommonException(500,e.getMessage());
+        }
+
+    }
+
+    @Override
+    public <ID> Boolean batchDelete(String index, List<ID> ids) {
+        BulkRequest.Builder builder = new BulkRequest.Builder();
+
+
+        ids.stream().forEach(id ->
+                builder.operations(b -> b.delete(d -> d.index(index).id(id.toString())))
+        );
+
+
+        try {
+            BulkResponse bulk = elasticsearchClient.bulk(builder.build());
+            Boolean success = isSuccess(bulk);
+            return success;
+        } catch (Exception e) {
+            log.error(e.getMessage(),e);
+            throw new CommonException(500,e.getMessage());
+        }
+
+    }
+
+    @Override
+    public <T, ID> T findById(String index, ID id, Class<T> clazz) {
+        try {
+            GetResponse<T> response = elasticsearchClient.get(GetRequest.of(builder -> builder.id(id.toString()).index(index)), clazz);
+            return response.source();
+        } catch (IOException e) {
+            log.error(e.getMessage(),e);
+            throw new CommonException(500,e.getMessage());
+        }
+    }
+
+    @Override
+    public <T, ID> List<T> findByIds(String index, List<ID> ids, Class<T> clazz) {
+        SearchRequest.Builder searchRequestBuild = new SearchRequest.Builder();
+        TermsQuery termsQuery = TermsQuery.of(t -> t.field(FieldConstants.ID).terms(new TermsQueryField.Builder().value(TermUtils.parse(ids)).build()));
+
+        searchRequestBuild.index(index)
+                .query(builder -> builder.terms(termsQuery));
+        try {
+
+            SearchResponse<T> searchResponse = elasticsearchClient.search(searchRequestBuild.build(), clazz);
+            return searchResponse.hits().hits()
+                    .stream()
+                    .map(tHit -> tHit.source())
+                    .collect(Collectors.toList());
+        } catch (IOException e) {
+            log.error(e.getMessage(),e);
+            throw new CommonException(500,e.getMessage());
+        }
+    }
+
+    @Override
+    public <T, ID> List<T> findByIds(String index, List<ID> ids, List<String> fields, Class<T> clazz) {
+        SearchRequest.Builder searchRequestBuild = new SearchRequest.Builder();
+        TermsQuery termsQuery = TermsQuery.of(t -> t.field(FieldConstants.ID).terms(new TermsQueryField.Builder().value(TermUtils.parse(ids)).build()));
+        searchRequestBuild.index(index)
+                .query(builder -> builder.terms(termsQuery));
+        if (CollUtils.isNotEmpty(fields)) {
+            List<FieldAndFormat> fieldAndFormats = fields.stream().map(field -> FieldAndFormat.of(builder -> builder.field(field))).collect(Collectors.toList());
+            searchRequestBuild.fields(fieldAndFormats);
+        }
+        try {
+
+            SearchResponse<T> searchResponse = elasticsearchClient.search(searchRequestBuild.build(), clazz);
+            return searchResponse.hits().hits()
+                    .stream()
+                    .map(tHit -> tHit.source())
+                    .collect(Collectors.toList());
+        } catch (IOException e) {
+            log.error(e.getMessage(),e);
+            throw new CommonException(500,e.getMessage());
+        }
+
+    }
+
+    @Override
+    public <T> PageResult<T> findForPage(PageQueryDTO pageQueryDTO, Class<T> targetClass) {
+
+        SearchRequest.Builder builder = new SearchRequest.Builder();
+        builder.from(pageQueryDTO.calFrom().intValue());
+        builder.size(pageQueryDTO.getPageSize().intValue());
+
+        SearchRequest searchRequest = new SearchRequest.Builder().build();
+        try {
+
+            SearchResponse<T> search = elasticsearchClient.search(searchRequest, targetClass);
+            long total = search.hits().total().value();
+            List<T> data = search.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
+            return PageResult.of(data,Integer.parseInt(pageQueryDTO.getPageSize()+""),pageQueryDTO.getPageNo(),total);
+        } catch (IOException e) {
+            log.error(e.getMessage(),e);
+            throw new CommonException(500,e.getMessage());
+        }
+    }
+
+    @Override
+    public <T> List<T> searchByWithGeo(String index, SearchRequest.Builder searchBuilder, String locationName, String location, double distance, String sortBy, Boolean isAsc, int size, Class<T> clazz) {
+        // 坐标,距离
+        searchBuilder.query(query ->
+            query.bool(q ->
+                q.filter(filter->
+                    filter.geoDistance(geo->{
+                        geo.distance(distance + "km");
+                        geo.field(locationName);
+                        geo.location(location1 -> location1.text(location));
+                        geo.distanceType(GeoDistanceType.Arc);
+                        return geo;}))
+            )
+        );
+        // 自定排序字段和排序
+        if(StringUtils.isNotEmpty(sortBy)){
+            searchBuilder.sort(sortOptionsBuilder ->
+                sortOptionsBuilder.field(fieldSortBuilder->{
+                    fieldSortBuilder.field(sortBy);
+                    fieldSortBuilder.order(BooleanUtils.isTrue(isAsc) ? SortOrder.Asc : SortOrder.Desc);
+                    return fieldSortBuilder;
+                })
+            );
+        }
+        searchBuilder.size(size);
+        searchBuilder.index(index);
+        try {
+            SearchResponse<T> searchResponse = elasticsearchClient.search(searchBuilder.build(), clazz);
+
+            return searchResponse.hits().hits()
+                    .stream()
+                    .map(tHit -> tHit.source())
+                    .collect(Collectors.toList());
+        }catch (IOException e){
+            log.error(e.getMessage(),e);
+            throw new CommonException(500,e.getMessage());
+        }
+    }
+
+    @Override
+    public <T> SearchResponse<T> search(SearchRequest searchRequest, Class<T> clazz) {
+        try {
+            return elasticsearchClient.search(searchRequest,clazz);
+        }catch (IOException e){
+            log.error(e.getMessage(),e);
+            throw new CommonException(500,e.getMessage());
+        }
+    }
+
+    private boolean isSuccess(WriteResponseBase writeResponseBase) {
+        //todo
+        return writeResponseBase != null &&
+                (Result.Created.equals(writeResponseBase.result()) ||
+                        Result.Deleted.equals(writeResponseBase.result()) ||
+                        Result.Updated.equals(writeResponseBase.result()) ||
+                        Result.NoOp.equals(writeResponseBase.result()));
+    }
+
+    private Boolean isSuccess(BulkResponse response) {
+        //todo
+        log.debug("bulk response : {}", JsonUtils.toJsonStr(response));
+        if(response.errors()) {
+            return false;
+        }
+        return response.items().stream()
+                .filter(item -> item.status() != 200)
+                .map(item -> false)
+                .findFirst().orElse(true);
+    }
+
+    public Boolean isSuccess(DeleteByQueryResponse deleteByQueryResponse) {
+        return deleteByQueryResponse.deleted() > 0;
+    }
+
+    /**
+     * 获取文档id, 如果文档中设置了id,使用文档的id,如果未设置,使用雪花算法生成
+     *
+     * @param document
+     * @param <T>
+     * @return
+     */
+    private <T> String getId(T document) {
+        Object objectId = ReflectUtils.getFieldValue(document, IdUtils.ID);
+        if (objectId == null) {
+            objectId = IdUtils.objectId();
+        }
+        return objectId.toString();
+    }
+
+
+}

+ 16 - 0
jzo2o-es/src/main/java/com/jzo2o/es/core/operations/DefaultIndexOperations.java

@@ -0,0 +1,16 @@
+package com.jzo2o.es.core.operations;
+
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @author itcast
+ */
+@Slf4j
+public class DefaultIndexOperations implements IndexOperations{
+    private final ElasticsearchClient elasticsearchClient;
+
+    public DefaultIndexOperations(ElasticsearchClient elasticsearchClient) {
+        this.elasticsearchClient = elasticsearchClient;
+    }
+}

+ 85 - 0
jzo2o-es/src/main/java/com/jzo2o/es/core/operations/DocumentOperations.java

@@ -0,0 +1,85 @@
+package com.jzo2o.es.core.operations;
+
+import com.jzo2o.common.model.PageResult;
+import com.jzo2o.common.model.dto.PageQueryDTO;
+import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
+import co.elastic.clients.elasticsearch.core.SearchRequest;
+import co.elastic.clients.elasticsearch.core.SearchResponse;
+import co.elastic.clients.util.ObjectBuilder;
+
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * @author itcast
+ */
+public interface DocumentOperations {
+
+    /**
+     * 新增文档
+     *
+     * @param index    文档所属索引
+     * @param document 文档
+     * @param <T>      document类型
+     * @return 文档创建结果
+     */
+    <T> Boolean insert(String index, T document);
+
+    <T> Boolean batchInsert(String index, List<T> documents);
+
+    <T> Boolean batchUpsert(String index, List<T> documents);
+
+    <T> Boolean updateById(String index, T document);
+
+    <ID> Boolean deleteById(String index, ID id);
+
+    /**
+     * 批量删除
+     * @param index
+     * @param ids
+     * @return
+     * @param <ID>
+     */
+    <ID> Boolean batchDelete(String index, List<ID> ids);
+
+    <T, ID> T findById(String index, ID id, Class<T> clazz);
+
+    <T, ID> List<T> findByIds(String index, List<ID> ids, Class<T> clazz);
+
+    <T, ID> List<T> findByIds(String index, List<ID> ids, List<String> includes, Class<T> clazz);
+
+    /**
+     * 还未写完 todo
+     * @param pageQueryDTO
+     * @param targetClass
+     * @return
+     * @param <T>
+     */
+
+    <T> PageResult<T> findForPage(PageQueryDTO pageQueryDTO, Class<T> targetClass);
+
+    /**
+     * 根据条件使用经纬度范围检索,先经过条件筛选,然后距离查询
+     * @param index
+     * @param searchBuilder
+     * @param location
+     * @param locationName
+     * @param distance
+     * @param sortBy
+     * @param isAsc
+     * @param size
+     * @return
+     * @param <T>
+     */
+    <T> List<T> searchByWithGeo(String index,
+                                SearchRequest.Builder searchBuilder,
+                                String locationName,
+                                String location,
+                                double distance,
+                                String sortBy,
+                                Boolean isAsc,
+                                int size,
+                                Class<T> clazz);
+
+    <T> SearchResponse<T> search(SearchRequest searchRequest, Class<T> clazz);
+}

+ 4 - 0
jzo2o-es/src/main/java/com/jzo2o/es/core/operations/IndexOperations.java

@@ -0,0 +1,4 @@
+package com.jzo2o.es.core.operations;
+
+public interface IndexOperations {
+}

+ 9 - 0
jzo2o-es/src/main/java/com/jzo2o/es/model/query/EsPageQuery.java

@@ -0,0 +1,9 @@
+package com.jzo2o.es.model.query;
+
+import lombok.Data;
+
+@Data
+public class EsPageQuery {
+
+
+}

+ 19 - 0
jzo2o-es/src/main/java/com/jzo2o/es/properties/EsProperties.java

@@ -0,0 +1,19 @@
+package com.jzo2o.es.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConfigurationProperties(prefix = "jzo2o.es")
+@Data
+public class EsProperties {
+    /**
+     * es host
+     */
+    private String host;
+    /**
+     * es 端口
+     */
+    private Integer port;
+}

+ 38 - 0
jzo2o-es/src/main/java/com/jzo2o/es/utils/DocumentUtils.java

@@ -0,0 +1,38 @@
+package com.jzo2o.es.utils;
+
+import com.jzo2o.common.utils.BeanUtils;
+import com.jzo2o.common.utils.CollUtils;
+import org.elasticsearch.xcontent.XContentBuilder;
+import org.elasticsearch.xcontent.XContentFactory;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class DocumentUtils {
+
+    /**
+     * 不可用于集合
+     * @param document
+     * @return
+     * @param <T>
+     * @throws IOException
+     */
+    public static <T> XContentBuilder convert(T document)  {
+
+        Map<String, Object> map = BeanUtils.beanToMap(document);
+        if (CollUtils.isEmpty(map)) {
+            return null;
+        }
+        try {
+            XContentBuilder xContentBuilder = XContentFactory.jsonBuilder();
+            for (Map.Entry<String, Object> entry : map.entrySet()) {
+                xContentBuilder.field(entry.getKey(), entry.getValue());
+            }
+            return xContentBuilder.endObject();
+        }catch (IOException e){
+            return null;
+        }
+
+    }
+
+}

+ 45 - 0
jzo2o-es/src/main/java/com/jzo2o/es/utils/SearchResponseUtils.java

@@ -0,0 +1,45 @@
+package com.jzo2o.es.utils;
+
+import com.jzo2o.common.utils.CollUtils;
+import com.jzo2o.common.utils.ObjectUtils;
+import co.elastic.clients.elasticsearch.core.SearchResponse;
+import co.elastic.clients.elasticsearch.core.search.Hit;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class SearchResponseUtils {
+
+    public static boolean isSuccess(SearchResponse<?> searchResponse) {
+        return searchResponse != null && ObjectUtils.isNotEmpty(searchResponse.hits()) && CollUtils.isNotEmpty(searchResponse.hits().hits());
+    }
+
+    public static boolean isNotSuccess(SearchResponse<?> searchResponse) {
+        return !isSuccess(searchResponse);
+    }
+
+    public static <T> List<T> getResponse(SearchResponse<T> searchResponse) {
+        return getResponse(searchResponse, null);
+    }
+
+
+    public static <T> List<T> getResponse(SearchResponse<T> searchResponse, Convert<T> convert) {
+        if (!isSuccess(searchResponse)) {
+            return null;
+        }
+        if (convert == null) {
+            return searchResponse.hits().hits().stream().map(hit -> hit.source()).collect(Collectors.toList());
+        }
+
+        return searchResponse.hits().hits()
+                .stream().map(hit -> {
+                    convert.convert(hit, hit.source());
+                    return hit.source();
+                }).collect(Collectors.toList());
+
+    }
+
+    public static interface Convert<T> {
+        void convert(Hit<T> hit, T t);
+    }
+}

+ 21 - 0
jzo2o-es/src/main/java/com/jzo2o/es/utils/TermUtils.java

@@ -0,0 +1,21 @@
+package com.jzo2o.es.utils;
+
+import com.jzo2o.common.utils.CollUtils;
+import co.elastic.clients.elasticsearch._types.FieldValue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TermUtils {
+
+
+
+    public static <T> List<FieldValue> parse(List<T> sources) {
+        if (CollUtils.isEmpty(sources)) {
+            return null;
+        }
+        List<FieldValue> values = new ArrayList<>();
+        sources.stream().forEach(s -> values.add(FieldValue.of(s.toString())));
+        return values;
+    }
+}

+ 2 - 0
jzo2o-es/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  com.jzo2o.es.config.EsConfiguration

+ 4 - 0
jzo2o-es/src/test/resources/application.yml

@@ -0,0 +1,4 @@
+jzo2o:
+  es:
+    host: 192.168.101.65
+    port: 9200

+ 40 - 0
jzo2o-knife4j-web/pom.xml

@@ -0,0 +1,40 @@
+<?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>jzo2o-parent</artifactId>
+        <groupId>com.jzo2o</groupId>
+        <version>1.0-SNAPSHOT</version>
+        <relativePath>../jzo2o-parent</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>jzo2o-knife4j-web</artifactId>
+    <version>${project.parent.version}</version>
+
+    <packaging>jar</packaging>
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.jzo2o</groupId>
+            <artifactId>jzo2o-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-spring-boot-starter</artifactId>
+        </dependency>
+    </dependencies>
+
+
+</project>

+ 53 - 0
jzo2o-knife4j-web/src/main/java/com/jzo2o/knife4j/config/Knife4jConfiguration.java

@@ -0,0 +1,53 @@
+package com.jzo2o.knife4j.config;
+
+import com.jzo2o.common.model.Result;
+import com.jzo2o.knife4j.filter.SwaggerFilter;
+import com.jzo2o.knife4j.properties.SwaggerProperties;
+import com.fasterxml.classmate.TypeResolver;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+
+import javax.annotation.Resource;
+
+@Configuration
+@EnableConfigurationProperties({SwaggerProperties.class})
+@Import(SwaggerFilter.class)
+@ConditionalOnProperty(prefix = "swagger", name = "enable", havingValue = "true")
+public class Knife4jConfiguration {
+
+    @Resource
+    private SwaggerProperties swaggerProperties;
+
+    @Bean(value = "defaultApi2")
+    public Docket defaultApi2(TypeResolver typeResolver) {
+        // 1.初始化Docket
+        Docket docket = new Docket(DocumentationType.SWAGGER_2);
+        // 2.是否需要包装R
+        docket.additionalModels(typeResolver.resolve(Result.class));
+        return docket.apiInfo(new ApiInfoBuilder()
+                        .title(this.swaggerProperties.getTitle())
+                        .description(this.swaggerProperties.getDescription())
+                        .contact(new Contact(
+                                this.swaggerProperties.getContactName(),
+                                this.swaggerProperties.getContactUrl(),
+                                this.swaggerProperties.getContactEmail()))
+                        .version(this.swaggerProperties.getVersion())
+                        .build())
+                .select()
+                //这里指定Controller扫描包路径
+                .apis(RequestHandlerSelectors.basePackage(this.swaggerProperties.getPackagePath()))
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+
+}

+ 29 - 0
jzo2o-knife4j-web/src/main/java/com/jzo2o/knife4j/filter/SwaggerFilter.java

@@ -0,0 +1,29 @@
+package com.jzo2o.knife4j.filter;
+
+import com.jzo2o.common.utils.IoUtils;
+import com.jzo2o.knife4j.response.SwaggerTransformServletResponse;
+import com.jzo2o.knife4j.utils.ResponseWrapper;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Component
+public class SwaggerFilter implements Filter {
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        if (((HttpServletRequest) servletRequest).getRequestURI().contains("/v2/api-docs")) {
+
+            ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) servletResponse);
+            filterChain.doFilter(servletRequest, responseWrapper);
+            responseWrapper.getResponseData();
+            byte[] responseData = SwaggerTransformServletResponse.getBody(responseWrapper.getResponseData());
+            IoUtils.write(servletResponse.getOutputStream(), false, responseData);
+        } else {
+            filterChain.doFilter(servletRequest, servletResponse);
+        }
+
+    }
+}

+ 53 - 0
jzo2o-knife4j-web/src/main/java/com/jzo2o/knife4j/properties/SwaggerProperties.java

@@ -0,0 +1,53 @@
+package com.jzo2o.knife4j.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.io.Serializable;
+
+/**
+ * swagger配置属性
+ *
+ * @Author itheima
+ * @Date 2023/04/06 17:25
+ */
+@Data
+@ConfigurationProperties(prefix = "swagger")
+public class SwaggerProperties implements Serializable {
+    private Boolean enableResponseWrap = false;
+
+    /**
+     * controller扫描路径
+     */
+    public String packagePath;
+
+    /**
+     * swagger文档标题
+     */
+    public String title;
+
+    /**
+     * 应用描述
+     */
+    public String description;
+
+    /**
+     * 联系人名称
+     */
+    public String contactName;
+
+    /**
+     * 联系人访问地址
+     */
+    public String contactUrl;
+
+    /**
+     * 联系人email
+     */
+    public String contactEmail;
+
+    /**
+     * 版本号
+     */
+    public String version;
+}

+ 145 - 0
jzo2o-knife4j-web/src/main/java/com/jzo2o/knife4j/response/SwaggerTransformServletResponse.java

@@ -0,0 +1,145 @@
+package com.jzo2o.knife4j.response;
+
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class SwaggerTransformServletResponse {
+
+    private static final String voidName = String.format("R«%s»", "Void");
+    private static final String rName = "R";
+    private static final String DEFINITIONS = "definitions";
+    private static final String PATHS = "paths";
+    private static final String RESPONSES = "responses";
+    private static final String SCHEMA = "schema";
+    private static final String REF = "$ref";
+    private static final String TYPE = "type";
+    private static final String TITLE = "title";
+    private static final String ORIGINAL_REF = "originalRef";
+    private static final String REF_PREFIX = "#/definitions/";
+
+
+    public static byte[] getBody(byte[] bytes) throws IOException {
+
+        JSONObject strData = JSONUtil.createObj()
+                .putOnce(TYPE, "string")
+                .putOnce("example", "")
+                .putOnce("description", "响应数据");
+        JSONObject rDefinition = buildDefinition(rName, strData);
+        JSONObject voidDefinition = buildDefinition(voidName, null);
+        JSONObject voidSchema = buildSchema(voidName);
+        JSONObject rSchema = buildSchema(rName);
+        // 2.转为json
+        String json = new String(bytes, StandardCharsets.UTF_8);
+        JSONObject jsonObject = JSONUtil.parseObj(json);
+        // 3.获取原始数据
+        // 3.1.获取paths
+        JSONObject paths = (JSONObject) jsonObject.getObj(PATHS);
+        // 3.2.获取definitions
+        JSONObject definitions = (JSONObject) jsonObject.getObj(DEFINITIONS);
+        // 3.3.遍历路径
+        for (Object pathObj : paths.values()) {
+            JSONObject path = (JSONObject) pathObj;
+            // 3.4.遍历请求方式对应的接口
+            for (Object pathValueObj : path.values()) {
+                JSONObject pathValue = (JSONObject) pathValueObj;
+                // 3.5.获取200响应结果
+                JSONObject responses = (JSONObject) pathValue.get(RESPONSES);
+                JSONObject resp200 = (JSONObject) responses.get("200");
+                // 3.6.获取旧的schema,基于schema做判断进行下一步处理
+                Object schemaObj = resp200.get(SCHEMA);
+                if (schemaObj != null) {
+                    // 有返回值
+                    JSONObject schema = (JSONObject) schemaObj;
+                    String type = schema.get(TYPE, String.class);
+                    if (type == null) {
+                        // 对象类型
+                        handleObjectSchema(resp200, definitions, schema);
+                    } else if (type.equals("array")) {
+                        // 数组类型
+                        handleArraySchema(resp200, definitions, schema);
+                    } else {
+                        // 基本类型
+                        handleBasicSchema(resp200, definitions, rDefinition,rSchema);
+                    }
+                } else {
+                    // 返回值是void
+                    handleVoidSchema(resp200, definitions, voidDefinition, voidSchema);
+                }
+
+            }
+        }
+
+        return jsonObject.toString().getBytes(StandardCharsets.UTF_8);
+    }
+
+    private static void handleVoidSchema(JSONObject resp200, JSONObject definitions,JSONObject voidDefinition,JSONObject voidSchema) {
+        // 1.写入definition
+        definitions.putIfAbsent(voidName, voidDefinition);
+        // 2.写入path
+        resp200.putOpt(SCHEMA, voidSchema);
+    }
+
+    private static void handleBasicSchema(JSONObject resp200, JSONObject definitions,JSONObject rDefinition,JSONObject rSchema) {
+        // 1.写入definition
+        definitions.putIfAbsent(rName, rDefinition);
+        // 2.写入path
+        resp200.putOpt(SCHEMA, rSchema);
+    }
+
+    private static void handleArraySchema(JSONObject resp200, JSONObject definitions, JSONObject oldSchema) {
+        // 1.写入definition
+        String originalRef = oldSchema.getByPath("items." + ORIGINAL_REF, String.class);
+        String name = String.format("R«List«%s»»", originalRef);
+        definitions.putIfAbsent(name, buildArrayDefinition(originalRef, name));
+        // 2.写入path
+        resp200.putOpt(SCHEMA, buildSchema(name));
+    }
+
+    private static void handleObjectSchema(JSONObject resp200, JSONObject definitions, JSONObject oldSchema) {
+        // 1.写入definition
+        String originalRef = oldSchema.get(ORIGINAL_REF, String.class);
+        String name = String.format("R«%s»", originalRef);
+        definitions.putIfAbsent(name, buildDefinition(name, buildSchema(originalRef)));
+        // 2.写入path
+        resp200.putOpt(SCHEMA, buildSchema(name));
+    }
+
+    private static JSONObject buildArrayDefinition(String originalRef, String title) {
+        JSONObject data = JSONUtil.createObj()
+                .putOnce("description", "响应数据")
+                .putOnce("type", "array")
+                .putOnce("items",
+                        buildSchema(originalRef));
+        return buildDefinition(title, data);
+    }
+
+    private static JSONObject buildDefinition(String title, Object data) {
+        JSONObject code = JSONUtil.createObj()
+                .putOnce(TYPE, "integer")
+                .putOnce("format", "int32")
+                .putOnce("example", 200)
+                .putOnce("description", "状态码,200-成功,其它-失败");
+        JSONObject msg = JSONUtil.createObj()
+                .putOnce(TYPE, "string")
+                .putOnce("example", "OK")
+                .putOnce("description", "响应消息");
+        JSONObject properties = JSONUtil.createObj().putOnce("code", code).putOnce("msg", msg);
+        if (data != null) {
+            properties.putOnce("data", data);
+        }
+        return JSONUtil.createObj()
+                .putOnce(TYPE, "object")
+                .putOnce(TITLE, title)
+                .putOnce("properties", properties)
+                .putOnce("description", "通用响应结果");
+    }
+
+    private static JSONObject buildSchema(String name) {
+        return JSONUtil.createObj()
+                .putOnce(REF, REF_PREFIX + name)
+                .putOnce(ORIGINAL_REF, name);
+    }
+}

+ 99 - 0
jzo2o-knife4j-web/src/main/java/com/jzo2o/knife4j/utils/ResponseWrapper.java

@@ -0,0 +1,99 @@
+package com.jzo2o.knife4j.utils;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * 重写response返回值
+ *
+ * @author itcast
+ */
+public class ResponseWrapper extends HttpServletResponseWrapper {
+    private ByteArrayOutputStream buffer = null;
+    private ServletOutputStream out = null;
+    private PrintWriter writer = null;
+
+    public ResponseWrapper(HttpServletResponse resp) throws IOException {
+        super(resp);
+        buffer = new ByteArrayOutputStream();// 真正存储数据的流
+        out = new WapperedOutputStream(buffer);
+        writer = new PrintWriter(new OutputStreamWriter(buffer));
+    }
+
+    @Override
+    public ServletOutputStream getOutputStream() throws IOException {
+        return out;
+    }
+
+    @Override
+    public PrintWriter getWriter() throws UnsupportedEncodingException {
+        return writer;
+    }
+
+    @Override
+    public void flushBuffer() throws IOException {
+        if (out != null) {
+            out.flush();
+        }
+        if (writer != null) {
+            writer.flush();
+        }
+    }
+
+    @Override
+    public void reset() {
+        buffer.reset();
+    }
+
+    public byte[] getResponseData() throws IOException {
+        flushBuffer();
+        return buffer.toByteArray();
+    }
+
+    public String getContent() throws IOException{
+        flushBuffer();
+        return buffer.toString();
+    }
+
+    private class WapperedOutputStream extends ServletOutputStream {
+        private ByteArrayOutputStream bos = null;
+
+        public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException {
+            bos = stream;
+        }
+
+        @Override
+        public void write(int b) throws IOException {
+            bos.write(b);
+        }
+
+        @Override
+        public void write(byte[] b) throws IOException {
+            bos.write(b, 0, b.length);
+        }
+
+        @Override
+        public void write(byte[] b, int off, int len) throws IOException {
+            bos.write(b, off, len);
+        }
+
+        @Override
+        public boolean isReady() {
+            return false;
+        }
+
+        @Override
+        public void setWriteListener(WriteListener writeListener) {
+
+        }
+    }
+}

+ 9 - 0
jzo2o-knife4j-web/src/main/resources/META-INF/spring-configuration-metadata.json

@@ -0,0 +1,9 @@
+{
+  "groups": [
+
+  ],
+  "properties": [
+
+  ],
+  "hints": []
+}

+ 2 - 0
jzo2o-knife4j-web/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.jzo2o.knife4j.config.Knife4jConfiguration

+ 74 - 0
jzo2o-mvc/pom.xml

@@ -0,0 +1,74 @@
+<?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>jzo2o-parent</artifactId>
+        <groupId>com.jzo2o</groupId>
+        <version>1.0-SNAPSHOT</version>
+        <relativePath>../jzo2o-parent</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>jzo2o-mvc</artifactId>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.jzo2o</groupId>
+            <artifactId>jzo2o-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tomcat.embed</groupId>
+            <artifactId>tomcat-embed-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!--undertow服务器-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-undertow</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.aspectj</groupId>
+            <artifactId>aspectjweaver</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+        </dependency>
+
+
+
+    </dependencies>
+</project>

+ 95 - 0
jzo2o-mvc/src/main/java/com/jzo2o/mvc/advice/CommonExceptionAdvice.java

@@ -0,0 +1,95 @@
+package com.jzo2o.mvc.advice;
+
+import com.jzo2o.common.constants.ErrorInfo;
+import com.jzo2o.common.expcetions.CommonException;
+import com.jzo2o.common.utils.*;
+import com.jzo2o.mvc.constants.HeaderConstants;
+import com.jzo2o.mvc.model.Result;
+import com.jzo2o.mvc.utils.RequestUtils;
+import com.jzo2o.mvc.utils.ResponseUtils;
+import feign.FeignException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import static com.jzo2o.mvc.constants.HeaderConstants.BODY_PROCESSED;
+
+/**
+ * @author itcast
+ */
+@RestControllerAdvice
+@Slf4j
+public class CommonExceptionAdvice {
+
+
+    /**
+     * 捕获feign异常
+     * @param e
+     * @return
+     */
+    @ExceptionHandler({FeignException.class})
+    public Result feignException(FeignException e) {
+        ResponseUtils.setResponseHeader(BODY_PROCESSED, "1");
+        Object headerValue = e.responseHeaders().get(HeaderConstants.INNER_ERROR);
+
+        if(RequestUtils.getRequest().getRequestURL().toString().contains("/inner/")) {
+            // 内部接口调用内部接口,异常抛出
+            if(ObjectUtils.isNull(headerValue)) {
+                throw new CommonException(ErrorInfo.Msg.REQUEST_FAILD);
+            }else {
+                String encodeMsg = JsonUtils.parseArray(headerValue).getStr(0);
+                String[] msgs = Base64Utils.decodeStr(encodeMsg).split("\\|");
+                throw new CommonException(NumberUtils.parseInt(msgs[0]), msgs[1]);
+            }
+        }else {
+            // 外部接口调用内部接口异常捕获
+            if(ObjectUtils.isNull(headerValue)) {
+                return Result.error(ErrorInfo.Msg.REQUEST_FAILD);
+            }else {
+                String encodeMsg = JsonUtils.parseArray(headerValue).getStr(0);
+                String[] msgs = Base64Utils.decodeStr(encodeMsg).split("\\|");
+                return Result.error(NumberUtils.parseInt(msgs[0]), msgs[1]);
+            }
+        }
+    }
+
+    /**
+     * 自定义异常处理
+     * @param e
+     * @return
+     */
+    @ExceptionHandler({CommonException.class})
+    public Result customException(CommonException e) {
+        log.error("请求异常,message:{},e", e.getMessage(),e);
+        // 标识异常已被处理
+        ResponseUtils.setResponseHeader(BODY_PROCESSED, "1");
+        if(RequestUtils.getRequest().getRequestURL().toString().contains("/inner/")) {
+            CommonException commonException = new CommonException(e.getCode(), e.getMessage());
+            ResponseUtils.setResponseHeader(HeaderConstants.INNER_ERROR, Base64Utils.encodeStr(e.getCode() + "|" + e.getMessage()));
+            throw commonException;
+        }
+        return Result.error(e.getCode(), e.getMessage());
+    }
+
+    /**
+     * 非自定义异常处理
+     * @param e 异常
+     * @return
+     */
+    @ExceptionHandler({Exception.class})
+    public Result noCustomException(Exception e) {
+        log.error("请求异常,", e);
+        // 标识异常已被处理
+        ResponseUtils.setResponseHeader(BODY_PROCESSED, "1");
+        if(RequestUtils.getRequest().getRequestURL().toString().contains("/inner/")) {
+            CommonException commonException = new CommonException(ErrorInfo.Msg.REQUEST_FAILD);
+
+            ResponseUtils.setResponseHeader(HeaderConstants.INNER_ERROR, Base64Utils.encodeStr( "500|" + ErrorInfo.Msg.REQUEST_FAILD));
+            throw commonException;
+        }
+        return Result.error(ErrorInfo.Msg.REQUEST_FAILD);
+    }
+
+
+
+}

+ 11 - 0
jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/AutoConfiguration.java

@@ -0,0 +1,11 @@
+package com.jzo2o.mvc.config;
+
+import com.jzo2o.mvc.handler.RequestIdHandlerImpl;
+import com.jzo2o.mvc.handler.UserInfoHandlerImpl;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+@Configuration
+@Import({RequestIdHandlerImpl.class, UserInfoHandlerImpl.class})
+public class AutoConfiguration {
+}

+ 34 - 0
jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/CrosConfig.java

@@ -0,0 +1,34 @@
+//package com.jzo2o.mvc.config;
+//
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.web.cors.CorsConfiguration;
+//import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+//import org.springframework.web.filter.CorsFilter;
+//
+//import java.util.Collections;
+//
+///**
+// * 解决apifox拉取api报错问题
+// */
+//@Configuration
+//public class CrosConfig {
+//
+//    @Bean
+//    public CorsFilter corsFilter() {
+//        // 跨域配置源
+//        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+//        //设置跨域的配置信息
+//        CorsConfiguration corsConfiguration = new CorsConfiguration();
+//        //1,允许任何来源
+//        corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*"));
+//        //2,允许任何请求头
+//        corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);
+//        //3,允许任何方法
+//        corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);
+//        //4,允许凭证
+//        corsConfiguration.setAllowCredentials(true);
+//        source.registerCorsConfiguration("/**", corsConfiguration);
+//        return new CorsFilter(source);
+//    }
+//}

+ 13 - 0
jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/FilterConfiguration.java

@@ -0,0 +1,13 @@
+package com.jzo2o.mvc.config;
+
+import com.jzo2o.mvc.filter.PackResultFilter;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+/**
+ * @author itcast
+ */
+@Configuration
+@Import(PackResultFilter.class)
+public class FilterConfiguration {
+}

+ 43 - 0
jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/JsonConfig.java

@@ -0,0 +1,43 @@
+package com.jzo2o.mvc.config;
+
+import com.jzo2o.common.utils.DateUtils;
+import com.jzo2o.mvc.serialize.BigDecimalSerializer;
+import com.jzo2o.mvc.serialize.LocalDateTimeSerializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+import static com.jzo2o.common.utils.DateUtils.DEFAULT_DATE_TIME_FORMAT;
+
+/**
+ * @author itcast
+ */
+@Configuration
+@ConditionalOnClass(ObjectMapper.class)
+public class JsonConfig {
+    @Bean
+    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
+        return jacksonObjectMapperBuilder -> {
+            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT);
+            jacksonObjectMapperBuilder.timeZone(DateUtils.TIME_ZONE_8);
+            jacksonObjectMapperBuilder.serializers(new com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer(dateTimeFormatter));
+            jacksonObjectMapperBuilder.deserializers(new LocalDateTimeDeserializer(dateTimeFormatter));
+            // long -> string
+            jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance);
+            jacksonObjectMapperBuilder.serializerByType(BigInteger.class, ToStringSerializer.instance);
+            // bigDecimal保留两位小数
+            jacksonObjectMapperBuilder.serializerByType(BigDecimal.class, BigDecimalSerializer.instance);
+            // LocalDateTime 格式yyyy-MM-dd HH:mm:ss
+            jacksonObjectMapperBuilder.serializerByType(LocalDateTime.class, LocalDateTimeSerializer.instance);
+        };
+    }
+}

+ 31 - 0
jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/MvcConfig.java

@@ -0,0 +1,31 @@
+package com.jzo2o.mvc.config;
+
+
+import com.jzo2o.mvc.advice.CommonExceptionAdvice;
+import com.jzo2o.mvc.interceptor.UserContextInteceptor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+@Slf4j
+public class MvcConfig implements WebMvcConfigurer {
+
+    /**
+     * <h1>通用的ControllerAdvice异常处理器</h1>
+     */
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        // token拦截器
+        registry.addInterceptor(new UserContextInteceptor())
+                .addPathPatterns("/**");
+    }
+
+    @Bean
+    public CommonExceptionAdvice commonExceptionAdvice() {
+        return new CommonExceptionAdvice();
+    }
+
+}

+ 27 - 0
jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/UserContextConfiguration.java

@@ -0,0 +1,27 @@
+//package com.jzo2o.mvc.config;
+//
+//import com.jzo2o.mvc.advice.CommonExceptionAdvice;
+//import com.jzo2o.mvc.interceptor.UserContextInteceptor;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+//import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+//
+///**
+// * @author itcast
+// */
+//@Configuration
+//public class UserContextConfiguration implements WebMvcConfigurer {
+//
+//    @Override
+//    public void addInterceptors(InterceptorRegistry registry) {
+//        // token拦截器
+//        registry.addInterceptor(new UserContextInteceptor())
+//                .addPathPatterns("/**");
+//    }
+//
+//    @Bean
+//    public CommonExceptionAdvice commonExceptionAdvice() {
+//        return new CommonExceptionAdvice();
+//    }
+//}

+ 10 - 0
jzo2o-mvc/src/main/java/com/jzo2o/mvc/constants/HeaderConstants.java

@@ -0,0 +1,10 @@
+package com.jzo2o.mvc.constants;
+
+public class HeaderConstants {
+    /**
+     * 判断响应参数是否已经处理,1:已经处理,其他未经处理
+     */
+    public static final String BODY_PROCESSED = "Processed-Mark";
+
+    public static final String INNER_ERROR = "INNER-ERROR";
+}

+ 52 - 0
jzo2o-mvc/src/main/java/com/jzo2o/mvc/filter/PackResultFilter.java

@@ -0,0 +1,52 @@
+package com.jzo2o.mvc.filter;
+
+import com.jzo2o.common.model.Result;
+import com.jzo2o.common.utils.IoUtils;
+import com.jzo2o.mvc.wrapper.ResponseWrapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+import static com.jzo2o.mvc.constants.HeaderConstants.BODY_PROCESSED;
+
+/**
+ * 用于包装外网访问
+ */
+@Component
+@Slf4j
+public class PackResultFilter implements Filter {
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        // 1.无需包装,放过拦截
+        String requestURI = ((HttpServletRequest) servletRequest).getRequestURI();
+        if (requestURI.contains(".") ||
+                requestURI.contains("/swagger") ||
+                requestURI.contains("/api-docs") ||
+                requestURI.contains("/inner")) {
+            filterChain.doFilter(servletRequest, servletResponse);
+            return;
+        }
+        // 2.包装响应值
+        // 2.1.处理业务,获取响应值
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+        ResponseWrapper responseWrapper = new ResponseWrapper(response);
+        filterChain.doFilter(servletRequest, responseWrapper);
+
+        // 无需包装
+        if (response.containsHeader(BODY_PROCESSED) && response.getHeader(BODY_PROCESSED).equals("1")) {
+            IoUtils.write(response.getOutputStream(), false, responseWrapper.getResponseData());
+            return;
+        }
+
+        // 2.2.包装
+        byte[] bytes = Result.plainOk(responseWrapper.getResponseData());
+        log.debug("result : {}", new String(bytes));
+        // 2.3.写入
+        response.setContentType("applicaton/json;charset=UTF-8");
+        IoUtils.write(response.getOutputStream(), false, bytes);
+    }
+}

+ 31 - 0
jzo2o-mvc/src/main/java/com/jzo2o/mvc/handler/RequestIdHandlerImpl.java

@@ -0,0 +1,31 @@
+package com.jzo2o.mvc.handler;
+
+import cn.hutool.core.util.IdUtil;
+import com.jzo2o.common.constants.HeaderConstants;
+import com.jzo2o.common.handler.RequestIdHandler;
+import com.jzo2o.common.model.CurrentUserInfo;
+import com.jzo2o.common.utils.StringUtils;
+import com.jzo2o.mvc.utils.RequestUtils;
+import com.jzo2o.mvc.utils.UserContext;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author itcast
+ */
+@Component
+public class RequestIdHandlerImpl implements RequestIdHandler {
+    @Override
+    public String getRequestId() {
+        // 从请求header头中获取请求id,获取不到id,生成新的请求id
+        CurrentUserInfo currentUserInfo = UserContext.currentUser();
+        if(currentUserInfo == null) {
+            return null;
+        }
+        String requestId = RequestUtils.getValueFromHeader(HeaderConstants.REQUEST_ID);
+        if (StringUtils.isEmpty(requestId)) {
+            return IdUtil.getSnowflakeNextIdStr();
+        } else {
+            return requestId;
+        }
+    }
+}

+ 14 - 0
jzo2o-mvc/src/main/java/com/jzo2o/mvc/handler/UserInfoHandlerImpl.java

@@ -0,0 +1,14 @@
+package com.jzo2o.mvc.handler;
+
+import com.jzo2o.common.handler.UserInfoHandler;
+import com.jzo2o.common.model.CurrentUserInfo;
+import com.jzo2o.mvc.utils.UserContext;
+import org.springframework.stereotype.Component;
+
+@Component
+public class UserInfoHandlerImpl implements UserInfoHandler {
+    @Override
+    public CurrentUserInfo currentUserInfo() {
+        return UserContext.currentUser();
+    }
+}

+ 47 - 0
jzo2o-mvc/src/main/java/com/jzo2o/mvc/interceptor/UserContextInteceptor.java

@@ -0,0 +1,47 @@
+package com.jzo2o.mvc.interceptor;
+
+import com.jzo2o.common.constants.HeaderConstants;
+import com.jzo2o.common.model.CurrentUserInfo;
+import com.jzo2o.common.utils.Base64Utils;
+import com.jzo2o.common.utils.JsonUtils;
+import com.jzo2o.mvc.utils.UserContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author itcast
+ */
+@Slf4j
+public class UserContextInteceptor implements HandlerInterceptor {
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        // 1.尝试获取头信息中的用户信息
+        String userInfo = request.getHeader(HeaderConstants.USER_INFO);
+        // 2.判断是否为空
+        if (userInfo == null) {
+            return true;
+        }
+        try {
+            // 3.base64解码用户信息
+            String decodeUserInfo = Base64Utils.decodeStr(userInfo);
+            CurrentUserInfo currentUserInfo = JsonUtils.toBean(decodeUserInfo, CurrentUserInfo.class);
+
+            // 4.转为用户id并保存
+            UserContext.set(currentUserInfo);
+            return true;
+        } catch (NumberFormatException e) {
+            log.error("用户身份信息格式不正确,{}, 原因:{}", userInfo, e.getMessage());
+            return true;
+        }
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+        // 清理用户信息
+        UserContext.clear();
+    }
+}

Some files were not shown because too many files changed in this diff